Android虚拟机与虚拟空间检测实战详解<二>

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层和动态配置,持续完善检测逻辑。

相关推荐
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95272 天前
Andorid Google 登录接入文档
android
黄林晴2 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android