> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# JS 逆向 Hook 与代码片段大全
- URL: https://blog.vercanti.com/js-ni-xiang-hook-yu-dai-ma-pian-duan-da-quan/
- Published: 2026-08-28T14:35:01.000Z
- Updated: 2026-08-28T14:57:52.000Z
- Description: Hook 的核心原理是利用 JavaScript 弱类型语言特性，用自定义函数替换原生函数，在调用时插入 debugger 或日志，从而定位加密逻辑。 两种核心实现方式： Hook 注入方式 浏览器 DevTools 场景 Node.js 补环境场景 参考 监控某个对象上指定属性的读取、写入、删除操作，精确定位属性被谁写入。 代理整个对象，监控其上所有属性的读写操作，适合不知道属性名时的全量监控。 对整个对象做 Proxy 代理，每次访问都打印 对象名.属性名 的完整路径，适合追踪链式访问。 Hook 内置对象的原型方法，监控 String、Array、
- Author: yellowdog
- Tags: js逆向, 代码段

> 官方文档：<https://developer.mozilla.org/zh-CN/docs/Web/JavaScript>

Hook 的核心原理是利用 JavaScript 弱类型语言特性，用自定义函数替换原生函数，在调用时插入 `debugger` 或日志，从而定位加密逻辑。

> 被 Hook 的函数必须和 `debugger` 在同一个作用域内，否则 Hook 无效。Hook 代码必须在目标代码执行之前注入，推荐通过 Tampermonkey 的 `@run-at document-start` 保证优先级。

两种核心实现方式：

| 方式                    | 适用场景        | 备注                       |
| --------------------- | ----------- | ------------------------ |
| Object.defineProperty | 监控对象属性读写    | 对已有属性可精确控制 getter/setter |
| Proxy                 | 代理整个对象的所有操作 | 更全面，但需要通过代理对象访问才能触发      |

---

## 目录

**Hook 注入方式**

