【JS 逆向百例】反混淆入門(mén)与倡,某鵬教育 JS 混淆還原

聲明

本文章中所有內(nèi)容僅供學(xué)習(xí)交流,抓包內(nèi)容昆稿、敏感網(wǎng)址纺座、數(shù)據(jù)接口均已做脫敏處理,嚴(yán)禁用于商業(yè)用途和非法用途溉潭,否則由此產(chǎn)生的一切后果均與作者無(wú)關(guān)净响,若有侵權(quán),請(qǐng)聯(lián)系我立即刪除喳瓣!

逆向目標(biāo)

  • 目標(biāo):某鵬教育登錄接口加密馋贤,含有簡(jiǎn)單的 JS 混淆
  • 主頁(yè):aHR0cHM6Ly9sZWFybi5vcGVuLmNvbS5jbi8=
  • 接口:aHR0cHM6Ly9sZWFybi5vcGVuLmNvbS5jbi9BY2NvdW50L1VuaXRMb2dpbg==
  • 逆向參數(shù):Form Data:black_box: eyJ2IjoiR01KM0VWWkVxMG0ydVh4WUd...

逆向過(guò)程

本次逆向的目標(biāo)同樣是一個(gè)登錄接口,其中的加密 JS 使用了簡(jiǎn)單的混淆畏陕,可作為混淆還原的入門(mén)級(jí)教程配乓,來(lái)到登錄頁(yè)面,隨便輸入賬號(hào)密碼進(jìn)行登錄惠毁,其中登錄的 POST 請(qǐng)求里扰付, Form Data 有個(gè)加密參數(shù) black_box,也就是本次逆向的目標(biāo)仁讨,抓包如下:

01.png

直接搜索 black_box羽莺,在 login.js 里可以很容易找到加密的地方,如下圖所示:

02.png

看一下 _fmOpt.getinfo() 這個(gè)方法洞豁,是調(diào)用了 fm.js 里的 OO0O0() 方法盐固,看這個(gè)又是 0 又是 O 的,多半是混淆了丈挟,如下圖所示:

03.png

點(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 的值孝情,如下圖所示:

04.png

仔細(xì)觀察,可以發(fā)現(xiàn) OQoOo 應(yīng)該是一個(gè)類似數(shù)組的東西洒嗤,通過(guò)傳入元素下標(biāo)來(lái)依次取其真實(shí)值箫荡,隨便搜索一個(gè)值,可以在代碼最后面找到一個(gè)數(shù)組渔隶,這個(gè)數(shù)組其實(shí)就是 OQoOo羔挡,可以傳入下標(biāo)來(lái)驗(yàn)證一下洁奈,如下圖所示:

05.png

到這里其實(shí)就知道了其大致混淆原理,我們可以把這個(gè)JS 拿下來(lái)绞灼,到本地寫(xiě)個(gè)小腳本利术,將這些值替換一下:

# ==================================
# --*-- coding: utf-8 --*--
# @Time    : 2021-11-09
# @Author  : 微信公眾號(hào):K哥爬蟲(chóng)
# @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 += j

for 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ù)使用的問(wèn)題,可以自己手動(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)過(guò)混淆后的 JS叮贩,這里替換方法有很多击狮,比如使用 Fiddler 等抓包工具替換響應(yīng)、使用 ReRes 之類的插件進(jìn)行替換益老、使用瀏覽器開(kāi)發(fā)者工具自帶的 Overrides 功能進(jìn)行替換(Chrome 64 之后才有的功能)等彪蓬,這里我們使用 Fiddler 的 Autoresponder 功能來(lái)替換。

實(shí)測(cè)這個(gè) fm.js 的后綴短時(shí)間內(nèi)不會(huì)改變捺萌,所以可以直接復(fù)制其完整地址來(lái)替換档冬,要嚴(yán)謹(jǐn)一點(diǎn)的話,我們可以用正則表達(dá)式來(lái)匹配這個(gè) t 值桃纯,在 Fiddler 里面選擇 AutoResponder酷誓,點(diǎn)擊 Add Rule,添加替換規(guī)則慈参,正則表達(dá)式的方法寫(xiě)法如下: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ā)送過(guò)去),Enable Latency 是設(shè)置延遲生效時(shí)間壮锻,不用勾選琐旁,如下圖所示:

06.png

替換后再次登錄,下斷點(diǎn)猜绣,可以看到現(xiàn)在的 JS 已經(jīng)清晰了不少灰殴,再看看這個(gè)函數(shù)最后的 return 語(yǔ)句,oQOQ0["blackBox"] 包含了 it掰邢、os牺陶、tv 三個(gè)參數(shù)辣之,使用 JSON 的 stringify 方法將其轉(zhuǎn)換成字符串掰伸,然后調(diào)用 QQo0 方法進(jìn)行加密,如下圖所示:

07.png

我們先來(lái)看看 oQOQ0["blackBox"] 里的四個(gè)參數(shù)怀估,其中 it狮鸭、osv 三個(gè)參數(shù)在這個(gè)函數(shù)開(kāi)始就已經(jīng)有定義多搀,v 就是 Q0oQQ["version"]歧蕉,是定值,直接搜索可以發(fā)現(xiàn)這個(gè)值是在最開(kāi)始的那個(gè)大列表里康铭,os 為定值惯退,it 是兩個(gè)時(shí)間戳相減的值,O000o 這個(gè)方法就是兩個(gè)值進(jìn)行相減从藤,oQOQo 這個(gè)時(shí)間戳可以搜索 var oQOQo催跪,是一開(kāi)始加載就生成的時(shí)間戳,JS 一開(kāi)始加載到點(diǎn)擊登陸進(jìn)入加密函數(shù)呛哟,也就一分鐘左右叠荠,所以這里我們可以直接生成一個(gè)五位隨機(jī)數(shù)(一分鐘左右在毫秒上的差值在五位數(shù)左右)。

08.png

現(xiàn)在就剩下一個(gè) t 參數(shù)了扫责,往下看 t 其實(shí)就是 Q0oQQ["tokens"]榛鼎,中間經(jīng)過(guò)了一個(gè) if-else 語(yǔ)句,可以埋下斷點(diǎn)進(jìn)行調(diào)試鳖孤,發(fā)現(xiàn)其實(shí)只執(zhí)行了 else 語(yǔ)句者娱,對(duì) t 賦值也就這一句,所以剩下的代碼其實(shí)在扣的時(shí)候都可以刪掉苏揣。

09.png

這個(gè) tokens 多次測(cè)試發(fā)現(xiàn)是不變的黄鳍,嘗試直接搜索一下 token 關(guān)鍵字,可以發(fā)現(xiàn)其賦值的地方平匈,對(duì) id 按照 | 符號(hào)進(jìn)行分割框沟,取其第 1 個(gè)索引值就是 tokens藏古,再看看 id 的值,并沒(méi)有找到明顯的生成邏輯忍燥,復(fù)制其值搜索一下拧晕,發(fā)現(xiàn)是通過(guò)一個(gè)接口返回的,可以直接寫(xiě)死梅垄,也可以自己先去請(qǐng)求一下這個(gè)接口厂捞,取其返回的值,如下圖所示:

10.png
11.png

自此所有參數(shù)都找完了队丝,回到原來(lái)的 return 位置靡馁,還差一個(gè)加密函數(shù),即 ooOoO["encode"]()机久,直接跟進(jìn)去臭墨,將這個(gè)方法扣下來(lái)即可,本地調(diào)試缺啥補(bǔ)啥吞加,將用到的函數(shù)補(bǔ)全就行了裙犹。

12.png

完整代碼

GitHub 關(guān)注 K 哥爬蟲(chóng),持續(xù)分享爬蟲(chóng)相關(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哥爬蟲(chóng)
# @FileName: open_login.py
# @Software: PyCharm
# ==================================


import time
import execjs
import requests


login_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_box


def 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()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市码党,隨后出現(xiàn)的幾起案子德崭,更是在濱河造成了極大的恐慌,老刑警劉巖揖盘,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眉厨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡兽狭,警方通過(guò)查閱死者的電腦和手機(jī)憾股,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)箕慧,“玉大人服球,你說(shuō)我怎么就攤上這事〉呓梗” “怎么了斩熊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)伐庭。 經(jīng)常有香客問(wèn)我粉渠,道長(zhǎng)分冈,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任渣叛,我火速辦了婚禮丈秩,結(jié)果婚禮上盯捌,老公的妹妹穿的比我還像新娘淳衙。我一直安慰自己,他們只是感情好饺著,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布箫攀。 她就那樣靜靜地躺著,像睡著了一般幼衰。 火紅的嫁衣襯著肌膚如雪靴跛。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天渡嚣,我揣著相機(jī)與錄音梢睛,去河邊找鬼。 笑死识椰,一個(gè)胖子當(dāng)著我的面吹牛绝葡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腹鹉,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼藏畅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了功咒?” 一聲冷哼從身側(cè)響起愉阎,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎力奋,沒(méi)想到半個(gè)月后榜旦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡景殷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年溅呢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滨彻。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡藕届,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出亭饵,到底是詐尸還是另有隱情休偶,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布辜羊,位于F島的核電站踏兜,受9級(jí)特大地震影響词顾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜碱妆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一肉盹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疹尾,春花似錦上忍、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至繁成,卻和暖如春吓笙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背巾腕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工面睛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人尊搬。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓叁鉴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親毁嗦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子亲茅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容