android 安全sdk相关

前述

        在网上有看到许多android安全sdk相关的内容,有重复的也有比较新鲜的内容,这里做一个整体的合集,以及后续又看到一些比较新的东西会一起放在这里。

        android内sdk目前可以分为以下几个部分(有一些部分可能会存在一些重合)——设备指纹,root相关,环境检测,模拟器特征,改机软件

设备指纹

1.android_id

google给的app唯一id,存放在/data/system/users/0/settings_ssaid.xml文件中,只有在文件存储位置处修改,设备重刷或设备恢复出厂设置等时候才会被修改。可以用Settings.Global.getString() 、Settings.Secure.getString()、Settings.System.getString()来获取(根据/data/system/users/0/下的文件名字推测可用这些方法获取该目录下一些文件的内容)

2.drmid(设备媒体id)

media drm的设备id,同时和app包名以及签名有关,即不同包名或者不同签名,drmid会不一样,可以视为另外一个android_id,缺点是还和包名有关,注意:极少数机型上获取该id的过程存在崩溃且google也在逐渐的废除该id,可参考:https://developer.android.com/identity/user-data-ids

UUID wideVineUuid = new UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L);
try {
    MediaDrm wvDrm = new MediaDrm(wideVineUuid);
    byte[] wideVineId = wvDrm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID);
    return Utils.md5(wideVineId);
} catch (Exception e) {
    return null;
}

3.gsfiid

gsfiid是Google Service Framework ID,Google服务框架 ID

public static String gsfid() {
    Context context = DeviceGlobal.getContext();
    Uri uri = Uri.parse("content://com.google.android.gsf.gservices");
    if (uri != null) {
        try (Cursor cursor = context.getContentResolver().query(uri,
                null, null,
                new String[]{"android_id"}, null)) {
            if (cursor.moveToFirst()) {
                return Long.toHexString(Long.parseLong(cursor.getString(1)));
            }
        } catch (Throwable tr) {
            Logger.w(TAG, tr.toString());
        }
    }
    return null;
}

4.apk相关

apk安装时间和路径,可以通过getPackageInfo中的firstInstallTime获取,可以作为唯一id和防刷

5.boot_id,oaid,imei,serial number

boot_id可以通过cat /proc/sys/kernel/random/boot_id获取,重启后会更新,是uuid格式字符串格式。

imei是SIM卡相关的信息,谷歌已经删除该属性来保护隐私性

serial number是手机adb devices的一串数字,可以通过getprop ro.serialno获取

oaid是国内android广告标识符,用户可以选择重置和退出广告跟踪。

6.文件哈希

使用stat获取/,/data/,/data/system/vendor/firmware,/system/bin,/vendor/lib,/systen/framework,/system/fonts,或者一些固定app目录的文件名,inode,uid,创建时间,最后修改时间组合等属性组合求hash作为指纹

7.网络相关

这里主要是指机器的ip,mac,网络的类型,这里的信息除了可以做设备指纹之外,也可以用来判断设备的环境等特征。获取方法可以通过ip -a或者ip neigh show命令行执行或者读取/proc/net/arp获取相关信息

root检测

1.path检测

getenv("path") 获取路径,和su或者magisk组合,判断root是否存在

2.属性获取

可以通过以下属性特征来判断设备是不是root了

getprop service.adb.root = "1"
getprop init.svc.adb_root = "running"
getprop ro.secure = "0"
getprop ro.debuggable = "1"
getprop ro.build.tags = "test-keys"

3.内存扫描

内存扫描有以下几种方式——1.检测/proc/self/maps 是否存在名为“/memfd:/jit-cache"的段(加载zygisk模块);2.检测栈空间[stack]的权限是否为"rw-p";3.检测map表是否存在匿名的且具有可执行属性。

4.selinux上下文检测

cat /proc/self/attr/prev,看文件中的内容是否为"u:r:zygote:s0",其中r表示进程,zygote表示进程权限

