Files
hello_js_reverse_skill/tools/cdp_dump_scripts.js
T
yml2213 51dff870cf feat(collect): 纯离线生成打通 1ms 签到文字点选 —— errorCode=0 收口
做了什么:
- 新增 tcaptcha/tdc_values.py: 37 个语义名的取值生成器(静态画像 / 会话量 /
  每次采集量三层)+ build_plain 四段组装 + generate_collect 纯离线出包
- crypto: 新增 generate_telemetry(mode="python_values");轨迹生成统一到
  trajectory_points()(首点绝对坐标 + 增量 + 固定收尾 [1,1,12])
- crypto.split_collect_segments 重写: pad 只有 1 个空格时旧 ` {2,}` 判据会漏段,
  且 UA 串里的普通空格会假阳性 —— 改为「模 24 算术 + 不落在 JSON 字符串内部 + 结构校验」
- tools/tdc_modules: trajectory 指纹原先指向只做 JSON.stringify 的模块,改为
  mouseMoveEvent/touchStartEvent(真轨迹模块),37/37 不变
- client 默认 telemetry_mode 改为 python_values
- 新增 scripts/verify/{align_session,audit_names,verify_values,verify_offline,send_test}.py
  与 tools/{cdp_collect,cdp_dump_scripts,tdc_profile}.py

为什么:
- 账本 #40「谁在发 HTTP 是 12 的剩余自变量」被推翻。同 aid/同客户端/同求解器、
  答案都对,只换 collect 构造方式: 本次会话构建生成 -> 0 (4/4),
  旧模板回填 -> 12 (2/2)。真因是 collect 必须与本次会话构建同源。

怎么验证:
- .venv/bin/python -m tcaptcha.selftest  全绿(含新增四段切分与纯离线闭环回归)
- scripts/verify/verify_values.py        4 份真机明文 152/152 逐槽全等
- scripts/verify/verify_offline.py       逐槽全等 + 分段几何一致
- scripts/verify/send_test.py --mode python_values  1ms 签到页文字点选 errorCode=0 x4
2026-10-12 03:38:18 +08:00

104 lines
4.4 KiB
JavaScript
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.
#!/usr/bin/env node
/**
* cdp_dump_scripts.js —— 把某个 target 里**所有已解析脚本**逐个导出并标注特征。
*
* 为什么需要(2026-10-12 踩坑):
* cdp_script_source.js 取的是 `hits[hits.length-1]`(最后一个匹配),
* 在验证码 iframe(同 target 里有 dy-jy.js / dy-ele.js / tdc.js 多个脚本)里
* **可能取错**。有一次取到的 205KB 文件既没有 __TENCENT_CHAOS_VM 也没有 delta,
* 根本不是 tdc.js。
*
* 本工具: 一律全量导出 + 对每个脚本标注是否含 VM 锚点 / delta / 长度,绝不猜。
*
* 用法:
* node tools/cdp_dump_scripts.js --match drag_ele --outdir /tmp/1ms_bridge/scripts
*/
const fs = require('fs');
const path = require('path');
function arg(name, def) {
const i = process.argv.indexOf('--' + name);
if (i < 0) return def;
const v = process.argv[i + 1];
return (v === undefined || v.startsWith('--')) ? true : v;
}
const sleep = ms => new Promise(r => setTimeout(r, ms));
class Cdp {
constructor(ws) { this.ws = ws; this.id = 0; this.waits = new Map(); this.handlers = []; }
static async connect(url) {
const ws = new WebSocket(url);
await new Promise((res, rej) => { ws.onopen = res; ws.onerror = rej; });
const c = new Cdp(ws);
ws.onmessage = ev => {
const m = JSON.parse(ev.data);
if (m.id && c.waits.has(m.id)) {
const { res, rej } = c.waits.get(m.id);
c.waits.delete(m.id);
m.error ? rej(new Error(JSON.stringify(m.error))) : res(m.result);
} else if (m.method) { for (const h of c.handlers) h(m); }
};
return c;
}
send(method, params = {}) {
const id = ++this.id;
this.ws.send(JSON.stringify({ id, method, params }));
return new Promise((res, rej) => this.waits.set(id, { res, rej }));
}
on(fn) { this.handlers.push(fn); }
close() { try { this.ws.close(); } catch (e) {} }
}
(async () => {
const port = parseInt(arg('port', '9222'), 10);
const match = arg('match', null);
const outdir = arg('outdir', '/tmp/cdp_scripts');
const wait = parseInt(arg('wait', '2000'), 10);
const targets = (await (await fetch(`http://127.0.0.1:${port}/json/list`)).json())
.filter(t => t.type === 'page' || t.type === 'iframe');
const target = match ? targets.find(t => (t.url || '').includes(match)) : targets[0];
if (!target) throw new Error('没有匹配的 target: ' + match);
console.error(`[target] ${target.type} ${target.url.slice(0, 90)}`);
const cdp = await Cdp.connect(target.webSocketDebuggerUrl);
const scripts = [];
cdp.on(m => { if (m.method === 'Debugger.scriptParsed') scripts.push(m.params); });
await cdp.send('Debugger.enable');
await sleep(wait);
await cdp.send('Runtime.enable');
await cdp.send('Runtime.evaluate', { expression: '1' });
await sleep(500);
fs.mkdirSync(outdir, { recursive: true });
const index = [];
const seen = new Set();
for (const s of scripts) {
if (seen.has(s.scriptId)) continue;
seen.add(s.scriptId);
let src = '';
try {
const r = await cdp.send('Debugger.getScriptSource', { scriptId: s.scriptId });
src = r.scriptSource || '';
} catch (e) { continue; }
if (!src) continue;
const hasVM = src.includes('__TENCENT_CHAOS_VM');
const hasDelta = src.includes('2654435769');
const hasTDCNAME = src.includes('TDC_NAME');
const base = (s.url || 'inline').split('?')[0].split('/').pop() || 'inline';
const fn = `${String(s.scriptId).replace(/[^\w.-]/g, '_')}_${base}`.slice(0, 80);
fs.writeFileSync(path.join(outdir, fn), src, 'utf8');
index.push({
scriptId: s.scriptId, url: String(s.url || '(inline)').slice(0, 130),
bytes: src.length, hasVM, hasDelta, hasTDCNAME, file: fn,
});
}
fs.writeFileSync(path.join(outdir, 'index.json'), JSON.stringify(index, null, 1), 'utf8');
for (const r of index) {
console.log(`${r.hasVM ? 'VM ' : ' '}${r.hasDelta ? 'DELTA' : ' '}`
+ ` ${String(r.bytes).padStart(7)}B ${r.url}`);
}
console.error(`\n-> ${outdir} (共 ${index.length} 个脚本, 含 VM 的 ${index.filter(x => x.hasVM).length} 个)`);
cdp.close();
})().catch(e => { console.error('ERR', e.message); process.exit(1); });