文章首发于: https://feinterview.poetries.top/blog/wireguard-mitmproxy-ios-app-https-capture
做移动端联调,抓包是绕不过去的基本功。Charles / Proxyman 这类工具走的是「手机设 Wi-Fi 代理指向电脑」的老路子,够用,但有两个死穴:一是很多 App 的流量不吃系统 HTTP 代理(尤其是自己管理连接、走 QUIC/WebSocket、或用了非标准端口的),你设了代理它照样绕过去;二是代理一开,手机所有 App 都受影响,还容易和科学上网工具打架。这篇文章换一条更「底层」的路:用 WireGuard 建一条手机到 Mac 的隧道,把手机全部流量(不管走不走系统代理)都逼进这条隧道,再在 Mac 上用 pf 防火墙把 80/443 透明重定向到 mitmproxy,最后靠 mitmproxy 的中间人证书解出 HTTPS 明文。
我最近就是用这套方案抓一台 iPhone 上某交易类 App(Binance 港版)的真机接口,从 Mac 装工具、生成密钥、扫码导入、pf 规则、到手机装证书信任,整条链路又踩了一遍,顺手把每步都记了下来。这篇就以这套实操为线索,把 Mac 端隧道 + 透明代理 和 iOS 端隧道 + 证书信任 两段拆开讲透,让你照着做一次就能通。
在本篇文章中,我们将从浅入深,和大家一起学习以下知识:
WireGuard隧道抓包相比传统Wi-Fi代理抓包强在哪、适合什么场景- 整条链路的数据流长什么样:手机 → 隧道 →
pf重定向 →mitmproxy→ 出网 Mac上怎么装wireguard-tools和mitmproxy(含brew cask装不出 CLI 的坑)- 怎么用
wg genkey生成双端密钥、写wg0.conf/phone.conf、qrencode扫码导入手机 pf规则怎么写才能同时做NAT出网 +80/443重定向到mitmproxy- 为什么透明模式的
mitmweb必须用sudo跑,不然一直报Insufficient privileges - iOS 怎么装
mitmproxy的CA证书,并在「关于本机」里开启完全信任这一步 - 抓不到某个 App 明文时,怎么判断是不是撞上了
SSL Pinning,以及能怎么办 - 怎么把整套抓包沉淀成
Claude Code的SKILL,让 AI 自己起服务、过滤域名、提取接口
# 一、整体架构
先把整条链路画清楚,你就知道每个组件在干嘛、凭证从哪来:
┌─────────────┐ WireGuard 加密隧道 ┌──────────────────────────────────────┐
│ iPhone │ 10.9.0.2 ─────────▶ │ Mac (10.9.0.1) │
│ │ (全流量进隧道 │ │
│ WireGuard │ AllowedIPs=0.0.0.0/0)│ utun (wg 接口) │
│ 已装CA并信任 │ │ │ │
└─────────────┘ │ ▼ pf rdr 80/443 │
│ mitmproxy 透明代理 :8080 │
│ │ (用自己的CA重签,解出明文) │
│ ▼ pf nat 出 en0 │
│ 物理网卡 en0 ───────▶ 互联网 │
│ │
│ mitmweb 界面 :9091 (看/改报文) │
└──────────────────────────────────────┘
三个关键点,理解了就不会瞎调:
- 为什么用隧道而不是
Wi-Fi代理:WireGuard客户端配AllowedIPs = 0.0.0.0/0,意味着手机把所有IP流量都塞进隧道,App 想绕都绕不开(这是隧道相对系统代理最大的优势)。 - 明文是怎么来的:
HTTPS是加密的,mitmproxy做「中间人」——用自己的根证书CA重新签发目标站点证书。手机必须先信任这张CA,加密流量才能被解开,否则你只能看到一堆CONNECT和加密字节。 - 谁来把
80/443交给mitmproxy:靠Mac的pf防火墙做重定向(rdr),其余端口(DNS、WebSocket、直连等)走NAT正常出网。
# 二、10 分钟跑通最小可用版本
下面是「复制即可跑」的最小闭环。全部操作在 Mac(Apple Silicon,Homebrew 装在 /opt/homebrew)+ 一台 iPhone 上完成,手机和电脑连同一个 Wi-Fi。
# Step 1:Mac 装 wireguard-tools 和 mitmproxy
wireguard-tools 直接 brew 装;国内网络慢可以挂 USTC 镜像:
# wireguard 命令行工具(wg / wg-quick)+ 生成二维码用的 qrencode
HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles \
HOMEBREW_NO_AUTO_UPDATE=1 \
brew install wireguard-tools qrencode
mitmproxy 这里有个大坑:brew 的 mitmproxy 现在是 cask,装出来只有一个 mitmproxy.app 的 GUI,没有 mitmweb / mitmdump 命令行二进制,透明代理根本起不来。正确做法是用 Python venv + pip 装:
mkdir -p ~/wg-capture && cd ~/wg-capture
python3 -m venv venv
# 国内用清华源;若卡在某个包的 hash 上,换官方 PyPI + 代理即可
~/wg-capture/venv/bin/pip install mitmproxy -i https://pypi.tuna.tsinghua.edu.cn/simple
# 验证:能打印版本就对了
~/wg-capture/venv/bin/mitmweb --version
装完先别急着跑,mitmproxy 第一次跑会在 ~/.mitmproxy 生成 CA,我们后面手机装的就是这张证书——所以后续所有命令都要用同一个 confdir,否则手机装的证书对不上。
# Step 2:生成双端密钥 + 写两份配置
WireGuard 是公私钥体系,服务端(Mac)和客户端(手机)各一对。umask 077 保证私钥不被别的用户读到:
cd ~/wg-capture && umask 077
wg genkey | tee server.key | wg pubkey > server.pub # Mac 端
wg genkey | tee phone.key | wg pubkey > phone.pub # 手机端
Mac 端配置 wg0.conf——Address 用一个私有网段,ListenPort 固定 51820,Peer 填手机的公钥:
[Interface]
Address = 10.9.0.1/24
ListenPort = 51820
PrivateKey = <server.key 的内容>
[Peer]
# phone
PublicKey = <phone.pub 的内容>
AllowedIPs = 10.9.0.2/32
手机端配置 phone.conf——重点是 AllowedIPs = 0.0.0.0/0(全流量进隧道),Endpoint 填 Mac 的 Wi-Fi 局域网 IP(用 ipconfig getifaddr en0 查,比如 192.168.5.136):
[Interface]
PrivateKey = <phone.key 的内容>
Address = 10.9.0.2/24
DNS = 8.8.8.8
[Peer]
PublicKey = <server.pub 的内容>
Endpoint = 192.168.5.136:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
# Step 3:扫码把配置导入手机 WireGuard
iOS 上装 WireGuard App(若在国区商店搜不到,用港区或美区 Apple ID 下载即可)。然后在 Mac 上把 phone.conf 转成二维码,手机 App 里点右上角 + → 「从二维码创建」扫一下就导入了:
cd ~/wg-capture
qrencode -o phone-qr.png -s 12 -m 4 < phone.conf && open phone-qr.png
导入后先别急着开隧道,等 Mac 侧的重定向和 mitmproxy 都起来再连,否则手机会短暂断网。下图是手机 WireGuard App 里导入好的隧道(我这条起名叫「抓包」):

