抓小程序包这件事,我前后在 iOS、安卓、Windows 三个平台上折腾了不下十轮。最快的一次十分钟就跑通了,最慢的一次耗掉整个下午,最后卡在一个特别不起眼的地方:描述文件装好了,证书也导进去了,唯独"证书信任设置"里那个开关没打开,结果小程序里所有 HTTPS 请求全部握手失败,页面一直转圈,关掉抓包工具立刻恢复正常。所以这篇就把我这几轮验证过的步骤、踩过的坑、以及哪些场景其实压根不用抓包,一次性讲清楚。内容主要面向三类人:正在调试自己小程序的开发者,需要排查线上接口问题的测试同学,以及想搞清楚小程序网络链路到底怎么走的同学。全程只讨论自己有权限调试的目标,别人的数据、别人的账号一律不碰。
1. 先判断要不要抓包:微信开发者工具能看到的,就别上抓包工具
很多人一上来就装 Charles、装 Fiddler,折腾两个小时证书,最后发现要看的东西在开发者工具里点一下就有。这个顺序反了,会白白浪费大量时间。抓包工具是"从外部看网络链路"的手段,成本高、变量多;而开发者工具是"从内部看请求对象",成本低、信息全。所以在动手之前,先花三分钟确认一下你要看的东西属于哪一类。
1.1 开发者工具 Network 面板能看到什么、看不到什么
只要你手上有小程序的源码或者调试权限,在开发者工具里跑起来,打开 Network 面板,wx.request发出的每一个请求都会完整列出来:完整 URL、请求方法、请求头、请求体、响应头、响应体、耗时分解(DNS、连接、TLS、等待、下载),甚至连wx.uploadFile、wx.downloadFile的进度都能看到。对于排查"接口返回 500""参数传错了""跨域配置不对"这类问题,这个面板基本就够了。
它看不到的主要是三类东西。第一类是微信客户端自己发出的请求,比如统计埋点、配置拉取、登录态续期这些,它们不走小程序的 JS 层,Network 面板里不会出现。第二类是 web-view 组件内部加载的网页请求,那部分是普通网页的行为,开发者工具里只能看到一部分。第三类也是最关键的:如果你手里根本没有小程序的源码,比如要排查一个已上线的正式版包,或者想看某个第三方小程序调了哪些接口,开发者工具这条路直接走不通,只能上抓包工具。另外,抓包和反编译是两件完全不同的事,反编译会牵涉代码保护和合规边界,本文只谈前者。
1.2 真机调试模式:把手机上的请求回传到电脑
这是被严重低估的一条路。开发者工具里的"真机调试"功能,手机扫码之后,手机上运行的小程序,它的网络请求、Console 日志、Storage 数据都会回流到电脑上的调试窗口里显示。整个过程不需要安装证书,不需要手机和电脑在同一个局域网,不需要改任何系统设置,扫码就能用。
我现在的习惯是:只要目标小程序我有调试权限,一律先用真机调试。它比抓包工具省掉了证书信任、网络转发、域名过滤这一整套麻烦事,而且拿到的请求体是解码后的结构化数据,不是十六进制流。它的限制也很明确:只能用于自己名下的开发版或体验版小程序,正式版包是连不上的。所以当你面对的是一个只能看到界面、拿不到代码的小程序时,才真正需要往下走。
1.3 什么情况下必须上抓包工具
把这几轮经验汇总一下,真正需要动用抓包工具的场景大概是这几种。一是要分析网络链路本身的问题,比如小程序首屏慢,你要区分是 DNS 慢、TLS 握手慢,还是连接没复用导致重复握手,这些信息只有在网络层抓包才看得到。二是要观察微信客户端自身与服务端的交互,比如登录态是怎么续期的、配置是什么时候拉的。三是需要做协议层的时间线还原,判断哪些请求是串行的、哪些是并行的。四是要看 TLS 握手细节、重传、丢包这类传输层问题。如果你的目标只是"看看这个接口返回了什么",那大概率用不着抓包工具。
2. 小程序流量的真实走向:为什么浏览器抓包那一套直接失效
搞清楚流量从哪里来、走哪条路,比记住工具菜单在哪里重要得多。很多人抓不到包,不是工具配错了,而是抓错了对象——盯着错误的进程或者错误的层次,怎么调都看不到东西。
2.1 wx.request 出微信客户端之后的链路
小程序里写wx.request,请求并不是由 JS 引擎直接发出去的。JS 层只是把参数交给微信客户端的原生网络模块,真正建立连接、发数据的是客户端的原生代码:在 iOS 上走系统提供的网络会话框架,在安卓上走微信自己的网络库。这就解释了一个现象——你在电脑浏览器上用的那些抓包插件、开发者工具的网络面板,对小程序统统无效,因为请求压根没在浏览器层出现过。
手机端的通用思路是从更外层切入:让手机的出网流量先经过电脑上运行的抓包工具,由工具完成 TLS 握手后再把明文拿给你看。这个方案能成立的前提只有一个,手机必须信任工具自签的那张根证书。所有的坑几乎都出在这一步,后面第 3 节会详细拆。
2.2 PC 端小程序进程与手机端小程序的链路差异
电脑端微信的小程序跑在一个叫WeChatAppEx.exe的进程里,它用的是 Chromium 内核,所以会像浏览器一样分出主进程、渲染子进程、GPU 进程等好几个实例。这一点很重要:你在任务管理器里盯着WeChat.exe看半天,是找不到小程序请求的,因为那些请求全在WeChatAppEx.exe身上。
Chromium 内核的网络栈通常会读取系统层的网络设置,所以电脑上打开抓包工具之后,把小程序完全退出再重新打开,多数情况下流量就会出现在工具里。但也确实遇到过某些版本不吃这一套,这时候就需要用进程级的流量接管工具,把WeChatAppEx.exe这个特定进程的流量单独导过去。另外要注意,小程序里wx.request走的通道和 web-view 里网页走的通道不完全一样,如果你的目标是网页里的请求,过滤条件得单独设。
2.3 两类抓包工具的能力边界
工具选错了,后面全是无用功。按原理分,能用来处理小程序流量的工具就那么几类,各自的擅长范围差别很大。
| 工具类型 | 代表工具 | 能否解 HTTPS | 最适合的场景 | 明显短板 |
|---|---|---|---|---|
| 解密型抓包工具 | Charles、Fiddler、Reqable、mitmproxy | 能,需安装并信任根证书 | 看请求头、请求体、响应结构 | 目标做证书校验时会直接断链 |
| 网卡层抓包 | Wireshark、tcpdump、dumpcap | 默认不能,需密钥日志文件 | 看握手、重传、连接复用、时序 | 数据量大,读包门槛高 |
| 手机端独立 App | 各类自带本地流量接管的安卓工具 | 能,走系统证书或本地接管 | 手边没电脑时快速看一眼 | 证书信任受系统版本限制大 |
| 无线层抓包 | 支持监听模式的无线网卡 + Wireshark | 不能 | 看无线帧、认证关联过程 | 需要特定网卡,配置麻烦 |
表格里最后一类容易被忽略,但确实有它的位置:当你需要看设备接入无线网络时的认证交互过程,或者设备根本没拿到 IP、连不上外网的时候,只有无线层的抓包手段能看到东西。至于命令行工具,它的价值在长时间、大流量场景,后面第 7 节会讲怎么用它避开内存问题。
3. 手机端抓包的坎:证书信任这一关怎么过
这一节是整篇里失败率最高的部分。我把 iOS 和安卓分开讲,因为两者的信任机制差别很大,一个坑在"开关",一个坑在"系统分区"。
3.1 iOS:装完描述文件还要开"完全信任"
iOS 上的流程分两步,很多人只做了第一步。第一步是在手机上访问抓包工具提供的证书下载地址(打开工具后界面里会给出),系统会提示安装描述文件,走完设置里的安装流程。第二步才是关键:进入系统设置的证书信任相关页面,把刚装的那张证书对应的开关手动打开,标记为完全信任。这一步不做,证书在系统里就是"已安装但不受信任"的状态,所有 HTTPS 请求都会因为证书链校验失败而中断。
失败时的现象很典型:小程序页面一直转圈或者白屏,抓包工具里能看到连接建立了,但握手阶段直接断开,没有任何明文数据。遇到这个现象,第一反应就应该是去检查那个信任开关。另外一个容易忘的点是证书有效期,工具生成的根证书有有效期限,装了半年之后突然抓不到包,先去确认证书是不是过期了,重新生成再装一遍。
3.2 安卓:Android 7 之后的用户证书信任变化
安卓这边的情况复杂一些。从 Android 7 开始,应用默认只信任系统分区里的证书,用户自己安装到用户分区的证书不再被默认信任。这意味着在较新的安卓设备上,就算你把证书装好了、系统设置里也能看到,小程序依然会认为连接不安全而拒绝。绕过这个限制的正规做法是把证书写进系统分区,而这需要设备具备 root 权限——root 本身会带来一堆副作用,包括部分应用直接拒绝运行、支付类功能受限、系统更新变麻烦,所以我个人不建议为了抓包去 root 主力机。
比较务实的替代方案是使用那些自带本地流量接管能力的安卓抓包工具。它们不依赖系统证书信任链,而是在设备上注册一个本地的流量接管服务,安装后按系统提示确认连接请求即可,不用 root,也能看到 HTTPS 明文。代价是这类工具的可视化分析能力普遍弱于桌面端工具,批量过滤和长时间记录不太行,适合"临时看一眼"而不是做系统性分析。
3.3 连通性自检的三个现象
与其在小程序里反复试,不如先用手机浏览器做三次自检,成本几秒钟,能立刻定位问题在哪一层。
| 自检动作 | 看到的结果 | 说明的问题 | 下一步 |
|---|---|---|---|
| 手机浏览器打开证书下载页 | 能下载并安装 | 网络可达,工具在正常工作 | 继续往下测 |
| 手机浏览器访问任意 HTTPS 站点 | 工具里能看到明文 | 证书已被信任,解密链路通了 | 直接去抓小程序 |
| 上面的站点只看到连接断开 | 工具里没有明文 | 证书没被信任或已过期 | 回到 3.1 或 3.2 处理 |
| 打开小程序,列表里没有目标域名 | 一条记录都没有 | 小程序可能走了独立通道,或进程没重启 | 完全退出小程序再进一次 |
| 打开小程序,页面转圈 | 有连接但握手失败 | 证书校验未通过,且该目标做了校验 | 参考第 5 节换思路 |
这里有个顺序上的小技巧:一定先确认"手机浏览器能不能看到明文",再去开小程序。因为浏览器的证书校验策略最宽松,如果连浏览器都解不开,那问题百分之百在证书环节,跟小程序本身没关系。
4. 电脑端抓小程序:找准进程比配工具更重要
电脑端的优势是工具功能强、过滤方便、可以离线慢慢筛,劣势是进程多、噪音大。这一节把定位流程按顺序讲清楚。
4.1 WeChatAppEx.exe 与那几个常被搞混的进程
打开任务管理器,把微信相关的进程展开,你会看到一长串:WeChat.exe是主进程,负责登录、消息、通讯录这些;WeChatAppEx.exe是小程序与内置浏览器的容器,基于 Chromium 内核;再往下还有若干带--type=renderer、--type=gpu-process参数的子进程。小程序里的网络请求,责任主体是WeChatAppEx.exe及其子进程。
如果想确认某个窗口对应哪个进程,可以借助进程查看工具,直接把窗口句柄拖进去,它会告诉你这个窗口属于哪个 PID。这在同时开了好几个小程序、或者同时开了小程序和公众号文章页的时候特别有用。经验是:不要按名字猜,按窗口反查,一次就准。还有一点,每次重新打开小程序,进程可能会变(旧进程退出、新进程起来),所以如果你用的是按进程筛选的方式,记得重新确认一次。
4.2 流量没进来时的排查顺序
"工具开着,小程序也在跑,但列表里一条目标记录都没有"——这是最常见的卡点。我一般按这个顺序排查:
- 先确认抓包工具确实在监听,换个浏览器访问一下普通网站,看工具里有没有记录。没记录说明工具本身没工作,或者端口被别的程序占了。
- 确认系统层的网络设置有没有被工具改过去。这类工具启动时通常会修改系统设置,正常退出时改回来;如果上次是强制结束进程的,设置可能残留在那里,导致所有程序都连不上网,或者反过来根本没生效。
- 把小程序彻底退干净再重新打开。很多设置只在进程启动时读取一次,不重启不生效,这是最容易被忽略的一步。
- 前面三步都确认了还是没流量,就考虑用进程级的流量接管工具,把
WeChatAppEx.exe的流量单独导到抓包工具的监听端口上,绕开系统设置这一层。 - 最后检查有没有别的抓包工具、安全软件、网络管理类程序在同时抢占,这种情况会导致流量被截到另一个工具里,你在当前工具里当然看不到。
4.3 用域名过滤把噪音压下去
微信自身的域名会产生大量噪音记录,刷新一下会话列表可能就是几十条,目标请求混在里面很难找。正确做法是加过滤规则,只保留你关心的域名。在 Charles 里用 Filter 输入框,直接填目标域名;在 Fiddler 里用 Filters 面板,勾选只显示指定 Host;Reqable 和 mitmproxy 也都支持类似的 Host 过滤语法。
我的习惯是先全量抓 30 秒左右,把会话存成归档文件,再离线加过滤慢慢看。这样做的好处是:如果第一次过滤条件写错了、把目标请求也筛掉了,不用重新复现问题,直接在归档里改条件就行。尤其是那些只在特定操作下才触发的请求,复现一次很费劲,存档比什么都重要。另外要注意区分"页面自己的域名"和"第三方统计、CDN 的域名",后者往往是噪音大户,可以在过滤规则里一并排除。
5. HTTPS 只看到乱码:用密钥日志文件让 Wireshark 解开
有时候你会发现,抓包工具里能看到连接、能看到握手过程,但内容全是十六进制,或者干脆就是连接被对方单方面断开。这时候别硬碰,换个思路从链路层入手。
5.1 解密型抓包工具失效的两种典型情况
第一种情况是目标做了证书校验,也就是常说的证书固定。客户端在代码里内置了它期望的证书指纹,握手时发现对方给的证书对不上,直接断开连接。这种情况下所有解密型抓包工具都会失效,你会看到连接建立了、握手开始了、然后被 RST 掉。遇到这种不要把时间浪费在"想办法绕过去"上,那是另一个层面的问题,而且涉及合规边界。
第二种情况是目标根本不走标准 HTTPS。有些客户端与服务端之间用的是自己封装的加密通道,在传输层看就是一段普通的 TCP 数据流,端口可能是 443 但不是标准 TLS 结构,任何基于证书的解密方案都无能为力。这两种情况的共同点是:解密型工具的路走到了尽头,但链路层的信息依然完整可看——握手耗时、连接复用情况、数据包大小分布、重传次数,这些数据本身就足够回答"为什么慢""为什么偶发失败"这类问题了。
5.2 SSLKEYLOGFILE 的设置与生效验证
Chromium 系的内核支持读取一个特定的环境变量,把 TLS 会话密钥以标准格式写到一个文件里。有了这个文件,Wireshark 就能解密对应的 TLS 流量,包括 TLS 1.3。电脑端小程序容器是 Chromium 内核,这条路值得一试。
Windows 上设置用户级环境变量的命令是:
# 设置用户级环境变量,路径按需修改 [Environment]::SetEnvironmentVariable("SSLKEYLOGFILE", "D:\capture\sslkeys.log", "User") # 验证是否写入成功 [Environment]::GetEnvironmentVariable("SSLKEYLOGFILE", "User")设置完成之后,必须把微信完全退出——注意是彻底退出,右下角托盘图标也要退出,只关窗口不算——然后重新启动。启动之后正常操作小程序,几秒钟后去看那个文件,如果里面开始出现一行行以CLIENT_RANDOM或CLIENT_HANDSHAKE_TRAFFIC_SECRET开头的内容,说明密钥导出成功了。如果文件一直不存在或者一直为空,说明当前版本的内嵌内核没有保留这个逻辑,这条路走不通,那就只做链路层分析。
注意:这个文件里包含的是本次会话的密钥材料,用完请及时删除,不要留在共享目录或者随手打包发出去。
5.3 Wireshark 里的配置项与解密后的验证
拿到密钥文件之后,在 Wireshark 里找到传输层安全相关的协议首选项,把密钥日志文件的路径填进去。抓包时用显示过滤器收窄范围:
tcp.port == 443配置正确之后,原本只显示 TLS 记录层的那些包,会多出一行应用层协议,能看到完整的请求方法和路径。判断是否解密成功的标志很简单:在协议列里如果出现了 HTTP 或 HTTP2 的字样,就说明成功了;如果始终只有 TLS,检查三件事——密钥文件里有没有对应这次连接的条目、配置文件路径有没有写对、抓包是不是在密钥生成之前就开始了(Wireshark 需要包和密钥都在手上才能解)。
值得一提的是,这条路拿到的明文比解密型工具更"原始",你能看到真实的帧顺序、真实的 ACK 时序、真实的 TCP 窗口变化。分析首屏慢的问题时,这些信息比请求体有用得多。
6. 抓到的包怎么读:从请求列表到可用结论
抓包本身不是目的,把一堆记录变成能指导优化的结论才是。这一节讲三个我常用的读包角度。
6.1 首屏链路:按时间轴还原加载顺序
把抓到的会话按时间排序,先找首页数据那个核心请求,然后看它相对于其他请求的位置。重点观察三件事:第一,有没有明显的串行瀑布——如果第二个请求的开始时间等于第一个请求的结束时间,说明代码里是等 A 返回才发 B,这种结构可以通过并行化直接砍掉一半时间。第二,连接复用了没有——如果同一域名的多个请求每次都重新握手,说明连接池没配好或者域名被打散了。第三,DNS 和 TLS 各占了多少时间——在时间轴详情里能看到细分,如果 TLS 握手占了首屏时间的三成以上,优化方向就是会话复用和预连接。
这套分析方法有个前提:你得抓到一次完整的冷启动流程。所以我一般会先把小程序从后台彻底清掉,再从头操作一遍,确保时间轴是干净的。
6.2 参数与响应的对照分析
看请求参数的时候,重点关注分页方式和筛选条件。分页是offset+limit还是游标式的cursor,直接决定了列表滚动时的请求数量级;筛选条件是全部塞在 URL 里还是放在请求体里,反映了接口设计的风格差异。看响应结构的时候,重点关注返回字段的组织方式,是平铺的还是嵌套的,有没有冗余字段,列表接口有没有一次性返回全量数据。
还有一个很实用的观察角度:同一个接口在不同场景下的调用差异。比如同一个列表接口,在"全部"标签下和在"已关注"标签下,请求参数差了什么,返回结构变了没有。这类对比能帮你在自己实现类似功能时少走弯路。这里必须强调一点:观察参数是为了理解接口设计思路,不是去伪造请求或者绕开权限校验。自己没权限调用的东西,看到也不要碰。
6.3 常见异常与对应现象排查表
| 现象 | 可能原因 | 用什么办法确认 |
|---|---|---|
| 请求列表里出现重复的同一接口 | 页面重渲染导致重复请求 | 对比请求时间与页面状态变化节点 |
| 大量请求失败但页面正常 | 失败的是统计类请求,不影响主流程 | 看失败请求的域名归属 |
| 接口返回很快但页面渲染慢 | 瓶颈在 JS 层或渲染层,不在网络 | 结合开发者工具的性能面板交叉验证 |
| 首次请求特别慢,后续很快 | 冷启动期 DNS 解析与握手成本 | 看时间轴里的 DNS 和 TLS 分段 |
| 同一接口偶发超时 | 连接池耗尽或网络抖动 | 看超时时刻前后的重传记录 |
| 请求体里有一串看不懂的签名 | 客户端生成的校验参数 | 只做记录,不要去逆向伪造 |
7. 亲测踩坑清单:这几个问题最耗时
最后把这几个反复出现、每次都耗费大量时间的问题集中列一下,都是实打实撞过的。
7.1 开抓包后小程序直接转圈
这个现象第一次遇到的时候我以为是工具把小程序的流量搞坏了,查了很久。实际原因是证书没有被信任,TLS 握手在客户端校验阶段就失败了,小程序拿不到任何数据,所以一直转圈。判断方法很简单:把抓包工具关掉,如果小程序立刻恢复正常,那就是证书的问题,不是网络转发的问题。去把信任开关补上就行。还有一种情况是工具异常退出后,系统网络设置残留在那里没改回来,导致整个电脑都上不了网,这时候手动把系统网络设置恢复默认即可,抓包前记得正常退出工具,别用强制结束。
7.2 装了证书仍然报证书错误
排查顺序是:证书有没有过期、是不是旧的证书还留在系统里没删干净、设备系统时间对不对。最后这一条最容易被忽略——如果手机或电脑的系统时间偏差太大,证书的有效期校验会直接失败,表现和证书没装一样。另外提醒一句,某些工具在更新版本之后会重新生成根证书,旧证书需要手动卸载,两台证书同时存在会互相干扰。
7.3 pyshark 抓不到包与长时间抓包的内存问题
用 pyshark 做自动化分析时踩过几个坑,基本都是环境问题而不是代码问题。第一,pyshark 依赖 tshark,必须保证 tshark 在系统 PATH 里能找到,装了 Wireshark 但没勾选命令行组件的情况下,导入时就报错。第二,在部分系统上实时抓包需要管理员权限,否则接口直接打开失败。第三,接口参数建议用名字而不是编号,编号在不同环境下会变。第四,在 Jupyter 这类已经跑着事件循环的环境里,实时抓取会因为事件循环冲突而报错,这种情况可以改用文件离线分析,先抓包存盘再读。
import pyshark # 离线分析更可靠:先存盘,再逐包读取 cap = pyshark.FileCapture( "capture.pcapng", display_filter="tcp.port == 443", use_json=True, include_raw=False ) for pkt in cap: print(pkt.number, pkt.sniff_time) cap.close()长时间抓包的内存问题也要提前防。图形界面工具默认把所有包存在内存里,挂几个小时之后内存暴涨,最后直接崩掉。正确做法是用命令行工具做环形缓冲,只保留最近若干个文件,每个文件到一定大小就切一个:
# 每 100MB 切一个文件,最多保留 20 个,循环覆盖 dumpcap -i 1 -s 0 -b filesize:102400 -b files:20 -w /capture/ring.pcapng这样跑一整天也不会有内存问题,事后从切分好的文件里挑时间段分析就行。我现在的习惯是:需要长时间观察的时候一律用命令行分片,图形界面只用来做最后的分析和展示。
最后分享一个我自己反复验证过的小技巧:抓包之前先在纸上写清楚"我要回答哪个问题",比如"首屏这个接口为什么慢",然后只抓能回答这个问题的数据。没有目标的抓包,最后大概率会变成对着几千条记录发呆。