feat(VM): 打通「清单项 -> cd 槽」的精确映射规则, 新增 tools/tdc_slotmap.py

装配循环(@4362)分支树读通:
  kind===2(@4820) -> 不进 cd: 清空 r3 转存 r4, r4 在收尾 @4905 另作他用
  v===null/undefined(@4799) -> 'null'
  typeof v==='number'(@4738) -> (v||0)
  kind===1(@4724) -> 原样拼(v 是 JSON 碎片串, 串内逗号会切出额外槽)
  其余(@4703) -> 加引号
  收尾 @4753: typeof w==='number' 才再 +1 槽

同一构建端到端校验: 37 项 -> 38 槽, 仅第 33 项(模块1)占 2 槽
('641547489,1791732001' -> 641547489 | 1791732001 精确命中)。
工具支持 --items(运行时取样) + --plain(解密明文) 对拍。
This commit is contained in:
yml2213
2026-10-12 00:36:31 +08:00
parent b7025356bc
commit 6460dd78d6
2 changed files with 175 additions and 1 deletions
+1 -1
View File
@@ -66,6 +66,7 @@
| 33 | **VM 程序结构层已吃透:顶层就是 62 个模块工厂的注册表** | 顶层 `@0` 做 `registry[id] = function(module, exports, require){...}`(`DUP; PUSH_IMM [id]; REVERSE_TOP2; JUMP -> CREATE_CLOSURE; SET_PROP`)。`tools/tdc_program.py --modules` 直接导出。修了两个真 bug:① 反汇编器漏了 `RET_TRUE`/`THROW` 是终结符(控制流穿过函数结尾 → 子程序归属全错);② 提升器终结符判定写死 opcode 数值 | 确证 |
| 34 | **`cd` 槽位序的来源已定位并可静态导出** | 装配链:清单模块 `arr[arr.length] = require(N)`(**追加,所以 require 顺序就是数组顺序**,已核)→ 分发模块按 `.on`/`.reset`/`.get` 分三数组 → 装配模块 `r3='{"cd":['` + `for(...) r3 += arr[i].get()`。线上构建实测 **37 个 require + 1 个字面量槽 = 38**,与真机 `cd` 槽数完全吻合;两份构建的 require 串不同(与 #31 一致)。工具:`tools/tdc_layout.py` | 确证(顺序)/ 语义标签部分 |
| 35 | **运行时反证通过:静态导出的 require 序 == 运行时真实 require 序(逐项一致)** | 在真机 iframe(`tdc.js` 实际执行的上下文,`window.TDC.getData` 就在这里)里包一层 `Function.prototype.apply`,模块工厂调用形状 = **4 参 `[object,object,object,function]`**(require 是最后一个),按工厂实例分组记账。触发一次刷新后取运行时串,用 `tools/cdp_script_source.js`(Debugger.scriptParsed + getScriptSource)取到**运行时真正执行的那份 tdc.js**(exactly-once,不能用重新 fetch 代替——#29 每次返回不同构建),两边串:<br>静态 = 运行时 = `[8,9,10,13,14,15,16,17,18,19,20,21,23,24,25,30,31,32,34,36,37,1,38,39,40,41,42,43,44,47,48,49,50,51,56,57,58]`(37 项,逐位相同)<br>→ 由此可判定:之前"部分槽语义对不上"是**标注启发式**的问题,**顺序本身正确**。工具:`scripts/experiment_require_order.py` | 确证 |
| 36 | **「清单项 → cd 槽」的精确规则(同一构建端到端校验确立)** | 装配循环(线上构建 `@4362`,370 条)分支树已读通:<br>`if(!item) continue;` `v,w,kind = item.get();`<br>· **`kind===2`(`@4820`)→ 不进 `cd`**:它把 `r3` 清空(`r3=""`)转存到 `r4`(`@4860`),`r4` 在收尾 `@4905` 被二次加工后另作他用(如随机串 `vXc29RxhvT34Q…`)<br>· `v===null/undefined`(`@4799`)→ `out += "null"`<br>· `typeof v==="number"`(`@4738`)→ `out += (v||0)`<br>· **`kind===1`(`@4724`)→ `out += v` 原样拼**:返回值是 JSON 碎片串,串里的逗号会**切出额外槽**(实测 `"641547489,1791732001"` → 2 槽)<br>· 其余(`@4703`)→ `out += '"' + v + '"'`<br>· 收尾 `@4753`:`typeof w === "number"` 才再 +1 槽(实测 kind=2 的第二元素是 null,不触发;此路径本次未命中)<br>→ **37 项 → 38 槽**,仅第 33 项(模块 1)占 2 槽。工具:`tools/tdc_slotmap.py`(`--items` 运行时取样 + `--plain` 明文对拍) | 确证 |
---
@@ -76,7 +77,6 @@
| 1 | 1ms aid=195672344 为何在答案正确时返回 `errorCode=12` | 需要"真实业务会话/真实验证码页面上下文"(登录态),而非环境指纹 | **中(未做单变量实验)** |
| 2 | 新版明文字段语义(哪一位是时间戳/指纹/轨迹) | 时间戳是 `1[6-9]\d{8}` 大整数;轨迹是 `[x,y,t]` 三元组 | 中高 |
| 3 | 明文里 `[360,360]`、`"2560-1440-1345-24-*-*-| -*"` 的作用 | 屏幕/窗口指纹串 | 中 |
| 4 | **采集器 `get()` 的返回值是 tuple, 装配模块按 kind 分支 —— "静态第 i 项 ↔ cd 第 i 槽"的朴素对齐会被破坏** | 装配模块里是 `r5 = item.get(); r9 = r5[0]; r10 = r5[1]; r11 = r5[2];` 然后按 `r11`(kind=1/2/其它)与 `typeof r9` 走不同分支再拼进 JSON。所以每个采集器"贡献几个槽/怎么格式化"要看 kind;实测 cap2 那份里 `c.screen.width` 的模块闭包返回 `[screen.width]`,而 cd 里 `2560` 落在 9 号槽、`1023` 落在 11 号槽 → 说明**不能按项序号直接对齐**。**未解**:kind 各值对应的拼装规则(读透装配模块 `@4362` 的 `4445~4939` 分支即可判定) | **待验证** |
---