几种常见的前端反调试方法及突破方式

file
:hushed:前年有段时间沉迷于某H5的大逃杀小游戏(闲的),起初也是中规中矩,后来因为存档丢失,遂开启了修改存档的不归路。
最近的版本也更新的新的游戏模式和枪械,原本的小破枪再也不能称霸战局了,想再次修改存档时,发现网页也做了诸多反调试限制,通过一番折腾成功突破,那就以此为例总结下几种前端反调试方法及突破方式。

欺负我没有鼠标/键盘?

实现方式

主要是对键盘快捷键、鼠标右键的限制,无法直接打开开发者工具,用来欺负一些只使用键盘或者鼠标的人(狗头),在一些禁止复制的文章网站也会遇到,具体代码如下

//屏蔽F12
$(document).keydown(function (event) {
    if (event.keyCode == 123) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            window.event.returnValue == false;
        }
    }
});
//屏蔽ctrl+shift+i
$(document).keydown(function (event) {
    if (event.ctrlKey && event.shiftKey && event.keyCode == 73) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            window.event.returnValue == false;
        }
    }
});
// 通过添加自定义事件屏蔽鼠标右键
$(document).ready(function () {
    $(document).bind("contextmenu", function (e) {
        return false;
    });
});

突破方法

解决方法主要有两种:

  1. 通过浏览器的菜单找到开发者工具打开,例如Chrome浏览器可以在菜单-更多工具-开发者工具直接打开
  2. 新建标签页,先打开开发者工具,再访问相关页面

更近一步,阻碍前进的debugger

实现方式

当打开开发者工具后,却被不断产生的莫名其妙的debugger断点卡着,在以前还只会禁用所有断点,虽然可以正常浏览,但是也无法继续调试了,要不忘初心啊,我是来改数据的(狗头)。
file

这种不断产生debugger断点的反调试方法可以利用递归或者定时器的方式实现,该网站的实现方式如下,将两者结合了起来:

// 通过递归反复构造匿名函数利用debugger断点阻止调试
var check = function () {
    function doCheck(a) {
        if (('' + a / a)['length'] !== 1 || a % 20 === 0) {
            (function () { }['constructor']('debugger')());
        } else {
            (function () { }['constructor']('debugger')());
        }
        doCheck(++a);
    }
    try {
        doCheck(0);
    } catch (err) { }
};
check();
// 同时利用定时器定期产生debugger断点
setInterval(function () {
    check();
}, 2000);

突破方法

  1. 如果只是为了开启开发者工具后也能够正常浏览网站,例如借鉴Copy元素的样式,那么直接禁用所有断点即可,但是也无法继续调试
    file

  2. 可以在断点的右键菜单,单独取消阻止调试的断点,但是有时会对匿名函数产生的断点失效
    file

  3. 利用Hook阻止调用产生debugger的匿名函数

    Function.prototype.temp_constructor= Function.prototype.constructor;
    Function.prototype.constructor=function(){
    if (arguments && typeof arguments[0]==="string"){
        if (arguments[0]==="debugger")
        return ""
    }
    return Function.prototype.temp_constructor.apply(this, arguments);
    };
  4. 对于这个已知名称为check的检测函数,也可以在控制台进行置空,在执行前将其设置为空函数

    check = function() {}

时停,但是被感知到了!

有些时候,在跳过第一个断点后,啪的一下很快啊,就是一个弹窗逮住我的调试行为,紧接着刷新,那么我的调试使如何被感知到的呢?
file

实现方式

利用定时器定期产生debugger断点,在断点前后利用变量beforeafter计算时间差,如果相差在2秒后则说明开发者工具被打开,弹出弹窗并刷新页面;不排除某些人拥有单身20年的手速,能够快速跳过断点,则对小时间差进行统计,当存在2次及以上的小时间差,也可以说明打开了开发者工具。

该网站的实现代码如下:

function consoleOpenCallback() {
    alert('关闭调试窗');
    window.location.reload();
}

var Anti_numtots = 0;
(function () {
    window._windon_handler = setInterval( function() {
        var before = new Date();
        debugger;
        var after = new Date();
        if (after.getTime() - before.getTime() > 100) {
            if (after.getTime() - before.getTime() > 2000) {
                consoleOpenCallback();
                clearInterval(_windon_handler);
            }else{
                Anti_numtots++;
                if(Anti_numtots>=2){
                    consoleOpenCallback();
                    clearInterval(_windon_handler);
                }
            }
        }else{
            Anti_numtots = 0;
        }
    }, 1000)
})();

突破方法

这种计算时间差的反调试方法突破方式与上一种是相同的,都是阻止断点暂停或产生。

对于该网站,由于已知存储定时器的对象位置为window._windon_handler,因此也可以利用clearInterval清除该定时器即可。

类似反调试实现

内存爆破

通过时间差,在检测到被调试后疯狂创建对象,导致页面卡死

setInterval(() => {
    let startTime = new Date();
    debugger;
    let endTime = new Date();
    let isDev = endTime - startTime > 100;
    let stack = [];
    if (isDev) {
        while (true) {
            stack.push(this);
            console.log(stack.length, this)
        }
    }
}, 1000)

通常通过忽略该断点暂停以及取反判断标识可以处理,需要具体情况具体分析。

参考文章

发布者

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注