常见签名校验
一般来说,签名校验的方式有以下几种
- 通过安卓提供的api,利用packagemanager获取签名数据,和已存的正确值进行对比
- 在native中,反射Java的api,进行对比,本质上和1相同
- 自己读取apk文件,解压,校验META-INF里的RSA文件
第一种和第二种,是大多数软件在用的,但是很容易被逆向者过掉,甚至有工具可以一键过掉。
第三种校验的准确性更高,缺点是效率低、易发生错误,并且v1以上的签名之后,并不生成RSA文件。那么校验文件也就不可取了。
这是常见的签名校验的示例:
/**
* 常见的签名校验
*/
private boolean doNormalSignCheck() {
String trueSignMD5 = "d0add9987c7c84aeb7198c3ff26ca152";
String nowSignMD5 = "";
try {
// 得到签名的MD5
PackageInfo packageInfo = getPackageManager().getPackageInfo(
getPackageName(),
PackageManager.GET_SIGNATURES);
Signature[] signs = packageInfo.signatures;
String signBase64 = Base64Util.encodeToString(signs[0].toByteArray());
nowSignMD5 = MD5Utils.MD5(signBase64);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return trueSignMD5.equals(nowSignMD5);
}
归根结底,问题就在于如何准确的获取签名。
反射机制
反射是Java的一种机制
下面是百度百科的介绍:
新的获取签名方式
一个通过反射获取Binder,进而获取签名值的示例。
val smc = Class.forName("android.os.ServiceManager")
val sCache = (smc.getDeclaredField("sCache").also {
if (!it.isAccessible) it.isAccessible = true
}.get(smc) as ArrayMap<String, IBinder>).also { it.clear() }
val gsm = smc.getDeclaredMethod("getService", String::class.java)
fun checkSignV2(context: Context, realSign: String): Boolean {
kotlin.runCatching {
if (sCache["package"] == null) {
val userId = Process.myUid() / 100000
val binder = HiddenApi.getServiceByMethod("package")
val bz = Class.forName("android.content.pm.IPackageManager\$Stub")
val am = bz.getDeclaredMethod("asInterface", IBinder::class.java)
val mmg = am.invoke(bz, binder)
val ic = Class.forName("android.content.pm.IPackageManager")
val gmi = ic.getDeclaredMethod("getPackageInfo", String::class.java, Int::class.java, Int::class.java)
val signatures: Array<Signature> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
(gmi.invoke(mmg, context.packageName, PackageManager.GET_SIGNING_CERTIFICATES, userId) as PackageInfo).signingInfo.apkContentsSigners
} else {
(gmi.invoke(mmg, context.packageName, PackageManager.GET_SIGNATURES, userId) as PackageInfo).signatures
}
val builder = StringBuilder()
signatures.forEach {
builder.append(it.toCharsString())
}
return builder.toString() == realSign
}
return false
}.onFailure {
it.printStackTrace()
}
return true
}
之前查验签名的PackageManager是用bind系统服务(package)实现的。而这个,直接对接系统服务。
补充:
多种校验方式结合,配合着类名校验更佳。
参考:https://blog.xinrao.co/index.php/archives/28/
https://blog.csdn.net/qq_40948137/article/details/115866451