前端反调试初探

Daotin 于 2021-08-18 发布 编辑
  1. 无限debugger反调试
  2. 禁用F12等按键
  3. 禁用F12等按键并debugger
  4. 另一种写法
  5. 扩展

任何网页开发者出于对自己网页的保护,都不想自己的前端代码被他人窃取或者抄袭。虽然我们平时都在忙着往代码里面写BUG,但是依然阻止他人对你代码的觊觎。

由此,前端反调试需求应运而生。

无限debugger反调试

(function() {
  setInterval(() => {
    debugger;
  }, 50);
}())

1、一定要使用匿名函数。否则别人直接在控制台重定义函数就完了,比如:function startDebug() {}; 2、一定要写在一行。否则别人可以通过设置条件断点false,来使debugger失效。

代码虽然简单,但是却很有效。

它会不停地打断你,页面跳到source页面,阻止你看他代码不断的产生不可回收的对象,占据你的内存,造成内存泄漏,没过多久浏览器就会卡顿。

缺点:可以通过禁用浏览器断点功能(Deactivate breakpoints)来禁止debugger,但是也存在很大的缺陷,因为对于其他代码,我们还是需要断点调试功能的。所以这个方法仅限于静态分析。

禁用F12等按键

没啥好说的,直接上代码。

document.oncontextmenu = function () {//右键禁用
    return false;
};
document.oncopy = function () {//复制禁用
    return false;
};
document.addEventListener('keydown', function (e) {//按键禁用
    try {
        e = e || event || window.event;
        var keycode = e.keyCode || e.which || e.charCode;
        var ctrlKey = e.ctrlKey || e.metaKey;
        if (keycode == 123) {//F12
            e.preventDefault();
            window.event.returnValue = false;
        } else if (keycode == 8) { // BackSpace
            var act = document.activeElement.tagName.toLowerCase();
            if (act.indexOf("body") != -1 || act.indexOf("html") != -1) {
                window.history.back();
                return false;
            }
            return true;
        } else if (ctrlKey && keycode == 83) {//ctrl+S
            e.preventDefault();
            window.event.returnValue = false;
        } else if (ctrlKey && keycode == 87) {//ctrl+W
            e.preventDefault();
            window.event.returnValue = false;
        } else if (ctrlKey && keycode == 107) {//ctrl++
            e.preventDefault();
            window.event.returnValue = false;
        } else if (ctrlKey && keycode == 109) {//ctrl+-
            e.preventDefault();
            window.event.returnValue = false;
        } else if (keycode == 122) {//F11
            $('#switchFullScreenCloseBtn').trigger("click");
        }

    } catch (e) {
        console.error(e);
    }
});

“return false” VS “ event.preventDefault()” VS  “event.stopPropagation()” 三者的区别?

    - 在jQuery中,等于前两者之和。

    - 在非jQuery中,等于event.preventDefault(); ,并不能阻止事件冒泡。

参考链接:https://stackoverflow.com/questions/1357118/event-preventdefault-vs-return-false

禁用F12等按键并debugger

通过第二步,禁止F12等按键后,虽然无法使用快捷键进入调试工具,但是如果通过浏览器右上角的【设置】,进入【更多工具】,再打开【开发人员工具】,调试界面还是可以打开的。

那么我们就要在他进入后,也要打开debugger,所以我们在第二步的基础上进行优化。

直接上代码了:

/**
 * 前端反调试 (Daotin added in 20210316)
 * closeDebugger: 关闭debugger
 * openDebugger: 打开debugger
 */

export default {
    closeDebugger: function () {

        /*
         * 反调试代码,如果还是打开了控制台,无限debugger,并且网页变得极其卡顿,原因未知(类似问卷星答题界面)
         */
        window._debuggerTimer = setInterval(function() { check() }, 5000); // 每5秒执行一次

        let check = function() {
            function doCheck(a) {
                if (("" + a/a)["length"] !== 1 || a % 20 === 0) {
                    ( function() {} ["constructor"]("debugger")() ) // 类似 new Function('debugger')()
                } else {
                    ( function() {} ["constructor"]("debugger")() ) 
                }
                console.log('----->无限debugger');
                doCheck(++a);
            }
            try { doCheck(0) } catch (err) {}
        };

        check();

        /**
         * 按键禁用
         */
        $(document).on('keydown',function (event) {
            let e = e || event || window.event;
            let keyCode = e.keyCode || e.which || e.charCode;
            let ctrlkey = e.ctrlKey || e.metaKey;

            if (keyCode == 122 || keyCode == 123) { // Prevent F11/F12
                return false;
            } else if (ctrlkey && keyCode == 83) { // Prevent Ctrl+S
                let saveBtn = $('.answer-start .savebtn')[0];
                console.log('saveBtn----->', saveBtn);
                if(saveBtn) {
                    // 使用 $(savaBtn).trigger('click'); 无效。
                    saveBtn.click(); // 触发自己的保存
                }
                return false;
            } else if (ctrlkey && keyCode == 87) { // Prevent Ctrl+W
                return false;
            } else if(keyCode == 8) { // Bankspace 键可以返回
                // var act = document.activeElement.tagName.toLowerCase();
                // if (act.indexOf("body") != -1 || act.indexOf("html") != -1) {
                //     window.history.back();
                //     return false;
                // }
                // return true;
            }
        });

        $(document).on("contextmenu", function (e) { //右键禁用
            return false;
        });

        $(document).on("copy", function (e) { //复制禁用
            return false;
        });

        console.log('----->', '前端防调试open');
    },
    openDebugger: function () {
        clearInterval(window._debuggerTimer);
        window._debuggerTimer = null;

        $(document).off('keydown');
        $(document).off("contextmenu");
        $(document).off("copy");

        console.log('----->', '前端防调试close');
    }
}

加了一段反调试代码,如果还是打开了控制台,无限debugger,并且网页变得极其卡顿。

另一种写法

增加utils/debugger.js文件,并在main.ts中引入该文件。当生产环境需要调试时,在控制台输入window.__debugger__ = true即可。


const forbid = () => {
  function block() {
    const interval = setInterval(() => {
      if (window.__debugger__ == true) return clearInterval(interval);
      (function () {
        return false;
      }
        ["constructor"](["d", "e", "b", "u", "g", "g", "e", "r"].join(""))
        ["call"]());
    }, 50);
  }
  try {
    block();
  } catch (err) {}
};
import.meta.env.MODE === "production" && forbid();

参考链接:

扩展

这样我们的前端反调试就完成了,能防住一些基本的小白用户,但是无法防住真正的大神。

那么,“反反调试”也出现了,真是道高一尺,魔高一丈。

可以参考这篇文章:前端Chrome反《反调试》

还有这篇文章,可以拦截debugger:【第2937期】JavaScript奇技淫巧:debugger拦截

总结:

(完)