第三方软件QQ登录时的签名校验及绕过

第三方软件使用QQ登录时校验过程

使用工具: SingHook

实现方法: 通过hook getPackageInfo函数来绕过签名校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
            "android.app.ApplicationPackageManager".hookAfterMethod(
lpparam.classLoader, "getPackageInfo", String::class.java, Int::class.java
) { param ->
val packageInfo = param.result.safeCast<PackageInfo>() ?: return@hookAfterMethod
val pkg = packageInfo.packageName
try {
val signatures = HostKVManager.createKVHelper(FAKE_SIGNATURE).getString(pkg)

if (signatures.isNotBlank()) {
packageInfo.setObjectField(
"signatures", arrayOf((Signature(signatures)))
)
// Log.d("packageName: ${lpparam.packageName}\npkg: $pkg\nsignatures: $signatures")
}
} catch (e: Exception) {
Log.e(e)
}
}

实验过程:

场景 结果
仅hook QQ/TIM ✔ 成功绕过
仅hook 目标 App ❌ 无效
QQ + 目标 App 同时hook ✔ 成功绕过

实验结论: 目标软件使用QQ登录时 签名校验是来自QQ端的getPackageInfo, 与目标软件无关

我之前还以为是目标软件主动构造的签名信息orz 想着hook目标软件相关方法来绕过

签名校验过程及绕过

签名校验流程

如何得到签名

通过getPackageInfo函数

getPackageInfo调用链

1
2
3
4
5
6
7
8
9
10
11
12
13
App进程

PackageManager.getPackageInfo("com.xxx.app", flags)

Binder IPC

:contentReference[oaicite:1]{index=1}(system_server)

查找内存/数据库中的包记录

构造 PackageInfo

返回给 App

如何绕过

我们从这个调用链来一一分析

API 层 Hook

hook getPackageInfo方法, 这里SignHook就是这么做的

Binder 层拦截

可以通过替换 PackageInfo.CREATOR,在 createFromParcel() 中构造伪造签名对象,从而影响系统服务下发的数据

system_server 内存修改

每次APP安装时PMS会将该APP的信息写入数据库

然后通过加载到内存实现随用随取

所以可以修改内存来临时篡改获取到的APP信息

文件层劫持

如果是通过访问 META-INF/ 签名文件来获取的签名信息可以hook IO函数来进行劫持

启动阶段劫持

对于加固或壳在初始化时就执行签名校验逻辑的

可以通过修改 AndroidManifest.xml 中声明的 Application 类或 AppComponentFactory,注入自定义逻辑,避开原始签名校验

因为系统在启动 App 进程前,会解析 AndroidManifest.xml 来决定怎么启动这个 App

内核级绕过

再深一点, 还可以通过 inline hook 或内核模块拦截 SVC 指令,直接修改 sys_opensys_read 返回值,实现更底层的绕过

参考文章

Android 签名校验与绕过思路以及检测与反检测的攻防对抗