> ## 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.

# 调试辅助代码段
- URL: https://blog.vercanti.com/diao-shi-fu-zhu-dai-ma-duan/
- Published: 2026-08-28T14:35:02.000Z
- Updated: 2026-08-28T14:57:53.000Z
- Description: 常用于 Console 面板快速调试，或注入到页面辅助分析的工具代码。 在任意位置插入，打印当前的函数调用链，不中断执行。 包装目标函数，记录每次调用的参数和返回值。 使用 Object.defineProperty 拦截属性的读写。 快速查找对象中所有函数类型的属性。 在嵌套对象中递归查找包含特定关键字的属性名。 有些网站在 JS 中用 console.log 输出加密中间值，但因日志太多难以查找，可以过滤关键词。 有些反调试代码通过检测函数的 toString() 结果来判断是否被 Hook。 在 XHR 断点命中后，一次性打印所有请求信息。 在 C
- Author: yellowdog
- Tags: js逆向, 代码段

> 官方文档：<https://developer.chrome.com/docs/devtools/console/api/>

常用于 Console 面板快速调试，或注入到页面辅助分析的工具代码。

---

## 打印调用堆栈

在任意位置插入，打印当前的函数调用链，不中断执行。

```javascript
console.trace('当前调用栈');

```

---

## 监控函数调用

包装目标函数，记录每次调用的参数和返回值。

```javascript
// 监控指定函数
function monitorFunc(obj, funcName) {
    var original = obj[funcName];
    obj[funcName] = function() {
        console.log(`[调用] ${funcName} 参数:`, JSON.stringify(arguments));
        var result = original.apply(this, arguments);
        console.log(`[返回] ${funcName} 结果:`, JSON.stringify(result));
        return result;
    };
}

// 使用示例
monitorFunc(window, 'btoa');
monitorFunc(JSON, 'stringify');

```

---

## 监控对象属性变化

使用 `Object.defineProperty` 拦截属性的读写。

```javascript
function watchProperty(obj, prop) {
    var _val = obj[prop];
    Object.defineProperty(obj, prop, {
        get: function() {
            console.log(`[GET] ${prop} =`, _val);
            return _val;
        },
        set: function(newVal) {
            console.log(`[SET] ${prop} =`, newVal);
            console.trace();
            _val = newVal;
        },
        configurable: true
    });
}

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

```

---

## 遍历对象找函数

快速查找对象中所有函数类型的属性。

```javascript
function findFunctions(obj, name) {
    name = name || 'obj';
    Object.keys(obj).forEach(function(key) {
        if (typeof obj[key] === 'function') {
            console.log(`${name}.${key}`);
        }
    });
}

findFunctions(window._utils, 'window._utils');

```

---

## 深度搜索对象属性

在嵌套对象中递归查找包含特定关键字的属性名。

```javascript
function deepSearch(obj, keyword, path, depth) {
    path = path || 'root';
    depth = depth || 0;
    if (depth > 5) return;  // 避免循环引用死循环

    try {
        Object.keys(obj).forEach(function(key) {
            var fullPath = path + '.' + key;
            if (key.toLowerCase().includes(keyword.toLowerCase())) {
                console.log(fullPath, '=', obj[key]);
            }
            if (obj[key] && typeof obj[key] === 'object') {
                deepSearch(obj[key], keyword, fullPath, depth + 1);
            }
        });
    } catch(e) {}
}

// 在 window 中查找含 'sign' 的属性
deepSearch(window, 'sign');

```

---

## 拦截 console.log

有些网站在 JS 中用 console.log 输出加密中间值，但因日志太多难以查找，可以过滤关键词。

```javascript
(function() {
    var _log = console.log.bind(console);
    console.log = function() {
        var args = Array.from(arguments);
        var str = args.join(' ');
        if (str.includes('sign') || str.includes('token')) {
            _log.apply(console, ['[过滤命中]', ...args]);
            debugger;
        } else {
            _log.apply(console, args);
        }
    };
})();

```

---

## 还原 toString 检测

有些反调试代码通过检测函数的 `toString()` 结果来判断是否被 Hook。

```javascript
// 让被 Hook 的函数的 toString 返回原生代码字符串
function fakeNative(func) {
    func.toString = function() {
        return 'function ' + func.name + '() { [native code] }';
    };
    return func;
}

// 使用示例
window.XMLHttpRequest.prototype.open = fakeNative(function open(method, url) {
    // Hook 逻辑
});

```

---

## 获取页面所有请求参数

在 XHR 断点命中后，一次性打印所有请求信息。

```javascript
// 在 XHR send 断点处执行
(function() {
    var xhrs = performance.getEntriesByType('resource');
    xhrs.filter(e => e.initiatorType === 'xmlhttprequest').forEach(function(e) {
        console.log(e.name);
    });
})();

```

---

## 复制变量到剪贴板

在 Console 中快速复制当前作用域的变量值。

```javascript
// 在断点处执行，复制变量到剪贴板
copy(JSON.stringify(signData));
// 或者
copy(btoa(JSON.stringify(encryptedResult)));

```

---