5.常见root应用检测

可以尝试包名检测,遍历/data/data/目录,看有没有一下目录

com.topjohnwu.magisk
eu.chainfire.supersu
com.noshufou.android.su
com.noshufou.android.su.elite
com.koushikdutta.superuser
com.thirdparty.superuser
com.yellowes.su
com.fox2code.mmm
io.github.vvb2060.magisk
com.kingroot.kinguser
com.kingo.root
com.smedialink.oneclickroot
com.zhiqupk.root.global
com.alephzain.framaroot
io.github.huskydg.magisk
me.weishu.kernelsu

6.其他

可以尝试访问/data,/system,/etc,查询是否有busybox可以执行——尝试which busybox或者是执行busybox

hook检测

1.frida检测

  • 默认端口检测——27042
  • 常见路径检测——/data/local/tmp/frida-server
  • 检查进程/proc/self/fd/目录,寻找是否存在关键字linjector
  • 内存模块扫描——frida-agent,frida-gadge,可以去/proc/self/maps文件下找找字符串
  • inlinehook检测frida——查询函数头部是不是0xd61f020058000050

2.Xposed检测

  • java.lang.ClassLoader.getSystemClassLoader.loadClass("de.robv.android.xposed.XposedHelpers") ——看xposed是否加载
  • 遍历/data/data/目录,寻找有无以下包名
de.robv.android.xposed.installer #Xposed框架
org.meowcat.edxposed.manager #EdXposed框架
com.tsng.hidemyapplist #隐藏应用列表
com.tsng.hidemyroot #隐藏Root
org.lsposed.manager #LSPosed框架
me.weishu.exp #VirtualXposed 太极
top.canyie.dreamland.manager #VirtualXposed 夢境
io.va.exposed #VirtualXposed 夢境
io.va.exposed64 #VirtualXposed 夢境
io.virtualapp #VirtualApp
io.virtualapp.sandvxposed64 #VirtualApp
  • 检测下列文件或文件路径是否存在
/sbin/.magisk/modules/riru_lsposed
/data/adb/lspd
/sbin/.magisk/modules/zygisk_lsposed
/sbin/.magisk/modules/riru_edxposed
/data/misc/riru/modules/edxp
/data/adb/riru/modules/edxp.prop
/sbin/.magisk/modules/taichi
/data/misc/taichi
/sbin/.magisk/modules/dreamland
/data/misc/riru/modules/dreamland
/data/adb/riru/modules/dreamland
/system/bin/app_process.orig
/system/xposed.prop
/system/framework/XposedBridge.jar
/system/lib/
libxposed_art.so
/system/lib/
libxposed_art.so
.no_orig
/system/lib64/
libxposed_art.so
/system/lib64/
libxposed_art.so
.no_orig
/system/bin/app_process_zposed
/system/framework/ZposedBridge.jar
/system/lib/
libzposed_art.so
  • artmethod结构体判断,artmethod中access_flags和entry_point_from_jni_在hook过程中会发生更改。

3.调试检测

  • 调试端口检测——读取/proc/net/tcp,ida远程调试端口用的是23946端口
  • 调试进程名检测——直接ps -a,看是否有android-server或者adb-server
  • tracepid检测——读取/proc/self/status,查看tracepid是不是为0
  • ptrace检测——因为每个进程只能被一个ptrace检测,所以可以自己启动一个ptrace或者对ptrace检测
  • 断电指令检测——检测bkpt指令
  • dalvik模式下可以用libdvm.so中dvmDbgIsDebuggerConnected()函数检测,art模式下使用libart.so中gDebuggerActive中
  • 可以读取/proc/self/cmdline查看内容是否为zygote
  • apk线程检测——so文件通常只有一个进程加载,可以查询/proc/pid/task下有数字的目标数量

模拟器云手机检测

1.特有的文件

模拟器特有文件

