概述

Android 环境检测,。

  • root 检测
  • 模拟器检测
  • Hook 检测
  • 调试检测

这个项目本质上是一个 Android 设备环境检测样本,目标不是单一 root 检测,而是构建一个多层、多信号源、可交叉验证的本地风控探针集合。整体设计思路接近移动安全 SDK:

  • Java 层负责高层 API、PackageManager、Settings、Build、ConnectivityManager、Keystore 等框架信号。
  • Native 层负责 libc 视角的文件、属性、内存、socket、maps、ELF 完整性检查。
  • Syscall 层负责绕过 Java Hook 和部分 libc inline hook,直接读取 /proc/sys/dev/__properties__/proc/net/unix 等低层数据。
  • UI 层把检测拆成 ROOT / HOOK / EMULATOR / DEBUG / NETWORK / SIDE_CHANNEL / READLINK / ZYGOTE / TAMPER / BOOTLOADER 十类,并额外采集一套设备指纹。

这不是“纯展示型 Demo”。其中有几块实现已经进入实战检测范式:

  • /dev/__properties__ 二进制 mmap 直读属性区
  • /proc/net/unix + task thread name 的 JDWP attach 检测
  • /proc/self/maps / smaps / 匿名 RWX 内存取证
  • 磁盘 ELF .text 与内存 .text 对比
  • ARM64 trampoline 模式识别
  • /proc/cmdline 与属性系统交叉验证,识别 resetprop
  • Key Attestation 读取 TEE 侧 Bootloader 状态

三层检测模型:

项目大量检测项都按三层实现:

  1. JAVA
    • 系统 API、反射、Java IO、PackageManager、Settings、Build、ConnectivityManager
  2. NATIVE
    • access/fopen/opendir/getifaddrs/socket/__system_property_get
  3. SYSCALL
    • 直接 openat/read/getdents64/readlinkat/statfs/uname/netlink

其设计目标是区分两类问题:

  • 真命中:设备确实存在 root / hook / debug / tamper 特征
  • 伪装/屏蔽:上层 API 被 Hook,但底层仍能读到真实数据

Root检测

绕过检测:利用 Xposed 模块(如 RootCloak)修改 Build.TAGSSELinux 状态,将 test-keys 强行改为特定指纹伪装。

Root 检测主要是检测是否存在su文件,以及关于常用的

su检测

检测典型路径:

  • /system/bin/su
  • /system/xbin/su
  • /sbin/su
  • /vendor/bin/su
  • /su/bin/su
  • 以及 busybox

三层分别通过:

  • Java File.exists
  • Native access
  • Syscall openat/stat
public static boolean CheckSu() {
    private static final String[] SU_SEARCH_PATHS = {
        "/system/bin/",
        "/system/xbin/", 
        "/system/sbin/",
        "/sbin/",
        "/vendor/bin/",
        "/data/local/xbin/",
        "/data/local/bin/",
        "/system/bin/.ext/",
        "/su/bin/"  // 某些系统的su路径
    };
    
    private static final String[] SU_NAMES = {
        "su",
        "su2",      // 隐藏的su
        "sus",      // 变体名称
        "daemonsu"  // SuperSU守护进程
    };
    
    try {
        for (String path : SU_SEARCH_PATHS) {
            for (String suName : SU_NAMES) {
                File suFile = new File(path, suName);
                if (suFile.exists() && suFile.canExecute()) {
                    Log.i(LOG_TAG, "Found su binary: " + suFile.getAbsolutePath());
                    return true;
                }
            }
        }
    } catch (SecurityException e) {
        Log.e(LOG_TAG, "Security exception while checking su", e);
        // 安全异常通常意味着检测受限
        return true; // 保守处理,认为可能已root
    } catch (Exception e) {
        Log.e(LOG_TAG, "Unexpected error checking su", e);
    }
    
    return false;
}

框架检测

Magisk 特征:

  • 包名:com.topjohnwu.magisk
  • 文件:
    • /sbin/.magisk
    • /data/adb/magisk
    • /data/adb/modules
    • /dev/.magisk.unblock

Apatch 特征:

  • 包名:me.bmax.apatch
  • 文件:
    • /data/adb/ap
    • /data/adb/apd

