mitmproxy 完全指南
最后更新:2026-03-31 相关文档:js逆向调试技巧(/js-ni-xiang-diao-shi-ji-qiao/) Frida基础(/frida-ji-chu-zhi-nan/) 反反爬技术汇总(/fan-fan-pa-ji-zhu-hui-zong/) mitmproxy 是一个开源的中间人代理工具,支持拦截、修改、重放 HTTP/HTTPS 流量。 mitmproxy 通过 Python 插件实现自动化处理。 只处理目标域名,减少无关噪声: 未安装证书时,HTTPS 请求会显示为 << Killed 或 SSL Error,需在目标设备安装并
官方文档:https://docs.mitmproxy.org/
适用版本:mitmproxy 10.x+(2026-05-08 核实)
最后更新:2026-03-31
1. 基础概念
mitmproxy 是什么
mitmproxy 是一个开源的中间人代理工具,支持拦截、修改、重放 HTTP/HTTPS 流量。
| 组件 | 说明 |
|---|---|
mitmproxy |
交互式终端界面(TUI) |
mitmweb |
Web 界面,可视化操作 |
mitmdump |
命令行脚本模式,可编写插件 |
安装
pip install mitmproxy
# 或下载预编译二进制
# https://mitmproxy.org/downloads/
证书安装(HTTPS 抓包前提)
# 1. 启动 mitmproxy
mitmproxy
# 2. 设备配置代理指向 mitmproxy(默认端口 8080)
# 3. 访问 http://mitm.it 下载证书
# 4. 安装证书为受信任根证书
# Android 证书安装(需 root)
adb push ~/.mitmproxy/mitmproxy-ca-cert.pem /sdcard/
# 在手机上:设置 → 安全 → 从存储设备安装证书
# iOS 证书安装
# 访问 http://mitm.it,下载 iOS 证书并信任
2. 基础使用
启动方式
# 普通代理模式
mitmproxy -p 8080
# Web 界面模式
mitmweb -p 8080
# 脚本模式
mitmdump -s script.py -p 8080
# 透明代理模式(不修改客户端配置,需 iptables 配合)
mitmproxy --mode transparent -p 8080
# 上游代理(mitmproxy → 上游代理 → 目标)
mitmproxy --mode upstream:http://proxy:8888
# SOCKS5 代理模式
mitmproxy --mode socks5 -p 1080
mitmproxy TUI 快捷键
| 按键 | 功能 |
|---|---|
Enter |
查看请求详情 |
q |
返回上一级 |
f |
设置过滤器 |
i |
设置拦截规则 |
a |
放行拦截的请求 |
r |
重放请求 |
e |
编辑请求/响应 |
d |
删除条目 |
z |
清空列表 |
w |
保存流量到文件 |
过滤语法
# 过滤域名
~d example.com
# 过滤 URL 包含关键词
~u api/sign
# 过滤请求方法
~m POST
# 过滤响应状态码
~s 200
# 组合条件
~d example.com & ~u api
# 排除静态资源
~d example.com & !~u \.(js|css|png|jpg|gif)$
3. 插件开发(核心用法)
mitmproxy 通过 Python 插件实现自动化处理。
插件基础结构
# script.py
from mitmproxy import http, ctx
class MyAddon:
def request(self, flow: http.HTTPFlow) -> None:
"""拦截请求"""
pass
def response(self, flow: http.HTTPFlow) -> None:
"""拦截响应"""
pass
addons = [MyAddon()]
打印所有请求
from mitmproxy import http
class Logger:
def request(self, flow: http.HTTPFlow) -> None:
print(f">> {flow.request.method} {flow.request.pretty_url}")
if flow.request.content:
print(f" Body: {flow.request.content[:200]}")
def response(self, flow: http.HTTPFlow) -> None:
print(f"<< {flow.response.status_code} {flow.request.pretty_url}")
addons = [Logger()]
捕获 API 响应(提取加密参数)
import json
from mitmproxy import http
class ApiCapture:
def response(self, flow: http.HTTPFlow) -> None:
# 只处理目标 API
if "api.example.com" not in flow.request.host:
return
if "/sign" not in flow.request.path:
return
# 获取请求头(找 sign 参数位置)
headers = dict(flow.request.headers)
print("Request Headers:", json.dumps(headers, indent=2))
# 获取请求体
try:
body = json.loads(flow.request.content)
print("Request Body:", json.dumps(body, indent=2, ensure_ascii=False))
except Exception:
print("Request Body (raw):", flow.request.content)
# 获取响应体
try:
resp = json.loads(flow.response.content)
print("Response:", json.dumps(resp, indent=2, ensure_ascii=False))
except Exception:
pass
addons = [ApiCapture()]
修改请求
from mitmproxy import http
class RequestModifier:
def request(self, flow: http.HTTPFlow) -> None:
# 添加/修改请求头
flow.request.headers["X-Custom-Header"] = "my-value"
flow.request.headers["Authorization"] = "Bearer fake-token"
# 修改请求参数
if flow.request.path.startswith("/api/search"):
flow.request.query["page"] = "1"
# 替换请求体(JSON)
if "application/json" in flow.request.headers.get("content-type", ""):
import json
body = json.loads(flow.request.content)
body["modified"] = True
flow.request.content = json.dumps(body).encode()
addons = [RequestModifier()]
修改响应(Mock 接口)
import json
from mitmproxy import http
class ResponseMocker:
def response(self, flow: http.HTTPFlow) -> None:
if "/api/list" in flow.request.path:
# 替换响应体
mock_data = {"code": 0, "data": [], "total": 0}
flow.response.content = json.dumps(mock_data).encode()
flow.response.headers["content-type"] = "application/json"
flow.response.status_code = 200
addons = [ResponseMocker()]
重定向请求(本地替换)
from mitmproxy import http
class LocalReplace:
def request(self, flow: http.HTTPFlow) -> None:
# 将线上 JS 替换为本地修改版
if flow.request.path.endswith("/encrypt.js"):
with open("/tmp/encrypt_patched.js", "rb") as f:
flow.response = http.Response.make(
200,
f.read(),
{"Content-Type": "application/javascript"},
)
addons = [LocalReplace()]
4. 流量保存与重放
# 保存流量
mitmdump -w traffic.mitm
# 从文件重放
mitmdump -n -r traffic.mitm -s process.py
# 过滤保存
mitmdump -w traffic.mitm "~d target.com & ~m POST"
# 从保存文件读取并分析
from mitmproxy.io import FlowReader
with open("traffic.mitm", "rb") as f:
reader = FlowReader(f)
for flow in reader.stream():
print(flow.request.pretty_url)
print(flow.response.status_code)
5. 绕过证书检测
Android SSL Pinning 绕过
# 配合 Frida 使用
# 参见 Frida基础.md 中的 SSL Pinning 绕过章节
# 或使用 apk-mitm 工具自动修改 APK 的 network security config
# pip install apk-mitm
# apk-mitm target.apk
忽略特定证书错误
# 允许所有不安全的证书(仅测试用)
mitmproxy --ssl-insecure
处理双向证书认证(Mutual TLS)
# mitmproxy 支持客户端证书
mitmproxy --client-certs /path/to/client.pem
6. 实战场景
场景 1:自动提取 sign 参数并用 Python 重放
# extract_sign.py
import json
import threading
from mitmproxy import http
captured_requests = []
class SignExtractor:
def request(self, flow: http.HTTPFlow) -> None:
if "api.example.com" not in flow.request.host:
return
captured_requests.append({
"url": flow.request.pretty_url,
"headers": dict(flow.request.headers),
"body": flow.request.get_text(),
"timestamp": flow.request.timestamp_start,
})
addons = [SignExtractor()]
场景 2:批量修改请求参数
import json
from mitmproxy import http
class BatchModifier:
"""自动翻页采集:修改 page 参数"""
def __init__(self):
self.current_page = 1
def request(self, flow: http.HTTPFlow) -> None:
if "/api/list" in flow.request.path:
if "application/json" in flow.request.headers.get("content-type", ""):
body = json.loads(flow.request.content)
body["page"] = self.current_page
flow.request.content = json.dumps(body).encode()
def response(self, flow: http.HTTPFlow) -> None:
if "/api/list" in flow.request.path:
data = json.loads(flow.response.content)
# 保存数据...
self.current_page += 1
addons = [BatchModifier()]
7. 最佳实践
精确过滤目标流量
只处理目标域名,减少无关噪声:
TARGET_HOST = "api.example.com"
def request(self, flow: http.HTTPFlow) -> None:
if flow.request.host != TARGET_HOST:
return
# 处理逻辑
使用 ctx.log 替代 print
from mitmproxy import ctx
ctx.log.info("处理请求")
ctx.log.warn("发现异常")
ctx.log.error("严重错误")
8. 踩坑与注意事项
HTTPS 需要安装证书
未安装证书时,HTTPS 请求会显示为 << Killed 或 SSL Error,需在目标设备安装并信任 mitmproxy 根证书。
响应乱码问题
有些响应是 gzip 压缩的,mitmproxy 自动解压,但手动读 flow.response.raw_content 会得到压缩数据,应使用 flow.response.content(自动解压)。
HTTP/2 兼容性
部分服务使用 HTTP/2,可禁用降级到 HTTP/1.1:
mitmproxy --no-http2
脚本热重载
修改插件脚本后不需要重启:
# mitmdump 支持脚本热重载
mitmdump -s script.py
# 在 mitmproxy TUI 中:P → 编辑脚本路径 → 重新加载
最佳实践
用 mitmdump 替代 mitmproxy 做批量处理:mitmproxy 需要交互终端,不适合自动化;mitmdump -s script.py 可在无头服务器上运行,脚本处理所有流量后直接输出,适合 CI/长期挂载场景。
addon 脚本用 response 钩子而非 request:修改响应内容(如注入 JS、解密响应体)在 response 钩子中处理;修改请求头或签名参数在 request 钩子中;两个钩子都可以访问完整的请求和响应数据。
证书信任一次性完成,不要反复安装:在目标设备安装 mitmproxy CA 证书后,该证书对所有 HTTPS 流量生效;若 SSL Pinning 导致证书被拒,需要配合 Frida 或 objection 绕过固定证书校验。
用过滤表达式减少噪音:mitmproxy -s script.py --filter '~url api.target.com' 只处理匹配 URL 的流量,避免广告/CDN 流量干扰分析;过滤表达式支持正则、域名、方法、状态码等维度。
脚本里读取请求体时注意编码:flow.request.content 返回 bytes,若请求体是 JSON 用 flow.request.json() 直接解析;若是 URL-encoded 用 flow.request.urlencoded_form 读取表单字段。
常见陷阱
陷阱:安装证书后 HTTPS 仍然失败
现象: 已在设备信任 mitmproxy CA,但 HTTPS 请求仍报证书错误。
原因: App 使用了 SSL Pinning,将服务端证书或公钥硬编码到 App 中,即使系统信任 mitmproxy 证书也会被拒绝。
解决: Android 用 Frida + ssl-kill-switch2,iOS 用 SSL Kill Switch 3 绕过 Pinning;或 hook App 的证书验证函数让其始终返回成功。
陷阱:修改响应后 App 崩溃或显示异常
现象: 在 response 钩子中修改了响应体,但 App 端崩溃或请求被标记为失败。
原因: 修改了响应体后 Content-Length 头还是旧值,导致 App 读取截断;或响应体被签名/校验,修改后校验失败。
解决: 修改 flow.response.content 后,mitmproxy 会自动更新 Content-Length(调用 set_content);若 App 校验响应签名,还需同时修改签名字段。
陷阱:mitmdump 脚本异常不显示错误信息
现象: 脚本运行但逻辑没有按预期执行,也没有报错。
原因: mitmdump 默认吞掉 addon 中未捕获的异常,异常信息只打印到 stderr。
解决: 在脚本中用 try/except 包裹并 print(traceback.format_exc()) 输出;或加 -v 参数提高日志详细程度:mitmdump -v -s script.py。