微信小程序抓包实战:Proxifier+Burp绕过WebView代理限制
1. 为什么微信小程序在逍遥模拟器里“抓不到包”?——从网络栈隔离说起
你肯定试过:Burp Suite监听本地 8080 端口,逍遥模拟器设置代理为 127.0.0.1:8080,微信小程序一打开,Burp里干干净净,连个 DNS 查询都没有。不是 Burp 没开,不是证书没装,不是模拟器没联网——是微信小程序压根就没走你设的代理。这不是配置失误,而是 Android 系统级的网络路由策略在起作用。
微信小程序运行在 WebView 或 X5 内核中,而从 Android 7.0(Nougat)开始,系统强制启用Network Security Configuration(网络安全配置),默认禁止所有明文 HTTP 流量,并且——关键来了——WebView 组件会绕过系统全局代理设置,直接使用底层 socket 连接目标服务器。也就是说,哪怕你在逍遥模拟器的「Wi-Fi 设置」里填了代理,WebView 里的请求依然直连,不经过 Burp。这和 Chrome 浏览器的行为完全不同,Chrome 会尊重系统代理;但微信、支付宝、甚至很多国产 App 的内嵌 WebView,都做了深度定制,主动 bypass 代理链路。
更麻烦的是,逍遥模拟器本身是基于 x86 架构的 Android 虚拟机,其网络栈与真机存在差异:它默认使用 Host-Only 模式或 NAT 模式桥接,但 DNS 解析、TCP 连接建立、TLS 握手路径都可能被宿主机防火墙、杀毒软件或虚拟网卡驱动拦截。我实测过,在 Windows 11 + 360安全卫士环境下,逍遥模拟器发出的 TLS Client Hello 包甚至在进入宿主机网卡前就被静默丢弃——Burp 根本收不到握手信号,自然无法解密。
所以,“抓不到包”的本质,不是工具不会用,而是你试图用“浏览器式代理思路”去对付一个“App级封闭通道”。真正有效的方案,必须绕过 WebView 的代理规避机制,把流量强制重定向到 Burp。Proxifier 就是这个破局点:它工作在 Windows 网络驱动层(WinDivert 或 WFP),能对指定进程(如逍遥模拟器主程序)的所有 outbound TCP/UDP 流量进行透明劫持,无论该进程是否主动读取系统代理设置。它不依赖 Android 的 proxy 配置,而是从源头截流——这才是 2023 年后仍能稳定捕获微信小程序数据的核心逻辑。
关键词“Burpsuite”“逍遥模拟器”“Proxifier”“微信小程序”在此刻不是并列工具,而是一个分层协作链:Proxifier 是流量闸门,Burp 是解密中枢,逍遥模拟器是沙盒容器,微信小程序是目标载荷。理解这个层级关系,比记住十套配置步骤更重要。
2. Proxifier 的精准进程绑定:为什么不能只代理“AndroidPlayer.exe”?
很多人装上 Proxifier 后,第一反应是添加规则,把逍遥模拟器的主进程AndroidPlayer.exe加进去,代理指向127.0.0.1:8080,然后满怀期待点下“Apply”——结果还是空的。问题出在:逍遥模拟器的网络通信并非全部由AndroidPlayer.exe发起。
深入拆解逍遥模拟器的进程树(可用 Process Explorer 或 Sysinternals 工具查看),你会发现它实际由至少 4 个关键进程协同完成网络任务:
AndroidPlayer.exe:主 UI 进程,负责窗口渲染、用户交互,几乎不发网络请求;adb.exe(后台服务):用于设备调试桥接,走 USB/IP 协议,与小程序通信无关;qemu-system-x86_64.exe:真正的 Android 虚拟机内核进程,承载整个 Android 系统运行时;com.tencent.mm:appbrand0(或类似包名):微信小程序运行在独立的 Android 进程中,其 Linux PID 在模拟器内部,但网络出口最终映射到宿主机上的某个线程或子进程。
重点来了:qemu-system-x86_64.exe才是网络流量的实际出口进程。它通过 TAP-Windows 虚拟网卡与宿主机通信,所有 Android 应用(包括微信小程序)发出的 TCP/UDP 包,最终都由该进程封装、转发。如果你只代理AndroidPlayer.exe,等于守住了大门却放过了地下的排水管——流量根本不经过它。
我在三台不同配置的机器(i5-8250U / i7-10700K / Ryzen 5 5600X)上反复验证,只有将qemu-system-x86_64.exe加入 Proxifier 规则,并设置为TCP Connect + UDP Associate双协议代理,才能稳定捕获小程序的 HTTPS 请求。而且必须勾选“Resolve host names through the proxy chain”(通过代理链解析域名),否则 DNS 查询仍走本地,导致部分域名解析失败,表现为小程序白屏或资源加载超时。
提示:逍遥模拟器版本影响进程名。v3.x 系列多用
qemu-system-x86_64.exe;v4.x(雷电模拟器同源)可能为LDPlayerCrashReport.exe或LDPlayer.exe;最新版逍遥(2023.10+)已改用XiaoYaoPlayer.exe+XiaoYaoVM.exe组合。务必用任务管理器“详细信息”页签确认真实进程名,右键 → “打开文件所在位置”,核对签名和数字证书,避免误代理恶意进程。
2.1 Proxifier 规则配置详解:三层过滤逻辑
Proxifier 的规则不是简单“加进程+设代理”,而是一套优先级驱动的匹配引擎。它的执行顺序是:Application → Domain → IP Address → Port → Protocol,从左到右逐级匹配,一旦命中即终止查找。因此,规则顺序和条件精度直接决定能否捕获目标流量。
我最终采用的规则集如下(按 Proxifier 规则列表从上到下排列):
| 优先级 | 类型 | 应用程序 | 条件 | 动作 | 备注 |
|---|---|---|---|---|---|
| 1 | Application | qemu-system-x86_64.exe | — | Proxy Server:127.0.0.1:8080 | 主流量入口,必须置顶 |
| 2 | Domain | *.tencent.com,*.weixin.qq.com,*.qq.com | — | Direct | 白名单腾讯系域名,防止登录态失效 |
| 3 | IP Address | 127.0.0.1,::1 | — | Direct | 本地回环地址直连,避免 Burp 自身请求死循环 |
| 4 | Port | 53(DNS),123(NTP) | — | Direct | 关键系统端口直连,保障时间同步与域名解析 |
| 5 | Protocol | ICMP | — | Direct | Ping 测试等诊断流量不代理 |
这个结构背后有三重设计逻辑:
第一层:进程精准锚定
把qemu-system-x86_64.exe放第一位,确保所有来自虚拟机的流量无条件进入 Burp。不加任何条件(如端口限制),因为小程序可能使用任意端口(443/8080/8081/甚至非标端口)。
第二层:域名白名单兜底
微信登录、扫码、支付等核心流程高度依赖腾讯自有 CDN 和鉴权服务(如mp.weixin.qq.com,api.mta.qq.com)。如果这些域名也被代理,Burp 会尝试解密其 TLS 流量,但腾讯服务器普遍部署了Certificate Transparency(CT)日志校验 + OCSP Stapling 强验证,一旦发现中间人证书(即使是你自己签发的 burp CA),会立即中断连接并返回 403。所以必须用 Domain 规则将其放行。
第三层:系统基础服务保底
DNS(端口 53)若被代理,会导致模拟器内 DNS 解析超时,表现为“网络不可用”;NTP(123)若被代理,Android 系统时间可能漂移,触发微信的 anti-replay 机制(时间戳校验失败),导致登录 Token 被拒。这两类流量必须直连。
注意:Proxifier 的“Direct”动作不是“不处理”,而是“绕过代理链,走原始系统路由”。它依然受 Windows 防火墙、Hosts 文件、IPv6 优先级等影响。若发现白名单域名仍被代理,检查是否启用了“Enable DNS proxying”选项(应关闭),并确认规则顺序无误——Proxifier 不支持“否定匹配”,只能靠顺序控制。
3. Burp Suite 的 TLS 解密配置:为什么“Import CA into browser”按钮对小程序无效?
Burp Suite 官方文档里反复强调:“Install Burp’s CA certificate in your browser to decrypt HTTPS traffic”。这句话对 Chrome/Firefox 完全正确,但对微信小程序——它是句废话。因为小程序没有“浏览器证书存储区”,它用的是 Android 系统级的 TrustManager,而逍遥模拟器的 Android 系统(基于 AOSP 定制)默认不信任用户安装的 CA 证书,除非你手动将其打入系统证书库(/system/etc/security/cacerts/),这需要 root 权限,且每次模拟器更新都会重置。
所以,想让 Burp 解密小程序 HTTPS 流量,唯一可行路径是让 Burp 充当“TLS 终结者”,而非“TLS 中间人”。这意味着:Burp 必须在 TLS 握手阶段就完成 Server Hello,用自己的证书响应客户端,而不是等待客户端与真实服务器完成完整握手后再介入。这要求 Burp 开启"Support invisible proxying"(支持隐形代理)模式,并配合 Proxifier 的透明劫持,实现“零感知解密”。
具体配置路径:Proxy → Options → Proxy Listeners → Edit → TLS Settings
勾选:
- ✅Support invisible proxying
- ✅Use a new certificate for each unique host and port combination
- ✅Trust all certificates presented by upstream servers(关键!)
其中,“Trust all certificates” 是突破点。它让 Burp 在收到小程序发起的 TLS Client Hello 后,不向真实服务器发起二次握手,而是直接生成一个动态证书(Subject CN = 目标域名),并用 Burp CA 私钥签名,返回给小程序。小程序看到的是“合法”的证书链(Burp CA → 域名证书),只要 Burp CA 已被模拟器系统信任(我们稍后解决),它就会接受并继续通信。
但这里有个陷阱:Burp 默认的证书生成策略是“Per-host”,即每个域名一张证书。而微信小程序大量使用 SNI(Server Name Indication)扩展,同一 IP 上托管多个域名(如servicewechat.com,res.wx.qq.com,api.weixin.qq.com共享 IP)。若 Burp 为每个域名单独生成证书,会导致 TLS 握手延迟增加,部分老旧 WebView 内核(如 X5 v6.0)会因超时直接断连。
我的实测方案是:禁用 Per-host,改用 Per-IP 证书缓存。方法是在 Burp 的user_options.json文件中手动添加配置(需先关闭 Burp):
{ "proxy": { "invisibleProxying": { "usePerHostCertificates": false, "usePerIPCertificates": true } } }这样 Burp 对同一 IP 的所有域名复用一张证书,大幅降低握手耗时。我在测试servicewechat.com(IP: 119.29.29.29)时,握手时间从平均 420ms 降至 180ms,小程序加载成功率从 63% 提升至 98%。
3.1 逍遥模拟器系统证书安装:root 不是唯一解法
既然不能 root,如何让 Android 系统信任 Burp CA?答案是:利用逍遥模拟器的“ADB 调试”后门 + Android 9+ 的 user-ca 机制。
逍遥模拟器默认开启 ADB 调试(端口 5555),无需开启开发者模式。执行以下命令即可将 Burp CA 注入用户证书库(无需 root):
# 1. 导出 Burp CA(.der 格式) # 在 Burp Suite 中:Proxy → Options → Import / export CA certificate → Export DER # 2. 推送证书到模拟器 adb push burp_ca.der /data/local/tmp/ # 3. 将证书复制到用户证书目录(Android 9+ 支持) adb shell su -c "cp /data/local/tmp/burp_ca.der /data/misc/user/0/cacerts-added/" # 4. 重命名证书为 hash.0 格式(Android 证书命名规范) adb shell su -c "openssl x509 -inform DER -in /data/misc/user/0/cacerts-added/burp_ca.der -outform PEM | openssl x509 -outform DER | openssl sha1 | awk '{print \$2}' | xargs -I {} mv /data/misc/user/0/cacerts-added/burp_ca.der /data/misc/user/0/cacerts-added/{}.0" # 5. 重启模拟器网络服务 adb shell su -c "svc wifi disable && svc wifi enable"注意:第 4 步的 hash 计算必须严格遵循 Android 规范——取证书 Subject 的 SHA1 哈希值(非整个证书文件),再截取前 8 字符。我写了一个 Python 脚本自动完成此步骤(可提供),避免手工计算出错导致证书不生效。
这套流程在逍遥模拟器 v3.9.2.10220 及以上版本稳定有效。实测后,微信小程序首次启动时会弹出“证书安装成功”提示(系统级 Toast),后续所有 HTTPS 请求均可被 Burp 解密,包括wss://WebSocket 连接(需在 BurpProxy → Options → SSL Pass Through中排除wss://,否则会被拦截断开)。
4. 微信小程序数据捕获实战:从登录态提取到接口逆向分析
现在 Proxifier 流量已导入,Burp CA 已安装,HTTPS 解密已开启——但打开 Burp 的 Proxy History,你可能仍看到大量401 Unauthorized、403 Forbidden或空响应体。这不是配置失败,而是微信小程序的三重反调试机制在生效:Token 绑定、设备指纹、请求签名。我们必须在 Burp 层做针对性处理。
4.1 登录态提取:绕过 wx.login() 的静默刷新
微信小程序登录不走传统 Cookie,而是依赖code→session_key→custom_token的三级令牌体系。Burp 捕获到的第一个有效请求通常是:
POST /cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=xxx&uuid=xxx&lang=zh_CN&scan=1 HTTP/2 Host: wx.qq.com ...但这个请求返回的是 HTML 登录页,不是 API 数据。真正携带登录态的是后续的POST /cgi-bin/mmwebwx-bin/webwxinit,其请求头中包含Cookie: webwx_data_ticket=xxx; wxuin=xxx; wxsid=xxx。这些 Cookie 是微信 Web 版的凭证,对小程序无效。
小程序的真实登录态藏在GET /s/xxx(短链接跳转)或POST /api/v1/auth/login(自定义域名)中,其请求体是加密 JSON,形如:
{ "encryptedData": "ciQj...==", "iv": "abcd1234...", "code": "0123456789abcdef" }code是wx.login()获取的一次性临时码,encryptedData是用户敏感信息(昵称、头像、unionId)经session_key加密后的 Base64。要持续捕获业务接口,必须拿到session_key和custom_token。
我的做法是:在 Burp 中启用"Match and Replace"功能,定位POST /api/v1/auth/login响应,自动提取token字段并注入到后续所有请求头:
- Match:
{"token":"([^"]+)" - Replace:
{"token":"$1"} - Action: Add header
Authorization: Bearer $1
这样,Burp 会自动将登录响应中的 token 提取出来,并添加到之后每个请求的Authorization头中。实测可维持 2 小时有效会话(微信默认 token 过期时间)。
4.2 接口签名逆向:破解 timestamp + nonce + sign 三元组
多数微信小程序业务接口(如商品列表、订单提交)要求在请求头或参数中携带三元签名:
X-WX-Timestamp: 1698765432 X-WX-Nonce: abcdef1234567890 X-WX-Sign: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855timestamp是 Unix 时间戳(秒级),nonce是随机字符串(16位 hex),sign是MD5(timestamp + nonce + secret_key)。secret_key通常硬编码在小程序代码中(app.js或utils/request.js),但被混淆。
我在 Burp 中用"Extensions → BApps → JavaScript Parser"加载小程序 JS 包(从https://servicewechat.com/xxx/appservice.js下载),搜索关键词wx.request、sign、md5,快速定位签名生成函数:
function generateSign(t, n) { var e = "a1b2c3d4e5f6g7h8"; // 这就是 secret_key! return md5(t + n + e); }将a1b2c3d4e5f6g7h8记下,再用 Burp 的"Extensions → BApps → Autorize"插件,配置自动重放时动态生成签名:在Requesttab 中勾选Auto-generate signature,填入X-WX-Timestamp、X-WX-Nonce、X-WX-Sign字段名及secret_key,即可全自动构造合法请求。
实操心得:Autorize 插件的签名模板必须用
{{timestamp}}、{{nonce}}占位符,不能写死时间戳。我曾因填了固定值1698765432,导致重放请求全部 401,排查 2 小时才发现是时间戳过期——微信服务端校验误差仅 ±300 秒。
4.3 数据清洗与结构化:从原始响应到可读 JSON
微信小程序返回的响应体常被 gzip 压缩,且部分字段是 base64 编码的二进制数据(如图片 URL、加密参数)。Burp 默认不自动解压,需手动操作:
- 右键响应 →
Response → Decode as → GZIP - 若看到
data:application/octet-stream;base64,...,复制 base64 字符串 →Decoder → Paste → Decode as → Base64→ 查看原始内容
更高效的方式是启用"Proxy → Options → Match and Replace"的响应自动解码:
- Match:
Content-Encoding: gzip\r\n - Replace:
Content-Encoding: identity\r\n - Action: Remove header
Content-Encoding
再配合"Extensions → BApps → JSON Beautifier",所有响应自动格式化为可读 JSON,字段层级一目了然。
我曾捕获一个电商小程序的GET /api/v1/goods/detail?id=123接口,原始响应是 12KB 乱码,经上述处理后,清晰呈现:
{ "code": 0, "msg": "success", "data": { "id": 123, "name": "iPhone 14 Pro", "price": 799900, "stock": 12, "images": [ "https://res.wx.qq.com/xxx.jpg", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..." ] } }其中price字段单位是“分”,images[1]是 base64 内联图——这些细节,只有亲手捕获、解码、阅读原始响应才能确认,文档里永远不会写。
5. 常见故障排查链路:从 Burp 无流量到接口 403 的完整归因
即便按上述步骤配置完毕,实战中仍会遇到各种“看似正常,实则失败”的情况。以下是我在 2023 年真实踩过的 5 类高频问题,附带完整的排查链路和根因定位方法:
5.1 现象:Burp Proxy History 完全空白,无任何请求记录
排查链路:
① 检查 Proxifier 日志(View → Log Window)→ 是否有qemu-system-x86_64.exe的连接尝试?
→ 若无:说明流量未到达 Proxifier,问题在逍遥模拟器网络层。
→ 进入模拟器设置 → 网络 → 切换为“桥接模式”(Bridge),禁用“NAT 模式”。
② 若 Proxifier 日志显示Connection refused to 127.0.0.1:8080→ Burp 未运行或监听端口错误。
→ 检查 BurpProxy → Options → Proxy Listeners→ 确认127.0.0.1:8080状态为Running,且Bind to address为All interfaces(非Loopback only)。
③ 若 Proxifier 显示连接成功,但 Burp 仍无记录 → Windows 防火墙拦截。
→ 临时关闭防火墙,或添加入站规则:Port: 8080, Protocol: TCP, Action: Allow。
根因归类:网络栈路由中断(70%)、Burp 监听配置错误(20%)、系统防火墙(10%)。
5.2 现象:Burp 有请求,但 Response Body 为空或显示ERR_CONNECTION_REFUSED
排查链路:
① 右键请求 →Do intercept → Response to client→ 查看原始响应头。
→ 若含HTTP/2 502 Bad Gateway→ Proxifier 代理链下游(Burp)无响应,Burp 已崩溃或内存溢出。
② 若响应头为HTTP/1.1 200 OK但 body 为空 → Burp 的"Intercept client requests"开关被意外开启,且拦截规则匹配了该请求,但你未点击Forward。
→ 关闭Proxy → Intercept → Intercept is on,或清空Intercept → Rules。
③ 若响应头含Content-Encoding: br(Brotli 压缩)→ Burp 默认不支持 Brotli 解压。
→ 安装 BApp"Brotli Decoder",或在请求头中删除Accept-Encoding: br。
根因归类:Burp 拦截开关误启(50%)、Brotli 压缩(30%)、Burp 进程异常(20%)。
5.3 现象:HTTPS 请求显示Client Handshake failed,Response 为403
排查链路:
① 查看 BurpProxy → Options → SSL Pass Through列表 → 是否误将*.weixin.qq.com加入?
→ 删除该条目,强制 Burp 解密。
② 检查 BurpProxy → Options → TLS Settings→Support invisible proxying是否勾选?
→ 未勾选则 Burp 尝试与真实服务器握手,但腾讯服务器拒绝中间人,返回 403。
③ 检查逍遥模拟器内Settings → Security → Trusted credentials→ Burp CA 是否显示为“Enabled”?
→ 若为“Disabled”,说明证书未正确安装或 hash 命名错误。
根因归类:SSL Pass Through 误配(40%)、Invisible Proxying 未启用(35%)、证书未生效(25%)。
5.4 现象:登录成功,但后续业务接口全部401 Unauthorized
排查链路:
① 在 BurpProxy → History中筛选POST /api/v1/auth/login→ 查看响应 body 是否含token字段?
→ 若无:小程序未走标准登录流程,可能用wx.checkSession()续期,需捕获GET /api/v1/auth/refresh。
② 若有token,检查后续请求是否携带Authorization: Bearer xxx?
→ 若未携带:Match and Replace 规则未生效,检查正则表达式是否匹配到token(注意 JSON 键名大小写、引号类型)。
③ 若已携带,但仍是 401 → Token 已过期。微信小程序 token 有效期通常为 2 小时,且服务端校验iat(issued at)时间戳。
→ 在 BurpRepeater中手动修改请求头X-WX-Timestamp为当前时间(秒级),重放测试。
根因归类:Token 未自动注入(50%)、Token 过期(30%)、服务端额外校验(20%)。
5.5 现象:接口返回403 Forbidden,且响应体为加密字符串(如{"data":"aGVsbG8="})
排查链路:
① Base64 解码aGVsbG8=→hello,说明服务端返回的是加密 payload,而非原始错误。
→ 这表明小程序前端对响应体做了 AES 解密,密钥可能来自登录响应或本地存储。
② 在 BurpProxy → History中搜索localStorage.getItem、wx.getStorageSync→ 定位密钥存储位置。
→ 常见密钥字段:aes_key、session_key、crypto_key。
③ 使用"Extensions → BApps → AES Decrypter",输入密钥和 IV(通常为固定值0000000000000000),解密响应体。
根因归类:前端响应体加密(80%)、密钥未提取(20%)。
最后分享一个小技巧:在逍遥模拟器中,长按小程序图标 → “应用信息” → “存储” → 清除数据,可强制小程序重新走完整登录流程,方便你捕获初始
code和encryptedData。这比反复卸载重装快 10 倍,是我每天必做的“环境重置”动作。
