mitmproxy入门三、直接python脚本运行&常用http处理方式
在mitmproxy入门第一篇中讲到通过类似“mitmdump -q -s main.py”这种命令可以实现脚本工作,其实也可以直接以运行python脚本的方式启动。本文为mintmproxy 版本10、python3.10
一、直接运行python文件启动
主入口参考文件main.py
import mitmproxy.http
from mitmproxy import ctx, http
#import threading
import asyncio
from mitmproxy.options import Options
from mitmproxy.tools import main
from mitmproxy.tools import dump
import action1
import action2
#这里的addons 是给mitmdump -q -s main.py命令执行方式用的
addons = [
action1.Action1(),
action2.Action2()
]
if __name__ == '__main__':
#from mitmproxy.tools.dump import DumpMaster
async def func_temp(host,port):
opts = Options(listen_host=host, listen_port=port)
#with_termlog设置为True会显示运行中mitmproxy遇到的错误;with_dumper是用来控制是否显示每个请求相关信息的
dm = dump.DumpMaster(opts, with_termlog=True, with_dumper=False)
# python脚本直接运行需要按如下格式一个一个add添加
dm.addons.add(action1.Action1())
dm.addons.add(action2.Action2())
#print(dm.addons)
#dm.addons.add([action1.Action1(),action2.Action2()])#这种语法不行
#dm.addons = [
# action1.Action1(),
# action2.Action2()
#]#这种也不行
try:
await dm.run()
except BaseException as e:
print(e)
dm.shutdown()
asyncio.run(func_temp('127.0.0.1', 8080))
参考action1.py
import mitmproxy.http
from mitmproxy import ctx, http
class Action1:
def request(self, flow: mitmproxy.http.HTTPFlow):
print("这里是action11----------")
print(flow.request.host)
#t = 1/0
#flow.response = http.Response.make(200,"111",)
return
def response(self, flow):
#t =1/0
#flow.response = http.Response.make(200,"111+111",)
return
参考action2.py
import mitmproxy.http
from mitmproxy import ctx, http
class Action2:
def request(self, flow: mitmproxy.http.HTTPFlow):
print("这里是action22----------")
print(flow.request.host)
#t = 1/0
#flow.response = http.Response.make(200,"222",)
return
def response(self, flow):
#t =1/0
#flow.response = http.Response.make(200,"222+222",)
return

