diff --git a/README.md b/README.md index 2ebedca..36a7e86 100644 --- a/README.md +++ b/README.md @@ -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 条重写 | diff --git a/SKILL.md b/SKILL.md index a0da93d..0a3cdc8 100644 --- a/SKILL.md +++ b/SKILL.md @@ -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="**/", 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 个场景速查 + 经验法则回迁核心层 | diff --git a/cases/universal-vmp-source-instrumentation.md b/cases/universal-vmp-source-instrumentation.md index 8768be5..4a6794b 100644 --- a/cases/universal-vmp-source-instrumentation.md +++ b/cases/universal-vmp-source-instrumentation.md @@ -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 等已知模式字段 \ No newline at end of file +- **低权重(参考)**:Cookie 中有 acw_tc / FSSBBIl1 / _abck / ak_bmsc / ttwid / msToken 等已知模式字段 diff --git a/references/experience-rules-full.md b/references/experience-rules-full.md index db4f543..9550c91 100644 --- a/references/experience-rules-full.md +++ b/references/experience-rules-full.md @@ -90,7 +90,7 @@ MCP 操作: → 是唯一能打开黑箱的工具 - instrumentation(action='log', tag='vmp1', type_filter='tap_get', limit=200) + instrumentation(action='log', tag_filter='vmp1', type_filter='tap_get', limit=200) → 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` 默认开启 diff --git a/references/jsvmp-source-instrumentation.md b/references/jsvmp-source-instrumentation.md index e79639b..6be245c 100644 --- a/references/jsvmp-source-instrumentation.md +++ b/references/jsvmp-source-instrumentation.md @@ -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"`(轻量兜底,零依赖) - **原理**:纯正则匹配 `[]`,替换为 `__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+) \ No newline at end of file +- MCP 上游:https://github.com/WhiteNightShadow/camoufox-reverse-mcp(当前基线 v1.1.1) diff --git a/references/mcp-cookbook.md b/references/mcp-cookbook.md index 0c25e82..4e544b5 100644 --- a/references/mcp-cookbook.md +++ b/references/mcp-cookbook.md @@ -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) -# 2. 装插桩(AST 模式优先,需 CDN;regex 模式兜底) +# 2. 装插桩(AST 在 MCP 侧运行;regex 模式兜底) [camoufox-reverse] instrumentation(action='install', 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", +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") ``` ### 使用 Cookie 归因分析(v2.5.0 新增) diff --git a/references/mcp-tool-reference.md b/references/mcp-tool-reference.md index e3ac6bb..ff02391 100644 --- a/references/mcp-tool-reference.md +++ b/references/mcp-tool-reference.md @@ -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=...)` | 源码级插桩(HTTP 层改写 VMP) | -| `instrumentation(action='log', tag=..., type_filter=...)` | 获取源码插桩日志(hot_keys/hot_methods/hot_functions) | +| `instrumentation(action='log', tag_filter=..., type_filter=...)` | 获取源码插桩日志(hot_keys/hot_methods/hot_functions) | | `instrumentation(action='status')` | 查看当前激活的源码插桩状态 | | `instrumentation(action='stop', url_pattern=...)` | 停止源码级插桩 | | `instrumentation(action='reload')` | 重载页面让 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')` | | diff --git a/references/path-a-four-tools.md b/references/path-a-four-tools.md index eb76201..38f9386 100644 --- a/references/path-a-four-tools.md +++ b/references/path-a-four-tools.md @@ -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', ...)` | 请求链路劫持、动态 Cookie、加密原语入口 | VM 内部自实现的 MD5/AES | 行为型 ✅ / 纯混淆 ✅ / 签名型 ❌ | | 第二斧 | 插桩解释器 | `hook_function(path, mode='trace', ...)` + `hook_jsvmp_interpreter(mode='proxy', trackProps=True)` | 能识别分发函数名时的调用链追踪 | 匿名 IIFE 包裹 + 高频日志爆炸 | 行为型 ✅ / 纯混淆 ✅ / 签名型 ❌ | | 第三斧 | 日志分析 | `get_jsvmp_log` + 反向追踪 | 已能捕获签名值 I/O 时反推公式 | 签名完全不出 VM 的黑箱模式 | 所有类型 ✅(纯被动分析,无副作用) | -| 第四斧 | 源码级插桩 | `instrumentation(action='install', ...)` + `instrumentation(action='log', ...)` | VM 内部调度、"全部在 opcode dispatch 循环里发生"的场景,`hot_keys` 直接暴露环境指纹集 | 极限大文件(5MB+)regex 模式覆盖率下降;AST 模式需 CDN | 所有类型 ✅,**签名型首选** | +| 第四斧 | 源码级插桩 | `instrumentation(action='install', ...)` + `instrumentation(action='log', ...)` | VM 内部调度、"全部在 opcode dispatch 循环里发生"的场景,`hot_keys` 直接暴露环境指纹集 | 极限大文件(5MB+)开销高;改写后必须验证 runtime 已执行 | 所有类型 ✅,**签名型首选** | ### 按反爬类型的适用性 @@ -56,7 +56,7 @@ 步骤 4:装源码级插桩(核心) instrumentation(action='install', url_pattern="**/", mode="ast", tag="vmp1") - (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) → 看 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 ) 模式选择: - - 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) → 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) → 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) → 看有没有 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', ...)` 对高频调用函数日志量可能爆炸** — 必须设置 `max_captures` 限制 4. **`get_trace_data` 返回的海量数据需要本地过滤** — 用反向追踪法效率最高 -5. **`instrumentation(action='install', mode="regex")` 对带模板字符串、正则字面量的代码可能误改写** — AST 模式更可靠 +5. **`instrumentation(action='install', mode="regex")` 对带模板字符串、正则字面量的代码可能误改写** — 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)` 配合更精确关键词 -9. **源码级插桩的两种模式都默认 `cache_rewritten=True`** — 避免重复改写 +9. **源码级插桩会在 MCP 内部按 URL 缓存改写结果** — 当前统一接口不需要传缓存参数 ---