使用Frida绕过Android SSL Pinning:从原理到实战抓包分析

发布时间:2026/7/27 10:45:29
使用Frida绕过Android SSL Pinning:从原理到实战抓包分析 1. 项目概述为什么我们要挑战SSL Pinning在移动安全测试和逆向分析领域SSL Pinning证书绑定就像一道坚固的城门它阻止了中间人攻击也让我们这些安全研究员和分析师在尝试理解应用网络通信时感到头疼。简单来说它让App只信任自己“认识”的特定证书而不是系统信任的根证书。这意味着即使你在设备上安装了Burp Suite或Charles的CA证书试图拦截HTTPS流量App也会直接报错连接中断。我最近在分析一个主流电商平台的APK时就正面撞上了这堵墙。它的网络请求全部加密常规抓包工具束手无策这正是SSL Pinning的典型表现。这个项目的核心目标就是使用动态插桩工具Frida来“解锁”或绕过这个电商APK的SSL Pinning防御机制。这不是为了进行恶意攻击而是出于安全研究、漏洞挖掘、协议分析或兼容性测试等正当目的。通过这个过程我们不仅能成功抓取到明文的网络请求和响应更能深入理解现代Android应用是如何实现和加固其网络通信安全的。对于移动安全工程师、逆向工程师以及对应用底层机制感兴趣的高级开发者来说掌握这套方法是一项非常实用的技能。接下来我将以这个电商APK为例完整拆解从环境准备、原理分析、脚本编写到实战操作的全过程并分享我踩过的坑和总结出的技巧。2. 核心原理与Frida工具链解析2.1 SSL Pinning的实现机制与常见位置要绕过防御首先得知道防御是怎么建立的。SSL Pinning的核心思想是“固定信任”。在TLS/SSL握手过程中服务器会出示它的证书链。普通客户端会检查证书是否由系统信任的根证书颁发机构签发。而启用了Pinning的客户端会额外检查服务器证书的“指纹”是否与自己预先存储的“指纹”匹配。这个指纹通常是证书公钥的哈希值如SHA-256。在Android中实现方式主要有以下几种网络安全性配置在res/xml/network_security_config.xml中声明pin-set。这是Google推荐的方式配置清晰但反编译后容易定位。OkHttp的CertificatePinner这是目前最主流的方式。开发者直接在代码中创建CertificatePinner对象为特定域名添加公钥哈希Pin。Apache HttpClient或自定义TrustManager通过重写X509TrustManager接口的方法在checkServerTrusted方法中加入自定义的证书验证逻辑。Native层验证将验证逻辑放在C/C编写的JNI本地库中增加逆向难度。我们的目标电商APK经过初步静态分析发现它主要使用了OkHttp3库并且在关键的网络请求模块中找到了CertificatePinner.Builder()的调用痕迹。这意味着我们的主攻方向是OkHttp的Pinning机制。2.2 Frida的工作原理动态插桩的艺术Frida之所以强大在于它采用了“注入”技术。它通过一个运行在目标进程中的“注入式”引擎Gum将一段JavaScript代码注入到目标App的运行时内存中。这段JS代码能够访问和操作该进程的所有内存空间、Java/ART虚拟机对象以及Native函数。具体到绕过SSL Pinning我们的思路是找到负责证书验证的关键类和方法然后在它们执行时通过Frida“劫持”这些方法修改其逻辑或返回值使其总是返回“验证成功”。例如让CertificatePinner.check()方法直接跳过验证或者让TrustManager.checkServerTrusted()方法不做任何抛出异常的操作。Frida提供了两种主要工作模式frida-server在已Root的Android设备上运行功能最全和frida-gadget将库打包进APK无需Root。为了通用性和便利性本项目将基于已Root的设备或模拟器进行使用frida-server模式。2.3 工具选型与环境清单工欲善其事必先利其器。以下是完成本次实战所需的核心工具及其作用Frida核心动态插桩工具。包括PC端的frida、frida-tools和运行在手机端的frida-server。Android设备/模拟器需要Root权限。推荐使用官方Android Studio自带的x86_64架构模拟器并刷入SuperSU或Magisk进行Root。真机Root风险较高模拟器更安全便捷。ADBAndroid调试桥用于连接设备、推送文件、执行Shell命令。反编译工具用于静态分析定位关键代码。Apktool反编译APK获取Smali代码和资源文件。Jadx-GUI将Dex文件反编译为可读性更高的Java代码是寻找突破口的首选工具。抓包工具用于验证绕过是否成功。Burp Suite Professional / Community Edition功能强大的抓包和渗透测试平台。Charles Proxy另一款优秀的HTTP代理工具。代码编辑器用于编写和调试Frida JavaScript脚本如VS Code。注意所有操作请在你自己拥有完全控制权的设备或应用上进行严格遵守法律法规仅用于安全学习和研究。3. 前期准备环境搭建与目标分析3.1 Frida环境部署详解首先在PC端安装Frida。建议使用Python虚拟环境避免包冲突。pip install frida-tools安装后通过frida --version确认安装成功。接下来是重头戏部署frida-server到Android设备。确定架构通过ADB连接设备执行adb shell getprop ro.product.cpu.abi。模拟器通常是x86_64真机多为arm64-v8a。下载对应版本前往Frida的GitHub Releases页面下载与你的PC端Frida版本号完全相同的frida-server。例如PC端是16.1.4就下载frida-server-16.1.4-android-x86_64.xz。推送并启动# 解压下载的.xz文件 xz -d frida-server-16.1.4-android-x86_64.xz # 推送至设备 adb push frida-server-16.1.4-android-x86_64 /data/local/tmp/ # 赋予可执行权限 adb shell chmod 755 /data/local/tmp/frida-server-16.1.4-android-x86_64 # 切换到后台运行 adb shell /data/local/tmp/frida-server-16.1.4-android-x86_64 验证连接在PC端执行frida-ps -U如果能看到设备上运行的进程列表恭喜你环境通了。3.2 目标APK的静态分析与突破口寻找将电商APK文件拖入Jadx-GUI。我们的目标是找到证书验证相关的代码。全局搜索在Jadx中按下CtrlShiftF搜索关键词如CertificatePinner、pin、checkServerTrusted、X509TrustManager。定位网络库查看“资源”树寻找okhttp3、okio等库的引用。在代码中搜索OkHttpClient.Builder()找到构建HTTP客户端的地方。分析关键代码在我们的目标APK中搜索CertificatePinner后找到了一个名为NetworkSecurityModule的类其中有一个方法buildHttpClient()。关键代码如下public OkHttpClient buildHttpClient() { CertificatePinner pinner new CertificatePinner.Builder() .add(api.myecommerce.com, sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) // 示例Pin .add(static.myecommerce.com, sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB) .build(); return new OkHttpClient.Builder() .certificatePinner(pinner) .connectTimeout(30, TimeUnit.SECONDS) .build(); }这里明确显示了为两个域名绑定了证书Pin。我们的Frida脚本就需要让这个pinner的检查失效。寻找备用方案同时我们也搜索了TrustManager发现另一个工具类中有自定义的MyTrustManager。这说明应用可能有多重验证我们需要准备多个脚本来应对。3.3 抓包环境配置为了验证Frida脚本的效果需要先配置好抓包环境即使目前会因为Pinning而失败。配置Burp Suite启动Burp在Proxy-Options中确保代理监听在8080端口例如127.0.0.1:8080。导出CA证书在浏览器中访问http://burp下载cacert.der证书将其转换为PEM格式openssl x509 -inform DER -in cacert.der -out cacert.pem并计算其SHA-256指纹备用openssl x509 -inform PEM -subject_hash_sha256 -in cacert.pem | head -1。这个指纹可能在后续脚本中用到。设备代理设置在Android设备的Wi-Fi设置中配置手动代理指向运行Burp的PC的IP地址和8080端口。安装CA证书将cacert.der文件推送到设备并在系统设置-安全-加密与凭据中从SD卡安装该证书。对于Android 7.0及以上还需要将证书安装到用户凭据而非系统凭据并且很多App默认不信任用户安装的证书这正是我们需要绕过Pinning的原因。此时打开目标电商App任何网络请求都会在Burp中显示TLS握手失败这正是SSL Pinning在起作用。4. Frida脚本编写与核心Hook点实战4.1 通用型脚本禁用所有SSL验证这是一个“简单粗暴”但往往有效的方案它试图在更底层禁用SSL验证。我们创建一个名为disable_ssl.js的脚本。Java.perform(function() { console.log([*] Starting SSL Pinning Bypass...); // Hook SSLContext.init 方法尝试替换 TrustManager var SSLContext Java.use(javax.net.ssl.SSLContext); SSLContext.init.overload([Ljavax.net.ssl.KeyManager;, [Ljavax.net.ssl.TrustManager;, java.security.SecureRandom).implementation function(keyManagers, trustManagers, secureRandom) { console.log([] SSLContext.init() hooked.); // 创建一个什么都不做信任所有证书的 TrustManager var TrustAllManager Java.registerClass({ name: com.bypass.TrustAllManager, implements: [Java.use(javax.net.ssl.X509TrustManager)], methods: { checkClientTrusted: function(chain, authType) { console.log([] TrustAllManager: checkClientTrusted called (Ignored)); }, checkServerTrusted: function(chain, authType) { console.log([] TrustAllManager: checkServerTrusted called (Ignored)); }, getAcceptedIssuers: function() { return []; } } }); var newTrustManagers [TrustAllManager.$new()]; return this.init(keyManagers, newTrustManagers, secureRandom); }; // Hook OkHttp 的 CertificatePinner try { var CertificatePinner Java.use(okhttp3.CertificatePinner); CertificatePinner.check.overload(java.lang.String, java.util.List).implementation function(hostname, pins) { console.log([] CertificatePinner.check() bypassed for: hostname); // 直接返回不执行任何检查 return; }; console.log([*] OkHttp CertificatePinner hook installed.); } catch (err) { console.log([-] OkHttp CertificatePinner not found: err.message); } // Hook Apache HttpClient的 TrustManager (备用) try { var X509TrustManager Java.use(org.apache.http.conn.ssl.X509TrustManager); X509TrustManager.checkServerTrusted.implementation function(chain, authType) { console.log([] Apache X509TrustManager.checkServerTrusted bypassed.); }; console.log([*] Apache HttpClient TrustManager hook installed.); } catch (err) { // 忽略可能未使用此库 } });这个脚本尝试了三个层面的Hook替换全局的SSLContext初始化时使用的TrustManager。直接让OkHttp的CertificatePinner.check()方法空跑。备用Hook Apache的信任管理器。实操心得这种“广撒网”的脚本在对付一些使用标准库的应用时可能有效但对于加固严重或使用非标准实现的应用往往需要更精准的打击。4.2 精准打击脚本针对特定OkHttpClient实例根据静态分析我们找到了具体的NetworkSecurityModule类。更精准的做法是Hook这个类的方法直接返回一个没有设置CertificatePinner的OkHttpClient。创建bypass_okhttp.js。Java.perform(function() { console.log([*] Targeting specific OkHttpClient builder...); // 定位我们找到的类 var NetworkSecurityModule Java.use(com.myecommerce.core.network.NetworkSecurityModule); NetworkSecurityModule.buildHttpClient.implementation function() { console.log([] Hooked buildHttpClient()!); // 调用原方法获取原始的OkHttpClient.Builder var originalBuilder this.buildHttpClient(); // 但是我们无法直接从一个构建好的Client中移除Pinner。 // 更优的策略是Hook OkHttpClient.Builder的certificatePinner方法使其传入一个空的pinner // 或者直接返回一个全新的、未设置pinner的Builder构建的Client // 方案创建一个新的Builder复制除pinner外的其他配置这里简化处理实际可能需要复制更多配置 console.log([] Returning a new OkHttpClient without CertificatePinner.); var OkHttpClient Java.use(okhttp3.OkHttpClient); var newClient new OkHttpClient.Builder() .connectTimeout(30, Java.use(java.util.concurrent.TimeUnit).SECONDS) .readTimeout(30, Java.use(java.util.concurrent.TimeUnit).SECONDS) .build(); return newClient; }; // 另一种更底层的方法直接让CertificatePinner.Builder的add方法失效 var CertificatePinnerBuilder Java.use(okhttp3.CertificatePinner$Builder); CertificatePinnerBuilder.add.overload(java.lang.String, java.lang.String).implementation function(pattern, pin) { console.log([] Neutralizing pin for pattern: pattern); // 调用原方法但传入一个无效的或空的pin这可能会出错。 // 更好的办法直接返回this但让后续的build()返回一个不执行检查的pinner // 这里我们选择调用原方法但记录日志后续通过hook check方法绕过。 return this.add(pattern, sha256/); // 传入一个明显错误的pin原逻辑可能会在build时报错但check已被我们禁用。 }; });这个脚本展示了两种思路一是替换整个OkHttpClient的构建结果二是在Pin被添加时就进行干扰。第一种方法在实战中更可靠但需要你清楚原始Client的所有配置超时、拦截器等否则可能导致应用功能异常。第二种方法风险在于如果应用在构建后立即验证Pin的格式可能会导致崩溃。4.3 应对加固与反调试进阶技巧一些应用会检测Frida的存在。常见的检测手段包括检查frida-server相关端口、进程名、文件特征或检测ptrace跟踪。我们的脚本也需要进行一些“反反调试”。端口检测绕过Frida默认使用27042端口。应用可能尝试连接这个端口。我们可以使用Frida的-l参数指定监听其他端口或者在脚本中Hook检测逻辑。frida -U -f com.myecommerce.app -l bypass_okhttp.js --listen 127.0.0.1:8088在脚本中可以尝试Hook常见的检测函数// 示例Hook 检查本地端口连接的函数 var InetAddress Java.use(java.net.InetAddress); InetAddress.isReachable.overload(int).implementation function(timeout) { var hostname this.getHostName(); if (hostname.indexOf(127.0.0.1) ! -1) { console.log([] Blocking reachability check to localhost: hostname); return false; // 让检测超时 } return this.isReachable(timeout); };文件与进程检测Hookjava.io.File的exists()、listFiles()方法或android.os.Process的/proc/self/status读取逻辑过滤掉与frida相关的信息。延时注入有些应用在启动时有强烈的反调试。我们可以先启动应用稍后再附加Frida。# 启动应用 adb shell am start -n com.myecommerce.app/.MainActivity # 等待几秒后附加 frida -U com.myecommerce.app -l bypass_okhttp.js使用Gadget模式对于无法Root的设备可以将frida-gadget库打包进APK。这需要反编译APK在lib目录添加对应架构的libfrida-gadget.so并修改AndroidManifest.xml或smali代码在应用启动时加载它。这个过程更复杂但对抗性更强。注意事项反反调试是一场猫鼠游戏。过于激进的反制可能破坏应用稳定性。在安全测试中优先考虑在修改过的、已去除反调试代码的测试版本APK上进行效率更高。5. 实战操作流程与验证5.1 完整操作步骤假设我们已经准备好了Root的Android模拟器IP:127.0.0.1:7555frida-server已在模拟器中运行。目标APKmyecommerce.apk已安装。Burp Suite代理已配置好PC IP:192.168.1.100:8080。步骤一启动应用并附加Frida# 连接到模拟器 adb connect 127.0.0.1:7555 # 强制停止目标应用确保干净状态 adb shell am force-stop com.myecommerce.app # 以挂起方式启动应用便于Frida在早期注入如果需要 adb shell am start -D -n com.myecommerce.app/.MainActivity # 使用Frida附加进程并注入我们的脚本 frida -U -f com.myecommerce.app -l .\disable_ssl.js --no-pause如果--no-pause参数无效或应用卡住可以不用-D启动直接启动应用后附加adb shell am start -n com.myecommerce.app/.MainActivity frida -U com.myecommerce.app -l .\disable_ssl.js步骤二观察Frida输出如果脚本注入成功你会在Frida控制台看到类似输出[*] Starting SSL Pinning Bypass... [*] OkHttp CertificatePinner hook installed. [] CertificatePinner.check() bypassed for: api.myecommerce.com [] TrustAllManager: checkServerTrusted called (Ignored)这表明Hook点已成功触发。步骤三触发网络请求并验证抓包在设备上操作电商App进行登录、浏览商品等操作。此时回到Burp Suite的Proxy-HTTP history选项卡。你应该能看到之前失败的TLS handshake failed消息变成了具体的HTTP/HTTPS请求和响应请求参数、响应体包括可能的JSON数据、登录token等都清晰可见。步骤四切换或组合脚本如果通用脚本无效则使用我们针对目标APK编写的精准脚本。# 先退出之前的Frida会话 (CtrlC) frida -U com.myecommerce.app -l .\bypass_okhttp.js再次触发网络请求观察Burp和Frida输出。5.2 验证成功的关键标志Burp Suite成功拦截HTTPS请求显示为明文状态码为200并能查看完整的请求/响应报文。Frida日志输出控制台打印出我们预设的Hook成功信息如[] CertificatePinner.check() bypassed for: ...。应用功能正常App没有崩溃网络操作登录、加载列表、提交订单等能正常完成。这说明我们的绕过没有破坏核心逻辑。对比验证关闭Frida重启App抓包立刻失败再次证明是Frida脚本起了作用。6. 常见问题排查与深度优化6.1 问题排查速查表问题现象可能原因排查步骤与解决方案frida-ps -U无输出或报错1.frida-server未运行或已退出。2. 设备未Root或ADB无Root权限。3. PC与设备Frida版本不匹配。1. 重新执行adb shell进入检查进程ps | grep frida若无则重新启动。2. 确认设备已Root尝试adb root。3. 使用frida --version和adb shell /data/local/tmp/frida-server --version核对版本。脚本注入失败提示TypeError: cannot read property perform of undefined目标进程可能尚未完成Java环境初始化。1. 使用setTimeout延迟执行Java.perform。2. 改用frida -U -f在应用启动时注入而非附加。Hook成功但抓包仍失败1. Hook点不正确或遗漏。2. 应用使用了自定义的SSL库或Native层验证。3. 代理设置问题或CA证书未正确安装。1. 在Jadx中搜索所有SSL、Trust、X509相关类扩大Hook范围。2. 使用frida-trace追踪libssl.so或libcrypto.so中的相关函数如SSL_CTX_set_cert_verify_callback。3. 检查设备Wi-Fi代理和Burp监听设置确保Burp的CA证书已安装到“用户凭据”。应用启动后立刻崩溃1. 脚本逻辑错误导致关键异常。2. 触发了应用的反调试或反Hook机制。1. 简化脚本逐个Hook点测试定位崩溃代码行。2. 尝试先不注入任何脚本确认应用本身是否稳定。3. 实施反反调试措施或寻找应用的反调试代码并绕过。能看到HTTP请求但响应体为空或乱码1. 数据可能被Gzip压缩。2. 使用了非HTTP协议如WebSocket、gRPC。1. 在Burp的Proxy-Options-Response Modification中禁用“解压压缩内容”。2. 使用Burp的“Logger”或“WebSockets”历史记录查看。对于gRPC需要专用解码器。6.2 脚本优化与高级Hook技巧条件性Hook避免无差别Hook所有实例减少性能开销和冲突风险。OkHttpClient.Builder.certificatePinner.implementation function(pinner) { var stack Java.use(android.util.Log).getStackTraceString(Java.use(java.lang.Exception).$new()); // 只对来自特定类的方法调用进行绕过 if (stack.indexOf(com.myecommerce.core.network.NetworkSecurityModule) -1) { console.log([] Bypassing pinner in NetworkSecurityModule); return this; // 返回Builder本身实际上没有设置pinner } // 其他情况调用原方法 return this.certificatePinner(pinner); };Native层Hook如果验证在Native层需要使用Frida的Interceptor。// 假设验证函数在 libnative.so 中函数签名是 int verify_cert(void* cert) var libnative Module.findBaseAddress(libnative.so); if (libnative) { var verify_cert libnative.add(0x1234); // 替换为实际偏移量或导出函数名 Interceptor.attach(verify_cert, { onEnter: function(args) { console.log([] Native verify_cert called.); }, onLeave: function(retval) { // 强制返回验证成功 (假设成功码为1) console.log([] Forcing verify_cert to return success.); retval.replace(ptr(0x1)); } }); }获取函数地址可能需要结合objdump、readelf或IDA Pro进行逆向分析。自动化与脚本管理对于复杂的应用可能需要多个脚本组合。可以使用Frida的ScriptAPI动态加载和管理脚本或者使用像objection这样的基于Frida的自动化测试工具它内置了android sslpinning disable等命令可以快速尝试多种绕过方法。6.3 安全研究与合规提醒最后必须强调所有技术都应在合法合规的范围内使用。授权测试仅对你拥有书面授权测试的应用进行操作。本地环境最好在完全隔离的虚拟环境或专属测试设备中进行。目的正当技术研究、安全评估、教育学习是唯一正当的目的。尊重版权不要破解、分发或用于商业用途。绕过SSL Pinning就像拿到了一把打开加密通信盒子的钥匙它让你能看清里面的结构但目的是为了加固盒子而不是偷走里面的东西。通过这次对电商APK的实战我们不仅掌握了一套具体的技术方法更重要的是理解了Android应用安全机制的攻防思路。在实际操作中耐心分析、多角度尝试、仔细验证是成功的关键。每个应用都像一座独特的城堡没有一成不变的万能钥匙但有了Frida这套强大的“开锁工具”和正确的思路你总能找到进入其中、一探究竟的路径。