feat: skill v3.2.0 — 硬约束Checklist五项压缩到三项,移除MCP session依赖,cases/成为唯一经验库

- SKILL.md 瘦身重写(核心层+按需加载references/)
- Checklist: CHECK-3/4(session)删除,五项→三项
- 新增6个references子文档(path-a/path-b/phase-details/experience-rules-full/mcp-tool-reference/common-pitfalls)
- 删除 references/domain-session-guide.md
- 工具引用对齐实际MCP(32工具),verify_signer_offline替代verify_against_session
- cases/_template.md 新增可验证事实清单段
- 经验法则session相关条目改写
- 红线四条、第一原则0-5、三分法、四板斧、六步法完整保留
This commit is contained in:
白夜
2026-04-18 13:36:14 +08:00
parent a5228600f4
commit 38b982fb1e
13 changed files with 2905 additions and 2067 deletions
+40 -38
View File
@@ -10,7 +10,7 @@ JSVMP(JavaScript Virtual Machine Protection)是一种高级 JS 代码保护
**核心原则:不反编译字节码,用行为追踪法(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 调度——是RS 5/6、Akamai sensor_data、webmssdk、obfuscator.io 这类"VM 自包含"场景的通用武器。详见 [`jsvmp-source-instrumentation.md`](./jsvmp-source-instrumentation.md)。
> **v2.5.0 更新**:第四板斧「源码级插桩」由 camoufox-reverse MCP v0.4.0 的 `instrumentation(action='install', ...)` 提供支持,在 HTTP 层改写 VMP 源码,对每个 `obj[key]` 和 `fn(args)` 插入 tap,捕获 VM 内部 switch/case 调度——是RS 5/6、Akamai sensor_data、webmssdk、obfuscator.io 这类"VM 自包含"场景的通用武器。详见 [`jsvmp-source-instrumentation.md`](./jsvmp-source-instrumentation.md)。 <!-- v3.1.0: migrated from instrument_jsvmp_source -->
---
@@ -52,18 +52,18 @@ function _0xefgh() {
```
MCP 操作(推荐顺序):
- list_scripts → 找到异常大的 JS 文件(100KB+)
- find_dispatch_loops(script_url=<大 JS 的 url>, min_case_count=20) ← v0.4.0 推荐首选
- scripts(action='list') → 找到异常大的 JS 文件(100KB+) <!-- v3.1.0: migrated from list_scripts -->
- search_code(keyword='switch', script_url=<大 JS 的 url>, context_chars=500) ← v0.4.0 推荐首选 <!-- v3.1.0: migrated from find_dispatch_loops -->
→ 返回 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 读取分发体
兜底手动方式(大文件 search_code 超时时):
- search_code(keyword="switch", script_url=N) → 前后 3 行上下文 <!-- v3.1.0: migrated from search_code_in_script -->
- search_code(keyword="case 0:|case 1:|case 2:", script_url=N) <!-- v3.1.0: migrated from search_code_in_script -->
- scripts(action='get', script_id=N, start_line=..., end_line=...) → 按 char_range 读取分发体 <!-- v3.1.0: migrated from get_script_source -->
```
---
@@ -77,7 +77,7 @@ MCP 操作(推荐顺序):
│ inject_hook_preset(xhr|fetch │
│ |crypto|cookie|websocket │
│ |debugger_bypass|runtime_probe)│
│ freeze_prototype │
│ hook_function(..., mode='intercept', ...) │ <!-- v3.1.0: migrated from freeze_prototype -->
│ analyze_cookie_sources │
└────────┬─────────────────────────┘
│
@@ -86,18 +86,19 @@ MCP 操作(推荐顺序):
中间层 ──→ ┌─┘ └─┐ ──→ 追踪执行链路
│
┌── 第二板斧 插桩解释器 ────────────┐ ┌── 第四板斧 源码级插桩 ──────────┐
│ 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 │
│ search_code(keyword='switch', │ │ instrumentation(action='install')│ <!-- v3.1.0: migrated from find_dispatch_loops, instrument_jsvmp_source -->
│ script_url=..., context_chars) │ │ instrumentation(action='log') │ <!-- v3.1.0: migrated from get_instrumentation_log -->
│ hook_function(mode='trace', │ │ → summary.hot_keys │ <!-- v3.1.0: migrated from trace_function -->
│ 分发函数/子函数) │ │ → summary.hot_methods │
│ hook_jsvmp_interpreter │ │ → summary.hot_functions │
│ (mode='proxy', trackProps=True)│ └────────┬────────────────────────┘ <!-- v3.1.0: migrated from trace_property_access -->
└────────┬────────────────────────┘
│
↓ ↓
┌── 第三板斧 日志分析 ──────────────────┐
│ get_trace_data + get_jsvmp_log │
│ + get_runtime_probe_log │
│ + get_instrumentation_log │
│ + instrumentation(action='log') │ <!-- v3.1.0: migrated from get_instrumentation_log -->
│ 反向追踪法(从签名值→明文) │
│ 多次请求对比法(找变化因子) │
└───────────────────────────────────────┘
@@ -117,13 +118,13 @@ MCP 操作(推荐顺序):
### 快速路径:v0.4.0 黄金 8 步流程(推荐先试,70%+ RS/Akamai/webmssdk 场景直接搞定)
```
Step 1 — find_dispatch_loops 确认 VMP + 记下 fn_name / char_range
Step 1 — search_code(keyword='switch', script_url=..., context_chars=500) 确认 VMP + 记下 fn_name / char_range <!-- v3.1.0: migrated from find_dispatch_loops -->
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 4 — instrumentation(action='install', url_pattern="**/<VMP 文件>", mode="ast", tag="vmp1") ← 核心新步 <!-- v3.1.0: migrated from instrument_jsvmp_source -->
Step 5 — instrumentation(action='reload') 让所有探针先于 VMP 生效 + 清日志 <!-- v3.1.0: migrated from reload_with_hooks -->
Step 6 — 触发业务操作
Step 7 — get_instrumentation_log(tag_filter="vmp1", limit=300) 看 hot_keys / hot_methods / hot_functions
Step 7 — instrumentation(action='log', tag_filter="vmp1", limit=300) 看 hot_keys / hot_methods / hot_functions <!-- v3.1.0: migrated from get_instrumentation_log -->
Step 8 — analyze_cookie_sources() 归因最终 cookie 来源
```
@@ -165,7 +166,7 @@ MCP 操作:
hook_code="console.log('[COOKIE_SET]', arguments[0], new Error().stack)",
position="before", non_overridable=True
) → 拦截 Cookie 写入(防覆盖)
- freeze_prototype(className="XMLHttpRequest", methodName="send")
- hook_function(className="XMLHttpRequest", methodName="send", mode='intercept') <!-- v3.1.0: migrated from freeze_prototype -->
→ 冻结 XHR.send 防止 VM 覆盖 Hook
- reload() → 刷新页面触发 Hook
- get_console_logs → 查看捕获的签名值和调用栈
@@ -226,9 +227,9 @@ MCP 操作:
```
MCP 操作:
- search_code(keyword="while.*true") → 找到解释器主循环
- search_code_in_script(script_id=N, keyword="case 0:|case 1:|case 2:")
- search_code(keyword="case 0:|case 1:|case 2:", script_url=N) <!-- v3.1.0: migrated from search_code_in_script -->
→ 在指定大文件中搜索 opcode 分发表(前后 3 行上下文,更精确)
- get_script_source → 读取解释器函数体
- scripts(action='get') → 读取解释器函数体 <!-- v3.1.0: migrated from get_script_source -->
判断标准:
- 函数体超过 500 行
@@ -244,8 +245,9 @@ MCP 操作:
```
MCP 操作:
- trace_function(
- hook_function( <!-- v3.1.0: migrated from trace_function -->
function_path="解释器分发函数名",
mode='trace',
log_args=true, log_return=true, max_captures=500,
persistent=True → 持久化追踪,跨导航不丢失
)
@@ -264,8 +266,8 @@ MCP 操作:
从第一轮日志中识别被频繁调用的子函数(通常 3-5 个核心函数):
MCP 操作:
- trace_function(function_path="子函数A", log_args=true, log_return=true, log_stack=true)
- trace_function(function_path="子函数B", log_args=true, log_return=true, log_stack=true)
- hook_function(function_path="子函数A", mode='trace', log_args=true, log_return=true, log_stack=true) <!-- v3.1.0: migrated from trace_function -->
- hook_function(function_path="子函数B", mode='trace', log_args=true, log_return=true, log_stack=true) <!-- v3.1.0: migrated from trace_function -->
- 再次触发请求
- get_trace_data → 分析子函数的输入输出
```
@@ -296,7 +298,7 @@ MCP 操作:
```
MCP 操作:
- trace_property_access(target_expression="疑似签名容器对象")
- hook_jsvmp_interpreter(mode='proxy', trackProps=True, target_expression="疑似签名容器对象") <!-- v3.1.0: migrated from trace_property_access -->
→ Proxy 级别属性访问追踪,自动记录读写操作
- get_property_access_log → 获取属性访问记录
- compare_env → 收集完整浏览器环境信息
@@ -445,7 +447,7 @@ MCP 操作:
· 在 add_init_script 中保存原始引用,在 VM 重写之前
- 或 Hook 网络层:
· start_network_capture → 不依赖 JS 层 Hook,直接在协议层捕获
· network_capture(action='start') → 不依赖 JS 层 Hook,直接在协议层捕获 <!-- v3.1.0: migrated from start_network_capture -->
· list_network_requests → get_network_request → 获取完整请求详情
```
@@ -470,7 +472,7 @@ MCP 操作:
```
- 不要试图理解嵌套关系
- 依然从 I/O 两端入手,Hook 最终的出口和最初的入口
- 增加 trace_function 的 max_captures 到 1000+
- 增加 hook_function(mode='trace') 的 max_captures 到 1000+ <!-- v3.1.0: migrated from trace_function -->
- 用时间戳过滤法缩小日志范围
```
@@ -533,9 +535,9 @@ navigate(url) 不加任何 hook 观察 redirect_chain
签名型 JSVMP 推荐走的路:
```
1. instrument_jsvmp_source(mode="ast", tag="...") (第四板斧)
2. reload_with_hooks() 让插桩后的 VMP 跑完挑战
3. get_instrumentation_log(tag_filter, type_filter="tap_get")
1. instrumentation(action='install', mode="ast", tag="...") (第四板斧) <!-- v3.1.0: migrated from instrument_jsvmp_source -->
2. instrumentation(action='reload') 让插桩后的 VMP 跑完挑战 <!-- v3.1.0: migrated from reload_with_hooks -->
3. instrumentation(action='log', tag_filter, type_filter="tap_get") <!-- v3.1.0: migrated from get_instrumentation_log -->
4. 基于 hot_keys 补最小环境,在 jsdom 或纯 Python 侧独立跑
(此时 jsdom 补环境的粒度比路径 B 小得多,只补 hot_keys 显示的属性)
```
@@ -600,18 +602,18 @@ JSVMP 签名还原决策树:
在 JSVMP 分析过程中,确保以下每项都已完成:
**第一板斧 — Hook 出入口**:
- [ ] `find_dispatch_loops` 确认是 VMP(case_count > 50)
- [ ] `search_code(keyword='switch', script_url=..., context_chars=500)` 确认是 VMP(case_count > 50) <!-- v3.1.0: migrated from find_dispatch_loops -->
- [ ] `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_function(..., mode='intercept', ...)` 防止 VMP 覆盖 hook <!-- v3.1.0: migrated from freeze_prototype -->
**第二板斧 — 插桩解释器**:
- [ ] `hook_jsvmp_interpreter(script_url=<VMP basename>)` 多路径探针(apply/call/bind + Reflect.*/Proxy)
- [ ] 分层 `trace_function`(粗 → 中 → 细,用 `max_captures` 限制日志量)
- [ ] `trace_property_access(["navigator.*", "screen.*", ...])` 监控签名容器
- [ ] 分层 `hook_function(mode='trace')`(粗 → 中 → 细,用 `max_captures` 限制日志量) <!-- v3.1.0: migrated from trace_function -->
- [ ] `hook_jsvmp_interpreter(mode='proxy', trackProps=True, targets=["navigator.*", "screen.*", ...])` 监控签名容器 <!-- v3.1.0: migrated from trace_property_access -->
**第三板斧 — 日志分析**:
- [ ] `get_jsvmp_log` + `get_trace_data` + `get_runtime_probe_log`(如果装了 runtime_probe)+ `get_console_logs`
@@ -619,13 +621,13 @@ JSVMP 签名还原决策树:
- [ ] 多次请求对比,确认变化因子和固定因子
**第四板斧 — 源码级插桩 [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`
- [ ] `instrumentation(action='install', url_pattern=..., mode="ast"|"regex", tag=...)` 装好 <!-- v3.1.0: migrated from instrument_jsvmp_source -->
- [ ] `instrumentation(action='reload')` 让插桩先于 VMP 生效 <!-- v3.1.0: migrated from reload_with_hooks -->
- [ ] 读 `instrumentation(action='log')` 三个 summary:`hot_keys` / `hot_methods` / `hot_functions` <!-- v3.1.0: migrated from get_instrumentation_log -->
- [ ] `hot_keys` 给出 VMP 读取的环境指纹集(top 30)
- [ ] `hot_methods` 给出 VMP 调用的方法(ObjectType.methodName 格式)
- [ ] `hot_functions` 给出 VMP 调用的函数名
- [ ] `stop_instrumentation` 完工清理
- [ ] `instrumentation(action='stop')` 完工清理 <!-- v3.1.0: migrated from stop_instrumentation -->
**环境/时序**:
- [ ] 若目标是首屏挑战页,用 `navigate(pre_inject_hooks=[...])` 或 `via_blank=True`