Android虚拟机与虚拟空间检测实战详解
本文系统介绍如何在Android应用中检测虚拟机(Emulator)和虚拟空间(多开、沙箱等),并结合代码示例,涵盖系统配置、硬件信息、签名校验、内存映射等多维度检测方案。适用于提升应用安全性和防作弊需求。
1. 虚拟机检测
虚拟机检测主要针对常见模拟器环境(如Genymotion、Google Emulator、VirtualBox等),主要通过系统配置和硬件特征进行识别。
kotlin
kotlin
复制编辑
fun isEmulator(): Boolean {
val checkProperty = Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.lowercase().contains("vbox")
|| Build.FINGERPRINT.lowercase().contains("test-keys")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86")
|| Build.MANUFACTURER.contains("Genymotion")
|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|| Build.PRODUCT == "google_sdk"
if (checkProperty) return true
val tm = Utils.getApp().getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager
val operatorName = tm?.networkOperatorName ?: ""
if (operatorName.lowercase() == "android") return true
val intent = Intent(Intent.ACTION_DIAL, Uri.parse("tel:123456"))
if (intent.resolveActivity(Utils.getApp().packageManager) == null) return true
return false
}
也可参考AntiFakerAndroidChecker库,通过Native层辅助检测,提升检测精度。
2. 虚拟空间检测
虚拟空间指宿主/沙箱机制,多开环境中App运行在隔离环境,绕过部分安全策略。针对虚拟空间,推荐多角度检测:
2.1 UID异常检测
正常第三方应用UID一般≥10000,低于该值可能为多开环境。
kotlin
kotlin
复制编辑
fun isUidSuspicious(context: Context): Boolean {
return context.applicationInfo.uid < 10000
}
2.2 硬件信息异常
检测MAC地址是否为虚拟默认值。
kotlin
kotlin
复制编辑
fun isHardwareInfoAbnormal(): Boolean {
val mac = getMacAddress().lowercase()
val virtualMacs = setOf("00:00:00:00:00:00", "02:00:00:00:00:00", "aa:bb:cc:dd:ee:ff")
return virtualMacs.contains(mac)
}
2.3 签名校验
对比App签名哈希、公钥和证书信息,防止被篡改或注入。
kotlin
kotlin
复制编辑
object SignatureChecker {
fun isSignatureTampered(context: Context, sha256: String, publicKey: String, issuerCN: String): Boolean {
return try {
val packageInfo = context.packageManager.getPackageInfo(
context.packageName,
PackageManager.GET_SIGNATURES or PackageManager.GET_SIGNING_CERTIFICATES
)
val signatures = packageInfo.signatures ?: return true
signatures.any { signature ->
val signSha256 = getSignatureSha256(signature)
val key = getPublicKeyFromSignature(signature)
val cert = getX509Certificate(signature)
signSha256 != sha256 || key != publicKey || cert.issuerDN.name != issuerCN || cert.notAfter.before(Date())
}
} catch (e: Exception) {
true
}
}
// 省略辅助方法,实现请参照源码
}
2.4 反射检测包名异常
检测是否存在Hook篡改。
kotlin
kotlin
复制编辑
fun isPackageNameHooked(context: Context): Boolean {
return try {
val method = Class.forName("android.content.Context").getMethod("getPackageName")
val result = method.invoke(context) as String
result != context.packageName
} catch (e: Exception) {
true
}
}
2.5 读取 /proc/self/maps 判断内存映射异常
kotlin
kotlin
复制编辑
fun isMapsFileAbnormal(packageName: String): Boolean {
val mapsFile = File("/proc/self/maps")
if (!mapsFile.exists() || !mapsFile.canRead()) return false
mapsFile.useLines { lines ->
lines.forEach { line ->
val lowerLine = line.lowercase()
if (lowerLine.contains(packageName) && lowerLine.contains("/data/data/") && !lowerLine.endsWith("/data/data/")) {
return true
}
}
}
return false
}
注意:Android高版本读取
/proc/self/maps
受限,可结合Native层读取绕过限制。
2.6 类加载器路径检测
检测ClassLoader路径中是否含虚拟空间关键词。
kotlin
kotlin
复制编辑
fun isClassLoaderSuspicious(): Boolean {
return try {
val path = YourClass::class.java.classLoader.toString().lowercase()
listOf("virtual", "xspace", "dual", "multi", "parallel", "cloner", "xposed").any { path.contains(it) }
} catch (e: Exception) {
false
}
}
2.7 父进程检测
检测当前进程父进程是否属于已知多开环境。
kotlin
kotlin
复制编辑
private val VIRTUAL_APPS = setOf(
"com.cmaster.cloner",
"com.lbe.parallel",
"com.vmos",
"org.app.virtual",
"de.robv.android.xposed.installer",
"com.excelliance.dualaid",
"com.bly.dkplat",
"com.qihoo.magic",
"com.oem.oemclone",
"com.island",
"com.shelter"
)
fun isParentProcessVirtual(): Boolean {
return try {
val pid = Process.myPid()
val stat = File("/proc/$pid/stat").readText().split(" ")
val ppid = stat.getOrNull(3)?.toIntOrNull() ?: return false
val parentCmd = File("/proc/$ppid/cmdline").readText().trim()
val parentPackage = parentCmd.substringAfterLast("/", parentCmd)
VIRTUAL_APPS.contains(parentPackage)
} catch (e: Exception) {
false
}
}
2.8 检测已安装虚拟空间App
kotlin
kotlin
复制编辑
fun isKnownVirtualAppInstalled(context: Context): Boolean {
val pm = context.packageManager
val installed = pm.getInstalledPackages(0).map { it.packageName }
return VIRTUAL_APPS.any { installed.contains(it) }
}
2.9 挂载点检测
检测挂载点路径中是否包含虚拟环境关键词。
kotlin
kotlin
复制编辑
fun hasSuspiciousMounts(): Boolean {
return try {
File("/proc/self/cgroup").useLines { lines ->
lines.any { it.contains("vmos") || it.contains("virtual") || it.contains("parallel") }
}
} catch (e: Exception) {
false
}
}
3. 注意事项
- Android不同版本和厂商对系统文件访问限制不同,检测结果可能存在差异。
- 建议多种检测方案结合使用,降低误判率。
- 防止误杀:检测逻辑应预留后门或远程配置,避免正常用户被误判影响体验。
- 测试时尽量覆盖主流Android版本(10、11、13等)和机型。
4. 总结
虚拟机和虚拟空间检测是提升Android应用安全的有效手段。本文通过多维度代码示例,介绍了常见模拟器检测、UID及硬件异常检测、签名校验、进程和挂载点检测等方法。建议结合Native层和动态配置,持续完善检测逻辑。