客户端证书导出 hook
🛡️ Hook
KeyStore$PrivateKeyEntry的getPrivateKey/getCertificateChain,顺便导出 p12。
📎 源码在线查看:
script.js(PrivateKeyEntry hook)
场景
服务器校验客户端(双向 mTLS)时,App 从 KeyStore 取私钥做认证。r0capture 在它被调用时顺便把私钥+证书导出成 p12,供逆向分析或中间人代理复用。
两个 hook 点
javascript
// 1. 取私钥时
Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function () {
var result = this.getPrivateKey()
var packageName = Java.use("android.app.ActivityThread").currentApplication()
.getApplicationContext().getPackageName();
storeP12(this.getPrivateKey(), this.getCertificate(),
'/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue');
var message = {};
message["function"] = "dumpClinetCertificate=>...pwd: r0ysue";
message["stack"] = Java.use("android.util.Log").getStackTraceString(
Java.use("java.lang.Throwable").$new());
var data = Memory.alloc(1);
send(message, Memory.readByteArray(data, 1)) // 1字节占位
return result;
}
// 2. 取证书链时(逻辑对称)
Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function () { ... }流程
为什么 data 是 1 字节
javascript
var data = Memory.alloc(1);
send(message, Memory.readByteArray(data, 1))Python 端 on_message 约定:data 长度 == 1 是「事件通知」,只打印 function + stack,不走流量 / pcap 逻辑。证书导出是事件而非流量,故用 1 字节占位触发事件分支。
事件 message 内容
function: "dumpClinetCertificate=>/sdcard/Download/com.app_a3F9c2D1b7.p12 pwd: r0ysue"
stack: "at com.app.net.Client.getCert(Client.java:42)\n..."function字段含导出路径 + 密码,直接告诉你去哪取stack标明 App 哪里在用客户端证书
取包名
javascript
var packageName = Java.use("android.app.ActivityThread")
.currentApplication()
.getApplicationContext()
.getPackageName();通过 ActivityThread.currentApplication() 反射拿当前应用上下文,再取包名,作为 p12 文件名前缀。
必须用 Spawn 模式
证书导出 hook 必须在 App 第一次取私钥之前就位。Attach 太晚,证书可能已被取走,hook 不到。详见 客户端证书导出指南。
同一证书多次导出
getPrivateKey 和 getCertificateChain 都导出,且同一证书可能被多次取——每次都导出一份独立的 p12(文件名带随机串不覆盖),均可用。