天天看點

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

接上一篇有關前端加密達到反爬的文章,是不是覺得用了javascript-obfuscator 就很安全了,那還真不一定啊,還是那句,反爬與反反爬一直在鬥争,沒有誰能絕對的壓制另一方,隻有使用者技術的高低。以下就是一個大神的針對javascript-obfuscator庫的破解。

死代碼與花指令

在開始之前,我們先了解一下這種「在代碼中插入大量無用代碼以混淆視聽」的混淆方式吧。這種混淆方式有兩種叫法,或者說是兩種做法,它們分别是「死代碼」和「花指令」。

死代碼

死代碼一開始是被用來描述一些人寫代碼時寫出的沒有用到的代碼的,為了編譯後的檔案盡可能地小,編譯器通常會對死代碼進行移除處理。

而在不知道什麼時候開始,死代碼被安全工作者們用來作為一種混淆機制,以将代碼量變得極為龐大,使進行逆向工程的人難以找到主要邏輯。

但死代碼有個很明顯的特征:它雖然看着代碼量很大,但實際卻完全不會在程式的正常代碼中被調用。

如果你有興趣的話,可以對一些包含了死代碼的代碼進行聚類分析,你會發現死代碼和正常代碼之間泾渭分明,正常代碼都是互相關聯着的,而死代碼卻是孤零零的一塊或者多塊,并且正常代碼還完全不會與死代碼産生關聯。

花指令

花指令是以前被大量運用在木馬、病毒的免殺上的一種反反彙編手段,花指令中的“指令”通常指的是彙編中的 jmp、call 之類的調用、跳轉指令,而攻擊者們會将這些指令巧妙地插入到惡意代碼的執行邏輯中,使得靜态分析工具在分析到這個位置時無法正常反彙編。

花指令曾經的目的主要有兩個,一個是使防毒軟體無法自動分析出惡意代碼,達到瞞天過海的效果;一個是給安全工作者在分析惡意軟體時設下層層阻攔,使安全工作者需要花費更多的時間才能理清代碼邏輯,達到拖延時間的效果。

同樣是不知道什麼時候開始,花指令也被安全工作者們用來作為一種混淆機制。在這種應用場景下,花指令和死代碼其實很類似,它們都是用了大量無用代碼來混淆視聽,但花指令和死代碼最大的差別就是,花指令的無用代碼是會被混在正常代碼中進行執行的。相比于死代碼而言,花指令會造成一些性能損失,但同時也會讓進行逆向工程的人更加難以分析。

但花指令也不是無懈可擊的,為了不影響程式正常的執行,花指令不能幹擾到程式的原有邏輯,舉個例子:

a = 1

b = 2

# 花指令開始,對變量進行了一通操作

a += 1

a += b

# 花指令結束,又把變量的值給變回去了

a -= ba -= 1c(a, b)

是以其實隻要你能看出這一通操作沒有任何意義,花指令也自然就沒法影響到你了。

小結

不管是死代碼還是花指令,其實都隻需要我們仔細觀察就能将其剔除,它們并不是什麼很難搞的東西,見得多了之後你甚至都不需要細看就能快速排除掉一些明顯不是正常代碼的部分,畢竟常見的混淆器中用到的代碼其實重合度是很高的,同樣的套路見多了之後自然很容易分辨。

更何況,代碼混淆是需要考慮性能損耗的,對方不可能為了防你逆向工程而無止盡地對代碼進行混淆,要不然人家正常業務也沒辦法進行了。

實戰

基礎知識了解完了,我們來進入實戰環節。

首先,我們打開 https://obfuscator.io/,這是 Obfuscator 的網頁版本,可以快速在網頁上進行混淆參數的配置,并且一鍵生成并導出混淆後的代碼。

順帶一提,Obfuscator 是一款非常優秀的 JavaScript 代碼混淆工具,但代碼結構都是固定的,如果想要更好的混淆效果,可将混淆後的代碼進行修改,進而讓别人更難分析和調試。

現在,我們用它給出的樣例代碼來進行混淆。樣例代碼如下:

