解析思路:
断点提取找到发送请求对象之前位置,可看到负载参数已经加密,加密方法为拦截器加密,一步步上调找到拦截器,拦截器对象包含request和response对象,找到request对象的js地址,下断点在方法开始和结束,调试进入断点,可以发现在函数开始的data负载是明文的,在函数结束为密文数据,在函数中查找将s['data']加密的方法,在前后下断点验证,扣js代码,发现js为框架js,将整个模块扣出,将未定义的comxxxxx替换为global,定义变量接收一下模块返回。
Hook注入过反debugger:
var AAA=Function.prototype.constructor
Function.prototype.constructor=function(x){
if(X!="debugger"){
return AAA(x)
};
return function(){};
}
js代码如下:
function PolicyInfoByTypeIdParam_encode(m, w) {
if (!w)
w = let123.Writer.create()
if (m.policyType != null && Object.hasOwnProperty.call(m, "policyType"))
w.uint32(10).string(m.policyType)
if (m.centralId != null && Object.hasOwnProperty.call(m, "centralId"))
w.uint32(18).string(m.centralId)
if (m.province != null && Object.hasOwnProperty.call(m, "province"))
w.uint32(26).string(m.province)
if (m.city != null && Object.hasOwnProperty.call(m, "city"))
w.uint32(34).string(m.city)
if (m.downtown != null && Object.hasOwnProperty.call(m, "downtown"))
w.uint32(42).string(m.downtown)
if (m.garden != null && Object.hasOwnProperty.call(m, "garden"))
w.uint32(50).string(m.garden)
if (m.sort != null && Object.hasOwnProperty.call(m, "sort"))
w.uint32(56).uint32(m.sort)
if (m.pageNum != null && Object.hasOwnProperty.call(m, "pageNum"))
w.uint32(64).uint32(m.pageNum)
if (m.pageSize != null && Object.hasOwnProperty.call(m, "pageSize"))
w.uint32(72).uint32(m.pageSize)
if (m.homePageFlag != null && Object.hasOwnProperty.call(m, "homePageFlag"))
w.uint32(80).uint32(m.homePageFlag)
return w
}
var let123;
!function(g){
xxxxxx省略,网页中扣
}();
function main(page,type){
var data = {
"pageNum": page,
"pageSize": 10,
"policyType": type,
"sort": 0,
"centralId": "",
"province": "",
"city": "",
"downtown": "",
"garden": ""
}
res = PolicyInfoByTypeIdParam_encode(data).finish().slice()
return
python异步代码如下:
import aiohttp
import asyncio
import execjs
import aiofiles
cookies = {
'Hm_lvt_6146f11e5afab71309b3accbfc4a932e': '1715668997,1715762038,1716444361',
'JSESSIONID': 'CC2103ACFEFB4772247641505A2D3368',
'Hm_lpvt_6146f11e5afab71309b3accbfc4a932e': '1716447925',
}
headers = {
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh,zh-CN;q=0.9',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Content-Type': 'application/octet-stream',
'Origin': 'https://www.spolicy.com',
'Pragma': 'no-cache',
'Referer': 'https://www.spolicy.com/typePolicy?id=4&name=%E6%94%BF%E7%AD%96%E6%96%87%E4%BB%B6',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
}
jscall = execjs.compile(open('./产业政策大数据平台请求参数逆向.js', 'r', encoding='utf-8').read())
async def fetch_data(session, url, page, idx):
parame = jscall.call('main', page, str(idx))
data = bytes(parame['data'])
async with session.post(url, cookies=cookies, headers=headers, data=data) as response:
result = await response.json()
async with aiofiles.open('response_data.txt', 'a') as f:
await f.write(str(result) + '\n')
async def main():
url = 'https://www.spolicy.com/info_api/policyType/showPolicyType'
async with aiohttp.ClientSession() as session:
tasks = []
for i in range(2, 4):
for page in range(1, 3):
task = asyncio.create_task(fetch_data(session, url, page, i))
tasks.append(task)
await asyncio.gather(*tasks)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
if loop.is_running():
asyncio.ensure_future(main())
else:
loop.run_until_complete(main())