本帖最后由 lichuntian00 于 2026-6-5 08:53 编辑
工具链:google px4真机、proxypin 1.2.7、IDA 9.0。
某海外社交app。
先hook下connect函数,看下app使用的网络请求库有哪些。
。
可以看到网络请求是从libsscronet,so发起的,打开ida分析。
原来使用的是Cronet网络请求库。
源码地址在 https://chromium.googlesource.com/chromium/src/+/main/components/cronet/。
net/socket/ssl_client_socket_impl.cc文件的 SSL_CTX_set_custom_verify是关键函数。
这里要注意,不是劫持该函数的返回值(在这里把返回值2改成0的没有用),而是修改它的第3个参数,证书验证回调。
Certificate is already set andVerifyCertChain has begun 这是特征字符串。
交叉引用定位到sub_2A0464,这个函数是VerifyCertChain的实现。
继续交叉引用。
是虚函数表。
那么,这里不能直接交叉引用找了,估计是BLR X8间接调用了。
太多了,动态hook吧。
0x488570
这里发起的间接调用。
反汇编。
。
。
这一行对应的是BLR X8间接调用。
在函数sub_4884C4内调用的这个函数就是从ssl对象中调用证书链验证器。
交叉引用到。
sub_4899AC对应。
是tls_handshaker的verifycallback状态机。
注意这是quic验证的路径交叉引用。
到
这个就是传递给SSL_CTX_set_custom_verify的第三个参数,把返回值改了就行。
先返回2,再返回0。
先异步,再成功。
这是quic的证书验证链。
我开启Proxypin代理的时候,发现这一整条链路根本没被调用,走的是另一条verifycallback,也就是tcp 证书验证链。
继续交叉引用。
这个函数sub_41C7C0 对应的是quic证书验证回调。
继续交叉引用。
这里对应的是tcp验证链,所以要hook 2C7104返回0。
抓包看看。
抓包脚本。
[JavaScript] 纯文本查看 复制代码 'use strict';
const LIB_NAME = "libsscronet.so";
const OFF_SUB_2C7104 = 0x2C7104;
let hooked = false;
function info(addr) {
try {
const p = ptr(addr);
const m = Process.findModuleByAddress(p);
if (!m) {
return p + " <unknown> " + DebugSymbol.fromAddress(p);
}
return p + " " + m.name + "!" + p.sub(m.base) + " " + DebugSymbol.fromAddress(p);
} catch (e) {
return addr + " <bad>";
}
}
function hookSub2C7104() {
if (hooked) {
return;
}
const base = Module.findBaseAddress(LIB_NAME);
if (!base) {
return;
}
hooked = true;
const target = base.add(OFF_SUB_2C7104);
console.log("[+] " + LIB_NAME + " base =", info(base));
console.log("[+] hook sub_2C7104 =", info(target));
Interceptor.attach(target, {
onEnter: function (args) {
this.tid = Process.getCurrentThreadId();
console.log("\n[+] sub_2C7104 ENTER");
console.log(" tid =", this.tid);
console.log(" arg0/ssl =", args[0]);
console.log(" arg1/alert=", args[1]);
console.log(" lr =", info(this.context.lr));
},
onLeave: function (retval) {
console.log("[+] sub_2C7104 LEAVE");
console.log(" tid =", this.tid);
console.log(" original retval =", retval);
retval.replace(0);
console.log(" patched retval = 0");
}
});
}
function hookDlopen() {
hookSub2C7104();
const android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
if (!android_dlopen_ext) {
console.log("[-] android_dlopen_ext not found");
return;
}
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
this.match = false;
try {
if (args[0]) {
const path = args[0].readCString();
if (path.indexOf(LIB_NAME) >= 0) {
this.match = true;
console.log("[+] dlopen:", path);
}
}
} catch (e) {}
},
onLeave: function (retval) {
if (this.match && !retval.isNull()) {
hookSub2C7104();
}
}
});
}
setImmediate(hookDlopen);
任何版本抓包。
[JavaScript] 纯文本查看 复制代码 setImmediate(function () {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), {
onEnter: function (args) {
this.soPath = args[0].readCString();
},
onLeave: function (retval) {
if (this.soPath && this.soPath.indexOf("libsscronet.so") >= 0) {
var addr = Module.findExportByName("libsscronet.so", "SSL_CTX_set_custom_verify");
Interceptor.attach(addr, {
onEnter: function (args) {
Interceptor.attach(args[2], {
onLeave: function (retval) {
retval.replace(0);
}
});
}
});
}
}
});
});
|