为什么装了 Burp 证书还是抓不到包
很多人第一次手机抓包都会卡在同一个地方:Burp Suite 的 CA 证书装进了手机,浏览器能抓到 HTTPS,但只要换成目标 App,Burp 里就只有一片 CONNECT 请求,没有任何明文。
这不是配置错了,而是 App 主动拒绝了你的证书。从 Android 7(API 24)开始,系统默认不再信任用户安装的 CA 证书,App 只认 /system/etc/security/cacerts 里的系统证书;再往上,大量 App 还会额外做一层 SSL Pinning(证书固定)——在代码里写死服务端证书的公钥或哈希,只要证书不匹配就直接断连,连系统证书都不认。
所以想在自己有权限的设备上做协议分析,绕过这两层限制是绕不开的基本功。本文把完整流程拆成可复现的步骤:环境准备 → frida-server 部署 → Burp 配置 → 三种绕过方案(objection 一键、通用 Frida 脚本、非 Java 层应对)→ 报错排查。
适用范围:你自己的设备、你自己的 App,或已获得书面授权的测试目标。用于分析第三方 App 的付费内容、破解会员、抓取他人数据,不在本文讨论范围内,也可能违反《网络安全法》与《刑法》第 285 条。
一、前置条件
- 一台已 Root 的 Android 设备:推荐 Magisk。KernelSU / APatch 也可以,但部分机型上 frida-server 的 SELinux 上下文需要额外处理。
- adb 可用:电脑上能
adb devices看到设备。 - frida 版本严格对齐:电脑端
frida(Python 包)与设备端frida-server必须版本号完全一致,差一个小版本就可能出现unable to connect to remote frida-server。这是新手最常踩的坑,先把这条记住。 - Burp Suite(社区版够用)或 mitmproxy。
- 目标 App 的包名,用
adb shell pm list packages | findstr 关键词查。
关于机型与 Root 方案的选择,站内另一篇《手机渗透选型指南:Kali NetHunter 完整版 VS Termux-X VS Kalidroid》对比过不同路线的取舍,思路同样适用于这里——先确认设备能稳定 Root,再谈抓包。
二、部署 frida-server
1. 确认设备架构
adb shell getprop ro.product.cpu.abi
常见结果:arm64-v8a(绝大多数现代手机)、armeabi-v7a(老设备)、x86_64(模拟器)。后面的包名后缀就按这个选。
2. 查看本机 frida 版本
pip show frida | findstr Version
# 或者
frida --version
假设输出 16.7.19,那么设备端就必须下 frida-server-16.7.19-android-arm64。
3. 下载并推送到设备
到 frida/frida Releases 找到对应版本的 frida-server-<版本>-android-<架构>.xz,解压得到一个无后缀的可执行文件,然后:
adb push frida-server /data/local/tmp/
adb shell "su -c 'chmod 755 /data/local/tmp/frida-server'"
adb shell "su -c '/data/local/tmp/frida-server &'"
如果设备上的 Magisk 管理着 /data/local/tmp 的 SELinux 上下文,直接运行可能报 Permission denied 或进程秒退。这时改用 Magisk 模块方式是更省心的选择:把 frida-server 放进模块的 system/bin/ 目录并写好 service.sh,开机自动以 root 启动,不用每次手动敲命令。
4. 验证连通性
frida-ps -U
能列出一屏进程名就说明通了。如果卡住或报错,先看文末的排查表。
三、配置 Burp Suite
- Burp → Proxy → Options,确认监听
0.0.0.0:8080,并勾选 Support invisible proxying(部分 App 不走系统代理时会用到)。 - 手机 Wi-Fi 设置里把代理指向电脑的局域网 IP + 8080。
- 浏览器访问
http://burp下载 CA 证书,重命名为cacert.der。 - 安装证书。注意:装成「用户证书」只在 Android 6 及以下管用,Android 7+ 必须让目标 App 认这张证书,见下一节。
如果你用的是 Kali NetHunter 手机直接抓包,Burp 可以跑在 chroot 里,代理指向 127.0.0.1,省掉局域网这一层。相关环境搭建可以参考《NetHunter 高级功能完全指南》。
四、方法一:objection 一键绕过(最快)
如果只是想快速验证一个 App 有没有做 Pinning,先用 objection 试,一条命令解决。
pip install objection
objection -g com.example.target explore
进入交互界面后执行:
android sslpinning disable
它会自动 Hook TrustManagerImpl、OkHttp CertificatePinner 等常见实现,并打印出被替换的类。看到 SSL Pinning disable 的成功日志后再去操作 App,Burp 里应该就有明文了。
局限:objection 覆盖的是常见 Java 实现,遇到自研校验、native 层校验或加固壳,基本无效。而且它会一并屏蔽一部分检测逻辑,容易让 App 行为异常。所以它适合「先探路」,真正的主力还是下面的手写脚本。
五、方法二:通用 Frida Hook 脚本(推荐)
1. 脚本全文
保存为 unpinning.js。这个脚本按「覆盖面优先」的思路写了四层 Hook:TrustManager、SSLContext、Conscrypt 的 TrustManagerImpl、OkHttp 的 CertificatePinner。每一层单独 try-catch,某一层不存在也不会影响其他层。
Java.perform(function () {
console.log("[*] unpinning script loaded");
// ---------- 第 1 层:替换 X509TrustManager ----------
try {
var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
var TrustManager = Java.registerClass({
name: "com.dsh.TrustAll",
implements: [X509TrustManager],
methods: {
checkClientTrusted: function (chain, authType) {},
checkServerTrusted: function (chain, authType) {},
getAcceptedIssuers: function () { return []; }
}
});
var TrustManagers = [TrustManager.$new()];
var SSLContext = Java.use("javax.net.ssl.SSLContext");
var init = SSLContext.init.overload(
"[Ljavax.net.ssl.KeyManager;",
"[Ljavax.net.ssl.TrustManager;",
"java.security.SecureRandom"
);
init.implementation = function (km, tm, sr) {
console.log("[+] SSLContext.init hooked");
return init.call(this, km, TrustManagers, sr);
};
} catch (e) {
console.log("[-] layer1 failed: " + e);
}
// ---------- 第 2 层:Conscrypt 的 TrustManagerImpl ----------
try {
var TMImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
TMImpl.verifyChain.implementation = function (
untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData
) {
console.log("[+] verifyChain bypass: " + host);
return untrustedChain;
};
TMImpl.checkTrustedRecursive.implementation = function (
certs, host, clientAuth, untrustedChain, trustAnchorChain, used
) {
console.log("[+] checkTrustedRecursive bypass: " + host);
return Java.use("java.util.ArrayList").$new();
};
} catch (e) {
console.log("[-] layer2 failed: " + e);
}
// ---------- 第 3 层:OkHttp 3 CertificatePinner ----------
try {
var Pinner = Java.use("okhttp3.CertificatePinner");
// 老版本 OkHttp
Pinner.check.overload("java.lang.String", "java.util.List").implementation = function (host, list) {
console.log("[+] CertificatePinner.check bypass: " + host);
return;
};
// 新版 OkHttp(Kotlin 内部方法)
try {
Pinner["check$okhttp"].overload("java.lang.String", "kotlin.jvm.functions.Function0").implementation = function (host, f) {
console.log("[+] check$okhttp bypass: " + host);
return;
};
} catch (e) {}
} catch (e) {
console.log("[-] layer3 failed: " + e);
}
// ---------- 第 4 层:HostnameVerifier 兜底 ----------
try {
var HostnameVerifier = Java.use("javax.net.ssl.HostnameVerifier");
var OkHostnameVerifier = Java.use("okhttp3.internal.tls.OkHostnameVerifier");
OkHostnameVerifier.verify.implementation = function (host, session) {
console.log("[+] HostnameVerifier bypass: " + host);
return true;
};
} catch (e) {
console.log("[-] layer4 failed: " + e);
}
});
2. 注入运行
方式 A:Spawning(推荐,能抓住 App 启动早期的校验)
frida -U -f com.example.target -l unpinning.js --no-pause
方式 B:Attach 到已运行的进程
frida -U com.example.target -l unpinning.js
方式 C:设备上直跑,不依赖电脑——把脚本 push 进手机,用 frida-server 自带的方式加载,适合出门在外做分析。
3. 原理拆解
四层 Hook 分别对应四条校验路径:
| 层 | 目标 | 作用 |
|---|---|---|
| 1 | SSLContext.init |
App 在建立 TLS 上下文时注入一个「什么都信」的 TrustManager,从源头上让信任链失效 |
| 2 | TrustManagerImpl |
系统自带的证书链校验函数,直接返回未校验的证书链,绕过 Android 7+ 的用户证书限制 |
| 3 | CertificatePinner.check |
OkHttp 的证书固定实现,把它改成空函数,等于关掉 Pinning |
| 4 | HostnameVerifier.verify |
兜底处理域名不匹配的情况(比如目标是 IP 直连或内网域名) |
需要理解一个前提:Frida 的 Hook 只在 Java 层有效。如果 App 把校验逻辑放进 .so 里(用 OpenSSL/BoringSSL 直接做 SSL_get_verify_result 校验),Java 层的 TrustManager 根本不会被调用,脚本也就无效——这时需要走下一节。
六、方法三:非 Java 层的应对
1. Flutter / 使用 BoringSSL 的 App
Flutter 的网络栈在 libflutter.so 里,完全不走 Java 的 TLS 实现,所以上面的脚本一定无效。判断方法很简单:frida-ps -Ua 看进程里有没有加载 libflutter.so;或者 Burp 里完全没有任何请求,App 却正常联网。
常见做法有两种:
- reFlutter:修改
libflutter.so中的证书校验逻辑后重新打包,让 App 信任用户证书。 - Frida 直接 Hook native 函数:Interceptor 拦截
SSL_get_verify_result返回X509_V_OK,或 HookSSL_CTX_set_custom_verify。
native Hook 的写法对地址偏移依赖很强,必须按目标 App 的架构和 so 版本单独定位,没有一份通用脚本能覆盖所有 App——这一点网上很多文章不会说清楚。
2. 遇到加固壳
如果 App 用了商业加固(爱加密、梆梆、360 等),frida-server 很可能在启动阶段就被检测到并主动退出。此时顺序应该是:先过检测,再谈 Pinning。可以用 Magisk 的 DenyList / Zygisk 隐藏 root,配合随机化 frida-server 的进程名与端口(默认 27042 太显眼)。这部分属于对抗升级,本文不作展开。
七、常见报错排查表
| 现象 | 原因 | 处理 |
|---|---|---|
unable to connect to remote frida-server |
版本不一致,或 frida-server 没在跑 | 对齐版本号;adb shell "ps -A | grep frida" 确认进程存在 |
Failed to spawn: need Gadget to attach on jailed Android |
设备没 Root 或 su 不可用 | 确认 adb shell su -c id 返回 uid=0 |
| 脚本加载了但 Burp 仍无明文 | 校验在 native 层,或 App 用了自研 TLS 栈 | 先看脚本日志里哪几层成功;四层全失败则走第六节 |
java.lang.ClassNotFoundException: okhttp3.CertificatePinner |
目标 App 没用 OkHttp(改用 HttpURLConnection / Cronet) | 正常现象,脚本其他层依然生效 |
| App 一启动就闪退 | 触发了完整性检测或 root 检测 | 改用 spawn + 更早注入;或先隐藏 root |
| Burp 里只有 CONNECT,没有明文 | App 不读系统代理 | 开启 invisible proxying,或用 iptables 透明代理转发 |
八、防御视角:这些手段怎么被检测
写攻击方法的同时,反过来看防御更有价值。App 侧常见的检测点包括:
- 端口探测:frida-server 默认监听 27042,直接连一下就知道。
- 进程/文件扫描:检查
/proc/*/maps里有没有frida、gadget字样。 - ptrace 状态:读取
/proc/self/status的TracerPid,非 0 即被调试。 - 完整性校验:对 so 做 CRC/Hash 比对,Hook 后字节被改就会暴露。
- 证书链二次校验:不只用系统校验,业务层再算一遍叶子证书哈希——这一层 Frida 脚本最难覆盖,必须逐 App 定位。
所以真正的加固思路不是「加一个 Pinning 就完事」,而是多点校验 + 校验点之间互相依赖。只做单点 Pinning 的 App,本质上和没做区别不大。
九、小结
把流程压缩成一句话:Root → 对齐版本的 frida-server → Burp 代理 → spawn 注入通用脚本 → 看日志判断卡在哪一层。90% 的情况走完这五步就能解决;剩下 10% 是 Flutter、加固壳和自研 TLS 栈,需要针对具体 App 做逆向定位。
下一步建议按方向深入:
- 想系统学移动端逆向:从 smali 与 Frida 内存漫游开始,别急着上加固对抗
- 想把手机变成随身测试平台:参考《手机无线审计新武器|Kali NetHunter 2026 完整实战指南》
- 想研究车载方向:站内的《OBD-II 与 CAN 总线抓包分析完整指南》是同一条「协议分析」主线
免责声明:本文所有技术内容仅用于自有设备的安全研究、授权渗透测试与防御建设。未经授权对他人系统进行抓包、破解或数据获取,可能触犯《中华人民共和国网络安全法》《刑法》第 285 条,后果自负。
FAQ
Q:没有 Root 的手机能抓 HTTPS 包吗?
能,但限制很多。可以抓自己开发的 App(在 network_security_config.xml 里信任用户证书),或者用 VPN 类抓包工具(如 HttpCanary 类方案)配合可被信任的证书。第三方 App 一旦做了 Pinning,无 Root 基本无解。
Q:frida-server 和 frida-gadget 该用哪个?
有 Root 用 frida-server,简单直接。无 Root 才考虑 gadget——需要把 frida-gadget.so 注入目标 APK 并重新签名,成本高,且只适合自己的 App。
Q:为什么脚本里每一层都要 try-catch?
因为不同 App 的实现差异极大:有的没有 OkHttp,有的 TrustManagerImpl 被 ROM 改过。任何一层抛异常都会中断 Java.perform 的后续代码,导致「脚本加载成功但什么都没生效」这种最难排查的情况。分层隔离是最省事的写法。
- 1、本网站名称: 星宇安全实验室
- 2、本站永久网址:https://bk.zhaozhiqiang.pw/
- 3、本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
- 4、本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
- 5、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
- 6、本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
- 7、文章版权归作者所有,未经允许请勿转载。















暂无评论内容