KernelSU 特征:

  • 包名:me.weishu.kernelsu
  • 文件:
    • /data/adb/ksu
    • /data/adb/ksud
    • /data/adb/ksu/bin/resetprop

Sukisu 特征:

  • 包名:

    • org.sukisu.manager
    • com.sukisu.ultra
  • 文件:

    • /data/adb/sukisu
    • 附加检测 /proc/filesystems 中的 susfs
public static boolean CheckRootFrame() {
    private static final String[] PKGS = {
        "com.topjohnwu.magisk",
        "com.sukisu.ultra",
        "me.bmax.apatch", 
        "me.weishu.kernelsu",
        "org.sukisu.manager"
    };
    
    for (String pkg : PKGS) {
        if (isPackageInstalled(pkg)) {
            return true;
        }
    }
    return false;
}

private static boolean isPackageInstalled(String packageName) {
    try {
        return Class.forName("android.app.AppGlobals")
            .getMethod("getPackageManager")
            .invoke(null)
            .getClass()
            .getMethod("getPackageInfo", String.class, int.class)
            .invoke(null, packageName, 0) != null;
    } catch (Exception e) {
        return false;
    }
}
// 快速检测 root 的关键特征
public static boolean checkBUILD() {
    
    // 2. 检测常见 root 文件
    String[] rootFiles = {
        "/system/app/Superuser.apk",
        "/system/app/SuperSU.apk",
        "/data/data/com.noshufou.android.su",
    };
    
    for (String file : rootFiles) {
        if (new File(file).exists()) {
            return true;
        }
    }
    

    
    return false;
}

属性检测

包名指纹

Root Manager 包名包括:

  • com.topjohnwu.magisk
  • me.weishu.kernelsu
  • me.bmax.apatch
  • org.sukisu.manager
  • 以及历史 SuperSU/KingRoot/KingoRoot/phh

Root隐藏模块

  • shamiko
  • zygisk-assistant
  • playintegrityfix
  • zygiskNext
  • rezygisk
  • lsposed

可疑挂载检测

重点读取:

  • /proc/self/mountinfo
  • /proc/self/mounts

关注特征:

  • magisk
  • zygisk
  • kernelsu
  • apatch
  • /data/adb/modules
  • 重复 cacerts 挂载

它试图识别:

  • 模块 overlay
  • root 文件隐藏
  • 证书注入

模拟器检测

  • 厂商/型号伪装:篡改 ro.product.manufacturerro.product.model,通常隐蔽地伪装成主流设备(如 Samsung、Xiaomi 或 Google Pixel)。
  • 传感器注入:针对真机必须具备的加速度传感器、陀螺仪等物理硬件,模拟器默认是缺失的。攻击者会利用工具注入伪造的传感器数据流。
  • 硬件特征重置:伪造 IMEI、IMSI 及 MAC 地址,避免被指纹算法识别为固定虚拟设备。
  • CPU 指令集:通过强制启用 ARM 转译层(如 Waydroid)或修改 ro.product.cpu.abi 来隐藏 x86 的模拟器特征。

目前流行的 Android 模拟器大概分为两种,一种是基于 Qemu,另一类是基于 Genymotion(VirtualBox 类),网上现在流行用一些模拟器特征进行鉴别,比如:

模拟器指纹

Build 特征:

  • goldfish
  • ranchu
  • generic
  • vbox86
  • nox
  • genymotion
  • Android SDK built for x86

Native 层还会检查:

  • QEMU 相关文件
  • 模拟器文件系统痕迹

docker云手机:

  • ro.boot.redroid_\*:这是最核心的检测点。这类属性通常在 Redroid 容器启动时由内核 cmdline 设置,用于传递配置参数。例如,当应用读取 ro.boot.redroid_widthro.boot.redroid_heightro.boot.redroid_fps 时,若是 Redroid 环境通常会返回非空值,而真机环境通常没有这些属性。
  • ro.kernel.redroid_\*:部分 Redroid 版本或定制内核可能会保留这类只读属性,以标识其运行在容器化内核之上。
  • init.svc.redroid_net:这是 Redroid 特有的网络服务初始化状态。Redroid 通常通过自定义服务管理网络桥接或配置,在真机或标准模拟器中通常不存在该服务。