"/boot/bstmods/vboxguest.ko",
"/boot/bstmods/vboxsf.ko",
"/dev/mtp_usb",
"/dev/qemu_pipe",
"/dev/socket/baseband_genyd",
"/dev/socket/genyd",
"/dev/socket/qemud",
"/dev/socket/windroyed-audio",
"/dev/socket/windroyed-camera",
"/dev/socket/windroyed-gps",
"/dev/socket/windroyed-sensors",
"/dev/vboxguest",
"/dev/vboxpci",
"/dev/vboxuser",
"/fstab.goldfish",
"/fstab.nox",
"/fstab.ranchu-encrypt",
"/fstab.ranchu-noencrypt",
"/fstab.ttVM_x86",
"/fstab.vbox86",
"/init.goldfish.rc",
"/init.magisk.rc",
"/init.nox.rc",
"/init.ranchu-encrypt.rc",
"/init.ranchu-noencrypt.rc",
"/init.ranchu.rc",
"/init.ttVM_x86.rc",
"/init.vbox86.rc",
"/init.vbox86p.rc",
"/init.windroye.rc",
"/init.windroye.sh",
"/init.x86.rc",
"/proc/irq/20/vboxguest",
"/sdcard/Android/data/com.redfinger.gamemanage",
"/stab.andy",
"/sys/bus/pci/drivers/vboxguest",
"/sys/bus/pci/drivers/vboxpci",
"/sys/bus/platform/drivers/qemu_pipe",
"/sys/bus/platform/drivers/qemu_pipe/qemu_pipe",
"/sys/bus/platform/drivers/qemu_trace",
"/sys/bus/virtio/drivers/itolsvmlgtp",
"/sys/bus/virtio/drivers/itoolsvmhft",
"/sys/class/bdi/vboxsf-1",
"/sys/class/bdi/vboxsf-2",
"/sys/class/bdi/vboxsf-3",
"/sys/class/misc/qemu_pipe",
"/sys/class/misc/vboxguest",
"/sys/class/misc/vboxuser",
"/sys/devices/platform/qemu_pipe",
"/sys/devices/virtual/bdi/vboxsf-1",
"/sys/devices/virtual/bdi/vboxsf-2",
"/sys/devices/virtual/bdi/vboxsf-3",
"/sys/devices/virtual/misc/qemu_pipe",
"/sys/devices/virtual/misc/vboxguest",
"/sys/devices/virtual/misc/vboxpci",
"/sys/devices/virtual/misc/vboxuser",
"/sys/fs/selinux/booleans/in_qemu",
"/sys/kernel/debug/bdi/vboxsf-1",
"/sys/kernel/debug/bdi/vboxsf-2",
"/sys/kernel/debug/x86",
"/sys/module/qemu_trace_sysfs",
"/sys/module/vboxguest",
"/sys/module/vboxguest/drivers/pci:vboxguest",
"/sys/module/vboxpcism",
"/sys/module/vboxsf",
"/sys/module/vboxvideo",
"/sys/module/virtio_pt/drivers/virtio:itoolsvmhft",
"/sys/module/virtio_pt_ie/drivers/virtio:itoolsvmlgtp",
"/sys/qemu_trace",
"/system/app/GenymotionLayout",
"/system/bin/OpenglService",
"/system/bin/androVM-vbox-sf",
"/system/bin/droid4x",
"/system/bin/droid4x-prop",
"/system/bin/droid4x-vbox-sf",
"/system/bin/droid4x_setprop",
"/system/bin/enable_nox",
"/system/bin/genymotion-vbox-sf",
"/system/bin/microvirt-prop",
"/system/bin/microvirt-vbox-sf",
"/system/bin/microvirt_setprop",
"/system/bin/microvirtd",
"/system/bin/mount.vboxsf",
"/system/bin/nox",
"/system/bin/nox-prop",
"/system/bin/nox-vbox-sf",
"/system/bin/nox_setprop",
"/system/bin/noxd",
"/system/bin/noxscreen",
"/system/bin/noxspeedup",
"/system/bin/qemu-props",
"/system/bin/qemud",
"/system/bin/shellnox",
"/system/bin/ttVM-prop",
"/system/bin/windroyed",
"/system/droid4x",
"/system/etc/init.droid4x.sh",
"/system/etc/init.tiantian.sh",
"/system/lib/egl/libEGL_emulation.so",
"/system/lib/egl/libEGL_tiantianVM.so",
"/system/lib/egl/libEGL_windroye.so",
"/system/lib/egl/libGLESv1_CM_emulation.so",
"/system/lib/egl/libGLESv1_CM_tiantianVM.so",
"/system/lib/egl/libGLESv1_CM_windroye.so",
"/system/lib/egl/libGLESv2_emulation.so",
"/system/lib/egl/libGLESv2_tiantianVM.so",
"/system/lib/egl/libGLESv2_windroye.so",
"/system/lib/hw/audio.primary.vbox86.so",
"/system/lib/hw/audio.primary.windroye.so",
"/system/lib/hw/audio.primary.x86.so",
"/system/lib/hw/autio.primary.nox.so",
"/system/lib/hw/camera.vbox86.so",
"/system/lib/hw/camera.windroye.jpeg.so",
"/system/lib/hw/camera.windroye.so",
"/system/lib/hw/camera.x86.so",
"/system/lib/hw/gps.nox.so",
"/system/lib/hw/gps.vbox86.so",
"/system/lib/hw/gps.windroye.so",
"/system/lib/hw/gralloc.nox.so",
"/system/lib/hw/gralloc.vbox86.so",
"/system/lib/hw/gralloc.windroye.so",
"/system/lib/hw/sensors.nox.so",
"/system/lib/hw/sensors.vbox86.so",
"/system/lib/hw/sensors.windroye.so",
"/system/lib/init.nox.sh",
"/system/lib/libGM_OpenglSystemCommon.so",
"/system/lib/libc_malloc_debug_qemu.so",
"/system/lib/libclcore_x86.bc",
"/system/lib/libdroid4x.so",
"/system/lib/libnoxd.so",
"/system/lib/libnoxspeedup.so",
"/system/lib/modules/3.10.30-android-x86.hd+",
"/system/lib/vboxguest.ko",
"/system/lib/vboxpcism.ko",
"/system/lib/vboxsf.ko",
"/system/lib/vboxvideo.ko",
"/system/lib64/egl/libEGL_emulation.so",
"/system/lib64/egl/libGLESv1_CM_emulation.so",
"/system/lib64/egl/libGLESv2_emulation.so",
"/vendor/lib64/egl/libEGL_emulation.so",
"/vendor/lib64/egl/libGLESv1_CM_emulation.so",
"/vendor/lib64/egl/libGLESv2_emulation.so",
"/vendor/lib64/libandroidemu.so",
"/system/lib64/hw/gralloc.ranchu.so",
"/system/lib64/libc_malloc_debug_qemu.so",
"/system/usr/Keylayout/droid4x_Virtual_Input.kl",
"/system/usr/idc/Genymotion_Virtual_Input.idc",
"/system/usr/idc/droid4x_Virtual_Input.idc",
"/system/usr/idc/nox_Virtual_Input.idc",
"/system/usr/idc/windroye.idc",
"/system/usr/keychars/nox_gpio.kcm",
"/system/usr/keychars/windroye.kcm",
"/system/usr/keylayout/Genymotion_Virtual_Input.kl",
"/system/usr/keylayout/nox_Virtual_Input.kl",
"/system/usr/keylayout/nox_gpio.kl",
"/system/usr/keylayout/windroye.kl",
"system/etc/init/ndk_translation_arm64.rc",
"/system/xbin/noxsu",
"/ueventd.android_x86.rc",
"/ueventd.andy.rc",
"/ueventd.goldfish.rc",
"/ueventd.nox.rc",
"/ueventd.ranchu.rc",
"/ueventd.ttVM_x86.rc",
"/ueventd.vbox86.rc",
"/vendor/lib64/libgoldfish-ril.so",
"/vendor/lib64/libgoldfish_codecs_common.so",
"/vendor/lib64/libstagefright_goldfish_avcdec.so",
"/vendor/lib64/libstagefright_goldfish_vpxdec.so",
"/x86.prop",
"/system/lib/virtio_pt.ko", //itoolsAVM
"dev/bst_gps", //BlueStacks
"/dev/bstgyro", //BlueStacks Game
"/system/bin/bstfolder", //BlueStacks Game
"/system/lib/msf.ko", //memu
"/dev/nemuuser", //Mumu
"/dev/nemuinit", //Mumu
"/system/lib/fastpipe.ko", //Dnplayer
"/fstab.andy", //AndY
"/dev/virtiopt",    //itoolsAVM or nox
"/sys/module/vboxsf/srcversion", //vbox
"/dev/goldfish_pipe"  //qemu

