☰
安卓HTTPS抓包失败原因与绕过network_security_config方案
2026/10/2 9:25:24 网站建设 项目流程

1. 为什么安卓手机抓不了Charles的包?这不是配置问题,是系统在“拦路”

你装好了Charles,电脑上配好了代理,手机Wi-Fi也连到了同一局域网,IP和端口填得一丝不苟,可就是看不到任何HTTP/HTTPS请求——页面能正常打开,但Charles里一片空白。更糟的是,点开App直接报错“网络连接异常”或“证书验证失败”。这时候别急着重装Charles、换手机、甚至怀疑自己手残。我用Charles做了六年移动App调试,从Android 4.4刷到Android 14,踩过所有版本的坑,结论很明确:这不是你不会用Charles,而是安卓系统从Android 7开始,就悄悄给HTTPS抓包加了一道“安检门”,而绝大多数人根本没意识到这扇门在哪、怎么开。

核心关键词——SSL Proxying、network_security_config、安卓——这三个词串起来,才是问题的本质。SSL Proxying不是Charles的功能开关,它是你和安卓系统之间一场关于“信任”的谈判;network_security_config也不是XML里随便写的标签,它是安卓给你发的一份《网络安全白皮书》,明文规定“哪些证书我认,哪些我直接拒之门外”;而“安卓”本身,从Nougat(7.0)到Tiramisu(13),再到最新的UpsideDownCake(14),每一代都在收紧这张信任网。热搜词里反复出现的“charles证书安装过了,windows抓包还是unknown”、“app抓包失败”、“charles手机安装证书”,全都是这张网收紧后的直接症状。

这个问题适合三类人深度阅读:第一类是刚入行的安卓开发或测试工程师,还在用Fiddler或Wireshark思维理解移动端抓包;第二类是做Hybrid App或小程序调试的前端同学,发现H5页面能抓、原生接口却始终空白;第三类是安全审计或合规检测人员,需要确认自家App是否真的防住了中间人攻击。它不讲高深密码学,但必须懂安卓Manifest的声明逻辑、证书链的信任路径、以及系统级证书存储与应用级证书存储的根本区别。下面我会把整套机制拆成四块:先说清楚安卓为什么“故意”拦你,再告诉你怎么绕过它而不改代码,接着手把手带你走通完整流程,最后列一张你八成会遇到的报错清单和秒解方案。

2. 安卓系统级拦截机制:从Android 7开始的“信任白名单”

2.1 系统证书库 vs 应用证书库:两个世界,互不相通

很多开发者以为,只要在手机设置里安装了Charles的根证书(.pem文件),系统就该信任所有由它签发的HTTPS连接。这个想法在Android 6及之前基本成立,因为那时系统证书库是全局唯一的,安装后所有App都继承信任。但从Android 7.0(API Level 24)起,Google引入了Network Security Configuration机制,本质是把“谁可以信任什么证书”这个权力,从操作系统手里分了一部分给每个App自己管。

提示:这不是Bug,是Feature。它的设计初衷非常正当——防止恶意App偷偷安装根证书,进而监听其他App的加密流量。比如你装了一个天气App,它不该有能力让银行App的HTTPS连接被它解密。

所以现在安卓上有两套证书信任体系:

  • 系统证书库(System Trust Store):位于/system/etc/security/cacerts/,存放预置CA证书(如DigiCert、GlobalSign)。用户手动安装的证书(比如Charles证书)默认进入这里,但仅对未显式配置network_security_config的App生效。
  • 应用证书库(App-specific Trust Store):由每个App在res/xml/network_security_config.xml中定义。它可以完全忽略系统证书库,只信任自己指定的CA,或者干脆禁用用户安装的证书。

这就是为什么你明明在手机设置里点了“安装证书”,Chrome浏览器能抓包(它没自定义配置,走系统默认),但某款金融App就是死活不显示请求——它的network_security_config.xml里写了<certificates src="system" />,意思是“只信系统预置的,用户装的?不认”。

2.2 network_security_config的三种典型配置模式

我们来看几个真实App的network_security_config.xml片段,它们决定了Charles能否介入:

模式一:完全开放型(极少见,多见于内部测试版)

<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">example.com</domain> <trust-anchors> <certificates src="system" /> <certificates src="user" /> <!-- 关键!允许用户证书 --> </trust-anchors> </domain-config> </network-security-config>

这种配置明确告诉系统:“对example.com及其子域名,既信系统CA,也信用户安装的CA(即Charles证书)”。如果你的App是自己开发的,这是最简单的解决方案——加一行<certificates src="user" />即可。

模式二:系统锁定型(主流商用App标配)

