聲明:
該文章為學習使用,嚴禁用于商業用途和非法用途,違者后果自負,由此產生的一切后果均與作者無關
一、找出需要加密的參數
- js運行 atob(‘aHR0cHM6Ly93d3cucWNjLmNvbS93ZWIvc2VhcmNoP2tleT0lRTQlQjglODclRTglQkUlQkUlRTklOUIlODYlRTUlOUIlQTI=’) 拿到網址,F12打開調試工具,點擊分頁找到 search/searchMultit 請求,鼠標右擊請求找到Copy>Copy as cUrl(cmd)
- 打開網站:https://spidertools.cn/#/curl2Request,把拷貝好的curl轉成python代碼
- 新建qichacha.py文件,把代碼復制到該文件內,把請求中的參數拷貝給data,請求中的data參數換成json,運行文件,發現請求成功并成功獲取到數據
- 然后把代碼中header、cookie注釋調試后會發現一個雖生成的header、QCCSESSID是加密的
- 在請求cookies中分析得知 ,QCCSESSID是后端生成的可以不用管
二、定位參數加密位置
- 由于加密的header是動態生成的,顯然使用關鍵字搜索無法定位到,直接切換到sources,添加XHR攔截 search/searchMulti
- 一直點擊跳到下一個函數,會看到作用域header里面已經沒有請求頭,在代碼里又看到熟悉的參數 x-pid,可以嘗試的分析里面的代碼
- 鼠標懸浮到 o.default 找到該函數的位置,會發現里面有個header 賦值的代碼,在該代碼打上斷點
- 結束此次斷點調試,點擊分頁重新發送請求,鼠標懸浮到 i 上發現有隨機生成的 header,并且很容易就找到 i = (0, a.default)(t, e.data),l = (0, r.default)(t, e.data, (0, s.default)()),i 是header的key,l是header的value
三、扣出加密代碼
- 創建qichacha.js文件,用于放扣出的js代碼
- 先把 header 的 key 和 value 扣出來,i = (0, a.default)(t, e.data),l = (0, r.default)(t, e.data, (0, s.default)()),把 t、e.data 在控制臺打印出來會發現 t 是請求路徑,e.data是請求參數
- 扣出加密header的key:i = (0, a.default)(t, e.data)
- 根據第二步已經知道 t 是請求路徑,e.data是請求參數,所以只要扣出 a.default 就行, 把 a.default 打印出來會發現,a.default是方法
- 點擊打印出的 a.default 方法,會快速找到該方法的位置,會發現只有 o.default、a.default是值得注意的方法,其他都是js語法,在方法內部打上斷點,并把 s 方法扣到qichacha.js,把 a.default 換成 s
- 開始扣出 s 方法中的 o.default:結束本次調試,點擊分頁重新發送請求,鼠標懸浮到 o.default,點擊藍色部分找到該方法,會再次發現 o.default 方法在該代碼打上斷點,并把 r 方法 js扣到qichacha.js,并把 s 中 o.default 替換成 r
- 結束本次斷點,點擊分頁重新發送請求,一直點擊跳到斷店調試,看到 e 是路徑+參數的字符串時,鼠標懸浮到 r 方法中的 o.default,點擊藍色部分找到該方法,會發現是個HMAC加密
- 分析是哪種 HMAC 加密,在控制臺分別打印出:加密數據 e、加密密鑰 t、解密結果(0,o.default)(e, t).toString(),打開網站 https://www.dute.org/hmac?ref=search,輸入密鑰 、加密數據,加密算法是 HMAC SHA 512
- 根據 HMAC SHA 512 算法完成 r 方法,經過測試相同的加密數據和加密密鑰,解密結果相同,至此 s 方法中的 o.default 完全扣出,至此 s 方法中的 o.defaul 完全扣出
- 開始扣出 s 方法中的 a.default:分析 s 方法中的 a.default,點擊藍色部分找到該方法,會發現是個 r 方法,里面值得注意的是 o.default,其他都是js語法,在 for循環打上斷點,,并把 r 方法 js扣到 qichacha.js 因為和之前 r 方法沖突命名為 r1,并把 s 中 a.default 替換成r1
- 結束本次斷點,點擊分頁重新發送請求,一直點擊跳到斷店調試,看到 r 方法停止調試,在控制臺輸出 o.default,把打印結果復制出來,補全 r1 方法,至此 s 方法中的 a.default 完全扣出
- 驗證結果 i = (0, a.default)(t, e.data),刪除除了 e.headers[i] = l 之外的其他斷點,點擊第一頁發送請求,把 i 打印控制臺,再運行 qichacha.js 文件打印 i,對比兩個 i 會發現兩個之一樣
- 扣出加密header的key:l = (0, r.default)(t, e.data, (0, s.default)())
- 根據第二步已經知道 t 是請求路徑,e.data是請求參數,所以只要扣出 r.default 、r.default 就行
- 點擊分頁重新發送請求,鼠標懸浮到 s.default,點擊藍色部分找到該方法,會發現 _default 方法,該方法中都是 js 語法,只需把該函數拷貝出來就行,拷貝出來后命名為 s1,把 s.default 換成 s1
- 測試 s1 方法會發現,報 windows.tid 為 undefined的錯誤,切換到提示工具 Elements ,全局搜索 windows.tid 會發現該值是固定的值,把該值賦值下來替換 s1 并刪除 s1
- 開始扣出 r.default 方法:鼠標懸浮到 r.defaul,點擊藍色部分找到該方法,會發現一個 s 方法,會發現只有 o.default、a.default是值得注意的方法,其他都是js語法,在方法內部打上斷點,并把 s 方法扣到qichacha.js 命名為 s1,把 r.default 換成 s1
- 仔細分析 s1 會發現和之前的 s 方法類似,可以先試著,把 s1 方法中的 o.default、a.default 替換成之前的 r、r1
- 測試 s1 方法:把 n、i、(0,o.default)(n + “pathString” + i + t, (0,a.default)(n)) 輸出控制臺,把 n、i、tid值傳給 s1 并打印出來,發現同樣的參數,得到值一樣,說明 s1 內部的 o.default、a.default 確實是 r、r1,結束斷點調試
四、驗證結果
- 修改qichacha.js,把 t、par作為參數傳給 main 方法,并運行文件,打印出生成的 key、value
- 點擊第一頁重新請求,在控制臺輸出 i、l,對比發現值都是一樣的
- 修改 qichacha.py 文件,運行文件,數據獲取成功
五、最終代碼
- qichacha.js
var cryptoJs = require('crypto-js')var r = function (e, t) {var hmacSha512 = cryptoJs.HmacSHA512(e, t);return hmacSha512.toString()
};var r1 = function () {var o = {"n": 20,"codes": {"0": "W","1": "l","2": "k","3": "B","4": "Q","5": "g","6": "f","7": "i","8": "i","9": "r","10": "v","11": "6","12": "A","13": "K","14": "N","15": "k","16": "4","17": "L","18": "1","19": "8"}};for (var e = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase(), t = e + e, n = "", i = 0; i < t.length; ++i) {var a = t[i].charCodeAt() % o.n;n += o.codes[a]}return n
};var s = function () {var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}, t = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase(), n = JSON.stringify(e).toLowerCase();return r(t + n, r1(t)).toLowerCase().substr(8, 20)
};var s1 = function () {var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}, t = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : "", n = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase(), i = JSON.stringify(e).toLowerCase();return r(n + "pathString" + i + t, r1(n))
};function main(t, par) {var i = s(t, par);var l = s1(t, par, 'f7d239d312096b665fd9e4a46e603592');return {key: i, value: l}
}var t = '/api/search/searchmulti';
var par = {"searchKey": "萬達集團","pageIndex": 1,"pageSize": 20,
}
console.log(main(t,par))
- qichacha.py
import requests
import execjs
import furlheaders = {"authority": "www.qcc.com","accept": "application/json, text/plain, */*","accept-language": "zh-CN,zh;q=0.9","cache-control": "no-cache","content-type": "application/json","origin": "https://www.qcc.com","pragma": "no-cache","referer": "https://www.qcc.com/web/search?key=^%^E4^%^B8^%^87^%^E8^%^BE^%^BE^%^E9^%^9B^%^86^%^E5^%^9B^%^A2","sec-ch-ua": "^\\^Google","sec-ch-ua-mobile": "?0","sec-ch-ua-platform": "^\\^Windows^^","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/119.0.0.0 Safari/537.36","x-requested-with": "XMLHttpRequest","x-pid": "30ccc65659628b892fb5c1c99a083a95",# "43d930d400d14394164a": "87ae6559513fa06d04d868f1a98d6e48637c09c76415caadecdd28f8e52f9912bca862cce00619ae312b20a57701dcf7858063dd9bb071fad27c01076e24be9d",
}
cookies = {"QCCSESSID": "eaa3818b5a849d53ca70e37dac","qcc_did": "fe88cac5-6065-4d4d-bdc6-f642aea5386b"
}
url = "https://www.qcc.com/api/search/searchMulti"
data = {"searchKey": "萬達集團","pageIndex": 2,"pageSize": 20
}with open('qichacha.js','r') as js_file:js = execjs.compile(js_file.read())url_info = furl.furl(url)get_headers = js.call('main',str(url_info.path),data)headers[get_headers['key']] = get_headers['value']print(headers)response = requests.post(url, headers=headers, cookies=cookies, json=data)print(response.text)print(response)