2.特有的属性

  • qemu特有的属性
"ro.kernel.qemu.avd_name",
"ro.kernel.qemu.gles",
"ro.kernel.qemu.gltransport",
"ro.kernel.qemu.opengles.version",
"ro.kernel.qemu.uirenderer",
"ro.kernel.qemu.vsync",
"ro.qemu.initrc",
"init.svc.qemu-props",
"qemu.adb.secure",
"qemu.cmdline",
"qemu.hw.mainkeys",
"qemu.logcat",
"ro.adb.qemud",
"qemu.sf.fake_camera",
"qemu.sf.lcd_density",
"qemu.timezone",
"init.svc.goldfish-logcat",
"ro.boottime.goldfish-logcat",
"ro.hardware.audio.primary",
"init.svc.ranchu-net",
"init.svc.ranchu-setup",
"ro.boottime.ranchu-net",
"ro.boottime.ranchu-setup",
"init.svc.droid4x",
"init.svc.noxd",
"init.svc.qemud",
"init.svc.goldfish-setup",
"init.svc.goldfish-logcat",
"init.svc.ttVM_x86-setup",
"vmos.browser.home",
"vmos.camera.enable",
"ro.trd_yehuo_searchbox",
"init.svc.microvirtd",
"init.svc.vbox86-setup",
"ro.ndk_translation.version",
"redroid.width",
"redroid.height",
"redroid.fps",
"ro.rf.vmname"
  • 模拟器属性