## 格式化输出对象

```javascript
// 替代 console.log，更清晰地显示嵌套对象
console.dir(complexObject, { depth: null });

// 以表格形式展示数组
console.table(arrayOfObjects);

```

---

## 时间戳快速转换

```javascript
// 当前时间戳（秒）
Math.floor(Date.now() / 1000)

// 当前时间戳（毫秒）
Date.now()

// 时间戳转可读时间
new Date(1700000000 * 1000).toLocaleString()

// 可读时间转时间戳
new Date('2024-01-01 00:00:00').getTime() / 1000

```

---

## Base64 互转

```javascript
// 编码
btoa('hello world')
btoa(unescape(encodeURIComponent('中文')))  // 支持中文

// 解码
atob('aGVsbG8gd29ybGQ=')
decodeURIComponent(escape(atob('5Lit5Zu9')))  // 解码中文

// Node.js 版本
Buffer.from('hello').toString('base64')
Buffer.from('aGVsbG8=', 'base64').toString()

```

---

## URL 编解码

```javascript
// 编码
encodeURIComponent('key=value&foo=bar')

// 解码
decodeURIComponent('key%3Dvalue%26foo%3Dbar')

// 解析 URL 参数
Object.fromEntries(new URLSearchParams('a=1&b=2'))
// { a: '1', b: '2' }

```

---

## 最佳实践

**优先用条件断点替代打印调用栈**：`console.trace()` 在调用频繁的函数里会刷屏，导致有效信息被淹没。在 DevTools Sources 面板对该函数入口设条件断点（右键行号 → Add conditional breakpoint），只在满足特定条件时暂停，效率更高。

**`JSON.parse(JSON.stringify(obj))` 快速深拷贝快照**：在 Hook 回调里打印对象时，对象可能在后续被修改。用 `JSON.parse(JSON.stringify(obj))` 先快照再打印，避免在 Console 展开时看到的是修改后的值。

```javascript
const snap = JSON.parse(JSON.stringify(complexObj));
console.log(snap); // 打印的是当前时刻的副本

```

**用 `copy()` 把大对象导出到剪贴板**：DevTools Console 里调用 `copy(data)` 可以把任意对象序列化为 JSON 复制到剪贴板，省去手动复制长字符串的麻烦，方便粘到编辑器进一步分析。

**用 `$_` 引用上一次 Console 表达式的结果**：连续操作时不需要反复把结果赋给变量，`$_` 始终指向最近一次表达式求值的结果。

```javascript
fetch('/api/user').then(r => r.json()) // 执行后 $_ 是 Promise
// 下一行输入
$_.then(console.log) // 打印 JSON 结果

```

**堆栈过深时用 `Error().stack` 代替 `console.trace()`**：`console.trace()` 输出格式因浏览器而异，而 `new Error().stack` 返回字符串，可以 `includes()` 过滤或 `.split('\n').slice(1, 6)` 只取前几层，便于程序化处理。

---

## 常见陷阱

### 陷阱：Console 打印对象显示的是修改后的值

**现象：** `console.log(obj)` 展开后，看到的属性值和打印时刻不符，像是"预言"了后续修改。

**原因：** Chrome Console 对对象是延迟求值的：展开前对象已经被后续代码修改，展开时看到的是最新状态。

**解决：** 用 `JSON.parse(JSON.stringify(obj))` 深拷贝后再打印，或用 `console.log(JSON.stringify(obj))` 打印字符串。

### 陷阱：`console.trace()` 在高频函数里导致页面卡死

**现象：** 注入 `console.trace()` 后页面响应变慢甚至崩溃，Console 刷出数万条堆栈。

**原因：** 目标函数每秒调用数百次，`console.trace()` 每次都要收集完整调用栈并渲染 DOM，开销巨大。

**解决：** 改用条件断点（`callCount++ > 100 && false`），或在调用前加计数器，只打印前几次。

```javascript
let _count = 0;
const orig = target.fn;
target.fn = function() {
    if (_count++ < 3) console.trace('fn called');
    return orig.apply(this, arguments);
};

```

### 陷阱：`encodeURIComponent` 与 `btoa` 混淆

**现象：** 调试时发现 URL 参数是 Base64 还是 URL 编码不确定，用错函数导致无法还原。

**原因：** URL 编码结果以 `%` 开头（如 `%7B`），Base64 结果只含 `A-Za-z0-9+/=`（标准）或 `-_`（URL safe 变体），两者形式不同但都常见于请求参数。

**解决：** 先观察字符集：含 `%` 是 URL 编码，纯字母数字且末尾可能有 `=` 是 Base64。用 `decodeURIComponent` 解 URL 编码，`atob` 解 Base64。

---

## 参见

- [hook](https://blog.vercanti.com/hook-dai-ma-duan/)
- [js逆向调试技巧](https://blog.vercanti.com/js-ni-xiang-diao-shi-ji-qiao/)
- [代码片段大全](https://blog.vercanti.com/js-ni-xiang-hook-yu-dai-ma-pian-duan-da-quan/)
- [补环境](https://blog.vercanti.com/bu-huan-jing/)