mitmproxy高级用法:自定义脚本拦截改包

mitmproxy 作为业界主流的中间人代理工具,早已超越了 "图形化抓包" 的基础定位。其真正的核心能力,在于通过 Python 自定义脚本实现自动化请求 / 响应拦截、动态篡改、流量 Mock 与协议扩展。相比 Fiddler、Charles 等工具的手动改包,mitmproxy 脚本可以实现毫秒级自动化改包、复杂规则匹配、批量流量处理,是逆向分析、接口调试、渗透测试的核心利器。

本文从核心机制出发,系统讲解 mitmproxy 自定义脚本的拦截改包玩法,从基础请求 / 响应修改到高级流式处理、条件过滤、Mock 响应,附完整可运行代码示例。

一、mitmproxy 脚本的核心机制:Addon 与事件钩子

mitmproxy 的脚本基于 Addon(插件)架构,所有自定义逻辑都通过注册事件钩子实现。当代理处理 HTTP/HTTPS 流量时,会在请求生命周期的不同节点触发对应事件,我们只需要编写对应事件的处理函数即可。

1.1 核心事件钩子

最常用的 HTTP 流量事件有两个:

  • request(flow):请求到达代理、尚未发送给服务器时触发,用于修改请求。
  • response(flow):响应返回代理、尚未发给客户端时触发,用于修改响应。

其他高频事件:

  • http_connect:处理 CONNECT 请求(HTTPS 握手阶段)
  • error:流量出错时触发
  • done:单条流量处理完成时触发

1.2 脚本运行方式

将逻辑保存为 .py 文件,通过 mitmdump(mitmproxy 的命令行版本)加载运行:

复制代码
mitmdump -s custom_intercept.py -p 8080

也可以在 mitmproxy 交互界面按 D 动态加载脚本。

1.3 核心操作对象:Flow

每个 HTTP 请求对应一个 flow 对象,包含完整的请求和响应上下文:

  • flow.request:请求对象,包含 method、url、headers、content、text、cookies 等可读写属性
  • flow.response:响应对象,包含 status_code、headers、content、text 等可读写属性

修改这些属性的值,就等价于完成了 "改包" 操作。

二、基础实战:请求拦截与篡改

请求拦截是最常用的场景,比如修改请求头、替换 URL 参数、篡改 POST 数据、添加自定义标识等。

2.1 修改请求头:添加 / 替换 Header

下面的脚本仅针对目标域名添加自定义请求头,并修改 User-Agent:

复制代码
"""
请求头篡改示例
"""
from mitmproxy import http

def request(flow: http.HTTPFlow) -> None:
    # 只处理目标域名的请求,避免全局污染
    if "api.example.com" in flow.request.host:
        # 添加自定义请求头
        flow.request.headers["X-Custom-Token"] = "test_token_123456"
        # 覆盖 User-Agent
        flow.request.headers["User-Agent"] = "Custom-Agent/1.0"
        # 删除指定请求头
        if "Referer" in flow.request.headers:
            del flow.request.headers["Referer"]

2.2 修改 GET 请求参数

通过 flow.request.query 可以直接操作 URL 查询参数:

复制代码
def request(flow: http.HTTPFlow) -> None:
    if flow.request.path.startswith("/search"):
        # 修改/新增 query 参数
        flow.request.query["page"] = "100"
        flow.request.query["keyword"] = "modified_keyword"
        # 删除指定参数
        if "debug" in flow.request.query:
            del flow.request.query["debug"]

2.3 修改 POST 请求体(JSON / 表单)

对于 JSON 格式的请求体,解析后修改字段再重新赋值即可:

复制代码
import json

def request(flow: http.HTTPFlow) -> None:
    if flow.request.method == "POST" and "/submit" in flow.request.path:
        content_type = flow.request.headers.get("Content-Type", "")
        if "application/json" in content_type:
            try:
                # 解析 JSON 请求体
                data = json.loads(flow.request.text)
                # 修改业务字段
                data["amount"] = 999
                data["user_id"] = "modified_user"
                # 写回,mitmproxy 会自动处理编码
                flow.request.text = json.dumps(data, ensure_ascii=False)
            except json.JSONDecodeError:
                pass

对于表单格式(x-www-form-urlencoded),可以用 urlencoded_form 直接操作:

复制代码
def request(flow: http.HTTPFlow) -> None:
    if flow.request.method == "POST":
        flow.request.urlencoded_form["username"] = "admin"
        flow.request.urlencoded_form["password"] = "123456"

三、响应拦截:篡改返回数据

