docs: 增加 AST 插桩健康门禁
This commit is contained in:
@@ -258,12 +258,13 @@ python main.py
|
||||
- **curl_cffi**: 带浏览器 TLS 指纹模拟的 HTTP 客户端
|
||||
|
||||
### 调试工具
|
||||
- **[camoufox-reverse MCP](https://github.com/WhiteNightShadow/camoufox-reverse-mcp) v1.0.0**: 反检测浏览器逆向分析(32 个核心工具,统一 API,含源码级插桩 + Cookie 归因分析)
|
||||
- **[camoufox-reverse MCP](https://github.com/WhiteNightShadow/camoufox-reverse-mcp) v1.1.1**: 反检测浏览器逆向分析(35 个工具,统一 API,含源码级插桩、Cookie 归因和引擎层属性追踪)
|
||||
|
||||
## 版本记录
|
||||
|
||||
| 版本 | 日期 | 要点 |
|
||||
|------|------|------|
|
||||
| v3.4.1 | 2026-07-29 | 增加 AST 源码插桩执行健康门禁;对齐 camoufox-reverse MCP v1.1.1;保持正常插桩流程不变,仅在异常时进入安全降级 |
|
||||
| v3.3.1 | 2026-04-21 | 经验法则精简:移除单站点经验(env-patch 行数阈值 / UA 矩阵 / cacheOpts 三件套),合并 evaluate_js 相关为 1 条,总数从 24 压缩到 22 条 |
|
||||
| v3.3.0 | 2026-04-19 | 核心层回归扩容:Phase 1-5 详细动作 + 10 个场景速查 + 经验法则回迁核心层;案例层强化(禁动清单 + UA 分支矩阵) |
|
||||
| v3.2.1 | 2026-04-19 | Phase 0.5.1 强化案例复用策略;经验法则第 11 条重写 |
|
||||
|
||||
@@ -12,7 +12,7 @@ description: >
|
||||
v3.2.0 移除 MCP session 依赖,Checklist 从五项压缩到三项,cases/ 成为唯一经验库。
|
||||
v3.3.0 核心层回归扩容:Phase 1-5 详细动作 + 10 个场景速查 + 经验法则回迁核心层。
|
||||
v3.3.1 经验法则精简至 22 条:移除单站点经验,合并 evaluate_js 规则。
|
||||
argument-hint: "<目标URL> [需要分析的加密参数名, 如 sign, m, token]"
|
||||
v3.4.1 增加 AST 源码插桩执行健康门禁,区分原始源码解析失败与改写产物执行失败。
|
||||
---
|
||||
|
||||
# ⚠️ 硬约束 Checklist(分析启动前必做,不可跳过)
|
||||
@@ -517,6 +517,14 @@ Actions:
|
||||
步骤 9:instrumentation(action='install',
|
||||
url_pattern="**/<VMP文件>", mode="ast", tag="vmp1")
|
||||
步骤 10:instrumentation(action='reload') → 重载让插桩先于 VMP 生效
|
||||
步骤 10.5:验证插桩实际执行(不可跳过)
|
||||
instrumentation(action='status') → 确认 files_rewritten > 0
|
||||
evaluate_js(expression="(() => ({
|
||||
tapInstalled: window.__mcp_tap_installed === true,
|
||||
logReady: Array.isArray(window.__mcp_vmp_log)
|
||||
}))()")
|
||||
→ files_rewritten > 0 但 tapInstalled=false:
|
||||
优先判定为改写产物未解析/未执行,不要误判为原始源码 esprima parse 失败
|
||||
步骤 11:instrumentation(action='log', tag_filter="vmp1", type_filter="tap_get")
|
||||
→ hot_keys 是 VMP 读取的环境属性 top 30
|
||||
步骤 12:instrumentation(action='log', tag_filter="vmp1", type_filter="tap_method")
|
||||
@@ -861,12 +869,14 @@ Actions:
|
||||
|
||||
梯度 1: 检查手头已抓的证据
|
||||
→ list_network_requests 看已抓的请求够不够分析
|
||||
→ instrumentation(action='status') 确认 files_rewritten / last_mode_used
|
||||
→ evaluate_js 检查 __mcp_tap_installed 与 __mcp_vmp_log
|
||||
→ instrumentation(action='log') 看插桩事件
|
||||
→ 如果已有证据没充分用 → 回去用
|
||||
|
||||
梯度 2: 换 Hook 模式 / 插桩模式
|
||||
→ proxy ↔ transparent
|
||||
→ ast ↔ regex(CSP 拦截时走 regex)
|
||||
→ ast ↔ regex(原始源码解析失败或改写产物未执行时走 regex)
|
||||
|
||||
梯度 3: 点对点 hook_function
|
||||
→ hook_function(function_path=<具体签名函数>, mode='trace')
|
||||
@@ -1340,6 +1350,7 @@ verify_signer_offline(
|
||||
|
||||
| 版本 | 日期 | 要点 |
|
||||
|------|------|------|
|
||||
| v3.4.1 | 2026-07-29 | 增加 AST 插桩执行健康门禁:`files_rewritten` 只表示完成改写,必须继续验证 runtime 标记;失败时按通用梯度降级,不引入站点特例 |
|
||||
| v3.4.0 | 2026-04-22 | Phase 3 新增引擎层追踪分支(trace_property_access)。依赖 camoufox-reverse 定制版浏览器 + MCP v1.1.0。装了定制版后补环境从"全量 diff"升级到"精准 trace",未装定制版自动降级到 compare_env,无破坏性变化。JS 层零痕迹,JSVMP 不可检测 |
|
||||
| v3.3.1 | 2026-04-19 | 经验法则精简至 22 条:移除单站点经验,合并 evaluate_js 规则 |
|
||||
| v3.3.0 | 2026-04-19 | 核心层回归扩容:Phase 1-5 详细动作 + 10 个场景速查 + 经验法则回迁核心层 |
|
||||
|
||||
@@ -183,10 +183,10 @@ function genSign(input) {
|
||||
## 踩坑记录
|
||||
|
||||
- **坑 1:instrument_jsvmp_source 必须在 navigate/reload 之前调用**——route 注册晚了就抓不到 VMP 脚本加载
|
||||
- **坑 2:AST 模式需要 cdnjs 访问**——代理环境下要保证 `cdnjs.cloudflare.com` 可访问,否则降级到 `mode="regex"`
|
||||
- **坑 2:AST 模式从 MCP v0.5.0 起在 MCP 侧使用 esprima,不依赖页面 CDN**——若原始源码 parse 失败或改写产物未执行,再降级到 `mode="regex"`
|
||||
- **坑 3:reload_with_hooks 只刷当前页,不改变 URL**——首屏挑战场景要用 `navigate(pre_inject_hooks=[...])` 而不是 reload_with_hooks
|
||||
- **坑 4:hot_keys 里 "[key of Symbol()]"**——这是 Symbol 键被 preview 化的表现,不是真实属性名,可以忽略
|
||||
- **坑 5:同一个 VMP 被多次加载(HMR / 页面跳转)** → 确保 `cache_rewritten=True`,否则每次重写开销巨大
|
||||
- **坑 5:同一个 VMP 被多次加载(HMR / 页面跳转)** → MCP 会按 URL 缓存改写结果;当前接口不需要传缓存参数
|
||||
- **坑 6:VMP 通过 eval / new Function 动态生成子 VMP** → 源码级插桩看不到动态生成的部分,需要额外 `hook_function(Function, ...)` 截获
|
||||
- **坑 7:Proxy(jsvmp_hook)与源码级插桩叠加时页面崩溃** → 尝试 `hook_jsvmp_interpreter(track_props=False)` 关闭 Proxy,只留 apply/Reflect.*
|
||||
|
||||
@@ -210,4 +210,4 @@ function genSign(input) {
|
||||
|
||||
- **高权重(直接走)**:`find_dispatch_loops` 返回 case_count > 50
|
||||
- **中权重(优先试)**:单 JS > 200KB + 含 while-switch + 参数长度 128/192/256 + Base64 变体
|
||||
- **低权重(参考)**:Cookie 中有 acw_tc / FSSBBIl1 / _abck / ak_bmsc / ttwid / msToken 等已知模式字段
|
||||
- **低权重(参考)**:Cookie 中有 acw_tc / FSSBBIl1 / _abck / ak_bmsc / ttwid / msToken 等已知模式字段
|
||||
|
||||
@@ -90,7 +90,7 @@ MCP 操作:
|
||||
<!-- v3.1.0: migrated from instrument_jsvmp_source -->
|
||||
→ 是唯一能打开黑箱的工具
|
||||
|
||||
instrumentation(action='log', tag='vmp1', type_filter='tap_get', limit=200)
|
||||
instrumentation(action='log', tag_filter='vmp1', type_filter='tap_get', limit=200)
|
||||
<!-- v3.1.0: migrated from get_instrumentation_log -->
|
||||
→ hot_keys 指纹学习法 30 秒就能告诉你 VMP 读了哪些环境属性
|
||||
```
|
||||
@@ -145,7 +145,7 @@ v0.5.0 把 AST 从页面内 Acorn 改为 MCP 侧 esprima,RS 412 挑战页也
|
||||
|
||||
> 原 #37
|
||||
|
||||
严格的 CSP 会阻止改写代码执行。v0.6.0 新增 `csp_bypass=True` 参数自动绕过。
|
||||
严格的 CSP 可能阻止改写代码执行。当前统一接口没有 `csp_bypass` 参数;若运行时健康检查失败,应停止该插桩并降级到更保守的观测方式。
|
||||
|
||||
### 规则 15:`navigate` 的 `collect_response_chain` 默认开启
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# JSVMP 源码级插桩专项指南(第四板斧)
|
||||
|
||||
> **v2.5.0 新增文档**。本指南讲解 camoufox-reverse MCP v0.4.0+ 提供的源码级插桩能力(`instrument_jsvmp_source` / `get_instrumentation_log` / `find_dispatch_loops`)的使用方法论,是 JSVMP 四板斧中"第四板斧"的完整说明。
|
||||
> **v2.5.0 新增文档,v3.4.1 更新健康诊断**。当前执行接口以 camoufox-reverse MCP v1.1.1 的 `instrumentation(action=...)` 为准;文中的旧工具名只用于解释能力来源。
|
||||
>
|
||||
> 与 `jsvmp-analysis.md` 的第一/二/三板斧互补——前三板斧诊断"VM 看外界 / 外界看 VM",本文档讲解"VM 看自己"。
|
||||
|
||||
@@ -69,40 +69,38 @@ __mcp_tap_call(fn, null, [args[0], args[1], ...], 'vmp1');
|
||||
|
||||
`instrument_jsvmp_source(mode=...)` 支持两种模式:
|
||||
|
||||
### 2.1 `mode="regex"`(默认,零依赖)
|
||||
### 2.1 `mode="regex"`(轻量兜底,零依赖)
|
||||
|
||||
- **原理**:纯正则匹配 `<identifier>[<expr>]`,替换为 `__mcp_tap_get(identifier, expr, tag)`
|
||||
- **优点**:无外部 CDN 依赖,页面离线也能用;速度极快;大文件安全(10MB+ VMP 也能秒级改写)
|
||||
- **覆盖率**:~80% 的 member access;**不覆盖函数调用 tap**(只改写 `obj[key]`,不改写 `fn(args)`)
|
||||
- **局限**:对带模板字符串、正则字面量、嵌套方括号的代码可能误改写;只改 member access,不改 call
|
||||
- **使用场景**:
|
||||
- 目标环境无法访问 cdnjs.cloudflare.com
|
||||
- 极限大文件(AST 模式内存超限)
|
||||
- 快速扫一遍看看 hot_keys 分布
|
||||
|
||||
### 2.2 `mode="ast"`(高保真,需 CDN)
|
||||
### 2.2 `mode="ast"`(默认,MCP 侧 esprima)
|
||||
|
||||
- **原理**:在页面内加载 Acorn(`cdnjs.cloudflare.com/ajax/libs/acorn/8.11.3/acorn.min.js`)解析源码为 AST,精确识别 `MemberExpression` / `CallExpression`,只对应该改写的节点插入 tap
|
||||
- **优点**:99% 覆盖率,**同时改写 member access 和 call**;不会误改写字符串字面量/正则
|
||||
- **依赖**:页面必须能访问 cdnjs CDN
|
||||
- **原理**:在 MCP 进程中用 esprima-python 解析源码为 AST,识别 `MemberExpression` / `CallExpression` 后插入 tap
|
||||
- **优点**:同时改写 member access 和 call;不会把字符串字面量或正则内容当作代码节点
|
||||
- **依赖**:不依赖页面 CDN;esprima 仅覆盖其支持的 JavaScript 语法版本
|
||||
- **使用场景**:
|
||||
- 绝大多数联网场景(默认推荐)
|
||||
- 绝大多数场景(默认推荐)
|
||||
- 需要 `hot_methods` / `hot_functions` 数据(只有 AST 模式会生成)
|
||||
- **失败回退**:AST 解析失败时(例如脚本有语法错误或 Acorn 版本不支持的语法),`instrument_jsvmp_source` 会自动 pass-through 源码不变,并在 console 记 warn
|
||||
- **失败回退**:原始源码解析失败时可自动回落 regex;`last_mode_used="ast"` 不代表改写产物已经在浏览器成功执行
|
||||
|
||||
> **v0.5.0 变化**:`mode="ast"` 的实现从 v0.4.x 的"页面内加载 acorn CDN"改为
|
||||
> **MCP 侧 esprima-python 解析**。这意味着:
|
||||
> - 挑战页(RS 412、Akamai sensor_data 挑战等)上 AST 模式**能用了**(旧实现因 CDN 被挑战拦住会静默失败)
|
||||
> - 零 JS 依赖,`instrument_jsvmp_source` 的 AST 模式现在**应成为默认选择**
|
||||
> - 零 JS 依赖,`instrumentation(action="install")` 的 AST 模式现在**应成为默认选择**
|
||||
> - 新增 `fallback_on_error=True`,esprima 解析失败时自动回落 regex
|
||||
> - 旧的 "ast_page" 模式保留但标注为 deprecated,仅做 A/B 对比用
|
||||
|
||||
### 2.3 选择建议
|
||||
|
||||
| 情况 | 选 | 原因 |
|
||||
|------|-----|-----|
|
||||
| 首次分析某个 VMP,联网环境 | **AST** | `hot_methods` + `hot_functions` 能 30 秒内画出 VMP 行为画像 |
|
||||
| 离线/受限网络 | regex | 零依赖 |
|
||||
| 首次分析某个 VMP | **AST** | `hot_methods` + `hot_functions` 能快速画出 VMP 行为画像 |
|
||||
| 只需快速观察 bracket member access | regex | 改写轻、无需 AST |
|
||||
| VMP 文件 > 5 MB | regex | AST 改写内存开销大 |
|
||||
| 关心 VMP 调了哪些方法 | **AST** | regex 不生成 tap_method / tap_call |
|
||||
| 关心 VMP 读了哪些属性 | 任一 | 两种模式都生成 tap_get |
|
||||
@@ -111,14 +109,14 @@ __mcp_tap_call(fn, null, [args[0], args[1], ...], 'vmp1');
|
||||
|
||||
## AST 模式健康诊断(v2.6.0 新增)
|
||||
|
||||
启用 `instrument_jsvmp_source(mode="ast")` 后,通过 `get_instrumentation_status()` 的 `active_patterns[i].last_mode_used` 字段判断实际走了哪条路径:
|
||||
启用 `instrumentation(action="install", mode="ast")` 后,先通过
|
||||
`instrumentation(action="status")` 的 `active_patterns[i].last_mode_used` 判断改写路径:
|
||||
|
||||
| `last_mode_used` | 含义 | 动作 |
|
||||
|---|---|---|
|
||||
| `"ast"` | MCP 侧 esprima 成功,全量 AST 改写 | 正常,继续 |
|
||||
| `"ast"` | 原始源码解析成功并完成 AST 改写 | 继续做运行时健康检查 |
|
||||
| `"regex"` | 你显式指定了 `mode="regex"` | 正常,继续 |
|
||||
| `"regex (fallback)"` | `mode="ast"` 但 esprima 解析失败,自动回落 regex | **警告**:看下文 |
|
||||
| `"ast_page"` | 走的是 deprecated 的页面内 Acorn 实现 | 迁到 `"ast"`,除非你在做 A/B |
|
||||
|
||||
`"regex (fallback)"` 持续出现时:
|
||||
|
||||
@@ -128,6 +126,26 @@ __mcp_tap_call(fn, null, [args[0], args[1], ...], 'vmp1');
|
||||
|
||||
一般来说,RS sdenv*.js / Akamai acmescripts*.js / webmssdk 都能被 esprima 解析。如果出现持续 fallback,报告给 MCP 仓库 issue 区。
|
||||
|
||||
### 改写产物运行时健康检查
|
||||
|
||||
`files_rewritten > 0` 和 `last_mode_used="ast"` 只证明 MCP 生成并下发了改写文本,
|
||||
不能证明浏览器成功解析和执行了它。每次 reload 后继续检查:
|
||||
|
||||
```text
|
||||
instrumentation(action="status")
|
||||
evaluate_js(expression="(() => ({
|
||||
tapInstalled: window.__mcp_tap_installed === true,
|
||||
logReady: Array.isArray(window.__mcp_vmp_log)
|
||||
}))()")
|
||||
instrumentation(action="log", tag_filter="vmp1", limit=10)
|
||||
```
|
||||
|
||||
判定:
|
||||
|
||||
- `files_rewritten > 0` 且 `tapInstalled=false`:优先怀疑改写产物语法损坏或执行前异常。常见触发结构是嵌套 `CallExpression` / `MemberExpression` / `NewExpression`,不是原始源码 parse 失败。
|
||||
- `tapInstalled=true` 但日志为空:runtime 已执行,继续检查 VMP 是否触发、tag/filter 是否正确,不要把空日志直接归因于语法错误。
|
||||
- 确认改写产物失败后,先停止原 AST route,再按 `ast → regex → transparent` 梯度重装;不要通过持续增大 `max_rewrites` 反复下发同一份失败产物。
|
||||
|
||||
---
|
||||
|
||||
## 3. 黄金 8 步流程
|
||||
@@ -179,8 +197,7 @@ Actions:
|
||||
tag="vmp1", # 一次插桩多 VMP 时区分
|
||||
rewrite_member_access=True,
|
||||
rewrite_calls=True,
|
||||
max_rewrites=5000,
|
||||
cache_rewritten=True # 避免重复改写
|
||||
max_rewrites=5000
|
||||
)
|
||||
→ 返回:{"status": "instrumenting", "pattern": "...", "mode": "ast", "tag": "vmp1"}
|
||||
```
|
||||
@@ -355,7 +372,7 @@ get_runtime_probe_log(type_filter="canvas_toDataURL", limit=50)
|
||||
|
||||
### Q2:AST 模式报 `parse_error`
|
||||
|
||||
**可能原因**:脚本有 Acorn 不支持的语法(如极老的 ES3 hack 或极新的 stage-0 提案)。
|
||||
**可能原因**:原始脚本包含 esprima-python 不支持的语法。
|
||||
|
||||
**对策**:
|
||||
- 降级为 `mode="regex"`(零依赖,不会 parse)
|
||||
@@ -364,15 +381,16 @@ get_runtime_probe_log(type_filter="canvas_toDataURL", limit=50)
|
||||
### Q3:改写后页面崩溃
|
||||
|
||||
**可能原因**:
|
||||
1. VMP 自己对自己的源码做了完整性校验(少见,但存在)——对策:用 regex 模式做轻改写,只改 member access
|
||||
2. Tag 变量名冲突——改个 `tag` 再试
|
||||
3. 改写后代码超过 JS 引擎 token 上限——把 `max_rewrites` 调小
|
||||
1. `files_rewritten > 0` 但 runtime 标记不存在——AST 改写产物可能因嵌套节点区间冲突而无法解析;停止 AST route 后降级 regex/transparent
|
||||
2. VMP 自己对源码做完整性校验——用 regex 模式做轻改写,只改 member access
|
||||
3. Tag 变量名冲突——改个 `tag` 再试
|
||||
4. 改写后代码过大——把 `max_rewrites` 调小
|
||||
|
||||
### Q4:hot_keys 没出现预期的环境指纹
|
||||
|
||||
**可能原因**:
|
||||
1. VMP 还没真正执行——加大 `wait_until="networkidle"`,或 `wait_for` 某个元素后再读日志
|
||||
2. 改写没成功——先 `get_instrumentation_status` 看 `files_rewritten` 是否 > 0
|
||||
2. 改写或执行没成功——先看 `files_rewritten`,再检查 `window.__mcp_tap_installed`
|
||||
3. tag 搞混了——检查 `tag_filter` 是否写对
|
||||
|
||||
### Q5:日志爆炸到上限 20000 条
|
||||
@@ -403,4 +421,4 @@ get_runtime_probe_log(type_filter="canvas_toDataURL", limit=50)
|
||||
- `references/jsdom-env-patches.md`:环境伪装补丁库(路径 B)
|
||||
- `references/mcp-cookbook.md` 场景 6:源码级插桩场景化操作
|
||||
- `cases/universal-vmp-source-instrumentation.md`:骨架案例模板
|
||||
- MCP 上游:https://github.com/WhiteNightShadow/camoufox-reverse-mcp(v0.4.0+)
|
||||
- MCP 上游:https://github.com/WhiteNightShadow/camoufox-reverse-mcp(当前基线 v1.1.1)
|
||||
|
||||
@@ -489,15 +489,14 @@ await navigate(url, pre_inject_hooks=["jsvmp_probe_transparent"])
|
||||
# 1. 先确认是不是 VMP(case_count > 50 基本是)
|
||||
[camoufox-reverse] search_code(keyword='switch', script_url="https://target.com/sdenv-xxx.js", context_chars=500) <!-- v3.1.0: migrated from find_dispatch_loops -->
|
||||
|
||||
# 2. 装插桩(AST 模式优先,需 CDN;regex 模式兜底)
|
||||
# 2. 装插桩(AST 在 MCP 侧运行;regex 模式兜底)
|
||||
[camoufox-reverse] instrumentation(action='install', <!-- v3.1.0: migrated from instrument_jsvmp_source -->
|
||||
url_pattern="**/sdenv-*.js", # glob 模式匹配多 hash 版本
|
||||
mode="ast", # 或 "regex"
|
||||
tag="vmp1", # 区分多 VMP 的标签
|
||||
rewrite_member_access=True, # 改写每个 obj[key]
|
||||
rewrite_calls=True, # 改写每个 fn(args) / obj.method(args)
|
||||
max_rewrites=5000, # 单文件改写上限
|
||||
cache_rewritten=True # 缓存改写后源码
|
||||
max_rewrites=5000 # 单文件改写上限
|
||||
)
|
||||
|
||||
# 3. 装完后必须重载让插桩生效
|
||||
@@ -538,17 +537,9 @@ await instrumentation(action='install', url_pattern="**/sdenv-*.js", <!-- v3.1.0
|
||||
)
|
||||
|
||||
# 查看实际走了哪条路径
|
||||
status = await check_environment() <!-- v3.1.0: migrated from get_instrumentation_status -->
|
||||
status = await instrumentation(action='status')
|
||||
# status["active_patterns"][0]["last_mode_used"] == "ast" (正常)
|
||||
# or "regex (fallback)" (esprima 对该 VMP 的语法支持不全)
|
||||
# or "ast_page" (你显式用了 deprecated 模式)
|
||||
```
|
||||
|
||||
**新增取值 mode="ast_page"**:
|
||||
```python
|
||||
# 仅用于 A/B 对比 v0.4.x 的旧页面内 Acorn 实现,deprecated
|
||||
# 生产不要用,挑战页会因 CDN 被拦静默失败
|
||||
await instrumentation(action='install', url_pattern="**/vmp.js", mode="ast_page") <!-- v3.1.0: migrated from instrument_jsvmp_source -->
|
||||
```
|
||||
|
||||
### 使用 Cookie 归因分析(v2.5.0 新增)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
# MCP 工具分类索引 + v0.9.0 迁移指南
|
||||
# MCP v1.1.1 工具索引 + v0.9.0 迁移历史
|
||||
|
||||
> **说明**:SKILL.md 核心层有工具分类索引,这里是详细版 + MCP v0.9.0 的迁移映射
|
||||
> **说明**:当前可调用工具以 SKILL.md 核心层和 MCP 工具自身 docstring 为准;本文后半部分保留 v0.8.x → v0.9.0 的历史迁移映射。
|
||||
>
|
||||
> **版本**:v3.1.0(MCP v0.9.0 工具名迁移)
|
||||
> **版本**:v3.4.1(MCP v1.1.1)
|
||||
>
|
||||
> **当前 MCP 版本**:camoufox-reverse MCP v0.9.0(~50 个工具,从 v0.8.x 的 ~80 个合并而来)
|
||||
> **当前 MCP 版本**:camoufox-reverse MCP v1.1.1(35 个工具)。v1.1.0 固定 `mcp>=1.29,<2` 并仅归一化安全的顶层可选参数 schema;v1.1.1 修复链式调用的重叠 AST 改写,并在插桩状态中暴露实际执行模式。
|
||||
|
||||
---
|
||||
|
||||
## 一、工具分类表
|
||||
## 一、v0.9.0 历史工具分类表
|
||||
|
||||
> 本节用于理解旧文档和迁移来源,包含后续已移除的 Session/Assertion 工具,不应作为当前工具清单。当前清单见 SKILL.md「核心武器」。
|
||||
|
||||
### Browser — 浏览器与页面控制
|
||||
|
||||
@@ -85,7 +87,7 @@
|
||||
| `dump_jsvmp_strings` | 提取 JSVMP 脚本中的字符串 |
|
||||
| `compare_env` | 采集浏览器环境指纹基准 |
|
||||
| `instrumentation(action='install', url_pattern=..., mode=..., tag=...)` <!-- v3.1.0: migrated from instrument_jsvmp_source --> | 源码级插桩(HTTP 层改写 VMP) |
|
||||
| `instrumentation(action='log', tag=..., type_filter=...)` <!-- v3.1.0: migrated from get_instrumentation_log --> | 获取源码插桩日志(hot_keys/hot_methods/hot_functions) |
|
||||
| `instrumentation(action='log', tag_filter=..., type_filter=...)` <!-- v3.1.0: migrated from get_instrumentation_log --> | 获取源码插桩日志(hot_keys/hot_methods/hot_functions) |
|
||||
| `instrumentation(action='status')` | 查看当前激活的源码插桩状态 |
|
||||
| `instrumentation(action='stop', url_pattern=...)` <!-- v3.1.0: migrated from stop_instrumentation --> | 停止源码级插桩 |
|
||||
| `instrumentation(action='reload')` <!-- v3.1.0: migrated from reload_with_hooks --> | 重载页面让 hooks 先于 VMP 生效 |
|
||||
@@ -143,7 +145,7 @@
|
||||
| `search_code_in_script(url, keyword)` | `search_code(keyword, script_url=url)` | 合并到 search_code,script_url 可选参数 |
|
||||
| `trace_function(path, ...)` | `hook_function(path, mode='trace', ...)` | 追踪合并到 hook_function |
|
||||
| `instrument_jsvmp_source(url_pattern, ...)` | `instrumentation(action='install', url_pattern=url_pattern, ...)` | 源码插桩统一接口 |
|
||||
| `get_instrumentation_log(tag=X)` | `instrumentation(action='log', tag=X)` | |
|
||||
| `get_instrumentation_log(tag=X)` | `instrumentation(action='log', tag_filter=X)` | |
|
||||
| `get_instrumentation_status()` | `instrumentation(action='status')` | |
|
||||
| `stop_instrumentation()` | `instrumentation(action='stop')` | |
|
||||
| `reload_with_hooks()` | `instrumentation(action='reload')` | |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
>
|
||||
> **前置要求**:已完成反爬类型三分法识别(见 SKILL.md「反爬类型识别与工具选择」段)
|
||||
>
|
||||
> **版本**:v3.1.0(MCP v0.9.0 工具名迁移)
|
||||
> **版本**:v3.4.1(MCP v1.1.1,增加 AST 插桩执行健康门禁)
|
||||
|
||||
---
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
| 第一斧 | Hook I/O | `inject_hook_preset(xhr/fetch/crypto/cookie)` + `hook_function('X.prototype.Y', mode='intercept', ...)` <!-- v3.1.0: migrated from freeze_prototype --> | 请求链路劫持、动态 Cookie、加密原语入口 | VM 内部自实现的 MD5/AES | 行为型 ✅ / 纯混淆 ✅ / 签名型 ❌ |
|
||||
| 第二斧 | 插桩解释器 | `hook_function(path, mode='trace', ...)` <!-- v3.1.0: migrated from trace_function --> + `hook_jsvmp_interpreter(mode='proxy', trackProps=True)` <!-- v3.1.0: migrated from trace_property_access --> | 能识别分发函数名时的调用链追踪 | 匿名 IIFE 包裹 + 高频日志爆炸 | 行为型 ✅ / 纯混淆 ✅ / 签名型 ❌ |
|
||||
| 第三斧 | 日志分析 | `get_jsvmp_log` + 反向追踪 | 已能捕获签名值 I/O 时反推公式 | 签名完全不出 VM 的黑箱模式 | 所有类型 ✅(纯被动分析,无副作用) |
|
||||
| 第四斧 | 源码级插桩 | `instrumentation(action='install', ...)` <!-- v3.1.0: migrated from instrument_jsvmp_source --> + `instrumentation(action='log', ...)` <!-- v3.1.0: migrated from get_instrumentation_log --> | VM 内部调度、"全部在 opcode dispatch 循环里发生"的场景,`hot_keys` 直接暴露环境指纹集 | 极限大文件(5MB+)regex 模式覆盖率下降;AST 模式需 CDN | 所有类型 ✅,**签名型首选** |
|
||||
| 第四斧 | 源码级插桩 | `instrumentation(action='install', ...)` <!-- v3.1.0: migrated from instrument_jsvmp_source --> + `instrumentation(action='log', ...)` <!-- v3.1.0: migrated from get_instrumentation_log --> | VM 内部调度、"全部在 opcode dispatch 循环里发生"的场景,`hot_keys` 直接暴露环境指纹集 | 极限大文件(5MB+)开销高;改写后必须验证 runtime 已执行 | 所有类型 ✅,**签名型首选** |
|
||||
|
||||
### 按反爬类型的适用性
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
步骤 4:装源码级插桩(核心)
|
||||
instrumentation(action='install', url_pattern="**/<VMP 文件>", mode="ast", tag="vmp1")
|
||||
<!-- v3.1.0: migrated from instrument_jsvmp_source -->
|
||||
(AST 模式覆盖率高,页面能联网时优先)
|
||||
(AST 在 MCP 侧运行,不依赖页面联网)
|
||||
|
||||
步骤 5:让所有探针先于 VMP 生效
|
||||
instrumentation(action='reload')
|
||||
@@ -67,7 +67,7 @@
|
||||
evaluate_js / click / type_text → 翻页、搜索、登录等
|
||||
|
||||
步骤 7:读 hot_keys(30 秒定位环境指纹集)
|
||||
instrumentation(action='log', tag='vmp1', type_filter='tap_get', limit=300)
|
||||
instrumentation(action='log', tag_filter='vmp1', type_filter='tap_get', limit=300)
|
||||
<!-- v3.1.0: migrated from get_instrumentation_log -->
|
||||
→ 看 hot_keys / hot_methods / hot_functions 三个摘要
|
||||
|
||||
@@ -274,15 +274,14 @@ MCP 操作:
|
||||
mode="ast",
|
||||
tag="vmp1",
|
||||
rewrite_member_access=True,
|
||||
rewrite_calls=True,
|
||||
csp_bypass=True
|
||||
rewrite_calls=True
|
||||
)
|
||||
<!-- v3.1.0: migrated from instrument_jsvmp_source -->
|
||||
|
||||
模式选择:
|
||||
- mode="ast"(默认推荐):MCP 侧 esprima 解析,99% 覆盖率,挑战页可用
|
||||
- mode="regex":纯正则改写,80% 覆盖率,无 CDN 依赖,大文件安全
|
||||
- AST 失败时自动 fallback 到 regex(fallback_on_error=True)
|
||||
- mode="ast"(默认推荐):MCP 侧 esprima 解析,挑战页可用
|
||||
- mode="regex":轻量正则改写,只覆盖 bracket member access
|
||||
- 原始源码 AST parse 失败时自动 fallback 到 regex(fallback_on_error=True)
|
||||
```
|
||||
|
||||
#### 步骤 10:让插桩先于 VMP 生效
|
||||
@@ -297,26 +296,43 @@ MCP 操作:
|
||||
⚠️ 签名型反爬:不要清 cookie!第一次挑战拿到的 cookie 要留下
|
||||
```
|
||||
|
||||
#### 步骤 10.5:验证改写产物实际执行
|
||||
|
||||
```
|
||||
MCP 操作:
|
||||
instrumentation(action='status')
|
||||
evaluate_js(expression="(() => ({
|
||||
tapInstalled: window.__mcp_tap_installed === true,
|
||||
logReady: Array.isArray(window.__mcp_vmp_log)
|
||||
}))()")
|
||||
|
||||
判定:
|
||||
- files_rewritten > 0 且 tapInstalled=false
|
||||
→ 改写文本已下发,但浏览器未执行;优先怀疑嵌套 AST 节点改写损坏
|
||||
- tapInstalled=true 且日志为空
|
||||
→ runtime 已执行;继续检查触发动作和 tag_filter
|
||||
```
|
||||
|
||||
#### 步骤 11:读取插桩日志(指纹学习的金矿)
|
||||
|
||||
```
|
||||
MCP 操作:
|
||||
|
||||
# 读取属性访问 hot_keys
|
||||
instrumentation(action='log', tag='vmp1', type_filter='tap_get', limit=200)
|
||||
instrumentation(action='log', tag_filter='vmp1', type_filter='tap_get', limit=200)
|
||||
<!-- v3.1.0: migrated from get_instrumentation_log -->
|
||||
→ summary.hot_keys 告诉你 VMP 读取了哪些属性,按频次倒排
|
||||
→ 典型 RS 输出:{"userAgent":120, "plugins":98, "webdriver":77, "cookie":43, ...}
|
||||
→ 这就是 VMP 参与签名哈希的完整环境指纹集
|
||||
|
||||
# 读取方法调用 hot_methods
|
||||
instrumentation(action='log', tag='vmp1', type_filter='tap_method', limit=200)
|
||||
instrumentation(action='log', tag_filter='vmp1', type_filter='tap_method', limit=200)
|
||||
<!-- v3.1.0: migrated from get_instrumentation_log -->
|
||||
→ summary.hot_methods 格式 ObjectType.methodName
|
||||
→ 能否看到 MD5/AES/HMAC 就是算法是否用标准加密的核心判据
|
||||
|
||||
# 读取函数调用 hot_functions
|
||||
instrumentation(action='log', tag='vmp1', type_filter='tap_call', limit=200)
|
||||
instrumentation(action='log', tag_filter='vmp1', type_filter='tap_call', limit=200)
|
||||
<!-- v3.1.0: migrated from get_instrumentation_log -->
|
||||
→ 看有没有 btoa/atob/encodeURIComponent 等熟识函数
|
||||
```
|
||||
@@ -338,7 +354,8 @@ MCP 操作:
|
||||
|
||||
| 失败表现 | 可能原因 | 应对 |
|
||||
|----------|----------|------|
|
||||
| `instrumentation(action='log')` 返回空 | CSP 阻断 / url_pattern 未匹配 | 检查 CSP;确认 url_pattern 匹配到目标脚本 |
|
||||
| `files_rewritten > 0` 但 `__mcp_tap_installed=false` | 改写产物未解析/未执行,常见于嵌套 call/member/new 链 | 停止 AST route,按 regex → transparent 降级 |
|
||||
| runtime 已安装但 `instrumentation(action='log')` 返回空 | VMP 未触发 / tag_filter 不匹配 | 重新触发业务动作并检查 tag_filter |
|
||||
| `hot_keys` 只有 < 5 个属性 | regex 模式覆盖率不足 | 切换 mode="ast" 或增大 context_chars |
|
||||
| 签名值始终不一致 | 环境指纹参与哈希但未补齐 | 根据 hot_keys 逐项补齐环境 |
|
||||
| `navigate` 反复 412 | 观察者效应——Hook 破坏了签名 | 移除所有 pre_inject_hooks,改用源码级插桩 |
|
||||
@@ -348,7 +365,7 @@ MCP 操作:
|
||||
|
||||
```
|
||||
L1: instrumentation(action='install', mode="ast")
|
||||
→ 失败
|
||||
→ status + runtime 标记健康检查失败
|
||||
L2: instrumentation(action='install', mode="regex")
|
||||
→ 覆盖率不足
|
||||
L3: hook_jsvmp_interpreter(mode="transparent")
|
||||
@@ -363,7 +380,7 @@ L6: 向用户说明情况,建议浏览器自动化或 sdenv
|
||||
**关键规则**:
|
||||
- L1→L2→L3 是标准降级路径,每级必须尝试
|
||||
- L3→L4 仅对行为型反爬开放,签名型必须走 L3→L5
|
||||
- 到达 L6 前必须在 Session 档案中记录完整降级路径
|
||||
- 到达 L6 前必须在当前 case 记录完整降级路径
|
||||
- **禁止从 L1 直接跳到 L6**
|
||||
|
||||
---
|
||||
@@ -404,11 +421,11 @@ L6: 向用户说明情况,建议浏览器自动化或 sdenv
|
||||
2. **先 Hook 出口确定"要什么",再 Hook 入口确定"给了什么"** — 出口驱动分析
|
||||
3. **`hook_function(path, mode='trace', ...)` 对高频调用函数日志量可能爆炸** <!-- v3.1.0: migrated from trace_function --> — 必须设置 `max_captures` 限制
|
||||
4. **`get_trace_data` 返回的海量数据需要本地过滤** — 用反向追踪法效率最高
|
||||
5. **`instrumentation(action='install', mode="regex")` 对带模板字符串、正则字面量的代码可能误改写** <!-- v3.1.0: migrated from instrument_jsvmp_source --> — AST 模式更可靠
|
||||
5. **`instrumentation(action='install', mode="regex")` 对带模板字符串、正则字面量的代码可能误改写** <!-- v3.1.0: migrated from instrument_jsvmp_source --> — AST 具备语法感知,但仍必须验证改写产物已执行
|
||||
6. **前三板斧对签名型反爬不可用** — Hook `Function.prototype.apply` 会改变 toString 原生性;Proxy 在 navigator 上会被检测
|
||||
7. **`dump_jsvmp_strings` 前提是字符串未被动态解密** — 看到 `decoded_strings` 全是单字母乱码就是加密的
|
||||
8. **`search_code(keyword='while')` 在超大文件(380KB+)会返回大量无关结果** — 应使用 `search_code(keyword, script_url=url)` <!-- v3.1.0: migrated from search_code_in_script --> 配合更精确关键词
|
||||
9. **源码级插桩的两种模式都默认 `cache_rewritten=True`** — 避免重复改写
|
||||
9. **源码级插桩会在 MCP 内部按 URL 缓存改写结果** — 当前统一接口不需要传缓存参数
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user