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

相关推荐
2501_916013748 小时前
iOS混淆工具有哪些?跨平台 App 混淆与保护的实用方案
android·ios·小程序·https·uni-app·iphone·webview
2501_915909068 小时前
iOS 文件管理实战指南,用户文件、安全访问与开发调试方案
android·ios·小程序·https·uni-app·iphone·webview
峥嵘life12 小时前
Android初学者系统开发学习路线参考
android·学习
Xu_youyaxianshen14 小时前
Android 缓存日志(Logcat)导出与分析全攻略
android·缓存·log日志
黑白小道士14 小时前
Kotlin 中,run、also、let、apply、with 是常用的作用域函数
android·kotlin
0wioiw017 小时前
Android-Kotlin基础(Jetpack③-LiveData)
android·开发语言·kotlin
xzkyd outpaper17 小时前
Android中Binder缓冲区为什么限制1MB,此外Bundle数据为什么要存储在Binder缓冲区中
android·binder
aqi0018 小时前
FFmpeg开发笔记(七十九)专注于视频弹幕功能的国产弹弹播放器
android·ffmpeg·音视频·直播·流媒体
深盾科技19 小时前
Android 安全编程:Kotlin 如何从语言层保障安全性
android·安全·kotlin