# 反调试对抗手册 ## 概述 现代网站常采用多种反调试技术阻止逆向分析。本手册涵盖常见反调试手段及其绕过策略。 ## 1. debugger 语句 ### 表现 ```javascript // 无限 debugger 循环 setInterval(function() { debugger; }, 100); // 条件 debugger (function check() { debugger; check(); })(); // 隐藏 debugger(通过 constructor) (function() { var a = new Function("debugger"); setInterval(a, 1000); })(); ``` ### 绕过方案 **方案 A:MCP 注入绕过** ``` [js-reverse] inject_before_load(script=反debugger脚本) ``` ```javascript // 覆写 Function 构造器,过滤 debugger const _Function = Function; Function = function() { const body = arguments[arguments.length - 1]; if (typeof body === 'string' && body.indexOf('debugger') !== -1) { arguments[arguments.length - 1] = body.replace(/debugger/g, ''); } return _Function.apply(this, arguments); }; Function.prototype = _Function.prototype; // 覆写定时器,过滤 debugger const _setInterval = setInterval; setInterval = function(fn, ms) { if (typeof fn === 'function' && fn.toString().indexOf('debugger') !== -1) { return -1; } if (typeof fn === 'string' && fn.indexOf('debugger') !== -1) { return -1; } return _setInterval.apply(this, arguments); }; ``` **方案 B:断点条件设置 false** ``` [js-reverse] set_breakpoint_on_text(text="debugger", condition="false") ``` ## 2. 开发者工具检测 ### 2.1 窗口尺寸检测 ```javascript setInterval(function() { if (window.outerHeight - window.innerHeight > 200 || window.outerWidth - window.innerWidth > 200) { // DevTools 已打开 document.body.innerHTML = ''; } }, 500); ``` **绕过**: ```javascript // 注入后锁定尺寸 Object.defineProperty(window, 'outerHeight', { get: () => window.innerHeight }); Object.defineProperty(window, 'outerWidth', { get: () => window.innerWidth }); ``` ### 2.2 console.log 计时检测 ```javascript setInterval(function() { var start = Date.now(); console.log('check'); console.clear(); if (Date.now() - start > 10) { // DevTools 已打开(console.log 在 DevTools 打开时较慢) window.location = 'about:blank'; } }, 1000); ``` **绕过**: ```javascript // 覆写 console 方法为空操作 const noop = function() {}; console.log = noop; console.clear = noop; ``` ### 2.3 toString 检测 ```javascript var devtools = /./; devtools.toString = function() { // 只有在 DevTools 打开时,console.log 才会调用对象的 toString isDevToolsOpen = true; return ''; }; console.log(devtools); ``` **绕过**: ```javascript // 覆写 console.log,阻止 toString 调用检测 const _log = console.log; console.log = function() { // 不调用 toString }; ``` ## 3. 代码完整性检测 ### 3.1 函数 toString 检测 ```javascript function critical() { // 重要逻辑 } if (critical.toString().indexOf('\n') !== -1) { // 代码被格式化,触发反调试 while(true) {} } ``` **绕过**: ```javascript // 保存原始 toString 结果 const origToString = critical.toString(); Object.defineProperty(critical, 'toString', { value: function() { return origToString; } }); ``` ### 3.2 源码长度检测 ```javascript if (someFunction.toString().length !== 1234) { // 代码被修改 throw new Error('Integrity check failed'); } ``` **绕过**:不修改原始函数,使用 `inject_before_load` 在执行之前收集所需数据。 ## 4. 时间差检测 ```javascript var t1 = Date.now(); // ... 执行代码 ... var t2 = Date.now(); if (t2 - t1 > 100) { // 可能在断点处暂停了 window.location = 'about:blank'; } ``` **绕过**: ```javascript // Hook Date.now 返回连续值 const _now = Date.now; let fakeTime = _now(); Date.now = function() { fakeTime += 1; return fakeTime; }; // 或使用 performance.now const _perfNow = performance.now; performance.now = function() { return _perfNow.call(performance); }; ``` ## 5. 环境检测 ### 5.1 Node.js 环境检测 ```javascript if (typeof process !== 'undefined' && process.versions && process.versions.node) { throw new Error('Node.js detected'); } if (typeof module !== 'undefined' && module.exports) { throw new Error('CommonJS detected'); } if (typeof global !== 'undefined') { throw new Error('Non-browser detected'); } ``` **绕过**:在 vm 沙箱中删除这些全局变量。 ### 5.2 浏览器指纹检测 ```javascript if (!window.chrome || !window.chrome.runtime) { throw new Error('Not Chrome'); } if (navigator.webdriver) { throw new Error('WebDriver detected'); } if (navigator.plugins.length === 0) { throw new Error('Headless browser detected'); } ``` **绕过**:补全环境变量(参考 `environment-patch.md`)。 ### 5.3 Selenium / Puppeteer 检测 ```javascript const checks = [ 'webdriver' in navigator, '_Selenium_IDE_Recorder' in window, 'callSelenium' in document, '__webdriver_script_fn' in document, '$cdc_asdjflasutopfhvcZLmcfl_' in document, '_phantom' in window, 'callPhantom' in window ]; if (checks.some(Boolean)) { throw new Error('Automation detected'); } ``` **绕过**: ```javascript // 清除自动化指纹 delete navigator.webdriver; Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); delete window._Selenium_IDE_Recorder; // ... 逐个清除 ``` ## 6. Object.defineProperty 篡改 ```javascript // 改变属性定义行为 const _defineProperty = Object.defineProperty; Object.defineProperty = function(obj, prop, descriptor) { if (prop === 'cookie') { return ""; // 阻止 Cookie Hook } return _defineProperty.apply(this, arguments); }; ``` **绕过**:在注入脚本中保存原始引用。 ```javascript // 在所有代码之前保存原始方法 const originalDefineProperty = Object.defineProperty; // 后续使用 originalDefineProperty 而非 Object.defineProperty ``` ## 7. Proxy 检测 ```javascript // 某些代码会检测对象是否被 Proxy 包装 try { new Proxy({}, {}); } catch(e) { // 环境异常 } ``` **注意**:使用 Proxy 做 Hook 时,确保 Proxy 行为与原始对象一致。 ## 通用反反调试注入脚本 以下脚本适合通过 `inject_before_load` 在页面加载前注入: ```javascript (function() { 'use strict'; // 保存原始方法引用 const originals = { defineProperty: Object.defineProperty, getOwnPropertyDescriptor: Object.getOwnPropertyDescriptor, setInterval: window.setInterval, setTimeout: window.setTimeout, Function: window.Function, eval: window.eval, dateNow: Date.now, }; // 1. 拦截 debugger const _Function = originals.Function; window.Function = function() { let body = arguments[arguments.length - 1]; if (typeof body === 'string' && body.includes('debugger')) { arguments[arguments.length - 1] = body.replace(/debugger\s*;?/g, ''); } return _Function.apply(this, arguments); }; window.Function.prototype = _Function.prototype; // 2. 过滤 debugger 定时器 const _setInterval = originals.setInterval; window.setInterval = function(fn, ms) { if (typeof fn === 'function' && fn.toString().includes('debugger')) return -1; if (typeof fn === 'string' && fn.includes('debugger')) return -1; return _setInterval.apply(this, arguments); }; const _setTimeout = originals.setTimeout; window.setTimeout = function(fn, ms) { if (typeof fn === 'function' && fn.toString().includes('debugger')) return -1; if (typeof fn === 'string' && fn.includes('debugger')) return -1; return _setTimeout.apply(this, arguments); }; // 3. 隐藏 webdriver 属性 originals.defineProperty.call(Object, navigator, 'webdriver', { get: () => undefined, configurable: true }); console.log('[AntiDebug] 反反调试脚本已注入'); })(); ```