- [注入方式](#%E6%B3%A8%E5%85%A5%E6%96%B9%E5%BC%8F)

**浏览器 DevTools 场景**

- [1\. 对象监控](#1-%E5%AF%B9%E8%B1%A1%E7%9B%91%E6%8E%A7)
- [2\. 函数 Hook](#2-%E5%87%BD%E6%95%B0-hook)
- [3\. 原型链 Hook](#3-%E5%8E%9F%E5%9E%8B%E9%93%BE-hook)
- [4\. XMLHttpRequest Hook](#4-xmlhttprequest-hook)
- [5\. fetch Hook](#5-fetch-hook)
- [6\. Cookie Hook](#6-cookie-hook)
- [7\. localStorage / sessionStorage Hook](#7-localstorage-sessionstorage-hook)
- [8\. debugger 反调试绕过](#8-debugger-%E5%8F%8D%E8%B0%83%E8%AF%95%E7%BB%95%E8%BF%87)
- [9\. JSON.stringify / JSON.parse Hook](#9-jsonstringify-jsonparse-hook)
- [10\. WebSocket Hook](#10-websocket-hook)
- [11\. eval / Function Hook](#11-eval-function-hook)
- [12\. Canvas 指纹 Hook](#12-canvas-%E6%8C%87%E7%BA%B9-hook)
- [13\. 定时器 Hook](#13-%E5%AE%9A%E6%97%B6%E5%99%A8-hook)
- [14\. DOM 监控](#14-dom-%E7%9B%91%E6%8E%A7)
- [15\. console 保护与重写](#15-console-%E4%BF%9D%E6%8A%A4%E4%B8%8E%E9%87%8D%E5%86%99)
- [16\. 加密函数定位](#16-%E5%8A%A0%E5%AF%86%E5%87%BD%E6%95%B0%E5%AE%9A%E4%BD%8D)
- [17\. 其他实用 Hook](#17-%E5%85%B6%E4%BB%96%E5%AE%9E%E7%94%A8-hook)

**Node.js 补环境场景**

- [18\. 环境 Hook 模块（Node.js 补环境）](#18-%E7%8E%AF%E5%A2%83-hook-%E6%A8%A1%E5%9D%97%EF%BC%88nodejs-%E8%A1%A5%E7%8E%AF%E5%A2%83%EF%BC%89)

**参考**

- [场景速查表](#%E5%9C%BA%E6%99%AF%E9%80%9F%E6%9F%A5%E8%A1%A8)
- [相关文档](#%E7%9B%B8%E5%85%B3%E6%96%87%E6%A1%A3)

---

## 注入方式

| 方式                    | 说明                                  |
| --------------------- | ----------------------------------- |
| 控制台直接粘贴               | 刷新页面前在 Console 面板粘贴执行，适合快速测试        |
| Tampermonkey 脚本       | 写成用户脚本，@run-at document-start，持久化注入 |
| Chrome Local Override | 下载目标 JS 文件，添加 Hook 代码，启用本地覆盖        |
| Fiddler/mitmproxy 注入  | 通过代理在响应体中动态插入 Hook 代码               |

---

## 1\. 对象监控

### 1.1 Object.defineProperty 监控属性读写

监控某个对象上指定属性的读取、写入、删除操作，精确定位属性被谁写入。

```javascript
// 使用场景：追踪 window 上某个变量被谁赋值
(function () {
    var _value;
    Object.defineProperty(window, 'targetProp', {
        configurable: true,
        get: function () {
            console.log('[GET] targetProp =', _value);
            console.log(new Error().stack);
            return _value;
        },
        set: function (val) {
            console.log('[SET] targetProp =', val);
            console.log(new Error().stack);
            _value = val;
        }
    });
})();

```

### 1.2 Proxy 代理整个对象

代理整个对象，监控其上所有属性的读写操作，适合不知道属性名时的全量监控。

```javascript
// 使用场景：监控 window 对象上所有属性的 get/set，找出未知的加密全局变量
const proxyWindow = new Proxy(window, {
    get: function (target, prop, receiver) {
        console.log('[GET]', prop, '=', target[prop]);
        return Reflect.get(target, prop, receiver);
    },
    set: function (target, prop, val) {
        console.log('[SET]', prop, '=', val);
        console.log(new Error().stack);
        return Reflect.set(target, prop, val);
    },
    deleteProperty: function (target, prop) {
        console.log('[DELETE]', prop);
        return Reflect.deleteProperty(target, prop);
    }
});

// 注意：Proxy 是一个壳，必须通过 proxyWindow 访问才会触发拦截
// window = proxyWindow; // 谨慎操作，某些场景会破坏页面

```

### 1.3 监控对象上特定属性（通用模板）

```javascript
// 使用场景：监控任意对象 obj 上名为 propName 的属性
function watchProperty(obj, propName) {
    var _val = obj[propName];
    Object.defineProperty(obj, propName, {
        configurable: true,
        enumerable: true,
        get: function () {
            console.log('[GET]', propName, '=', _val);
            return _val;
        },
        set: function (newVal) {
            console.log('[SET]', propName, ':', _val, '->', newVal);
            console.log(new Error().stack);
            _val = newVal;
        }
    });
}

// 调用示例
watchProperty(window, 'sign');
watchProperty(window, '_token');

```

### 1.4 watch() — 带路径追踪的 Proxy 监控

对整个对象做 Proxy 代理，每次访问都打印 `对象名.属性名` 的完整路径，适合追踪链式访问。

```javascript
function watch(obj, name) {
    const handler = {
        get: function (target, prop) {
            console.log('get:', `${name}.${prop}`, '===>', Reflect.get(...arguments));
            return Reflect.get(...arguments);
        },
        set: function (target, prop, value, receiver) {
            console.log('set:', `${name}.${prop}`, '===>', value);
            return Reflect.set(target, prop, value, receiver);
        }
    };
    return new Proxy(obj, handler);
}

// 用法：把目标对象替换为被监控的代理对象
window.navigator = watch(window.navigator, 'navigator');

// 监控自定义对象
const config = watch({ token: '', uid: 0 }, 'config');
config.token = 'abc';  // 控制台输出: set: config.token ===> abc

```

---

## 2\. 函数 Hook

### 2.1 简单函数替换（记录参数和返回值）

```javascript
// 使用场景：Hook 任意全局函数，记录每次调用的入参和出参
(function () {
    var _original = window.targetFunction;
    window.targetFunction = function () {
        console.log('[CALL] targetFunction args:', arguments);
        console.log(new Error().stack);
        var result = _original.apply(this, arguments);
        console.log('[RETURN] targetFunction result:', result);
        return result;
    };
    // 伪装成原生函数，防止被检测
    window.targetFunction.toString = function () {
        return _original.toString();
    };
})();

```

### 2.2 Hook alert（常用于快速测试）

```javascript
// 使用场景：阻止页面弹出 alert 干扰调试，同时记录 alert 内容
var _alert = window.alert;
window.alert = function () {
    console.log('[ALERT]', arguments[0]);
    console.log(new Error().stack);
    // _alert.apply(this, arguments); // 注释掉则阻止弹窗
};
window.alert.toString = function () {
    return 'function alert() { [native code] }';
};

```

### 2.3 通用函数 Hook 模板（带 debugger）

```javascript
// 使用场景：在加密函数调用时自动断下，配合调用栈分析
(function () {
    var _fn = window.encryptFunc; // 替换为目标函数名
    window.encryptFunc = function () {
        debugger; // 断下后查看 Call Stack
        return _fn.apply(this, arguments);
    };
    window.encryptFunc.toString = _fn.toString.bind(_fn);
})();

```

---

## 3\. 原型链 Hook

Hook 内置对象的原型方法，监控 String、Array、Object 等原生方法的调用。

### 3.1 String.prototype.split Hook

```javascript
// 使用场景：某些加密函数通过 split 分割字符串，hook 后可找到分割时机
String.prototype._split = String.prototype.split;
String.prototype.split = function (val) {
    var str = this.toString();
    if (str.length > 10) { // 过滤短字符串，减少噪音
        console.log('[split] this =', str, ', separator =', val);
        console.log(new Error().stack);
    }
    return str._split(val);
};
String.prototype.split.toString = function () {
    return 'function split() { [native code] }';
};

```

### 3.2 RegExp.prototype.test Hook

```javascript
// 使用场景：绕过正则检测（如反调试检测、UA 检测），或定位正则使用位置
RegExp.prototype._test = RegExp.prototype.test;
RegExp.prototype.test = function (val) {
    console.log('[RegExp.test] pattern =', this.toString(), ', input =', val);
    return this._test(val);
    // 如需强制返回 true：return true;
};
RegExp.prototype.test.toString = function () {
    return 'function test() { [native code] }';
};

```

### 3.3 Array.prototype.push Hook

```javascript
// 使用场景：监控数组被 push 元素，定位加密结果被存入数组的位置
Array.prototype._push = Array.prototype.push;
Array.prototype.push = function () {
    console.log('[Array.push] item =', arguments[0], ', array length =', this.length);
    console.log(new Error().stack);
    return this._push.apply(this, arguments);
};
Array.prototype.push.toString = function () {
    return 'function push() { [native code] }';
};

```

### 3.4 Object.prototype 通用 Hook（慎用）

```javascript
// 使用场景：监控某个属性名在所有对象上的读写（很重，调试时用，上线禁用）
(function () {
    var _defineProperty = Object.defineProperty;
    Object.defineProperty = function (obj, prop, descriptor) {
        if (prop === 'sign') { // 替换为目标属性名
            console.log('[defineProperty] prop =', prop, ', obj =', obj);
            console.log(new Error().stack);
        }
        return _defineProperty.call(this, obj, prop, descriptor);
    };
})();

```

---

## 4\. XMLHttpRequest Hook

### 4.1 完整 XHR Hook（记录请求方法、URL、请求头、请求体）

```javascript
// 使用场景：全量监控 XHR 请求，记录所有请求信息和调用栈
(function () {
    var hook_open = XMLHttpRequest.prototype.open;
    var hook_setRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
    var hook_send = XMLHttpRequest.prototype.send;

    XMLHttpRequest.prototype.open = function () {
        this._method = arguments[0];
        this._url = arguments[1];
        return hook_open.call(this, ...arguments);
    };

    XMLHttpRequest.prototype.setRequestHeader = function () {
        console.log(
            '[XHR setRequestHeader] url =', this._url,
            '\n  header:', arguments[0], '=', arguments[1]
        );
        console.log(new Error().stack);
        return hook_setRequestHeader.call(this, ...arguments);
    };

    XMLHttpRequest.prototype.send = function () {
        this._data = arguments[0];
        if (this._data != null) {
            console.log(
                '[XHR send]\n  method:', this._method,
                '\n  url:', this._url,
                '\n  body:', this._data
            );
        } else {
            console.log('[XHR send]\n  method:', this._method, '\n  url:', this._url);
        }
        console.log(new Error().stack);
        return hook_send.call(this, ...arguments);
    };
})();

```

### 4.2 XHR URL 断点 Hook（定位请求发起位置）

```javascript
// 使用场景：URL 包含目标关键字时断下，定位请求发起的代码位置
(function () {
    var _open = window.XMLHttpRequest.prototype.open;
    window.XMLHttpRequest.prototype.open = function (method, url, async) {
        if (url.indexOf('login') !== -1) {  // 替换为目标 URL 关键字
            debugger;
        }
        return _open.apply(this, arguments);
    };
})();

```

### 4.3 XHR 请求头断点 Hook（定位自定义签名 header）

```javascript
// 使用场景：定位 X-Sign、Authorization 等自定义请求头的生成位置
(function () {
    var _setRequestHeader = window.XMLHttpRequest.prototype.setRequestHeader;
    window.XMLHttpRequest.prototype.setRequestHeader = function (key, value) {
        if (key === 'X-Sign') {  // 替换为目标 header key
            debugger;
        }
        return _setRequestHeader.apply(this, arguments);
    };
})();

```

### 4.4 XHR 响应拦截 Hook

```javascript
// 使用场景：监控并可修改 XHR 的响应数据
(function () {
    var _open = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (method, url) {
        this._url = url;
        this.addEventListener('readystatechange', function () {
            if (this.readyState === 4) {
                console.log('[XHR response] url =', this._url);
                console.log('[XHR response] status =', this.status);
                console.log('[XHR response] body =', this.responseText.substring(0, 200));
            }
        });
        return _open.apply(this, arguments);
    };
})();

```

### 4.5 Proxy 方式 Hook XHR（拦截构造过程）

```javascript
// 使用场景：在创建 XHR 实例时即介入，适合需要修改响应的场景
XMLHttpRequest = new Proxy(XMLHttpRequest, {
    construct: function (target, args) {
        var xhr = new target(...args);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 1) {
                // 请求发送前，可在此注入 header
                xhr.setRequestHeader('X-Custom', 'injected');
            }
            if (xhr.readyState === 4) {
                if (xhr.status === 401) {
                    console.log('[XHR] 401 Unauthorized');
                }
            }
        };
        return xhr;
    }
});

```

---

## 5\. fetch Hook

### 5.1 基础 fetch Hook（记录所有参数和调用栈）

```javascript
// 使用场景：全量监控 fetch 请求，记录 URL、配置项和调用栈
(function () {
    var _fetch = window.fetch;
    window.fetch = function () {
        console.log('[fetch] args:', ...arguments);
        console.log(new Error().stack);
        return _fetch.apply(this, arguments);
    };
})();

```

### 5.2 fetch 请求拦截（可修改请求参数）

```javascript
// 使用场景：拦截特定 URL 的 fetch 请求，可修改请求参数或断下
const { fetch: _fetch } = window;
window.fetch = async function (...args) {
    let [resource, config] = args;

    // 请求拦截：打印请求信息
    console.log('[fetch request] url =', resource);
    console.log('[fetch request] config =', config);

    // 可在此修改 resource 或 config
    // resource = 'https://new-url.example.com/api';

    const response = await _fetch(resource, config);
    return response;
};

```

### 5.3 fetch 响应拦截（可读取/修改响应体）

```javascript
// 使用场景：读取 fetch 响应内容，定位响应数据解密位置
const { fetch: _fetch } = window;
window.fetch = async function (...args) {
    let [resource, config] = args;
    let response = await _fetch(resource, config);

    // 克隆响应（原始响应只能被消费一次）
    const cloned = response.clone();
    cloned.text().then(function (body) {
        console.log('[fetch response] url =', resource);
        console.log('[fetch response] status =', response.status);
        console.log('[fetch response] body =', body.substring(0, 300));
    });

    return response;
};

```

### 5.4 Proxy 方式 Hook fetch

```javascript
// 使用场景：使用 Proxy apply 陷阱拦截 fetch，等价于上面的写法
window.fetch = new Proxy(window.fetch, {
    apply: function (target, that, args) {
        console.log('[fetch] url =', args[0]);
        console.log(new Error().stack);
        var result = target.apply(that, args);
        result.then(function (res) {
            if (res.status === 401) {
                console.log('[fetch] 401 Unauthorized');
            }
        });
        return result;
    }
});

```

---

## 6\. Cookie Hook

### 6.1 Cookie 写入监控（带条件断点）

```javascript
// 使用场景：定位特定 Cookie 的生成位置，当 cookie 包含目标 key 时断下
(function () {
    var cookieTemp = '';
    Object.defineProperty(document, 'cookie', {
        set: function (val) {
            if (val.indexOf('your_key') !== -1) {  // 替换为目标 cookie key
                debugger;
            }
            console.log('[Cookie SET]', val);
            console.log(new Error().stack);
            cookieTemp = val;
            return val;
        },
        get: function () {
            return cookieTemp;
        }
    });
})();

```

### 6.2 Cookie Hook v2（使用原生 getter/setter，更稳定）

```javascript
// 使用场景：更稳定地 Hook cookie，通过获取 Document.prototype 上的原生访问器实现
(function () {
    var cookie_accessor = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
    var get_cookie = cookie_accessor.get;
    var set_cookie = cookie_accessor.set;

    Object.defineProperty(document, 'cookie', {
        get: function () {
            return get_cookie.call(document);
        },
        set: function (cookie) {
            console.log('[Cookie SET]', cookie);
            console.log(new Error().stack);
            set_cookie.call(document, cookie);
        }
    });
})();

```

### 6.3 Cookie 完整管理 Hook（维护本地 cookie 字符串）

```javascript
// 使用场景：完整接管 cookie 的读写，维护 key-value 映射，记录每次变更
(function () {
    'use strict';

    document.temp_cookie = document.cookie;

    Object.defineProperty(document, 'cookie', {
        get: function () {
            return document.temp_cookie;
        },
        set: function (cookie) {
            console.log('[Cookie SET]', cookie);
            console.log(new Error().stack);

            var key_arr = [];
            var val_arr = [];

            // 处理带过期时间等属性的 cookie（格式：key=val; expires=...）
            if (cookie.split('=').length > 2) {
                var temp_k = cookie.split('=')[0];
                var temp_val = cookie.substring(cookie.indexOf('=') + 1, cookie.indexOf(';'));
                key_arr.push(temp_k);
                val_arr.push(temp_val);
            }

            // 合并已有 cookie
            if (document.temp_cookie !== '') {
                document.temp_cookie.split('; ').forEach(function (item) {
                    var k = item.split('=')[0];
                    var v = item.split('=')[1];
                    if (typeof temp_k !== 'undefined' && temp_k === k) {
                        // skip, 已处理
                    } else {
                        key_arr.push(k);
                        val_arr.push(v);
                    }
                });
                document.temp_cookie = '';
            }

            // 处理简单 cookie（格式：key=val）
            if (!(cookie.split('=').length > 2)) {
                var newKey = cookie.split('=')[0];
                var newVal = cookie.split('=')[1];
                var found = false;
                for (var i = 0; i < key_arr.length; i++) {
                    if (key_arr[i] === newKey) {
                        val_arr[i] = newVal;
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    key_arr.push(newKey);
                    val_arr.push(newVal);
                }
            }

            // 重新组合 cookie 字符串
            document.temp_cookie = key_arr.map(function (k, i) {
                return k + '=' + val_arr[i];
            }).join('; ');
        }
    });
})();

```

---

## 7\. localStorage / sessionStorage Hook

### 7.1 localStorage 全方法监控

```javascript
// 使用场景：监控所有 localStorage 操作（setItem/getItem/removeItem/clear），打印调用栈
(function () {
    'use strict';

    var _setItem = localStorage.setItem;
    localStorage.setItem = function () {
        console.log('[localStorage.setItem] key =', arguments[0], ', value =', arguments[1]);
        console.log(new Error().stack);
        return _setItem.call(this, ...arguments);
    };

    var _getItem = localStorage.getItem;
    localStorage.getItem = function () {
        var val = _getItem.call(this, ...arguments);
        console.log('[localStorage.getItem] key =', arguments[0], ', value =', val);
        console.log(new Error().stack);
        return val;
    };

    var _removeItem = localStorage.removeItem;
    localStorage.removeItem = function () {
        console.log('[localStorage.removeItem] key =', arguments[0]);
        console.log(new Error().stack);
        return _removeItem.call(this, ...arguments);
    };

    var _clear = localStorage.clear;
    localStorage.clear = function () {
        console.log('[localStorage.clear] all items removed');
        console.log(new Error().stack);
        return _clear.call(this, ...arguments);
    };
})();

```

### 7.2 sessionStorage 全方法监控

```javascript
// 使用场景：监控所有 sessionStorage 操作
(function () {
    'use strict';

    var _setItem = sessionStorage.setItem;
    sessionStorage.setItem = function () {
        console.log('[sessionStorage.setItem] key =', arguments[0], ', value =', arguments[1]);
        console.log(new Error().stack);
        return _setItem.call(this, ...arguments);
    };

    var _getItem = sessionStorage.getItem;
    sessionStorage.getItem = function () {
        var val = _getItem.call(this, ...arguments);
        console.log('[sessionStorage.getItem] key =', arguments[0], ', value =', val);
        console.log(new Error().stack);
        return val;
    };

    var _removeItem = sessionStorage.removeItem;
    sessionStorage.removeItem = function () {
        console.log('[sessionStorage.removeItem] key =', arguments[0]);
        console.log(new Error().stack);
        return _removeItem.call(this, ...arguments);
    };

    var _clear = sessionStorage.clear;
    sessionStorage.clear = function () {
        console.log('[sessionStorage.clear] all items removed');
        console.log(new Error().stack);
        return _clear.call(this, ...arguments);
    };
})();

```

### 7.3 带条件断点的 localStorage Hook

```javascript
// 使用场景：只在操作特定 key 时断下，过滤无关的读写操作
(function () {
    var TARGET_KEY = 'token'; // 替换为目标 key
    var _setItem = localStorage.setItem;
    localStorage.setItem = function (key, value) {
        if (key === TARGET_KEY) {
            console.log('[localStorage.setItem TARGET]', key, '=', value);
            debugger;
        }
        return _setItem.call(this, key, value);
    };
})();

```

---

## 8\. debugger 反调试绕过

### 8.1 置空定时器（绕过 setInterval 无限 debugger）

```javascript
// 使用场景：绕过通过 setInterval 循环触发 debugger 的反调试
// 方式一：直接置空 setInterval，会影响页面所有定时器（副作用大）
setInterval = function () {};

// 方式二：Hook setInterval，过滤含 debugger 的函数（推荐）
var _setInterval = setInterval;
setInterval = function (func, timer) {
    if (func.toString().indexOf('debugger') !== -1) {
        console.log('[反调试] 检测到 setInterval 无限 debugger，已拦截');
        return null;
    }
    return _setInterval(func, timer);
};
setInterval.toString = function () {
    return 'function setInterval() { [native code] }';
};

```

### 8.2 Hook Function 构造函数（绕过 new Function('debugger')）

```javascript
// 使用场景：绕过通过 Function('debugger') 实现的无限 debugger（OB 混淆常见）
(function () {
    var _constructor = window.Function.prototype.constructor;
    window.Function.prototype.constructor = function () {
        var fnContent = arguments[arguments.length - 1];
        if (typeof fnContent === 'string' && fnContent.indexOf('debugger') !== -1) {
            console.log('[反调试] 拦截 Function("debugger")');
            return function () {};
        }
        return _constructor.apply(this, arguments);
    };
})();

```

### 8.3 绕过 eval('debugger')

```javascript
// 使用场景：绕过通过 eval('debugger') 实现的反调试
var _eval = window.eval;
window.eval = function (src) {
    if (typeof src === 'string' && src.indexOf('debugger') !== -1) {
        console.log('[反调试] 拦截 eval("debugger")');
        return undefined;
    }
    return _eval.call(this, src);
};
window.eval.toString = function () {
    return 'function eval() { [native code] }';
};

```

### 8.4 清除所有已有定时器

```javascript
// 使用场景：页面已有无限 debugger 定时器在运行，批量清除
for (var i = 1; i < 99999; i++) {
    window.clearInterval(i);
    window.clearTimeout(i);
}

```

### 8.5 组合反调试绕过（推荐一起使用）

```javascript
// 使用场景：同时覆盖多种无限 debugger 实现方式
(function () {
    // 绕过 Function('debugger') 和 constructor('debugger')
    var _FunctionConstructor = Function.prototype.constructor;
    Function.prototype.constructor = function (arg) {
        if (typeof arg === 'string' && arg.indexOf('debugger') !== -1) {
            return function () {};
        }
        return _FunctionConstructor.apply(this, arguments);
    };

    // 绕过 setInterval(fn, t) 中含 debugger 的函数
    var _setInterval = window.setInterval;
    window.setInterval = function (func, timer) {
        if (typeof func === 'function' && func.toString().indexOf('debugger') !== -1) {
            return null;
        }
        return _setInterval.call(this, func, timer);
    };

    // 绕过 setTimeout(fn, t) 中含 debugger 的函数
    var _setTimeout = window.setTimeout;
    window.setTimeout = function (func, timer) {
        if (typeof func === 'function' && func.toString().indexOf('debugger') !== -1) {
            return null;
        }
        return _setTimeout.call(this, func, timer);
    };
})();

```

---

## 9\. JSON.stringify / JSON.parse Hook

### 9.1 JSON.stringify 和 JSON.parse 完整 Hook

```javascript
// 使用场景：监控请求体序列化（stringify）和响应体反序列化（parse），定位加密参数拼接位置
(function () {
    var _parse = JSON.parse;
    JSON.parse = function (str) {
        console.log('[JSON.parse] input =', str);
        console.log(new Error().stack);
        console.log('------------------------------------------------------------');
        return _parse(str);
    };

    var _stringify = JSON.stringify;
    JSON.stringify = function (obj) {
        console.log('[JSON.stringify] input =', obj);
        console.log(new Error().stack);
        console.log('------------------------------------------------------------');
        return _stringify(obj);
    };
})();

```

### 9.2 JSON Hook 带条件断点

```javascript
// 使用场景：只在序列化/反序列化结果包含特定字段时断下
(function () {
    var TARGET = 'sign'; // 替换为目标字段名
    var _stringify = JSON.stringify;
    JSON.stringify = function (obj) {
        var result = _stringify(obj);
        if (result && result.indexOf(TARGET) !== -1) {
            console.log('[JSON.stringify 命中]', result);
            debugger;
        }
        return result;
    };

    var _parse = JSON.parse;
    JSON.parse = function (str) {
        if (typeof str === 'string' && str.indexOf(TARGET) !== -1) {
            console.log('[JSON.parse 命中]', str);
            debugger;
        }
        return _parse(str);
    };
})();

```

---

## 10\. WebSocket Hook

### 10.1 Proxy 方式 Hook WebSocket（推荐）

```javascript
// 使用场景：监控 WebSocket 连接的所有发送消息、接收消息、连接和关闭事件
(function () {
    var WebSocketProxy = new Proxy(window.WebSocket, {
        construct: function (target, args) {
            console.log('[WebSocket] 连接 URL:', args[0]);
            var ws = new target(...args);

            // 拦截 send（出站消息）
            var _send = ws.send.bind(ws);
            ws.send = function (data) {
                console.log('[WebSocket SEND]', data);
                console.log(new Error().stack);
                return _send(data);
            };

            // 拦截 onmessage（入站消息）
            var _onmessage = ws.onmessage;
            ws.onmessage = function (event) {
                console.log('[WebSocket RECV]', event.data);
                if (_onmessage) return _onmessage.call(this, event);
            };

            // 拦截 close
            var _close = ws.close.bind(ws);
            ws.close = function () {
                console.log('[WebSocket CLOSE]');
                return _close();
            };

            // 拦截 addEventListener（有些页面不用 onmessage，用事件监听）
            var _addEventListener = ws.addEventListener.bind(ws);
            ws.addEventListener = function (eventName, listener, options) {
                if (eventName === 'message') {
                    var _listener = listener;
                    listener = function (event) {
                        console.log('[WebSocket addEventListener message]', event.data);
                        return _listener.call(this, event);
                    };
                }
                return _addEventListener(eventName, listener, options);
            };

            // 保存到全局方便调试
            window._websockets = window._websockets || [];
            window._websockets.push(ws);

            return ws;
        }
    });

    window.WebSocket = WebSocketProxy;
})();

```

### 10.2 wsHook 风格（轻量 Hook，适合快速使用）

```javascript
// 使用场景：简单场景下拦截 WebSocket 消息，可修改发送内容或阻止接收
var wsHook = {};
wsHook.before = null; // function(data, url, ws) { ... return data; }
wsHook.after = null;  // function(event, url, ws) { ... return event; }

(function () {
    var _WebSocket = window.WebSocket;
    window.WebSocket = function (url, protocols) {
        var ws = protocols ? new _WebSocket(url, protocols) : new _WebSocket(url);
        var _send = ws.send.bind(ws);

        ws.send = function (data) {
            if (wsHook.before) {
                data = wsHook.before(data, url, ws);
                if (data === null) return; // 返回 null 则阻止发送
            }
            return _send(data);
        };

        ws.addEventListener('message', function (event) {
            if (wsHook.after) {
                var result = wsHook.after(event, url, ws);
                if (result === null) return; // 返回 null 则阻止触发 onmessage
            }
        });

        return ws;
    };
    window.WebSocket.prototype = _WebSocket.prototype;
})();

// 使用示例
wsHook.before = function (data, url, ws) {
    console.log('[WS SEND]', url, data);
    return data;
};
wsHook.after = function (event, url, ws) {
    console.log('[WS RECV]', url, event.data);
    return event;
};

```

---

## 11\. eval / Function Hook

### 11.1 eval Hook（监控动态代码执行）

```javascript
// 使用场景：监控通过 eval 执行的动态代码，分析混淆代码中的动态生成逻辑
(function () {
    window.__cr_eval = window.eval;
    var myeval = function (src) {
        console.log('[eval]', src);
        console.log('======= eval end =======');
        debugger;
        return window.__cr_eval(src);
    };
    var _myeval = myeval.bind(null);
    _myeval.toString = window.__cr_eval.toString;
    Object.defineProperty(window, 'eval', { value: _myeval });
})();

```

### 11.2 eval 替换为 console.log（只打印不执行）

```javascript
// 使用场景：直接输出 eval 内容而不执行，安全查看动态代码
var _eval_bak = window.eval;
window.eval = console.log;
// 恢复：window.eval = _eval_bak;

```

### 11.3 Function 构造函数 Hook（监控 new Function）

```javascript
// 使用场景：监控通过 new Function(...) 动态创建的函数，常见于 OB 混淆代码
(function () {
    window.__cr_fun = window.Function;
    var myfun = function () {
        var args = Array.prototype.slice.call(arguments, 0, -1).join(',');
        var src = arguments[arguments.length - 1];
        console.log('[Function] args =', args);
        console.log('[Function] body =', src);
        console.log('======= Function end =======');
        debugger;
        return window.__cr_fun.apply(this, arguments);
    };
    myfun.toString = function () {
        return window.__cr_fun + '';
    };
    Object.defineProperty(window, 'Function', { value: myfun });
})();

```

---

## 12\. Canvas 指纹 Hook

### 12.1 Hook toDataURL（拦截 Canvas 导出图像）

```javascript
// 使用场景：拦截 canvas.toDataURL 调用，定位指纹采集代码位置，或返回固定值欺骗检测
(function () {
    var _toDataURL = HTMLCanvasElement.prototype.toDataURL;
    HTMLCanvasElement.prototype.toDataURL = function (type) {
        console.log('[canvas.toDataURL] type =', type);
        console.log('[canvas.toDataURL] canvas size:', this.width, 'x', this.height);
        console.log(new Error().stack);
        debugger; // 定位调用位置后可去掉
        return _toDataURL.apply(this, arguments);
        // 如需返回固定值（欺骗指纹）：
        // return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
    };
})();

```

### 12.2 Hook getImageData（拦截像素数据读取）

```javascript
// 使用场景：拦截 canvas 像素数据读取，阻止指纹采集
(function () {
    var _getImageData = CanvasRenderingContext2D.prototype.getImageData;
    CanvasRenderingContext2D.prototype.getImageData = function (sx, sy, sw, sh) {
        console.log('[canvas.getImageData] rect:', sx, sy, sw, sh);
        console.log(new Error().stack);
        debugger;
        return _getImageData.apply(this, arguments);
    };
})();

```

### 12.3 Canvas 指纹添加噪声（修改像素返回干扰值）

```javascript
// 使用场景：为每次 canvas 采集添加随机噪声，使指纹不固定
(function () {
    var _getImageData = CanvasRenderingContext2D.prototype.getImageData;
    CanvasRenderingContext2D.prototype.getImageData = function (sx, sy, sw, sh) {
        var imageData = _getImageData.apply(this, arguments);
        // 在像素数据中添加微小随机噪声
        for (var i = 0; i < imageData.data.length; i += 4) {
            imageData.data[i] += Math.floor(Math.random() * 3) - 1;     // R
            imageData.data[i + 1] += Math.floor(Math.random() * 3) - 1; // G
            imageData.data[i + 2] += Math.floor(Math.random() * 3) - 1; // B
            // imageData.data[i + 3] 不修改 Alpha 通道
        }
        return imageData;
    };
})();

```

### 12.4 Hook toBlob

```javascript
// 使用场景：拦截通过 toBlob 方式获取 canvas 数据的指纹采集
(function () {
    var _toBlob = HTMLCanvasElement.prototype.toBlob;
    HTMLCanvasElement.prototype.toBlob = function (callback, type, quality) {
        console.log('[canvas.toBlob] type =', type);
        console.log(new Error().stack);
        return _toBlob.apply(this, arguments);
    };
})();

```

---

## 13\. 定时器 Hook

### 13.1 Hook setInterval（监控/过滤定时任务）

```javascript
// 使用场景：查看页面注册了哪些定时任务，过滤含 debugger 的定时器
(function () {
    var _setInterval = window.setInterval;
    window.setInterval = function (func, delay) {
        var funcStr = typeof func === 'function' ? func.toString() : String(func);
        console.log('[setInterval] delay =', delay, '\n  func =', funcStr.substring(0, 100));
        console.log(new Error().stack);
        // 过滤 debugger
        if (funcStr.indexOf('debugger') !== -1) {
            console.log('[setInterval] 拦截含 debugger 的定时器');
            return null;
        }
        return _setInterval.apply(this, arguments);
    };
    window.setInterval.toString = function () {
        return 'function setInterval() { [native code] }';
    };
})();

```

### 13.2 Hook setTimeout

```javascript
// 使用场景：监控延迟执行的任务，追踪延迟触发的加密逻辑
(function () {
    var _setTimeout = window.setTimeout;
    window.setTimeout = function (func, delay) {
        var funcStr = typeof func === 'function' ? func.toString() : String(func);
        if (delay > 0) { // 过滤 delay=0 的短任务，减少噪音
            console.log('[setTimeout] delay =', delay, '\n  func =', funcStr.substring(0, 100));
        }
        if (funcStr.indexOf('debugger') !== -1) {
            console.log('[setTimeout] 拦截含 debugger 的延迟任务');
            return null;
        }
        return _setTimeout.apply(this, arguments);
    };
    window.setTimeout.toString = function () {
        return 'function setTimeout() { [native code] }';
    };
})();

```

### 13.3 Hook requestAnimationFrame

```javascript
// 使用场景：部分反调试使用 requestAnimationFrame 循环，hook 可定位或阻断
(function () {
    var _raf = window.requestAnimationFrame;
    window.requestAnimationFrame = function (callback) {
        var _callback = callback;
        callback = function (timestamp) {
            // console.log('[rAF] callback called');
            return _callback(timestamp);
        };
        return _raf(callback);
    };
})();

```

---

## 14\. DOM 监控

### 14.1 MutationObserver 监控 DOM 变化

```javascript
// 使用场景：监控页面 DOM 的动态变化，检测动态插入的 script 标签或 DOM 注入
(function () {
    var observer = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            // 监控新增节点
            mutation.addedNodes.forEach(function (node) {
                if (node.nodeName === 'SCRIPT') {
                    console.log('[MutationObserver] 新增 script 标签:', node.src || node.textContent.substring(0, 100));
                    console.log(new Error().stack);
                }
                if (node.nodeType === 1) { // ELEMENT_NODE
                    console.log('[MutationObserver] 新增元素:', node.outerHTML ? node.outerHTML.substring(0, 150) : node);
                }
            });
            // 监控属性变化
            if (mutation.type === 'attributes') {
                console.log('[MutationObserver] 属性变化:', mutation.target.tagName,
                    mutation.attributeName, '=', mutation.target.getAttribute(mutation.attributeName));
            }
        });
    });

    observer.observe(document.documentElement, {
        childList: true,    // 监控直接子节点增删
        subtree: true,      // 监控所有后代节点
        attributes: true,   // 监控属性变化
        characterData: true // 监控文本节点变化
    });
})();

```

### 14.2 Hook innerHTML / outerHTML

```javascript
// 使用场景：监控动态 HTML 注入（可用于 XSS 分析或追踪动态渲染）
(function () {
    var prop = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
    var _get = prop.get;
    var _set = prop.set;

    Object.defineProperty(Element.prototype, 'innerHTML', {
        get: function () {
            return _get.call(this);
        },
        set: function (val) {
            console.log('[innerHTML SET]', this.tagName, val.substring(0, 150));
            console.log(new Error().stack);
            return _set.call(this, val);
        }
    });
})();

```

### 14.3 监控特定元素属性变化

```javascript
// 使用场景：监控某个 input 的 value 属性被动态修改
var targetEl = document.querySelector('#captcha-input'); // 替换为目标选择器
if (targetEl) {
    var observer = new MutationObserver(function (mutations) {
        mutations.forEach(function (m) {
            console.log('[DOM 属性变化]', m.attributeName, '=', targetEl.getAttribute(m.attributeName));
        });
    });
    observer.observe(targetEl, { attributes: true });
}

```

### 14.4 HTMLScriptElement.src Hook — 监控动态脚本注入

当页面通过 JS 动态创建 `<script>` 标签加载外部脚本时触发断点。

**方式一：defineProperty（推荐，精确监控 src）**

```javascript
// 拦截所有 script 元素的 src 赋值
Object.defineProperty(HTMLScriptElement.prototype, 'src', {
    set: function (value) {
        console.log('[script.src]', value);
        debugger;  // 触发断点，查看调用栈定位加载来源
    }
});

```

**方式二：Proxy 代理整个原型（监控所有属性）**

```javascript
// 拦截原型上的所有属性赋值，不仅限于 src
HTMLScriptElement.prototype = new Proxy(HTMLScriptElement.prototype, {
    set: function (target, prop, value) {
        console.log('[HTMLScriptElement]', prop, '=', value);
        debugger;
        return Reflect.set(target, prop, value);
    }
});

```

> 两种方式不要同时使用，会产生冲突。推荐用方式一定向监控 src，用方式二时监控所有属性。适用场景：网站动态插入第三方脚本（风控 JS、指纹采集 SDK），定位其加载时机和来源。

---

## 15\. console 保护与重写

### 15.1 冻结 console（防止被网站覆盖）

```javascript
// 使用场景：部分网站会覆盖 console.log 来阻止调试输出，冻结后无法修改
(function () {
    Object.freeze(console);
})();

```

### 15.2 防止 console.log 被重写（检测并恢复）

```javascript
// 使用场景：在页面加载完成后检查 console.log 是否被覆盖，如被覆盖则恢复
(function () {
    var _log = console.log;
    var waiting_time = 3;
    var restored = false;

    function restoreLog() {
        if (console.log.toString() !== 'function log() { [native code] }') {
            console.log = _log;
            console.warn('[保护] console.log 被重写，已恢复原生实现');
        }
        restored = true;
    }

    window.addEventListener('load', restoreLog);
    setTimeout(function () {
        if (!restored) restoreLog();
    }, waiting_time * 1000);
})();

```

### 15.3 阻止 console.clear

```javascript
// 使用场景：防止网站调用 console.clear() 清空调试输出
(function () {
    console.clear = function () {
        console.log('[console.clear 已被禁用]');
    };
})();

```

### 15.4 console.log Hook（记录所有输出）

```javascript
// 使用场景：监控页面内所有 console.log 输出，追踪关键日志
(function () {
    var _log = console.log;
    console.log = function () {
        // 可在此过滤或修改日志
        _log.apply(console, arguments);
        // 可同时上报到自己的服务器：
        // fetch('/log', { method: 'POST', body: JSON.stringify([...arguments]) });
    };
})();

```

---

## 16\. 加密函数定位

### 16.1 通过调用栈定位（通用方法）

```javascript
// 使用场景：在任意位置打印当前调用栈，顺着栈向上找到加密函数入口
console.log(new Error().stack);
// 或者
console.trace('当前调用栈');

```

### 16.2 搜索关键词定位加密函数

```
// 在 Sources 面板 Ctrl+Shift+F 全局搜索以下特征词：
// MD5 特征：1732584193 / 271733879 / md5
// SHA1 特征：1009589776 / sha1
// SHA256 特征：sha256 / 0x6a09e667
// Base64 特征：ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno
// AES 特征：AES / aes / CryptoJS
// RSA 特征：RSAKey / BigInteger / PublicKey

```

### 16.3 Hook CryptoJS 加密方法（定位 AES/DES/RC4 等）

```javascript
// 使用场景：在引入了 CryptoJS 的页面，Hook 所有对称加密方法
(function () {
    function hookCryptoMethod(obj, methodName) {
        if (!obj || !obj[methodName]) return;
        var _original = obj[methodName];
        obj[methodName] = function () {
            console.log('[CryptoJS.' + methodName + '] args:', ...arguments);
            console.log(new Error().stack);
            var result = _original.apply(this, arguments);
            console.log('[CryptoJS.' + methodName + '] result:', result.toString ? result.toString() : result);
            return result;
        };
    }

    // 等待 CryptoJS 加载后执行
    var timer = setInterval(function () {
        if (typeof CryptoJS !== 'undefined') {
            clearInterval(timer);
            if (CryptoJS.AES) {
                hookCryptoMethod(CryptoJS.AES, 'encrypt');
                hookCryptoMethod(CryptoJS.AES, 'decrypt');
            }
            if (CryptoJS.DES) {
                hookCryptoMethod(CryptoJS.DES, 'encrypt');
                hookCryptoMethod(CryptoJS.DES, 'decrypt');
            }
            if (CryptoJS.TripleDES) {
                hookCryptoMethod(CryptoJS.TripleDES, 'encrypt');
                hookCryptoMethod(CryptoJS.TripleDES, 'decrypt');
            }
            if (CryptoJS.RC4) {
                hookCryptoMethod(CryptoJS.RC4, 'encrypt');
                hookCryptoMethod(CryptoJS.RC4, 'decrypt');
            }
            if (CryptoJS.MD5) hookCryptoMethod(CryptoJS, 'MD5');
            if (CryptoJS.HmacSHA256) {
                hookCryptoMethod(CryptoJS, 'HmacSHA256');
                hookCryptoMethod(CryptoJS, 'HmacSHA1');
            }
            console.log('[CryptoJS Hook] 已注入所有加密方法');
        }
    }, 100);
})();

```

### 16.4 Hook JSEncrypt RSA 加密

```javascript
// 使用场景：页面使用 JSEncrypt 进行 RSA 加密时，拦截 encrypt 方法获取明文和密文
(function () {
    var timer = setInterval(function () {
        if (typeof JSEncrypt !== 'undefined') {
            clearInterval(timer);
            var _encrypt = JSEncrypt.prototype.encrypt;
            JSEncrypt.prototype.encrypt = function (data) {
                console.log('[JSEncrypt.encrypt] 明文:', data);
                var result = _encrypt.call(this, data);
                console.log('[JSEncrypt.encrypt] 密文:', result);
                console.log(new Error().stack);
                return result;
            };
            console.log('[JSEncrypt Hook] 已注入 RSA encrypt');
        }
    }, 100);
})();

```

### 16.5 Hook window.btoa / atob（Base64）

```javascript
// 使用场景：监控 Base64 编解码，定位数据编码位置
(function () {
    var _btoa = window.btoa;
    window.btoa = function (str) {
        console.log('[btoa] input =', str);
        console.log(new Error().stack);
        return _btoa.call(this, str);
    };

    var _atob = window.atob;
    window.atob = function (str) {
        var result = _atob.call(this, str);
        console.log('[atob] input =', str, ', output =', result);
        console.log(new Error().stack);
        return result;
    };
})();

```

### 16.6 XHR 断点触发后顺着调用栈追溯（流程说明）

```
// 逆向定位流程：
// 1. 用 XHR URL Hook 或 XHR Header Hook 让请求断下
// 2. 打开 DevTools -> Sources -> Call Stack 面板
// 3. 从栈底往上逐帧查看，跳过 jQuery/axios/webpack 等库的帧
// 4. 找到业务代码帧，双击定位到对应源码行
// 5. 在那一行附近搜索参数名（如 sign、token），找到生成逻辑
// 6. 在生成处打断点，重放请求，单步执行追踪

```

---

## 17\. 其他实用 Hook

### 17.1 阻止 window.close 和 history 跳转

```javascript
// 使用场景：阻止目标网站动态关闭标签页或强制跳转历史
(function () {
    window.close = function () {};
    window.history.go = function () {};
    window.history.back = function () {};
})();

```

### 17.2 固定 window 尺寸（绕过尺寸检测）

```javascript
// 使用场景：某些网站通过检测窗口尺寸判断是否打开了 DevTools，固定尺寸可绕过
(function () {
    var FIXED_HEIGHT = 660;
    var FIXED_WIDTH = 1366;

    var hProp = Object.getOwnPropertyDescriptor(window, 'innerHeight');
    Object.defineProperty(window, 'innerHeight', {
        get: function () { return FIXED_HEIGHT; },
        set: function () { if (hProp && hProp.set) hProp.set.call(window, FIXED_HEIGHT); }
    });

    var wProp = Object.getOwnPropertyDescriptor(window, 'innerWidth');
    Object.defineProperty(window, 'innerWidth', {
        get: function () { return FIXED_WIDTH; },
        set: function () { if (wProp && wProp.set) wProp.set.call(window, FIXED_WIDTH); }
    });
})();

```

### 17.3 Hook Image.src（监控埋点和追踪像素）

```javascript
// 使用场景：某些埋点通过 new Image().src = url 发请求，hook 可截获
(function () {
    var prop = Object.getOwnPropertyDescriptor(Image.prototype, 'src');
    var _nativeSet = prop.set;
    Object.defineProperty(Image.prototype, 'src', {
        get: prop.get,
        set: function (url) {
            console.log('[Image.src]', url);
            console.log(new Error().stack);
            return _nativeSet.call(this, url);
        }
    });
})();

```

### 17.4 Hook window.open

```javascript
// 使用场景：阻止页面自动弹出新窗口，或监控弹窗 URL
(function () {
    var _open = window.open;
    window.open = function (url) {
        console.log('[window.open]', url);
        console.log(new Error().stack);
        // return _open.call(this, url); // 注释掉则阻止弹窗
    };
})();

```

### 17.5 格式化检测绕过（防检测 toString）

```javascript
// 使用场景：部分网站检测函数是否被格式化（代码是否被美化），通过伪装 toString 绕过
// 原理：让被 hook 的函数的 toString 返回原生代码字符串，欺骗检测
function makeNative(fn, nativeFn) {
    fn.toString = function () {
        return nativeFn.toString();
    };
    return fn;
}

// 示例：Hook eval 并伪装
var _eval = window.eval;
window.eval = makeNative(function (src) {
    console.log('[eval]', src);
    return _eval(src);
}, _eval);

```

---

## 18\. 环境 Hook 模块（Node.js 补环境）

在 Node.js 本地运行浏览器加密 JS 时使用，补全浏览器环境并对全局对象挂载深度 Proxy。

**功能列表：**

| 功能       | 说明                                        |
| -------- | ----------------------------------------- |
| 深度 Proxy | 对任意对象/函数做递归代理，追踪 get/set/has/call         |
| 浏览器环境模拟  | 补全 window/navigator/document/localStorage |
| 定时器拦截    | 记录 setTimeout/setInterval 调用              |
| 全局对象代理   | 对 window/document/navigator 等批量挂载 Proxy   |
| 防重复安装    | 通过 \_\_envHookInstalled 标志防止重复执行          |

### 18.1 配置项

```javascript
const CONFIG = {
    enableStack: true,   // 是否在日志中输出调用栈
    deepProxy: true,     // 是否对 get 返回值递归代理（深度追踪，会产生大量日志）
    logGet: true,        // 记录属性读取
    logSet: true,        // 记录属性写入
    logHas: false,       // 记录 in 操作符检测（通常不需要）
    logCall: true        // 记录函数调用
};

```

### 18.2 核心代理 createProxy()

对任意对象/函数递归包裹 Proxy，追踪所有操作并记录访问路径。WeakMap 缓存避免对同一对象重复代理。

```javascript
function createProxy(target, path = "") {
    if (!isObject(target) && typeof target !== "function") return target;
    if (proxyCache.has(target)) return proxyCache.get(target);

    const handler = {
        get(target, property, receiver) {
            const value = Reflect.get(target, property, receiver);
            if (CONFIG.logGet) {
                console.log({ type: "get", path, property, valueType: typeof value, stack: safeStack() });
            }
            // deepProxy: 对返回值也代理，可以追踪链式调用
            if (CONFIG.deepProxy) return createProxy(value, `${path}.${String(property)}`);
            return value;
        },
        set(target, property, value, receiver) {
            if (CONFIG.logSet) console.log({ type: "set", path, property, value });
            return Reflect.set(target, property, value, receiver);
        },
        has(target, property) {
            if (CONFIG.logHas) console.log({ type: "has", path, property });
            return Reflect.has(target, property);
        },
        apply(target, thisArg, args) {
            if (CONFIG.logCall) console.log({ type: "call", path, args, stack: safeStack() });
            return Reflect.apply(target, thisArg, args);
        }
    };

    const proxy = new Proxy(target, handler);
    proxyCache.set(target, proxy);
    return proxy;
}

```

### 18.3 浏览器环境模拟 patchBrowserEnv()

```javascript
function patchBrowserEnv() {
    if (!global.window) global.window = global;

    if (!global.navigator) {
        global.navigator = {
            userAgent: "Mozilla/5.0",
            platform: "Win32",
            languages: ["zh-CN", "zh"],
            webdriver: false       // 关键：不能为 true，否则触发反爬检测
        };
    }

    if (!global.document) {
        global.document = {
            cookie: "",
            createElement: () => ({})
        };
    }

    if (!global.localStorage) {
        const store = {};
        global.localStorage = {
            getItem:    k       => store[k],
            setItem:    (k, v)  => store[k] = v,
            removeItem: k       => delete store[k]
        };
    }
}

```

### 18.4 完整代码

将此代码放在目标 JS 文件最顶部，在 `require` 或 `eval` 之前执行。

```javascript
(function () {
  if (global.__envHookInstalled) return;
  global.__envHookInstalled = true;

  const CONFIG = {
    enableStack: true,
    deepProxy: true,
    logGet: true,
    logSet: true,
    logHas: false,
    logCall: true
  };

  const proxyCache = new WeakMap();

  function safeStack() {
    if (!CONFIG.enableStack) return null;
    return new Error().stack;
  }

  function isObject(v) {
    return typeof v === "object" && v !== null;
  }

  function createProxy(target, path = "") {
    if (!isObject(target) && typeof target !== "function") return target;
    if (proxyCache.has(target)) return proxyCache.get(target);

    const handler = {
      get(target, property, receiver) {
        const value = Reflect.get(target, property, receiver);
        if (CONFIG.logGet) {
          console.log({ type: "get", path, property, valueType: typeof value, stack: safeStack() });
        }
        if (CONFIG.deepProxy) return createProxy(value, `${path}.${String(property)}`);
        return value;
      },
      set(target, property, value, receiver) {
        if (CONFIG.logSet) console.log({ type: "set", path, property, value });
        return Reflect.set(target, property, value, receiver);
      },
      has(target, property) {
        if (CONFIG.logHas) console.log({ type: "has", path, property });
        return Reflect.has(target, property);
      },
      apply(target, thisArg, args) {
        if (CONFIG.logCall) console.log({ type: "call", path, args, stack: safeStack() });
        return Reflect.apply(target, thisArg, args);
      }
    };

    const proxy = new Proxy(target, handler);
    proxyCache.set(target, proxy);
    return proxy;
  }

  function patchBrowserEnv() {
    if (!global.window) global.window = global;
    if (!global.navigator) {
      global.navigator = {
        userAgent: "Mozilla/5.0", platform: "Win32",
        languages: ["zh-CN", "zh"], webdriver: false
      };
    }
    if (!global.document) {
      global.document = { cookie: "", createElement: () => ({}) };
    }
    if (!global.localStorage) {
      const store = {};
      global.localStorage = {
        getItem: k => store[k],
        setItem: (k, v) => store[k] = v,
        removeItem: k => delete store[k]
      };
    }
  }

  function patchTimers() {
    const _setTimeout  = global.setTimeout;
    const _setInterval = global.setInterval;
    global.setTimeout  = (fn, delay, ...args) => { console.log("setTimeout", delay);  return _setTimeout(fn, delay, ...args);  };
    global.setInterval = (fn, delay, ...args) => { console.log("setInterval", delay); return _setInterval(fn, delay, ...args); };
  }

  function hookGlobals(list) {
    for (const name of list) {
      if (!(name in global)) continue;
      const original = global[name];
      Object.defineProperty(global, name, {
        configurable: true, enumerable: true,
        get() { return createProxy(original, name); }
      });
    }
  }

  patchBrowserEnv();
  patchTimers();
  hookGlobals(["window", "document", "navigator", "location", "history", "screen", "localStorage", "sessionStorage"]);

  console.log("Environment hook installed");
})();

```

> 当 `deepProxy: true` 时，链式属性访问会产生大量日志。建议先设置 `CONFIG.deepProxy = false; CONFIG.logGet = false;` 只看 set 和 call，找到目标后再开启深度追踪。

---

## 场景速查表

| 目标                    | 推荐 Hook                                        |
| --------------------- | ---------------------------------------------- |
| 定位 Cookie 生成          | Cookie Hook（6.1 / 6.2）                         |
| 定位 XHR 请求加密参数         | XHR send Hook（4.1） + JSON.stringify Hook（9.1）  |
| 定位请求头（X-Sign 等）生成     | XHR Header Hook（4.3）                           |
| 定位 fetch 请求参数         | fetch 请求拦截（5.2）                                |
| 定位 localStorage token | localStorage 条件断点 Hook（7.3）                    |
| 监控 WebSocket 消息       | WebSocket Proxy Hook（10.1）                     |
| 绕过无限 debugger         | 组合反调试绕过（8.5）                                   |
| 分析动态混淆代码              | eval Hook（11.1） + Function Hook（11.3）          |
| 阻止 Canvas 指纹采集        | toDataURL Hook（12.1） + getImageData Hook（12.2） |
| 定位 AES/DES 加密调用       | CryptoJS Hook（16.3）                            |
| 定位 RSA 加密调用           | JSEncrypt Hook（16.4）                           |
| 追踪 Base64 编码          | btoa/atob Hook（16.5）                           |
| 监控 DOM 动态注入           | MutationObserver（14.1）                         |
| 监控动态脚本加载              | HTMLScriptElement.src Hook（14.4）               |
| 防止控制台被清空              | console.clear 阻止（15.3）                         |
| Node.js 本地补环境         | 环境 Hook 模块（18.4）                               |
| 监控对象全部属性访问路径          | watch()（1.4）                                   |

---

## 最佳实践

**Hook 必须在目标代码执行前注入**：通过 Tampermonkey 设 `@run-at document-start`，或在 DevTools Sources → Snippets 中在页面加载前手动执行，否则原生函数已被调用，Hook 无法捕获历史调用。

**按需选择 Hook 粒度：属性描述符 vs Proxy**：对单个属性或已知键名用 `Object.defineProperty`，对整个对象的所有属性访问用 `Proxy`。Proxy 开销更高但覆盖面广，不要对高频调用对象无差别使用 Proxy。

```javascript
// 仅 Hook 一个属性（开销低）
Object.defineProperty(document, 'cookie', {
    get() { console.trace('cookie read'); return cookieValue; },
    set(v) { console.trace('cookie write', v); cookieValue = v; }
});

// Hook 整个对象所有访问（覆盖广但开销高）
window = new Proxy(window, { get(t, k) { console.log('window.' + k); return t[k]; } });

```

**条件 Hook 减少干扰**：只在参数满足特定条件时插入 `debugger`，避免正常调用淹没真正的加密调用。

```javascript
const orig = JSON.stringify;
JSON.stringify = function(val) {
    if (JSON.stringify(val).includes('sign')) debugger; // 只在有 sign 字段时断点
    return orig.apply(this, arguments);
};

```

**恢复原函数要保存引用**：Hook 前先把原函数存到变量，调试完后能恢复，避免影响页面正常功能。

```javascript
const _origFetch = window.fetch;
window.fetch = function(...args) {
    console.log('fetch:', args[0]);
    return _origFetch.apply(this, args);
};
// 调试完后：window.fetch = _origFetch;

```

**用快速索引表（文末）定位目标 Hook**：本文末尾有按场景分类的快速查找表，遇到未知加密时先查表确定 Hook 类型，再找对应章节。

---

## 常见陷阱

### 陷阱：Hook 后目标函数返回 undefined 导致页面报错

**现象：** 注入 Hook 后，页面报错 `Cannot read properties of undefined`，原功能失效。

**原因：** Hook 函数忘记 `return orig.apply(this, arguments)`，导致原函数不被调用，返回值丢失。

**解决：** 任何替换原函数的 Hook，最后一行必须 `return` 原函数调用结果。

```javascript
// 错误：没有 return
window.btoa = function(s) {
    console.log('btoa:', s);
    orig_btoa(s); // 没有 return，调用方拿到 undefined
};

// 正确：return 原函数结果
window.btoa = function(s) {
    console.log('btoa:', s);
    return orig_btoa.call(this, s);
};

```

### 陷阱：Hook 不生效——目标代码用的是局部变量副本

**现象：** 已替换 `window.btoa`，但加密函数调用的 `btoa` 没有触发 Hook。

**原因：** 目标代码在模块作用域顶部执行了 `const btoa = window.btoa`，后续使用局部变量，Hook 替换 `window.btoa` 对它无效。

**解决：** 必须在目标代码执行之前注入 Hook；或改用 `Object.defineProperty(globalThis, 'btoa', ...)` 并设 `configurable: false` 阻止被覆盖。

### 陷阱：`debugger` 在 Hook 里无效

**现象：** Hook 函数里写了 `debugger`，但 DevTools 不暂停。

**原因：** DevTools 设置了"Pause on caught exceptions"且 `debugger` 被异常捕获；或者 DevTools 未打开，`debugger` 语句静默跳过；或者该脚本是 Worker 线程，需要在 Workers 面板调试。

**解决：** 确保 DevTools 已打开再刷新；检查是否在 Worker 上下文（URL 搜索 `worker.js`）；尝试换成 `console.trace()` 先验证 Hook 是否被触发。

---

## 参见

- [hook](https://blog.vercanti.com/hook-dai-ma-duan/)
- [补环境](https://blog.vercanti.com/bu-huan-jing/)
- [js逆向调试技巧](https://blog.vercanti.com/js-ni-xiang-diao-shi-ji-qiao/)
- [Babel AST入门](https://blog.vercanti.com/babel-ast-ru-men/)
- [调试辅助](https://blog.vercanti.com/diao-shi-fu-zhu-dai-ma-duan/)