javascript
【JS 逆向百例】反混淆入门,某鹏教育 JS 混淆还原
關(guān)注微信公眾號(hào):K哥爬蟲,持續(xù)分享爬蟲進(jìn)階、JS/安卓逆向等技術(shù)干貨!
聲明
本文章中所有內(nèi)容僅供學(xué)習(xí)交流,抓包內(nèi)容、敏感網(wǎng)址、數(shù)據(jù)接口均已做脫敏處理,嚴(yán)禁用于商業(yè)用途和非法用途,否則由此產(chǎn)生的一切后果均與作者無關(guān),若有侵權(quán),請(qǐng)聯(lián)系我立即刪除!
逆向目標(biāo)
- 目標(biāo):某鵬教育登錄接口加密,含有簡(jiǎn)單的 JS 混淆
- 主頁(yè):aHR0cHM6Ly9sZWFybi5vcGVuLmNvbS5jbi8=
- 接口:aHR0cHM6Ly9sZWFybi5vcGVuLmNvbS5jbi9BY2NvdW50L1VuaXRMb2dpbg==
- 逆向參數(shù):Form Data:black_box: eyJ2IjoiR01KM0VWWkVxMG0ydVh4WUd...
逆向過程
本次逆向的目標(biāo)同樣是一個(gè)登錄接口,其中的加密 JS 使用了簡(jiǎn)單的混淆,可作為混淆還原的入門級(jí)教程,來到登錄頁(yè)面,隨便輸入賬號(hào)密碼進(jìn)行登錄,其中登錄的 POST 請(qǐng)求里, Form Data 有個(gè)加密參數(shù) black_box,也就是本次逆向的目標(biāo),抓包如下:
直接搜索 black_box,在 login.js 里可以很容易找到加密的地方,如下圖所示:
看一下 _fmOpt.getinfo() 這個(gè)方法,是調(diào)用了 fm.js 里的 OO0O0() 方法,看這個(gè)又是 0 又是 O 的,多半是混淆了,如下圖所示:
點(diǎn)進(jìn)去看一下,整個(gè) fm.js 都是混淆代碼,我們選中類似 OQoOo[251] 的代碼,可以看到實(shí)際上是一個(gè)字符串對(duì)象,也可以直接在 Console 里輸出看到其實(shí)際值,這個(gè) OO0O0 方法返回的 oOoo0[OQoOo[448]](JSON[OQoOo[35]](O0oOo[OQoOo[460]])),就是 black_box 的值,如下圖所示:
仔細(xì)觀察,可以發(fā)現(xiàn) OQoOo 應(yīng)該是一個(gè)類似數(shù)組的東西,通過傳入元素下標(biāo)來依次取其真實(shí)值,隨便搜索一個(gè)值,可以在代碼最后面找到一個(gè)數(shù)組,這個(gè)數(shù)組其實(shí)就是 OQoOo,可以傳入下標(biāo)來驗(yàn)證一下,如下圖所示:
到這里其實(shí)就知道了其大致混淆原理,我們可以把這個(gè)JS 拿下來,到本地寫個(gè)小腳本,將這些值替換一下:
# ================================== # --*-- coding: utf-8 --*-- # @Time : 2021-11-09 # @Author : 微信公眾號(hào):K哥爬蟲 # @FileName: replace_js.py # @Software: PyCharm # @describe: 混淆還原小腳本 # ==================================# 待替換的值(太多了,僅列出少部分) # 以實(shí)際列表為準(zhǔn),要和 fm_old.js 里的列表一致 item = ['referrer', 'absolute', 'replace',...]# 混淆后的 JS with open("fm_old.js", "r", encoding="utf-8") as f:js_lines = f.readlines()js = "" for j in js_lines:js += jfor i in item:# Qo00o 需要根據(jù)你 fm_old.js 具體的字符串進(jìn)行替換str_old = "Qo00o[{}]".format(item.index(i))js = js.replace(str_old, '"' + i + '"')# 還原后的 JS with open("fm_new.js", "w", encoding="utf-8") as f:f.write(js)使用此腳本替換后,可能會(huì)發(fā)現(xiàn) JS 會(huì)報(bào)錯(cuò),原因是一些換行符、斜杠解析錯(cuò)誤,以及雙引號(hào)重復(fù)使用的問題,可以自己手動(dòng)修改一下。
這里需要注意的一點(diǎn),fm.js 后面還有個(gè)后綴,類似 t=454594,t=454570 等,不同的后綴得到的 JS 內(nèi)容也有差異,各種函數(shù)變量名和那個(gè)列表元素順序不同,實(shí)際上調(diào)用的方法是同一個(gè),所以影響不大,只需要注意替換時(shí)列表內(nèi)容、需要替換的那個(gè)字符串和你下載的 JS 文件里的一致即可。
將 JS 還原后,我們可以將還原后的 JS 替換掉網(wǎng)站本身經(jīng)過混淆后的 JS,這里替換方法有很多,比如使用 Fiddler 等抓包工具替換響應(yīng)、使用 ReRes 之類的插件進(jìn)行替換、使用瀏覽器開發(fā)者工具自帶的 Overrides 功能進(jìn)行替換(Chrome 64 之后才有的功能)等,這里我們使用 Fiddler 的 Autoresponder 功能來替換。
實(shí)測(cè)這個(gè) fm.js 的后綴短時(shí)間內(nèi)不會(huì)改變,所以可以直接復(fù)制其完整地址來替換,要嚴(yán)謹(jǐn)一點(diǎn)的話,我們可以用正則表達(dá)式來匹配這個(gè) t 值,在 Fiddler 里面選擇 AutoResponder,點(diǎn)擊 Add Rule,添加替換規(guī)則,正則表達(dá)式的方法寫法如下:regex:https:\/\/static\.tongdun\.net\/v3\/fm\.js\?t=\d+,注意 regex 前綴必不可少,上方依次選中 Enable rules(應(yīng)用規(guī)則)、Accept all CONNECTs(接受所有連接)、Unmatched requests passthrough(不匹配規(guī)則的就按照之前的請(qǐng)求地址發(fā)送過去),Enable Latency 是設(shè)置延遲生效時(shí)間,不用勾選,如下圖所示:
替換后再次登錄,下斷點(diǎn),可以看到現(xiàn)在的 JS 已經(jīng)清晰了不少,再看看這個(gè)函數(shù)最后的 return 語句,oQOQ0["blackBox"] 包含了 it、os、t、v 三個(gè)參數(shù),使用 JSON 的 stringify 方法將其轉(zhuǎn)換成字符串,然后調(diào)用 QQo0 方法進(jìn)行加密,如下圖所示:
我們先來看看 oQOQ0["blackBox"] 里的四個(gè)參數(shù),其中 it、os、v 三個(gè)參數(shù)在這個(gè)函數(shù)開始就已經(jīng)有定義,v 就是 Q0oQQ["version"],是定值,直接搜索可以發(fā)現(xiàn)這個(gè)值是在最開始的那個(gè)大列表里,os 為定值,it 是兩個(gè)時(shí)間戳相減的值,O000o 這個(gè)方法就是兩個(gè)值進(jìn)行相減,oQOQo 這個(gè)時(shí)間戳可以搜索 var oQOQo,是一開始加載就生成的時(shí)間戳,JS 一開始加載到點(diǎn)擊登陸進(jìn)入加密函數(shù),也就一分鐘左右,所以這里我們可以直接生成一個(gè)五位隨機(jī)數(shù)(一分鐘左右在毫秒上的差值在五位數(shù)左右)。
現(xiàn)在就剩下一個(gè) t 參數(shù)了,往下看 t 其實(shí)就是 Q0oQQ["tokens"],中間經(jīng)過了一個(gè) if-else 語句,可以埋下斷點(diǎn)進(jìn)行調(diào)試,發(fā)現(xiàn)其實(shí)只執(zhí)行了 else 語句,對(duì) t 賦值也就這一句,所以剩下的代碼其實(shí)在扣的時(shí)候都可以刪掉。
這個(gè) tokens 多次測(cè)試發(fā)現(xiàn)是不變的,嘗試直接搜索一下 token 關(guān)鍵字,可以發(fā)現(xiàn)其賦值的地方,對(duì) id 按照 | 符號(hào)進(jìn)行分割,取其第 1 個(gè)索引值就是 tokens,再看看 id 的值,并沒有找到明顯的生成邏輯,復(fù)制其值搜索一下,發(fā)現(xiàn)是通過一個(gè)接口返回的,可以直接寫死,也可以自己先去請(qǐng)求一下這個(gè)接口,取其返回的值,如下圖所示:
自此所有參數(shù)都找完了,回到原來的 return 位置,還差一個(gè)加密函數(shù),即 ooOoO["encode"](),直接跟進(jìn)去,將這個(gè)方法扣下來即可,本地調(diào)試缺啥補(bǔ)啥,將用到的函數(shù)補(bǔ)全就行了。
完整代碼
GitHub 關(guān)注 K 哥爬蟲,持續(xù)分享爬蟲相關(guān)代碼!歡迎 star !https://github.com/kgepachong/
以下只演示部分關(guān)鍵代碼,不能直接運(yùn)行! 完整代碼倉(cāng)庫(kù)地址:https://github.com/kgepachong/crawler/
JavaScript 加密關(guān)鍵代碼架構(gòu)
function oQ0OQ(Q0o0, o0OQ) {return Q0o0 < o0OQ; }function O000O(Q0o0, o0OQ) {return Q0o0 >> o0OQ; }function Qo0oo(Q0o0, o0OQ) {return Q0o0 | o0OQ; }function OOO0Q(Q0o0, o0OQ) {return Q0o0 << o0OQ; }function OooQo(Q0o0, o0OQ) {return Q0o0 & o0OQ; }function Oo0OO(Q0o0, o0OQ) {return Q0o0 + o0OQ; }var oQoo0 = {}; oQoo0["_keyStr"] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", oQoo0["encode"] = function QQQ0(Q0o0) {var o0OQ = 62;while (o0OQ) {switch (o0OQ) {case 116 + 13 - 65: {}case 118 + 8 - 63: {}case 94 + 8 - 40: {}case 122 + 6 - 63: {}}}}; oQoo0["_utf8_encode"] = function oOQ0(Q0o0) {}function OOoO0() {var tokens = "e0ia+fB5zvGuTjFDgcKahQwg2UEH8b0k7EK/Ukt4KwzyCbpm11jjy8Au64MC6s7HvLRacUxd7ka4AdDidJmYAA==";var version = "+X+3JWoUVBc12xtmgMpwzjAone3cp6/4QuFj7oWKNk+C4tqy4un/e29cODlhRmDy";var Oo0O0 = {};Oo0O0["blackBox"] = {};Oo0O0["blackBox"]["v"] = version;Oo0O0["blackBox"]["os"] = "web";Oo0O0["blackBox"]["it"] = parseInt(Math.random() * 100000);Oo0O0["blackBox"]["t"] = tokens;return oQoo0["encode"](JSON.stringify(Oo0O0["blackBox"])); }// 測(cè)試樣例 console.log(OOoO0())Python 登錄關(guān)鍵代碼
# ================================== # --*-- coding: utf-8 --*-- # @Time : 2021-11-10 # @Author : 微信公眾號(hào):K哥爬蟲 # @FileName: open_login.py # @Software: PyCharm # ==================================import time import execjs import requestslogin_url = "脫敏處理,完整代碼關(guān)注 GitHub:https://github.com/kgepachong/crawler"def get_black_box():with open('get_black_box.js', 'r', encoding='utf-8') as f:exec_js = f.read()black_box = execjs.compile(exec_js).call('OOoO0')return black_boxdef login(black_box, username, password):params = {"bust": str(int(time.time() * 1000))}data = {"loginName": username,"passWord": password,"validateNum": "","black_box": black_box}headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"}response = requests.post(url=login_url, params=params, data=data, headers=headers)print(response.json())def main():username = input("請(qǐng)輸入登錄賬號(hào): ")password = input("請(qǐng)輸入登錄密碼: ")black_box = get_black_box()login(black_box, username, password)if __name__ == '__main__':main()總結(jié)
以上是生活随笔為你收集整理的【JS 逆向百例】反混淆入门,某鹏教育 JS 混淆还原的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 好消息,我国前三季度GDP实现转正,全年
- 下一篇: 兴业信用卡美食优惠 兴业银行大众点评活动