Files
hello_js_reverse_skill/tcaptcha/TOOLCHAIN_REPORT.md
T
yml2213 d52368be2b feat(工具链): 纯 Python 可达性 CFG 反汇编器 + 废弃旧线性反汇编器
做了什么:
- 新增 tools/tdc_disasm.py: 纯 Python 提取 tdc.js 字节码数组 + 可达性 CFG 反汇编
  * opcode 表由 VM 源码 AST 推导 (70 槽 / 58 handler, 操作数个数 = handler 内 z[H++] 次数)
  * 复刻 VM 内联的 base64 解码 + 游程展开 (含科学计数法常量), 无任何 JS 运行时
  * 遍历根含 JUMP/JUMP_IF_TRUE/PUSH_TRY catch/CREATE_CLOSURE 闭包体
- tools/tdc_vm_disasm.js 降级为废弃 shim (转发到 Python 版)
- TOOLCHAIN_REPORT.md 更新工具行与复验命令; tdc_sample.disasm.txt 用正确实现重新生成

为什么:
- 旧线性反汇编对字节码里的数据区继续扫描会失步: 实测 73 处 UNKNOWN (16 处 >69 的
  "不可能" opcode), 且未把闭包体当遍历根 -> 大半个程序不可达, 输出不可信

怎么验证:
- .venv/bin/python tools/tdc_disasm.py tcaptcha/core_js/tdc_sample.js
  -> bytecode=45636 (与旧实现一致), 可达指令 26168, illegal opcodes: []
- 反汇编首次干净读出 XTEA 轮函数: PUSH_IMM 2654435769 / ADD / ... / SHR_UNSIGNED 11 / &3
  并定位密钥数组在寄存器 7, 本构建轮加盐为 723726 / 855311 (均为立即数)
2026-10-11 21:47:27 +08:00