文件检测

检查特定的 QEMU/KVM 遗留文件(如 /dev/qemu_pipe, /system/bin/qemu-props)或非通用驱动文件。若这些文件存在,极大概率是模拟器。

// 通过特征文件检测

private static final String[] EMU_PROPS =
  {"goldfish","ranchu","generic","vbox86","nox","genymotion","sdk","google_sdk","Emulator","Android SDK built for
  x86"};
          private static final String[] EMU_PKGS =
  {"com.bignox.app.store.hd","com.microvirt.guide","com.bluestacks.settings","com.microvirt.market","com.microvir
  t.launcher"};
      
private static boolean isEmulatorByFiles() {
    String[] suspiciousPaths = {
            "/init.goldfish.rc",           // Android模拟器特征文件
            "/dev/socket/qemud",            // QEMU模拟器
            "/dev/qemu_pipe",               // QEMU管道
            "/system/bin/qemu-props",       // QEMU属性
            "/proc/tty/drivers",            // 可检查goldfish驱动
            "/system/etc/init.goldfish.sh"  // Goldfish脚本
    };
    
    for (String path : suspiciousPaths) {
        File file = new File(path);
        if (file.exists()) {
            return true;
        }
    }

属性检测

属性特征:

检查系统属性(如 ro.hardwarero.product.model 等)是否包含这些字符串。

"goldfish", "ranchu", "generic", "vbox86", "nox", "genymotion", 
"sdk", "google_sdk", "Emulator", "Android SDK built for x86"

常见模拟器相关应用包名

"com.bignox.app.store.hd"      // 夜神模拟器市场
"com.microvirt.guide"           // 微讯(逍遥模拟器)
"com.bluestacks.settings"       // BlueStacks 设置
"com.microvirt.market"          // 逍遥市场
"com.microvirt.launcher"        // 逍遥桌面
"com.microvirt.installe"        // 逍遥安装器
"com.bluestacks.appmart"        // BlueStacks 应用市场
"com.bluestacks.home"           // BlueStacks 主页
"com.bignox.google.installer"   // 夜神安装器
 
检测关键词对应模拟器典型文件路径
libnoxd.so夜神模拟器 (Nox)/system/lib/libnoxd.so
noxd夜神模拟器 (Nox)相关进程或文件路径
microvirt逍遥模拟器 (Microvirt)/system/lib/libmicrovirt.so
bluestacks蓝叠模拟器 (BlueStacks)/data/app/com.bluestacks.*.apk
droid4x海马玩模拟器 (Droid4X)/data/data/com.droid4x.

REDROID_PROPERTIES– Redroid(容器化安卓,常用于云手机)特征

"ro.boot.redroid_height", "ro.boot.redroid_width", ... 
"ro.kernel.redroid.width", "ro.kernel.redroid.fps", "init.svc.redroid_net"
    
    private static final String[] EMULATOR_PROPERTIES = {"goldfish", "ranchu", "generic", "vbox86", "nox", "genymotion", "sdk", "google_sdk", "Emulator", "Android SDK built for x86"};
    private static final String[] EMULATOR_PACKAGES = {"com.bignox.app.store.hd", "com.microvirt.guide", "com.bluestacks.settings", "com.microvirt.market", "com.microvirt.launcher", "com.microvirt.installe", "com.bluestacks.appmart", "com.bluestacks.home", "com.bignox.google.installer"};
    private static final String[] REDROID_PROPERTIES = {"ro.boot.redroid_height", "ro.boot.redroid_width", "ro.boot.redroid_wifi", "ro.boot.redroid_wifi_gateway", "ro.boot.redroid_net_dns1", "ro.boot.redroid_net_ndns", "ro.kernel.redroid.width", "ro.kernel.redroid.height", "ro.kernel.redroid.fps", "init.svc.redroid_net"};
    private static final String[] VBOX_PATHS = {"/sys/module/vboxguest", "/sys/module/vboxvideo", "/sys/module/vboxsf", "/sys/class/misc/vboxuser", "/sys/class/misc/vboxguest", "/sys/class/bdi/vboxsf-c", "/sys/devices/virtual/misc/vboxuser", "/sys/devices/virtual/misc/vboxguest", "/sys/devices/virtual/bdi/vboxsf-c", "/sys/bus/pci/drivers/vboxguest", "/dev/vboxuser", "/dev/vboxguest", "/init.vbox86.rc", "/system/lib/hw/gralloc.vbox86.so", "/system/lib/hw/sensors.vbox86.so", "/system/lib/hw/audio.primary.vbox86.so", "/system/lib/hw/gps.vbox86.so", "/system/lib/vboxguest.ko", "/system/lib/vboxvideo.ko", "/system/lib/vboxsf.ko", "/system/bin/mount.vboxsf", "/system/xbin/mount.vboxsf", "/system/bin/androVM-vbox-sf"};
    private static final String[] NOX_PATHS = {"/system/lib/libnoxd.so", "/system/lib/libnoxspeedup.so", "/system/bin/nox-prop", "/system/bin/noxd", "/system/bin/shellnox", "/system/bin/noxscreen", "/system/bin/noxspeedup", "/system/bin/enable_nox", "/data/property/persist.nox.simulator_version", "/data/misc/profiles/ref/com.bignox.app.store.hd", "/data/misc/profiles/ref/com.bignox.google.installer"};
    private static final String[] ROCKCHIP_PATHS = {"/sys/bus/platform/driver/rockchip-system-monitor", "/sys/bus/platform/driver/rockchip-nocp", "/sys/bus/platform/driver/rockchip-pm", "/sys/bus/platform/driver/rockchip-pcie", "/sys/bus/platform/driver/rockchip-pinctrl", "/sys/bus/platform/driver/dwmmc_rockchip", "/sys/bus/nvmem/deivces/rockchip-otp0", "/sys/devices/platform/rockchip-system-monitor", "/sys/module/nvmem_rockchip_otp", "/sys/module/nvmem_rockchip_otp/parameters/rockchip_otp_wr_magic", "/sys/module/rockchip_pwm_remotectl", "/sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl", "/proc/irq/69/rockchip_thermal", "/proc/irq/19/rockchip_usb2phy", "/vendor/etc/init/hw/init.rockchip.rc", "/vendor/etc/init/init.rockchip.drmservice.rc", "/vendor/bin/rockchip.drmservice"};
    private static final String[] OTHER_EMULATOR_PATHS = {"/system/bin/droid4x", "/system/bin/droid4x-prop", "/system/lib/libdroid4x.so", "/system/lib/libmicrovirt.so", "/system/bin/microvirt-prop", "/data/data/com.microvirt.market", "/data/data/com.microvirt.launcher", "/data/data/com.microvirt.installe", "/data/data/com.bluestacks.appmart", "/data/data/com.bluestacks.home", "/data/bluestacks.prop", "/data/.bluestacks.prop", "/system/bin/ttVM-vbox-sf"};
public static boolean checkBUILD() {
    // 1. Build.FINGERPRINT 中含有 emulator、generic、vbox86p 等标识
    String fingerprint = android.os.Build.FINGERPRINT;
    if (fingerprint != null) {
        String lowerFingerprint = fingerprint.toLowerCase();
        if (lowerFingerprint.contains("emulator") || 
            lowerFingerprint.contains("generic") || 
            lowerFingerprint.contains("vbox86p") ||
            lowerFingerprint.contains("test-keys")) {
            return true;
        }
    }
    
    // 2. 检测 ro.build.tags 是否是 sdk 或包含 test-keys
    String buildTags = android.os.Build.TAGS;
    if (buildTags != null) {
        String lowerBuildTags = buildTags.toLowerCase();
        if (lowerBuildTags.contains("test-keys") || 
            lowerBuildTags.equals("sdk") ||
            lowerBuildTags.contains("sdk")) {
            return true;
        }
    }
    
    // 3. 检测 ro.hardware 是否是 ranchu 或其他模拟器特征
    String hardware = android.os.Build.HARDWARE;
    if (hardware != null) {
        String lowerHardware = hardware.toLowerCase();
        if (lowerHardware.equals("ranchu") || 
            lowerHardware.equals("goldfish") || 
            lowerHardware.contains("qemu") ||
            lowerHardware.equals("vbox86") ||
            lowerHardware.contains("virtual")) {
            return true;
        }
    }
    
    // 4. 额外的 Build 属性检测(增强模拟器识别)
    String product = android.os.Build.PRODUCT;
    if (product != null) {
        String lowerProduct = product.toLowerCase();
        if (lowerProduct.equals("sdk") || 
            lowerProduct.equals("sdk_x86") || 
            lowerProduct.equals("sdk_google_phone_x86") || 
            lowerProduct.equals("generic") || 
            lowerProduct.equals("google_sdk") ||
            lowerProduct.contains("emulator") ||
            lowerProduct.contains("sdk_gphone")) {
            return true;
        }
    }
    
    String board = android.os.Build.BOARD;
    if (board != null) {
        String lowerBoard = board.toLowerCase();
        if (lowerBoard.equals("unknown") || 
            lowerBoard.contains("goldfish") || 
            lowerBoard.contains("ranchu")) {
            return true;
        }
    }
    
    String device = android.os.Build.DEVICE;
    if (device != null) {
        String lowerDevice = device.toLowerCase();
        if (lowerDevice.contains("generic") || 
            lowerDevice.contains("emulator") ||
            lowerDevice.contains("goldfish") ||
            lowerDevice.equals("vbox86p")) {
            return true;
        }
    }
    
    String brand = android.os.Build.BRAND;
    String model = android.os.Build.MODEL;
    if (brand != null && model != null) {
        String lowerBrand = brand.toLowerCase();
        String lowerModel = model.toLowerCase();
        if ((lowerBrand.equals("generic") && lowerModel.startsWith("android sdk")) ||
            (lowerBrand.contains("google") && lowerModel.contains("sdk")) ||
            (lowerBrand.equals("generic") && lowerModel.contains("google"))) {
            return true;
        }
    }
    
    String manufacturer = android.os.Build.MANUFACTURER;
    if (manufacturer != null) {
        String lowerManufacturer = manufacturer.toLowerCase();
        if (lowerManufacturer.equals("unknown") || 
            lowerManufacturer.contains("genymotion") ||
            lowerManufacturer.equals("vbox86")) {
            return true;
        }
    }
    
    String host = android.os.Build.HOST;
    if (host != null) {
        String lowerHost = host.toLowerCase();
        if (lowerHost.contains("build") || 
            lowerHost.contains("test") ||
            lowerHost.contains("emulator")) {
            return true;
        }
    }
    
    String serial = android.os.Build.SERIAL;
    if (serial != null && serial.equalsIgnoreCase("unknown")) {
        return true;
    }
    
    return false;
}

硬件检测

  • 传感器数量过少

  • 电池信息异常

  • CPU 当前频率全部相同、全为 0、或全部不可读

  • 多开/双开应用

  • 云手机特征属性

  • 电池状态:真机电池电量通常是波动的,且必须连接充电器才能显示“充电中”;模拟器常显示电量恒定 100% 且永远在充电。

import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build;

/**
 * 通过电池状态判断是否为模拟器
 * 特征:模拟器常见电量为100%且始终处于充电状态
 */
public class BatterySimulatorDetector {

    public static boolean isSimulatorByBattery(Context context) {
        if (context == null) {
            return false;
        }
        
        Intent batteryIntent = context.registerReceiver(
                null,
                new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
        );
        
        if (batteryIntent == null) {
            // 无法获取电池信息,保守判定为真机(避免误判)
            return false;
        }
        
        // 获取当前电量百分比 (0~100)
        int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        int batteryPct = -1;
        if (level != -1 && scale != -1) {
            batteryPct = (level * 100) / scale;
        }
        
        // 获取充电状态
        int status = batteryIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
        boolean isCharging = (status == BatteryManager.BATTERY_STATUS_CHARGING ||
                              status == BatteryManager.BATTERY_STATUS_FULL);
        
        // 获取电源插头类型(AC/USB/无线)
        int plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
        boolean isPlugged = (plugged != 0);
        
        // 模拟器典型特征:电量恒定100% 且 始终处于充电状态(即便拔掉电源也显示充电)
        boolean isAlwaysFullAndCharging = (batteryPct == 100 && isCharging);
        
        // 额外特征:某些模拟器没有真正的电池,EXTRA_PRESENT 可能为 false
        boolean isBatteryPresent = batteryIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
        
        // 组合判断:电量100%且正在充电,或者电池不存在
        if (isAlwaysFullAndCharging || !isBatteryPresent) {
            return true;
        }
        
        // 更进一步的检测:真机电量很少会长时间稳定在100%且插电状态不变
        // 若需要更严格检测,可配合历史记录(需多次调用),此处仅做单次快照
        
        return false;
    }
}
  • 运营商与网络:模拟器的 getSimOperatorName() 常返回 "Android" 或其他非真实运营商标识。
import android.content.Context;
import android.telephony.TelephonyManager;

/**
 * 通过运营商与网络信息检测是否为模拟器
 * 特征:模拟器的 getSimOperatorName() 常返回 "Android" 或其他虚假运营商名称
 */
public class OperatorSimulatorDetector {

    /**
     * 检测运营商信息是否异常(疑似模拟器)
     * @param context 上下文
     * @return true 表示可能是模拟器,false 表示正常真机特征
     */
    public static boolean isSimulatorByOperator(Context context) {
        if (context == null) {
            return false;
        }

        TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        if (tm == null) {
            // 没有电话服务(例如平板),放弃此项检测
            return false;
        }

        // 获取运营商名称(中国联通/移动/电信、vodafone、AT&T 等)
        String simOperatorName = tm.getSimOperatorName();
        String networkOperatorName = tm.getNetworkOperatorName();
        String simOperator = tm.getSimOperator();          // MCC+MNC,如 46001
        String networkOperator = tm.getNetworkOperator();

        // 模拟器常见虚假值
        if (simOperatorName == null) simOperatorName = "";
        if (networkOperatorName == null) networkOperatorName = "";

        String lowerSim = simOperatorName.toLowerCase();
        String lowerNetwork = networkOperatorName.toLowerCase();

        // 1. 返回 "Android" 或 "Android Emulator"
        if (lowerSim.contains("android") || lowerNetwork.contains("android")) {
            return true;
        }

        // 2. 返回空字符串或 "null"(某些模拟器)
        if (lowerSim.trim().isEmpty() || "null".equals(lowerSim) ||
            lowerNetwork.trim().isEmpty() || "null".equals(lowerNetwork)) {
            return true;
        }

        // 3. 运营商代码异常:真机 MCC 前两位范围 200-799,中国是 460
        //    模拟器可能返回 "310260"(美国 T-Mobile 测试)或 "00101" 等异常值
        if (simOperator != null && simOperator.length() >= 5) {
            String mcc = simOperator.substring(0, 3);
            try {
                int mccInt = Integer.parseInt(mcc);
                // 有效 MCC 范围参考:200~799
                if (mccInt < 200 || mccInt > 799) {
                    return true;
                }
            } catch (NumberFormatException e) {
                // 无法解析,视为可疑
                return true;
            }
        }

        // 4. 某些模拟器 SIM 状态为无 SIM 卡,但网络运营商名称却返回非空值(矛盾)
        int simState = tm.getSimState();
        if (simState == TelephonyManager.SIM_STATE_ABSENT ||
            simState == TelephonyManager.SIM_STATE_UNKNOWN) {
            // 没有 SIM 卡时,真机的 networkOperatorName 往往也为空或 "null"
            // 模拟器可能却返回 "Android" 之类,上面已捕捉;此处不做额外判断避免误报
        }

        // 若以上条件均不满足,且运营商名称是真实运营商常见关键词,判定为真机
        // 常见真实运营商关键词(可根据需要扩展)
        String[] realOperators = {
            "china", "移动", "联通", "电信", "cmcc", "china mobile",
            "china unicom", "china telecom", "vodafone", "t-mobile",
            "at&t", "verizon", "orange", "telefonica", "australia"
        };
        for (String keyword : realOperators) {
            if (lowerSim.contains(keyword) || lowerNetwork.contains(keyword)) {
                return false;  // 发现真实运营商,确认真机
            }
        }

        // 默认认为安全(不判为模拟器)
        return false;
    }
}
  • CPU 名称:绝大多数个人电脑 CPU 运行模拟器会暴露 Intel/AMD 信息,而非 ARM 架构(ro.board.platform=msm 通常是真机)。
  • 传感器监听:注册 SensorManager 监听器。真机通常至少拥有 3 个以上传感器。如果监听不到任何加速度或方向传感器,判定为风险环境。
// 通过CPU信息检测模拟器
private static boolean isEmulatorByCpuInfo() {
    try {
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/cpuinfo")));
        String line;
        while ((line = br.readLine()) != null) {
            // 检测x86架构
            if (line.contains("flags") && (line.contains("hypervisor") || line.contains("vmx") && !line.contains("svm"))) {
                br.close();
                return true;
            }
            // 检测Intel CPU(真机ARM设备通常显示其他品牌)
            if (line.startsWith("vendor_id") && line.contains("GenuineIntel")) {
                br.close();
                return true;
            }
            // 检测异常microcode
            if (line.startsWith("microcode") && line.contains("0xffffffff")) {
                br.close();
                return true;
            }
            // 检测特定模拟器CPU型号
            if (line.startsWith("model name") && (line.toLowerCase().contains("intel") || line.contains("QEMU"))) {
                br.close();
                return true;
            }
            // 检测bugs特征
            if (line.startsWith("bugs") && (line.contains("spectre_v1") && line.contains("spectre_v2") &&
                    line.contains("spec_store_bypass") && line.contains("swapgs") &&
                    line.contains("retbleed") && line.contains("bhi") && line.contains("its"))) {
                br.close();
                return true;
            }
        }
        br.close();
    } catch (Exception e) {
        // 忽略异常
    }
    return false;
}
  • 电话管理器:尝试获取 getDeviceId()getLine1Number()。模拟器常返回全 “0” 或空值,直接标记为异常。
public static boolean checkCpuInfo() {
    // 通过获取CPU信息,将x86的给过滤掉(真机一般都是基于ARM)
    // 检测flags值
    // 检测vendor_id:GenuineIntel
    // microcode:0xffffffff
    // model name:Intel(R) Core(TM) Ultra 5 125H
    // bugs:spectre_v1 spectre_v2 spec_store_bypass swapgs retbleed bhi its

    if (isEmulatorByCpuInfo()) {
        return true; // 是模拟器
    }

    // IMEI/IMSI 是否为默认固定值(如 000000000000000)
    if (isDefaultDeviceId()) {
        return true;
    }

    // OpenGL 渲染信息中包含 QEMU / SwiftShader 等字符串
    if (isEmulatorByOpenGL()) {
        return true;
    }

    // 传感器数量与恒定值判断
    if (isEmulatorBySensors()) {
        return true;
    }

    // 网卡接口名(如 eth0)
    if (isEmulatorByNetworkInterface()) {
        return true;
    }

    // 读取 /init.goldfish.rc, /dev/socket/qemud 等模拟器特征文件
    if (isEmulatorByFiles()) {
        return true;
    }

    return false;
}



// 检测默认设备ID
private static boolean isDefaultDeviceId() {
    try {
        android.telephony.TelephonyManager tm = (android.telephony.TelephonyManager)
                android.app.ActivityThread.currentApplication().getSystemService(android.content.Context.TELEPHONY_SERVICE);
        
        if (tm != null) {
            String imei = tm.getImei();
            String imsi = tm.getSubscriberId();
            
            if (imei != null && (imei.equals("000000000000000") || imei.matches("0+"))) {
                return true;
            }
            if (imsi != null && (imsi.equals("000000000000000") || imsi.matches("0+"))) {
                return true;
            }
        }
    } catch (Exception e) {
        // 忽略异常
    }
    return false;
}

// 通过OpenGL信息检测
private static boolean isEmulatorByOpenGL() {
    try {
        android.opengl.GLES10.glGetString(android.opengl.GLES10.GL_RENDERER);
        String renderer = android.opengl.GLES10.glGetString(android.opengl.GLES10.GL_RENDERER);
        if (renderer != null) {
            String lowerRenderer = renderer.toLowerCase();
            if (lowerRenderer.contains("qemu") || lowerRenderer.contains("swiftshader") ||
                    lowerRenderer.contains("bluestacks") || lowerRenderer.contains("nox") ||
                    lowerRenderer.contains("mumu") || lowerRenderer.contains("virtualbox") ||
                    lowerRenderer.contains("vbox") || lowerRenderer.contains("genymotion")) {
                return true;
            }
        }
    } catch (Exception e) {
        // 忽略异常
    }
    return false;
}

// 通过传感器检测
private static boolean isEmulatorBySensors() {
    try {
        android.hardware.SensorManager sm = (android.hardware.SensorManager)
                android.app.ActivityThread.currentApplication().getSystemService(android.content.Context.SENSOR_SERVICE);
        
        if (sm != null) {
            java.util.List<android.hardware.Sensor> sensors = sm.getSensorList(android.hardware.Sensor.TYPE_ALL);
            // 模拟器通常只有2-3个传感器,真机通常有10个以上
            if (sensors == null || sensors.size() < 5) {
                return true;
            }
            
            // 检查特定传感器是否存在
            boolean hasAccelerometer = false;
            boolean hasMagnetic = false;
            for (android.hardware.Sensor sensor : sensors) {
                if (sensor.getType() == android.hardware.Sensor.TYPE_ACCELEROMETER) {
                    hasAccelerometer = true;
                }
                if (sensor.getType() == android.hardware.Sensor.TYPE_MAGNETIC_FIELD) {
                    hasMagnetic = true;
                }
            }
            if (!hasAccelerometer || !hasMagnetic) {
                return true;
            }
        }
    } catch (Exception e) {
        // 忽略异常
    }
    return false;
}

// 通过网卡接口名检测
private static boolean isEmulatorByNetworkInterface() {
    try {
        java.util.Enumeration<java.net.NetworkInterface> interfaces = java.net.NetworkInterface.getNetworkInterfaces();
        if (interfaces != null) {
            while (interfaces.hasMoreElements()) {
                java.net.NetworkInterface networkInterface = interfaces.nextElement();
                String name = networkInterface.getName();
                if (name != null) {
                    String lowerName = name.toLowerCase();
                    // 模拟器常见网卡名称
                    if (lowerName.equals("eth0") || lowerName.startsWith("vmnet") ||
                            lowerName.contains("vbox") || lowerName.contains("virtual")) {
                        return true;
                    }
                }
            }
        }
    } catch (Exception e) {
        // 忽略异常
    }
    return false;
}


    
    // 检查特定驱动
    try {
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/tty/drivers")));
        String line;
        while ((line = br.readLine()) != null) {
            if (line.contains("goldfish")) {
                br.close();
                return true;
            }
        }
        br.close();
    } catch (Exception e) {
        // 忽略异常
    }
    
    return false;
}

检测IMEI/IMSI需要添加权限:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

Host App 检测

目标宿主

内置目标包括:

  • VMOS Pro:com.vmos.pro
  • VMOS:com.vmos.glb
  • VPhoneGaGa:com.vphonegaga.titan
  • X8 Sandbox:com.x8zs.sandbox
  • F1 VM:com.f1player
  • Parallel Space:com.lbe.parallel.intl
  • Parallel Space Lite:com.parallel.space.lite
  • GSpace:com.excean.gspace
  • VirtualApp:io.virtualapp
  • VirtualXposed:io.va.exposed
  • Dual Space:com.bly.dkplat
  • Genymotion:com.genymotion.superuser

检测方式

它使用四类方法:

  • PackageManager
  • FUSE 路径 stat
  • native /data/data stat
  • 特殊路径

特殊路径如:

  • /sdcard/Android/data/com.vmos.pro
  • /sdcard/Android/data/com.vphonegaga.titan
  • /sdcard/Android/data/com.x8zs.sandbox
  • /sdcard/Android/data/io.va.exposed

结果语义

Host app 只是 INFO 级旁证。

原因:

  • 用户安装了 VMOS/Parallel Space,不代表当前 app 运行在其中
  • Package visibility 可能受 Android 11+ 限制
  • 宿主可以改包名或隐藏数据路径

但如果 Host app 与 Dex/UID/Classpath 命中同时出现,置信度会显著提高。