Android虚拟机、模拟器识别

来源:互联网 发布:华硕b85gamer声卡软件 编辑:程序博客网 时间:2024/05/20 21:48

源码地址:https://github.com/ysrc/Anti-Emulator (如果你也和我当初一样在so文件调用和生成的相关问题上出现了问题,欢迎留言交流)

在我们开发的App中,我们可能不希望它被运行在模拟器上,所以我们需要一种手段去检测模拟器,当当前设备被检测为模拟器时,我们就直接结束掉App进程。目前常见的检测模拟器手段主要被应用在游戏领域和加固领域。

通常我们去检测模拟器时会利用一些Android系统的运行特征,但这些方式比较复杂也比较难以理解,需要对Android系统有比较深入的了解,如何有一种办法比较高容错率并且检测效果还不错呢,之前,一般的检测模拟器的手段都是通过检测一些系统特定属性,如检测当前设备手机号,设备DeviceId,dev下是否存在socket/qemud和qemu_pipe两个文件,以及build.prop下的一些属性,在分析某赚软件时,它正是使用的这种检测方式,但是当当前设备被root后,就可以通过安装一些修改设备信息的软件来躲避这种检测方式,最近在看到某款游戏的检测手段时,看到了基于文件特征的检测方式,在这个基础上我又加了一些检测方式用来判断模拟器。

目前市面上流行的Android模拟器主要有Genymotion,天天模拟器,夜神模拟器,海马玩模拟器,畅玩模拟器,itools模拟器,逍遥模拟器,文卓爷模拟器,原生Android模拟器,BlueStacks,我们都知道除了原生模拟器之外,大部分Android模拟器都是基于VirtualBox的,这是重要的检测点之一,其次相比于手机,模拟器上少了一些重要特征,如蓝牙功能,温度传感器等等,这些都是可以用来检测模拟器的依据,同时,每种定制版本的模拟器上,都会有一些它特有的可执行文件,如下是我在测试多种模拟器时收集的特征文件:

if (anti("/system/lib/libdroid4x.so")) {   //文卓爷
}
if (anti("/system/bin/windroyed")) {   //文卓爷
}
if (anti("/system/bin/microvirtd")) {  //逍遥
}
if (anti("/system/bin/nox-prop")) {  //夜神
}
if (anti("/system/bin/ttVM-prop")) { //天天模拟器
}

如上图,每个模拟器都编译了自己的动态库或是可执行文件,这些文件在手机上是不存在的,那么我们就可以以此来判断当前设备是否是模拟器,如何去检测该模拟器是否存在也很简单,anti函数如下:

int anti(char *res) {
   struct stat buf;
   
int result = stat(res, &buf) == 0 ? 1 : 0;
   
if (result) {
       LOGE("the %s is exist", res);
       
//    LOGE("this is a Emulator!!!");
   
}
   return result;
}

C提供了一个stat函数用来判断当前文件是否存在,如果执行成功则会返回0,使用这种检测手段就需要去收集大量的模拟器特征,在测试时,本人也碰到了一些问题,如检测这个文件时:

anti("/system/lib/libc_malloc_debug_qemu.so");
//cm,魔趣等基于aosp改版的系统上会存在libc_malloc_debug_qemu.so这个文件

我发现在一些debug版本的定制版系统上都存在这个文件,但测试了大量官方rom时都是没有问题的,所以这种方式的可靠度是不够高的,本人在这个判断的基础上再去判断了一次,如下:

if (anti("/system/lib/libc_malloc_debug_qemu.so")) {
   //cm,魔趣等基于aosp改版的系统上会存在libc_malloc_debug_qemu.so这个文件
   
if (access("/system/lib/libbluetooth_jni.so", F_OK) != 0) {
       LOGE("the bluetooth is not exist");
       
i++;//在误报情况下,再去检测当前设备是否存在蓝牙,不存在则判断为模拟器
   
}
}

目前市面上手机都是拥有蓝牙功能的,设备用于蓝牙功能时,我们发现系统的system/lib下有一个libbluetooth_jni.so文件,而这个文件在模拟器上是不存在的。

此类类似的特征文件还有一些,这里就不一个个列举了,除了检测这些固定文件外,我们还可以去检测一些模拟器特定的系统属性,在adb shell下输入getprop即可看到大量系统属性,所以这里就是通过一定系统属性去检测当前设备,如init.svc.vbox86-setup(基于VirtualBox的模拟器都会存在该属性):


通过一些测试我们可以过滤出一些比较敏感的信息,这些属性我们都可以用作检测模拟器的手段,在so里去获取这些属性也很简单,系统为我们提供了现成的api函数:

int anti2(char *res) {
   char buff[PROP_VALUE_MAX];
   
memset(buff, 0, PROP_VALUE_MAX);
   
int result =
           __system_property_get(res, (char *) &buff) > 0 ? 1 : 0; //返回命令行内容的长度
   
if (result != 0) {
       LOGE("the %s result is %s", res, buff);
       
//  LOGE("this is a Emulator!!!");
   
}
   return result;
}

通过__system_property_get我们就可以拿到该属性对应的值,值会保存在buff中。

当然了常规通过build.prop的检测方式也未尝一无是处,在检测模拟器时可以多维度去检测,在build.prop中一些字段也是比较重要的,如ro.product.name获取当前设备名称:

char *model = getDeviceInfo("ro.product.name");
if (!strcmp(model, "ChangWan")) {
} else if (!strcmp(model, "Droid4X")) {                     //0均为模拟器
} else if (!strcmp(model, "lgshouyou")) {
} else if (!strcmp(model, "nox")) {
} else if (!strcmp(model, "ttVM_Hdragon")) {
}

通过和一些常见模拟器设备名称进行对比,但是目前模拟器都可以手动去修改imei,deviceID,以及设备信息,这种检测方式效果不大。

本人测试时还发现,当试图去检测一些特殊信息,如当前设备cpu温度时,搜集了一些资料,提供了一个adb方式去获取,/sys/class/thermal/thermal_zoneX/temp(其中X是核心数量),但发现模拟器上没有thermal_zoneX目录,只有两个cooling_deviceX目录,因而有了一个新的判断依据,如下:

int checkTemp() {
   DIR *dirptr = NULL; //当前手机的温度检测,手机下均有thermal_zone文件
   
int i = 0;
   
struct dirent *entry;
   
if ((dirptr = opendir("/sys/class/thermal/")) != NULL) {
       while (entry = readdir(dirptr)) {
           // LOGE("%s  \n", entry->d_name);
           
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
               continue;
           
}
           char *tmp = entry->d_name;
           
if (strstr(tmp, "thermal_zone") != NULL) {
               i++;
           
}
       }
       closedir(dirptr);
   
} else {
       LOGE("open thermal fail");
   
}
   return i;
}

使用opendir函数去访问thermal目录,遍历该目录下所有目录,当不存在thermal_zone目录时,则判断当前设备为模拟器,最后放上几张模拟器测试图。

海马玩:

逍遥模拟器:

真机Nexus 5:

真机乐视2:

以上只是提供了一些检测模拟器的思路,真正的使用还需要大量的进行测试,提高检测的准确性和稳定性。

原创粉丝点击