refactor: 将文件从子目录提升到仓库根目录
所有文件从 hello_js_reverse_skill/ 子目录移至仓库根目录, 使项目结构更清晰,便于作为 Skill 直接引用。 Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,337 @@
|
||||
# 反调试对抗手册
|
||||
|
||||
## 概述
|
||||
|
||||
现代网站常采用多种反调试技术阻止逆向分析。本手册涵盖常见反调试手段及其绕过策略。
|
||||
|
||||
## 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] 反反调试脚本已注入');
|
||||
})();
|
||||
```
|
||||
Reference in New Issue
Block a user