118 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 腾讯验证码 (TCaptcha / TDC) 逆向工具链建设与协议解构报告
依据 `AGENTS.md`(自顶向下,拒绝打地鼠,工具优先)原则,已完成腾讯天御验证码全链路核心资产拉取、AST 工具链构建、混沌虚拟机(Chaos VM)反汇编以及运行时语义探针建设。
---
## 一、已建成的工具集清单
| 工具 | 路径 | 核心功能 | 解决什么问题 |
| :--- | :--- | :--- | :--- |
| **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 解构) |
| **深度 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 数据层) |
---
## 二、核心逆向成果与证据账本
### 1. TDC 混沌虚拟机(Tencent Chaos VM)指令集
`__TENCENT_CHAOS_VM` 采用经典基于栈的虚拟机架构,包含 70 条操作码:
- 操作码分发器循环:`for (var M = !1; !M;) M = G[z[H++]]();`
- `z`: 45,636 整数组成的解压字节码序列
- `H`: 程序计数器 (PC)
- 关键操作码:
- `00`: `PUSH_STR_CHAR`(字符串构造)
- `03/30/62/35/36/65`: 位运算 (`^`, `&`, `|`, `>>`, `<<`, `>>>`)
- `06/34/41/61`: 函数调用与对象实例化 (`apply`, `new`)
- `46/68/47`: 虚拟寄存器读写与分配
- `56/48/49`: 异常处理栈(Try-Catch 保护链)
- `57`: 闭包创建器(动态子程序展开)
### 2. TDC 关键检测项白名单(已由探针全量捕获,共 95 项)
探针运行实测表明,TDC 实际探测的环境特征仅 95 项,绝非海量属性:
1. **自动化/反爬标记检测**:
- `document.$cdc_asdjflasutopfhvcZLmcfl_`(精准探测 ChromeDriver 变量)
- `window.navigator.webdriver`
- `window.domAutomation`
- `window.fxdriver_id`
- `window.callPhantom` / `casper` / `CasperError` / `patchRequire`
- `window.ubot` / `WebPage`
2. **显示与硬件画像**:
- `screen.width` (2560), `screen.height` (1440), `availHeight` (1345), `colorDepth` (24)
- `deviceXDPI`, `logicalXDPI`, `fontSmoothingEnabled`
- `navigator.hardwareConcurrency` (10), `platform` ("MacIntel"), `languages`
3. **渲染上下文**:
- WebGL Renderer: `"ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)"`
- WebGL Vendor: `"Google Inc. (Apple)"`
- Canvas Fingerprint: 成功样本原图 Base64 (368 字节)
4. **容器关系**:
- `window.top !== window`(识别是否运行在验证码 iframe 内)
- `document.getElementById("tCaptchaDyContent")`
- `window.TCaptchaReferrer`(宿主页面 URL 绑定)
### 3. L4 遥测协议数据解密 (`cd` 38 维字段)
成功解密的 `collect` 明文格式为 `{"cd": [...], "sd": {"od": "C", "ft": "..."}}`:
- `cd[0..1]`: 屏幕宽、平台 (`2560`, `"MacIntel"`)
- `cd[2]`: Canvas 指纹
- `cd[8]`: 宿主页面 URL (`document.referrer`)
- `cd[9]`: User-Agent
- `cd[15/18]`: WebGL 渲染器与厂商
- `cd[19/22/27]`: **当前会话的秒级与毫秒级 Epoch 时间戳**(必须与请求自洽,旧方案硬编码是致死原因之一)
- `cd[20]`: **鼠标相对增量轨迹**:首点为绝对坐标 `[start_x, start_y, delay_ms]`,后续为增量 `[dx, dy, dt_ms]`(dt 均值 16~18ms,模拟 60Hz 采样)
- `cd[26]`: 会话校验和与随机种子
- `cd[31]`: 执行容器类型 (`"iframe"`)
---
## 三、关于 errorCode=12 的诚实结论(**已更正**)
> 本报告早期版本曾把 12 归因于"`re.sub` 破坏校验和 / 时间戳错位 / 机械假轨迹 / CDP 特征泄露"。
> 这 4 条**均与本项目自己的证据账本冲突**(见 `har/1ms_dianxuan/ANALYSIS.md` §7 与 `PROGRESS.md` §三),
> 现更正如下:
| 早期断言 | 现状 |
| --- | --- |
| 时间戳错位(硬编码数天前)| ❌ 撤回:纯算路径自模板回填起就刷新为当前时间戳,仍 12 |
| 机械假轨迹 dt=0/1ms | ❌ 撤回:换真实 ms 分布后仍 12 |
| CDP 特征泄露 | ❌ 撤回:纯 Python(无 CDP)后端同样 12 |
| `re.sub` 破坏内部校验和 | ⚠️ 仅为假设,无对照实验 |
**已确证**:12 与遥测后端 / 出口 IP / 静默时长均无关。真正的自变量
(登录态 / 真实验证码页面上下文)尚未由单变量实验区分 —— 见 `ANALYSIS.md` §7.5。
### 关于深度 Proxy 探针(`tools/tdc_probe.js`)的定位
探针是**分析工具**(用于枚举 tdc.js 真实读取了哪些环境属性),**已不在遥测生成路径上**
(`crypto.generate_telemetry` 只保留纯算)。它注入的 Canvas / WebGL / 屏幕取值来自
**单份成功样本**,属于"复刻一份画像"而非"真实环境",因此不作为交付依据。
---
## 四、一键复验入口
```bash
# 1. 验证 Webpack 解包与核心模块提取
node tools/unpack_webpack.js
# 2. 验证 TDC 虚拟机反汇编与字符串提取
node tools/tdc_vm_disasm.js tcaptcha/core_js/tdc_sample.js # 已废弃, 转发到下面的纯 Python 实现
.venv/bin/python tools/tdc_disasm.py tcaptcha/core_js/tdc_sample.js
# 3. 验证深度 Proxy 语义探针执行并产出 100% 合法 collect 与 eks
node tools/tdc_probe.js tcaptcha/core_js/tdc_sample.js
# 4. 验证探针生成的 collect 成功解密
.venv/bin/python -c '
import json, base64, urllib.parse, subprocess
from tcaptcha.crypto import extract_xtea_key, xtea_decrypt_bytes
with open("tcaptcha/core_js/tdc_sample.js") as f: code = f.read()
key = extract_xtea_key(code)
res = subprocess.check_output(["node", "-e", "const {probeTdc}=require(\"./tools/tdc_probe.js\"); const r=probeTdc(\"tcaptcha/core_js/tdc_sample.js\"); console.log(r.collect);"], text=True).strip()
raw = base64.b64decode(urllib.parse.unquote(res))
plain = xtea_decrypt_bytes(raw, key).rstrip(b"\\x00")
print("成功解密探针生成的遥测载荷,长度:", len(plain))
print("明文前 150 字节:", plain[:150].decode("latin1"))
'
```