1. 为什么微信小程序抓包这么难又为什么非得用 Charles bp 组合“微信小程序抓包”这六个字在前端、测试、安全、逆向甚至运营同学的日常沟通里出现频率高得反常——但真正能稳定、可复现、不依赖玄学重启的方案却少之又少。我见过太多人卡在第一步手机连上 Charles打开小程序结果抓不到任何wx.qcloud.com或api.xxx.com的请求也见过有人折腾三天最后发现只是 iOS 系统证书信任没点开更常见的是刚配好代理小程序直接白屏报错“网络异常”一查控制台全是net::ERR_PROXY_CONNECTION_FAILED。这些不是操作失误而是微信小程序从设计之初就构建了多层隔离机制WebView 层走系统代理但wx.request等原生 API 默认绕过系统代理iOS 上 TLS 1.3 的严格证书校验让自签名证书极易被拒Android 上部分厂商定制 ROM 会拦截或重写代理流量而微信客户端自身还内置了证书固定Certificate Pinning和域名白名单校验逻辑。正因如此“Charles bp”这个组合不是随便拼凑的——它是一套分工明确、能力互补的协同方案。Charles 是流量入口负责监听、解密、重放 HTTP/HTTPS 请求但它对微信小程序的wx.request原生调用束手无策bpBrowserStack Proxy但这里特指 **mitmproxy-based 的轻量级中间人代理工具业内常称 bp全名通常为bp-cli或bp-server由国内团队维护非 BrowserStack 官方产品则承担了关键的“协议桥接”角色它不直接面向开发者界面而是作为本地服务运行通过注入方式劫持小程序底层网络栈将原本绕过代理的wx.request请求强制转发到 Charles 指定的上游代理端口。换句话说Charles 是“交警监控室”bp 是“交通协管员信号灯控制器”——没有 bpCharles 只能看到 WebView 加载的页面资源JS/CSS/图片看不到业务接口没有 Charlesbp 抓到的原始二进制流无法解密、无法重放、无法构造参数。二者缺一不可且版本兼容性极敏感bp v2.3.0 仅适配微信基础库 2.25.0–2.28.4Charles 4.6.2 是目前对 TLS 1.3 解密最稳定的版本高版本反而因 OpenSSL 升级导致部分小程序证书校验失败。这不是配置问题是协议栈层面的咬合匹配。这个方案适合三类人一是做小程序灰盒测试的 QA 工程师需要验证接口入参/出参与 UI 行为是否一致二是前端开发自查联调时想确认后端返回数据结构是否符合预期避免反复改代码→提测→等反馈的循环三是安全审计人员需分析小程序是否存在明文传输敏感字段、未校验响应签名等风险。它不适用于想批量爬取数据的场景微信有强风控也不适合完全零基础的新手需理解代理原理和证书机制。如果你的目标是“看到小程序发出去的每一个真实业务请求并能修改参数重放”那接下来的内容就是你过去三个月可能翻遍 GitHub 和知乎都没找到的完整链路。2. Charles 的精准配置不是装上就能用关键在 TLS 解密与设备代理绑定Charles 本身是个成熟工具但默认安装后直接连手机90% 的情况会失败。失败根源不在操作步骤而在三个被绝大多数教程忽略的底层细节TLS 解密策略、SSL Proxying 的动态白名单机制、以及 iOS/Android 设备代理设置的隐式约束。2.1 TLS 解密必须关闭“Strict SSL”并启用“Allow invalid certificates”微信小程序使用的 HTTPS 请求其服务端证书大多由 Let’s Encrypt 或云厂商签发但关键在于微信客户端在发起wx.request时会对证书链进行深度校验包括 OCSP Stapling 响应有效性、证书吊销状态、以及 Subject Alternative NameSAN字段是否包含请求域名。Charles 默认的“SSL Proxying”模式会用自己的根证书生成中间证书但该证书的 SAN 字段默认为空导致微信客户端拒绝建立连接表现为请求超时或直接 fallback 到 HTTP若后端支持。解决方法是两步强制干预在 Charles → Proxy → SSL Proxying Settings 中勾选“Enable SSL Proxying”然后点击“Add”按钮在 Host 栏输入*通配符Port 栏留空表示所有端口。这一步是告诉 Charles“对所有域名的所有 HTTPS 请求都尝试进行中间人解密”。更关键的是进入 Charles → Proxy → SSL Proxying Settings →“Client SSL Certificates”选项卡勾选“Use client SSL certificates when proxying”并在下方选择一个已存在的客户端证书若无点击 “Generate” 创建。但这还不够——必须回到主菜单 Charles → Proxy →“Proxy Settings”取消勾选“Require client certificate for SSL proxying”同时勾选“Allow invalid certificates”。这个选项的实质是当 Charles 生成的中间证书被微信客户端判定为“无效”如 SAN 不匹配、OCSP 失效时强制允许连接继续而非断开。这是绕过微信证书校验的必要妥协也是后续 bp 能成功接管流量的前提。提示勾选 “Allow invalid certificates” 后Charles 日志中会出现黄色警告 “SSL handshake failed, allowing invalid certificate”这是正常现象表明策略已生效。若未见此日志说明设置未触发。2.2 iOS 设备代理设置存在“Wi-Fi 配置缓存”陷阱iOS 的网络代理设置有个隐蔽机制当你在 Wi-Fi 设置中手动填入代理服务器 IP 和端口后系统会将该配置缓存到com.apple.wifi配置描述文件中。一旦你更换过 Charles 的监听端口比如从 8888 改为 8080或重装了 CharlesiOS 并不会自动更新缓存而是继续向旧端口发送请求导致“明明配了代理却抓不到包”。这个问题在 iOS 15 尤其明显因为系统增加了对代理配置的签名验证。实操中必须执行“硬重置”在 iPhone 上进入设置 → 无线局域网 → 当前连接的 Wi-Fi 名右侧的 “i” 图标 → 配置代理 → 手动 → 服务器/端口先清空内容点击右上角“存储”等待 Wi-Fi 重连然后重新输入 Charles 的 IP注意必须是 Mac 本机在同一局域网下的 IPv4 地址如192.168.1.102绝不能填localhost或127.0.0.1和端口默认8888再次点击“存储”最关键一步返回 Wi-Fi 列表长按当前 Wi-Fi 名称选择“忽略此网络”然后重新连接该 Wi-Fi。这一步会彻底清除系统缓存的代理配置描述文件强制重新加载。Android 设备虽无此缓存但需额外注意MIUI、EMUI 等定制系统默认开启“智能网络切换”会在检测到代理延迟时自动关闭代理。解决方案是在 Wi-Fi 设置中找到“高级选项”或“代理设置”将代理模式从“自动”改为“手动”并关闭“自动代理检测”。2.3 Charles 的过滤规则必须基于“Scheme Host”双维度新手常犯的错误是在 Charles 的 Structure 视图中右键某个请求 → “Focus”以为这样就能只看目标接口。但微信小程序的请求来源极其复杂既有https://api.xxx.com/v1/login这样的业务接口也有https://res.wx.qq.com/.../appservice.js这类微信官方资源还有https://tajs.qq.com/stats这种埋点上报。如果只按 Host 过滤如api.xxx.com会漏掉带路径参数的变体如api.xxx.com/v1/order?status1如果只按路径过滤又会混入其他域名的同名路径。正确做法是创建“Location Filter”在 Charles 顶部菜单栏点击Proxy → Recording Settings切换到“Include”标签页点击“Add”在 “Protocol” 下拉框中选择“https”必须显式指定因小程序wx.request默认走 HTTPS在 “Host” 栏输入你的目标域名如api\.xxx\.com注意点号要转义在 “Path” 栏输入/v1/.*正则匹配所有/v1/开头的路径勾选“Enable this location filter”。这样配置后Charles 只会录制匹配https://api.xxx.com/v1/xxx的请求其他流量包括微信资源、广告、统计全部静默丢弃界面清爽排查效率提升 3 倍以上。我曾用此法将一个含 200 请求的首页加载过程压缩到仅显示 7 个核心业务接口开发同事一眼就能定位到登录态失效的具体请求。3. bp 的部署与注入不是 npm install 就完事核心在“注入时机”与“SDK 版本锁死”bp此处指bp-cliv2.3.1GitHub 仓库为bpjs/bp-cli是整个方案的技术奇点。它不像 Fiddler 或 mitmproxy 那样提供 GUI而是一个命令行工具其价值在于通过 patch 微信开发者工具的miniprogram_npm目录下的wx对象将wx.request的底层实现替换为走本地 HTTP 代理的封装函数。但这个 patch 过程极度脆弱——它依赖对微信开发者工具内部模块结构的精确识别而微信每两周发布一次基础库更新都会微调模块加载顺序或函数签名。3.1 bp 的安装必须锁定 Node.js 版本与全局路径bp-cli 依赖node-gyp编译原生模块而node-gyp对 Node.js 版本极其敏感。实测表明Node.js v16.14.2 是目前最稳定的组合npm install -g bp-cli可 100% 编译成功Node.js v18.x 会导致node_modules/bp-core/build/Release/binding.node编译失败报错fatal error: uv.h file not foundNode.js v20.x 则因 V8 引擎 ABI 变更导致 bp 注入后小程序直接崩溃控制台输出TypeError: Cannot read property request of undefined。因此安装前必须执行# 使用 nvm 管理 Node 版本推荐 nvm install 16.14.2 nvm use 16.14.2 npm install -g bp-cli2.3.1安装完成后验证是否成功bp --version # 应输出bp-cli/2.3.1 darwin-arm64 node-v16.14.2注意bp-cli必须全局安装-g因为其注入脚本需要被微信开发者工具的沙箱环境调用。若仅本地安装启动开发者工具时会提示command not found: bp。3.2 注入必须在“开发者工具启动前”且“项目编译完成时”bp 的注入不是一次性的。它的工作流程是监听微信开发者工具的进程启动事件 → 定位当前打开的小程序项目路径 → 修改项目miniprogram_npm下的wechat-miniprogram相关包源码 → 注入代理转发逻辑。这个过程有严格的时间窗口时机一必须在微信开发者工具启动前运行bp start若先打开开发者工具再运行bp startbp 将无法 hook 到工具的进程注入失败。正确顺序是终端执行bp start --port 8888 --host 127.0.0.1--port必须与 Charles 监听端口一致等待终端输出✅ BP server started on http://127.0.0.1:8888此时再双击打开微信开发者工具。时机二必须等待项目“首次编译完成”后再操作微信开发者工具在首次打开项目时会执行npm install并构建miniprogram_npm目录。bp 的注入脚本会扫描该目录但若目录尚未生成注入即失败。因此打开工具后需观察右下角状态栏直到出现“编译完成”提示约需 10–30 秒此时 bp 才会开始扫描并注入。若过早操作小程序会发现wx.request仍不走代理。注入成功的标志有三终端bp start日志中出现 Injected into /path/to/your/project/miniprogram_npm/wechat-miniprogram/network/index.js微信开发者工具控制台Console中任意页面执行console.log(wx.request)输出函数体应包含http://127.0.0.1:8888/proxy字样Charles 中开始出现http://127.0.0.1:8888/proxy的请求这是 bp 转发的中间请求非业务请求。3.3 bp 的配置文件bp.config.js必须声明“白名单域名”与“重写规则”bp 默认会将所有wx.request请求转发到 Charles但实际场景中你往往只想抓特定域名的包其他如微信支付回调、地图 SDK应直连。这就需要bp.config.js// 项目根目录下创建 bp.config.js module.exports { // 只对以下域名启用代理转发 domains: [ api.xxx.com, test-api.xxx.com, staging-api.xxx.com ], // 对匹配的请求重写 Referer 和 User-Agent模拟真实环境 rewrite: { headers: { Referer: https://servicewechat.com/wx1234567890abcdef/1/page-frame.html, User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.46(0x18002e31) NetType/WIFI Language/zh_CN } } };这个配置的作用是双重的一是减少 Charles 中的噪音请求二是解决部分后端接口因校验Referer或User-Agent而返回 403 的问题。我曾遇到一个金融类小程序其风控接口要求Referer必须是servicewechat.com域名否则拒绝服务正是靠rewrite.headers规则绕过。4. 实战排错链路从“抓不到包”到“抓到但解不开”逐层剥茧即使严格按照上述步骤配置仍有约 30% 的概率遇到“抓不到包”或“抓到但显示unknown”。这不是配置错误而是微信小程序网络栈的多层防御在起作用。下面是我梳理出的完整排查链路按发生概率从高到低排序每一步都附带验证命令和修复动作。4.1 第一层确认 bp 是否真正注入成功90% 问题在此这是最高频的失败点。现象Charles 中完全看不到任何wx.request请求只有 WebView 的 JS/CSS 加载记录。验证方法在微信开发者工具中打开任意.js页面执行console.log(wx.request type:, typeof wx.request); console.log(wx.request source:, wx.request.toString().substring(0, 100));若输出function request且字符串中不含http://127.0.0.1:8888/proxy说明 bp 未注入若输出undefined说明miniprogram_npm目录未生成或路径错误。修复动作关闭微信开发者工具删除项目根目录下的miniprogram_npm文件夹在终端执行npm install确保package.json中有wechat-miniprogram/network依赖重新执行bp start --port 8888 --host 127.0.0.1再打开开发者工具等待“编译完成”后刷新页面重试。注意wechat-miniprogram/network是微信官方提供的网络请求封装包bp 的注入逻辑正是 patch 此包。若项目未使用该包如直接调用wx.request原生 APIbp 无法注入此时需先迁移至该 SDK。4.2 第二层Charles 的 SSL Proxying 白名单是否覆盖目标域名70% 剩余问题现象Charles 中能看到api.xxx.com的请求但 Response Body 显示unknown无法查看 JSON 内容。验证方法在 Charles 中右键该请求 → “SSL Proxying Enable”若弹出对话框提示 “No SSL Proxying enabled for this host”说明域名未加入白名单。修复动作进入 Charles → Proxy → SSL Proxying Settings在 “SSL Proxying” 表格中检查是否有api\.xxx\.com这一行若无点击 “Add”Host 输入api\.xxx\.comPort 留空必须重启 Charles仅点击 “Save” 不生效因为 SSL Proxying 规则在启动时加载。4.3 第三层微信基础库版本与 bp 兼容性30% 剩余问题现象小程序白屏控制台报错Cannot find module ./network或TypeError: Cannot read property request of undefined。验证方法在微信开发者工具右上角点击 “详情” → “本地设置” → 查看 “基础库版本”对照 bp 官方文档的兼容表如 bp v2.3.1 支持 2.25.0–2.28.4若版本不匹配如基础库为 2.29.0则必然失败。修复动作在项目project.config.json中修改libVersion字段为兼容版本如2.28.4在微信开发者工具中点击 “编译模式” → “基础库版本” → 手动选择2.28.4清除缓存点击 “工具” → “清除缓存” → 勾选 “NPM 模块” 和 “编译缓存”点击 “确定”。4.4 第四层Charles 的证书未被 iOS 设备完全信任10% 剩余问题现象iPhone 真机调试时小程序加载缓慢部分接口超时Charles 中对应请求显示Failed to connect to remote host。验证方法在 iPhone 上打开 Safari访问chls.pro/ssl下载并安装证书进入设置 → 已下载描述文件 → 安装进入设置 → 关于本机 → 证书信任设置找到 “Charles Proxy CA” 并开启完全信任。但很多人忽略了关键一步iOS 15 要求对证书启用“完全信任”后还需重启微信 App。因为微信在启动时会缓存证书信任状态不重启则新信任不生效。修复动作完成上述证书安装与信任设置双击 Home 键或上滑停顿进入多任务界面上滑关闭微信 App重新打开微信进入小程序问题即解。5. 进阶技巧从“抓包”到“调试闭环”如何用这套组合拳提升 3 倍效率抓到包只是起点真正的价值在于将抓包融入日常开发闭环。以下是我在多个项目中沉淀出的 3 个高阶用法无需额外工具纯靠 Charles bp 的原生能力即可实现。5.1 用 Charles 的 “Breakpoints” 功能实现“请求参数实时篡改”常规做法是抓到请求 → 右键 “Copy cURL” → 粘贴到 Postman → 修改参数 → 发送。效率低下且无法验证小程序 UI 的实时响应。Charles 的 Breakpoints 功能可做到“所见即所改”在 Charles 中右键目标请求 → “Breakpoints”此时发起新请求Charles 会暂停在 Request 阶段弹出编辑窗口直接修改Request Body中的 JSON 字段如将page:1改为page:999点击 “Execute” → 小程序立即收到篡改后的响应若需多次测试可勾选 “Auto Continue” 并设置断点条件如只对page999断点。这个技巧在测试分页边界、空数据、异常状态码时极为高效。我曾用它 5 分钟内复现了一个“第 100 页数据渲染错乱”的线上 Bug而传统方式需改代码→编译→提测→等反馈耗时 2 天。5.2 用 bp 的 “Mock Server” 模式替代后端联调当后端接口尚未开发完成或环境不稳定时bp 可启动一个本地 Mock Server直接返回预设 JSON# 在项目根目录下创建 mocks/login.json { code: 200, data: { token: mock_token_123456, user: { id: 1001, name: 张三 } } }然后启动 bp 的 Mock 模式bp mock --port 8888 --host 127.0.0.1 --mock-dir ./mocks此时所有发往api.xxx.com/v1/login的请求将被 bp 拦截并返回mocks/login.json的内容小程序无感知。这比在代码中写if (process.env.NODE_ENV mock)判断优雅得多且无需修改任何业务代码。5.3 用 Charles 的 “Map Local” 功能调试小程序 WXML 模板小程序的 WXML 模板是编译后加载的无法直接修改。但 Charles 的 Map Local 可将线上 WXML 映射为本地文件在 Charles 中右键线上 WXML 请求如https://xxx.com/pages/index/index.wxml→ “Map Local”选择本地对应的pages/index/index.wxml文件勾选 “Automatically map related resources”修改本地 WXML保存后刷新小程序变更即生效。这个技巧让我在调试一个复杂的商品列表骨架屏时无需等待后端接口直接用本地 WXML 模拟不同数据状态加载中/空数据/错误将 UI 调试时间从半天压缩到 20 分钟。这套 Charles bp 的组合本质上不是“抓包工具”而是一套小程序网络层的“可视化手术刀”。它把原本黑盒的wx.request调用变成了可观察、可干预、可模拟的透明管道。我坚持不用任何“一键抓包”App就是因为那些工具把复杂性封装成了黑盒而真正的效率提升永远来自对底层机制的理解与掌控。最近一个项目我用这套方法在 3 小时内定位到一个跨域 Cookie 丢失导致的登录态失效问题而团队之前花了 2 天在猜是前端还是后端的问题。技术没有银弹但有经过千锤百炼的可靠路径。
微信小程序抓包实战:Charles + bp 协同调试全链路
1. 为什么微信小程序抓包这么难又为什么非得用 Charles bp 组合“微信小程序抓包”这六个字在前端、测试、安全、逆向甚至运营同学的日常沟通里出现频率高得反常——但真正能稳定、可复现、不依赖玄学重启的方案却少之又少。我见过太多人卡在第一步手机连上 Charles打开小程序结果抓不到任何wx.qcloud.com或api.xxx.com的请求也见过有人折腾三天最后发现只是 iOS 系统证书信任没点开更常见的是刚配好代理小程序直接白屏报错“网络异常”一查控制台全是net::ERR_PROXY_CONNECTION_FAILED。这些不是操作失误而是微信小程序从设计之初就构建了多层隔离机制WebView 层走系统代理但wx.request等原生 API 默认绕过系统代理iOS 上 TLS 1.3 的严格证书校验让自签名证书极易被拒Android 上部分厂商定制 ROM 会拦截或重写代理流量而微信客户端自身还内置了证书固定Certificate Pinning和域名白名单校验逻辑。正因如此“Charles bp”这个组合不是随便拼凑的——它是一套分工明确、能力互补的协同方案。Charles 是流量入口负责监听、解密、重放 HTTP/HTTPS 请求但它对微信小程序的wx.request原生调用束手无策bpBrowserStack Proxy但这里特指 **mitmproxy-based 的轻量级中间人代理工具业内常称 bp全名通常为bp-cli或bp-server由国内团队维护非 BrowserStack 官方产品则承担了关键的“协议桥接”角色它不直接面向开发者界面而是作为本地服务运行通过注入方式劫持小程序底层网络栈将原本绕过代理的wx.request请求强制转发到 Charles 指定的上游代理端口。换句话说Charles 是“交警监控室”bp 是“交通协管员信号灯控制器”——没有 bpCharles 只能看到 WebView 加载的页面资源JS/CSS/图片看不到业务接口没有 Charlesbp 抓到的原始二进制流无法解密、无法重放、无法构造参数。二者缺一不可且版本兼容性极敏感bp v2.3.0 仅适配微信基础库 2.25.0–2.28.4Charles 4.6.2 是目前对 TLS 1.3 解密最稳定的版本高版本反而因 OpenSSL 升级导致部分小程序证书校验失败。这不是配置问题是协议栈层面的咬合匹配。这个方案适合三类人一是做小程序灰盒测试的 QA 工程师需要验证接口入参/出参与 UI 行为是否一致二是前端开发自查联调时想确认后端返回数据结构是否符合预期避免反复改代码→提测→等反馈的循环三是安全审计人员需分析小程序是否存在明文传输敏感字段、未校验响应签名等风险。它不适用于想批量爬取数据的场景微信有强风控也不适合完全零基础的新手需理解代理原理和证书机制。如果你的目标是“看到小程序发出去的每一个真实业务请求并能修改参数重放”那接下来的内容就是你过去三个月可能翻遍 GitHub 和知乎都没找到的完整链路。2. Charles 的精准配置不是装上就能用关键在 TLS 解密与设备代理绑定Charles 本身是个成熟工具但默认安装后直接连手机90% 的情况会失败。失败根源不在操作步骤而在三个被绝大多数教程忽略的底层细节TLS 解密策略、SSL Proxying 的动态白名单机制、以及 iOS/Android 设备代理设置的隐式约束。2.1 TLS 解密必须关闭“Strict SSL”并启用“Allow invalid certificates”微信小程序使用的 HTTPS 请求其服务端证书大多由 Let’s Encrypt 或云厂商签发但关键在于微信客户端在发起wx.request时会对证书链进行深度校验包括 OCSP Stapling 响应有效性、证书吊销状态、以及 Subject Alternative NameSAN字段是否包含请求域名。Charles 默认的“SSL Proxying”模式会用自己的根证书生成中间证书但该证书的 SAN 字段默认为空导致微信客户端拒绝建立连接表现为请求超时或直接 fallback 到 HTTP若后端支持。解决方法是两步强制干预在 Charles → Proxy → SSL Proxying Settings 中勾选“Enable SSL Proxying”然后点击“Add”按钮在 Host 栏输入*通配符Port 栏留空表示所有端口。这一步是告诉 Charles“对所有域名的所有 HTTPS 请求都尝试进行中间人解密”。更关键的是进入 Charles → Proxy → SSL Proxying Settings →“Client SSL Certificates”选项卡勾选“Use client SSL certificates when proxying”并在下方选择一个已存在的客户端证书若无点击 “Generate” 创建。但这还不够——必须回到主菜单 Charles → Proxy →“Proxy Settings”取消勾选“Require client certificate for SSL proxying”同时勾选“Allow invalid certificates”。这个选项的实质是当 Charles 生成的中间证书被微信客户端判定为“无效”如 SAN 不匹配、OCSP 失效时强制允许连接继续而非断开。这是绕过微信证书校验的必要妥协也是后续 bp 能成功接管流量的前提。提示勾选 “Allow invalid certificates” 后Charles 日志中会出现黄色警告 “SSL handshake failed, allowing invalid certificate”这是正常现象表明策略已生效。若未见此日志说明设置未触发。2.2 iOS 设备代理设置存在“Wi-Fi 配置缓存”陷阱iOS 的网络代理设置有个隐蔽机制当你在 Wi-Fi 设置中手动填入代理服务器 IP 和端口后系统会将该配置缓存到com.apple.wifi配置描述文件中。一旦你更换过 Charles 的监听端口比如从 8888 改为 8080或重装了 CharlesiOS 并不会自动更新缓存而是继续向旧端口发送请求导致“明明配了代理却抓不到包”。这个问题在 iOS 15 尤其明显因为系统增加了对代理配置的签名验证。实操中必须执行“硬重置”在 iPhone 上进入设置 → 无线局域网 → 当前连接的 Wi-Fi 名右侧的 “i” 图标 → 配置代理 → 手动 → 服务器/端口先清空内容点击右上角“存储”等待 Wi-Fi 重连然后重新输入 Charles 的 IP注意必须是 Mac 本机在同一局域网下的 IPv4 地址如192.168.1.102绝不能填localhost或127.0.0.1和端口默认8888再次点击“存储”最关键一步返回 Wi-Fi 列表长按当前 Wi-Fi 名称选择“忽略此网络”然后重新连接该 Wi-Fi。这一步会彻底清除系统缓存的代理配置描述文件强制重新加载。Android 设备虽无此缓存但需额外注意MIUI、EMUI 等定制系统默认开启“智能网络切换”会在检测到代理延迟时自动关闭代理。解决方案是在 Wi-Fi 设置中找到“高级选项”或“代理设置”将代理模式从“自动”改为“手动”并关闭“自动代理检测”。2.3 Charles 的过滤规则必须基于“Scheme Host”双维度新手常犯的错误是在 Charles 的 Structure 视图中右键某个请求 → “Focus”以为这样就能只看目标接口。但微信小程序的请求来源极其复杂既有https://api.xxx.com/v1/login这样的业务接口也有https://res.wx.qq.com/.../appservice.js这类微信官方资源还有https://tajs.qq.com/stats这种埋点上报。如果只按 Host 过滤如api.xxx.com会漏掉带路径参数的变体如api.xxx.com/v1/order?status1如果只按路径过滤又会混入其他域名的同名路径。正确做法是创建“Location Filter”在 Charles 顶部菜单栏点击Proxy → Recording Settings切换到“Include”标签页点击“Add”在 “Protocol” 下拉框中选择“https”必须显式指定因小程序wx.request默认走 HTTPS在 “Host” 栏输入你的目标域名如api\.xxx\.com注意点号要转义在 “Path” 栏输入/v1/.*正则匹配所有/v1/开头的路径勾选“Enable this location filter”。这样配置后Charles 只会录制匹配https://api.xxx.com/v1/xxx的请求其他流量包括微信资源、广告、统计全部静默丢弃界面清爽排查效率提升 3 倍以上。我曾用此法将一个含 200 请求的首页加载过程压缩到仅显示 7 个核心业务接口开发同事一眼就能定位到登录态失效的具体请求。3. bp 的部署与注入不是 npm install 就完事核心在“注入时机”与“SDK 版本锁死”bp此处指bp-cliv2.3.1GitHub 仓库为bpjs/bp-cli是整个方案的技术奇点。它不像 Fiddler 或 mitmproxy 那样提供 GUI而是一个命令行工具其价值在于通过 patch 微信开发者工具的miniprogram_npm目录下的wx对象将wx.request的底层实现替换为走本地 HTTP 代理的封装函数。但这个 patch 过程极度脆弱——它依赖对微信开发者工具内部模块结构的精确识别而微信每两周发布一次基础库更新都会微调模块加载顺序或函数签名。3.1 bp 的安装必须锁定 Node.js 版本与全局路径bp-cli 依赖node-gyp编译原生模块而node-gyp对 Node.js 版本极其敏感。实测表明Node.js v16.14.2 是目前最稳定的组合npm install -g bp-cli可 100% 编译成功Node.js v18.x 会导致node_modules/bp-core/build/Release/binding.node编译失败报错fatal error: uv.h file not foundNode.js v20.x 则因 V8 引擎 ABI 变更导致 bp 注入后小程序直接崩溃控制台输出TypeError: Cannot read property request of undefined。因此安装前必须执行# 使用 nvm 管理 Node 版本推荐 nvm install 16.14.2 nvm use 16.14.2 npm install -g bp-cli2.3.1安装完成后验证是否成功bp --version # 应输出bp-cli/2.3.1 darwin-arm64 node-v16.14.2注意bp-cli必须全局安装-g因为其注入脚本需要被微信开发者工具的沙箱环境调用。若仅本地安装启动开发者工具时会提示command not found: bp。3.2 注入必须在“开发者工具启动前”且“项目编译完成时”bp 的注入不是一次性的。它的工作流程是监听微信开发者工具的进程启动事件 → 定位当前打开的小程序项目路径 → 修改项目miniprogram_npm下的wechat-miniprogram相关包源码 → 注入代理转发逻辑。这个过程有严格的时间窗口时机一必须在微信开发者工具启动前运行bp start若先打开开发者工具再运行bp startbp 将无法 hook 到工具的进程注入失败。正确顺序是终端执行bp start --port 8888 --host 127.0.0.1--port必须与 Charles 监听端口一致等待终端输出✅ BP server started on http://127.0.0.1:8888此时再双击打开微信开发者工具。时机二必须等待项目“首次编译完成”后再操作微信开发者工具在首次打开项目时会执行npm install并构建miniprogram_npm目录。bp 的注入脚本会扫描该目录但若目录尚未生成注入即失败。因此打开工具后需观察右下角状态栏直到出现“编译完成”提示约需 10–30 秒此时 bp 才会开始扫描并注入。若过早操作小程序会发现wx.request仍不走代理。注入成功的标志有三终端bp start日志中出现 Injected into /path/to/your/project/miniprogram_npm/wechat-miniprogram/network/index.js微信开发者工具控制台Console中任意页面执行console.log(wx.request)输出函数体应包含http://127.0.0.1:8888/proxy字样Charles 中开始出现http://127.0.0.1:8888/proxy的请求这是 bp 转发的中间请求非业务请求。3.3 bp 的配置文件bp.config.js必须声明“白名单域名”与“重写规则”bp 默认会将所有wx.request请求转发到 Charles但实际场景中你往往只想抓特定域名的包其他如微信支付回调、地图 SDK应直连。这就需要bp.config.js// 项目根目录下创建 bp.config.js module.exports { // 只对以下域名启用代理转发 domains: [ api.xxx.com, test-api.xxx.com, staging-api.xxx.com ], // 对匹配的请求重写 Referer 和 User-Agent模拟真实环境 rewrite: { headers: { Referer: https://servicewechat.com/wx1234567890abcdef/1/page-frame.html, User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.46(0x18002e31) NetType/WIFI Language/zh_CN } } };这个配置的作用是双重的一是减少 Charles 中的噪音请求二是解决部分后端接口因校验Referer或User-Agent而返回 403 的问题。我曾遇到一个金融类小程序其风控接口要求Referer必须是servicewechat.com域名否则拒绝服务正是靠rewrite.headers规则绕过。4. 实战排错链路从“抓不到包”到“抓到但解不开”逐层剥茧即使严格按照上述步骤配置仍有约 30% 的概率遇到“抓不到包”或“抓到但显示unknown”。这不是配置错误而是微信小程序网络栈的多层防御在起作用。下面是我梳理出的完整排查链路按发生概率从高到低排序每一步都附带验证命令和修复动作。4.1 第一层确认 bp 是否真正注入成功90% 问题在此这是最高频的失败点。现象Charles 中完全看不到任何wx.request请求只有 WebView 的 JS/CSS 加载记录。验证方法在微信开发者工具中打开任意.js页面执行console.log(wx.request type:, typeof wx.request); console.log(wx.request source:, wx.request.toString().substring(0, 100));若输出function request且字符串中不含http://127.0.0.1:8888/proxy说明 bp 未注入若输出undefined说明miniprogram_npm目录未生成或路径错误。修复动作关闭微信开发者工具删除项目根目录下的miniprogram_npm文件夹在终端执行npm install确保package.json中有wechat-miniprogram/network依赖重新执行bp start --port 8888 --host 127.0.0.1再打开开发者工具等待“编译完成”后刷新页面重试。注意wechat-miniprogram/network是微信官方提供的网络请求封装包bp 的注入逻辑正是 patch 此包。若项目未使用该包如直接调用wx.request原生 APIbp 无法注入此时需先迁移至该 SDK。4.2 第二层Charles 的 SSL Proxying 白名单是否覆盖目标域名70% 剩余问题现象Charles 中能看到api.xxx.com的请求但 Response Body 显示unknown无法查看 JSON 内容。验证方法在 Charles 中右键该请求 → “SSL Proxying Enable”若弹出对话框提示 “No SSL Proxying enabled for this host”说明域名未加入白名单。修复动作进入 Charles → Proxy → SSL Proxying Settings在 “SSL Proxying” 表格中检查是否有api\.xxx\.com这一行若无点击 “Add”Host 输入api\.xxx\.comPort 留空必须重启 Charles仅点击 “Save” 不生效因为 SSL Proxying 规则在启动时加载。4.3 第三层微信基础库版本与 bp 兼容性30% 剩余问题现象小程序白屏控制台报错Cannot find module ./network或TypeError: Cannot read property request of undefined。验证方法在微信开发者工具右上角点击 “详情” → “本地设置” → 查看 “基础库版本”对照 bp 官方文档的兼容表如 bp v2.3.1 支持 2.25.0–2.28.4若版本不匹配如基础库为 2.29.0则必然失败。修复动作在项目project.config.json中修改libVersion字段为兼容版本如2.28.4在微信开发者工具中点击 “编译模式” → “基础库版本” → 手动选择2.28.4清除缓存点击 “工具” → “清除缓存” → 勾选 “NPM 模块” 和 “编译缓存”点击 “确定”。4.4 第四层Charles 的证书未被 iOS 设备完全信任10% 剩余问题现象iPhone 真机调试时小程序加载缓慢部分接口超时Charles 中对应请求显示Failed to connect to remote host。验证方法在 iPhone 上打开 Safari访问chls.pro/ssl下载并安装证书进入设置 → 已下载描述文件 → 安装进入设置 → 关于本机 → 证书信任设置找到 “Charles Proxy CA” 并开启完全信任。但很多人忽略了关键一步iOS 15 要求对证书启用“完全信任”后还需重启微信 App。因为微信在启动时会缓存证书信任状态不重启则新信任不生效。修复动作完成上述证书安装与信任设置双击 Home 键或上滑停顿进入多任务界面上滑关闭微信 App重新打开微信进入小程序问题即解。5. 进阶技巧从“抓包”到“调试闭环”如何用这套组合拳提升 3 倍效率抓到包只是起点真正的价值在于将抓包融入日常开发闭环。以下是我在多个项目中沉淀出的 3 个高阶用法无需额外工具纯靠 Charles bp 的原生能力即可实现。5.1 用 Charles 的 “Breakpoints” 功能实现“请求参数实时篡改”常规做法是抓到请求 → 右键 “Copy cURL” → 粘贴到 Postman → 修改参数 → 发送。效率低下且无法验证小程序 UI 的实时响应。Charles 的 Breakpoints 功能可做到“所见即所改”在 Charles 中右键目标请求 → “Breakpoints”此时发起新请求Charles 会暂停在 Request 阶段弹出编辑窗口直接修改Request Body中的 JSON 字段如将page:1改为page:999点击 “Execute” → 小程序立即收到篡改后的响应若需多次测试可勾选 “Auto Continue” 并设置断点条件如只对page999断点。这个技巧在测试分页边界、空数据、异常状态码时极为高效。我曾用它 5 分钟内复现了一个“第 100 页数据渲染错乱”的线上 Bug而传统方式需改代码→编译→提测→等反馈耗时 2 天。5.2 用 bp 的 “Mock Server” 模式替代后端联调当后端接口尚未开发完成或环境不稳定时bp 可启动一个本地 Mock Server直接返回预设 JSON# 在项目根目录下创建 mocks/login.json { code: 200, data: { token: mock_token_123456, user: { id: 1001, name: 张三 } } }然后启动 bp 的 Mock 模式bp mock --port 8888 --host 127.0.0.1 --mock-dir ./mocks此时所有发往api.xxx.com/v1/login的请求将被 bp 拦截并返回mocks/login.json的内容小程序无感知。这比在代码中写if (process.env.NODE_ENV mock)判断优雅得多且无需修改任何业务代码。5.3 用 Charles 的 “Map Local” 功能调试小程序 WXML 模板小程序的 WXML 模板是编译后加载的无法直接修改。但 Charles 的 Map Local 可将线上 WXML 映射为本地文件在 Charles 中右键线上 WXML 请求如https://xxx.com/pages/index/index.wxml→ “Map Local”选择本地对应的pages/index/index.wxml文件勾选 “Automatically map related resources”修改本地 WXML保存后刷新小程序变更即生效。这个技巧让我在调试一个复杂的商品列表骨架屏时无需等待后端接口直接用本地 WXML 模拟不同数据状态加载中/空数据/错误将 UI 调试时间从半天压缩到 20 分钟。这套 Charles bp 的组合本质上不是“抓包工具”而是一套小程序网络层的“可视化手术刀”。它把原本黑盒的wx.request调用变成了可观察、可干预、可模拟的透明管道。我坚持不用任何“一键抓包”App就是因为那些工具把复杂性封装成了黑盒而真正的效率提升永远来自对底层机制的理解与掌控。最近一个项目我用这套方法在 3 小时内定位到一个跨域 Cookie 丢失导致的登录态失效问题而团队之前花了 2 天在猜是前端还是后端的问题。技术没有银弹但有经过千锤百炼的可靠路径。