前置: 本次代码均为可执行示例,将所有依赖安装完成后 可直接执行
可能出现的问题:
1. 之前未开启过本地代理 可能会报错.需要在本地设置代理里 开启一次
2. 缺少依赖 将文件内使用到的依赖全部安装即可
3. 端口冲突.本地已启动占用该配置端口的服务 导致端口冲突无法启动 更改端口即可
4. 服务停止后 仍然无法连接网络 代码内已对强杀信号做了拦截 但是不排除会有其他问题导致程序异常所以未在服务停止时将本地代理关闭 所以需要手动前往设置 代理处关闭
三方依赖: requirements.txt
asyncio
json
sys
winreg
ctypes
signal
mitmproxy
loguru
使用pipinstall -r 命令批量安装即可
建议使用Python版本:3.8+
示例
import asyncio
import json
import sys
import winreg
import ctypes
import signal
from mitmproxy import addons
from mitmproxy.http import HTTPFlow
from mitmproxy.options import Options
from mitmproxy.master import Master
from mitmproxy.addons import dumper, errorcheck, keepserving, readfile, termlog
from loguru import logger
# 这里是读取配置文件 如果后续不使用配置文件的话 可以注释掉 在最下方直接传参
with open('./application.json', 'r', encoding='utf-8') as f:
conf = json.load(f)
# ProxyLocal 本地代理类
class ProxyLocal:
# 加载本地配置 为了修改本地代理
INTERNET_SETTINGS = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
r'Software\Microsoft\Windows\CurrentVersion\Internet Settings',
0, winreg.KEY_ALL_ACCESS)
# 设置刷新
INTERNET_OPTION_REFRESH = 37
INTERNET_OPTION_SETTINGS_CHANGED = 39
internet_set_option = ctypes.windll.Wininet.InternetSetOptionW
# 修改配置的键值对
def set_key(self, name, value):
# 修改键值
_, reg_type = winreg.QueryValueEx(self.INTERNET_SETTINGS, name)
winreg.SetValueEx(self.INTERNET_SETTINGS, name, 0, reg_type, value)
def updateProxy(self, proxy: dict):
# 启用代理 可设置多个参数
for k, v in proxy.items():
self.set_key(k, v)
# 启用
self.internet_set_option(0, self.INTERNET_OPTION_REFRESH, 0, 0)
self.internet_set_option(0, self.INTERNET_OPTION_SETTINGS_CHANGED, 0, 0)
# 这里重写Master类 是为了解决在python中运行DumMaster方法报错 No running event loop 的错误
# 原始方法中 并未开放对事件轮询的入参 所以这里有可能是源码的bug 也有可能有其他的入口参数
class Masters(Master):
# 重写创建服务的继承类 开放事件循环
def __init__(
self,
options: Options,
with_termlog=True,
with_dumper=True,
evn_loop: asyncio.AbstractEventLoop = None
) -> None:
super().__init__(options, event_loop=evn_loop)
if with_termlog:
self.addons.add(termlog.TermLog())
self.addons.add(*addons.default_addons())
if with_dumper:
self.addons.add(dumper.Dumper())
self.addons.add(
keepserving.KeepServing(),
readfile.ReadFileStdin(),
errorcheck.ErrorCheck(),
)
# 这里是插件类, 也是后续框架执行的过程中进行拦截的一些操作 主要逻辑就在这里,过滤 打印 记录等 都在这个方法里
# 名称固定为response
class PrintRequestsUrl:
def response(self, flow: HTTPFlow):
requestsInfo = {
'url': flow.request.url,
'scheme': flow.request.data.scheme,
'headers': flow.request.headers,
'methods': flow.request.method
}
logger.info(f"拦截到的请求:{requestsInfo}")
responseInfo = {
'status': flow.response.status_code,
'data': flow.response.text,
'headers': flow.response.headers
}
logger.info(f'这里是响应{responseInfo}')
# 入口方法
def run(Host: str, Port: int, log: bool = False):
# 开启本地代理
proxy = ProxyLocal()
# 更新配置
proxy.updateProxy(
{
# 1是开启 0是关闭
"ProxyEnable": 1,
"ProxyOverride": u'*.local;<local>', # 绕过本地
"ProxyServer": u"%s:%d" % (Host, Port) # 设置需要配置的代理ip+端口
}
)
p = PrintRequestsUrl() # 示例化插件类
# Options 是配置 可选 具体参数可以点进Options中查看所有配置项
opts = Options(listen_host=Host, listen_port=Port, add_upstream_certs_to_client_chain=True)
loop = asyncio.get_event_loop() # 获取异步事件循环
# 实例化执行主类
m = Masters(options=opts, evn_loop=loop, with_termlog=log)
# 将差距类示例注册到框架执行的步骤中
m.addons.add(p)
logger.info(f'当前代理配置为:{Host}:{Port},拦截本地请求开始')
signal_handler(m, proxy) # 这里是监听系统的一些信号 例如Kill命令 和ctrl+c关闭
loop.run_until_complete(asyncio.wait([m.run()])) # 阻塞执行异步事件 等待Coroutine和Task执行完成
def signal_handler(m: Masters, p: ProxyLocal):
SIG_List = [signal.SIGINT, signal.SIGTERM] # 其余信号源可以查看源码中的说明;
# 回调方法. 当信号执行后 signal中执行的方法
def handler(num, f):
logger.info('服务即将关闭,正在准备服务资源回收,关闭代理服务以及关闭本地代理')
m.shutdown() # 关闭代理服务
p.updateProxy({
"ProxyEnable": 0
}) # 将本地代理关闭
logger.info('资源已回收,正在关闭残留线程')
sys.exit()# 退出进程
for s in SIG_List:
signal.signal(s, handler)
if __name__ == '__main__':
run(conf['host'], conf['port'])
总结:
mitmproxy 不单单可以进行请求拦截 也可以作用在正反向代理,以及中间人攻击等场景中
同时我们在工作中会经常运用到一些抓包工具 但是在一些自动化操作中又无法进行手动抓包时 就可以通过mitmproxy 对本地代理进行请求拦截 进而过滤出一些我们想要的接口来 或者进行一些中间人攻击操作