如何在运行时编程计算APK校验和(适配API28-33、Gradle6.6.1)
运行时计算APK校验和实现防篡改方案(适配minSdk28+)
问题分析
你之前使用applicationInfo.publicSourceDir计算校验和不准确,核心原因是:
- 若应用通过App Bundle(AAB)分发,该路径指向拆分后的
base.apk,不包含动态模块或后续更新的资源; - 部分场景下该路径可能指向系统缓存的APK副本,而非实际安装的完整APK文件,导致资源修改后校验和未变化。
可行实现方案
针对你的环境(minSdkVersion=28,targetSdkVersion=33),以下是可靠的实现步骤:
1. 正确获取APK完整路径
使用PackageInfo.applicationInfo.sourceDir获取主APK文件路径,该路径在minSdk28+下会指向包含应用核心代码、资源的完整APK文件,无论是否为AAB分发的拆分包,修改资源后文件内容会同步变化。
2. 实现APK文件哈希计算
推荐使用SHA-256算法(安全性高于MD5)计算文件校验和,以下是Kotlin实现代码:
import java.io.FileInputStream import java.security.MessageDigest object ChecksumUtils { fun calculateApkSha256(filePath: String): String? { return try { val digest = MessageDigest.getInstance("SHA-256") FileInputStream(filePath).use { inputStream -> val buffer = ByteArray(8192) var readBytes = inputStream.read(buffer) while (readBytes != -1) { digest.update(buffer, 0, readBytes) readBytes = inputStream.read(buffer) } } val hashBytes = digest.digest() // 转换为十六进制字符串 hashBytes.joinToString("") { "%02x".format(it) } } catch (e: Exception) { e.printStackTrace() null } } }
3. 在应用中调用校验逻辑
在Activity或Context中获取APK路径并计算校验和:
val packageInfo = packageManager.getPackageInfo(packageName, 0) val apkFilePath = packageInfo.applicationInfo.sourceDir val validChecksum = getValidChecksumFromServer() // 从后端获取合法校验和 val currentChecksum = ChecksumUtils.calculateApkSha256(apkFilePath) if (currentChecksum == validChecksum) { // 校验通过,正常启动应用 } else { // 校验失败,触发防篡改逻辑(如退出应用、上报服务器) }
关键注意事项
- 权限处理:minSdk28+(含Android 10+)中,应用读取自身安装目录下的APK文件无需申请
READ_EXTERNAL_STORAGE权限,系统默认允许。 - 校验和存储:禁止将合法校验和硬编码在APK中,必须通过后端服务器实时获取;无网络场景可将校验和的二次哈希值存储在JNI层,增加篡改难度。
- 代码混淆:确保校验逻辑不被反编译篡改,在ProGuard规则中添加:
-keep class com.yourpackage.utils.ChecksumUtils { public static java.lang.String calculateApkSha256(java.lang.String); } - 反Hook防护:攻击者可能通过Xposed/Frida Hook校验方法返回假结果,可在校验逻辑中加入反Hook检测(如检测调试器、异常进程),或直接将校验逻辑移至Native层实现。
验证方法
修改APK中的资源文件(如替换drawable下的图片),重新签名安装后,运行应用计算校验和,结果应与原APK的校验和完全不同。
内容的提问来源于stack exchange,提问作者kaveri kataraki
相关产品推荐
相关产品推荐

