文章参考:https://www.cnblogs.com/grandlulu/p/9525417.html
github地址:https://github.com/mitmproxy/mitmproxy ;可以通过Python管理工具pip安转
官网地址:
代理工具SwitchOmega官网:https://proxy-switchyomega.com/
SwitchOmega安装教程:
核心工具:
mitmproxy:命令行交互工具
mitmdump:命令行非交互工具,类似tcpdump
mitmweb:带有web界面的代理工具
要启动 mitmproxy 用 mitmproxy、mitmdump、mitmweb 这三个命令中的任意一个即可,这三个命令功能一致,且都可以加载自定义脚本,唯一的区别是交互界面的不同。
设置代理【参考Charles代理设置】:
默认抓取80端口,可以通过 -p参数设置随意端口
如:mitmweb -p 8888
抓取https需要安装证书:
切换代理环境下,输入:
下载对应系统,并进行信任
注意点(当时更改网络时,需要再次添加证书)
比如,我把有线换成了无线网络,但是电脑的ip更改了,还是需要再次添加证书,我就是今天测试找了好久的原因,然后才知道每次更改网络了都是要添加证书的。
mitmweb
在发送请求的时候拦截,可进行串改后放行
放行之后,可在response中编辑,给前端返回的内容
mitmdump
完成了上述mitmweb工作,我们已经具备了操作 mitmproxy 的基本能力 了。接下来开始开发自定义脚本,这才是 mitmproxy 真正强大的地方。
脚本的编写需要遵循 mitmproxy 规定的套路,这样的套路有两个,使用时选其中一个套路即可。
第一个套路是,编写一个 py 文件供 mitmproxy 加载,文件中定义了若干函数,这些函数实现了某些 mitmproxy 提供的事件,mitmproxy 会在某个事件发生时调用对应的函数,形如:
import mitmproxy.httpfrom mitmproxy import ctx num = 0 def request(flow: mitmproxy.http.HTTPFlow):global numnum = num + 1ctx.log.info("Weve seen %d flows" % num)第二个套路是,编写一个 py 文件供 mitmproxy 加载,文件定义了变量 addons,addons 是个数组,每个元素是一个类实例,这些类有若干方法,这些方法实现了某些 mitmproxy 提供的事件,mitmproxy 会在某个事件发生时调用对应的方法。这些类,称为一个个 addon,比如一个叫 Counter 的 addon:
import mitmproxy.httpfrom mitmproxy import ctx class Counter:def __init__(self):self.num = 0 def request(self, flow: mitmproxy.http.HTTPFlow):self.num = self.num + 1ctx.log.info("Weve seen %d flows" % self.num) addons = [Counter()]这里强烈建议使用第二种套路,直觉上就会感觉第二种套路更为先进,使用会更方便也更容易管理和拓展。况且这也是官方内置的一些 addon 的实现方式。
常用指令
-p 8888 # 指定端口-s xxx.py # 执行指定脚本-q quite# 仅匹配脚本过滤后的数据包-w# 写入文件-n# 不启动代理-r# 读取文件内容"~m post" # 仅匹配post请求 m->method例1:监听8999端口,并录制请求数据,保存到baidu.txt文件
mitmdump -p 8999 -w baidu.txt打印含颜色的日志:
log: 带有输出不同颜色的功能
info:白色
warn:黄色
error:红色
**** 注意这里主要使用cmd,使用Powershell显示出来的颜色效果不完整******
代码:
from mitmproxy import http, ctxclass Demo:def request(self, flow: http.HTTPFlow):url = flow.request.urlif abc in url:ctx.log.info(Color White: + url)ctx.log.warn(Color Yellow: + url)ctx.log.error(Color Red: + url)addons = [Demo()]在cmd/iterm2执行:
mitmdump -s ./work/AutoFrameWorks/InterfaceFrameWork/tmp/mitmDemoOne.py后搜索abc
在Powershell/pycharm执行:
mitmdump -s ./InterfaceFrameWork/tmp/mitmDemoOne.py后搜索abc
事件
当 request 发生时,会打印日志。
这里对应的是 request 事件,那拢共有哪些事件呢?不多,也不少,这里详细介绍一下。
“生命周期”这里指在哪一个层面看待事件,举例来说,同样是一次 web 请求,我可以理解为“HTTP 请求 -> HTTP 响应”的过程,也可以理解为“TCP 连接 -> TCP 通信 -> TCP 断开”的过程。那么,如果我想拒绝来个某个 IP 的客户端请求,应当注册函数到针对 TCP 生命周期 的 tcp_start 事件,又或者,我想阻断对某个特定域名的请求时,则应当注册函数到针对 HTTP 声明周期的 http_connect 事件。其他情况同理。
1. 针对 HTTP 生命周期
def http_connect(self, flow: mitmproxy.http.HTTPFlow):(Called when) 收到了来自客户端的 HTTP CONNECT 请求。在 flow 上设置非 2xx 响应将返回该响应并断开连接。CONNECT 不是常用的 HTTP 请求方法,目的是与服务器建立代理连接,仅是 client 与 proxy 的之间的交流,所以 CONNECT 请求不会触发 request、response 等其他常规的 HTTP 事件。
def requestheaders(self, flow: mitmproxy.http.HTTPFlow):(Called when) 来自客户端的 HTTP 请求的头部被成功读取。此时 flow 中的 request 的 body 是空的。
def request(self, flow: mitmproxy.http.HTTPFlow):(Called when) 来自客户端的 HTTP 请求被成功完整读取。
def responseheaders(self, flow: mitmproxy.http.HTTPFlow):(Called when) 来自服务端的 HTTP 响应的头部被成功读取。此时 flow 中的 response 的 body 是空的。
def response(self, flow: mitmproxy.http.HTTPFlow):(Called when) 来自服务端端的 HTTP 响应被成功完整读取。
def error(self, flow: mitmproxy.http.HTTPFlow):(Called when) 发生了一个 HTTP 错误。比如无效的服务端响应、连接断开等。注意与“有效的 HTTP 错误返回”不是一回事,后者是一个正确的服务端响应,只是 HTTP code 表示错误而已。
2. 针对 TCP 生命周期
def tcp_start(self, flow: mitmproxy.tcp.TCPFlow):(Called when) 建立了一个 TCP 连接。
def tcp_message(self, flow: mitmproxy.tcp.TCPFlow):(Called when) TCP 连接收到了一条消息,最近一条消息存于 flow.messages[-1]。消息是可修改的。
def tcp_error(self, flow: mitmproxy.tcp.TCPFlow):(Called when) 发生了 TCP 错误。
def tcp_end(self, flow: mitmproxy.tcp.TCPFlow):(Called when) TCP 连接关闭。
3. 针对 Websocket 生命周期
def websocket_handshake(self, flow: mitmproxy.http.HTTPFlow):(Called when) 客户端试图建立一个 websocket 连接。可以通过控制 HTTP 头部中针对 websocket 的条目来改变握手行为。flow 的 request 属性保证是非空的的。
def websocket_start(self, flow: mitmproxy.websocket.WebSocketFlow):(Called when) 建立了一个 websocket 连接。
def websocket_message(self, flow: mitmproxy.websocket.WebSocketFlow):(Called when) 收到一条来自客户端或服务端的 websocket 消息。最近一条消息存于 flow.messages[-1]。消息是可修改的。目前有两种消息类型,对应 BINARY 类型的 frame 或 TEXT 类型的 frame。
def websocket_error(self, flow: mitmproxy.websocket.WebSocketFlow):(Called when) 发生了 websocket 错误。
def websocket_end(self, flow: mitmproxy.websocket.WebSocketFlow):(Called when) websocket 连接关闭。
4. 针对网络连接生命周期
def clientconnect(self, layer: mitmproxy.proxy.protocol.Layer):(Called when) 客户端连接到了 mitmproxy。注意一条连接可能对应多个 HTTP 请求。
def clientdisconnect(self, layer: mitmproxy.proxy.protocol.Layer):(Called when) 客户端断开了和 mitmproxy 的连接。
def serverconnect(self, conn: mitmproxy.connections.ServerConnection):(Called when) mitmproxy 连接到了服务端。注意一条连接可能对应多个 HTTP 请求。
def serverdisconnect(self, conn: mitmproxy.connections.ServerConnection):(Called when) mitmproxy 断开了和服务端的连接。
def next_layer(self, layer: mitmproxy.proxy.protocol.Layer):(Called when) 网络 layer 发生切换。你可以通过返回一个新的 layer 对象来改变将被使用的 layer。
5. 通用生命周期
def configure(self, updated: typing.Set[str]):(Called when) 配置发生变化。updated 参数是一个类似集合的对象,包含了所有变化了的选项。在 mitmproxy 启动时,该事件也会触发,且 updated 包含所有选项。
def done(self):(Called when) addon 关闭或被移除,又或者 mitmproxy 本身关闭。由于会先等事件循环终止后再触发该事件,所以这是一个 addon 可以看见的最后一个事件。由于此时 log 也已经关闭,所以此时调用 log 函数没有任何输出。
def load(self, entry: mitmproxy.addonmanager.Loader):(Called when) addon 第一次加载时。entry 参数是一个 Loader 对象,包含有添加选项、命令的方法。这里是 addon 配置它自己的地方。
def log(self, entry: mitmproxy.log.LogEntry):(Called when) 通过 mitmproxy.ctx.log 产生了一条新日志。小心不要在这个事件内打日志,否则会造成死循环。
def running(self)(Called when) mitmproxy 完全启动并开始运行。此时,mitmproxy 已经绑定了端口,所有的 addon 都被加载了。
def update(self, flows: typing.Sequence[mitmproxy.flow.Flow]):(Called when) 一个或多个 flow 对象被修改了,通常是来自一个不同的 addon。
个例1
在百度上搜索任意字符,都将修改成搜索电话卡哈哈
import mitmproxy.httpclass demo:def request(self, flow: mitmproxy.http.HTTPFlow):request = flow.requestif "www.baidu.com" in request.url:print("正在输入查询参数:", request.query.get(wd))request.query.set_all(wd, [电话卡哈哈])print(修改后的查询参数:, request.query.get(wd))addons = [demo()]个例2
替换返回体
import mitmproxy.httpclass replaceresponce:def response(self, flow: mitmproxy.http.HTTPFlow):if flow.request.host == "www.baidu.com":# 方式一:将整个页面替换flow.response.set_text(text="替换所有返回")# 方式二:替换返回体中所有的你好为"喝咖啡好久"text = flow.response.get_text() # 先获取所有返回体# flow.response.set_text(text=text.replace(你好, 喝咖啡好久))# 拒绝响应 -401flow.response = mitmproxy.http.HTTPResponse.make(401)# 拒绝响应 -404#flow.response = mitmproxy.http.HTTPResponse.make(404)addons = [replaceresponce()]方式一展示:
方式二展示:
拒绝响应展示:
实战
估计看了那么多的事件你已经晕了,正常,鬼才会记得那么多事件。事实上考虑到 mitmproxy 的实际使用场景,大多数情况下我们只会用到针对 HTTP 生命周期的几个事件。再精简一点,甚至只需要用到 http_connect、request、response 三个事件就能完成大多数需求了。
现有如下需求:
1.因为百度搜索不靠谱,所有当客户端发起百度搜索时,记录下用户的搜索词,再修改请求,将搜索词改为“360 搜索”;
2.因为 360 搜索还是不靠谱的,所有当客户端访问 360 搜索时,将页面中所有“搜索”字样改为“请使用谷歌”。
3.因为谷歌是个不存在的网站,所有就不要浪费时间去尝试连接服务端了,所有当发现客户端试图访问谷歌时,直接断开连接。
将上述功能组装成名为 Joker 的 addon,并保留之前展示名为 Counter 的 addon,都加载进 mitmproxy。
from mitmproxy import http, ctxclass mitmDemoTwo:# 先实现第一个需求: # 篡改客户端请求,改为“360搜索”,所以实现一个 request 事件: def request(self, flow: http.HTTPFlow):# 忽略非百度搜索地址if flow.request.host != "www.baidu.com" or not flow.request.path.startswith("/s"):return # 确认请求参数中有搜索词if "wd" not in flow.request.query.keys():ctx.log.warn(f"can not get search word from {flow.request.pretty_url}")return # 输出原始的搜索词ctx.log.info(f"catch search word : {flow.request.query.get(wd)}")# 替换搜索词为"360搜索"flow.request.query.set_all(wd, [360搜索])# 第二个需求需要篡改服务端响应,所以实现一个 response 事件: # 将页面中所有“搜索”字样改为“请使用谷歌”def response(self, flow: http.HTTPFlow):# 忽略非360搜索地址if flow.request.host != "www.so.com":# 将响应中所有“搜索”替换为“请使用谷歌”text = flow.response.get_text()text = text.replace("搜索", "请使用谷歌")flow.response.set_text(text)# 第三个需求需要拒绝客户端请求,所以实现一个 http_connect 事件:def http_connect(self, flow: http.HTTPFlow):# 确认客户端是想访问 www.google.comif flow.request.host == "www.google.com":# 返回一个非 2XX 响应断开连接flow.response = http.HTTPResponse.make(404)第四个需求,创建一个 counter.py 文件,内容为:
import mitmproxy.httpfrom mitmproxy import ctx class Counter:def __init__(self):self.num = 0 def request(self, flow: mitmproxy.http.HTTPFlow):self.num = self.num + 1ctx.log.info("Weve seen %d flows" % self.num)创建一个 addons.py 文件,内容为:
import counterimport mitmDemoTwo addons = [counter.Counter(),mitmDemoTwo.mitmDemoTwo(),]将三个文件放在相同的文件夹,在该文件夹内启动命令行,运行:
每个需求需要单独执行,所以mitmDemoTwo中要注释掉其他的方法,只留其中一个方法
mitmweb -s addons.py可见可通过代理的response爬虫
在手机端的应用
抓包前提:
说下手机和电脑的网络关系:
可行的几种方式,之前看人家写局域网,我真的不太懂,现在懂了我觉得这样说肯定有之前和我一样不懂得人。
我尝试成功有以下几种:
电脑和手机连接同一个wifi.电脑连接有线,手机连接电脑开的热点(电脑开的热点)电脑连接wifi,电脑使用自身带的热点功能开热点,或者使用360免费wifi。
电脑配置证书:
这个就是按上面配置代理步骤即可。
手机连接抓包:
这里按电脑和手机连接同一个wifi讲吧,然后把wifi设置代理。
①、手机设置代理
Windows端在cmd中输入ipconfig:
然后把手机wifi地方添加代理:
②、手机下载证书:
还是浏览器访问mitm.it,不过这次选择安卓的下载。
③、安装证书:
进入设置的系统与安全中,从存储设备安装。
然后就可以愉快的使用windows的mitmdump抓包了。
至此可以抓包了。
如果无法设置,或者设置之后进入下载证书页面失败,可以直接导入证书安装即可