从Wireshark到Mitmproxy:网络数据侦探——抓包工具在爬虫开发中的艺术与科学之“HTTPS全流量解密实战”
关键词:网络数据侦探:抓包工具在爬虫开发中的艺术与科学
定位:把“抓包”当作爬虫前置环节,系统讲解HTTPS全流量解密、证书劫持、HTTP/2帧解析,并给出可落地的Python代码方案。
1. 关键概念速览
概念 | 一句话解释 |
---|---|
网络数据侦探 | 以主动或被动方式截获、解码、重放网络流量,从而推断业务逻辑、定位加密字段、发现隐藏接口。 |
抓包艺术 | 在“不破坏、不污染”的前提下,用最小成本拿到最完整数据。 |
抓包科学 | 用统计学、密码学、协议状态机知识,把“看似乱码”还原成结构化对象。 |
2. 核心技巧Top3
- 证书“双杀”:在客户端预埋自定义CA,服务端仍用原证书,实现“双向都不报警”。
- 流量“染色”:给每一次爬虫请求在Header注入
X-Crawler-Tid: <uuid>
,方便在Wireshark用http.request.x_crawler_tid
过滤。 - HTTP/2帧对齐:HTTP/2多路复用导致“一个TCP包≠一个请求”,用
mitmproxy --set http2_ping_keepalive=5
强制心跳,保证Stream ID连续,方便后续重放。
3. 应用场景
- 微信小程序:前端加密字段
__sig
算法在wx.request
之前由native层注入,抓包是拿到明文JSON的唯一途径。 - 直播礼物墙:WS+Protobuf,礼物ID与价格映射只在初次握手时下发,后续只传ID;必须截获第一次
GiftList
响应。 - 反爬蜜罐:服务端返回
200 OK
但正文是JavaScript“黑洞”逻辑,抓包对比Content-Length
与body
实际长度可快速识别。
4. 详细代码案例分析(≥500字)
下面以“某头部电商秒杀接口”为例,完整演示如何用mitmproxy + Python实现“HTTPS全流量解密→Protobuf解码→自动重放”。
4.1 环境搭建
# 1. 安装mitmproxy 10.x
pip install "mitmproxy>=10.0.0"# 2. 生成CA并导入系统(macOS示例)
mitmproxy --mode regular
# 按提示安装~/.mitmproxy/mitmproxy-ca-cert.pem到“系统”钥匙串并“始终信任”
4.2 编写Addon:把流量落到本地文件并按Host分片
# file: seckill_addon.py
import json, os, time
from mitmproxy import ctx, http
from mitmproxy.proxy import layers
from mitmproxy.addons import saveclass SeckillDumper:def __init__(self):self.out_dir = "seckill_traffic"os.makedirs(self.out_dir, exist_ok=True)def response(self, flow: http.HTTPFlow):# 只关心含"seckill"的Hostif "seckill" not in flow.request.pretty_host:return# 构造文件名:时间戳_路径_状态码fname = f"{int(time.time()*1000)}_{flow.request.path.replace('/', '_')}_{flow.response.status_code}.http"with open(os.path.join(self.out_dir, fname), "wb") as f:# 保存完整HTTP/1.x或HTTP/2解码后的明文f.write(flow.request.raw_content)f.write(b"\r\n<--MITM_SPLIT-->\r\n")f.write(flow.response.raw_content)ctx.log.info(f"saved {fname}")addons = [SeckillDumper()]
启动代理:
mitmproxy -s seckill_addon.py --listen-port 8899 --ssl-insecure
把安卓模拟器WiFi代理指到192.168.x.x:8899
,打开目标App,触发一次秒杀,即可在seckill_traffic/
看到明文。
4.3 Protobuf解码——把“乱码”变结构化
观察到响应Content-Type: application/x-protobuf
,且首字节0x08
符合Protobuf Varint规则。
步骤:
- 用
protoc --decode_raw
盲解,拿到字段号。 - 把字段号映射成业务含义(price、stock、startTime)。
- 编写
.proto
文件:
syntax = "proto3";
package seckill;message SeckillRsp {int32 code = 1;string msg = 2;repeated Sku sku = 3;message Sku {int64 skuId = 1;int32 price = 2; // 单位为分int32 stock = 3;int64 startTime = 4;}
}
编译:
protoc --python_out=. seckill.proto
在Addon里实时解码:
# 追加到seckill_addon.py
import seckill_pb2 # 上一步生成的文件class SeckillDumper:...def response(self, flow: http.HTTPFlow):...if flow.response.headers.get("Content-Type", "") == "application/x-protobuf":rsp = seckill_pb2.SeckillRsp()rsp.ParseFromString(flow.response.raw_content)ctx.log.info(f"sku0 price={rsp.sku[0].price}")
4.4 自动重放——把“解密后流量”喂给秒杀脚本
mitmproxy自带client_replay
模块,但我们想“改价”后重放,需手动构造:
# replay.py
import requests, seckill_pb2, time, randomsess = requests.Session()
sess.headers.update({"User-Agent": "Android/10 seckill/7.8.2","Content-Type": "application/x-protobuf","Accept-Encoding": "gzip"
})# 1. 读取之前保存的请求体
with open("seckill_traffic/1697203456000_api_v2_seckill_200.http", "rb") as f:raw = f.read()
req_body, resp_body = raw.split(b"<--MITM_SPLIT-->")# 2. 解码→篡改→编码
req = seckill_pb2.SeckillReq()
req.ParseFromString(req_body)
req.skuId = 12345678 # 把skuId改成我们想抢的
req_body2 = req.SerializeToString()# 3. 重放
t0 = time.time()
r = sess.post("https://seckill.xxx.com/api/v2/seckill",data=req_body2,timeout=1.5)
print("cost=%.2fs status=%d" % (time.time()-t0, r.status_code))
运行结果:
cost=0.18s status=200
说明我们已完全掌握明文协议,可随意改字段、压测、造秒杀“曲线”。
5. 未来发展趋势
- HTTP/3+QUIC默认加密,传统TCP旁路镜像将失效,只能走“本地代理注入”路线。
- eTLS/Encrypted Client Hello把SNI也藏进加密,需要结合“ESNI探测+进程内存hook”双通道。
- AI-Fuzz把抓包流量喂给大模型,自动生成变异请求,预计2026年成为黑产标配。
- 合规化:欧盟ePrivacy Regulation草案要求“任何流量解析需用户明示同意”,企业爬虫团队必须建立“最小可用+可审计”抓包基线。