代理 mitmproxy Python启动 非命令行启动(多种方式) 使用笔记(一)
代理 mitmproxy Python启动 非命令行启动(多种方式) 使用笔记(一)
在进行 APP
应用操作时,难免会遇到抓包操作,于是我们这里使用 mitmproxy
来完成这能力
目录
- mitmproxy 简介
- mitmproxy 常用的命令行启动
- 方式一:通过
main.mitmdump
启动,会阻塞主进程
- 方式二:通过
dump.DumpMaster
类加asyncio.run
启动,会阻塞主进程
- 方式三:改进
dump.DumpMaster
类加asyncio.run
启动,不会阻塞主进程
,同一个进程内,提供一个开启、关闭
mitmproxy的方案,但是仍有问题需要处理 - 方式四:使用
multiprocessing
多进程启动不会阻塞主进程
,用多进程启动,并可开启、关闭
- 方式五:使用
QProcess
启动,PyQt5,6/PySide2,6
适用不会阻塞主进程
,并可开启、关闭
简介
mitmproxy
是一组工具,为 HTTP/1、HTTP/2 和 WebSocket 提供交互式、支持 SSL/TLS 的拦截代理,官方文档见这里
常用的命令行启动
- 基础启动
mitmweb # 启动带WEB 可视化界面的代理,默认:host=127.0.0.1,port=8080
mitmproxy # 启动无可视化代理,默认:host=127.0.0.1,port=8080
- 自定义地址端口,允许公共 IP 地址的连接,带脚本插件启动
# host=192.167.6.120,port=8888,允许公共 IP 地址的连接:block_global=false,脚本插件:test_addon.py
mitmweb --listen-host=192.167.6.120 -p=8888 --set block_global=false -s .\test_addon.py
mitmproxy --listen-host=192.167.6.120 -p=8888 --set block_global=false -s .\test_addon.py
注意:为什么要加 block_global=false
?,若不加,则只能本地拦截,而移动设备请求无法被拦截
报错如下:Client connection from 192.167.6.166 killed by block_global option
非命令行脚本直接启动
方式一:通过main.mitmdump
启动
通过main.mitmdump
函数, main.mitmdump
启动后,会阻塞主进程
,这点需要注意
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File : test_mitmproxy.py
@ Author : yqbao
@ Version : V1.0.0
@ Description : 通过`main.mitmdump`函数
"""
from mitmproxy.http import HTTPFlow
from mitmproxy.tools import main
class Counter:
def request(self, flow: HTTPFlow):
request = flow.request # 获取请求对象
print("request data = ", request.data)
def response(self, flow: HTTPFlow):
if flow.response.status_code != 200:
return
response = flow.response # 获取响应对象
print(f"response content = {response.content}")
addons = [
Counter()
]
if __name__ == '__main__':
main.mitmdump(['-s', __file__, '--listen-host', '192.167.6.120', '-p', '8888', '--set', 'block_global=false'])
方式二:通过dump.DumpMaster
类加asyncio.run
启动
通过dump.DumpMaster
类, asyncio.run
启动后,会阻塞主进程
,这点需要注意
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File : test_mitmproxy.py
@ Author : yqbao
@ Version : V1.0.0
@ Description : 通过`dump.DumpMaster`类
"""
import asyncio
from mitmproxy import http, options, optmanager
from mitmproxy.tools import dump
class Counter:
... # 同方式一,无改动
async def func_temp(host: str = '127.0.0.1', port: int = 8080, conf_file: str = None):
opts = options.Options(listen_host=host, listen_port=port)
# print(opts.keys()) # 输出选项名称
# 加载配置文件,如果你的参数在 opts.keys() 中不存在,则必须使用配置文件,否则不需要用
if conf_file:
optmanager.load_paths(opts, conf_file)
dm = dump.DumpMaster(opts)
dm.addons.add(Counter())
try:
await dm.run()
except BaseException as e:
print(e)
dm.shutdown()
if __name__ == '__main__':
asyncio.run(func_temp(conf_file=r'.\.mitmproxy\config.yaml'))
配置文件如下,配置文件中的选项,请参考官网,见这里
%YAML 1.2
---
listen_host: 192.167.6.120
listen_port: 8888
block_global: false
...
方式三:改进asyncio.run
启动
这种方式是对第2种的改进,主要目的是不阻塞主进程
,此处提供一个开启、关闭
的方案,但是仍有问题需要处理
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File : test_mitmproxy.py
@ Author : yqbao
@ Version : V1.0.0
@ Description : 通过`dump.DumpMaster`类
"""
import asyncio
import threading
from mitmproxy import http, options, optmanager
from mitmproxy.tools import dump
class Counter:
... # 同方式一,无改动
async def func_temp(host: str = '127.0.0.1', port: int = 8080, conf_file: str = None):
... # 同方式二,无改动
def start_loop(loop):
"""创建事件循环loop的函数"""
asyncio.set_event_loop(loop)
loop.run_forever()
async def stop_loop():
"""关闭事件循环"""
loop = asyncio.get_event_loop()
loop.stop()
loop.close()
def start_mitmproxy() -> (threading.Thread, asyncio.AbstractEventLoop, Future)::
"""启动mitmproxy"""
new_loop = asyncio.new_event_loop()
t = threading.Thread(target=start_loop, args=(new_loop,)) # 开启新的线程去启动事件循环
t.start()
conf_file = r'../.mitmproxy/config.yaml'
future = asyncio.run_coroutine_threadsafe(func(conf_file=conf_file), new_loop)
return t, new_loop, future
# TODO 这会停止事件循环,mitmproxy也就会关闭代理,但是所占用的端口无法被释放,除非关掉主进程,
# 若你不想关闭主进程,又想在同一个端口多次启停 mitmproxy,就需要注意这一点
def stop_mitmproxy(thread: threading.Thread, loop: asyncio.AbstractEventLoop, future: Future) -> None:
"""关闭mitmproxy"""
try:
asyncio.run_coroutine_threadsafe(stop_loop(), loop)
future.cancel()
thread.join()
except BaseException:
pass
if __name__ == '__main__':
import time
th, loop, futur = start_mitmproxy()
time.sleep(30)
stop_mitmproxy(th, loop, futur)
方式四:使用 multiprocessing
多进程启动
使用 multiprocessing
多进程启动与管理,不阻塞主进程
,并可开启、关闭
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File : test_mitmproxy.py
@ Author : yqbao
@ Version : V1.0.0
@ Description : multiprocessing
"""
from multiprocessing import Process
from mitmproxy.tools import main
class Counter:
... # 同方式一,无改动
addons = [
Counter()
]
def start_mitmproxy():
main.mitmdump(['-s', __file__, '--listen-host', '0.0.0.0', '-p', '8888', '--set', 'block_global=false'])
def run() -> Process:
"""运行多进程"""
p = Process(target=start_mitmproxy, name='mitmproxy')
p.start()
return p
def stop(p: Process) -> None:
"""终止多进程"""
p.terminate()
p.join()
if __name__ == '__main__':
import time
pro = run()
time.sleep(30)
stop(pro)
方式五:使用QProcess
启动
PyQt5,6/PySide2,6
适用,因为使用QProcess
实现,QProcess
用于启动外部程序,并加以管理,不会阻塞主进程
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File : test_mitmproxy.py
@ Author : yqbao
@ Version : V1.0.0
@ Description : PyQt5 适用 QProcess
"""
from PyQt5.QtCore import QObject, QProcess
from mitmproxy import http
class Counter:
... # 同方式一,无改动
addons = [
Counter()
]
class MitmdumpQProcess(QObject):
def __init__(self, parent, basic_signal: BaseSignal):
super().__init__(parent=parent)
self.basic_signal = basic_signal # 调度者的信号,如果需要的话
self.p = None # 默认值
def start_process(self):
"""启动"""
if self.p is not None:
return
print("Executing process")
self.p = QProcess()
self.p.readyReadStandardOutput.connect(self.__handle_stdout) # 捕获消息,并输出
self.p.readyReadStandardError.connect(self.__handle_stderr) # 捕获消息,并输出
self.p.stateChanged.connect(self.__handle_state)
self.p.finished.connect(self.__process_finished) # 结束时清理
options = [
'--listen-host', '0.0.0.0',
'-p', '8888',
'--set', 'block_global=false',
'-s', f'{__file__}',
]
# 请注意,若使用 PyInstaller 打包,需要将 mitmdump.exe 存放在同级目录内
self.p.start("mitmdump", options)
def __handle_stderr(self):
data = self.p.readAllStandardError()
stderr = bytes(data).decode("utf8")
print(stderr)
def __handle_stdout(self):
data = self.p.readAllStandardOutput()
stdout = bytes(data).decode("utf8")
print(stdout)
@staticmethod
def __handle_state(state):
states = {
QProcess.NotRunning: 'Not running',
QProcess.Starting: 'Starting',
QProcess.Running: 'Running',
QProcess.NormalExit: 'Normal Exit'
}
state_name = states[state]
print(f"State changed: {state_name}")
def __process_finished(self):
print("Process finished.")
self.p = None
def kill_process(self):
"""杀死"""
self.p.terminate()
self.p.kill()
参考文章:
mitmproxy 官方文档
mitmproxy入门三、直接python脚本运行&常用http处理方式
PyCharm中直接启动mitmproxy并自动打开&关闭系统代理
本文章的原文地址
GitHub主页
本文来自博客园作者:星尘的博客,转载请注明出处:https://www.cnblogs.com/yqbaowo/p/18175510