# -*- coding: utf-8 -*- """ 在线抓取**全新**样本(2026-10-12 起,只使用本脚本产出的数据)。 抓什么: 1. prehandle -> sess / comm_captcha_cfg.tdc_path 2. 连抓 N 份 tdc.js(同一个 URL **每次请求都返回不同构建**,账本 #29) 3. 每份都算出: XTEA 密钥、eks、37 模块的字符串指纹、槽位序 产出目录: har/online/<时间戳>/ 用法: .venv/bin/python scripts/fetch_online_samples.py --n 4 """ from __future__ import annotations import argparse import hashlib import json import random import sys import time from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT / "tools")) from tcaptcha.client import TencentCaptchaClient # noqa: E402 from tcaptcha.crypto import extract_eks # noqa: E402 from tcaptcha.tdc_key import extract_xtea_key_pure # noqa: E402 OUT = ROOT / "har" / "online" def main() -> int: ap = argparse.ArgumentParser(description="在线抓取全新 tdc.js 样本") ap.add_argument("--n", type=int, default=4, help="抓几份 tdc.js") ap.add_argument("--aid", default="2039519451", help="雨云 aid(宽松,适合取样)") ap.add_argument("--tag", default=None, help="输出子目录名(默认时间戳)") ap.add_argument("--gap", type=float, default=1.0, help="两次抓取间隔秒") args = ap.parse_args() tag = args.tag or time.strftime("%Y%m%d-%H%M%S") d = OUT / tag d.mkdir(parents=True, exist_ok=True) print("输出目录:", d) client = TencentCaptchaClient(aid=args.aid, entry_url="https://app.rainyun.com/account/reward/earn#") try: tkid = str(random.randint(100000000, 999999999)) ch, cfg, sid = client.prehandle(tkid) print("prehandle: sess=%s sid=%s kind=%s" % (ch.sess[:16], sid, ch.kind)) tdc_path = cfg.get("tdc_path", "") if not tdc_path: print("❌ prehandle 没给 tdc_path") return 1 print("tdc_path:", tdc_path[:90]) (d / "meta.json").write_text(json.dumps({ "aid": args.aid, "sess": ch.sess, "sid": sid, "tdc_path": tdc_path, "kind": ch.kind, "fetched_at": time.strftime("%Y-%m-%d %H:%M:%S"), }, ensure_ascii=False, indent=1), encoding="utf-8") seen = set() got = 0 for i in range(args.n * 3): # 允许重试,直到拿到 n 份不同构建 if got >= args.n: break body = client.fetch_tdc(tdc_path) md5 = hashlib.md5(body.encode("utf-8")).hexdigest() if md5 in seen: time.sleep(args.gap) continue seen.add(md5) got += 1 name = "tdc_%02d_%s.js" % (got, md5[:8]) (d / name).write_text(body, encoding="utf-8") info = {"md5": md5, "bytes": len(body), "index": got} try: info["key"] = extract_xtea_key_pure(body)["key"] except Exception as e: info["key_error"] = "%s: %s" % (type(e).__name__, str(e)[:120]) info["eks"] = extract_eks(body)[:40] print(" [%d] %s %6d B md5=%s key=%s" % (got, name, len(body), md5[:12], info.get("key"))) (d / (name[:-3] + ".json")).write_text( json.dumps(info, ensure_ascii=False, indent=1), encoding="utf-8") time.sleep(args.gap) print() print(">>> 拿到 %d 份互不相同的新构建 -> %s" % (got, d)) return 0 if got else 1 finally: client.close() if __name__ == "__main__": raise SystemExit(main())