第 13 章 前端 JS 逆向
JS 逆向 = 找到网页里的签名/加密参数怎么算出来的。目标不是"破解"而是复现:把浏览器里的算法搬回 Node,让你能自己生成合法参数。
📍 知识点地图 | 主题:前端JS逆向 | 前置:第9章 | 后续:第100章 | 核心概念:Observe-Capture-Rebuild、补环境、AST
13.1 核心原则(Observe-first)
Observe-first 先观察,不猜环境
Hook-preferred 优先 Hook,不盲目断点
Breakpoint-last 断点作为最后手段
Rebuild-oriented 一切为了本地复现
Evidence-first 证据先行,每个补丁有依据
13.2 五阶段工作流(本仓库方法论核心)
阶段 1: Observe(观察)
目标:确认目标请求、相关脚本、候选函数。
1. 打开目标页面
2. 看网络请求列表,找到目标请求(带签名的那个)
3. 看请求的 Initiator(调用来源)
4. 列出页面脚本,缩小范围
5. 产出: 目标 URL、initiator 线索、可疑脚本 URL
阶段 2: Capture(采样)
目标:最小侵入地拿到参数样例、调用顺序、运行时证据。
1. break_on_xhr 在 XHR 发送时断下
2. 看调用栈/暂停状态
3. 定位到生成签名的函数
4. 在函数入口/出口打印参数和返回值
阶段 3: Rebuild(重建)
目标:把页面证据整理成 Node 可跑的复现代码。
规则:
- 补环境必须基于页面观测证据
- 不允许空想式补 window/document/navigator/crypto/storage
- 每次只记录一个最小因果补丁决策
阶段 4: Patch(补环境)
目标:按报错和"第一个差异点"驱动补环境,直到本地稳定产出目标参数。
规则:
- 先看缺什么,再补什么
- 一次只做一个最小补丁
- 每次补丁后立即复测
- 每次补丁写入任务记录
阶段 5: DeepDive(深挖)
目标:本地跑通后做去混淆、控制流还原、业务逻辑提纯。
- 任务只出签名 → 此阶段可跳过
- 要长期复用算法 → 必须做 AST 去混淆 + 逻辑还原
13.3 常用工具面
| 能力 | 工具 |
|---|---|
| 网络请求观察 | DevTools / anything-analyzer / jshookmcp |
| XHR 断点 | DevTools / jshookmcp break_on_xhr |
| 脚本源码 | list_scripts / get_script_source / search_in_sources |
| 运行时 Hook | jshookmcp evaluate_script |
| 补环境复现 | Node + jsdom/自制环境 |
| AST 去混淆 | Babel / webcrack / jsnice |
| 请求重放 | Burp / mitmproxy / 自己写 fetch |
13.4 定位签名函数的黄金链路
1. 找目标请求 → Initiator 里往上翻调用栈
2. 常见藏身处:
- 拦截器层(axios/fetch 拦截器)
- 统一封装 request.js
- webpack chunk 里的工具函数
- 独立加密 JS(加密参数生成)
3. 断在 XHR send/header 设置处,往回看参数来源
4. 打印关键函数入参出参(Hook 优先)
5. SourceMap 有就恢复源码(有时 map 直接公开)
13.5 补环境实战要点
Node 里跑浏览器代码常见的缺失:window document navigator localStorage crypto。
// 最小补丁示例(有依据地补!)
global.window = global;
global.navigator = { userAgent: "Mozilla/5.0 ..." };
global.document = { createElement: () => ({ getContext: () => null }) };
// crypto: Node 20+ 有 globalThis.crypto,老代码用 require("crypto") 填
关键纪律:页面观察证据显示什么,才补什么。逐字段补,逐字段验证。
13.6 AST 去混淆入门
混淆常见形态:
- 字符串数组 + 下标访问(数组化字符串)
- 控制流平坦化
- 死代码/冗余包裹
- 变量名混淆(对逻辑无影响,可跳过)
工具链: Babel parser → transform → generator
思路: 提取字符串数组 → 全局替换下标引用 → 手动化简主要函数
13.7 常见前端签名模式
| 模式 | 识别 | 复现 |
|---|---|---|
| MD5/SHA1 拼接 | 字符串拼接 + 标准哈希 | crypto-js/node crypto 复现 |
| HMAC | 固定 key | crypto.createHmac |
| AES 加密参数 | crypto-js AES | 找 key 和 iv(常硬编码或可预测) |
| RSA 加密 | JSEncrypt | 找公钥(通常明文在 JS 里) |
| 时间戳+随机数 | 服务端校验 | 模拟时间戳格式 |
| 自定义混淆算法 | 乱函数名 | AST 去混淆还原 |
| 风控指纹 | 设备信息采集 | 补环境模拟指纹 |
13.8 常见失败与回退
□ 页面一直 429/验证码 → 检查频率/代理(ProxyCat 轮换 IP)
□ Hook 不到函数 → 换 breakpoint / 看 SourceMap / 全局搜特征字符串
□ 本地跑不通 → 对比页面与 Node 输出的第一个差异点(first divergence)
□ 混淆太重 → 换 AST 方案 / 直接浏览器内执行复现(走 Playwright)
动手练习
- 找一个带签名参数的公开网站(或教学站),完成 Observe → Capture:定位签名函数。
- 把签名函数带回 Node,用补环境跑通,产出和你浏览器里一样的参数。
- 用 Babel 对一段数组化字符串混淆代码去混淆。
- 练习证据纪律:每个补丁都记录"为什么补"。
深入阅读
- 仓库:
skills/js-reverse/SKILL.md(五阶段工作流总纲) - 仓库:
skills/js-reverse/references/env-patching.md(补环境方法论) - 仓库:
skills/js-reverse/references/local-rebuild.md/node-env-rebuild.md(本地复现) - 仓库:
skills/js-reverse/references/ast-deobfuscation.md(AST 去混淆) - 仓库:
skills/js-reverse/references/instrumentation.md/fallbacks.md - 实战案例:
skills/field-journal/seed-004_js-sign-webpack.md、2026-07-05_dsl-vm-captcha-reverse.md