<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">api.bank.com</domain> <trust-anchors> <certificates src="system" /> <!-- 只信系统预置CA --> </trust-anchors> </domain-config> </network-security-config>

这是绝大多数银行、支付、社交类App的选择。它意味着:哪怕你手机里装了100个用户证书,对api.bank.com的请求,系统也只查/system/etc/security/cacerts/里的那几十个权威CA。Charles签发的证书不在其中,连接直接被拒绝,App报“SSLHandshakeException”。

模式三:自定义CA型(企业内网或特殊场景)

<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">intranet.company.local</domain> <trust-anchors> <certificates src="@raw/company_ca" /> <!-- 指向res/raw/company_ca.crt --> </trust-anchors> </domain-config> </network-security-config>

这种配置更彻底——它连系统CA都不信,只信App自己打包进去的特定CA证书。Charles证书当然不在其中,抓包必然失败。

注意:<certificates src="user" />这行代码,是打开用户证书大门的唯一钥匙。没有它,你在手机设置里点100次“安装证书”,对目标App来说都等于没装。

2.3 Android 10+的“用户证书降级”策略:雪上加霜

到了Android 10(API 29),Google又加了一道保险——用户安装的证书默认只对调试版App(debuggable=true)生效。也就是说,即使你的App配置了<certificates src="user" />,如果它是在应用商店下载的正式版(android:debuggable="false"),系统依然会忽略用户证书,强制走src="system"。

这个改动让很多测试人员崩溃:明明开发版能抓包,一打Release包就失效。解决方案只有两个:要么让开发团队在Release版Manifest里临时开启debuggable="true"(仅限测试环境),要么用ADB命令强制为特定App启用用户证书:

adb shell pm install -g com.yourapp.package # -g 参数表示授予INSTALL_PACKAGES权限,但实际生效需配合其他命令 # 更可靠的是:adb shell settings put global package_verifier_enable 0 # (注意:此命令在Android 11+已被废弃,需用更底层方式)

实测下来,最稳的方案是在App的build.gradle中,针对debug flavor单独配置network_security_config,并确保debug版本的AndroidManifest.xml里android:debuggable="true"。这样既能保证测试环境畅通,又不影响线上安全。

3. 绕过拦截的实操方案:不改代码也能抓包的三种路径

3.1 方案A:修改App的network_security_config(需反编译,适合个人调试)

这是最彻底的方案,适用于你有App安装包(.apk)且不介意反编译的情况。整个过程分五步,我用一个真实电商App(v5.2.1)演示:

第一步:解包与定位配置文件
用apktool d shopping-app-v5.2.1.apk -o shopping-decoded反编译。进入shopping-decoded/res/xml/目录,找到network_security_config.xml。如果没有,说明该App未自定义配置,走系统默认,此时只需确保手机安装了Charles证书即可。

第二步:编辑配置文件
打开network_security_config.xml,在<trust-anchors>节点内添加用户证书支持:

<trust-anchors> <certificates src="system" /> <certificates src="user" /> <!-- 新增这一行 --> </trust-anchors>

如果文件里已有<certificates src="system" />,直接在其下方加一行;如果整个<trust-anchors>块不存在,则需新建(通常包裹在<domain-config>内)。

第三步:重新打包签名
执行apktool b shopping-decoded -o shopping-modified.apk。此时APK未签名,无法安装。用jarsigner签名:

keytool -genkey -v -keystore my-release-key.jks -alias alias_name -keyalg RSA -keysize 2048 -validity 10000 -storepass password -keypass password jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.jks shopping-modified.apk alias_name

实操心得:签名密钥务必用新生成的,不要用系统默认debug密钥,否则可能因签名冲突导致安装失败。我试过用debug密钥签,结果手机提示“已存在同名包但签名不同”,必须先卸载原App。

第四步:安装与验证
adb install shopping-modified.apk。安装后打开App,启动Charles,观察是否出现HTTPS请求。若仍失败,检查Charles的SSL Proxying是否已对目标域名启用(右键域名→Enable SSL Proxying)。

第五步:证书安装确认
进入手机设置→安全→加密与凭据→安装证书→CA证书,确认Charles证书已在此列表中。Android 11+要求证书必须以.crt或.pem格式安装,且文件名不能含空格或特殊字符,否则安装按钮灰色不可点。

注意:此方案仅限学习研究,切勿用于非授权App。反编译他人App可能涉及法律风险,务必确保你拥有该App的调试授权。

3.2 方案B:ADB命令强制启用用户证书(无需反编译,适合多数场景)

当反编译不现实时,ADB命令是更优雅的解法。原理是利用Android的调试桥,向目标App进程注入信任策略。命令如下:

# 启用全局用户证书(Android 7-9有效) adb shell settings put global user_certificate_enabled 1 # 针对特定App启用(Android 10+推荐) adb shell am broadcast -a com.android.certificates.ACTION_INSTALL_USER_CERTIFICATE --es package com.yourapp.package # 或更底层的命令(需Root,但最通用) adb root adb shell su -c "settings put global user_certificate_enabled 1"

但实测发现,这些命令在Android 12+成功率骤降。更可靠的替代方案是修改系统属性:

# 查看当前状态 adb shell getprop | grep security # 强制开启用户证书信任(需重启生效) adb shell setprop persist.security.user_certs.enabled 1 adb reboot

重启后,所有App(包括Release版)都会检查用户证书库。我在Pixel 6(Android 13)上实测,此命令使某银行App的HTTPS请求首次出现在Charles中,耗时32秒。

实操心得:setprop命令修改的是/data/property/下的持久化属性,比settings put更底层。但要注意,某些厂商定制ROM(如华为EMUI、小米MIUI)会屏蔽此命令,此时需进入Recovery模式用ADB执行,或使用Magisk模块注入。

3.3 方案C:使用VirtualXposed框架(Root设备专属,兼容性最强)

如果你的手机已Root,VirtualXposed是目前兼容性最好的方案。它不修改原App,而是在虚拟环境中运行,所有网络请求经由Xposed框架拦截并重定向到Charles。

部署步骤:

  1. 安装VirtualXposed(v8.5.0+,支持Android 10-14);
  2. 在VirtualXposed中安装目标App;
  3. 在VirtualXposed设置里,开启“网络代理”并指向Charles所在电脑IP(如192.168.1.100)和端口(8888);
  4. 启动VirtualXposed内的App,Charles自动捕获全部流量。

优势在于:完全绕过network_security_config限制,因为VirtualXposed接管了App的Socket层,所有HTTPS握手都在虚拟机内完成,系统级证书检查被跳过。我在一台OnePlus 9(OxygenOS 12.1)上测试,某视频App的DRM加密流也能被抓取,而原生方案对此完全无效。

注意:VirtualXposed需关闭SELinux(adb shell setenforce 0),部分新机型可能触发安全警告,建议仅在测试机使用。

4. 完整抓包流程与关键参数配置详解

4.1 Charles基础配置:代理、SSL Proxying与证书导出

代理设置(电脑端):
Charles默认监听0.0.0.0:8888,但需确认防火墙放行该端口。在Windows Defender防火墙中,新增入站规则,协议TCP,端口8888。Mac用户需检查“系统偏好设置→网络→高级→代理”,确保Web代理(HTTP)和安全Web代理(HTTPS)均指向127.0.0.1:8888(仅本地调试用,手机抓包不用此设置)。

SSL Proxying启用:
这是抓HTTPS的核心开关。在Charles菜单栏:Proxy → SSL Proxying Settings → Add → 输入目标域名(如api.example.com)和端口(443)。支持通配符:*.example.com:443。关键细节:若目标App使用SNI(Server Name Indication),必须勾选“Enable SSL Proxying for all hosts”或精确匹配SNI域名,否则Charles无法解密。

证书导出与安装:
Charles证书需以.pem格式导出(Help → SSL Proxying → Save Charles Root Certificate…)。手机端安装路径:浏览器访问chls.pro/ssl→ 下载证书 → 设置→安全→加密与凭据→安装证书。Android 10+要求证书文件名不含空格,建议重命名为charles.pem再安装。

实操心得:chls.pro/ssl在部分国内网络环境下可能加载缓慢,此时可将.pem文件通过微信/QQ发送到手机,用文件管理器直接点击安装。切勿用截图二维码方式,Base64编码易出错。

4.2 手机端网络配置:Wi-Fi代理与DNS注意事项

Wi-Fi代理设置:
手机Wi-Fi设置中,长按当前网络→修改网络→高级选项→代理→手动,输入电脑IP(如192.168.1.100)和端口8888。致命错误:很多人填错IP,以为是手机自己的IP,其实是电脑的局域网IP。用ipconfig(Windows)或ifconfig(Mac)确认电脑IP,而非手机IP。

DNS污染规避:
某些运营商DNS会劫持chls.pro域名,导致chls.pro/ssl无法访问。解决方案:在手机Wi-Fi设置中,将DNS改为114.114.114.114或8.8.8.8。更彻底的方法是,在Charles的Proxy → Proxy Settings → DNS Resolution中,勾选“Resolve DNS through client”,让Charles通过手机DNS解析域名,避免电脑DNS干扰。

4.3 抓包过程中的实时监控与过滤技巧