"init.svc.microvirtd",
"bst.version",
"ro.phoenix.version.code",
"ro.phoenix.version.codename",
"init.svc.droid4x",
"microvirt.memu_version",
"microvirt.imsi",
"microvirt.simserial",
"ro.px.version.build",
"ro.phoenix.os.branch",
"init.svc.su_kpbs_daemon",
"init.svc.noxd",
"init.svc.ttVM_x86-setup",
"init.svc.xxkmsg",
"ro.bild.remixos.version",
"microvirt.mut",
"init.svc.ldinit",
"sys.tencent.os_version",
"sys.tencent.android_id",
"ro.genymotion.version",
"init.svc.pkVM_x86-setup",
"ro.andy.version",
"ro.build.version.release",
"ro.product.model",
"ro.product.brand",
"ro.boot.bootloader",
"ro.build.version.securitypatch",
"ro.build.version.incremental",
"gsm.version.baseband",
"gsm.version.ril-impl",
"ro.build.fingerprint",
"ro.build.description",
"ro.build.product",
"ro.boot.vbmeta.digest",
"ro.hardware",
"ro.product.name",
"ro.product.board",
"ro.recovery_id",
"ro.expect.recovery_id",
"ro.board.platform",
"ro.product.manufacturer",
"ro.product.device",
"sys.usb.state",
"ro.setupwizard.mode",
"ro.build.id",
"ro.build.tags",
"ro.build.type",
"ro.debuggable"

