24、模拟器检测绕过:Build特征检测、传感器检测、Frida Hook绕过
模拟器检测,这几乎是每个Android逆向工程师都会遇到的坎。说实话,我早年刚入行时,第一次碰到这玩意儿还挺懵的——明明代码逻辑都分析清楚了,一跑就闪退,后来才发现是模拟器检测在作祟。今天咱们就把这块硬骨头啃下来。
模拟器检测说白了,就是App在判断自己是不是跑在真机上。如果是模拟器,它就拒绝执行核心逻辑,或者直接退出。常见的检测手段分三类:Build特征、传感器特征,还有针对Frida的Hook检测。咱们一个一个来拆解。
24.1 Build特征检测与绕过
这是最基础也是最常见的检测方式。App会读取系统属性,比如ro.build.fingerprint、ro.product.model、ro.hardware等。模拟器通常会有一些固定的特征值,比如generic、sdk_phone、Android SDK built for x86。
我个人习惯先拿一个模拟器跑一下,看看这些值长什么样。比如在Genymotion上,ro.product.manufacturer往往是unknown,而真机通常是Xiaomi、samsung之类的。
核心思路:找到检测点,然后Hook掉对应的系统调用,返回伪造的真机值。
举个例子,App可能会这样检测:
String model = Build.MODEL;
if (model.contains("sdk") || model.contains("generic")) {
// 检测到模拟器,退出
System.exit(0);
}
绕过方法很简单,用Frida Hook掉Build类:
Java.perform(function() {
var Build = Java.use("android.os.Build");
Build.MODEL.value = "Pixel 6";
Build.MANUFACTURER.value = "Google";
Build.FINGERPRINT.value = "google/raven/raven:13/TQ1A.230205.002/12345678:user/release-keys";
Build.HARDWARE.value = "raviole";
Build.PRODUCT.value = "raven";
Build.DEVICE.value = "raven";
Build.BOARD.value = "raviole";
Build.BRAND.value = "google";
});
嗯,这里要注意一点:有些App会通过反射读取Build类的字段,或者直接读取系统属性文件/system/build.prop。对于后者,我们还需要Hook SystemProperties.get()方法。
避坑指南:我曾经遇到过一个App,它不光检查Build特征,还检查/proc/version里的内核信息。模拟器的内核版本通常带有generic字样,真机则是具体的内核版本号。所以记得把内核信息也一并伪造了。
24.2 传感器检测与绕过
这个就有点意思了。模拟器通常没有真实的物理传感器,或者传感器返回的数据是固定的、不真实的。App会检查传感器列表,或者读取传感器数据的变化模式。
为什么会这样?因为真机上的传感器数据是连续变化的,比如加速度计、陀螺仪。而模拟器要么没有这些传感器,要么返回的数据是恒定值。
常见的检测点包括:
- 传感器列表检查:检查是否存在
TYPE_ACCELEROMETER、TYPE_GYROSCOPE等传感器 - 传感器数据变化:连续读取几次,看数值是否变化
- 传感器精度:真机的传感器精度通常较高,模拟器可能返回0或固定值
绕过方法也不复杂。我们可以Hook掉SensorManager的相关方法:
Java.perform(function() {
var SensorManager = Java.use("android.hardware.SensorManager");
var Sensor = Java.use("android.hardware.Sensor");
// Hook getDefaultSensor,返回一个伪造的传感器
SensorManager.getDefaultSensor.overload('int').implementation = function(type) {
var result = this.getDefaultSensor(type);
if (result == null) {
// 如果模拟器没有这个传感器,我们伪造一个
console.log("伪造传感器: " + type);
// 这里可以返回一个自定义的Sensor对象
}
return result;
};
// Hook传感器事件监听器,伪造数据变化
var SensorEventListener = Java.use("android.hardware.SensorEventListener");
SensorEventListener.onSensorChanged.implementation = function(event) {
// 修改事件数据,模拟真实传感器变化
event.values[0] = Math.random() * 10; // 伪造加速度计X轴
event.values[1] = Math.random() * 10; // 伪造Y轴
event.values[2] = Math.random() * 10; // 伪造Z轴
this.onSensorChanged(event);
};
});
不过说实话,传感器检测这块水挺深的。我记得有一次,一个金融类App不光检查传感器是否存在,还检查传感器数据的统计特征——比如方差、标准差。模拟器的数据太规律了,方差很小,真机的数据方差大得多。这种情况下,光伪造数值还不够,还得让数据看起来"自然"。
警告:有些App会同时检查多个传感器,并且要求传感器数据之间有一定的物理关联性。比如加速度计和陀螺仪的数据应该符合运动学规律。如果只伪造其中一个,另一个没动,很容易露馅。
24.3 Frida Hook检测与绕过
这个就比较高级了。App会检测Frida的运行痕迹,比如检查端口、检查进程名、检查Dex加载等。说白了,就是App在反制我们的Hook工具。
常见的Frida检测手段:
| 检测方式 | 原理 | 绕过方法 |
|---|---|---|
| 端口检测 | 检查27042端口是否被占用 | 修改Frida默认端口 |
| 进程名检测 | 检查/proc/self/status或/proc/self/cmdline | 使用Frida的spawn模式,或修改进程名 |
| Dex加载检测 | 检查DexClassLoader中是否有frida相关的类 | 使用Frida的Gadget模式,或编译自定义Frida |
| 线程名检测 | 检查线程名是否包含"frida"、"gmain"等 | 使用Frida的隐藏模式 |
| 文件检测 | 检查/data/local/tmp/下是否有frida-server | 重命名frida-server,或使用Gadget模式 |
我个人最常用的绕过方式是使用Frida的Gadget模式。把frida-gadget.so注入到App的lib目录下,然后通过配置文件控制。这样就没有独立的frida-server进程了,端口检测和进程名检测直接失效。
配置示例:
// libfrida-gadget.config.so
{
"interaction": {
"type": "listen",
"address": "127.0.0.1:27042"
},
"reconnect": true,
"timeout": 5000
}
然后启动App时,Frida Gadget会自动加载,我们再用frida -H 127.0.0.1:27042连接即可。
核心要点:Frida检测绕过的本质是让App找不到Frida的痕迹。要么隐藏Frida,要么让Frida看起来像App的一部分。
还有一种更狠的检测方式——检查maps文件。App会读取/proc/self/maps,看看有没有加载frida-agent.so或者frida-gadget.so。对于这种情况,我建议使用Frida的objc模式或者stalker模式,但更直接的办法是编译一个自定义的Frida,把所有的特征字符串都改掉。
嗯,这里要提醒一下:有些App会做多层次的检测。比如先检测端口,再检测进程名,再检测maps文件。如果只绕过了一层,后面还有坑等着你。所以我的习惯是,先静态分析App的so文件,把所有检测点都找出来,然后一次性全部Hook掉。
24.4 综合绕过策略
说了这么多,咱们来总结一下。模拟器检测绕过不是单一技巧,而是一套组合拳。我个人建议按以下步骤来:
- 静态分析:先反编译App,搜索关键词如"emulator"、"simulator"、"generic"、"sdk"等,找到所有检测点
- 动态调试:用Frida跟踪系统调用,看App到底调用了哪些API
- 分层绕过:先绕过Build特征,再绕过传感器,最后处理Frida检测
- 验证:绕过之后,确保App的核心功能正常运行,不要出现奇怪的崩溃
下面这张图展示了模拟器检测与绕过的整体流程:
最后说一句,模拟器检测绕过是个动态博弈的过程。App开发者会不断更新检测手段,我们也要不断更新绕过技巧。但万变不离其宗——只要理解了检测的原理,绕过就只是时间问题。
个人建议:平时多收集一些App的检测样本,建立自己的检测库。我每次遇到新的检测方式,都会记录下来,久而久之就成了一个很实用的参考手册。
公众号:蓝海资料掘金营,微信deep3321