前端代码混淆后怎么防调试?控制台一打就崩了

Good“一茹 阅读 88

我最近在搞一个付费的H5小游戏,为了防止别人直接扒代码,用了webpack加了Terser做混淆,还加了点反调试的代码。但上线后发现,只要用户打开DevTools,页面就直接白屏或者卡死,体验太差了。我试过网上那种覆盖console.log的方法,但好像没用。

比如下面这段简单的防调试代码,本地测试没问题,但放线上就不行:

<script>
(function() {
  let devtools = false;
  setInterval(() => {
    if (devtools) return;
    console.log('check');
    devtools = /./.test(/./);
  }, 1000);
})();
</script>

有没有更稳妥的防调试方案?既不让代码被轻易读,又不至于把正常用户搞崩溃?

我来解答 赞 9 收藏
二维码
手机扫码查看
2 条解答
萌新.熙然
遇到这个问题确实挺头疼的,既要防止调试,又不能影响用户体验。你提到的覆盖 console.log 方法有时候确实不太管用,因为现代浏览器提供了多种绕过这些简单检测的方法。

一种稍微复杂但更可靠的方案是结合多种检测方法,并且确保这些检测不会过于频繁地影响性能。可以尝试以下步骤:

1. 检查 DevTools 是否打开。你可以使用 window.onresize 事件来检测窗口大小的变化,有时候 DevTools 打开会导致窗口尺寸变化。
2. 利用 debugger 语句和 try...catch 来检测。当 DevTools 打开时,debugger 语句会触发断点。
3. 使用 MutationObserver 来监听开发者工具的打开。开发者工具通常会改变 DOM 结构,可以通过观察这些变化来判断。

下面是一个综合了这些方法的示例代码:

function detectDevTools() {
const threshold = 160;
const devtools = /./;
devtools.toString = function() {
this.opened = true;
};
console.log(devtools);
window.addEventListener('resize', function() {
if (window.outerWidth - window.innerWidth > threshold || window.outerHeight - window.innerHeight > threshold) {
handleDevTools();
}
});
setInterval(function() {
if (devtools.opened) {
handleDevTools();
}
try {
debugger;
handleDevTools();
} catch (e) {}
}, 1000);

function handleDevTools() {
// 这里处理检测到 DevTools 的情况,比如显示一个友好的提示信息或者重定向用户
console.error('DevTools detected! Please close it to continue.');
// 你可以在这里添加更多的逻辑,比如禁用某些功能或者重定向用户
}

const observer = new MutationObserver(handleDevTools);
observer.observe(document, { attributes: true, childList: true, subtree: true });
}

detectDevTools();


注意:这种方法也不是绝对安全的,高级用户仍然可能找到绕过的方式。不过,它比单纯的 console.log 检测要更有效一些。另外,确保这些检测不会过度干扰用户体验,毕竟大多数用户是无辜的。
点赞
2026-03-25 08:08
一翼杨
一翼杨 Lv1
你这个问题的核心在于:反调试和用户体验本身就是矛盾的,你得做个取舍。

先说你那段代码的问题。那个 /./.test(/./) 的检测方式本身就不靠谱,浏览器版本更新后很容易失效,而且这种定时器检测本身就会带来性能开销。

更关键的是,一旦你在代码里写死"检测到调试就崩溃",那这个逻辑本身就成了可以被逆向的弱点——攻击者只需要hook掉你的检测函数就行。

我的建议是换个思路:不要试图阻止调试,而是让代码被调试的价值降到最低。

具体做法:

1. 代码层面:用webpack + terser做混淆是OK的,但别在代码里留明显的反调试逻辑。把核心逻辑放到WebAssembly或者用asm.js写,这样逆向难度会高很多。

2. 动态加载:关键代码用eval或者new Function动态生成,代码不在HTML文件里明着写。比如:

// 核心逻辑分段加密,运行时解密
const encryptedCode = "gAAAAABk..."; // 这里放加密后的代码
function loadCore() {
const decoded = atob(encryptedCode); // base64解码
const code = decodeURIComponent(escape(decoded)); // 解密
return new Function(code)();
}
loadCore();


3. 如果非要检测调试行为,可以把检测逻辑藏得深一点,并且用更隐蔽的方式,比如检测performance.now()的时间差、或者检测某些DOM属性的访问:

// 检测调试器attach时的特征
(function() {
let lastTime = performance.now();
debugger; // 断点停在这里
const diff = performance.now() - lastTime;
if (diff > 100) {
// 可能是调试器让时间变慢了
// 可以做一些轻量级的干扰,比如随机注入错误代码
}
})();


4. 最重要的一点:别让页面直接崩掉。你可以用更隐蔽的方式,比如在检测到异常时让某些功能随机失效、或者返回错误的数据,而不是直接白屏。这样用户不会立刻意识到你在反调试。

5. 考虑加个域名绑定,把关键逻辑放在服务端,前端只做展示。H5游戏本身就应该把核心逻辑放后端,前端只负责渲染。

说白了,前端安全的上限就那么高,再怎么防也挡不住有心的攻击者。你要做的是提高破解成本,而不是试图完全阻止调试。把精力放在游戏本身的可玩性上,比纠结这个划算多了。
点赞
2026-03-17 15:18