mitmproxy
安装
在 linux 中:
1
| sudo pip3 install mitmproxy
|
在 windows 中,以管理员身份运行 cmd 或 power shell:
完成后,系统将拥有 mitmproxy
、mitmdump
、mitmweb
三个命令,由于 mitmproxy
命令不支持在 windows 系统中运行(这没关系,不用担心),我们可以拿 mitmdump
测试一下安装是否成功,执行:
应当可以看到类似于这样的输出:
1 2 3 4
| Mitmproxy: 4.0.1 Python: 3.6.5 OpenSSL: OpenSSL 1.1.0h 27 Mar 2018 Platform: Windows-10-10.0.16299-SP0
|
组件
mitmproxy
命令启动后,会提供一个命令行界面,用户可以实时看到发生的请求,并通过命令过滤请求,查看请求数据。
mitmweb
命令启动后,会提供一个 web 界面,用户可以实时看到发生的请求,并通过 GUI 交互来过滤请求,查看请求数据。
mitmdump
命令启动后,你应该猜到了,没有界面,程序默默运行,所以 mitmdump 无法提供过滤请求、查看数据的功能,只能结合自定义脚本,默默工作。
1 2
| # chrome 指定代理 "chrome" --proxy-server=127.0.0.1:8080 --ignore-certificate-errors
|
脚本
官方内置的一些 addon
1 2 3 4 5 6 7 8 9
| import mitmproxy.http from mitmproxy import ctx
num = 0
def request(flow: mitmproxy.http.HTTPFlow): global num num = num + 1 ctx.log.info("We've seen %d flows" % num)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import mitmproxy.http from mitmproxy import ctx
class Counter: def __init__(self): self.num = 0
def request(self, flow: mitmproxy.http.HTTPFlow): self.num = self.num + 1 ctx.log.info("We've seen %d flows" % self.num)
addons = [ Counter() ]
|
各种生命周期
Http
1
| def http_connect(self, flow: mitmproxy.http.HTTPFlow):
|
收到了来自客户端的 HTTP CONNECT 请求。在 flow 上设置非 2xx 响应将返回该响应并断开连接。CONNECT 不是常用的 HTTP 请求方法,目的是与服务器建立代理连接,仅是 client 与 proxy 的之间的交流,所以 CONNECT 请求不会触发 request、response 等其他常规的 HTTP 事件。
1
| def requestheaders(self, flow: mitmproxy.http.HTTPFlow):
|
来自客户端的 HTTP 请求的头部被成功读取。此时 flow 中的 request 的 body 是空的。
1
| def request(self, flow: mitmproxy.http.HTTPFlow):
|
来自客户端的 HTTP 请求被成功完整读取。
1
| def responseheaders(self, flow: mitmproxy.http.HTTPFlow):
|
来自服务端的 HTTP 响应的头部被成功读取。此时 flow 中的 response 的 body 是空的。
1
| def response(self, flow: mitmproxy.http.HTTPFlow):
|
来自服务端端的 HTTP 响应被成功完整读取。
1
| def error(self, flow: mitmproxy.http.HTTPFlow):
|
发生了一个 HTTP 错误。比如无效的服务端响应、连接断开等。注意与“有效的 HTTP 错误返回”不是一回事,后者是一个正确的服务端响应,只是 HTTP code 表示错误而已。
TCP
1
| def tcp_start(self, flow: mitmproxy.tcp.TCPFlow):
|
建立了一个 TCP 连接。
1
| def tcp_message(self, flow: mitmproxy.tcp.TCPFlow):
|
TCP 连接收到了一条消息,最近一条消息存于 flow.messages[-1]。消息是可修改的。
1
| def tcp_error(self, flow: mitmproxy.tcp.TCPFlow):
|
发生了 TCP 错误。
1
| def tcp_end(self, flow: mitmproxy.tcp.TCPFlow):
|
TCP 连接关闭。
Websocket 生命周期
1
| def websocket_handshake(self, flow: mitmproxy.http.HTTPFlow):
|
客户端试图建立一个 websocket 连接。可以通过控制 HTTP 头部中针对 websocket 的条目来改变握手行为。flow 的 request 属性保证是非空的的。
1
| def websocket_start(self, flow: mitmproxy.websocket.WebSocketFlow):
|
建立了一个 websocket 连接。
1
| def websocket_message(self, flow: mitmproxy.websocket.WebSocketFlow):
|
收到一条来自客户端或服务端的 websocket 消息。最近一条消息存于 flow.messages[-1]。消息是可修改的。目前有两种消息类型,对应 BINARY 类型的 frame 或 TEXT 类型的 frame。
1
| def websocket_error(self, flow: mitmproxy.websocket.WebSocketFlow):
|
发生了 websocket 错误。
1
| def websocket_end(self, flow: mitmproxy.websocket.WebSocketFlow):
|
websocket 连接关闭。
针对网络连接生命周期
1
| def clientconnect(self, layer: mitmproxy.proxy.protocol.Layer):
|
客户端连接到了 mitmproxy。注意一条连接可能对应多个 HTTP 请求。
1
| def clientdisconnect(self, layer: mitmproxy.proxy.protocol.Layer):
|
客户端断开了和 mitmproxy 的连接。
1
| def serverconnect(self, conn: mitmproxy.connections.ServerConnection):
|
mitmproxy 连接到了服务端。注意一条连接可能对应多个 HTTP 请求。
1
| def serverdisconnect(self, conn: mitmproxy.connections.ServerConnection):
|
mitmproxy 断开了和服务端的连接。
1
| def next_layer(self, layer: mitmproxy.proxy.protocol.Layer):
|
网络 layer 发生切换。你可以通过返回一个新的 layer 对象来改变将被使用的 layer。
通用生命周期
1
| def configure(self, updated: typing.Set[str]):
|
配置发生变化。updated 参数是一个类似集合的对象,包含了所有变化了的选项。在 mitmproxy 启动时,该事件也会触发,且 updated 包含所有选项。
addon 关闭或被移除,又或者 mitmproxy 本身关闭。由于会先等事件循环终止后再触发该事件,所以这是一个 addon 可以看见的最后一个事件。由于此时 log 也已经关闭,所以此时调用 log 函数没有任何输出。
1
| def load(self, entry: mitmproxy.addonmanager.Loader):
|
addon 第一次加载时。entry 参数是一个 Loader 对象,包含有添加选项、命令的方法。这里是 addon 配置它自己的地方。
1
| def log(self, entry: mitmproxy.log.LogEntry):
|
通过 mitmproxy.ctx.log 产生了一条新日志。小心不要在这个事件内打日志,否则会造成死循环。
mitmproxy 完全启动并开始运行。此时,mitmproxy 已经绑定了端口,所有的 addon 都被加载了。
1
| def update(self, flows: typing.Sequence[mitmproxy.flow.Flow]):
|
一个或多个 flow 对象被修改了,通常是来自一个不同的 addon。
示例