目标库:libcachehandler.so(商业 RASP Bugsmirror Defender,特征串 BugsmirrorDefenderValidation / com.bugsmirror.samplekeyattestation)
分析环境:AArch64,IDA + Frida Gadget
关键函数(设备版 libcachehandler.so_fixed 偏移):
说明:以下 IST34 小节的偏移取自解密重建版 libcachehandler_decrypted_rebuild.so(结果读取器为 sub_EA95C),已用 IDA 逐条验证。
注:61007 的 IST1…IST34 在上报层都汇聚到 category 1004(if (sub_EA95C(1004)) 上报),根因归一;但检测层 IST31 与 IST34 是两套不同原理:IST31(sub_11DEE8)是硬件认证链验签 + pin 比对,IST34(check_android_keystore_integrity_trap)是**「echo-trap」回声陷阱**——两者互补,详见第二节。
IST34 与 IST31 上报层同归 category 1004,但检测原理完全不同:IST31 打「你伪造的链没有 Google 真签名」,IST34 打「你的 hook 太贪,连我自己塞进 KeyStore 的已知证书都乱改」。一个从密码学正确性下手,一个从「keystore hook 的副作用」下手,互为补充;凡是粗暴全局替换 getCertificateChain 返回值的伪造模块,两条都躲不掉。
三个门槛:
内嵌两块 base64(重建版直接写入):
流程:
前面还用 KeyPairGenerator + setAttestationChallenge("trap_bait") 生成过一个真 attestation key,那是诱饵,让 hook 以为这是常规 attestation 调用。
第 1 步是核心:App 只在「设备报告有 StrongBox」时才跑这套强完整性检查(IST31)。
所以:把设备的 StrongBox 特性关掉,IST31 直接不跑。 这是最干净的绕过。
Hook 改机 魔改ts 最近新出的漏洞机免解锁root提权三绿转发 均可实现绕过 感兴趣的可以自己测试一下 这个检测思路还是比较有意思的
第四步应对方法均已实现
| code |
arg5 / 含义 |
检测的东西 |
应对 |
| 61007 |
61007-IST1 … IST34 |
AndroidKeyStore 密钥认证链真伪(核心,见下) |
关掉 StrongBox 特性 |
| 61009 |
61009-IST1/2-<包名> |
已装应用黑名单(77 项,如 bin.mt.plus MT管理器) |
卸载/改包名 或 Frida 藏包名 |
| 10002 |
CA compromised by proxy tool: |
抓包/中间人代理 CA(Charles/Fiddler/HttpCanary…) |
卸抓包工具/删其 CA |
| 10004 |
90040-IST5 |
popen shell 命令环境探测 |
视命令而定 |
| 00000 |
Device environment is not correct. |
兜底异常:某检查抛异常没走完(本例是 StrongBox 请求失败引发) |
消除引发异常的根因 |
| 变量 |
内容 |
| 私钥 |
一段 PKCS#8 EC 私钥(secp256r1) |
| 证书 |
对应的一张自签证书,CN = EchoTrapStatic,notBefore 2026-05-07 / notAfter 2126-04-13 |
| code |
检测 |
应对 |
| 61009 |
getInstalledPackages 拿已装应用,逐个与 77 项编码黑名单 strcmp(sub_14BDA8 解码后比),命中报 61009-IST2-<包名> |
卸载/改包名命中项;或 Frida hook 解码器 sub_14BDA8,解出黑名单包名时原地改首字节使其匹配不上 |
| 10002 |
扫已装 CA 证书,发现抓包/代理工具 CA(拼串 CA compromised by proxy tool: ) |
卸抓包工具,删其用户 CA |
| 10004 / 90040-IST5 |
sub_14BDA8 解出一条 shell 命令 → popen(cmd,"r") 读输出判断环境 |
看具体命令(hook sub_14BDA8 打印);隐藏对应痕迹 |
| 00000 |
兜底:检查抛异常没走完 → Device environment is not correct. |
消除引发异常的根因(本例=StrongBox 请求失败,用方法 A 关特性即可根除) |
v2 = sub_EA95C(1004);
if (__system_property_get("ro.build.version.sdk", nptr) <= 0) return;
v3 = (v2 & 1) != 0 ? 29 : 33;
v4 = atoi(nptr);
if (v4 < 1 || v4 < v3 || (byte_15FCDB & 1)) return;
if ((sub_EA95C(1004) & 1) != 0 && check_android_keystore_integrity_trap(a1))
handle_security_violation(a1, "61007", 0, 0, 0, "61007-IST34");
if ( !GetArrayLength(chain)
|| chain[0].getEncoded().length != 内嵌证书长度 )
{
return true;
}
return memcmp(内嵌证书DER, chain[0].getEncoded(), 长度) != 0;
目标库:libcachehandler.so(商业 RASP Bugsmirror Defender,特征串 BugsmirrorDefenderValidation / com.bugsmirror.samplekeyattestation)
分析环境:AArch64,IDA + Frida Gadget
关键函数(设备版 libcachehandler.so_fixed 偏移):
sub_14D5E0 — 上报汇聚点 handle_security_violation(ctx, code, sev, a3, a4, ist)
sub_DF838(id) — 结果读取器,读全局结果位表(sub_EA95C 为解密重建版对应函数)
sub_143BB4 / sub_11DEE8 — keystore attestation 校验(IST31 所在)
check_android_keystore_integrity_trap(重建版 sub_11D478)— echo-trap 回声陷阱本体(IST34)
check_su_in_system_image(重建版 sub_11B9B8)— IST34 上报判定点,调用上面的 echo-trap
sub_14BDA8 — 运行期字符串解码器
说明:以下 IST34 小节的偏移取自解密重建版 libcachehandler_decrypted_rebuild.so(结果读取器为 sub_EA95C),已用 IDA 逐条验证。
注:61007 的 IST1…IST34 在上报层都汇聚到 category 1004(if (sub_EA95C(1004)) 上报),根因归一;但检测层 IST31 与 IST34 是两套不同原理:IST31(sub_11DEE8)是硬件认证链验签 + pin 比对,IST34(check_android_keystore_integrity_trap)是**「echo-trap」回声陷阱**——两者互补,详见第二节。
前面还用 KeyPairGenerator + setAttestationChallenge("trap_bait") 生成过一个真 attestation key,那是诱饵,让 hook 以为这是常规 attestation 调用。
| code |
arg5 / 含义 |
检测的东西 |
应对 |
| 61007 |
61007-IST1 … IST34 |
AndroidKeyStore 密钥认证链真伪(核心,见下) |
关掉 StrongBox 特性 |
| 61009 |
61009-IST1/2-<包名> |
已装应用黑名单(77 项,如 bin.mt.plus MT管理器) |
卸载/改包名 或 Frida 藏包名 |
| 10002 |
CA compromised by proxy tool: |
抓包/中间人代理 CA(Charles/Fiddler/HttpCanary…) |
卸抓包工具/删其 CA |
| 10004 |
90040-IST5 |
popen shell 命令环境探测 |
视命令而定 |
| 00000 |
Device environment is not correct. |
兜底异常:某检查抛异常没走完(本例是 StrongBox 请求失败引发) |
消除引发异常的根因 |
| 变量 |
内容 |
| 私钥 |
一段 PKCS#8 EC 私钥(secp256r1) |
| 证书 |
对应的一张自签证书,CN = EchoTrapStatic,notBefore 2026-05-07 / notAfter 2126-04-13 |
| code |
检测 |
应对 |
| 61009 |
getInstalledPackages 拿已装应用,逐个与 77 项编码黑名单 strcmp(sub_14BDA8 解码后比),命中报 61009-IST2-<包名> |
卸载/改包名命中项;或 Frida hook 解码器 sub_14BDA8,解出黑名单包名时原地改首字节使其匹配不上 |
| 10002 |
扫已装 CA 证书,发现抓包/代理工具 CA(拼串 CA compromised by proxy tool: ) |
卸抓包工具,删其用户 CA |
| 10004 / 90040-IST5 |
sub_14BDA8 解出一条 shell 命令 → popen(cmd,"r") 读输出判断环境 |
看具体命令(hook sub_14BDA8 打印);隐藏对应痕迹 |
| 00000 |
兜底:检查抛异常没走完 → Device environment is not correct. |
消除引发异常的根因(本例=StrongBox 请求失败,用方法 A 关特性即可根除) |
sub_14D5E0 — 上报汇聚点 handle_security_violation(ctx, code, sev, a3, a4, ist)
sub_DF838(id) — 结果读取器,读全局结果位表(sub_EA95C 为解密重建版对应函数)
sub_143BB4 / sub_11DEE8 — keystore attestation 校验(IST31 所在)
check_android_keystore_integrity_trap(重建版 sub_11D478)— echo-trap 回声陷阱本体(IST34)
check_su_in_system_image(重建版 sub_11B9B8)— IST34 上报判定点,调用上面的 echo-trap
sub_14BDA8 — 运行期字符串解码器
- 所有
61007-ISTx 上报都是 if (sub_DF838(1004)) 上报——只读 category 1004 的结果位。
- category 1004 由 keystore attestation 校验置位。
- 它验的是「链是不是 Google 真私钥签的」,不是「内容填得对不对」,也不查解锁状态(无
getExtensionValue、无 RootOfTrust、无 verifiedBoot/locked 解析)。
- 内嵌 pin 里有一张真实 Google/TEE 中间证(CRL 序列号
10997554208317698753)及其 EC 公钥 MFkw…,是它的参照锚点。
- 这类模块把真链 leaf 以上整段换成自己 keybox 的中间证/根证(
newChain = leaf + keybox.certificates)。
- 换出来的链没有 Google 真私钥的签名,验签/pin 对不上 → 必中。
- 数学上无解:要伪造一条能通过 Google 签名验证的链,需要 Google 的私钥,你没有。换正版三绿 keybox 也不行(pin 的是它自带的特定链,不是你 keybox 的链)。
- 正常设备:塞进去什么读回来就是什么,
memcmp == 0,返回 false,不报。
- 装了 keystore 伪造模块:这类模块为骗过 attestation 会无差别拦截
getCertificateChain,把返回链整段换成自己 keybox 的伪造链。于是连跟 attestation 毫无关系的 echo_trap_key 也被替换 —— 读回的 leaf 不再是那张 EchoTrapStatic 自签证书,长度/字节对不上 → IST34 命中。
- IST34 挂在
sub_EA95C(1004) 已置位的前置下 —— 只要让 1004 不置位(方法 A 关 StrongBox 让 IST31 整条不跑),echo-trap 根本不会被调,IST34 一并消失。
- 运行期 hook(方法 C):
sub_EA95C 对 id==1004 返回 0,同时压掉 IST31 与本条前置。
- 若单独针对 echo-trap:让你的 keystore hook 对 alias
echo_trap_key 放行(不替换其返回链) —— 成熟伪造模块应做的「白名单」逻辑,粗暴全局替换的会死在这里。
hasSystemFeature(strongbox) = true(真 Pixel 6 等)→ 走 StrongBox 路径 → 跑链校验 → 伪造链被逮 → 61007;若让 StrongBox 请求失败 → 抛 StrongBoxUnavailableException → 00000「环境不对」。
hasSystemFeature(strongbox) = false → App 不请求 StrongBox → 整条强完整性检查跳过 → 没有 61007,也没有 00000。
hasSystemFeature("android.hardware.strongbox_keystore") —— 设备有 StrongBox 则 setIsStrongBoxBacked(true)。
KeyPairGenerator 现场生成一个带 attestation 的 key(EC secp256r1 / RSA,固定证书 notBefore/notAfter)。
getCertificateChain 取回证书链。
- 逐级
cert.verify(上一级公钥) —— 密码学验签,确认每张证书由上一级真实签发。
checkValidity() —— 证书未过期。
- 比对链上证书的公钥 / 序列号 / IssuerDN 与内嵌 pin 值(内嵌了正版 Google/TEE 认证链的参考值,
sub_122BD4/sub_122EE8)。
- 验签失败 / pin 对不上 → 置 category 1004 → 报 61007。
byte_15FCDB 单次守卫 —— 全程只跑一次(函数末尾置 1)。
- SDK 门槛 —— Android 8/8.1 以下不跑。
sub_EA95C(1004) & 1 必须已为真 —— 即 IST34 是 IST31 的「二次确认」:只有当链校验已把 1004 置位、判定环境可疑,才拉起 echo-trap 再确认一遍,成立就补报 IST34。
sub_147EF0(WolfSSL)把两段 base64 解成 DER,记下证书 DER 及其长度作为参照锚点;解码失败抛 WolfSSL failed to decode Private Key / Certificate。
- 私钥经
PKCS8EncodedKeySpec + KeyFactory("EC").generatePrivate 还原;证书经 CertificateFactory("X.509").generateCertificate 还原。
keyStore.setKeyEntry("echo_trap_key", privKey, pwd, [cert]) —— 把这张内容完全已知的 leaf 证书塞进 AndroidKeyStore。
前面还用 KeyPairGenerator + setAttestationChallenge("trap_bait") 生成过一个真 attestation key,那是诱饵,让 hook 以为这是常规 attestation 调用。
chain = keyStore.getCertificateChain("echo_trap_key") —— 原样读回。
keyStore.deleteEntry("echo_trap_key") —— 用完删除,不留痕。
- 逐字节比对(核心判定):
sub_14D5E0 — 上报汇聚点 handle_security_violation(ctx, code, sev, a3, a4, ist)sub_DF838(id) — 结果读取器,读全局结果位表(sub_EA95C 为解密重建版对应函数)
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
最后于 1天前
被小七烤地瓜编辑
,原因: