JS逆向之補(bǔ)環(huán)境過瑞數(shù)詳解
“瑞數(shù)” 是逆向路上的一座大山,是許多JS逆向者繞不開的一堵圍墻,也是跳槽簡歷上的一個(gè)亮點(diǎn)檐晕,我們必須得在下次跳槽前攻克它!! 好在現(xiàn)在網(wǎng)上有很多講解瑞數(shù)相關(guān)的文章辟灰,貼心的一步一步教我們?nèi)シ治鋈饠?shù)流程个榕,分析如何去扣瑞數(shù)邏輯,企圖以此教會我們 (手動狗頭)芥喇。卻鮮有文章詳細(xì)去講解如何通過純補(bǔ)環(huán)境的方式過瑞數(shù)西采。今天,它來了继控!
為了讓大家徹底搞定瑞數(shù)這個(gè)老大哥械馆,本文將從以下四個(gè)部分進(jìn)行描述:
- rs的流程邏輯
- 淺談扣代碼過rs
- 詳解補(bǔ)環(huán)境過rs
- 扣代碼與補(bǔ)環(huán)境對比
- 彎道超車環(huán)節(jié)
篇幅較長,坐穩(wěn)發(fā)車咯武通!
注:本文內(nèi)容均以一個(gè)新人友好型 rs4網(wǎng)站: 網(wǎng)上房地產(chǎn)狱杰,為例;
一:rs的流程邏輯
我們在做逆向的時(shí)候厅须,首先得分析出哪些加密參數(shù)
是需要逆向的,然后再是去逆向這些參數(shù)食棕。當(dāng)然瑞數(shù)也是一樣朗和。
所以我們第一步就是明確逆向的目標(biāo)
:
- 現(xiàn)象:上了rs的網(wǎng)站會請求兩次page_url,第二次請求page_url時(shí)才能得到正確的頁面內(nèi)容簿晓;
-
分析:分析其請求體眶拉,發(fā)現(xiàn)第二次請求page_url時(shí)帶上了cookie_s和cookie_t, 而cookies_s是來自第一次請求page_url時(shí)其響應(yīng)頭set的;
- 結(jié)論:那么我們的目標(biāo)就確定了 -- 破解
cookie_t的生成邏輯
;
現(xiàn)在,我們知道了需要逆向的加密參數(shù)是cookie_t憔儿,那么cookie_t是從何而來呢忆植,我們先分析一下網(wǎng)站的請求
瑞數(shù)網(wǎng)站請求流程分析:
第一次請求:
請求page_url,響應(yīng)狀態(tài)碼202谒臼,響應(yīng)頭中 set了 cookie_s朝刊;
響應(yīng)體是HTML源碼,從上到下可以分為四部分:我先劇透一下
它們的作用
-
一個(gè)meta標(biāo)簽蜈缤,其content內(nèi)容很長且是
動態(tài)
的(每次請求會變化),會在eval執(zhí)行第二層JS代碼時(shí)使用到; -
一個(gè)外鏈js文件系吭,一般同一頁面中其內(nèi)容是
固定
的谬擦,下面的自執(zhí)行函數(shù)會解密文件內(nèi)容生成eval執(zhí)行時(shí)需要的JS源碼,也就是第二層vm代碼趾徽; -
一個(gè)大自執(zhí)行函數(shù)(每次請求首頁都會
動態(tài)
變化)续滋,主要是解密外鏈JS內(nèi)容,給window添加一些屬性如$_ts孵奶,會在vm中使用; - 末尾的兩個(gè)script標(biāo)簽中的函數(shù)調(diào)用疲酌,會
更新cookie
,使其變長拒课。這里我們可以不用關(guān)注這個(gè)徐勃。
第二次請求:
請求外鏈js事示,一般內(nèi)容是固定的;
第三次請求:
請求page_url,返回200僻肖,攜帶cookie_s,cookie_T即可正常請求肖爵;
那么當(dāng)我們在瀏覽器中訪問該網(wǎng)站時(shí)到底發(fā)生了什么?其中有什么值得我們關(guān)注的臀脏?
我們先人工模擬一下瀏覽器加載page_url源碼
會發(fā)生什么:
- 瀏覽器加載meta 劝堪;
- 瀏覽器請求
外鏈js
并執(zhí)行js內(nèi)容; - 執(zhí)行page_url源碼
自執(zhí)行函數(shù)
揉稚,它內(nèi)部會將外鏈js解密
成eval需要的萬行js字符串秒啦,并給window.$_ts添加了很多屬性,然后調(diào)用eval函數(shù)進(jìn)入VM
執(zhí)行解密后的js搀玖,生成cookie余境,eval執(zhí)行完畢,繼續(xù)執(zhí)行自執(zhí)行函數(shù)灌诅; - 執(zhí)行末尾script標(biāo)簽中的代碼芳来,這些代碼會更新cookie_t(可以不用管)
- 執(zhí)行setTimeout、eventlistener回調(diào)函數(shù)(可以不用管)
瑞數(shù)執(zhí)行流程圖解
如下:
這里我們需要關(guān)注eval調(diào)用
的位置(也就是VM的入口
)猜拾,cookie生成的位置即舌。
注:瀏覽器v8調(diào)用eval執(zhí)行代碼時(shí)會開啟一個(gè)虛擬機(jī)(VM+數(shù)字)去執(zhí)行JS代碼。
我們直接hook eval
或搜索.call
就可以定位到調(diào)用eval的位置
_$Ln就是
解密后
的js代碼字符串挎袜;進(jìn)入vm是一個(gè)自執(zhí)行函數(shù)顽聂,其中有生成cookie
的邏輯,定位cookie可以直接hook或在vm代碼中搜索 (5)hook cookie代碼:
// hook 指定cookie賦值
(function () {
'use strict';
Object.defineProperty(document, 'cookie', {
set: function (cookie) {
if(cookie.indexOf("FSSBBIl1UgzbN7N80T")!= -1){
debugger;
}
return cookie;
}
});
})();
好了盯仪,以上就是瑞數(shù)的整體流程邏輯紊搪,那么我們該如何通過扣代碼
去生成cookie_t過瑞數(shù)呢?
二全景、淺談扣代碼過瑞數(shù)
由于每次請求瑞數(shù)page_url嗦明,其源碼是動態(tài)
變化的,VM代碼
也是動態(tài)變化的蚪燕,所以我們要保存一份靜態(tài)代碼方便調(diào)試娶牌;
直接如圖這樣,固定page_url響應(yīng)即可馆纳,
這樣請求時(shí)诗良,page_url中的 外鏈JS
是固定的,自執(zhí)行函數(shù)
是固定的鲁驶,VM中的代碼
也是固定的鉴裹,那么這份固定代碼每次生成的cookie_t 是不是也固定呢?
答案:不是,因?yàn)閏ookie_t的生成用到了隨機(jī)數(shù)和時(shí)間戳径荔,我們將這兩個(gè)變量也hook固定住督禽,最終生成的cookie_t就是固定的啦。
此時(shí)我們就可以開始扣這份靜態(tài)代碼了总处,扣到node生成的cookie_t
與瀏覽器執(zhí)行靜態(tài)代碼生成的cookie_t
一致就說明扣成功了狈惫。
扣代碼需要扣兩部分,page_url源碼部分和VM中生成cookie_t部分鹦马。
按照我們之前的分析胧谈,VM中代碼會用到window.$_ts
,因此我們得先保證從page_url源碼中扣下來的代碼執(zhí)行到eval時(shí)荸频,能正常生成window.$_ts
菱肖。
我們將page_url的自執(zhí)行函數(shù)
和外鏈JS內(nèi)容
先扣下來,meta content也先扣下來旭从,然后根據(jù)執(zhí)行報(bào)錯(cuò)簡單補(bǔ)一下環(huán)境稳强,使得扣下來代碼執(zhí)行到eval時(shí),其window.$_ts
和解密出的VM JS代碼
與本地靜態(tài)代碼生成的一致和悦。
這就說明我們page_url源碼部分扣好了键袱,接下來就可以去扣VM中生成cookie_t的部分。
上面我們分析到cookie的定位
摹闽,可以知道 _$bO這個(gè)函數(shù)中生成了二次cookie_t,那這就是我們扣代碼的起點(diǎn)
褐健,將該函數(shù)扣到文件末尾付鹿,直接執(zhí)行,然后缺啥補(bǔ)啥蚜迅,遇到使用BOM舵匾、DOM api
的代碼時(shí),我們可以使用等價(jià)邏輯替換
谁不,比如此處原邏輯是獲取meta標(biāo)簽中的content內(nèi)容坐梯,然后再刪除meta標(biāo)簽,我們可以直接等價(jià)替換成 return meta_content刹帕。如圖:
如果扣到最后吵血,node生成的cookie_t與本地靜態(tài)代碼生成的不一致,則說明我們扣的代碼有些環(huán)境檢測沒過偷溺,我們可以根據(jù)node與瀏覽器執(zhí)行的差異去定位蹋辅,比如node執(zhí)行到某個(gè)地方的值與瀏覽器執(zhí)行靜態(tài)代碼得到的不一樣,說明前面邏輯有差異挫掏,繼續(xù)向前定位侦另,如此反復(fù)即可找出所有遺漏的環(huán)境檢測,完成VM部分扣取。
此時(shí)我們就完成了這份靜態(tài)rs代碼的扣取并取得成功褒傅,但是rs網(wǎng)站的代碼是動態(tài)
的啊弃锐,每次請求時(shí) window.$_ts
和VM js
都會變化,難道我們每份都要去扣嗎殿托?不霹菊,其實(shí)我們現(xiàn)在離真正成功只差一個(gè)映射,
VM的萬行js代碼 雖然每次都會變化碌尔,但是變化的只是變量名浇辜,其他的都不變。
映射就是將動態(tài)window.$_ts
中的屬性名與 我們扣的VM 中JS用到的window.$_ts
中的固定屬性名一一對應(yīng)唾戚。
所以在扣的過程中我們需要注意VM中哪些地方使用了外部變量
柳洋,(即一個(gè)函數(shù)中,哪個(gè)變量來自其他作用域叹坦,就算外部變量)熊镣,我們需要關(guān)注這些外部變量是從哪來的,如果是VM自執(zhí)行函數(shù)中定義的募书,那就不用管绪囱,如果是來自window.$_ts
,則需要記錄下來莹捡,這就是需要映射傳入的鬼吵。
這里的計(jì)算邏輯使用到了
window.$_ts._$IK
,所以我們要做映射時(shí)要傳入這個(gè)值篮赢;window.$_ts={_$IK:對應(yīng)的動態(tài)屬性名}
解決完映射齿椅,也就成功扣代碼過rs了。
好了启泣,扣代碼到此為止涣脚,接下來就是我們本文的重點(diǎn)。
三寥茫、詳解補(bǔ)環(huán)境過rs
不知道補(bǔ)環(huán)境原理的同志可以參考我上篇文章:JS逆向之瀏覽器補(bǔ)環(huán)境詳解
;
其實(shí)純補(bǔ)環(huán)境
過瑞數(shù)原理很簡單遣蚀,我們來觀察瑞數(shù)執(zhí)行流程圖解,基于瀏覽器環(huán)境執(zhí)行這些動態(tài)JS可以生成可用的 cookie_t纱耻。那么只要我們補(bǔ)的瀏覽器環(huán)境足夠完美芭梯,使得在這些動態(tài)JS看來,我們補(bǔ)的環(huán)境===瀏覽器環(huán)境
弄喘,那么我們補(bǔ)的環(huán)境執(zhí)行這些動態(tài)JS粥帚,同樣也能生成可用的 cookie_t,然后我們再通過 document.cookie 將cookie_t 提取出來不就好了限次。
用偽代碼表示就是:
// 補(bǔ)的環(huán)境頭
window = this;
... 省略大量環(huán)境頭
// 模擬meta標(biāo)簽及其content
document.createElement('meta');
Meta$content = "{qYnKTJPAw84QfF5jm0I2_1IqhgTvRw8Y0yCBPxIVn6od8AeJE6CBz8ZSU6U...省略";
// 固定的外鏈js
$_ts=window['$_ts'];if(!$_ts)$_ts={};$_ts.scj=[];$_ts['dfe1675']='t??t?+...省略';
// page_url動態(tài)自執(zhí)行函數(shù)
(function(){var _$CK=0,_$WI=[[9,3,6,0,4,1; ...ret = _$su.call(_$fr, _$WR); 很長...省略}}}}}}}})();;
// 獲取cookie
function get_cookie(){
return document.cookie;
}
// 獲取MmEwMD參數(shù)
function get_mme(){{
XMLHttpRequest.prototype.open("GET","http://脫敏/",true);
return XMLHttpRequest.prototype.uri;
}}
這就是我們最后要補(bǔ)成的文件樣子芒涡,由于Meta$content 和page_url動態(tài)自執(zhí)行函數(shù)是動態(tài)變化的柴灯,因此我們每次請求page_url 都需要用正則提取出這兩個(gè)字符串,然后拼接到該文件中费尽,然后python pyexecjs調(diào)用get_cookie即可得到可用cookie_t;
在上面的扣代碼過瑞數(shù)中也提到了赠群,由于有隨機(jī)數(shù)和時(shí)間戳參與生成cookie_t 運(yùn)算,導(dǎo)致同一份靜態(tài)JS代碼
生成的cookie_t 是變化的旱幼,我們可以通過hook
使得時(shí)間戳和隨機(jī)數(shù)固定查描,這樣同一份靜態(tài)JS生成的cookie_t就是固定的。
// 固定定隨機(jī)數(shù)和時(shí)間戳
Date.prototype.getTime = function(){return 1672931693};
Math.random = function(){return 0.5};
我們也可以通過最終生成的cookie_t 來判斷柏卤,自己補(bǔ)的環(huán)境
是不是===瀏覽器環(huán)境
冬三。
原理很簡單,接下來就是如何實(shí)踐缘缚,我們需要補(bǔ)出一份完美的環(huán)境頭使得這份靜態(tài)JS
執(zhí)行得到的cookie_t與瀏覽器執(zhí)行得到的一致勾笆。
因?yàn)檠a(bǔ)環(huán)境是一個(gè)系統(tǒng)性工作,是有通用套路的桥滨,我們可以使用上篇文章提到的補(bǔ)環(huán)境框架
進(jìn)行系統(tǒng)性補(bǔ)環(huán)境窝爪,我們要做的就是根據(jù)log輸出
以及出現(xiàn)的問題不斷完善這個(gè)框架。
如圖齐媒,啟動框架蒲每,上面是我們補(bǔ)的環(huán)境頭,下面是我們扣的代碼喻括,
繼續(xù)調(diào)試邀杏,當(dāng)我們Proxy攔截器
攔截到BOM、DOM api
的使用時(shí)唬血,就會debugger住望蜡,我們可以根據(jù) 調(diào)用棧 去查看是哪行代碼使用的瀏覽器環(huán)境,然后再看到框架中模擬的結(jié)果是不是跟瀏覽器一致刁品,如果不一致,則需要補(bǔ)這個(gè)環(huán)境浩姥。
如此往復(fù)的補(bǔ)環(huán)境挑随,直到最后可以生成cookie_t
,判斷是否與瀏覽器本地生成的一致勒叠,如果不一致則使用二分法去定位兜挨,看是哪個(gè)瀏覽器環(huán)境沒補(bǔ)好,直到 最終得到正確的cookie_t
眯分,這里貼一下最后執(zhí)行效果圖:
這是最后打印的一部分環(huán)境檢測點(diǎn)
:
這是取出最終得到的cookie_t
:
同理拌汇,MmEwMD
參數(shù)的補(bǔ)環(huán)境也是一樣的邏輯,當(dāng)環(huán)境頭補(bǔ)到完美時(shí)弊决,在python中執(zhí)行最終結(jié)果文件噪舀,即可得到如下結(jié)果:
四魁淳、補(bǔ)環(huán)境與扣代碼總結(jié):
對于js逆向
來說,這是兩種常規(guī)且實(shí)用的手段与倡,也各有優(yōu)劣勢界逛;
不管使用哪種方式,我們都是先從網(wǎng)站中將加密JS代碼扣出纺座,然后再選擇是繼續(xù)扣代碼息拜,將使用到的瀏覽器環(huán)境api
進(jìn)行邏輯替換;還是使用補(bǔ)環(huán)境净响,讓加密JS代碼仿佛在瀏覽器環(huán)境中運(yùn)行少欺。
- 扣代碼與補(bǔ)環(huán)境都依賴對JS的熟練度,扣代碼更側(cè)重js語法和代碼邏輯馋贤,補(bǔ)環(huán)境更側(cè)重原型鏈及BOM赞别、DOM對象的模擬。
- 扣代碼熟練度依賴逆向經(jīng)驗(yàn)掸掸,補(bǔ)環(huán)境幾乎只依賴JS熟練度氯庆。
- 扣代碼需要調(diào)試跟蹤大量邏輯,對于rs扰付,如果不解混淆的話堤撵,屁股得坐出痔瘡;
- 扣代碼需要替換環(huán)境檢測邏輯羽莺,所以也需要知道哪里使用了瀏覽器環(huán)境实昨; 補(bǔ)環(huán)境框架可以只用于監(jiān)測瀏覽器環(huán)境的使用,可以當(dāng)做扣代碼的一個(gè)輔助工具盐固。
- 由于瑞數(shù)是動態(tài)的荒给,扣代碼只能扣一份靜態(tài)的,所以需要找到vm中使用到的所有動態(tài)屬性進(jìn)行映射刁卜。而補(bǔ)環(huán)境是通用的志电,補(bǔ)的越多,可通殺的網(wǎng)站就越多蛔趴。
- 扣代碼比補(bǔ)環(huán)境執(zhí)行效率高挑辆,畢竟補(bǔ)環(huán)境的代碼數(shù)比扣代碼多很多,可以通過剔除不需要的環(huán)境來縮小差距孝情;
- 扣代碼人工耗時(shí)遠(yuǎn)高于補(bǔ)環(huán)境鱼蝉。
總而言之,扣代碼
側(cè)重js語法和代碼邏輯箫荡,其熟練度依賴于逆向經(jīng)驗(yàn)魁亦,對不同網(wǎng)站要扣的不一樣,難以通用羔挡,人工效率低洁奈,但是程序執(zhí)行效率高间唉。
補(bǔ)環(huán)境
側(cè)重原型鏈及瀏覽器環(huán)境模擬,熟練度幾乎只依賴對JS的原理掌握程度睬魂,對于不同網(wǎng)站補(bǔ)的越多可通殺的網(wǎng)站越多终吼,人工效率巨高,但是程序執(zhí)行效率不高氯哮。
五际跪、彎道超車環(huán)節(jié)
過瑞數(shù)幾乎是每個(gè)新手逆向者的小目標(biāo),也是面試官常見的提問點(diǎn)喉钢。通過本篇文章姆打,我們已經(jīng)了解了 瑞數(shù)的流程及破解思路
,接下來我們可以嘗試自己從頭實(shí)現(xiàn)一個(gè)完善的補(bǔ)環(huán)境框架肠虽,去純環(huán)境黑盒過瑞數(shù)
幔戏,但是這會花費(fèi)很長一段時(shí)間來進(jìn)行開發(fā),而且其中有很多重復(fù)性工作比較無聊(復(fù)制粘貼對比等)税课。
走快車道:
本文該版本 補(bǔ)環(huán)境框架 是基于上篇文章框架進(jìn)行系統(tǒng)性完善的闲延,目前可以過瑞數(shù)這種級別網(wǎng)站,可以說是相當(dāng)完善了韩玩,補(bǔ)了相當(dāng)多的環(huán)境垒玲,如果你想省下大量時(shí)間、極大提高效率
找颓、直接彎道超車
的話合愈,可以 v我:dengshengfeng666 付費(fèi)源碼借鑒;
固定價(jià)199击狮,付完直接發(fā)框架項(xiàng)目源碼(有最新圖文readme可直接小白上手)佛析,后續(xù)有疑問可以直接問我。
該版本與上版本改進(jìn)點(diǎn):
- 優(yōu)化Proxy13種攔截器彪蓬,做到了Proxy的極限寸莫,并使其能遞歸代理,支持a.b.c.d.e...級別的深度檢測档冬;
- 完善最終結(jié)果文件.js的調(diào)用機(jī)制膘茎,使其無需改動,能直接用于V8和node調(diào)用捣郊;
- 新增部分BOM辽狈、DOM對象慈参,如:XMLHttpRequest呛牲,XMLHttpRequestEventTarget等等;
- 補(bǔ)全rs用到的所有瀏覽器環(huán)境方法驮配,使其可以黑盒過rs娘扩;
- 優(yōu)化調(diào)試方式着茸,可以在想斷的時(shí)候斷點(diǎn),不想斷的時(shí)候跳過檢測琐旁;
- 增加py直接調(diào)用結(jié)果文件案例涮阔,可以使用python以v8和node方式調(diào)用;
- 優(yōu)化readme灰殴,圖文介紹環(huán)境配置及使用方式敬特。
彎道超車,從我做起
該版本框架目錄: