JavaScript 逆向爬蟲中的瀏覽器調(diào)試常見技巧(下)
上一篇文章聊到 JS 逆向中關(guān)于瀏覽器調(diào)試的常用技巧,文末的實戰(zhàn)并沒有闡述加密參數(shù)的破解過程
JavaScript 逆向爬蟲中的瀏覽器調(diào)試常見技巧
本篇文章將繼續(xù)聊聊破解加密參數(shù)的完整流程
1. 分析
在 Network 面板下的 Filter 輸入框中輸入關(guān)鍵字:api/movie status-code:200
然后,在底部頁面導(dǎo)航區(qū)域切換頁面,篩選出發(fā)送的網(wǎng)絡(luò)請求

PS:同一個頁面重復(fù)發(fā)送請求時,token 值都不一同,說明 token 值的生成規(guī)則與時間戳有一定的關(guān)系
回到上一節(jié)通過 XHR 斷點 + Call Stack 從源碼中找到真實發(fā)送網(wǎng)絡(luò)請求的位置

參數(shù) params 中的 token 值來源于變量 e,我們繼續(xù)進行分析
變量 a
變量 a 由頁碼數(shù) page、每一頁的限制數(shù)目 limit?計算所得
即:?
var a = (this.page - 1) * this.limit?Object(i["a"])
在 console 控制臺打印后發(fā)送是一個具體的函數(shù),變量?e 由這個函數(shù)生成
其中,參數(shù)?
this.$store.state.url.index為當前請求的路徑這里為:
/api/movie
接下來,在右側(cè)?Watch 面板添加對函數(shù)名?Object(i["a"]) 的監(jiān)聽,獲取函數(shù)的真實位置
即:函數(shù) i 的返回值即為 token

在函數(shù) i?中添加斷點,重新刷新頁面后觀察右側(cè)的Scope?面板區(qū)

我們發(fā)現(xiàn)以下規(guī)律:
arguments
該數(shù)組來源上面?Object(i["a"]) 函數(shù)傳遞的兩個參數(shù),即:path 和變量 a
r
數(shù)組 r 在 arguments 的基礎(chǔ)上添加了一個 10 位的時間戳
n.SHA1(r.join(",")).toString(n.enc.Hex)
數(shù)組 r 通過符號
,?合并成一個字符串,通過 SHA1 加密后賦值給?on.enc.Base64.stringify(n.enc.Utf8.parse([o, t].join(",")))
字符串 o 和時間戳字符串組成一個數(shù)組,然后通過符號 ,?合并成一個新的字符串
,轉(zhuǎn)換為 base64 賦值給 c,最后作為函數(shù)返回值返回
2. 逆向
首先,我們新建一個 JS 文件,用于編寫生成?token 的邏輯
2-1??時間戳及參數(shù)
其中函數(shù) get_token() 中的參數(shù) page、limit 分別對應(yīng)頁碼數(shù)(從 0 開始)、分頁數(shù)目
/**
?*??當前時間戳(10位)
?*/
function?get_timestramp()?{
????return?Math.round(new?Date().getTime()?/?1000).toString();
}
function?get_token(page,?limit){
????//獲取時間戳
????var?current_timestrap?=?get_timestramp()
????let?a?=?(page?-?1)?*?limit
????//組成第一個參數(shù)
????var?arguments?=?['/api/movie',?a,?current_timestrap]
????...
}
2-2? 加密
通過上面的分析,先進行一次 SHA1 加密,然后再進行一次?Base64 編碼轉(zhuǎn)換
//加密
function?encode(r,?t)?{
????var?o?=?sha1(r.join(","))
????console.log("SHA1加密后的結(jié)果為:",?o)
????//轉(zhuǎn)為base64
????var?pre?=?[o,?t].join(",")
????var?result?=?stringToBase64(pre)
????return?result
}
...
//SHA1加密
function?sha1(s)?{
???...
???//受限篇幅,源碼上傳在文末
}
...
//字符串base64編碼
function?stringToBase64(str)?{
????return?new?Buffer.from(str).toString("base64");
}
2-3??安裝依賴
由于需要通過 Python?調(diào)用 JS,這里以?PyExecJS?這種方式為例進行說明
#?安裝依賴庫
pip3?install?PyExecJS
當然,也可以參考下面文章中的其他方案
最全總結(jié)!聊聊 Python 調(diào)用 JS 的幾種方式
2-4? 測試一下
讀取本地 JS 文件,調(diào)用該文件中獲取 token 的方法,返回值作為 token?作為參數(shù)進行請求即可
import?requests
import?execjs
#?安裝依賴:pip3 install PyExecJS
headers?=?{
????...
}
def?js_from_file(file_name):
????"""
????讀取js文件
????:return:
????"""
????with?open(file_name,?'r',?encoding='UTF-8')?as?file:
????????result?=?file.read()
????return?result
#?參數(shù)
page?=?2
limit?=?10
context?=?execjs.compile(js_from_file('./mt.js'))
token?=?context.call("get_token",?page,?limit)
print("獲取token:",?token)
params?=?{
????'limit':?limit,
????'offset':?(page?-?1)?*?10,
????'token':?token,
}
response?=?requests.get('**',?headers=headers,?params=params)
print(response.text)
print(response.status_code)3. 總結(jié)
本文作為上篇文章的一個補充,詳細說明了獲取加密參數(shù)的完整流程
我已將文中所有源碼上傳到后臺,回復(fù)關(guān)鍵字「?js220409」即可以獲取
如果你覺得文章還不錯,請大家?點贊、分享、留言?下,因為這將是我持續(xù)輸出更多優(yōu)質(zhì)文章的最強動力!
END

