mitmproxy 简单使用

mitmproxy

安装

在 linux 中:

1
sudo pip3 install mitmproxy

在 windows 中,以管理员身份运行 cmd 或 power shell:

1
pip3 install mitmproxy

完成后,系统将拥有 mitmproxymitmdumpmitmweb 三个命令,由于 mitmproxy 命令不支持在 windows 系统中运行(这没关系,不用担心),我们可以拿 mitmdump 测试一下安装是否成功,执行:

1
mitmdump --version

应当可以看到类似于这样的输出:

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()
]
1
mitmweb -s addons.py

各种生命周期

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 包含所有选项。

1
def done(self):

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 产生了一条新日志。小心不要在这个事件内打日志,否则会造成死循环。

1
def running(self):

mitmproxy 完全启动并开始运行。此时,mitmproxy 已经绑定了端口,所有的 addon 都被加载了。

1
def update(self, flows: typing.Sequence[mitmproxy.flow.Flow]):

一个或多个 flow 对象被修改了,通常是来自一个不同的 addon。

示例