响应拦截用于修改服务器返回的内容,比如修改接口返回码、替换页面内容、注入 JS、篡改业务数据等。

3.1 修改响应状态码与响应头

复制代码
def response(flow: http.HTTPFlow) -> None:
    if "/block" in flow.request.path:
        # 将 403 状态码改为 200
        flow.response.status_code = 200
        # 添加跨域响应头
        flow.response.headers["Access-Control-Allow-Origin"] = "*"
        flow.response.headers["Access-Control-Allow-Credentials"] = "true"

3.2 修改 JSON 响应体

这是接口调试最常用的场景,比如修改订单金额、修改用户等级、绕过状态校验:

复制代码
import json

def response(flow: http.HTTPFlow) -> None:
    if "/api/user/info" in flow.request.path:
        try:
            data = json.loads(flow.response.text)
            # 修改业务字段
            data["data"]["vip_level"] = 99
            data["data"]["balance"] = 99999.99
            data["code"] = 0
            # 写回响应体
            flow.response.text = json.dumps(data, ensure_ascii=False)
        except json.JSONDecodeError:
            pass

3.3 替换网页内容 / 注入脚本

针对 HTML 页面,可以批量替换关键字,或者注入自定义 JS/CSS:

复制代码
def response(flow: http.HTTPFlow) -> None:
    content_type = flow.response.headers.get("Content-Type", "")
    if "text/html" in content_type:
        # 批量替换页面文字
        flow.response.text = flow.response.text.replace("原价", "优惠价")
        # 注入自定义 JS 脚本
        inject_js = '<script>console.log("mitmproxy注入成功")</script>'
        flow.response.text = flow.response.text.replace("</body>", inject_js + "</body>")

注意:使用 flow.response.text 时,mitmproxy 会自动处理 gzip/deflate 压缩与字符编码;如果直接操作 flow.response.content(字节),需要手动处理压缩。

四、高级拦截技巧

掌握基础修改后,结合条件过滤、流式处理、Mock 响应等技巧,可以应对更复杂的业务场景。

4.1 精准条件过滤:只拦截目标流量

不要全局修改所有流量,建议通过域名、路径、方法、Header 等多维度组合过滤:

复制代码
import re

def request(flow: http.HTTPFlow) -> None:
    # 组合条件:POST 方法 + 指定域名 + 路径正则匹配
    if (flow.request.method == "POST"
        and flow.request.host == "api.target.com"
        and re.match(r"/api/v\d+/order/create", flow.request.path)):
        # 执行改包逻辑
        pass

4.2 请求重定向:转发到其他环境

将请求透明转发到测试环境或本地服务,无需修改客户端任何配置:

复制代码
def request(flow: http.HTTPFlow) -> None:
    if flow.request.host == "prod.example.com":
        # 重定向到测试环境域名
        flow.request.host = "test.example.com"
        # 也可以修改端口或完整 URL
        # flow.request.port = 8081
        # flow.request.url = "http://localhost:3000/mock/api"

4.3 直接 Mock 响应:跳过后端请求

无需等待服务器返回,直接在代理层返回自定义响应,常用于前端 Mock 或绕过后端校验:

复制代码
from mitmproxy import http
import json

def request(flow: http.HTTPFlow) -> None:
    if "/api/mock/data" in flow.request.path:
        # 构造自定义响应
        mock_body = {
            "code": 0,
            "msg": "success",
            "data": {"id": 1, "name": "mock数据"}
        }
        flow.response = http.Response.make(
            200,  # 状态码
            json.dumps(mock_body, ensure_ascii=False).encode("utf-8"),  # 响应体
            {"Content-Type": "application/json; charset=utf-8"}  # 响应头
        )

注意:在 request 事件中直接给 flow.response 赋值,会中断请求转发,直接将结果返回给客户端。

4.4 流式拦截:处理大文件 / 流媒体

默认情况下 mitmproxy 会等待完整响应加载后再触发 response 事件,大文件会占用大量内存。对于流媒体、大文件下载,可以使用流式钩子边接收边修改:

复制代码
def responseheaders(flow: http.HTTPFlow) -> None:
    # 开启流式处理模式
    flow.response.stream = True

def response_stream(flow: http.HTTPFlow, chunk: bytes) -> bytes:
    # 对每个数据块进行修改
    if b"old_string" in chunk:
        chunk = chunk.replace(b"old_string", b"new_string")
    return chunk

批量修改或注入 Cookie,实现会话劫持、追踪绕过等效果:

复制代码
def request(flow: http.HTTPFlow) -> None:
    # 注入会话 Cookie
    flow.request.cookies["session_id"] = "hijacked_session_001"
    # 删除追踪 Cookie
    if "track_id" in flow.request.cookies:
        del flow.request.cookies["track_id"]

五、进阶:完整 Addon 插件结构

简单脚本可以直接写函数,复杂逻辑建议封装成 Addon 类,支持状态保持、配置项、自定义命令等高级能力。

复制代码
"""
完整 Addon 示例:可配置的接口篡改插件
"""
from mitmproxy import http, ctx
import json

class DataModifier:
    def __init__(self):
        # 内部状态计数器
        self.modify_count = 0

    def load(self, loader):
        # 注册命令行配置项
        loader.add_option(
            name="enable_modify",
            typespec=bool,
            default=True,
            help="是否启用数据修改"
        )

    def response(self, flow: http.HTTPFlow) -> None:
        # 读取配置项,控制开关
        if not ctx.options.enable_modify:
            return

        if "/api/order/list" in flow.request.path:
            try:
                data = json.loads(flow.response.text)
                data["data"]["total"] = 999
                flow.response.text = json.dumps(data, ensure_ascii=False)
                self.modify_count += 1
                ctx.log.info(f"已修改第 {self.modify_count} 个响应")
            except json.JSONDecodeError:
                pass

# 注册 Addon 插件
addons = [DataModifier()]

运行时通过参数动态控制:

复制代码
mitmdump -s modifier_addon.py --set enable_modify=true

六、常见坑与最佳实践

  1. text 与 content 的区别
    • text:字符串类型,自动处理编码和解压缩,适合文本类数据。
    • content:字节类型,原始二进制数据,适合图片、文件等二进制数据。
    • 修改二进制内容请操作 content,避免编码损坏。
  2. HTTPS 证书问题
    • 必须在客户端安装 mitmproxy 根证书,否则 HTTPS 流量无法解密和修改。
    • 对于开启证书绑定(SSL Pinning)的 App,需要配合 Frida 等工具绕过。
  3. 性能注意事项
    • 避免在事件函数中做耗时操作(如网络请求、复杂计算),会拖慢代理速度。
    • 大文件、流媒体务必开启流式处理模式。
  4. 编码与乱码
    • 修改中文时确保使用 ensure_ascii=False,并指定正确的 charset。
    • 遇到非 UTF-8 编码的响应,手动通过 flow.response.content.decode("gbk") 处理。
  5. 调试技巧
    • 使用 ctx.log.info() 输出日志,不要用 print。
    • 配合 mitmweb 图形界面查看流量,验证改包是否生效。

写在最后

mitmproxy 的自定义脚本能力,本质上是给 HTTP 流量加上了一层可编程的中间层。从简单的参数修改到复杂的协议逆向、流量劫持、自动化测试,只要能抽象成 "请求 / 响应处理逻辑",都可以通过 mitmproxy 脚本实现。

以上覆盖了 90% 以上的改包场景,建议从基础示例开始,逐步叠加条件过滤和业务逻辑,最终打造属于自己的专属代理工具。

相关推荐
Fluxproxy2 小时前
Python爬虫异步提速终极方案|线程池ThreadPoolExecutor实战,告别单线程慢速爬取
开发语言·爬虫·python
独立开发者阿乐2 小时前
llms.txt 是什么?为什么 AI 爬虫需要这个文件
人工智能·爬虫·geo优化·ai爬虫·llms.txt·ai收录·robots.txt
傻啦嘿哟5 小时前
高校信息爬虫:爬取全国高校列表与排名,做择校分析
爬虫
深蓝电商API18 小时前
HTTP/2 协议逆向:从 Fiddler 抓不到包说起
爬虫·网络协议·http·fiddler
隐擎fox1 天前
深入网络风控底层:解密 IP 信誉评分模型、ASN 属性判定与多租户原生网络隔离架构
网络·爬虫·网络协议·tcp/ip·架构·跨境电商·指纹浏览器
zzp282181 天前
Estyapp逆向分析
爬虫
天启HTTP2 天前
爬虫防封核心原理:IP轮换机制与风控规避逻辑
linux·服务器·网络·爬虫·tcp/ip
SEO_juper2 天前
用 Python 写一个 GEO 可见性检查脚本:你的网站现在能被 AI 引用吗
开发语言·人工智能·爬虫·python·seo·外贸独立站
阿狗童鞋2 天前
Python爬虫进阶实战指南
开发语言·爬虫·python