feat(vm): 自上而下重构工具链 —— 新增提升器(lift)与查询CLI(query)

做了什么:
- 新增 tools/tdc_lift.py: 字节码 -> 可读伪代码 (AGENTS.md §3.3 步骤5)
  * 表达式栈还原 (算术/比较/调用/属性)
  * 寄存器引用惯用法还原 (PUSH_IMM_ARRAY n + MAKE_REG_PROP_REF -> rN[key])
  * 控制流 label/goto/if; CREATE_CLOSURE 递归展开成具名函数
- 新增 tools/tdc_query.py: who rN / at @pc / funcs / grep RE (对齐 §4.2 验收)
- 新增 tools/tdc_keytrace.js: 用真 VM 当神谕, 定位某值在哪段字节码诞生
- VM_SPEC §7/§8 重写为"自上而下五件套 + 路线", 并把 tdc_vm_run.py 标注为归档(打地鼠陷阱)
- 修正 lift 的 [n]->rN 归一 与 标签刷屏

为什么:
- 之前用 Python 复刻 JS 运行时去"跑"VM = §2.3 铁律2 的打地鼠陷阱, 收敛极慢
- §3.3 步骤5 要的是"读指令流得到算法; 闭包递归展开" -> 提升(lift)才是正路
- §2.1 最后一步: 读出算法后用目标语言原生重写, 跳过 VM/环境层

怎么验证:
- lift 首次把 XTEA 轮读成可读式: r2=(r2+2654435769); r?+=(((r6<<4)^(r6>>>5))+r6)^(r2+(r7[((r2>>>11)&3)]+855311))
- query 三个验收问题均可秒答: who r2 / at @9285 / funcs
This commit is contained in:
yml2213
2026-10-11 22:15:43 +08:00
parent 9008af0d18
commit 2e7aaff9ec
4 changed files with 552 additions and 16 deletions
+31 -16
View File
@@ -201,25 +201,40 @@ E.push(function g() {
---
## 7. 工具链映射
## 7. 工具链映射(对齐 AGENTS.md §3.2 五件套)
| 工具 | 作用 | 状态 |
| --- | --- | --- |
| `tools/tdc_disasm.py` | 纯 Python:提取字节码 + 可达性 CFG 反汇编(0 非法 opcode) | ✅ 可用(canonical) |
| `tools/tdc_vm_run.py` | 纯 Python:58 个 opcode 的具体解释器 + 宽松 JS 宿主 | 🚧 WIP(跑通顶层引导,未达密钥) |
| `tools/tdc_probe.js` | 深度 Proxy 沙箱驱动真实 tdc.js(**分析参考**,非交付路径) | 分析工具 |
| `tcaptcha/tdc_worker.js` | Node 插桩抓 XTEA 密钥常量(当前唯一 Node 依赖) | 可用·有缓存 |
> 目标(§4.2 验收):能秒答「**这个寄存器/字段谁写的**」「**这段代码在哪**」「**有多少子程序**」。
| # | 环节 | 工具 | 输入 → 输出 | 状态 |
| --- | --- | --- | --- | --- |
| ① | 清点 inventory | `tcaptcha/VM_SPEC.md` §3 + `tools/tdc_disasm.py` | tdc.js → opcode 表(70槽/58 handler/arity) + 字节码 | ✅ |
| ② | 提取 disasm | `tools/tdc_disasm.py` | tdc.js → 可达性 CFG 反汇编(0 非法 opcode) | ✅ |
| ③ | 运行时追踪 trace | `tools/tdc_trace.js` | 跑一次 → 真值 PC 序列(用于逐条对拍) | ✅ |
| ④ | **执行序渲染 lift** | `tools/tdc_lift.py` | 字节码 → **可读伪代码**(表达式栈还原 + 控制流 label/goto + **闭包递归展开**) | ✅ |
| ⑤ | 查询 CLI query | `tools/tdc_query.py` | `who rN` / `at @pc` / `funcs` / `grep RE` | ✅ |
| 辅助 | 神谕定位 | `tools/tdc_keytrace.js` | 驱动真 VM,定位"某个值在哪段字节码诞生" | ✅ |
| 归档 | ~~复刻 JS 运行时~~ | `tools/tdc_vm_run.py` | 用 Python 重写 JS 语义去"跑"VM | 🗄 归档为储备(**打地鼠陷阱**,§2.3 铁律2) |
**lift 实例(XTEA 轮,直接可读)**:
```
@09285:
r? = (r? + ((((r6 << 4) ^ (r6 >>> 5)) + r6) ^ (r2 + (r7[(3 & r2)] + 855311))));
r2 = (r2 + 2654435769); // sum += DELTA
if (([6] == ((0 >>> 11) & 3))) goto @9398;
if ((3 == ((r2 >>> 11) & 3))) goto @9361;
```
(教科书 XTEA:`v += (((v<<4)^(v>>>5)) + v) ^ (sum + K[(sum>>>11)&3])`;`855311` 即该构建的轮加盐。)
---
## 8. 解释器路线图(要"彻底理顺"必须补齐的点)
## 8. 下一步路线(自上而下)
1. **模块表**:记录每个 `CREATE_CLOSURE` 工厂的 `target`,识别"注册 vs 调用"。
2. **模块系统语义**:复刻 `require/exports` 式的装配顺序(谁在何时被调用)。
3. **宿主 `c` 的最小可用面**:`Object/Array/String/Math/JSON/RegExp/Date` + 事件/DOM 桩(严格只补"模块初始化真正读到的",不盲目补)。
4. **驱动入口**:复刻页面初始化序列,直到进入 XTEA 那段闭包。
5. **抓取产物**:寄存器中出现的"4 个可打印字符拼成的 32bit 数组" = XTEA 密钥(已确证形态)。
6. **校验**:与 `extract_xtea_key`(Node)结果逐字对齐,对齐后才敢替换。
**已完成的 L3 解构**:①字节码提取 ②正确反汇编 ③全部 opcode 语义 ④提升器(闭包递归展开)
⑤查询 CLI ⑥密钥形态与流向定位(`caps=[r7=r8]`、XTEA 轮 `reg7[(sum>>>11)&3]`)。
**下一步(不改路线)**:
1. 用 ④ 的 lilted 产物 **读** 密钥例程(不再"模拟")→ 用 Python 原生重写(§2.1 最后一步);
2. 与 `extract_xtea_key`(Node 神谕)逐字对齐后,替换掉最后一个 Node 依赖;
3. 把 lift 的栈模型补全(`SWAP/SET_STACK_LEN/REPLACE_TOP_IMM` 的表达式还原),消除残留的 `r<?>`。
> 已完成:①字节码提取 ②正确反汇编 ③全部 opcode 语义 ④密钥形态与流向定位(`caps=[r7=r8]`,XTEA 轮 `reg7[(sum>>>11)&3]`,本构建轮加盐 `723726`/`855311`)。
> 未完成:⑤模块系统 + 宿主驱动(本路线图的 1–4)。