MITM 抓包代理的实现方案:从用户态代理到驱动层

六种实现层次对比

方案典型工具特点
用户态代理mitmproxy, Charles, Burp Suite配置简单,需安装根证书
TUN 虚拟网卡Clash Meta, sing-box, Surge不依赖系统代理,可抓 UDP/游戏
WinDivertWinDivert + 自定义程序Windows 驱动层,游戏加速器常用
WFP企业安全/DLP 产品微软官方,性能高,不易检测
NDIS FilterWireshark/Npcap二层帧,可抓所有流量
Socket HookFrida, Xposed拦截加密前明文,绕过证书绑定

用户态代理(最常见)

流量路径:

App → 系统代理 → MITM Proxy → 目标服务器

HTTPS 需要:

  1. 生成 CA 根证书,安装到系统信任
  2. 收到 CONNECT hostname:443 后,动态签发 hostname 的域名证书
  3. 与客户端建立一条 TLS,与服务端建立另一条 TLS,在中间解密/重新加密

TUN 虚拟网卡方案

App → 虚拟网卡(TUN) → 用户态协议栈 → 真实网卡

Linux 创建 TUN:

int tun_fd = open("/dev/net/tun", O_RDWR);
// 配置后:所有流量走 TUN,用户态读 IP 包
read(tun_fd, buf, sizeof(buf));

优点:不需要配系统代理,UDP 也能抓,Clash/sing-box 都用这套。

Python 实现最小 HTTPS MITM

第一步:生成 CA

openssl genrsa -out ca.key 2048
openssl req -x509 -new -key ca.key -out ca.crt -days 3650

安装 ca.crt 到系统根证书信任。

第二步:动态签发域名证书

from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import datetime

def gen_cert(hostname: str, ca_key, ca_cert):
    key = rsa.generate_private_key(public_exponent=65537, key_size=2048)

    cert = (
        x509.CertificateBuilder()
        .subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, hostname)]))
        .issuer_name(ca_cert.subject)
        .public_key(key.public_key())
        .serial_number(x509.random_serial_number())
        .not_valid_before(datetime.datetime.utcnow())
        .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))
        .add_extension(x509.SubjectAlternativeName([x509.DNSName(hostname)]), critical=False)
        .sign(ca_key, hashes.SHA256())
    )

    return key, cert

第三步:TLS 劫持

import ssl

# 客户端侧:用伪造证书与客户端握手
def mitm_tls_client(client_socket, hostname, certfile, keyfile):
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ctx.load_cert_chain(certfile, keyfile)
    return ctx.wrap_socket(client_socket, server_side=True)

# 服务端侧:正常连接真实服务器
def connect_server(hostname, port):
    ctx = ssl.create_default_context()
    raw = socket.create_connection((hostname, port))
    return ctx.wrap_socket(raw, server_hostname=hostname)

两端建立后,中间就能读到明文 HTTP 请求:

req = client_tls.recv(8192)
print(req.decode())  # POST /v1/chat/completions ...

HTTP 解析推荐

不要手动解析 HTTP,使用成熟库:

pip install h11   # 轻量,推荐
# 或
pip install httptools

证书缓存

对同一域名动态生成一次后缓存,避免每次重复生成:

cert_cache = {}

def get_cert(hostname):
    if hostname not in cert_cache:
        key, cert = gen_cert(hostname, ca_key, ca_cert)
        cert_cache[hostname] = (key, cert)
    return cert_cache[hostname]

最小模块划分

自己实现一个基础 HTTPS MITM 代理,核心代码约 500~1000 行:

proxy/
├── listener.py      # 监听连接
├── connect.py       # 处理 CONNECT 方法
├── tls.py           # TLS 劫持
├── cert.py          # 动态证书生成/缓存
├── http.py          # HTTP 解析
└── logger.py        # 请求日志

局限性

用户态 HTTP Proxy 在以下场景会遇到问题:

  • QUIC/HTTP3(基于 UDP,不走 CONNECT)
  • App 硬编码不走系统代理
  • Certificate Pinning(固定证书哈希)
  • gRPC over HTTP2

对于需要捕获所有流量的场景(如 AI Agent 全局监听),TUN + gVisor Netstack 扩展性更好。