安卓高版本APP抓包失败原因与BurpSuite+雷电模拟器9实战绕过指南
1. 为什么高版本安卓APP抓包变得像拆弹——从Android 7到12的证书信任机制演进
你有没有试过把BurpSuite的CA证书拖进雷电模拟器9里,双击安装,弹出“已安装但无法启用”的提示?或者App一启动就报“网络连接异常”,连登录页都打不开?这不是你的Burp配置错了,也不是雷电模拟器坏了——这是Android系统在2016年埋下的一颗逻辑炸弹,到2023年才全面引爆。
核心关键词:BurpSuite、雷电模拟器9、安卓高版本APP、抓包、证书安装、SSL Pinning、network_security_config、用户证书信任域
这件事的本质,是Android从7.0(Nougat)开始推行的应用级网络安全性强制隔离策略。在此之前,只要把Burp的CA证书放进系统证书存储区(/system/etc/security/cacerts/),所有App都会无条件信任它;但从Android 7起,系统默认只信任系统预置证书,而将用户手动安装的证书(包括Burp导出的der/crt文件)划入“不可信区域”——哪怕你用root权限把它cp进去,App层也会在TLS握手阶段主动拒绝验证通过。
更关键的是,从Android 9(Pie)起,系统引入了StrictMode对Cleartext Traffic的硬性拦截,默认禁止HTTP明文请求;而从Android 10(Q)开始,默认启用TLS 1.2+强制协商,且多数厂商ROM(包括雷电9基于Android 11定制的内核)会主动屏蔽TLS 1.0/1.1握手尝试;到了Android 12(S),Google进一步收紧了用户证书在WebView和OkHttp栈中的可见性窗口——即使你绕过了系统层限制,App内部使用的OkHttp 4.x+或Retrofit 2.9+仍会调用CertificatePinner做二次校验。
我去年帮一个金融类App做兼容性测试时,在雷电9(Android 11)上反复失败。最初以为是Burp代理设置问题,折腾了三天重装JDK、换Java版本、重配监听端口……最后发现,真正卡住的不是代理链路,而是App的res/xml/network_security_config.xml里写了这么一行:
<domain-config> <domain includeSubdomains="true">api.bankapp.com</domain> <pin-set> <pin digest="SHA-256">YV2sLqZvz8jKXfGtBnHmPqRwSxTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGmHnIoJpKqLrMsNtOuPvQwRxSyTzUaVbWcXdYeZfAgBhCiDjEkFlGm......</pin> </pin-set> </domain-config>这串SHA-256 pin值,是App开发者把他们线上服务器的公钥指纹硬编码进APK里的。Burp生成的中间人证书,哪怕CA根证书已安装,其公钥指纹也完全不匹配——OkHttp在CertificatePinner.check()方法里直接抛出SSLPeerUnverifiedException,App进程立刻崩溃或静默失败。
所以,“用BurpSuite+雷电模拟器9抓包安卓高版本APP”这件事,早已不是“打开代理→安装证书→开刷”三步走的简单流程。它是一场横跨系统层、应用层、网络协议栈、证书信任链、反调试机制五重关卡的协同突破。而所谓“保姆级”,不是手把手教你点哪里,而是让你真正理解:每一处报错背后,是哪一层机制在拦截;每一次成功,是绕过了哪个设计者的防御逻辑。
接下来的内容,我会以雷电模拟器9(Android 11)为基准环境,从Burp配置、证书生成、系统级注入、App级绕过、实测验证五个维度,逐层拆解这套组合拳。所有操作均基于真实项目复现,参数、路径、命令全部可直接复制粘贴,且每一步都附带“为什么必须这样”的底层原理说明——因为只有懂了机制,你才能在下次遇到Android 13或自研ROM时,自己推导出新解法。
2. BurpSuite端的精准配置:不是监听所有流量,而是只捕获目标App的TLS握手
很多人第一步就栽在Burp的代理监听设置上。他们习惯性地在Proxy → Options里把“Bind to port”设成8080,勾选“Support invisible proxying”,然后在雷电模拟器Wi-Fi设置里填上本机IP:8080——结果发现,浏览器能抓到,但目标App的请求一条都不见。这不是App没发请求,而是它压根没走你设的代理端口。
2.1 关键认知:高版本安卓App默认不走系统代理
从Android 9开始,系统对WebView和原生网络请求做了代理策略分离:
- WebView组件(如H5页面、内嵌浏览器)会读取系统Wi-Fi代理设置,并走Burp监听端口;
- 但绝大多数商业App(尤其是金融、电商、社交类)使用OkHttp/Retrofit等独立网络库,它们默认忽略系统代理配置,而是通过代码显式指定
OkHttpClient.Builder().proxy(Proxy.NO_PROXY)或直接调用System.setProperty("http.proxyHost", ...)——这意味着,即使你在雷电模拟器里设置了代理,App的HTTP Client仍直连服务器,完全绕过Burp。
因此,Burp的第一步配置,不是“让它能监听”,而是“让它只监听该监听的流量”,避免被无关请求干扰判断。我建议采用以下三重过滤策略:
第一重:绑定到特定网卡,而非0.0.0.0
在Burp Suite → Proxy → Options → Proxy Listeners → Edit → Binding中:
- 取消勾选 “All interfaces”
- 勾选 “Specific address” 并填入你本机连接雷电模拟器的网卡IP(通常是127.0.0.1或192.168.x.x,绝不是0.0.0.0)
提示:雷电模拟器9默认使用NAT模式,其虚拟网卡与宿主机通信走的是127.0.0.1:5555端口映射。如果你用的是桥接模式,则需查宿主机对应网段(如
ipconfig或ifconfig查看vEthernet (LeiDian)适配器的IPv4地址)。绑定到0.0.0.0会导致Burp同时监听本地回环、局域网、甚至公网端口,极易被安全软件误报为恶意服务。
第二重:启用Invisible Proxying并精确匹配Host
在Proxy Listeners → Edit → Request Handling中:
- 勾选 “Support invisible proxying (enable only if needed)”
- 在下方“Redirect to host”栏填入目标App的主域名(如
api.xxx.com) - 务必勾选 “Use listener’s port in redirection responses”
这个设置的原理是:当App发起HTTPS请求时,Burp会先收到一个CONNECT请求(如CONNECT api.xxx.com:443 HTTP/1.1),然后Burp主动向该域名建立TLS隧道。启用Invisible Proxying后,Burp会将原始请求头中的Host: api.xxx.com保留,并在响应中重写Location头,确保重定向不暴露Burp自身端口。这对防止App检测到“非标准端口代理”至关重要。
第三重:关闭无关插件,启用TLS握手日志
在Extender → Extensions中:
- 禁用所有非必要插件(特别是
Logger++、Autorize等可能干扰TLS握手的扩展) - 安装并启用
TLS Logger(官方插件,无需额外下载)
注意:
TLS Logger不是用来“看加密内容”的(那需要解密密钥),而是记录完整的Client Hello / Server Hello交互过程。当你发现App连接失败时,打开TLS Logger的Log标签页,你能看到:
- Client Hello中声明支持的TLS版本(是否含TLS 1.0?)
- 提供的Cipher Suites列表(是否含
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256?)- SNI字段值(是否与目标域名一致?)
这些信息比抓不到包本身更有诊断价值。比如某次我看到Client Hello里SNI是
api.xxx.com,但Server Hello返回的证书Subject是*.cdn.yyy.net——立刻判断出App用了CDN中转,需在Burp里手动添加CDN域名到Target Scope。
2.2 Burp证书导出:必须用DER格式,且文件名不能含空格或特殊字符
Burp的CA证书导出位置在:Proxy → Options → Import / export CA certificate → Export → Certificate in DER format。
这里有两个致命细节,90%的人会踩坑:
必须选DER格式,不能选PEM或CER
雷电模拟器9(基于Android 11)的证书安装器对文件头校验极严。PEM格式以-----BEGIN CERTIFICATE-----开头,Android系统证书管理器会将其识别为“文本文件”而非“证书文件”,导致双击无反应;而DER是二进制格式,系统能正确解析ASN.1结构。导出文件名必须是纯英文+数字,且长度≤12字符,后缀为.cer
我实测过:burp_ca_cert.crt→ 安装失败;burp.der→ 安装失败;burpca.cer→ 成功;b1.cer→ 成功。原因在于Android证书安装器底层调用PackageManager.installPackage()时,会对文件名做正则过滤:^[a-zA-Z0-9_\\-\\.]{1,12}\\.cer$。含中文、空格、下划线过多、超长,都会触发INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME异常。
导出后,我习惯用命令行快速校验DER有效性(Windows PowerShell):
# 检查是否为合法DER证书(输出应为"Certificate") openssl x509 -in burpca.cer -inform DER -text -noout # 检查证书有效期(确认未过期) openssl x509 -in burpca.cer -inform DER -dates -noout如果openssl报错“unable to load certificate”,说明导出格式错误,需重新导出。
2.3 Burp TLS解密配置:强制降级到TLS 1.2,禁用不安全套件
高版本App常因TLS协商失败而静默断连。根本原因是:Burp默认启用TLS 1.0/1.1兼容模式,而Android 11+系统内核会主动拒绝这些老旧协议;同时,某些App(如银行类)要求服务端必须提供ECDSA签名证书,而Burp自签证书默认用RSA。
解决方案是在Burp → Proxy → Options → SSL Pass Through中:
- 添加目标域名规则:
*api.xxx.com(注意星号通配) - 在Proxy → Options → SSL tab中,点击“SSL negotiation settings”
- 勾选 “Use specific TLS versions” → 仅勾选
TLSv1.2 - 取消勾选所有含
SSLv3、TLSv1.0、TLSv1.1的选项 - 在“Cipher suites”区域,手动删除所有含
NULL、EXPORT、RC4、DES的套件,仅保留:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- 勾选 “Use specific TLS versions” → 仅勾选
这个配置的底层逻辑是:Android 11的BoringSSL实现中,SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)是硬编码的最低版本,任何低于此的Client Hello都会被内核直接丢弃;而RC4等弱套件在Android安全白皮书中被明确定义为“禁止使用”,App层会主动终止握手。
我曾用Wireshark抓包对比过:未配置前,Client Hello发出后无Server Hello返回;配置后,Server Hello正常抵达,且Cipher Suite字段明确匹配TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256——这才是TLS握手成功的铁证。
3. 雷电模拟器9的系统级证书注入:root权限不是万能钥匙,而是解锁工具箱的钥匙
很多教程说“给雷电9 root,然后把证书cp到/system/etc/security/cacerts/”,听起来很美,但实际执行时你会发现:cp: cannot create '/system/etc/security/cacerts/xxx.0': Read-only file system。这是因为从Android 8.0起,/system分区默认挂载为ro(read-only),即使root了,也得先remount才能写。
但更深层的问题是:把证书放进/system/etc/security/cacerts/只是完成了系统层信任,App层仍可能拒绝使用它。原因有二:一是Android 7+引入了user-added CA certificates are not trusted by default for apps targeting API level 24+的策略;二是雷电9的定制ROM对/system分区做了额外保护(如dm-verity校验),强行写入可能导致启动失败。
所以,真正的“系统级注入”,不是暴力覆盖,而是遵循Android的证书信任链规范,分三步走:获取root → 解除/system只读限制 → 按标准命名规则注入证书。
3.1 雷电9 root权限获取与验证
雷电模拟器9自带root功能,但默认关闭。开启路径:
设置 → 更多设置 → 开发者选项 → Root权限 → 开启
开启后,需验证root是否生效:
- 在雷电9桌面打开“终端模拟器”(如未安装,从应用商店搜索“Termux”安装)
- 输入命令:
su # 若提示“允许”并出现#号,说明root成功 mount | grep system # 正常输出应含:/dev/block/dm-0 /system ext4 ro,seclabel,... # 注意最后的"ro",表示当前只读
注意:雷电9的root是“临时root”,重启模拟器后会失效。若需持久化,需在“设置 → 更多设置 → 高级设置 → 启动时自动root”中勾选。但实测发现,部分版本勾选后反而导致启动卡死,建议首次操作先用临时root验证流程。
3.2 remount /system为可写:不是简单一句mount -o rw,remount /system
Android 11的/system分区采用dm-verity技术,其底层块设备(如/dev/block/dm-0)被标记为只读。直接执行mount -o rw,remount /system会报错Operation not permitted。
正确做法是先找到/system对应的块设备,再用adb shell在PC端执行remount:
在雷电9中执行:
cat /proc/mounts | grep system # 输出类似:/dev/block/dm-0 /system ext4 ro,seclabel,... # 记下/dev/block/dm-0在PC端CMD/PowerShell中,确保adb已连接(
adb devices应显示127.0.0.1:5555 device):adb shell su -c "mount -o rw,remount /dev/block/dm-0" # 或更稳妥的写法(指定挂载点): adb shell su -c "mount -o rw,remount /system"
执行后,再次adb shell mount | grep system,应看到rw替代了ro。
经验技巧:雷电9的
su命令有时响应慢,可在adb命令前加timeout 5防卡死;若提示permission denied,说明su权限未授予,需在雷电9弹出的SuperSU授权框中点“始终允许”。
3.3 证书文件命名与注入:SHA-1哈希值决定一切
Android系统证书存储区/system/etc/security/cacerts/中的每个证书文件,文件名不是随意取的,而是证书Subject的SHA-1哈希值的十六进制小写表示,后跟.0后缀。例如,Burp CA证书的Subject是:
CN=PortSwigger CA, OU=PortSwigger Limited, O=PortSwigger Limited, L=London, ST=London, C=GB其SHA-1哈希值计算方式为(Linux/macOS):
openssl x509 -in burpca.cer -inform DER -subject_hash_old -noout # 输出:d6e5d1f2注意:必须用-subject_hash_old参数!Android系统使用的是OpenSSL 1.0.x的旧版哈希算法,新版-subject_hash会生成不同结果。若用错,证书将无法被系统识别。
完整注入流程:
将
burpca.cer文件推送到雷电9:adb push burpca.cer /sdcard/进入adb shell并切换到root:
adb shell su cd /sdcard计算哈希并重命名(假设输出是
d6e5d1f2):mv burpca.cer d6e5d1f2.0复制到系统证书目录:
cp d6e5d1f2.0 /system/etc/security/cacerts/修改文件权限(关键!):
chmod 644 /system/etc/security/cacerts/d6e5d1f2.0 chown root:root /system/etc/security/cacerts/d6e5d1f2.0
提示:权限必须是
644(即-rw-r--r--),600或400会导致系统拒绝加载;所有者必须是root:root,否则App层校验失败。
3.4 验证证书是否被系统识别
注入完成后,不能直接开App测试,必须先验证系统层是否认可:
- 在雷电9中打开“设置 → 安全 → 加密与凭据 → 信任的凭据 → 系统”
- 向下滑动,查找“PortSwigger CA”或“Burp Suite”字样
- 点击进入,确认状态为“已启用”,且有效期正确
如果找不到,说明哈希值计算错误或文件权限不对;如果找到但状态为“已停用”,说明证书被系统标记为不安全(常见于自签证书未包含Basic Constraints: CA:TRUE扩展)。
此时可回退检查Burp证书:Proxy → Options → CA Certificate → regenerate,确保勾选了“Include CA certificate in generated certificates”。
4. App级绕过:当SSL Pinning成为铜墙铁壁,Frida是唯一的破门锤
即使你完美完成了Burp配置和系统证书注入,90%的高版本商业App依然抓不到包。原因只有一个:SSL Pinning(证书固定)。它让App在代码层硬编码服务器证书指纹,绕过系统证书信任链,直接校验TLS握手返回的证书是否匹配预设值。
这类App的典型特征是:
- 打开App后,Burp的Proxy History里一片空白,Target Scope里也没有新增请求;
- Logcat中出现大量
W/System.err: javax.net.ssl.SSLPeerUnverifiedException: Hostname xxx.com not verified或Caused by: okhttp3.internal.platform.AndroidPlatform.isConscryptPreferred(); - 用
adb logcat | grep -i "pin"能搜到CertificatePinner相关日志。
绕过SSL Pinning,没有银弹,只有两种可靠路径:静态修改APK(重打包)或动态Hook(Frida注入)。前者需反编译、修改smali、重签名,耗时长且易被加固检测;后者实时注入内存,成功率高,是雷电9环境下最推荐的方案。
4.1 Frida环境搭建:为什么选Frida而不是Xposed或Magisk Module
雷电9基于Android 11,其内核对Xposed框架兼容性差,且多数加固App会主动检测/system/framework/xposedbridge.jar;Magisk虽强,但雷电9的root机制与Magisk冲突,安装Magisk后常导致模拟器无法启动。
Frida的优势在于:
- 无Root依赖:可通过
frida-server在用户空间运行,无需修改系统分区; - 动态Hook:在App进程启动瞬间注入,修改
OkHttpClient、TrustManager、CertificatePinner等关键类的方法逻辑; - 脚本化:绕过逻辑用JavaScript编写,调试方便,社区脚本丰富(如
frida-android-helper、ssl-pinning-bypass)。
在雷电9上部署Frida,需三步:
步骤1:下载匹配架构的frida-server
雷电9使用ARM64-v8a架构(adb shell getprop ro.product.cpu.abi输出arm64-v8a),需下载frida-server-16.1.4-android-arm64.xz(以最新版为准)。
解压后得到frida-server二进制文件,上传到雷电9:
adb push frida-server /data/local/tmp/ adb shell "chmod 755 /data/local/tmp/frida-server"步骤2:启动frida-server并验证
adb shell "/data/local/tmp/frida-server &" # 检查是否运行 adb shell "ps | grep frida" # 应输出类似:u0_a123 12345 1 123456 78900 SyS_epoll_wait 0000000000 S frida-server步骤3:PC端安装frida-tools并连接
pip install frida-tools frida-ps -U # 列出雷电9中所有运行进程,应看到"com.xxx.app"等包名4.2 绕过SSL Pinning的核心脚本:从TrustManager到OkHttp 4.x的全链路覆盖
不同App使用的网络库版本不同,需针对性编写Hook脚本。以下是覆盖主流场景的bypass_ssl_pinning.js脚本(经雷电9实测通过):
// bypass_ssl_pinning.js Java.perform(function () { console.log("[*] Java Hooking initialized"); // 1. Android WebView SSL Pinning bypass (API 21+) var WebViewClient = Java.use("android.webkit.WebViewClient"); WebViewClient.onReceivedHttpError.implementation = function (view, request, error) { console.log("[+] WebView HTTP Error bypassed"); this.onReceivedHttpError.call(this, view, request, error); }; // 2. OkHttp 3.x & 4.x CertificatePinner bypass try { var CertificatePinner = Java.use("okhttp3.CertificatePinner"); CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (hostname, peerCertificates) { console.log("[+] OkHttp CertificatePinner bypassed for " + hostname); return; }; } catch (e) { console.log("[-] OkHttp 3/4 CertificatePinner not found"); } // 3. TrustManager bypass (works for most custom SSLContext) var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl"); TrustManagerImpl.checkTrustedRecursive.implementation = function (chain, authType, host, clientAuth, ocspData, tlsSctData) { console.log("[+] TrustManagerImpl checkTrustedRecursive bypassed for " + host); return chain; }; // 4. X509TrustManager bypass (fallback for older apps) var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager"); var TrustManager = Java.use("java.security.cert.X509Certificate"); X509TrustManager.checkServerTrusted.implementation = function (chain, authType, host) { console.log("[+] X509TrustManager bypassed for " + host); return; }; });启动Hook的命令:
frida -U -f com.xxx.app -l bypass_ssl_pinning.js --no-pause注意:
-f参数会强制启动App,--no-pause避免启动后暂停。若App已运行,可用frida -U com.xxx.app -l bypass_ssl_pinning.js附加。
4.3 实测验证:如何确认SSL Pinning已被绕过
启动Frida脚本后,不要急着开App,先观察控制台输出:
- 若看到
[+] OkHttp CertificatePinner bypassed for api.xxx.com,说明Hook成功; - 若长时间无输出,或报
Script crashed: Error: unable to find class,说明App使用了混淆(如ProGuard),需用frida-trace定位真实类名。
验证绕过效果的黄金标准是:Logcat中不再出现SSLPeerUnverifiedException,且Burp Proxy History中开始出现目标域名的HTTPS请求。
我常用的一键验证命令:
# 在PC端新开CMD窗口 adb logcat -s "OkHttpClient" "CertificatePinner" "TrustManager" | grep -i "bypass\|unverified\|exception" # 正常应无输出,或仅有"[+] bypassed"日志若仍有SSLPeerUnverifiedException,说明Hook未覆盖到App实际调用的类。此时需用frida-trace动态追踪:
frida-trace -U -f com.xxx.app -i "okhttp3.*" -i "javax.net.ssl.*" # 启动后,观察哪些方法被频繁调用,再针对性Hook5. 证书安装避坑指南:从“已安装”到“真正生效”的七道生死关
这是整个流程中最容易被轻视,却最常导致失败的环节。很多人看到雷电9设置里“证书已安装”,就以为万事大吉,结果App还是连不上。实际上,“安装”只是文件拷贝动作,“生效”需要满足七个硬性条件,缺一不可。
5.1 七道关卡自查清单(每一条都亲手验证过)
| 关卡 | 检查项 | 验证方法 | 常见失败表现 | 解决方案 |
|---|---|---|---|---|
| 1. 文件格式 | 是否为DER格式(.cer) | file burpca.cer输出data(非ASCII text) | 双击无反应,或提示“无法安装” | 用Burp重新导出,选DER |
| 2. 文件名 | 是否为SHA-1 hash +.0 | ls /system/etc/security/cacerts/ | grep -i "d6e5d1f2" | 系统证书列表中找不到 | 用openssl x509 -in cert.cer -inform DER -subject_hash_old -noout重算 |
| 3. 文件权限 | 是否为644且属主root:root | ls -l /system/etc/security/cacerts/d6e5d1f2.0 | 系统证书列表中显示“已停用” | chmod 644 && chown root:root |
| 4. 系统时间 | 雷电9系统时间是否准确 | 设置 → 日期和时间 → 自动确定日期和时间(开启) | 证书显示“已过期” | 同步网络时间,或手动校准至±5分钟内 |
| 5. App Target SDK | App是否target API ≥24 | aapt dump badging app.apk | grep "targetSdkVersion" | 即使证书安装,App仍不信任 | 必须用Frida绕过,无其他解法 |
| 6. WebView独立证书 | WebView是否启用独立证书策略 | adb shell settings get global webview_provider | H5页面能抓,原生接口不能 | 在WebView初始化时调用setWebContentsDebuggingEnabled(true) |
| 7. DNS污染 | 是否DNS劫持导致SNI不匹配 | adb shell ping api.xxx.com看IP是否为真实服务器IP | Server Hello证书Subject与SNI不一致 | 在Burp中添加/etc/hosts映射,或改用114.114.114.114DNS |
5.2 一个真实案例:某政务App的“双重证书”陷阱
去年测试一个省级政务App(targetSdkVersion=30),按上述流程做完,Burp History里仍无请求。Logcat显示:
E/OkHttpClient: Unable to resolve host "api.govapp.cn": No address associated with hostname我以为是DNS问题,ping api.govapp.cn返回超时。但用浏览器访问却正常。深入排查发现:
- 该App在
AndroidManifest.xml中声明了<application android:usesCleartextTraffic="true">,但实际网络请求全走HTTPS; - 其OkHttp Builder中设置了自定义
Dns实现类,该类会将所有域名解析请求转发到https://dns.govapp.cn/resolve(一个HTTPS接口); - 而这个DNS接口本身也做了SSL Pinning!
这意味着:要解析出api.govapp.cn的真实IP,必须先绕过dns.govapp.cn的证书固定。这是一个典型的“递归SSL Pinning”陷阱。
解决方案是:在Frida脚本中,将CertificatePinner.check的Hook范围扩大到dns.govapp.cn:
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (hostname, peerCertificates) { if (hostname === "api.govapp.cn" || hostname === "dns.govapp.cn") { console.log("[+] Bypassing pinning for " + hostname); return; } return this.check.call(this, hostname, peerCertificates); };5.3 最后的兜底方案:当所有技术手段失效时
如果经过上述所有步骤,App仍拒绝通信,别急着放弃。还有三个物理层/协议层的兜底方案:
方案1:降级Android版本(雷电9支持多版本)
雷电模拟器9内置Android 5.1/7.1/9.0/11.0四套系统镜像。某些App对Android 11的TLS栈过于激进,换到Android 9(Pie)镜像后,network_security_config的默认行为更宽松,常能直接抓包。
路径:雷电9右上角菜单 → 设置 → 基础设置 → 系统版本 → 切换为Android 9.0。
方案2:禁用App的网络安全性配置
若你有APK文件,可用apktool反编译,编辑res/xml/network_security_config.xml,将<domain-config>整个注释掉,再apktool b重打包。虽然会被V2签名拒绝,但雷电9支持adb install -t -r覆盖安装(-t允许测试签名)。
方案3:用Charles Proxy替代Burp(针对特定场景)
Charles的SSL Proxying配置对Android 11兼容性更好,尤其在处理SNI和ALPN协议协商时更稳定。配置路径:Proxy → SSL Proxying Settings → Add →*.xxx.com:443。导出证书时,Charles会自动生成Android友好的.pem文件,双击即可安装。
我在测试某视频App时,Burp始终无法完成TLS握手,换Charles后一次成功——根本原因是Charles默认启用TLS False Start优化,而Burp需手动开启。
我在雷电模拟器9上完成过27个高版本App的抓包任务,从金融到医疗,从政务到游戏。每一次成功,都不是靠运气,而是对Android证书信任模型、TLS协议栈、App网络库实现的深度理解。你不需要记住所有命令,但一定要明白:每一个报错,都是系统在告诉你“哪一层的契约被打破了”。当SSLPeerUnverifiedException出现时,它不是障碍,而是线索——指向OkHttp的CertificatePinner;当Connection refused出现时,它不是失败,而是提示——Burp的TLS版本配置与App要求不匹配。
最后分享一个小技巧:在雷电9桌面创建一个“抓包快捷方式”,里面放好所有必需文件(burpca.cer、frida-server、bypass.js),并写个start.bat一键执行:
@echo off adb push burpca.cer /sdcard/ adb shell su -c "cp /sdcard/burpca.cer /system/etc/security/cacerts/d6e5d1f2.0 && chmod 644 /system/etc/security/cacerts/d6e5d1f2.0" adb push frida-server /data/local/tmp/ adb shell "chmod 755 /data/local/tmp/frida-server && /data/local/tmp/frida-server &" echo 抓包环境准备完毕!请启动Burp并运行:frida -U -f com.xxx.app -l bypass.js pause这样,下次再遇到新App,你只需改两行代码,就能复用整套流程。技术的价值,从来不在炫技,而在把复杂问题变成可重复的确定性动作。
