SSL_read hook
📥 抓 HTTPS 响应明文(服务端 → 本端)的核心 hook。
📎 源码在线查看:
script.js#L277-L303(SSL_read hook)
SSL_read 原型
c
int SSL_read(SSL *ssl, void *buf, int num);
// 从 SSL 连接读取已解密的明文数据到 buf,返回读取字节数hook 实现
javascript
Interceptor.attach(addresses["SSL_read"], {
onEnter: function (args) {
// args[0]=ssl, args[1]=buf, args[2]=num
var message = getPortsAndAddresses(SSL_get_fd(args[0]), true);
message["ssl_session_id"] = getSslSessionId(args[0]);
message["function"] = "SSL_read";
message["stack"] = SSLstackread; // Conscrypt SSLInputStream.read 时填入
this.message = message;
this.buf = args[1];
},
onLeave: function (retval) {
retval |= 0;
if (retval <= 0) {
return;
}
send(this.message, this.buf.readByteArray(retval));
}
});onEnter:记录上下文
SSL_get_fd(args[0]):从 ssl 对象拿底层 socket fdgetPortsAndAddresses(fd, true):isRead=true表示收,src 取对端getSslSessionId(args[0]):会话 IDSSLstackread:由 ConscryptSSLInputStream.readhook 时填入的 Java 调用栈this.buf = args[1]:保存 buf 指针,onLeave 时从它读出明文
onLeave:读取并回传
javascript
onLeave: function (retval) {
retval |= 0; // Cast to 32-bit integer
if (retval <= 0) { // 0 或负数 = 无数据/出错
return;
}
send(this.message, this.buf.readByteArray(retval));
}为什么在 onLeave 才 send
SSL_read 的明文是函数执行后才写入 buf 的,且实际长度由返回值 retval 决定(可能小于请求的 num)。所以必须等 onLeave:
retval |= 0:转 32 位整数(Frida 的 retval 可能是 NativePointer)retval <= 0:返回 0 或负数表示无数据 / 出错,跳过this.buf.readByteArray(retval):从 buf 读 retval 字节明文send(message, data):连同 message 回传 Python
与 SSL_write 的对比
| 维度 | SSL_read | SSL_write |
|---|---|---|
| 方向 | 服务端 → 本端(响应) | 本端 → 服务端(请求) |
| 明文可见时机 | onLeave(执行后写 buf) | onEnter(args[1] 现成) |
| send 位置 | onLeave | onEnter |
| isRead 参数 | true | false |
| stack 全局 | SSLstackread | SSLstackwrite |