JS逆向: 解决调试chrome extension时, 遇到的无限递归debugger反调试
反调试是一种重要的软件保护技术,特别是在各种游戏保护中被尤其重视。 另外,恶意代码往往也会利用反调试来对抗安全分析。 当程序意识到自己可能处于调试中的时候,可能会改变正常的执行路径或者修改自身程序让自己崩溃,从而增加调试时间和复杂度。
在Windows和Android平台下, 反调试随处可见, 它的存在有效的增加了软件逆向的成本, 遇到反调试, 大部分情况下需要见招拆招, 下面将介绍笔者遇到的第一个js反调试手段, 为了能理解其原理和灵活的拆招方式, 笔者将举两个demo案例来进行说明, 并在结尾附上实战案例.
案例1
新建一个html文件, 写入如下代码
<html>
<head>
<script>
setInterval(function () {
debugger;
}, 500);
</script>
</head>
<body></body>
</html>
用浏览器打开这个文件后, 可以正常运行, 但按下F12(打开开发者工具)后,程序就会断到debugger
这行代码, 直接影响到我们进行调试分析.
你有张良计, 我有过墙梯, 遇到这种情况, 有如下几种方案:
-
点击
源代码
->文件系统
->向工作区添加文件夹
, 随便选择一个文件夹, 用于存储当前网页资源的副本, 之后浏览器会弹出一个选项, 点击允许
.然后我们切换到
源代码
->替换
, 勾选启用本地替换
, 找到网页资源的副本, 这时网站代码便可随意修改, 下图中我将debugger
指令去掉了, 然后F5刷新, 更改便生效了
-
由于该案例中代码比较简单, 实际上有另一种更加简单方式来绕过这个反调试, 如图所示, 直接忽略掉这个debugger指令 , 就能绕过该反调试了
案例2
<html>
<head>
<script>
var count = 0;
function checkDebugger() {
if(count>=20)
{
count=0;
return;
}
count++;
debugger;
checkDebugger();
}
setInterval(function(){
checkDebugger();
},5000);
</script>
</head>
<body></body>
</html>
这个案例会递归调用, 使用案例1中的方法2就不起作用了, 甚至有的程序会检测代码完整性, 使得案例1中的方法1也失效(点名chrome extension Line), 下面将介绍另一种办法来pass这个反调试.
观察代码, 不难发现, 我们只需要把这里的定时器给去掉, 就可以达成目的. 在控制台输入以下代码
// 先保存原来的setInterval函数指针
x = setInterval
// 将新函数的地址赋值给旧的setInterval指针
// 新函数里判断setInterval的第二个参数:间隔时间, 如果是5000,则啥也不干, 否则放行
setInterval = function(a,b){if(b!=5000){x();}}
这个方法可以使上例中的定时器失效, 且不影响其他定时器的逻辑, 不过仍然有些瑕疵, 更好的办法是,使checkDebugger
失效, 为此, 我们可以在控制台输入以下代码
checkDebugger=function(){}
实战
如下是chrome extension store的某个扩展程序, 打开开发者面板后, 立马断到了debuuger处, 无论如何单步, 执行, 永远出不去, 程序一直呈卡死状态. 且使用案例1的方案1来修改代码, 会导致程序直接崩溃, 目前尚未找到检测代码完整性的地方( 这里就不得不吐槽chrome dev tools了, 如果是其他调试器, 只需对代码下内存断点, 即可找到扫描检测代码完整性的地方)
这段程序大概长这样
...............
function b(a) {
var c = {
'DZPan': function(e, f) {
return e === f;
},
'HPymq': 'strin' + 'g',
'keOAk': function(e) {
return e();
},
'gOBNe': function(e, f) {
return e !== f;
},
'ohRmf': function(e, f) {
return e + f;
},
'hgrur': function(e, f) {
return e / f;
},
'Cmstj': 'lengt' + 'h',
'qZWsJ': function(e, f) {
return e % f;
},
'bNwsW': function(e, f) {
return e(f);
},
'JUpev': function(e, f) {
return e(f);
}
};
function d(e) {
if (c['DZPan'](typeof e, c['HPymq'])) {
var f = function() {
while (!![]) {}
};
return c['keOAk'](f);
} else {
if (c['gOBNe'](c['ohRmf']('', c['hgrur'](e, e))[c['Cmstj']], -0xa89 + -0x1a0d * -0x1 + -0xb * 0x169) || c['DZPan'](c['qZWsJ'](e, 0x11 * -0x134 + 0x1 * -0x1db3 + 0x323b), -0x17f8 + 0xabd + 0x469 * 0x3))
debugger ;
else
debugger ;
}
c['bNwsW'](d, ++e);
}
try {
if (a)
return d;
else
c['JUpev'](d, -0x4fa * -0x1 + 0x1f * -0xe2 + 0x1664);
} catch (e) {}
}
其中最外层的b()函数是一个全局范围的函数, c是它的局部变量, 反调试代码主要是在d()函数里, c['bNwsW']传递进去的第一个参数就是d本身, 这样一来, 就实现了无限递归debugger, 直到stack overflow.
对于此, 我们有多种思路,如将c['bNwsW']置程空函数, 阻断递归调用. 不过需要注意的是, c是b的局部变量, 想要更改c, 则当前栈必须在b函数的范围内.
c['bNwsW'] = function(){}
改完之后, 再跑一次, 发现c['bNwsW']又被改回来了, 原来每次进入b函数, 都会给c重新赋值. 既然这样, 那我们就把'bNwsW': function(e, f)
'JUpev': function(e, f)
两个函数给删掉吧, 复制原本的b函数, 在控制台输入以下代码:
b= function(a) {
var c = {
'DZPan': function(e, f) {
return e === f;
},
'HPymq': 'strin' + 'g',
'keOAk': function(e) {
return e();
},
'gOBNe': function(e, f) {
return e !== f;
},
'ohRmf': function(e, f) {
return e + f;
},
'hgrur': function(e, f) {
return e / f;
},
'Cmstj': 'lengt' + 'h',
'qZWsJ': function(e, f) {
return e % f;
},
'bNwsW': function(e, f) {
/********************/
/* 这里删掉原本的实现 */
/********************/
},
'JUpev': function(e, f) {
/********************/
/* 这里删掉原本的实现 */
/********************/
}
};
function d(e) {
if (c['DZPan'](typeof e, c['HPymq'])) {
var f = function() {
while (!![]) {}
};
return c['keOAk'](f);
} else {
if (c['gOBNe'](c['ohRmf']('', c['hgrur'](e, e))[c['Cmstj']], -0xa89 + -0x1a0d * -0x1 + -0xb * 0x169) || c['DZPan'](c['qZWsJ'](e, 0x11 * -0x134 + 0x1 * -0x1db3 + 0x323b), -0x17f8 + 0xabd + 0x469 * 0x3))
debugger ;
else
debugger ;
}
c['bNwsW'](d, ++e);
}
try {
if (a)
return d;
else
c['JUpev'](d, -0x4fa * -0x1 + 0x1f * -0xe2 + 0x1664);
} catch (e) {}
}
大功告成!
经过更深入的调试, 发现整个b函数就没啥实际作用, 完全是用来反调试的, 这样我们就可以直接将b函数置空, 而不用担心会影响到正常的功能.
b=function(){}
c['JUpev']=function(){}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步