Frida Hook SSL Pinning抓包全攻略
Frida Hook SSL Pinning 实现抓包完整方案
SSL Pinning(证书绑定)是移动应用常用的安全机制,它通过将服务器证书硬编码或校验逻辑内置于客户端,来防止中间人攻击,这直接导致常规抓包工具(如Charles、Fiddler)无法捕获HTTPS流量。使用Frida进行动态Hook,可以绕过此校验机制,是实现抓包的有效技术手段。
1. 核心原理与Hook目标
SSL Pinning的校验逻辑通常存在于两个层面:Java/Android框架层和Native(C/C++)层。Frida可以同时对这两层进行Hook。
| 校验层次 | 常见Hook目标 | 作用与说明 |
|---|---|---|
| Java层 | TrustManager、HostnameVerifier、OkHttp的证书校验方法 | 拦截并修改Java层的证书验证逻辑,使其直接返回“信任”或“验证通过”。这是最通用和常见的方法。 |
| Native层 | libssl.so、libcrypto.so或应用自定义的libxxx.so(如libsscronet.so)中的函数(如SSL_CTX_set_custom_verify) | 当应用在Native层实现更底层的证书校验时,需要Hook相应的Native函数,修改其返回值(例如,将校验失败改为成功)。 |
2. 通用Java层Hook方案(以OkHttp为例)
大多数Android应用使用OkHttp或类似网络库,Hook其验证器是最快的方法。
步骤1:定位关键类与方法
使用逆向工具(如Jadx-GUI)分析目标APK,搜索关键词如:
X509TrustManagerHostnameVerifierCertificatePinnercheckServerTrusted
通常,找到的验证方法会返回void或boolean,我们的目标是让其不抛异常或返回true。
步骤2:编写Frida Hook脚本
以下脚本演示了如何Hook常见的TrustManager和HostnameVerifier:
// ssl_pinning_bypass.js Java.perform(function () { console.log("[*] 开始Hook SSL Pinning 相关方法..."); // 1. Hook TrustManager, 绕过证书链验证 var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager'); var X509ExtendedTrustManager = Java.use('javax.net.ssl.X509ExtendedTrustManager'); var TrustManagerHook = function() { return { // 客户端证书验证 - 直接置空,不执行任何操作 checkClientTrusted: function(chain, authType) { console.log("[+] Bypassing checkClientTrusted"); }, // 服务器证书验证 - 关键:置空以信任所有服务器证书 checkServerTrusted: function(chain, authType) { console.log("[+] Bypassing checkServerTrusted for authType: " + authType); }, getAcceptedIssuers: function() { return []; } }; }; // 替换应用的TrustManager实现 Java.choose('com.example.SomeClass', { onMatch: function(instance) { // 找到并替换实例中的TrustManager }, onComplete: function() {} }); // 更直接的方法:Hook所有实现的checkServerTrusted方法 var trustManagerClasses = [X509TrustManager, X509ExtendedTrustManager]; trustManagerClasses.forEach(function(clazz) { if (clazz.checkServerTrusted) { clazz.checkServerTrusted.implementation = function(chain, authType) { console.log("[+] Bypassed checkServerTrusted via Frida Hook"); return; // 不抛异常即表示信任 }; } }); // 2. Hook HostnameVerifier, 绕过主机名验证 var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier'); HostnameVerifier.verify.implementation = function(hostname, session) { console.log("[+] Bypassing HostnameVerifier for: " + hostname); return true; // 始终返回true,验证通过 }; // 3. Hook OkHttp的CertificatePinner (如果使用) try { var CertificatePinner = Java.use('okhttp3.CertificatePinner'); CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(pin, certs) { console.log("[+] Bypassing OkHttp CertificatePinner check for: " + pin); return; // 不执行任何检查 }; } catch (e) { console.log("[!] OkHttp CertificatePinner not found or hook failed: " + e); } console.log("[*] SSL Pinning Java层Hook完成!"); });步骤3:执行Hook脚本
将上述脚本保存为bypass.js,并通过以下命令注入到目标进程:
# 附加到已运行的应用 frida -U -f com.target.app -l bypass.js --no-pause # 或重新启动应用并注入 frida -U -f com.target.app -l bypass.js3. Native层Hook方案(针对深度校验)
对于在Native层(如libsscronet.so)实现校验的应用,需要Hook Native函数。
步骤1:定位Native校验函数
使用IDA Pro等工具分析应用的Native库(.so文件),寻找与SSL验证相关的函数,常见函数名包含verify、SSL_CTX、cert等关键词。
步骤2:编写Native Hook脚本
以下示例展示如何Hook一个假设的native_ssl_verify函数,以及如何监控特定SO库的加载以进行Hook:
// native_hook.js Java.perform(function () { console.log("[*] 准备Hook Native层SSL函数..."); // 方案A:拦截so库加载,并在其加载后立即Hook内部函数 var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); if (android_dlopen_ext) { Interceptor.attach(android_dlopen_ext, { onEnter: function(args) { var soName = args[0].readCString(); // 当目标so(如libsscronet.so)加载时 if (soName && soName.indexOf("libsscronet.so") !== -1) { console.log("[+] 目标SO加载: " + soName); this.targetSo = true; } }, onLeave: function(retval) { if (this.targetSo) { // 延迟执行,确保so完全加载 setTimeout(hookInTargetSo, 500); } } }); } function hookInTargetSo() { var libtarget = Module.findBaseAddress("libsscronet.so"); if (libtarget) { console.log("[+] libsscronet.so 基址: " + libtarget); // 假设通过逆向分析,找到关键函数偏移为 0x123456 var verifyFuncAddr = libtarget.add(0x123456); // Hook该函数,强制其返回0(表示校验成功) Interceptor.attach(verifyFuncAddr, { onEnter: function(args) { console.log("[+] Native SSL Verify 函数被调用"); }, onLeave: function(retval) { console.log("[+] 强制Native验证函数返回 0"); retval.replace(ptr(0)); // 替换返回值为0 } }); } } // 方案B:直接Hook标准libssl.so中的函数 (如果应用使用系统库) var sslVerify = Module.findExportByName("libssl.so", "SSL_verify_cert_chain"); if (sslVerify) { Interceptor.attach(sslVerify, { onLeave: function(retval) { console.log("[+] Bypassing SSL_verify_cert_chain, original ret: " + retval); retval.replace(ptr(1)); // 假设1表示成功 } }); } });4. 集成工具与自动化
为了提高效率,可以结合一些成熟的Frida脚本或工具:
- Objection:一个基于Frida的运行时移动探索工具,它内置了SSL Pinning绕过命令,可以一键尝试多种绕过方式。
objection -g com.target.app explore # 在 objection 会话中执行: android sslpinning disable - r0capture:一个专用于Android应用抓包的工具,集成了Frida脚本,能自动处理SSL Pinning并导出流量。
- JustTrustMe 模块 (Xposed):虽然这不是Frida方案,但思路类似。它是一个Xposed模块,通过Hook Android的证书验证API来全局禁用SSL Pinning。在具备Xposed环境的设备上安装即可,无需为每个应用单独编写脚本。
5. 流程与验证
- 环境准备:确保手机已root或为模拟器,并安装Frida-server,两边版本一致可以避免很多问题,如果使用objection,也需要版本匹配。
- 启动抓包代理:配置好Charles或Burp Suite(证书制作流程稍复杂点)的代理设置。
- 逆向分析:使用Jadx、IDA等工具快速定位应用的网络库和可能的校验点。
- 编写与注入脚本:根据分析结果,选择Java层或Native层脚本进行注入。
- 触发网络请求:操作应用,使其产生网络流量。
- 验证抓包:在Charles/Burp中查看HTTPS请求是否已被成功解密和捕获。若成功,将能看到明文的请求和响应数据。
注意事项:
- 稳定性:Hook Native函数可能导致应用崩溃,需谨慎操作。
- 对抗升级:应用可能更新校验逻辑,需要重新分析。
- 法律风险:此技术仅用于安全研究、测试自己拥有或已获授权的应用,严禁用于非法用途。
通过上述Frida Hook方案,你可以有效地绕过大多数APP的SSL Pinning机制,为安全分析、漏洞挖掘和协议研究铺平道路。
参考来源
- 如何解决APP抓包问题【网络安全】
- 某金融app的加解密hook+rpc+绕过SSLPinning抓包
- chatGPT与逆向的相遇,快速解决sslpinning抓包问题
- 实用FRIDA进阶:内存漫游、hook anywhere、抓包
