概述
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 状态
三层检测模型:
项目大量检测项都按三层实现:
JAVA- 系统 API、反射、Java IO、PackageManager、Settings、Build、ConnectivityManager
NATIVEaccess/fopen/opendir/getifaddrs/socket/__system_property_get
SYSCALL- 直接
openat/read/getdents64/readlinkat/statfs/uname/netlink
- 直接
其设计目标是区分两类问题:
- 真命中:设备确实存在 root / hook / debug / tamper 特征
- 伪装/屏蔽:上层 API 被 Hook,但底层仍能读到真实数据
Root检测
绕过检测:利用 Xposed 模块(如 RootCloak)修改 Build.TAGS 和 SELinux 状态,将 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.managercom.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.magiskme.weishu.kernelsume.bmax.apatchorg.sukisu.manager- 以及历史
SuperSU/KingRoot/KingoRoot/phh
Root隐藏模块
shamikozygisk-assistantplayintegrityfixzygiskNextrezygisklsposed
可疑挂载检测
重点读取:
/proc/self/mountinfo/proc/self/mounts
关注特征:
magiskzygiskkernelsuapatch/data/adb/modules- 重复
cacerts挂载
它试图识别:
- 模块 overlay
- root 文件隐藏
- 证书注入
模拟器检测
- 厂商/型号伪装:篡改
ro.product.manufacturer和ro.product.model,通常隐蔽地伪装成主流设备(如 Samsung、Xiaomi 或 Google Pixel)。 - 传感器注入:针对真机必须具备的加速度传感器、陀螺仪等物理硬件,模拟器默认是缺失的。攻击者会利用工具注入伪造的传感器数据流。
- 硬件特征重置:伪造 IMEI、IMSI 及 MAC 地址,避免被指纹算法识别为固定虚拟设备。
- CPU 指令集:通过强制启用 ARM 转译层(如 Waydroid)或修改
ro.product.cpu.abi来隐藏 x86 的模拟器特征。
目前流行的 Android 模拟器大概分为两种,一种是基于 Qemu,另一类是基于 Genymotion(VirtualBox 类),网上现在流行用一些模拟器特征进行鉴别,比如:
模拟器指纹
Build 特征:
goldfishranchugenericvbox86noxgenymotionAndroid SDK built for x86
Native 层还会检查:
- QEMU 相关文件
- 模拟器文件系统痕迹
docker云手机:
ro.boot.redroid_\*:这是最核心的检测点。这类属性通常在 Redroid 容器启动时由内核 cmdline 设置,用于传递配置参数。例如,当应用读取ro.boot.redroid_width、ro.boot.redroid_height或ro.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.hardware、ro.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/datastat - 特殊路径
特殊路径如:
/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 命中同时出现,置信度会显著提高。