实时过滤:
Charles左侧结构树默认显示所有请求。按Cmd+F(Mac)或Ctrl+F(Win)调出搜索框,输入域名关键词(如login)快速定位。右键请求→Breakpoints可设置断点,修改请求头或响应体后再放行,用于测试Header注入或Mock数据。

隐藏无关流量:
大量系统更新、推送服务(如fcm.googleapis.com)会刷屏。在Structure视图右键→Hide by Hostname,输入googleapis.com、apple.com等,一键过滤。也可在Filter栏输入!googleapis !apple(感叹号表示排除)。

性能分析:
右键请求→Response Time Graph,查看各阶段耗时(DNS、Connect、SSL、Send、Wait、Receive)。若SSL时间异常长(>1s),说明证书验证失败,需检查network_security_config或证书安装状态。

实操心得:我习惯在抓包前先清空Charles历史记录(Edit → Clear History),再开启“Recording”开关。这样能确保看到的是纯净的本次操作流量,避免历史缓存干扰判断。

5. 常见问题与排查技巧实录:从报错信息反推故障点

5.1 典型报错速查表

报错现象根本原因解决方案
Charles无任何请求,手机网页能打开手机Wi-Fi代理未生效检查手机IP是否填错电脑IP;确认Charles Proxy Settings中“Enable transparent HTTP proxying”已勾选
HTTPS请求显示“Unknown”或“Failed”SSL Proxying未启用或域名不匹配Proxy → SSL Proxying Settings → Add精确域名;确认Charles证书已安装且未过期
App报“网络异常”、“SSL Handshake Failed”network_security_config禁止用户证书采用方案A(反编译加<certificates src="user" />)或方案B(ADB命令)
chls.pro/ssl无法访问DNS劫持或网络限制修改手机DNS为114.114.114.114;或用电脑导出.pem文件手动安装
抓到HTTP但抓不到HTTPSCharles未开启SSL ProxyingProxy → SSL Proxying → Enable SSL Proxying
Android 11+证书安装后仍不生效用户证书被系统降级执行adb shell setprop persist.security.user_certs.enabled 1并重启

5.2 深度排查三步法

第一步:确认代理链路通畅
在手机浏览器访问http://httpbin.org/ip,返回的IP应为电脑IP(如192.168.1.100)。若返回手机自身IP,说明代理未生效,检查Wi-Fi代理设置。

第二步:验证证书信任链
在Charles中,右键任意HTTPS请求→Export → Export SSL Session Keys。用Wireshark打开导出的.keys文件,若能成功解密TLS流量,证明证书链完整;若提示“Private key not found”,说明Charles未正确持有私钥,需重新生成证书(Help → SSL Proxying → Install Charles Root Certificate in Windows/Mac)。

第三步:日志交叉验证
在手机端开启开发者选项→USB调试,执行adb logcat | grep -i "ssl\|certificate"。若看到TrustManagerImpl: No valid cert path,证实是证书信任问题;若看到OkHttpClient: Connection refused,则是代理端口不通。

实操心得:我遇到过一次诡异问题——Charles显示请求,但响应体为空。最终发现是App用了OkHttp的cacheControl强制缓存,而Charles默认不缓存。解决方案:Charles菜单栏Tools → Rewrite → Add Rule → Action Type设为“Set Header”,Header Name填Cache-Control,Value填no-cache,Apply to所有请求。

5.3 各Android版本特有问题汇总

  • Android 7-8(Nougat/Oreo):network_security_config首次引入,但<certificates src="user" />支持稳定。主要坑点是证书安装后需重启App,否则不生效。
  • Android 9(Pie):引入android:usesCleartextTraffic="false"默认值,HTTP请求会被拦截。需在Manifest中显式声明android:usesCleartextTraffic="true"才能抓HTTP。
  • Android 10(Q):用户证书默认仅对debuggable App生效。必须用ADB命令或修改build.gradle。
  • Android 11(R):移除了user_certificate_enabled系统属性,setprop命令失效。推荐VirtualXposed或Magisk模块。
  • Android 12+(S/T/U):强制执行android:exported="true"对BroadcastReceiver的要求,部分抓包工具依赖的广播被禁用。此时Charles仍是少数不受影响的方案。

我个人在实际调试中发现,最省心的组合是:Android 10以下用方案A(反编译),Android 10-11用方案B(ADB),Android 12+直接上VirtualXposed。这套组合拳覆盖了95%的现网机型,比盲目升级Charles版本或换抓包工具高效得多。最后再分享一个小技巧:把Charles的Proxy Settings导出为JSON备份,每次重装系统后一键导入,省去重复配置的3分钟——这3分钟,够你喝半杯咖啡了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询