3.系统架构判断

主要是判断系统是x86还是arm,arm64的,通常手机是arm或者arm64架构的,模拟器是x86架构的,这里获取的方式使用seccomp来判断

先安装seccomp架子
void install_check_arch_seccomp() {
    struct sock_filter filter[15] = {
            BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (uint32_t) offsetof(struct seccomp_data, nr)),
            BPF_JUMP(BPF_JMP + BPF_JEQ, __NR_getpid, 0, 12),
            BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (uint32_t) offsetof(struct seccomp_data, args[0])),
            BPF_JUMP(BPF_JMP + BPF_JEQ, DetectX86Flag, 0, 10),
            BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (uint32_t) offsetof(struct seccomp_data, arch)),
            BPF_JUMP(BPF_JMP + BPF_JEQ, AUDIT_ARCH_X86_64, 0, 1),
            BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (864 & SECCOMP_RET_DATA)),
            BPF_JUMP(BPF_JMP + BPF_JEQ, AUDIT_ARCH_I386, 0, 1),
            BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (386 & SECCOMP_RET_DATA)),
            BPF_JUMP(BPF_JMP + BPF_JEQ, AUDIT_ARCH_ARM, 0, 1),
            BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (0xA32 & SECCOMP_RET_DATA)),
            BPF_JUMP(BPF_JMP + BPF_JEQ, AUDIT_ARCH_AARCH64, 0, 1),
            BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (0xA64 & SECCOMP_RET_DATA)),
            BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (6 & SECCOMP_RET_DATA)),
            BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
 
    };
    struct sock_fprog program = {
            .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])),
            .filter = filter
    };
    errno = 0;
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
        LOG(ERROR) << "prctl(PR_SET_NO_NEW_PRIVS) " << strerror(errno);
    }
    errno = 0;
    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program)) {
        LOG(ERROR) << "prctl(PR_SET_SECCOMP) " << strerror(errno);
    }
}

配合上面的代码 。启动调用getpid, 上面的架子会对getpid函数进行拦截 ,然后架构进行判断
string check_arch_by_seccomp() {
    if (get_sdk_level() < __ANDROID_API_N_MR1__){
        return "";
    }
    errno = 0;
    syscall(__NR_getpid, DetectX86Flag);
    if (errno == 386) {
        return "I386设备";
    } else if (errno == 864) {
        return "X86_64设备";
    } else if (errno == 0xA32 || errno == 0xA64) {
        return "";
    }else if (errno == 0) {
        //可能是没有开启seccomp
        return "";
    }
    return ("疑似X86模拟器设备"+ to_string(errno));
}

4.硬件模块

  • 温度模块——可以通过检测cpu温度记录(在/sys/class/thermal/)来判断模拟器,真机存在,模拟器不存在
int thermal_check() {
    DIR *dir_ptr;
    int count = 0;
    struct dirent *entry;
    if ((dir_ptr = opendir("/sys/class/thermal/")) != nullptr) {
        while ((entry = readdir(dir_ptr))) {
            if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
                continue;
            }
            char *tmp = entry->d_name;
            if (strstr(tmp, "thermal_zone") != nullptr) {
                count++;
            }
        }
        closedir(dir_ptr);
    } else {
        count = -1;
    }
    return count;
}
  • 相机相关——摄像头个数,闪光灯是否存在,模拟器通常只有一个摄像头,没有闪关灯
try {
    CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
    String[] cameraIds = manager.getCameraIdList();
    //摄像头个数
    CLog.i("cameraIds -> "+ Arrays.toString(cameraIds));
    if(cameraIds.length < CAMERA_MINIMUM_QUANTITY_LIMIT){
        items.add(
                new ListItemBean(
                "当前手机可能是模拟器&云手机",
                ListItemBean.RiskLeave.Warn,
                "camera size -> "+cameraIds.length
        ));
    }
} catch (Throwable ignored) {
 
}
  • 传感器相关——可以检测传感器数目和种类。和正常的同类型设备相比,模拟器拥有的传感器数目比正常的少许多
