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
This commit is contained in:
yml2213
2026-10-12 03:38:18 +08:00
parent 80a122245a
commit 51dff870cf
18 changed files with 2102 additions and 91 deletions
+75
View File
@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
"""全量配平:37 个语义名 <-> 真机明文的 38 个槽。
约束(这些是硬证据,不是猜):
1. 值内容唯一确定语义名(MacIntel/UTF-8/ANGLE/GgoA.../98k/iframe...)
2. screen_fingerprint `"1440-2560-2530-24-*-*-|-*"` 反解出
screen_height=1440 / screen_width=2560 / screen_color_depth=24
3. itoken 有 `^\\d+:\\d+$` 特征 -> 占 **2 槽**(`641547489,1791732001`)
4. 37 项 -> 38 槽(仅 itoken 多占 1 槽),可与明文槽数对账
输出: 每个语义名的取值 + 置信度,供人工核对。
"""
import json
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "tools"))
import tdc_modules as tm # noqa: E402
RAW = "/tmp/1ms_bridge/real_collect_plain.txt"
raw = open(RAW, encoding="latin1").read().rstrip("\x00")
cd = json.loads(re.sub(r" {2,}", "", raw))["cd"]
print("明文 38 槽:")
for i, v in enumerate(cd):
print(" %2d %s" % (i, json.dumps(v, ensure_ascii=False)[:78]))
print()
rows = tm.analyze("har/1ms_dianxuan/tdc.js")
names = [r["name"] for r in rows]
print("语义名 %d 个" % len(names))
print()
# ---- 按内容给每个槽打标签(不依赖位置)----
def tags(v):
t = []
if isinstance(v, str):
if "Mozilla" in v: t.append("user_agent")
if v == "MacIntel": t.append("platform")
if re.fullmatch(r"[+-]\d\d", v): t.append("timezone_offset")
if v.upper() == "UTF-8": t.append("charset")
if "ANGLE" in v: t.append("webgl_renderer")
if "Inc." in v: t.append("webgl_vendor")
if v.startswith("GgoA"): t.append("canvas_fingerprint")
if "rand=" in v and "captcha" in v: t.append("template_url")
if v == "98k": t.append("constant_98k")
if v == "iframe": t.append("iframe_detect")
if "*" in v and "-" in v: t.append("screen_fingerprint")
if v.startswith("http") and "1ms.run" in v: t.append("location_href")
elif isinstance(v, list):
if v and all(isinstance(x, list) and len(x) == 3 for x in v): t.append("trajectory")
if v and all(isinstance(x, str) for x in v): t.append("languages")
elif isinstance(v, int) and not isinstance(v, bool):
if 1_600_000_000 <= v <= 2_100_000_000: t.append("timestamp?")
elif v > 1_000_000: t.append("bigint?")
else: t.append("smallint?")
elif isinstance(v, str) and v == "":
t.append("emptystr?")
return t
print("逐槽标签:")
for i, v in enumerate(cd):
print(" %2d %-22s %s" % (i, ",".join(tags(v)), json.dumps(v, ensure_ascii=False)[:50]))
print()
# ---- 反解 screen_fingerprint 的约束 ----
fp = next((v for v in cd if isinstance(v, str) and "*" in v and "-" in v), None)
if fp:
p = fp.split("-")
print("screen_fingerprint = %r -> 高%s 宽%s 色深%s" % (fp, p[0], p[1], p[3]))
for target, want in (("screen_height", int(p[0])), ("screen_width", int(p[1])),
("screen_color_depth", int(p[3]))):
idxs = [i for i, v in enumerate(cd) if v == want]
print(" %-20s 期望 %s -> 明文里出现在槽 %s" % (target, want, idxs))