/** * Tencent Turing Captcha TDC Deep Proxy Runtime Probe * --------------------------------------------------- * 依据 AGENTS.md §4.3 与 §2.3 规范,通过深度 Proxy 探针拦截并记录 TDC 执行时 * 实际读取的全部环境属性清单与调用时序,彻底告别盲目补环境。 */ const fs = require("fs"); const path = require("path"); const vm = require("vm"); function createDeepProxy(target, prefix, logSet) { return new Proxy(target, { get(obj, prop, receiver) { if (typeof prop === "symbol" || prop === "inspect" || prop.startsWith("_")) { return Reflect.get(obj, prop, receiver); } const BUILTIN_TYPES = new Set([ "String", "Number", "Boolean", "Array", "Object", "Function", "RegExp", "Date", "Math", "JSON", "Error", "TypeError", "Uint8Array", "Uint8ClampedArray", "parseInt", "parseFloat", "encodeURIComponent", "decodeURIComponent", "setTimeout", "clearTimeout", "setInterval", "clearInterval" ]); if (BUILTIN_TYPES.has(String(prop))) { return Reflect.get(obj, prop, receiver); } const fullName = `${prefix}.${String(prop)}`; logSet.add(`GET: ${fullName}`); const val = Reflect.get(obj, prop, receiver); if (val !== null && typeof val === "object" && !(val instanceof RegExp)) { return createDeepProxy(val, fullName, logSet); } if (typeof val === "function") { const fnWrapper = function (...args) { logSet.add(`CALL: ${fullName}(${args.map((a) => JSON.stringify(a)).join(", ")})`); return val.apply(this === receiver ? obj : this, args); }; fnWrapper.prototype = val.prototype; return fnWrapper; } return val; }, set(obj, prop, value, receiver) { const fullName = `${prefix}.${String(prop)}`; logSet.add(`SET: ${fullName}`); return Reflect.set(obj, prop, value, receiver); }, }); } function createVirtualDate(initialTime) { let currentTime = initialTime || (Date.now() - 3200); const RealDate = Date; function FakeDate(...args) { if (!(this instanceof FakeDate)) { return new RealDate(currentTime).toString(); } if (args.length === 0) { return new RealDate(currentTime); } return new RealDate(...args); } FakeDate.prototype = Object.create(RealDate.prototype); FakeDate.now = () => currentTime; FakeDate.parse = RealDate.parse; FakeDate.UTC = RealDate.UTC; return { FakeDate, advance: (ms) => { currentTime += ms; return currentTime; }, now: () => currentTime, }; } function probeTdc(tdcFilePath, options = {}) { let tdcCode = options.tdcCode || fs.readFileSync(tdcFilePath, "utf-8"); // ---- 神谕钩子(自包含, 写在沙箱内的 window 上): 记录 PC, 并在进入 XTEA 例程时 dump 密钥寄存器 ---- const TARGET_PC = parseInt(process.env.XTEA_PC || "9109", 10); const BASE = (process.env.BASE_KEY || "1817655617,1381395306,1245924197,1767660346"); const HOOK = "window.__PC=window.__PC||[];window.__W=[" + BASE + "];" + "window.__H=function(H,E){window.__PC.push(H);" + "if(H<=" + TARGET_PC + "&&!window.__ARR){" + "for(var i=0;i=0)window.__WRD={at:H,reg:f,prev:window.__PC.slice(-120)};}" + "};"; // 可选: 在指定 PC dump 全部寄存器 (神谕二分/定位用) const DUMP_PC = parseInt(process.env.DUMP_PC || "-1", 10); if (DUMP_PC >= 0) { tdcCode = "window.__DPC=" + DUMP_PC + ";window.__DUMP=[];(function(){var _h=window.__H;" + "window.__H=function(H,E){_h(H,E);if(H===window.__DPC&&window.__DUMP.length<8){var o=[];" + "for(var i=0;i { if (type.includes("webgl")) { return { getExtension: (name) => { if (name === "WEBGL_debug_renderer_info") { return { UNMASKED_VENDOR_WEBGL: 37445, UNMASKED_RENDERER_WEBGL: 37446 }; } return null; }, getParameter: (param) => { if (param === 37445) return "Google Inc. (Apple)"; if (param === 37446) { return "ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)"; } return ""; }, getSupportedExtensions: () => ["WEBGL_debug_renderer_info"], }; } return { getImageData: () => ({ data: new Uint8ClampedArray(16) }), toDataURL: () => "data:image/png;base64,GgoAAAANSUhEUgAAASwAAACWCAYAAATf3v8bh+ryWcfSBSLkmN3N1PQinPL7Bi3TG0qE2CogAAAABJRU5ErkJggg==", }; }, toDataURL: () => "data:image/png;base64,GgoAAAANSUhEUgAAASwAAACWCAYAAATf3v8bh+ryWcfSBSLkmN3N1PQinPL7Bi3TG0qE2CogAAAABJRU5ErkJggg==", }; const fakeLoc = { href: "https://turing.captcha.gtimg.com/1/template/drag_ele.html?rand=1519713624347", hostname: "turing.captcha.gtimg.com", protocol: "https:", }; const fakeDoc = { referrer: options.referrer || "https://1ms.run/console/personal/benefits/check-in", characterSet: "UTF-8", documentElement: { clientWidth: 360, clientHeight: 360 }, createElement: (tag) => { if (String(tag).toLowerCase() === "canvas") return fakeCanvas; return { appendChild: () => {}, style: {}, setAttribute: () => {} }; }, getElementById: () => null, cookie: "", }; Object.defineProperty(fakeDoc, "onmousemove", { set(fn) { mouseHandler = fn; }, get() { return mouseHandler; }, }); const baseWindow = { screen: realScreen, navigator: realNavigator, document: fakeDoc, location: fakeLoc, innerWidth: 360, innerHeight: 360, devicePixelRatio: 2, Date: vClock.FakeDate, Math: Math, String: String, Number: Number, Boolean: Boolean, Array: Array, Object: Object, Function: Function, RegExp: RegExp, JSON: JSON, Error: Error, TypeError: TypeError, Uint8Array: Uint8Array, Uint8ClampedArray: Uint8ClampedArray, parseInt: parseInt, parseFloat: parseFloat, encodeURIComponent: encodeURIComponent, decodeURIComponent: decodeURIComponent, setTimeout: setTimeout, clearTimeout: clearTimeout, setInterval: setInterval, clearInterval: clearInterval, addEventListener: () => {}, removeEventListener: () => {}, }; // Attach window.top to simulate running in an iframe baseWindow.top = {}; baseWindow.window = baseWindow; baseWindow.self = baseWindow; const proxiedWindow = createDeepProxy(baseWindow, "window", accesses); // Execute in isolated V8 Context const sandbox = vm.createContext(proxiedWindow); if (options.verbose) console.log(`Running TDC inside Semantic Probe...`); vm.runInContext(tdcCode, sandbox); // Dispatch simulated human trajectory if clickPoints provided const clickPoints = options.clickPoints || options.trajectory || []; if (mouseHandler && clickPoints.length > 0) { const scale = 320.0 / 672.0; // 1. Initial human observation hesitation (400 ~ 650ms) vClock.advance(Math.floor(420 + Math.random() * 200)); // Starting mouse position (natural bottom-ish area: x~140, y~300) let curX = Math.round(130 + (Math.random() - 0.5) * 30); let curY = Math.round(290 + (Math.random() - 0.5) * 20); for (let pIdx = 0; pIdx < clickPoints.length; pIdx++) { const pt = clickPoints[pIdx]; // Target coordinates in iframe frame (left offset ~20, top ~60) const targetX = Math.round(20 + pt[0] * scale + (Math.random() - 0.5) * 4); const targetY = Math.round(60 + pt[1] * scale + (Math.random() - 0.5) * 4); const startStepX = curX; const startStepY = curY; // 15 ~ 22 movement steps per target point const steps = Math.floor(15 + Math.random() * 6); for (let s = 1; s <= steps; s++) { const t = s / steps; // Human kinematics: Ease-in-out cubic curve const ease = t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; const jitterX = s < steps ? (Math.random() - 0.5) * 1.6 : 0; const jitterY = s < steps ? (Math.random() - 0.5) * 1.6 : 0; curX = Math.round(startStepX + (targetX - startStepX) * ease + jitterX); curY = Math.round(startStepY + (targetY - startStepY) * ease + jitterY); // Advance 16 ~ 18ms per frame (60Hz vsync sampling) vClock.advance(16 + (Math.random() < 0.25 ? 1 : 0)); mouseHandler({ type: "mousemove", clientX: curX, clientY: curY, pageX: curX, pageY: curY, offsetX: curX, offsetY: curY, }); } // Pause at target (click dwell time: 90 ~ 160ms) const dwellMs = Math.floor(90 + Math.random() * 70); vClock.advance(dwellMs); mouseHandler({ type: "mousemove", clientX: curX, clientY: curY, pageX: curX, pageY: curY, offsetX: curX, offsetY: curY, }); } // Post-click think & submit delay (200 ~ 350ms) vClock.advance(Math.floor(220 + Math.random() * 120)); } if (options.verbose) console.log(`TDC loaded. Calling TDC.getData(!0)...`); let collect = null; let eks = null; if (sandbox.TDC && sandbox.TDC.getData) { collect = sandbox.TDC.getData(true); if (sandbox.TDC.getInfo) { const info = sandbox.TDC.getInfo(); eks = info ? info.info : null; } } const result = { success: !!collect, collect: collect, eks: eks, collectLen: collect ? collect.length : 0, collectPreview: collect ? collect.slice(0, 80) : null, eksPreview: eks ? eks.slice(0, 80) : null, accessCount: accesses.size, accesses: Array.from(accesses).sort(), __pc_len: (sandbox.__PC || []).length, __xtea: sandbox.__XT || null, __arr: sandbox.__ARR || null, __wrd: sandbox.__WRD || null, __dump: sandbox.__DUMP || null, __wp: sandbox.__WP || null, }; return result; } if (require.main === module) { const arg2 = process.argv[2]; if (arg2 === "-") { // Pipeline mode for Python / external callers let tdcCode = ""; process.stdin.setEncoding("utf-8"); process.stdin.on("data", (chunk) => { tdcCode += chunk; }); process.stdin.on("end", () => { try { const opts = process.argv[3] ? JSON.parse(process.argv[3]) : {}; // Temporary write to memory / execute directly const report = probeTdc(null, { ...opts, tdcCode }); process.stdout.write( JSON.stringify({ success: report.success, collect: report.collect, tlg: String(report.collectLen), eks: report.eks, source: "probe", }) + "\n" ); } catch (err) { process.stdout.write( JSON.stringify({ success: false, error: String(err) }) + "\n" ); } }); } else { const tdcFile = arg2 || "tcaptcha/core_js/tdc_sample.js"; const report = probeTdc(tdcFile); console.log("\n--- TDC Execution Result ---"); console.log(`Success: ${report.success}`); console.log(`Collect Length: ${report.collectLen}`); console.log(`Collect Preview: ${report.collectPreview}`); console.log(`EKS Preview: ${report.eksPreview}`); console.log(`Total Environment Accesses: ${report.accessCount}`); console.log("\n--- Top 35 Environment Accesses Logged by Probe ---"); report.accesses.slice(0, 35).forEach((a) => console.log(a)); const outReport = tdcFile.replace(/\.js$/, ".probe_report.json"); fs.writeFileSync(outReport, JSON.stringify(report, null, 2), "utf-8"); console.log(`\nDetailed probe report written to ${outReport}`); } } module.exports = { probeTdc };