在对移动端 App 进行数据抓包时,常会遇到以下问题:
-
配置了代理后,App 提示**"网络连接不可用"**,Charles 中出现大量红锁或
SSLHandshake握手失败。 -
配置了代理后,网页能正常抓包,但目标 App 的流量完全不在 Charles 中显示。
-
针对 Flutter 开发的 App,普通的系统证书信任和代理设置完全无效。
本教程将深入剖析这些防御机制的原理,并提供从易到难的终极解决方案。
目录
-
[突破 SSL Pinning(证书绑定)](#突破 SSL Pinning(证书绑定) "#2-%E7%AA%81%E7%A0%B4-ssl-pinning%E8%AF%81%E4%B9%A6%E7%BB%91%E5%AE%9A")
-
[突破 Flutter 应用的证书校验限制](#突破 Flutter 应用的证书校验限制 "#3-%E7%AA%81%E7%A0%B4-flutter-%E5%BA%94%E7%94%A8%E7%9A%84%E8%AF%81%E4%B9%A6%E6%A0%A1%E9%AA%8C%E9%99%90%E5%88%B6")
1. 突破"绕过系统代理/直连"防御
1.1 防御原理
许多 App 在网络请求代码中,显式将代理设置为了 NO_PROXY(直连模式),或者在 iOS 中通过底层 API 绕过了系统全局 Wi-Fi 代理设置。这导致即使你在手机 Wi-Fi 属性中配置了 Charles 的 IP 和端口,App 的流量依然会直接发送到服务器,不经过 Charles。
1.2 解决方案:VPN 虚拟网卡强力劫持
既然 App 能够绕过 Wi-Fi 的 HTTP 代理,我们可以在手机本地建立一个虚拟的 VPN 通道 。VPN 通道工作在网络层,会强行接管手机内的所有 TCP/UDP 流量,并将其打包转发给 PC 端的 Charles,使 App 无法通过 NO_PROXY 绕过。
🛠️ iOS 设备配置步骤(推荐 Shadowrocket)
-
在 iPhone 上安装 Shadowrocket(小火箭) 或 Surge / Loon。
-
打开小火箭,点击右上角
+添加服务器。 -
配置参数:
* 类型 (Type) :选择 HTTP 或 SOCKS5。
* 服务器 (Address):填写电脑的局域网 IP 地址。
* 端口 (Port) :填写 Charles 的端口(默认 8888)。
-
保存配置,在小火箭主页下方将 全局路由 (Global Routing) 修改为
代理 (Proxy)。 -
开启小火箭的连接开关,允许系统创建 VPN。
-
此时,App 的流量就会被强制导入 Charles。
🛠️ Android 设备配置步骤(推荐 Postern)
-
在安卓手机上安装 Postern 软件。
-
打开 Postern,点击
配置代理服务器,添加你的 PC 端 Charles IP 与端口8888,类型选择SOCKS5并保存。 -
点击
配置规则,添加一条规则:类型选择匹配所有,动作选择通过代理连接,代理服务器选择刚刚添加的配置,保存。 -
开启 Postern 的 VPN 代理服务即可。
2. 突破 SSL Pinning(证书绑定)
2.1 防御原理
SSL Pinning(证书绑定) 是一种高强度的安全校验机制。App 在发起 HTTPS 请求时,只信任其二进制包内预埋的特定证书公钥。当它检测到当前连接使用的是 Charles 自签名的根证书时,出于防中间人攻击的目的,会立即切断网络连接。
2.2 解决方案一:使用 Root / 越狱插件(一键全局绕过)
如果你的手机已经具备 Root 权限(Android)或越狱状态(iOS),使用现成的系统级 Hook 插件是最稳妥、快捷的方式。
🛠️ iOS 越狱机配置:
-
打开 Cydia 或 Sileo,添加主流源,搜索并安装插件
SSL Kill Switch 2。 -
安装后,进入 iPhone
设置-> 找到SSL Kill Switch 2-> 开启Disable Certificate Validation开关。 -
该插件会自动 Hook 系统底层的
Security.framework,强制使所有 App 的安全校验逻辑失效。
🛠️ Android Root 机配置:
-
确保手机已通过 Magisk 刷入 LSPosed 框架。
-
下载并安装模块
JustTrustMe(老系统推荐)或TrustMeAlready(支持高版本 Android)。 -
在 LSPosed 控制台中激活该模块,并勾选目标 App。
-
重启目标 App,其内置的证书校验逻辑就会被自动绕过。
2.3 解决方案二:使用 Frida + Objection 动态插桩(免越狱/Root 首选)
如果你使用的是未越狱的设备,或者不想安装系统插件,可以通过 Frida 在内存中临时将验证函数"修改"掉。
bash
# 1. 确保 PC 和手机已搭建好 Frida 环境,且运行了 frida-server。
# 2. 在 PC 终端中通过 objection 注入启动目标 App
objection -g "你的目标应用显示名称" explore
进入 Objection 交互式控制台后,输入以下命令即可动态绕过证书绑定:
bash
# 对于 iOS 设备:
ios sslpinning disable
# 对于 Android 设备:
android sslpinning disable
(objection 会自动寻找常用的 AFNetworking、Alamofire、TrustKit 等库,并在内存中 Hook 它们的验证函数使其强行返回成功)
3. 突破 Flutter 应用的证书校验限制
3.1 防御原理
Flutter 应用具有极高抓包难度的原因在于其完全不走系统的网络协议栈。Flutter 拥有独立编译的二进制 Dart 虚拟机,其内部的加密库(BoringSSL)并不使用 iOS/Android 系统的根证书信任列表,并且忽略系统配置的全局代理。
3.2 解决方案:Frida 强制 Hook BoringSSL 校验函数
既然 Flutter 运行在其自带的 C/C++ 动态链接库(libflutter.so 或 App.framework)中,我们就需要使用 Frida 对其底层的汇编函数进行硬核 Hook。
🛠️ 实战步骤:
- 保存以下通用的 Flutter 绕过 JavaScript 脚本为
flutter_bypass.js:
javascript
// 通用 Flutter SSL Pinning 绕过脚本片段
// 原理:定位 libflutter.so/App.framework 中的 ssl_crypto_x509_session_verify_cert_chain 并强制返回 1 (Success)
function bypassFlutter() {
var m = Process.findModuleByName("libflutter.so") || Process.findModuleByName("App");
if (!m) {
console.log("[!] 未找到 Flutter 引擎模块");
return;
}
console.log("[*] 找到 Flutter 引擎基址: " + m.base);
// 不同的 Flutter 版本,其底层函数的特征码有所不同
// 以下脚本会利用内存特征码搜索定位 verify_cert_chain 校验函数
Memory.scan(m.base, m.size, "ff 03 05 d1 fc 6f 0f a9 f8 5f 10 a9 f6 57 11 a9", {
onMatch: function (address, size) {
console.log("[*] 成功定位 Flutter SSL 校验函数地址: " + address);
Interceptor.attach(address, {
onLeave: function (retval) {
console.log("[+] 绕过 Flutter 证书链验证,强制返回成功");
retval.replace(1); // 强制返回 1 (Verify Success)
}
});
},
onComplete: function () {
console.log("[*] 内存特征扫描结束");
}
});
}
Java.perform(function () {
bypassFlutter();
});
- 配合 VPN 劫持工具(如 Shadowrocket 转发到电脑),并使用以下命令启动注入:
```bash
frida -U -f <包名> -l flutter_bypass.js
```
4. 高级排查决策树(如何选择方案)
当你面对一个新 App,不知道它采用了何种防御手段时,请按照以下"决策树"步骤逐步推进:
通过以上系统化的层层排查,配合局域网环境及代理接管,可以突破市面上 99% 移动端应用的抓包限制。