feat(v2.5.0): upgrade to four-axe JSVMP methodology + MCP v0.4.0 alignment
- Add fourth axe: source-level instrumentation via instrument_jsvmp_source - Add new reference: references/jsvmp-source-instrumentation.md - Align MCP tool count 52 -> 65 across SKILL.md / README.md / mcp-cookbook.md - New scenario 11: universal JSVMP source instrumentation (Rui Shu / Akamai / webmssdk) - Enhance scenario 2 with analyze_cookie_sources for cookie attribution - Extend experience rules from 22 to 27 - New case skeleton: cases/universal-vmp-source-instrumentation.md - New case: cases/jsvmp-ruishu6-cookie-412-sdenv.md - Update jsvmp-analysis.md from three-axe to four-axe methodology - Document new presets 'cookie' and 'runtime_probe' - Document pre_inject_hooks / via_blank / reload_with_hooks for first-screen challenges - Add troubleshooting section for 412/403 first-screen challenges
This commit is contained in:
+152
-43
@@ -8,7 +8,9 @@ JSVMP(JavaScript Virtual Machine Protection)是一种高级 JS 代码保护
|
||||
- 代码以字节码数组 + 解释器循环的形式运行
|
||||
- 常见于瑞数、极验、某数等商业级反爬方案
|
||||
|
||||
**核心原则:不反编译字节码,用行为追踪法(Hook / 插桩 / 日志分析)从 I/O 两端夹逼定位签名逻辑。**
|
||||
**核心原则:不反编译字节码,用行为追踪法(Hook / 插桩 / 日志分析 / 源码级插桩四板斧)从 I/O 两端夹逼 + 中间层观察定位签名逻辑。**
|
||||
|
||||
> **v2.5.0 更新**:第四板斧「源码级插桩」由 camoufox-reverse MCP v0.4.0 的 `instrument_jsvmp_source` 提供支持,在 HTTP 层改写 VMP 源码,对每个 `obj[key]` 和 `fn(args)` 插入 tap,捕获 VM 内部 switch/case 调度——是瑞数 5/6、Akamai sensor_data、webmssdk、obfuscator.io 这类"VM 自包含"场景的通用武器。详见 [`jsvmp-source-instrumentation.md`](./jsvmp-source-instrumentation.md)。
|
||||
|
||||
---
|
||||
|
||||
@@ -49,22 +51,89 @@ function _0xefgh() {
|
||||
### 如何确认是 JSVMP
|
||||
|
||||
```
|
||||
MCP 操作:
|
||||
- list_scripts → 找到异常大的 JS 文件
|
||||
- get_script_source(script_id=N, start_line=1, end_line=50) → 查看文件头部结构
|
||||
- search_code(keyword="while") → 搜索解释器循环
|
||||
- search_code_in_script(script_id=N, keyword="switch") → 在指定大文件中精确搜索(前后 3 行上下文)
|
||||
- search_code(keyword="push|pop|shift") → 搜索栈操作
|
||||
- dump_jsvmp_strings → 提取字符串数组,识别 API 名称和混淆模式(快速判断)
|
||||
MCP 操作(推荐顺序):
|
||||
- list_scripts → 找到异常大的 JS 文件(100KB+)
|
||||
- find_dispatch_loops(script_url=<大 JS 的 url>, min_case_count=20) ← v0.4.0 推荐首选
|
||||
→ 返回 candidates: [{fn_name, case_count, char_range, preview}]
|
||||
→ case_count > 50 的基本确认是 VMP 解释器
|
||||
- dump_jsvmp_strings(script_url=<大 JS 的 url>) → 提取字符串数组
|
||||
→ 看 suspicious_patterns 是否包含 "JSVMP interpreter loop (while+switch, large source)"
|
||||
→ 看 api_names 是否包含 navigator/screen/encrypt/md5/hmac 等
|
||||
|
||||
兜底手动方式(大文件 find_dispatch_loops 超时时):
|
||||
- search_code_in_script(script_id=N, keyword="switch") → 前后 3 行上下文
|
||||
- search_code_in_script(script_id=N, keyword="case 0:|case 1:|case 2:")
|
||||
- get_script_source(script_id=N, start_line=..., end_line=...) → 按 char_range 读取分发体
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三板斧方法论
|
||||
## 四板斧方法论(v2.5.0 更新)
|
||||
|
||||
### 快速路径:一键 JSVMP 分析(推荐先试)
|
||||
### 四板斧关系图
|
||||
|
||||
对于典型 JSVMP,可以跳过手动 Hook 直接使用专项工具:
|
||||
```
|
||||
┌── 第一板斧 Hook 出入口 ──────────┐
|
||||
│ inject_hook_preset(xhr|fetch │
|
||||
│ |crypto|cookie|websocket │
|
||||
│ |debugger_bypass|runtime_probe)│
|
||||
│ freeze_prototype │
|
||||
│ analyze_cookie_sources │
|
||||
└────────┬─────────────────────────┘
|
||||
│
|
||||
夹逼 I/O ──→ ┌─┴─┐ ──→ 推断签名公式
|
||||
│VMP│
|
||||
中间层 ──→ ┌─┘ └─┐ ──→ 追踪执行链路
|
||||
│
|
||||
┌── 第二板斧 插桩解释器 ────────────┐ ┌── 第四板斧 源码级插桩 ──────────┐
|
||||
│ find_dispatch_loops │ │ instrument_jsvmp_source (新) │
|
||||
│ trace_function(分发函数/子函数) │ │ get_instrumentation_log │
|
||||
│ trace_property_access │ │ → summary.hot_keys │
|
||||
│ hook_jsvmp_interpreter (多路径) │ │ → summary.hot_methods │
|
||||
└────────┬─────────────────────────┘ │ → summary.hot_functions │
|
||||
└────────┬────────────────────────┘
|
||||
│
|
||||
↓ ↓
|
||||
┌── 第三板斧 日志分析 ──────────────────┐
|
||||
│ get_trace_data + get_jsvmp_log │
|
||||
│ + get_runtime_probe_log │
|
||||
│ + get_instrumentation_log │
|
||||
│ 反向追踪法(从签名值→明文) │
|
||||
│ 多次请求对比法(找变化因子) │
|
||||
└───────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**板斧选择矩阵**:
|
||||
|
||||
| VMP 特征 | 推荐板斧组合 |
|
||||
|---------|-------------|
|
||||
| 签名通过 CryptoJS / atob / MD5 等可 hook API 走 | 第一 + 第三(hot API hook + 日志反推) |
|
||||
| VMP 通过 `Function.apply/call` 调子函数 | 第二 + 第三(`hook_jsvmp_interpreter` 多路径 + 日志) |
|
||||
| VMP 自包含,算法全在 switch/case 内(瑞数 5/6、Akamai、webmssdk、obfuscator.io) | **第四板斧首选**,配合第一板斧做 I/O 边界 |
|
||||
| VMP 深度绑定浏览器环境(compare_env 差异大 + 签名随环境变) | 路径 B 环境伪装(见 SKILL.md 场景 10),配第四板斧 `hot_keys` 定位指纹集 |
|
||||
|
||||
---
|
||||
|
||||
### 快速路径:v0.4.0 黄金 8 步流程(推荐先试,70%+ 瑞数/Akamai/webmssdk 场景直接搞定)
|
||||
|
||||
```
|
||||
Step 1 — find_dispatch_loops 确认 VMP + 记下 fn_name / char_range
|
||||
Step 2 — hook_jsvmp_interpreter(script_url=<VMP basename>) 装多路径探针
|
||||
Step 3 — inject_hook_preset("cookie", persistent=True) + inject_hook_preset("xhr", persistent=True)
|
||||
Step 4 — instrument_jsvmp_source(url_pattern="**/<VMP 文件>", mode="ast", tag="vmp1") ← 核心新步
|
||||
Step 5 — reload_with_hooks() 让所有探针先于 VMP 生效 + 清日志
|
||||
Step 6 — 触发业务操作
|
||||
Step 7 — get_instrumentation_log(tag_filter="vmp1", limit=300) 看 hot_keys / hot_methods / hot_functions
|
||||
Step 8 — analyze_cookie_sources() 归因最终 cookie 来源
|
||||
```
|
||||
|
||||
详细步骤见 [`jsvmp-source-instrumentation.md`](./jsvmp-source-instrumentation.md)「黄金 8 步流程」节。
|
||||
|
||||
---
|
||||
|
||||
### 三板斧旧流程(保留以应对第四板斧无法使用的场景)
|
||||
|
||||
对于典型 JSVMP,也可以继续用以下一键工具:
|
||||
|
||||
```
|
||||
MCP 操作:
|
||||
@@ -438,49 +507,89 @@ MCP 操作:
|
||||
|
||||
---
|
||||
|
||||
## 还原策略决策
|
||||
## 还原策略决策(v2.5.0 扩充源码级插桩分支)
|
||||
|
||||
```
|
||||
JSVMP 签名还原决策树:
|
||||
├─ Hook 确认使用标准算法(MD5/HMAC/AES)?
|
||||
|
||||
第 0 步:先看 hot_methods(第四板斧 summary)
|
||||
├─ hot_methods 包含 CryptoJS.MD5 / SubtleCrypto.digest / HMAC / btoa
|
||||
│ → 算法是标准加密,继续看第 1 步分支
|
||||
│
|
||||
├─ hot_methods 全是自定义 fn 名,几乎没有 CryptoJS/Subtle
|
||||
│ → VMP 自实现加密,直接跳到第 3 步(沙箱运行完整 VM)
|
||||
|
||||
第 1 步:标准算法分支
|
||||
├─ 能从 hot_methods + 调用栈反推出明文拼接顺序?
|
||||
│ ├─ YES → 纯算法还原(Node.js crypto / Python hashlib+pycryptodome)
|
||||
│ │ · 还原拼接顺序和密钥即可
|
||||
│ │ · 这是最理想的结果
|
||||
│ └─ NO → 继续分析
|
||||
│
|
||||
├─ 能提取出独立的签名函数?
|
||||
│ ├─ YES → 沙箱执行(Node.js vm / Python execjs)
|
||||
│ │ · 提取函数及其依赖
|
||||
│ │ · 构造最小执行环境
|
||||
│ └─ NO → 继续分析
|
||||
│
|
||||
├─ VM 劫持了整个请求链路?
|
||||
│ ├─ YES → 加载完整 VM(仅调用签名入口)
|
||||
│ │ · 下载完整 JS 文件
|
||||
│ │ · 在 Node.js vm 中加载
|
||||
│ │ · 补最小浏览器环境
|
||||
│ │ · 调用签名函数获取结果
|
||||
│ └─ NO → 浏览器自动化兜底
|
||||
│
|
||||
└─ 无法脱离浏览器
|
||||
→ 使用 Camoufox 浏览器自动化(最后手段)
|
||||
│ └─ NO(明文来自 VM 内部)→ 进第 2 步
|
||||
|
||||
第 2 步:能提取独立的签名函数?
|
||||
├─ YES → 沙箱执行(Node.js vm / Python execjs)
|
||||
│ · 提取函数及其依赖
|
||||
│ · 构造最小执行环境(根据 hot_keys 对齐环境变量)
|
||||
└─ NO → 第 3 步
|
||||
|
||||
第 3 步:VMP 劫持了整个请求链路 + hot_keys 显示大量环境读取?
|
||||
├─ YES → 走路径 B(jsdom 环境伪装)
|
||||
│ · hot_keys 直接告诉你要对齐哪些环境属性(省去 58 项人工 diff)
|
||||
│ · analyze_cookie_sources 确认 cookie 是否来自 HTTP Set-Cookie
|
||||
│ · 参考 references/jsdom-env-patches.md
|
||||
└─ NO(jsdom 加载失败 / VMP 有 CDN 反射检测)→ 第 4 步
|
||||
|
||||
第 4 步:加载完整 VM(仅调用签名入口)
|
||||
· 下载完整 JS 文件
|
||||
· 在 Node.js vm 中加载
|
||||
· 补最小浏览器环境(仍然根据 hot_keys 指导)
|
||||
· 调用签名函数获取结果
|
||||
|
||||
第 5 步:实在无法脱离浏览器
|
||||
→ 使用 Camoufox 浏览器自动化(最后手段,不是协议优先方案)
|
||||
```
|
||||
|
||||
**关键:第四板斧的 `hot_keys` / `hot_methods` 是所有决策的共同输入**——不要在没看 hot_keys 之前就选策略。即使最后选了路径 B,`hot_keys` 也能把"要对齐什么环境"从 58 项人工 diff 降为 20 项精确对齐。
|
||||
|
||||
---
|
||||
|
||||
## 实战检查清单
|
||||
## 实战检查清单(v2.5.0 扩充四板斧项)
|
||||
|
||||
在 JSVMP 分析过程中,确保以下每项都已完成:
|
||||
|
||||
- [ ] 确认文件是 JSVMP(解释器循环 + 字节码数组 + 栈操作)
|
||||
- [ ] Hook XHR/Fetch 出口,捕获签名值的最终形态
|
||||
- [ ] Hook Cookie setter,确认是否有动态 Cookie
|
||||
- [ ] Hook 加密原语入口(btoa/atob/CryptoJS/MD5/SHA)
|
||||
**第一板斧 — Hook 出入口**:
|
||||
- [ ] `find_dispatch_loops` 确认是 VMP(case_count > 50)
|
||||
- [ ] `inject_hook_preset("xhr", persistent=True)` + `("fetch", persistent=True)` 捕获请求出口
|
||||
- [ ] `inject_hook_preset("cookie", persistent=True)` **原型链级** cookie hook(替代手写 Document.prototype.cookie 的做法)
|
||||
- [ ] `analyze_cookie_sources` 归因最终 cookie(HTTP vs JS)
|
||||
- [ ] `inject_hook_preset("crypto", persistent=True)` 捕获加密原语入口(btoa/atob/CryptoJS/MD5/SHA)
|
||||
- [ ] Hook String.fromCharCode(JSVMP 高频信号)
|
||||
- [ ] 定位解释器核心分发函数
|
||||
- [ ] 分层插桩(粗 → 中 → 细)
|
||||
- [ ] 日志反向追踪,找到签名值首次出现的位置
|
||||
- [ ] `freeze_prototype` 防止 VMP 覆盖 hook
|
||||
|
||||
**第二板斧 — 插桩解释器**:
|
||||
- [ ] `hook_jsvmp_interpreter(script_url=<VMP basename>)` 多路径探针(apply/call/bind + Reflect.*/Proxy)
|
||||
- [ ] 分层 `trace_function`(粗 → 中 → 细,用 `max_captures` 限制日志量)
|
||||
- [ ] `trace_property_access(["navigator.*", "screen.*", ...])` 监控签名容器
|
||||
|
||||
**第三板斧 — 日志分析**:
|
||||
- [ ] `get_jsvmp_log` + `get_trace_data` + `get_runtime_probe_log`(如果装了 runtime_probe)+ `get_console_logs`
|
||||
- [ ] 反向追踪法,找到签名值首次出现的位置
|
||||
- [ ] 多次请求对比,确认变化因子和固定因子
|
||||
- [ ] 根据输出特征判断算法类型
|
||||
- [ ] 选择还原策略(纯算法 / 沙箱 / 完整 VM / 浏览器)
|
||||
|
||||
**第四板斧 — 源码级插桩 [v2.5.0 新增]**:
|
||||
- [ ] `instrument_jsvmp_source(url_pattern=..., mode="ast"|"regex", tag=...)` 装好
|
||||
- [ ] `reload_with_hooks()` 让插桩先于 VMP 生效
|
||||
- [ ] 读 `get_instrumentation_log` 三个 summary:`hot_keys` / `hot_methods` / `hot_functions`
|
||||
- [ ] `hot_keys` 给出 VMP 读取的环境指纹集(top 30)
|
||||
- [ ] `hot_methods` 给出 VMP 调用的方法(ObjectType.methodName 格式)
|
||||
- [ ] `hot_functions` 给出 VMP 调用的函数名
|
||||
- [ ] `stop_instrumentation` 完工清理
|
||||
|
||||
**环境/时序**:
|
||||
- [ ] 若目标是首屏挑战页,用 `navigate(pre_inject_hooks=[...])` 或 `via_blank=True`
|
||||
- [ ] `compare_env` 采集环境基准
|
||||
- [ ] `bypass_debugger_trap` 绕过反调试
|
||||
|
||||
**还原与验证**:
|
||||
- [ ] 根据 hot_methods 判断算法类型
|
||||
- [ ] 选择还原策略(纯算法 / 沙箱 / 完整 VM / jsdom 环境伪装 / 浏览器)
|
||||
- [ ] 实现并验证签名计算结果
|
||||
- [ ] 端到端验证(连续 5+ 次请求稳定)
|
||||
|
||||
Reference in New Issue
Block a user