try {
    //3,检测传感器类型,支持的全部类型传感器
    SensorManager sm = (SensorManager) context.getSystemService(SENSOR_SERVICE);
    List<Sensor> sensorlist = sm.getSensorList(Sensor.TYPE_ALL);
 
    ArrayList<Integer> sensorTypeS = new ArrayList<>();
    for (Sensor sensor : sensorlist) {
        //获取传感器类型
        int type = sensor.getType();
        if (!sensorTypeS.contains(type)) {
            //发现一种类型则添加一种类型
            sensorTypeS.add(type);
        }
    }
    //小米k40 51个传感器类型
    //普通的pix 27个
    //华为荣耀20 18个传感器
    CLog.e("sensor types size -> " + sensorlist.size());
    //我们认为传感器少于20个则认为是风险设备
    if (sensorlist.size() < SENSOR_MINIMUM_QUANTITY_LIMIT) {
        items.add(new ListItemBean(
                "当前手机可能是模拟器&云手机",
                ListItemBean.RiskLeave.Warn,
                "sensor size -> ("+ sensorlist.size()+") \n" +
                "sensor type size -> ("+sensorTypeS.size()+") \n"
                //+ "sensor info -> \n"+ Sensorlist   //打印全部传感器信息
        ));
    }
传感器名称(有AOSP)——
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    ArrayList<Sensor> aospSensor = new ArrayList<>();
    for(Sensor sensor:sensorlist){
        if(sensor.getVendor().contains("AOSP")){
            aospSensor.add(sensor);
        }
    }
    if (aospSensor.size()>3) {
        CLog.e("传感器参数是否异常(生产厂商为AOSP)");
        items.add(new ListItemBean(
                "当前手机可能是模拟器&云手机",
                ListItemBean.RiskLeave.Warn,
                aospSensor.size()
                        +"/"+sensorlist.size()+"传感器参数异常 -> "+ aospSensor
        ));
    }
}
  • 电池状态——电池电压,电池是否在充电,充电功率等状态
private final BroadcastReceiver batteryInfoReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
 
        // 电池状态
        int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
 
        // 电压(以毫伏为单位)
        int voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);
 
        // 获取电池电流(毫安)
        int currentNow = -1;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            BatteryManager batteryManager = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
            currentNow = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_NOW);
        }
 
        // 判断是否在充电
        if (plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
            // 在充电
            if (voltage != -1 && currentNow != -1) {
                float voltageInVolts = voltage / 1000f; // 将电压转换为伏特
                float currentInAmperes = currentNow / 1000000f; // 将电流转换为安培
                float chargingPower = voltageInVolts * currentInAmperes; // 计算充电功率(瓦特)
                CLog.i(String.format("充电功率: %.2fW", chargingPower));
                if (Math.abs(chargingPower) > 300) {
                    CLog.e("充电功率过高");
                    handlerItemData(new ListItemBean(
                            "电池异常:充电功率过高(可能是云手机)",
                            ListItemBean.RiskLeave.Deadly,
                            "检测到过大的充电功率 -> " + String.format("%.2fW", Math.abs(chargingPower))
                    ));
                }
            }
        }
 
    }
};

环境检测

1.bootloader解锁状态

手机未解锁
ro.boot.verifiedbootstate == green 
ro.boot.flash.locked == 1
ro.boot.vbmeta.device_state == locked
vendor.boot.vbmeta.device_state == locked
ro.secureboot.lockstate == locked
ro.warranty_bit == 0 
ro.boot.warranty_bit == 0
ro.vendor.warranty_bit == 0