# Step 4:起隧道 + pf 重定向(一个脚本搞定)
把下面存成 ~/wg-capture/up.sh。它做三件事:起 WireGuard 隧道、打开 IP 转发、加载 pf 规则(NAT 出网 + 80/443 重定向到 mitmproxy):
#!/bin/bash
set -e
DIR="$(cd "$(dirname "$0")" && pwd)"
CONF="$DIR/wg0.conf"
UPLINK="${UPLINK:-en0}" # 出网物理网卡,Wi-Fi 一般是 en0
MITM_PORT="${MITM_PORT:-8080}" # mitmproxy 透明代理端口
wg-quick up "$CONF"
WG_IF="$(wg show interfaces)" # wg-quick 在 macOS 上会映射到某个 utunN
sysctl -w net.inet.ip.forwarding=1
pfctl -f - -e <<EOF
nat on $UPLINK from 10.9.0.0/24 to any -> ($UPLINK)
rdr pass on $WG_IF inet proto tcp from 10.9.0.0/24 to any port { 80, 443 } -> 127.0.0.1 port $MITM_PORT
EOF
echo "✅ 隧道 + NAT + 重定向就绪 (WG_IF=$WG_IF, UPLINK=$UPLINK)"
跑它(要 sudo,因为动 pf 和路由):
sudo ~/wg-capture/up.sh
# Step 5:sudo 起 mitmweb 透明代理
这一步最容易翻车:透明模式下 mitmproxy 需要读 /dev/pf 还原每个连接的真实目标地址,必须用 sudo,否则一直报 Insufficient privileges to access pfctl、解不了密。同时用 --set confdir 锁定到 ~/.mitmproxy,保证复用 Step 1 生成的那张 CA:
起来后终端会打印一行带 token 的地址,浏览器打开它就是抓包界面:
Web server listening at http://127.0.0.1:9091/?token=xxxxxxxx
# 三、iOS 装 mitmproxy 证书并开启完全信任



