#!/usr/bin/env node /** * 用**真 VM** 当神谕: 定位「XTEA 密钥数组」在哪段字节码被造出来。 * * 思路(绕开"用 Python 复刻 JS 运行时"的泥潭): * 给真 VM 的分发器打钩子, 每执行一条指令就扫一遍当前 E(寄存器区+栈), * 一旦出现「4 个元素、且每个元素 4 字节都是可打印 ASCII 的 32bit 数」 * —— 那就是 XTEA 密钥数组(已确证形态) —— 立刻 dump 最近 N 条 PC。 * 那段 PC 窗口就是密钥例程, 之后手工提升成 Python 即可。 * * 用法: node tools/tdc_keytrace.js [--window=400] */ const fs = require("fs"); const file = process.argv[2] || "tcaptcha/core_js/tdc_sample.js"; const winArg = process.argv.find((a) => a.startsWith("--window=")); const WINDOW = winArg ? parseInt(winArg.split("=")[1], 10) : 400; let src = fs.readFileSync(file, "utf8"); const needle = "M=G[z[H++]]()"; if (!src.includes(needle)) { console.error("[!] 未找到分发器"); process.exit(2); } globalThis.__PC = []; globalThis.__HITS = []; // 目标: 密钥 4 个 ASCII 打包字对应的字符串 (由分析确定, 可用环境变量覆盖) const KEY_ASCII = process.env.KEY_ASCII || "ldFPRVkjJCKeigfH"; const CHUNKS = [KEY_ASCII.slice(0, 4), KEY_ASCII.slice(4, 8), KEY_ASCII.slice(8, 12), KEY_ASCII.slice(12, 16)]; function printablePacked(v) { if (!Number.isInteger(v)) return false; const b = [(v >>> 24) & 255, (v >>> 16) & 255, (v >>> 8) & 255, v & 255]; return b.every((x) => x >= 32 && x < 127); } function isKeyArray(a) { if (!a || typeof a !== "object" || typeof a.length !== "number" || a.length !== 4) return false; for (let i = 0; i < 4; i++) if (!printablePacked(a[i])) return false; return true; } function asciiOf(a) { return a.map((x) => String.fromCharCode((x >>> 24) & 255, (x >>> 16) & 255, (x >>> 8) & 255, x & 255)).join(""); } // 命中判定: 4-int 数组 / 含任意 key chunk 的字符串 / 恰好16字符的字符串 function detect(v, depth, seen) { if (typeof v === "string") { if (v.indexOf(KEY_ASCII) >= 0) return "str-full:" + v; for (const c of CHUNKS) if (v.indexOf(c) >= 0) return "str-chunk:" + v; return null; } if (v && typeof v === "object") { if (depth > 3 || seen.has(v)) return null; seen.add(v); if (typeof v.length === "number" && isKeyArray(v)) return "arr4:" + asciiOf(v); // 数组元素 if (typeof v.length === "number") { for (let i = 0; i < v.length; i++) { const d = detect(v[i], depth + 1, seen); if (d) return "[" + i + "]." + d; } } // 对象属性 (含字符串键) let ks = []; try { ks = Object.keys(v); } catch (e) { ks = []; } for (const k of ks) { let x; try { x = v[k]; } catch (e) { continue; } const d = detect(x, depth + 1, seen); if (d) return "." + k + ":" + d; } } return null; } globalThis.__SCAN = function (E) { try { for (let i = 0; i < E.length; i++) { const cell = E[i]; if (!cell) continue; // 寄存器单元可能是 [值], 也可能本身就是那个值; 两种都扫 const cands = [cell]; if (Array.isArray(cell)) { cands.push(cell[0]); if (Array.isArray(cell[0])) cands.push(cell[0]); } let d = null, v = null; for (const c of cands) { d = detect(c, 0, new Set()); if (d) { v = c; break; } } if (d) { globalThis.__HITS.push({ reg: i, how: d, value: typeof v === "string" ? v : (Array.isArray(v) ? Array.from(v) : String(v)), pc: globalThis.__PC[globalThis.__PC.length - 1], window: globalThis.__PC.slice(-WINDOW), }); return; } } } catch (e) { /* ignore */ } }; const TARGET_PC = parseInt(process.env.TARGET_PC || "9109", 10); globalThis.__XTEA = null; globalThis.__HOOK = function (H, E) { if (H === TARGET_PC && !globalThis.__XTEA) { const cell = E[7]; globalThis.__XTEA = { at: H, r7_cell: (Array.isArray(cell) ? JSON.stringify(cell).slice(0, 400) : String(cell)), r7_val: (cell && typeof cell[0] === "object") ? JSON.stringify(cell[0]).slice(0, 400) : String(cell && cell[0]), prev_pcs: globalThis.__PC.slice(-300), }; } }; src = src.replace(needle, "M=(globalThis.__PC.push(H),globalThis.__HOOK(H,E),G[z[H++]]())"); globalThis.window = globalThis; globalThis.document = { cookie: "", referrer: "", createElement: () => ({ getContext: () => null, toDataURL: () => "", style: {} }), getElementById: () => null }; globalThis.navigator = { userAgent: "Mozilla/5.0", platform: "MacIntel", languages: ["zh-CN"], language: "zh-CN", hardwareConcurrency: 10, cookieEnabled: true }; globalThis.screen = { width: 2560, height: 1440, availWidth: 2560, availHeight: 1345, colorDepth: 24, pixelDepth: 24 }; globalThis.location = { href: "https://turing.captcha.gtimg.com/1/template/drag_ele.html", hostname: "turing.captcha.gtimg.com", protocol: "https:" }; globalThis.localStorage = { getItem: () => null, setItem: () => {} }; globalThis.sessionStorage = { getItem: () => null, setItem: () => {} }; globalThis.requestAnimationFrame = (f) => setTimeout(f, 0); globalThis.addEventListener = () => {}; globalThis.removeEventListener = () => {}; globalThis.__TENCENT_CHAOS_STACK = {}; let err = null; try { eval(src); } catch (e) { err = String((e && e.stack) || e); } // 顶层只"注册模块"; 真正算密钥要等宿主调用 -> 手动驱动 const names = Object.keys(globalThis).filter((k) => /^(TDC|TCaptcha|__|tcaptcha)/i.test(k)); process.stderr.write("[keytrace] globalThis 候选: " + JSON.stringify(names.slice(0, 20)) + "\n"); try { if (globalThis.TDC && typeof globalThis.TDC.getData === "function") { process.stderr.write("[keytrace] 调用 TDC.getData(true) 驱动...\n"); globalThis.__PC.push(-1); const c = globalThis.TDC.getData(true); globalThis.__COLLECT = c; process.stderr.write(`[keytrace] getData 返回长度=${c ? c.length : 0} 头部=${String(c).slice(0,90)}\n`); } else { process.stderr.write("[keytrace] 未找到 TDC.getData\n"); } } catch (e) { process.stderr.write("[keytrace] 驱动异常: " + String(e).slice(0, 200) + "\n"); } process.stderr.write(`[keytrace] 执行 PC=${globalThis.__PC.length} 命中=${globalThis.__HITS.length}${err ? " 顶层抛错: " + err.slice(0, 120) : ""}\n`); let out = globalThis.__HITS[0] || null; if (out) { process.stderr.write(`[keytrace] 命中寄存器 r${out.reg}, 密钥=${JSON.stringify(out.key)}, ASCII=${out.ascii}, 命中时 PC=@${out.pc}\n`); } const pcs=globalThis.__PC; const uniq=new Set(pcs); const inRange=[...uniq].filter(x=>x>=9000&&x<=9600); process.stdout.write(JSON.stringify({ key_hit: out, xtea: globalThis.__XTEA, pc_total: pcs.length, pc_unique: uniq.size, min: Math.min(...pcs), max: Math.max(...pcs), has_9109: uniq.has(9109), range9000_9600: inRange.sort((a,b)=>a-b).slice(0,40) }));