一、逆向案例之Python逆向實現請求加密
//具體代碼如下 function l(t, e) {return t.toString().toUpperCase() > e.toString().toUpperCase() ? 1 : t.toString().toUpperCase() == e.toString().toUpperCase() ? 0 : -1}function u(t) {for (var e = Object.keys(t).sort(l), n = "", a = 0; a < e.length; a++)if (void 0 !== t[e[a]])if (t[e[a]] && t[e[a]]instanceof Object || t[e[a]]instanceof Array) {var i = JSON.stringify(t[e[a]]);n += e[a] + i} elsen += e[a] + t[e[a]];return n}function d(t) {for (var e in t)"" !== t[e] && void 0 !== t[e] || delete t[e];var n = r["a"] + u(t);return s(n).toLocaleLowerCase()}
1.1、步驟1,通過https://curlconverter.com/ 生成基礎爬蟲代碼
?1.2、Python代碼的結構調整
?1.3、補充知識點1:0值的概念? ? ??
在整型中,只有0是false? 其他的不管是正數1 2 3...還是-1,-2,-3...都是true
在字符串中,只有“”,是false ;其他的“ ”,“0”...等只要不是空字符串都是true
?1.4、補充知識點2:與或非邏輯運算? 短路運算
與 and(Pyhon)? &&(JS)? 全真為真,有假既假
或 or(Python)? || (JS)? ?有真既真,全假為假
?1.5、在對列表或字典進行便利的時候,不能直接對其進行操作(增刪改),需要用t.copy
""" 一、處理“零值” 【t是一個字典】for (var e in t)"" !== t[e] && void 0 !== t[e] || delete t[e];1、Python沒===2、Python 的 or 后面不能使用del命令,所以只能使用t.pop進行刪除操作3、在對列表或字典進行便利的時候,不能直接對其進行操作(增刪改),需要用t.copyt.copy()產生一個新的字典去循環便利, t.pop()在對原始字典進行操作處理"""# 方式1:for key in t.copy():print(key) # 獲取每一個keyt[key] != "" and t[key] != 0 or t.pop(key)print(t)
1.6、列表和元組排序:sort()? ? ? ?sorted()
l = [10, 2, 3, 1, 5] l.sort() # 從小到大 print(l) # [1, 2, 3, 5, 10]l = [10, 2, 3, 1, 5] l.sort(reverse=True) # 從大到小 print(l) # [10, 5, 3, 2, 1]l = ["2", "1", "10", "100", "5"] l.sort() # 字符串安裝ASCII碼字母表排序從小到大,所以結果有點亂七八糟 print(l) # ['1', '10', '100', '2', '5']# 補充知識點:sorted() l = [10, 2, 3, 1, 5] print(sorted(l)) # [1, 2, 3, 5, 10]# sorted() 比sort()強大的多,sort()不管什么是什么元素直接就給你硬排,而且需要對象進行調用 # sorted() 有返回值,可以自定義排序規則# 案例1 # my_sort函數是排序規則 只能有一個形參item,是l2中的每一個元素 def my_sort(item):# 然后根據每一個元素中的具體數據進行排序,根據什么排序,就返回什么,例如根據年齡就返回item[1]return item[1]# l2必須是列表或者元組,不能是字典,字典是無序的沒辦法排序 l2 = [["yuan", 23, 175], ["rain", 18, 180], ["alvin", 45, 165]] # 對誰進行排序l2 如何排序,排序規則 key print(sorted(l2, key=my_sort)) # [['rain', 18, 180], ['yuan', 23, 175], ['alvin', 45, 165]] print(sorted(l2, key=lambda item: item[1])) # 使用lambda函數# [['rain', 18, 180], ['yuan', 23, 175], ['alvin', 45, 165]]# 案例2 def my_sort2(item):return item["height"]l3 = [{"name": "yuan","age": 23,"height": 175},{"name": "rain","age": 18,"height": 180},{"name": "alvin","age": 45,"height": 165}, ] print(sorted(l3, key=my_sort2)) # [{'name': 'alvin', 'age': 45, 'height': 165}, {'name': 'yuan', 'age': 23, 'height': 175}, {'name': 'rain', 'age': 18, 'height': 180}]# 由以上案例1和案例2,總結如下 t = {"ts": 1754484535355,"pageNo": 4,"pageSize": 20,"total": 2749,"KIND": "GCJS","GGTYPE": "1","timeType": "6","BeginTime": "2025-02-06 00:00:00","EndTime": "2025-08-06 23:59:59" }# 由于字典沒辦法進行排序,所以只能先把字典轉列表 t_list = t.items() print("::::",t_list) # [('ts', 1754484535355), ('pageNo', 4), ('pageSize', 20), ('total', 2749), ('KIND', 'GCJS'), ('GGTYPE', '1'), ('timeType', '6'), ('BeginTime', '2025-02-06 00:00:00'), ('EndTime', '2025-08-06 23:59:59')]# 案例2 def my_sort3(item):return item[0]print(sorted(t_list,key=my_sort3)) # [('BeginTime', '2025-02-06 00:00:00'), ('EndTime', '2025-08-06 23:59:59'), ('GGTYPE', '1'), ('KIND', 'GCJS'), ('pageNo', 4), ('pageSize', 20), ('timeType', '6'), ('total', 2749), ('ts', 1754484535355)]
1.7、最終代碼:01逆向案例之Python逆向實現請求加密.py
import requests from hashlib import md5 import timeheaders = {'Accept': 'application/json, text/plain, */*','Accept-Language': 'zh-CN,zh;q=0.9','Connection': 'keep-alive','Content-Type': 'application/json;charset=UTF-8','Origin': 'https://ggzyfw.fujian.gov.cn','Referer': 'https://ggzyfw.fujian.gov.cn/business/list/','Sec-Fetch-Dest': 'empty','Sec-Fetch-Mode': 'cors','Sec-Fetch-Site': 'same-origin','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',# 'portal-sign': 'af2344abf132bc8634f2fc0e28466209', 因為需要替換它,所以這個不需要了'sec-ch-ua': '"Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"', }# 構建一個函數,用來生成sign,并且放到請求頭中 def get_sign(t):""" 一、處理“零值”for (var e in t)"" !== t[e] && void 0 !== t[e] || delete t[e];1、Python沒===2、Python 的 or 后面不能使用del命令,所以只能使用t.pop進行刪除操作3、在對列表或字典進行便利的時候,不能直接對其進行操作(增刪改),需要用t.copyt.copy()產生一個新的字典去循環便利, t.pop()在對原始字典進行操作處理"""# 方式1:# for key in t.copy():# print(key) # 獲取每一個key# t[key] != "" and t[key] != 0 or t.pop(key)# print(t)# 方式2:# 創建一個新的字典,保存更新后的數據new_t = {}for key, val in t.items():if val == "" or val == 0:continuenew_t[key] = val# print(new_t)# {'pageNo': 1, 'pageSize': 20, 'total': 2810, 'KIND': 'GCJS', 'GGTYPE': '1', 'timeType': '6', 'BeginTime': '2025-02-13 00:00:00', 'EndTime': '2025-08-13 23:59:59', 'ts': 1755068622063}# 二、排序new_t_list = new_t.items() # 把字典轉換為列表def my_sort3(item):return item[0]# 使用sorted() 對列表進行排序ret = sorted(new_t_list, key=my_sort3)s1 = ""# 便利列表 并把列表中的每個元組的key和val進行拼接,最后在拼接成一個字符串for key, val in ret:s1 += key + str(val) # key先和val拼接,val有可能是數字所以需要轉字符串print(s1)# BeginTime2025-02-13 00:00:00EndTime2025-08-13 23:59:59GGTYPE1KINDGCJSpageNo1pageSize20timeType6total2810ts1755068622063# 三、拼接參數字符串s2 = "B3978D054A72A7002063637CCDF6B2E5"s = s2 + s1# 四、md5值計算md5_obj = md5() #md5_obj.update(s.encode()) # 使用s的二進制字節串,所以.encodereturn md5_obj.hexdigest() # 返回32位的值# 創建一個main函數,把所有的業務流程放到該處 def main():json_data = {'pageNo': 1,'pageSize': 20,'total': 2810,'AREACODE': '','M_PROJECT_TYPE': '','KIND': 'GCJS','GGTYPE': '1','PROTYPE': '','timeType': '6','BeginTime': '2025-02-13 00:00:00','EndTime': '2025-08-13 23:59:59','createTime': '','ts': int(time.time() * 1000), # 獲取當前的時間戳}# 把Python實現的加密字段替換到請求頭中sign = get_sign(json_data)print(sign)headers["portal-sign"] = signresponse = requests.post('https://ggzyfw.fujian.gov.cn/FwPortalApi/Trade/TradeInfo', headers=headers,json=json_data)print(response.text)main()
?二、入口定位:請求堆棧&&攔截器關鍵字
入口定位-- 關鍵字搜索-- 方法關鍵字-- encrypt-- decrypt-- key關鍵字(最高頻)-- headers關鍵字-- 路徑關鍵字-- 攔截器關鍵字 如果把加密的內容放到了攔截器了,就直接用這個方式搜索interceptors.request.use(func)interceptors.response.use(func)-- 請求堆棧--主要關注請求,響應無關請求入口定位,與響應無關-- hook
2.1、請求堆棧
2.1.1、什么是堆棧,堆棧如何操作
?2.1.2、堆棧中的參數傳遞
?備注:由此我們可以推出,如果當我們不知道如何定位加密參數portal_sign找不到這個字段的時候,我們只要定位到最后的一個調用,根據堆棧的請求順序,倒序的去尋找portal_sign字段是在那個函數中產生的,然后在進行解析和代碼的處理
2.1.3、如何準確搞笑的定位入口
方式一:這種方式不好用,有其他的一些接口經常干擾
?方式二:經常使用這種方式
?補充:
2.2、攔截器關鍵字:n.then(t.shift(), t.shift())
?類似以上的多個接口都是一樣的加密方式,都有portal_sign
n =?n.then(t.shift(), t.shift());攔截器
-- 攔截器關鍵字 如果把加密的內容放到了攔截器了,就直接用這個方式搜索interceptors.request.use(func)interceptors.response.use(func)
當我們判斷這個網站把接口請求解密的動作放到了攔截器中,就不要用別的關鍵字搜索了
直接搜索
?這是前端人員固定語法,調用的一個庫,所以直接進行搜索 攔截器關鍵詞,就可以定位到入口