Files
hello_js_reverse_skill/scripts/verify/align_session.py
T
yml2213 344eea2194 refactor(结构): 分层重组 + 清除全部废弃路线
做了什么:
- 包改为分层: tcaptcha/{protocol,telemetry,solvers,ocr,adapters,models,tests}
  * protocol/  client.py  transport.py  proxy.py
  * telemetry/ crypto.py  values.py  key.py  opcodes.py
               disasm.py  program.py  layout.py  modules.py
  * solvers/   text_click.py  icon_click.py  icon_click_rotate.py  slider.py
  * ocr/       engines.py  slicer.py
  * adapters/  one_ms.py
- tools/ 里原本是**包运行时依赖**的 tdc_{disasm,program,layout,modules}.py 并入 telemetry/
  (放在 tools/ 属于分层倒置),tools/ 只留 CLI: tdc_key_pure.py / tdc_profile.py / cdp_*.js
- crypto.py 529 -> 340 行: 切除「成功样本模板回填」与「Node 插桩兜底」两条老路;
  generate_telemetry 只剩 python_values 一种模式,传别的会直接报错并说明原因
- 求解器改名分清: solver.py -> solvers/icon_click.py(client 实际在用的 YOLO+ResNet18 ONNX),
  icon_click_solver.py -> solvers/icon_click_rotate.py(仅测试在用,留作对照)
- pyproject: 去掉 ultralytics/onnx(训练栈已删),补上真正在用的 onnxruntime

删除了什么:
- 雨云整条线 + dist/rainyun_qinglong(104M 旧快照)
- 模板回填: models/success_collect_template.b64、tdc_worker.js、analyze_collect_layout.py
- 「补环境」时代浏览器 JS: chrome_fetch/manual_captcha/probe_captcha/solve_in_browser/tdc_browser
- 旧 VM 探索工具 10 个(tdc_collectgen/tdc_slotmap/tdc_lift/tdc_vm_run/tdc_query/tdc_probe 等)
  + 8 个基于它们的旧 verify 脚本
- 训练/标注/评估: train_classifier/train_detector/prepare_yolo/web_labeler/collector/
  siamese/evaluate/benchmark_verify
- tcaptcha/core_js/(60 个分析产物)、TOOLCHAIN_REPORT.md(整篇在描述已删工具)
- 3 个已无任何引用的 PyTorch 权重:icon_classifier.pt 43M / yolo_detector.pt 5.9M /
  siamese_matcher.pt 1.6M —— 线上只跑 onnx,tcaptcha/ 117M -> 66M

为什么:
- 项目后面要专门承载「滑块 / 文字点选 / 图标点选」三种求解器;扁平包 + 运行时依赖混在
  tools/ + 两条已废弃的 collect 生成路线,会让后续扩展持续踩坑

怎么验证:
- .venv/bin/python -m tcaptcha.selftest                    全绿
- scripts/verify/verify_offline.py                         逐槽全等 + 分段几何一致
- scripts/verify/verify_values.py                          4 份真机明文 152/152 逐槽全等
- scripts/verify/send_test.py --mode python_values         1ms 签到页文字点选 errorCode=0
- 回滚点: tag backup/pre-refactor-20261012
2026-10-12 12:34:57 +08:00

94 lines
3.3 KiB
Python
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.
# -*- coding: utf-8 -*-
"""把「同会话 collect 明文」与「同会话 tdc.js 的语义名」按位置精确对齐。
这是"不手工配平"的正解,依据都是已确证的链路:
* 槽位序 == 清单模块的 require 序(账本 #34/#35,运行时反证过)
* 语义名 == tdc_modules.analyze(同一份 tdc.js)(账本 #44,37/37 唯一)
* collect 4 段加密(账本 #41),解密后按空格 pad 切回 cd
输入(全部来自**同一次会话**,无需任何人工假设):
/tmp/1ms_bridge/session_build.js 运行时 tdc.js(Debugger 取的)
/tmp/1ms_bridge/session_collect.json 同会话 TDC.getData(true) + getInfo
用法:
.venv/bin/python scripts/verify/align_session.py
"""
import base64
import json
import re
import sys
from pathlib import Path
from urllib.parse import unquote
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "tools"))
from tcaptcha.telemetry import modules as tm # noqa: E402
from tcaptcha.telemetry.crypto import split_collect_segments, xtea_decrypt_bytes # noqa: E402
from tcaptcha.telemetry.key import extract_xtea_key_pure # noqa: E402
BUILD = "/tmp/1ms_bridge/session_build.js"
SESS = "/tmp/1ms_bridge/session_collect.json"
js = open(BUILD, encoding="utf-8", errors="replace").read()
print("运行时 tdc.js: %d 字节" % len(js))
key = extract_xtea_key_pure(js)["key"]
print("本会话密钥 :", key)
print()
sess = json.load(open(SESS, encoding="utf-8"))
if "collect" not in sess and isinstance(sess.get("data"), dict):
sess = sess["data"] # cdp_collect.js 落盘的形状是 {target, ok, data:{...}}
collect = unquote(sess["collect"]) # 浏览器侧返回的是 URL-encoded
print("collect: %d 字符" % len(collect))
# 解密(4 段拼接:整体解一次即可,因为各段独立 XTEA-ECB)
raw = base64.b64decode(collect)
dec = xtea_decrypt_bytes(raw, key).decode("latin1")
plain = dec.rstrip("\x00")
print("解密 %d 字节" % len(dec))
runs = [m.span() for m in re.finditer(r" {2,}", plain)]
print("空格 pad 串:", runs)
print()
if len(runs) == 3:
c1, traj, c2, sd = split_collect_segments(plain)
print("段: chunk1=%d traj=%d chunk2=%d sd=%d" % (len(c1), len(traj), len(c2), len(sd)))
else:
c1 = traj = c2 = sd = None
print()
logical = re.sub(r" {2,}", "", plain)
obj = json.loads(logical)
cd = obj["cd"]
print("cd 槽数:", len(cd))
print()
rows = tm.analyze(BUILD)
names = [r["name"] for r in rows]
mods = [r["module"] for r in rows]
print("清单项 %d, 语义名 %d" % (len(mods), len(names)))
print()
# itoken 占 2 槽(^\\d+:\\d+$),其余 1 槽
multi = [i for i, n in enumerate(names) if n.startswith("itoken")]
slots = 0
print("%-2s %-6s %-34s %s" % ("#", "模块", "语义名", "值"))
print("-" * 110)
out = {}
k = 0
for i, (m, nm) in enumerate(zip(mods, names)):
n = 2 if i in multi else 1
if k + n > len(cd):
break
vals = cd[k:k + n]
k += n
val = vals[0] if n == 1 else vals
out[nm] = val
print("%-2d %-6d %-34s %s" % (i, m, nm, json.dumps(val, ensure_ascii=False)[:60]))
print()
print("覆盖槽数 %d / cd %d" % (k, len(cd)))
print()
Path("/tmp/1ms_bridge/name_values.json").write_text(
json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8")
print(">>> 语义名 -> 真值 已存 /tmp/1ms_bridge/name_values.json")