2.SIM卡是否存在

        虽然sim相关的许多参数不可以再获取,但还可以根据sim卡是否存在和sim卡上一些国家的信息来实现一些检测

try {
    TelephonyManager telephonyManager =
            (TelephonyManager) DeviceGlobal.getContext().getSystemService(Context.TELEPHONY_SERVICE);
    if (telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY) {
        return "1";
    }
} catch (Throwable ignored) {
}
return "";

gsm_sim_alpha——getprop gsm.sim.operator.alpha
gsm_sim_iso_country——getprop gsm.sim.operator.iso-country
gsm_sim_numeric——getprop gsm.sim.operator.numeric
gsm_operator_alpha——getprop gsm.operator.alpha

3.设备类型

        ro.build.characteristics 是一个Android系统属性,用于描述设备的特征和功能。根据设备类型和用途,此属性可能具有不同的值。常见的值包括:

  • default,nosdcard:默认特征,适用于大多数设备。
  • tablet:平板电脑特征。
  • tv:电视设备特征。
  • car:车载设备特征。
  • watch:智能手表特征。
  • emulator:模拟器特征。
  • auto:汽车设备特征。
  • iot:物联网设备特征。

4.设备是否支持指纹锁以及是否录入指纹

        可以通过context.getPackageManager().hasSystemFeature("android.hardware.fingerprint")获取设备是否支持指纹,可以通过下面的代码来获取设备是否录入指纹

public static String finger_enrolled() {
    try {
        FingerprintManager fingerprintManager =
                (FingerprintManager) DeviceGlobal.getContext().getSystemService(Context.FINGERPRINT_SERVICE);
        if (fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) {
            return "1";
        }
    } catch (Throwable ignored) {
    }
    return "";
}

5.输入法检测

        获取正在使用的输入法是不是官方的输入法,有可能检测到的输入法是一些自动点击相关的输入法。

JSONObject result = new JSONObject();
InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
    List<InputMethodInfo> inputMethodManagerList = inputMethodManager.getInputMethodList();
    if (inputMethodManagerList != null) {
        for (InputMethodInfo inputMethodInfo : inputMethodManagerList) {
            try {
                result.put(inputMethodInfo.getPackageName(), inputMethodInfo.getServiceName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

6.无障碍权限检测

        大部分自动点击软件都会获取无障碍权限来获取app的控件相关信息以及点击信息,可以看无障碍权限是否打开或者使用无障碍权限的app来做辅助判断

//无障碍权限打开
Settings.Secure.getString(context.getContentResolver(),"accessibility_enabled") = 1 ||
Settings.Global.getString(context.getContentResolver(),"accessibility_enabled") = 1 ||
Settings.System.getString(context.getContentResolver(),"accessibility_enabled")

//无障碍权限相关的app
Settings.Secure.getString(context.getContentResolver(),"enabled_accessibility_services") ||
Settings.Global.getString(context.getContentResolver(),"enabled_accessibility_services") ||
Settings.System.getString(context.getContentResolver(),"enabled_accessibility_services") ||
context.getSystemService.getEnabledAccessibilityServiceList 中获取无障碍服务并开启的app

7.vpn和proxy相关

        对app抓包或者有些app会有ip限制,可能需要挂vpn或者使用系统proxy进行代理,下面是对这些进行检测的代码

proxy 获取 —— 
String proxyHost = System.getProperty("http.proxyHost");
String proxyPortStr = System.getProperty("http.proxyPort");

vpn检测 ——
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
https://blog.youkuaiyun.com/u013334392/article/details/102395120
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    Network network = cm.getActiveNetwork();
    if (network != null) {
        NetworkCapabilities caps = cm.getNetworkCapabilities(network);
        if (caps != null && caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
            return "1";
        }
    }
}else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    NetworkInfo networkInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_VPN);
    if (networkInfo != null && networkInfo.isConnectedOrConnecting()) {
        return "2";
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值