JS调试与反调试
基础知识
浏览器调试检测
-
开发者工具栏检测
/* * 通过内外高宽判断是否开启开发者工具 * 缺点:本身就容易造成误判,因此一般服务方不采用该方法 * */ var isDevToolsOpen = false; function detectDevTools(interval_id) { const threshold = 600; const width = window.outerWidth - window.innerWidth; const height = window.outerHeight - window.innerHeight; if (width > threshold || height > threshold){ // 被调试,执行反制手段 isDevToolsOpen = true; // 反制函数 alert("you are captured !!!"); // 拆除interval clearInterval(intervalDetectId) } } // 不间断检测是否开启调试工具 var intervalDetectId = setInterval( detectDevTools,1000, )
破解方法:js文件劫持、将调试窗口与主窗口分离
-
通过代码执行时间
/* * 通过代码执行时间来判断是否被调试,特殊代码debugger在调试状态下会暂停程序运行 * 缺点: * */ var isDevToolsOpen = false; function detectDevTools() { var startTime = Date.now(); for(let i=0;i<3;i++){ eval('debugger'); } var endTime = Date.now(); console.log(endTime-startTime) var isDev = endTime-startTime> 100; if (isDev){ // 被调试,执行反制手段 isDevToolsOpen = true; // 反制函数 alert("you are captured !!!"); // 拆除interval clearInterval(intervalDetectId) } } // 不间断检测是否开启调试工具 var intervalDetectId = setInterval( detectDevTools,1000, )
破解方法:js文件劫持、hook关键函数
- 补充:此外还有一些其他放大,但可能并不适用,目前暂没发现其他有效的方法。
Debugger专区
/*
既可以作为检测的一部分,也可以直接干扰调试者。
debugger只在调试状态下有效,因此可以基于debugger干扰分析者。
例如:无限debugger(函数自调、定时任务、循环定时任务等)
*/
- 无限debuuger
/*
* 无限debugger
*
* */
function debuggers() {
eval("debugger");
}
setInterval(debuggers,1000);
解决方案:文件劫持、hook函数
- 混淆技术
/*
更多的是使用混淆技术执行debugger,来增加调试者的难度
较为详细的博客有:https://blog.csdn.net/xw1680/article/details/138547184
*/
/*
调用时混淆
1)基于Function.constructor,直接Function或某一个类型向上找原型为Function。
Function.constructor("debugger").call()
2)基于Object.constructor
Object.constructor("debugger").call()
debugger字符串混淆
1)拆解:eval("debugg"+"er")
整个代码混淆
在线网站等,如博客所提到:https://blog.csdn.net/qq_15326513/article/details/131741451
*/
- console.clear
/*
console.clear()会清除控制台的调试信息,因此无限执行此函数能增加调试者的调试难度。多与setInterval一起
*/
function debuggers() {
console.clear()
}
setInterval(debuggers,1000);
常用的反反调试
由于准确地检测调试存在困难,因此现在大多基于debugger来增加调试难度,故反反调试也多关注debugger
使用hook函数和文件劫持是最有效地方式。
-
hook函数
/* 通过hook函数的方式,能有效解决大多问题,例如hook “eval”函数 hook的主要步骤:1、保留原函数;2、重写函数;3、非目标调用原函数 优点:打击面积小,针对性强 */ var eval_src = eval; eval = function(args_str){ if(args_str.toLowerCase().indexOf('debugger')!=-1){ return; }else{ eval_src(args_str); } }
-
文件劫持
使用浏览器工具或其他代理工具进行劫持替换
-
条件断点等
测试实例
网站:https://www.aqistudy.cn/
尝试祛除反调试
1)针对index页面的调试检测
/*
<script type="text/javascript">
var debugflag = false;
endebug(false, function () {
document.write('检测到非法调试, 请关闭调试终端后刷新本页面重试!');
document.write("<br/>");
document.write("Welcome for People, Not Welcome for Machine!");
debugflag = true;
});
//txsdefwsw();
document.onkeydown = function() {
if ((e.ctrlKey) && (e.keyCode == 83)) {
alert("检测到非法调试,CTRL + S被管理员禁用");
return false;
}
}
document.onkeydown = function() {
var e = window.event || arguments[0];
if (e.keyCode == 123) {
alert("检测到非法调试,F12被管理员禁用");
return false;
}
}
document.oncontextmenu = function() {
alert('检测到非法调试,右键被管理员禁用');
return false;
}
$(function()
{
if (!debugflag && !window.navigator.webdriver) {
loadTab();
}
if(!isSupportCanvas())
{
$("#browertip").show();
}
});
function isSupportCanvas()
{
var elem = document.createElement('canvas');
return !!(elem.getContext && elem.getContext('2d'));
}
</script>
*/
解决方案:文件劫持,删除检测代码
2)针对关键eval执行的混淆代码:eval(d2EFOt36ua(`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`));
1、进入查看js代码:
/*
var debugflag = false;
document.onkeydown = function() {
if ((e.ctrlKey) && (e.keyCode == 83)) {
alert("检测到非法调试,CTRL + S被管理员禁用");
return false;
}
}
document.onkeydown = function() {
var e = window.event || arguments[0];
if (e.keyCode == 123) {
alert("检测到非法调试,F12被管理员禁用");
return false;
}
}
document.oncontextmenu = function() {
alert('检测到非法调试,右键被管理员禁用');
return false;
}
!function () {
const handler = setInterval(() => {
if (window.outerWidth - window.innerWidth > 300 ||
window.outerHeight - window.innerHeight > 300) {
// document.write((window.outerWidth - window.innerWidth) + ',' + (window.outerHeight - window.innerHeight));
document.write('检测到非法调试, 请关闭调试终端后刷新本页面重试!<br/>');
document.write("Welcome for People, Not Welcome for Machine!<br/>");
debugflag = true;
}
const before = new Date();
(function() {}
["constructor"]("debugger")())
const after = new Date();
const cost = after.getTime() - before.getTime();
if (cost > 50) {
debugflag = true;
document.write('检测到非法调试, 请关闭调试终端后刷新本页面重试!<br/>');
document.write("Welcome for People, Not Welcome for Machine!<br/>");
}
}, 2000)
}();
*/
2、分析进行了内外窗口判断,代码运行时间判断
解决方案:在eval外覆盖原内外窗口大小;hook "Function的构造函数"
window.outerWidth = 2056
window.innerWidth = 2000
window.outerHeight = 800
window.innerHeight = 700
var Function_constructor_s = Function.constructor
Function.prototype.constructor = function(args_str){
if (args_str.toLowerCase().indexOf("debugger")!=-1){
return function(){};
}else{
return Function_constructor_s(args_str);
}
}
此外还发现无限调用console.clear(),没有再继续分析