//Paste your JavaScript code here

functionhi() {

console.log("Hello World!");

}

hi();

注意,我們需要勾選以下選項:

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

這三個選項的效果分别是:

•Compact code将代碼中的換行符全部去掉,使得代碼看起來毫無結構性。也就是所謂的代碼壓縮。•Self Defending在代碼中插入自檢代碼,用來幹擾逆向工程的人對代碼進行格式化、變量重命名操作,如果代碼被格式化了就會無法正常運作。•Dead Code Injection在代碼中插入死代碼,也就是本文的重點。

配置好參數後點選 Obfuscate 按鈕,即可生成按配置混淆後的代碼,我生成的代碼是這樣的(長圖警告):

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

可以看到,原本短短的幾行代碼,在經過混淆後變成了這麼多。而且這個代碼還是經過壓縮的,完全看不出層級。

當然,這個代碼是可以正常運作的,我們用NodeJS跑一遍看看:

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

看起來混淆并沒有影響到正常的代碼邏輯,我們再把這一坨代碼給格式化一下看看:

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

果不其然,格式化後的代碼直接就沒法運作了。在平時我們遇到這種情況時要記住,原代碼可以正常運作但格式化之後不行,那麼這個報錯肯定是跟格式化代碼有關系的,至于它報錯的内容具體是啥意思其實并不重要。

那麼怎麼辦呢?我們來靜态分析一下它的代碼就知道了。

先來看看第一段代碼:

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

定義了一個數組并初始化,顯然不可能造成什麼問題。

接着看看第二段代碼(長圖警告):

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

這是一個自執行的函數,沒有傳回值。但是注意,它的第一個實參是 _0x2831,也就是之前定義的那個數組,對應的形參是 _0x528cba。我們可以根據這個來判斷它對 _0x2831 做了些什麼。

現在我們來一段一段地分析這第二大段代碼中的每一段代碼,首先是第一段代碼:

var _0x1b0e99 = function(_0x5beb46) {while (--_0x5beb46) {

_0x528cba['push'](_0x528cba['shift']());

}

};

這麼短的代碼相信大家都應該能看懂,是對 _0x528cba 進行 shift 操作,而 _0x528cba 是自執行函數的形參,實參是 _0x2831。換句話說,它就是對實參進行 shift 操作。不過這裡它隻是聲明,并沒有調用,是以還不會去改變實參。

然後是第二段代碼和第三段代碼,這裡因為代碼量太大就不整個貼出來了,之前已經貼過完整代碼了。

第二段代碼是定義了一個函數,而第三段代碼則是調用這個函數,是以我們主要分析這第二段代碼即可。

如果你不會分析,可以跳過它聲明的語句,它真正開始執行的是這行代碼:

var _0x53c9b6 = _0x1d1bc5['updateCookie']();

不要看它這段代碼裡面既有 setCookie,又有 getCookie,其實它跟 cookie 沒有半毛錢關系,它隻是一個 object 的 key 值,僅此而已。

是以,我們隻需要關注它有沒有改變實參,有沒有改變全局變量。整個代碼全局變量隻有一個 _0x2831,它也是實參,也就是說隻需要關心這個 _0x2831 即可。

通過上面的分析我們可以知道,它的第一段代碼定義了一個函數,确實改變了實參,但是沒有調用。是以,我們得找找看它在哪裡被調用的,直接搜函數名 _0x1b0e99,定位到這裡:

_0x4c51d1(_0x1b0e99, _0x283138);

這時,_0x1b0e99是第一個實參,第二個實參 _0x283138 則是自執行函數的形參,它對應的實參是 0x1bf,是一個整形的數值。這下,我們隻需要看看 _0x4c51d1 的函數聲明即可:

var _0x4c51d1 = function(_0x3d5743, _0x3c21e0) {

_0x3d5743(++_0x3c21e0);

};

這麼大一段代碼,其實真正改變實參的隻有這裡。我們将這兩行代碼結合一下,就會變成這樣:

_0x1b0e99(++_0x283138);