上图蓝色框的是如果要直接在python脚本中运行所需添加的内容格式。
二、常用的操作http请求的示例
直接在上面action1.py里面做下修改,可以方便地进行测试(测试代码有点乱)
import mitmproxy.http
from mitmproxy import ctx, http
import time
class Action1:
def request(self, flow: mitmproxy.http.HTTPFlow):
#flow.request.host='www.google.com'
#t=1/0
#print("请求将被kill,后续将不会有response")
#flow.kill()
if 'action' in flow.request.url:
print("触发了拦截,会阻塞后面的请求,即使请求不进入本逻辑")
flow.intercept()
a = input("输入1 放行: ")
if a=='1':
#time.sleep(20)
flow.resume()#继续流动 - 在一个intercept()之后调用
else:
print("输入不是1,此请求不再放行")
else:
print("不拦截")
#pretty_host #类似于host,但使用主机头作为附加的首选数据源。这在透明模式下很有用,host只有IP地址,但可能不会反映实际的目的地,因为主机头可能被欺骗。
flow.request.method #请求方式。POST、GET等
flow.request.scheme #什么请求 ,应为“http”或“https”
#flow.response = http.Response.make(200,"111",)
flow.request.query #返回MultiDictView类型的数据,url直接带的键值参数
flow.request.query.keys()#取得所有请求参数(不包含参数对应的值)
print(list(flow.request.query.keys()))
print(" ")
act1 =flow.request.query.get('action')#取得请求参数wd的值
print(f"act1={act1}")
#flow.request.query.set_all(key,[value])#修改请求参数
flow.request.query.set_all('action',['edit-action'])
act2 =flow.request.query.get('action')
print(f"act2={act2}")
flow.request.cookies["log_id"] = "007"#修改cookie
flow.request.get_content()#bytes,结果如flow.request.get_text()
flow.request.raw_content #bytes,结果如flow.request.get_content()
flow.request.urlencoded_form #MultiDictView,content-type:application/x-www-form-urlencoded时的请求参数,不包含url直接带的键值参数
flow.request.urlencoded_form["code"] = "123456"#修改或赋值
flow.request.urlencoded_form = [("code", "123456"),("name","lucy")]
flow.request.multipart_form #MultiDictView,content-type:multipart/form-data
return
def response(self, flow):
#t =1/0
#flow.response = http.Response.make(200,"111+111",)
#flow.response = flow.response.make(404)#返回404
#print(flow.response.headers)
for (k,v) in flow.response.headers.items():
print(f"{k}:{v}")
print(" ")
#print(flow.response.get_text())
flow.response.status_code #状态码
flow.response.text#返回内容,已解码
#print(flow.response.text)#返回内容,已解码
flow.response.content #返回内容,二进制
#flow.response.setText()#修改返回内容,不需要转码
flow.response.set_text(flow.response.get_text().replace('<title>', '<title>返回title——'))
flow.response.headers["isMitmproxy"]='yes'#给返回添加返回头
#读取文件,在当前文件路径下执行脚本,否则需要写文件的绝对路径;不然会找不到该json文件
with open('1.json','rb') as f:
#从json文件中读取数据成python对象
res = json.load(f)
#将读取的python对象转成json字符串发送给客户端
flow.response.set_text(json.dumps(res))
return
如果是接口测试用,有时候需要利用postman或其他apifox、apipost之类的工具导入接口,手动创建接口参数这些填写很麻烦,即使curl导入也不太方便,这时候我们可以把经过mitmproxy代理的相关参数进行整理并打印出来,然后复制到软件中。
def request(self, flow):
# 请求
ctx.log.error(f"【1】请求url:{flow.request.url}")
ctx.log.error(f"【1】请求url(某些情况下地址是ip这时需要取pretty_url):{flow.request.pretty_url}")
ctx.log.error(f"【1】请求host:{flow.request.host}")
ctx.log.error(f"【1】请求host(某些情况下地址是ip这时需要取pretty_host):{flow.request.pretty_host}")
ctx.log.error(f"【1】请求路径:{flow.request.path}")
ctx.log.error(f"【1】请求content:{flow.request.content}")
ctx.log.error("请求参数Params:")
for k in flow.request.query:
ctx.log.error(f"{k}:{flow.request.query[k]}")
ctx.log.error(" ")
ctx.log.error(f"【2】请求Cookie:")
for k in flow.request.cookies:
ctx.log.error(f"{k}:{flow.request.cookies[k]}")
ctx.log.error(" ")
ctx.log.error(f"【3】请求头:")
for k in flow.request.headers:
ctx.log.error(f"{k}:{flow.request.headers[k]}")
ctx.log.error(" ")
if 'Content-Type' in flow.request.headers:
ctx.log.error(f"【4】请求类型:{flow.request.headers['Content-Type']}")
else:
ctx.log.error(f"【4】请求类型:无")
#body 需要根据Content-Type去生成
ctx.log.error("")
ctx.log.error(f"【5】请求体文本:{flow.request.get_text()}")
ctx.log.error("")
if 'application/x-www-form-urlencoded' in str(flow.request.headers):
#s = urllib.parse.unquote(flow.request.get_text().replace("=",":").replace("&","rn"))
#ctx.log.error("【6】请求体x-www-form-urlencoded:n"+s)
ctx.log.error("【6】请求体x-www-form-urlencoded:")
for k in flow.request.urlencoded_form:
ctx.log.error(f"{k}:{flow.request.urlencoded_form[k]}")
ctx.log.error("")
elif 'application/json' in str(flow.request.headers):
ctx.log.error("【6】请求体json:n"+flow.request.get_text())
else:
ctx.log.error("【6】获取请求体失败,或请求体格式暂不支持")
ctx.log.error("")
pass
三、处理请求时常用的代码片段(收集自网络)
1、将url参数转为字典
from urllib.parse import parse_qs, urlparse
url = 'https://www.test.com?action=add&id=007&user=&wd=&action=add2'
query = urlparse(url).query
print("原始url:"+url)
params = parse_qs(query)
print(params)
运行结果:
原始url:https://www.test.com?action=add&id=007&user=&wd=&action=add2
{'action': ['add', 'add2'], 'id': ['007']}
2、将字典转为url参数
from urllib.parse import urlencode
params = {'action': 'add', 'id': 2}
result = urlencode(params)
print(result)
运行结果:
action=add&id=2
3、过滤函数,在所有处理请求前先过滤不匹配条件的请求
def allowed_urls(ori_str):#判断提供的字符串/url 是否符合匹配条件
check_dic=["ranjuan","ntest"]#需要包含的关键字
check_pass=["act","99"]#需要排除的关键字, 匹配条件是既需要包含关键字,又不能出现要排除的关键字
#return all(any(i in j for j in bvalue) for i in avalue)
if any(e in ori_str for e in check_dic):
if any(e in ori_str for e in check_pass):
return False
else:
return True
return False
print(allowed_urls('http://ranjuan.cn?ction=1')) #True
print(allowed_urls('http://ranjuan.cn?action=1'))#False
print(allowed_urls('http://ranjuan.cn?cction=199'))#False
print(allowed_urls('http://ranjuantesst.cn?ction=1'))#True
print(allowed_urls('http://rantestjuan.cn?ction=1'))#True
4、md5函数
import hashlib
stringA='123456'
str_md5 = hashlib.md5(stringA.encode(encoding='utf-8')).hexdigest()
print(str_md5)
5、AES加密函数
2024.10更新:python中如果用AES模块按下面方法还是不行,建议直接先卸载crypto,pycrypto;卸载完毕后只要直接安装pycryptodome即可(腾讯云函数python3.9环境 测试成功【pip install pycryptodome -t .】)!
aes加密使用会有一些坑,第一个就是python模块安装crypto后需要重命令相关的2个文件夹名字(首字母改成大写),然后再安装pycryptodome模块。另外就是密钥key建议一定要用16位长度,否则跨语言进行AES加解密时会自己python上写的可以加解密,但是对方那边就是解密不了(对方在自己的开发语言里面加解密也是正常的)。如果长度确实不够,那么填充key的方式两边需要确保一致才行!
def pads(text,length=16):
"""
#填充函数,使被加密数据的字节码长度是block_size的整数倍
"""
print(f"pads明文"+str(text))
count = len(text.encode('utf-8'))
add = length - (count % length)
entext = text + (chr(add) * add)
print(f"pads结果"+str(entext.encode("utf8")))
return entext.encode("utf8")
from Crypto.Cipher import AES
import base64
#1、pip install Crypto
#2、要去安装目录下把两个crypto的文件夹(我这是crypto、crypto-1.4.1.dist-info)首字母大写后再安装pycryptodome。
#3、pip install pycryptodome
#
text = 'hello world'
password = 'ABCdefG890123456' #秘钥,b就是表示为bytes类型;限制为16
aes = AES.new(password.encode('utf-8'),AES.MODE_ECB) #密码正好长度为16时可不用填充,一定要16位很关键
en_text = aes.encrypt(pads(text)) #加密明文
encrypted_text = str(base64.encodebytes(en_text), encoding='utf-8')
print("密文:",encrypted_text)


基于互联网精神,在注明出处的前提下本站文章可自由转载!
本文链接:https://ranjuan.cn/mitmproxy-python-http-methods-use/
微信赞赏
支付宝赞赏
发表评论