百趣云 百趣云的博客

模拟器检测对抗:从 build.prop 到传感器伪装

检测与对抗的猫鼠游戏

用模拟器跑 App 做协议分析或自动化,第一步就得过模拟器检测。检测方的思路是"找模拟器和真机的差异点",对抗方的思路是"消除这些差异"。先摸清对手检测什么,再逐项伪装。

检测点全景与逐项对抗

1. build.prop 硬件指纹(最基础的检测)

模拟器的 build 信息一眼假:

ro.product.model= sdk_gphone64_arm64   ← 模拟器特征
ro.hardware = ranchu / goldfish        ← 模拟器虚拟硬件
ro.build.fingerprint = google/sdk_gphone...

对抗:改机工具或直接改 prop(需要 root):

# Magisk 的 resetprop 命令
resetprop ro.product.model "SM-G998B"
resetprop ro.product.brand "samsung"
resetprop ro.hardware "qcom"
resetprop ro.build.fingerprint "samsung/p3sxxx/p3s:13/TP1A.220624.014/G998BXXU5EWA1:user/release-keys"

注意成套修改:型号、品牌、硬件平台、指纹串必须来自同一款真实机型,混搭会触发一致性校验。从真实设备的 getprop 输出里整套复制最稳。

2. 文件特征检测

检测代码会探测模拟器特有文件:

/dev/socket/qemud          ← QEMU 模拟器
/dev/goldfish_pipe         ← goldfish 虚拟设备
/system/lib/libc_malloc_debug_qemu.so

对抗:Xposed/Frida hook File.exists / access 系统调用,对这些路径返回"不存在":

var File = Java.use('java.io.File');
File.exists.implementation = function() {
    var path = this.getAbsolutePath();
    var fake = ['/dev/socket/qemud', '/dev/goldfish_pipe', 'qemu'];
    for (var i = 0; i < fake.length; i++) {
        if (path.indexOf(fake[i]) >= 0) return false;
    }
    return this.exists();
};

3. 传感器检测(高阶检测)

真机有加速度计、陀螺仪、光线传感器且数据持续微动;模拟器要么没有传感器,要么数据恒为 0。检测代码注册传感器监听,读不到数据或数据无波动就判定模拟器。

对抗:Frida hook SensorManager.registerListener,伪造传感器数据回调:

var SensorEventListener = Java.use('android.hardware.SensorEventListener');
// 拦截注册,主动注入模拟数据
setInterval(function() {
    // 构造带噪声的重力传感器数据:9.8 附近微抖动
    var x = 0.1 + Math.random() * 0.05;
    var y = 0.2 + Math.random() * 0.05;
    var z = 9.78 + Math.random() * 0.04;
    // 通过反射调用 listener.onSensorChanged(...)
}, 100);

4. 电池/温度/运营商等环境项

模拟器电量常固定、无 SIM 卡信息、无基站信息。getSimOperatorgetNetworkOperator 返回空是强特征。对抗同样是 hook 这些 TelephonyManager 方法返回真实值。

检测代码长什么样

了解对手才能赢。典型的综合检测实现:

public boolean isEmulator(Context ctx) {
    int score = 0;
    if (Build.HARDWARE.contains("goldfish") || Build.HARDWARE.contains("ranchu")) score += 3;
    if (Build.MODEL.contains("sdk") || Build.MODEL.contains("Emulator")) score += 3;
    if (new File("/dev/socket/qemud").exists()) score += 5;
    if (getSensorCount(ctx) < 5) score += 2;
    if (TextUtils.isEmpty(getSimOperator(ctx))) score += 2;
    return score >= 5;   // 打分制,不是单点判定
}

注意是打分制——这意味着你不需要伪装所有点,把高分项(文件特征、硬件指纹)消掉,低分项(传感器)蒙混,总分不过线就能过。

精髓:对抗的三个层次

  1. 优先用真机云手机,其次改模拟器。红手指、雷电云手机这类是真机集群,天然免疫模拟器检测,成本允许时是最优解
  2. 伪装要成套。散装的伪装(改了型号没改指纹)比不伪装更可疑——一致性校验专抓这种
  3. 先逆向检测逻辑再动手。用 jadx 搜 isEmulatorcheckEmugoldfish 等关键词,把 App 的检测点列成清单,逐条对抗,别盲改

总结

  1. 检测四板斧:build 指纹、文件特征、传感器、运营商信息
  2. 打分制检测意味着消掉高分项即可过线
  3. 伪装信息必须成套(同机型全套 prop)
  4. 先逆向检测代码列清单,再逐项对抗

交流微信:run1255

By 百趣云 阅读量:2 On