1. 種類劃分
1.1. 按代碼邏輯劃分
1.1. 無限循環(huán)
- for 循環(huán)
- while 循環(huán)
注意:使用 while 循環(huán)一定會有次數(shù)上限顿痪,否則瀏覽器會卡死掉访敌。
1.2. 無限遞歸
- 調(diào)用自身
1.3. 兩個方法循環(huán)互調(diào)
- 顧名思義
1.4. 計時器
setInterval
這個是 JS 語言當(dāng)中的 定時器
吵血,它有兩個參數(shù):第一個是要執(zhí)行的代碼,第二個執(zhí)行時間先朦。
下方是 sojson 混淆后的一段代碼次洼,其中使用了 setInterval
定時器進(jìn)行了反 debugger 操作。
這個一段代碼執(zhí)行的話會一直斷住辛萍,不管你怎么跳轉(zhuǎn)到下一個斷點凡人,它都還是會一直斷,其實這個就是一個無限debugger的代碼叹阔。
1.2. 按是否可混淆劃分
1.2.1. 不可混淆
直接使用代碼挠轴,
debugger;
1.2.2. 可混淆
eval("debugger;")
1.2.3. 可重度混淆
Function("debugger").call()/apply() 或賦值 bind()
xxx.constructor("debugger").call("action")
Fuction.constructor("debugger").call("action")
(function(){return !![];}["constructor"]("debugger")["call"]("action"))
1.3. 按設(shè)置節(jié)點劃分
- 無限 debugger 貫穿全局
這種情況,多是由setInterval
定時器實現(xiàn)的耳幢。針對這種情況岸晦,我們需要將定時器函數(shù)置空或者重寫欧啤。 - 在加密業(yè)務(wù)邏輯之前設(shè)置
- 在加密業(yè)務(wù)邏輯之后設(shè)置
2. 應(yīng)對方法
2.1. 針對靜態(tài)文件
2.1.1. conditional breakpoint
在 JS 代碼 debugger 行數(shù)位置,鼠標(biāo)右鍵添加 conditional breakpoint启上,其中條件 condition 設(shè)為 false邢隧;
2.1.2. Fiddler AutoResponder 篡改 JS 代碼
這種方式的核心思路,是替換 JS 文件中的 debugger 關(guān)鍵字冈在,并保存為本地文件倒慧,在請求返回的時候、通過正則匹配等方式包券、攔截并替換返回的 JS 代碼纫谅,已達(dá)到繞過 debugger 的目的。
關(guān)于這種方法溅固,請參考我的另一篇文章:《JS逆向:fiddler 篡改 js 破解企查查無限 debugger 問題》付秕,文中有詳細(xì)的描述。
2.2. 針對動態(tài)文件
2.2.1. 手動在瀏覽器中 Hook
第一步侍郭、打 script 斷點
這一步的目的询吴,是為了讓瀏覽器在剛運行時就被斷下來,以方便進(jìn)行 Hook
第二步亮元、Hook 無限 debugger 函數(shù)
- 手動置空 debugger 函數(shù)猛计。
打script
斷點,使網(wǎng)頁在 debugger 之前下斷爆捞,手動將debugger所在函數(shù)置空有滑。這種方法簡單粗暴,但是會影響原有的業(yè)務(wù)邏輯嵌削。
包含debugger函數(shù) = function (){};
- 手動重寫 debugger 函數(shù)毛好。
打script
斷點,然后在 debugger 函數(shù)被調(diào)用的地方下斷苛秕,手動將debugger所在函數(shù)中的 debugger 邏輯刪除肌访,然后 把 debugger 函數(shù)賦值給 調(diào)用函數(shù)。
注意:這一步賦值不能省艇劫,否則無法調(diào)用吼驶。
- Hook Funciton
F_ = Function
Function = function(a){
if (a!=='debugger'){return F_(a)}
}
- Hook Function 構(gòu)造器函數(shù)
Function.prototype.constructor_ = Function.prototype.constructor;
Function.prototype.constructor = function(x){
if (x!=='debugger'){return Function.prototype.constructor_(x)}
}
如果覺得上面的代碼太粗暴,也可使用下方代碼店煞。
Function.prototype.__constructor_back = Function.prototype.constructor;
Function.prototype.constructor = function() {
if(arguments && typeof arguments[0]==='string'){
//alert("new function: "+ arguments[0]);
if("debugger" === arguments[0]){
//arguments[0]="console.log(\"anti debugger\");";
//arguments[0]=";";
return
}
}
return Function.prototype.__constructor_back.apply(this,arguments);
}
- Hook eval 函數(shù)
eval_ = eval;
//下面這樣寫蟹演,是為了過瑞數(shù)的 eval.toString 檢測
eval = function(a){if(a=='debugger'){return ''}else{return eval_(a)}}
- Hook conole.log,為了防止調(diào)試過程中顷蟀,console.log 被重寫酒请,也可以在此時對 console.log 進(jìn)行 hook,之后在 console.log 無法正常打印的位置鸣个,再將 console.log 進(jìn)行復(fù)原羞反;
Hook console.log:
console.log_ = console.log
將 console.log 復(fù)原:
console.log = console.log_
- Hook setInterval 函數(shù)
-- 業(yè)務(wù)代碼和 setInterval 無關(guān)
setInterval = function(){}
-- 業(yè)務(wù)代碼和 setInterval 有關(guān)
setInterval_back = setInterval
setInterval = function(a,b){
if(a.toString().indexOf('debugger') == -1){
return null;
}
setInterval_back(a, b)
}
2.2.2. Fiddler + 編程貓插件 + Hook
使用這種方法布朦,就不需要再打 script
斷點。
注意:如果
script
斷點無法在 debugger 函數(shù)之前斷下來昼窗,那就只能用這種方式進(jìn)行攔截 Hook是趴,才能過掉 debugger。
使用編程貓插件用到的代碼澄惊,和在瀏覽器中手動 Hook 基本一致唆途。在 Hook eval
過瑞數(shù) debugger 的時候,可以用到下面的代碼掸驱。
//配合編程貓專用工具進(jìn)行hook
(function() {
'use strict';
//過 瑞數(shù) debuger
var eval_ = window.eval;
window.eval = function(x){
eval_(x.replace("debugger;"," ; "));
};
//過 瑞數(shù) debuger檢測
window.eval.toString = eval_.toString;
})();