一文詳解常見的JavaScript混淆類型
一、變量/函數(shù)名混淆
原理:將有意義的變量名替換為無意義的短名(如 _0xabc12, a, b1),讓代碼失去可讀性。
示例
function _0x1f2a(_0x1234) {
console['log'](_0x1234);
}
_0x1f2a('你好');
原始邏輯是:
function sayHello(name) {
console.log(name);
}
sayHello('你好');
目的:隱藏函數(shù)功能,增加逆向難度。
解決方法
使用
js-beautify美化代碼使用 Babel AST 或正則替換還原變量名
人工或自動語義命名(如 第一層, 參數(shù)1, 輸出)
二、字符串混淆
原理:將字符串轉(zhuǎn)為 Hex、Unicode、Base64 等格式,運(yùn)行時再解密。
示例
var a = '\x63\x6f\x6e\x73\x6f\x6c\x65';
var b = a + '\x2e\x6c\x6f\x67';
eval(b + '("hello")');
等價于:
console.log("hello");
目的:隱藏字符串內(nèi)容,如 API、函數(shù)名、密鑰。
解決方法
用
console.log打印eval參數(shù)寫腳本解碼 hex/base64/unicode
Babel AST 靜態(tài)替換解碼函數(shù)
三、控制流扁平化
原理:將原本順序執(zhí)行的代碼拆成塊,用 switch/while/jump 執(zhí)行,打亂執(zhí)行順序。
示例
(function(){
var _idx = 0;
var _arr = ['a', 'b', 'c'];
while(true){
switch(_arr[_idx++]) {
case 'a': console.log("1"); break;
case 'b': console.log("2"); break;
case 'c': return;
}
}
})();
目的:打亂邏輯結(jié)構(gòu),阻止靜態(tài)分析。
解決方法
靜態(tài)分析 switch case 執(zhí)行順序
用 Babel 拆成順序邏輯
人工逐步調(diào)試每一步
四、數(shù)組映射字符串
原理:把關(guān)鍵字符串存在數(shù)組中,用數(shù)組下標(biāo)訪問調(diào)用。
示例
var arr = ['log', 'hello']; console[arr[0]](arr[1]);
等價于:
console.log("hello");
目的:隱藏調(diào)用結(jié)構(gòu)。
解決方法
使用腳本將數(shù)組反查原值并替換所有引用
Babel traverse 實(shí)現(xiàn)自動替換
五、多層 eval
原理:用多層嵌套的 eval 執(zhí)行,防止靜態(tài)閱讀和調(diào)試。
示例
eval(eval("String.fromCharCode(99,111,110,115,111,108,101)+'.log(\"hi\")'"));
最終等價于:
console.log("hi");
解決方法
Hook
eval函數(shù)打印執(zhí)行參數(shù)用
console.log替代eval查看內(nèi)部結(jié)構(gòu)把 eval 參數(shù)提取出來還原
六、new Function 動態(tài)執(zhí)行
原理:用 new Function() 構(gòu)造運(yùn)行時代碼。
示例
var fn = new Function('console.log("hi")');
fn();
目的:等價于 eval,隱藏邏輯。
解決方法
替換為
console.log輸出代碼內(nèi)容在瀏覽器中注入 Hook Function 構(gòu)造器
七、動態(tài)網(wǎng)絡(luò)加載代碼
原理:運(yùn)行時從網(wǎng)絡(luò)下載 JS 腳本,使用 eval 或 script 標(biāo)簽執(zhí)行。
示例
fetch('/api/js').then(r => r.text()).then(eval);
目的:繞過靜態(tài)分析,延遲投毒。
解決方法
抓包獲取真實(shí)腳本內(nèi)容
屏蔽動態(tài)加載,分析本地腳本
Hook fetch / xhr 返回體
八、JS VM 虛擬機(jī)混淆
原理:把 JS 編譯成字節(jié)碼,運(yùn)行一個自定義解釋器執(zhí)行。
示例
var code = [0x12, 0x34, 0x78]; MyVM.run(code);
目的:完全隱藏真實(shí)邏輯,甚至加殼。
解決方法
分析 VM 實(shí)現(xiàn),還原 opcode 與行為映射
手動構(gòu)建 opcode -> JS 代碼的轉(zhuǎn)換腳本
跟蹤運(yùn)行行為,dump 中間變量
九、JSFuck 編碼混淆
原理:用僅 [ ] ( ) + ! 六個字符實(shí)現(xiàn) JS 代碼。
示例
[][(![]+[])[+[]]+([][[]]+[])[+!+[]]+...][([]+[])[+[]]+...][([]+[])[+[]]+...]('alert(1)')();
解決方法
使用 https://enkhee-osiris.github.io/Decoder-JSFuck/ 解碼
手寫 JSFuck 解碼器或 AST 解析器還原
十、反調(diào)試/反控制臺
原理:檢測是否打開了 devtools 調(diào)試器,阻止控制臺輸出。
示例
setInterval(function(){
if(window.outerHeight - window.innerHeight > 100) {
alert("調(diào)試器檢測");
}
}, 1000);
目的:防止調(diào)試器斷點(diǎn)分析。
解決方法
注釋相關(guān)檢測代碼
重寫相關(guān)函數(shù)如
console.log,window.outerHeight使用無頭瀏覽器配合 bypass 技術(shù)
十一、實(shí)戰(zhàn)舉例
11.1 混淆代碼
(function (_0x3f12d1, _0x1b23f4) {
var _0xabc123 = function (_0x5a72c1) {
while (--_0x5a72c1) {
_0x3f12d1['push'](_0x3f12d1['shift']());
}
};
_0xabc123(++_0x1b23f4);
})(['log', 'Hello\x20World'], 0x1);
var _0x55ee = function (_0x1d3f84, _0x2f7e3b) {
_0x1d3f84 = _0x1d3f84 - 0x0;
var _0x4328cb = ['log', 'Hello World'];
return _0x4328cb[_0x1d3f84];
};
console[_0x55ee(0x0)](_0x55ee(0x1));
11.2 初步分析
混淆點(diǎn):
立即執(zhí)行函數(shù) IIFE 中對數(shù)組進(jìn)行了 shift/rotate 操作
_0x55ee是一個偏移映射函數(shù)(類數(shù)組解碼)控制臺調(diào)用變成了動態(tài)索引訪問
目標(biāo)是還原出最原始的代碼。
11.3 逐步還原
步驟 1:恢復(fù) IIFE 之后的數(shù)組內(nèi)容
(function (_0x3f12d1, _0x1b23f4) {
var _0xabc123 = function (_0x5a72c1) {
while (--_0x5a72c1) {
//arr.shift():移除并返回?cái)?shù)組開頭的元素。
//arr.push(value):將一個元素添加到數(shù)組末尾。
_0x3f12d1['push'](_0x3f12d1['shift']());
}
};
_0xabc123(++_0x1b23f4);
})(['log', 'Hello World'], 0x1);
執(zhí)行邏輯等價于:
let arr = ['log', 'Hello World']; arr.push(arr.shift()); // 'log' 被移到最后 // => ['Hello World', 'log']
所以 _0x55ee 實(shí)際訪問的是:
['Hello World', 'log']
但是注意:混淆里寫的是 ['log', 'Hello World'],所以 IIFE 執(zhí)行后變成 ['Hello World', 'log']。
步驟 2:解讀 _0x55ee 作用
var _0x55ee = function (_0x1d3f84, _0x2f7e3b) {
_0x1d3f84 = _0x1d3f84 - 0x0;
var _0x4328cb = ['log', 'Hello World'];
return _0x4328cb[_0x1d3f84];
};
但注意:這個數(shù)組 ['log', 'Hello World'] 是原始的,沒有做 shift。
所以這里 _0x55ee(0x0) 實(shí)際返回的是 'log',_0x55ee(0x1) 返回 'Hello World'。
步驟 3:替換 console 調(diào)用部分
console[_0x55ee(0x0)](_0x55ee(0x1));
等價于:
console['log']('Hello World');
11.4 還原代碼
完全還原結(jié)果如下:
console.log('Hello World');
11.5 總結(jié)分析
| 混淆點(diǎn) | 意圖 | 解決方式 |
|---|---|---|
| 數(shù)組移位 | 打亂數(shù)組索引邏輯 | 運(yùn)行一次 IIFE 獲取結(jié)果 |
索引函數(shù) _0x55ee | 模擬字符串映射 | 提前計(jì)算函數(shù)返回值并替換 |
動態(tài)調(diào)用 console[...] | 避免搜索關(guān)鍵字 | 靜態(tài)替換成 console.log |
總結(jié)
到此這篇關(guān)于JavaScript混淆類型的文章就介紹到這了,更多相關(guān)JS混淆類型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript顯示當(dāng)前文檔最后修改日期的方法
這篇文章主要介紹了JavaScript顯示當(dāng)前文檔最后修改日期的方法,實(shí)例分析了javascript中document.lastModified的使用技巧,非常具有實(shí)用價值,需要的朋友可以參考下2015-03-03
JavaScript 異步調(diào)用框架 (Part 3 - 代碼實(shí)現(xiàn))
在上一篇文章里,我們說到了要實(shí)現(xiàn)一個Async.Operation類,通過addCallback方法傳遞回調(diào)函數(shù),并且通過yield方法返回回調(diào)結(jié)果。現(xiàn)在我們就來實(shí)現(xiàn)這個類吧。2009-08-08
java前端javascript生成動態(tài)表格示例演示
這篇文章主要為大家介紹了java前端javascript生成動態(tài)表格的實(shí)現(xiàn)示例及演示,2022-03-03
js根據(jù)手機(jī)客戶端瀏覽器類型,判斷跳轉(zhuǎn)官網(wǎng)/手機(jī)網(wǎng)站多個實(shí)例代碼
這篇文章主要介紹了js根據(jù)手機(jī)客戶端瀏覽器類型,判斷跳轉(zhuǎn)官網(wǎng)/手機(jī)網(wǎng)站多個實(shí)例代碼,需要的朋友可以參考下2016-04-04
js實(shí)現(xiàn)敏感詞過濾算法及實(shí)現(xiàn)邏輯
這篇文章主要介紹了js實(shí)現(xiàn)敏感詞過濾算法及實(shí)現(xiàn)邏輯,文中介紹了dfa算法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-07-07

