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

6.5 KiB
Raw Blame History

腾讯验证码 (TCaptcha / TDC) 逆向工具链建设与协议解构报告

依据 AGENTS.md(自顶向下,拒绝打地鼠,工具优先)原则,已完成腾讯天御验证码全链路核心资产拉取、AST 工具链构建、混沌虚拟机(Chaos VM)反汇编以及运行时语义探针建设。


一、已建成的工具集清单

工具 路径 核心功能 解决什么问题
Webpack 解包索引器 tools/unpack_webpack.js 将 230KB 单文件 bundle 拆解为 122 个独立模块,建立加签/风控关键词索引 圈定加签核心,剔除 80% 开源库与 UI 噪音 (L1 解构)
Chaos VM 字节码反汇编器 (纯 Python) tools/tdc_disasm.py 纯 Python 提取 45,636 字节码 + 可达性 CFG 反汇编(26,168 条可达指令,0 处非法 opcode);opcode 表由 VM 源码 AST 推导 彻底透明化 TDC 字节码调度器 (L2/L3 解构)
深度 Proxy 语义探针 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 / 屏幕取值来自 单份成功样本,属于"复刻一份画像"而非"真实环境",因此不作为交付依据。


四、一键复验入口

# 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"))
'