是以,第二大段代碼這個自執行函數中的第二段代碼隻有這一句是真正改變實參的地方,其他的全部是垃圾代碼,直接删除即可。删除後,這個自執行函數就變成了這樣:

(function(_0x528cba, _0x283138) {var _0x1b0e99 = function(_0x5beb46) {while (--_0x5beb46) {

_0x528cba['push'](_0x528cba['shift']());

}

};

_0x1b0e99(++_0x283138);

}(_0x2831,0x1bf));

這樣看起來就清爽多了,運作試試看:

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

還是報同樣的錯誤,接着往下分析第三段代碼(長圖警告):

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

這是一個函數,可以看到,引用全局變量 _0x2831 的隻有這一行:

var _0x1b0e99 = _0x2831[_0x528cba];

這是一個指派語句,但是不會改變 _0x2831 這個變量,是以我們隻需要重點關注它的傳回值 _0x1b0e99 就好。

再來看看它最後指派的地方,有兩處,一處在 if 語句裡面:

_0x1b0e99 = _0x1b0e['SmClCt'](_0x1b0e99, _0x283138);

另外一處在 else 語句裡面:

_0x1b0e99 = _0x309846;

那它到底是執行的那行代碼呢,來看看 if 語句的條件:

_0x309846 === undefined

繼續分析上面的代碼:

var _0x309846 = _0x1b0e['jZzRvK'][_0x528cba];

以及 _0x1b0e['jZzRvK'] 最近的定義的地方:

_0x1b0e['jZzRvK'] = {};

這樣就清楚了,_0x309846 === undefined 這個條件是成立的,是以 _0x1b0e99 最後指派的地方是這裡:

_0x1b0e99 = _0x1b0e['SmClCt'](_0x1b0e99, _0x283138);

函數 _0x1b0e['SmClCt'] 指派在這裡:

_0x1b0e['SmClCt'] = _0x5beb46;

而實參,則是在 _0x5beb46 函數之前定義過,是以隻需要這兩行代碼提到 _0x5beb46 函數之後即可。注意,這裡為了清楚一點,可以這樣操作:

if (_0x1b0e['DVdkAf'] ===undefined) {

............................

_0x1b0e['SmClCt'] =_0x5beb46;

_0x1b0e['jZzRvK'] ={};

_0x1b0e['DVdkAf'] = !![];

}

_0x1b0e99= _0x1b0e['SmClCt'](_0x1b0e99, _0x283138);return _0x1b0e99;

根據上面的思路繼續分析 hi 函數,同樣也注入了一些不會改變全局變量 _0x2831 的垃圾代碼,真正有效的代碼隻有這一行:

console[_0x1b0e('0xc', '^G6o')](_0x1b0e('0xd', 'Bi36'));

删除掉這些垃圾代碼後,我們就可以知道這份代碼原來長什麼樣了。

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

BOOM!結果就是這麼三行代碼:

functionhi() {

console[_0x1b0e('0xc', '^G6o')](_0x1b0e('0xd', 'Bi36'));

}

最後我們再運作一下試試看吧:

python爬蟲處理js混淆加密_python爬蟲之破解javascript-obfuscator的混淆加密

沒有報錯,代碼成功運作了~

總結

碰到大段代碼時不要慌,先試着分析一下,把無用代碼剔除掉之後其實最後剩下的可能就隻有幾行而已。當然實際情況中你往往會碰到混淆參數更複雜的代碼,你需要讓程式來幫助你進行分析,而想要寫出這種程式又會使用到 AST 操作,是以說掌握 AST 操作還是很有必要的,建議學習一下。

以上為轉載的内容全文,怎麼樣,一樣可以解出來,傻眼了吧,但是,也不是說javascript-obfuscator就完全沒用了,當然有用,必須有用,隻是看怎麼用,你想一下,如果讓這個混淆加密定期換一套呢?我換的速度難道還能比你解密的速度慢嗎?然後就算你能解密出來,我再加幾道關卡呢?讓你解的費勁,惡心你,擊垮你的信心,等等的,是以用處是很大的。隻是看你怎麼用了