上一篇:前端实战篇:JavaScript 反调试技巧的简单应用(上)
前言
上一篇我们通过对函数的重新定义和设置断点的方式来中断或阻止脚本的代码执行达到反调试的目的,这一篇我们继续探讨通过其他方式来反调试
三、时间差异
这是一种从传统反逆向技术那里借鉴过来的基于时间的反调试技巧。当脚本在DevTools等工具环境下执行时,运行速度会非常慢(时间久),所以我们就可以根据运行时间来判断脚本当前是否正在被调试。比如说,我们可以通过测量代码中两个设置点之间的运行时间,然后用这个值作为参考,如果运行时间超过这个值,说明脚本当前在调试器中运行。
实例代码如下:
set Interval(function(){
var startTime = performance.now(), check,diff;
for (check = 0; check < 1000; check++){
console.log(check);
console.clear();
}
diff = performance.now() - startTime;
if (diff > 200){
alert("Debugger detected!");
}
},500);
四、DevTools检测(Chrome)
这项技术利用的是div元素中的id属性,当div元素被发送至控制台(例如console.log(div))时,浏览器会自动尝试获取其中的元素id。如果代码在调用了console.log之后又调用了getter方法,说明控制台当前正在运行。
实例代码如下:
let div = document.createElement('div');
let loop = setInterval(() => {
console.log(div);
console.clear();
});
Object.defineProperty(div,"id", {get: () => {
clearInterval(loop);
alert("Dev Tools detected!");
}});
五、隐式流完整性控制
当我们尝试对代码进行反混淆处理时,我们首先会尝试重命名某些函数或变量,但是在JavaScript中我们可以检测函数名是否被修改过,或者说我们可以直接通过堆栈跟踪来获取其原始名称或调用顺序。
arguments.callee.caller可以帮助我们创建一个堆栈跟踪来存储之前执行过的函数,示例代码如下:
function getCallStack() {
var stack = "#", total = 0, fn =arguments.callee;
while ( (fn = fn.caller) ) {
stack = stack + "" +fn.name;
total++
}
return stack
}
function test1() {
console.log(getCallStack());
}
function test2() {
test1();
}
function test3() {
test2();
}
function test4() {
test3();
}
test4();
备注:源代码的混淆程度越强,达到的效果就越好。
六、代理对象
代理对象是目前JavaScript中最有用的一个工具,这种对象可以帮助我们了解代码中的其他对象,包括修改其行为以及触发特定环境下的对象活动。比如说,我们可以创建一个嗲哩对象并跟踪每一次document.createElemen调用,然后记录下相关信息:
const handler = { // Our hook to keep the track
apply: function (target, thisArg, args){
console.log("Intercepted a call tocreateElement with args: " + args);
return target.apply(thisArg, args)
}
}
控制台输出:
VM64:3 Intercepted a call to createElement with args: div
我们可以利用这些信息并通过拦截某些特定函数来调试代码,但是本文的主要目的是为了介绍反调试技术,那么我们如何检测“对方”是否使用了代理对象呢?其实这就是一场“猫抓老鼠”的游戏,比如说,我们可以使用相同的代码段,然后尝试调用toString方法并捕获异常:
try {
document.createElement.toString();
}catch(e){
console.log("I saw your proxy!");
}
信息如下:
"function createElement() { [native code] }"
使用代理结果查看:
consthandler = {
apply: function (target, thisArg, args){
console.log("Intercepted a call tocreateElement with args: " + args);
return target.apply(thisArg, args)
}
}
document.createElement= new Proxy(document.createElement, handler);
//Callour not-so-virgin-after-that-party createElement
try {
document.createElement.toString();
}catch(e) {
console.log("I saw your proxy!");
}
输出结果:
VM391:13 I saw your proxy!
我们还可以添加toString方法:
const handler = {
apply: function (target, thisArg, args){
console.log("Intercepted a call tocreateElement with args: " + args);
return target.apply(thisArg, args)
}
}
document.createElement= new Proxy(document.createElement, handler);
document.createElement= Function.prototype.toString.bind(document.createElement); //Add toString
//Callour not-so-virgin-after-that-party createElement
try {
document.createElement.toString();
}catch(e) {
console.log("I saw your proxy!");
}
输出结果:
"function createElement() { [native code] }"
更多精彩内容可以关注“IT实战联盟”公众号,也可以留言和作者互动或者加入我们交流群哦~~~