feat(密钥): 纯 Python 静态提取 XTEA 密钥 —— 去掉最后一个 Node 依赖 (6/6 构建与神谕逐字一致)

做了什么
- 新增 tcaptcha/tdc_opcodes.py: 按构建从解释器源码推导 opcode 表。
  实测 opcode 编号**每份构建整体重排**(同一源码 pc0 的 SET_STACK_LEN 在 6 份构建里
  = 53/19/32/57/55/1, JUMP = 60/17/42/58/21/2); 旧固化表只对 core_js 有效 ——
  用它反汇编其它构建会在 pc0 撞上 RET_TRUE, 得到"空反汇编"的假象。
  做法: 定位主循环 X[Y[Z++]]() 的 handler 数组 -> JS 感知切分 -> handler 源码规范化
  (去空白 + 局部变量按首次出现重命名) 取指纹 -> 与内置 58 条 core_js 参考指纹匹配;
  数组下标即该构建的 opcode, argc 由语义名取 (与 handler 体内 x[y++] 次数实测一致)。
- 新增 tcaptcha/tdc_key.py: 纯 Python XTEA 密钥静态还原器 (零 Node / 零 pc 硬编码)。
  反汇编 -> 定位含 delta 的 XTEA 闭包 -> 用"两层捕获链"选出真密钥数组:
  字闭包捕获的 B 帧寄存器, 经"定义 B 的那个闭包"的 caps 映射到 G 帧后须 == XTEA 闭包
  捕获的寄存器; 借此排除脚本里并行计算的第二套诱饵字 -> 读 4 个 (源串, 偏移, 下标)
  -> 折入轮加盐。
- tools/tdc_disasm.py: disassemble_cfg/_decode_one 支持注入按构建推导的 opcode 表
  (names/arity/dynamic), 默认仍回落 core_js 表; 控制流 opcode 改为按语义名定位。
- tools/tdc_key_pure.py: CLI (-v 打印推导过程 / --opcodes 打印 opcode 表 / --json)。
- tcaptcha/crypto.py: extract_xtea_key 改为"纯 Python 主路径 + Node 插桩兜底"。
- 新增 tcaptcha/tests/test_tdc_key.py 并接入 selftest;
  新增入库 fixture tcaptcha/tests/fixtures/tdc_fresh_2026-10-11.js (现场新抓的 tdc.js)。

为什么
- AGENTS.md §2.4 分层交付 / §1.1: 去掉最后一个 Node 依赖, 提高并发与抗发版能力;
- §5.7 负结果也是资产: 显式撤回"opcode 数值跨构建固定"与"密钥只能靠 Node 插桩拿"两条旧结论。

怎么验证
- 6 份构建纯 Python 结果 == Node 插桩 extract_xtea_key, **逐字一致**:
  core_js / root / 1ms tdc.js / tdc_live / tdc_latest / 2026-10-11 现场新抓 tdc_fresh。
- opcode 表 6/6 构建 58/58 handler 精确匹配, 0 未识别。
- .venv/bin/python -m tcaptcha.selftest 全绿 (含既有逐字节对拍 test_xtea_key)。
- 把 node_bin 指向不存在的程序时 generate_telemetry 仍产出 collect (证明已零 Node)。
This commit is contained in:
yml2213
2026-10-11 22:49:23 +08:00
parent 9286f77661
commit 2e2bbc876a
11 changed files with 1058 additions and 30 deletions
+8 -1
View File
@@ -9,7 +9,9 @@
| 工具 | 路径 | 核心功能 | 解决什么问题 |
| :--- | :--- | :--- | :--- |
| **Webpack 解包索引器** | [`tools/unpack_webpack.js`](file:///Users/yml/reverse/hello_js_reverse_skill/tools/unpack_webpack.js) | 将 230KB 单文件 bundle 拆解为 122 个独立模块,建立加签/风控关键词索引 | 圈定加签核心,剔除 80% 开源库与 UI 噪音 (L1 解构) |
| **Chaos VM 字节码反汇编器 (纯 Python)** | [`tools/tdc_disasm.py`](file:///Users/yml/reverse/hello_js_reverse_skill/tools/tdc_disasm.py) | 纯 Python 提取 45,636 字节码 + 可达性 CFG 反汇编(26,168 条可达指令,**0 处非法 opcode**);opcode 表由 VM 源码 AST 推导 | 彻底透明化 TDC 字节码调度器 (L2/L3 解构) |
| **Chaos VM 字节码反汇编器 (纯 Python)** | [`tools/tdc_disasm.py`](file:///Users/yml/reverse/hello_js_reverse_skill/tools/tdc_disasm.py) | 纯 Python 提取 45,636 字节码 + 可达性 CFG 反汇编(26,168 条可达指令,**0 处非法 opcode**);opcode 表可**按构建注入**(见下) | 彻底透明化 TDC 字节码调度器 (L2/L3 解构) |
| **按构建推导 opcode 表** | [`tcaptcha/tdc_opcodes.py`](file:///Users/yml/reverse/hello_js_reverse_skill/tcaptcha/tdc_opcodes.py) | 从解释器主循环 `X[Y[Z++]]()` 取出 handler 数组 → 解析 → 规范化指纹 → 跨构建匹配出**该构建的 opcode 表** | **opcode 编号每份构建整体重排**,写死即换脚本全错(6/6 构建 58/58 handler 匹配) |
| **纯 Python XTEA 密钥静态提取** | [`tcaptcha/tdc_key.py`](file:///Users/yml/reverse/hello_js_reverse_skill/tcaptcha/tdc_key.py) + [`tools/tdc_key_pure.py`](file:///Users/yml/reverse/hello_js_reverse_skill/tools/tdc_key_pure.py) | 反汇编 → 定位 XTEA 闭包(delta) → 两层捕获链选出真密钥数组 → 读 4 个字 + 折盐 | **零 Node** 拿到该构建 XTEA 密钥;6/6 构建与 Node 插桩逐字一致 |
| **深度 Proxy 语义探针** | [`tools/tdc_probe.js`](file:///Users/yml/reverse/hello_js_reverse_skill/tools/tdc_probe.js) | 零 Headless/CDP 依赖,通过深度 Proxy 沙箱执行 `tdc.js`,拦截并记录全部 95 项环境读取 | 毫秒级生成与当前会话时间戳和密钥 100% 自洽的动态 `collect` 与 `eks` (L4 数据层) |
---
@@ -114,4 +116,9 @@ plain = xtea_decrypt_bytes(raw, key).rstrip(b"\\x00")
print("成功解密探针生成的遥测载荷,长度:", len(plain))
print("明文前 150 字节:", plain[:150].decode("latin1"))
'
# 5. 纯 Python XTEA 密钥提取 (零 Node) + 跨构建回归
.venv/bin/python tools/tdc_key_pure.py -v har/1ms_dianxuan/live/tdc_fresh.js
.venv/bin/python -m tcaptcha.tests.test_tdc_key # 6 份构建 (含实时新抓) 全部断言
.venv/bin/python tools/tdc_key_pure.py --opcodes tcaptcha/tdc_sample.js # 打印该构建的 opcode 表
```