feat(纯算): 增加 captcha 域 cookie 透传 + H3 单变量实验脚本
做了什么: - TencentCaptchaClient 新增可选 cookies 参数, 逐条写入 captcha 域 Cookie 头 (默认 None, 行为不变) - OneMsAdapter 新增 captcha_cookies 透传 - 新增 scripts/experiment_h3_cookie_ab.py: 只改 cookie 一个变量做 A/B 对照并自动判读 为什么: - 账本 §7.5 的 H3 (腾讯云登录 cookie uin/skey/qcmainCSRFToken/intl) 是唯一 尚未单变量验证过的差异; 其余 (环境 / 后端 / 出口 IP / 静默时长) 均已排除 怎么验证: - 离线: 带 cookie 时请求头为 Cookie=uin=..;intl=1, 不带时为 None (默认行为不变) - .venv/bin/python -m tcaptcha.selftest 全绿 (crypto_core + glyph 8/8 + xtea_key)
This commit is contained in:
@@ -0,0 +1,113 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
H3 单变量实验: captcha 域「带 / 不带」腾讯云登录 cookie 对 errorCode=12 的影响
|
||||||
|
|
||||||
|
依据: AGENTS.md §2.1 (用服务端响应画生死线) 与 har/1ms_dianxuan/ANALYSIS.md §7.5。
|
||||||
|
|
||||||
|
背景 (已确证): 12 与遥测后端 / 出口 IP / 静默时长均无关。成功样本
|
||||||
|
(har/签到成功-1.har) 的 captcha 域 prehandle 带着腾讯云登录 cookie
|
||||||
|
(uin / skey / qcmainCSRFToken / intl), 而本客户端此前完全不带 —— 这是唯一
|
||||||
|
未被单变量验证过的差异 (H3)。
|
||||||
|
|
||||||
|
本脚本**只改一个变量 (cookie)**, 其余参数完全相同, 依次跑两次:
|
||||||
|
A) 不带 cookie B) 带 cookie
|
||||||
|
判读:
|
||||||
|
A=12 且 B=0 -> H3 成立 (纯算流程只需补上该 cookie)
|
||||||
|
A=12 且 B=12 -> H3 排除, 转 H1/H2 (真实验证码页面上下文 / 自动化检测)
|
||||||
|
|
||||||
|
用法:
|
||||||
|
.venv/bin/python scripts/experiment_h3_cookie_ab.py --aid 195672344 \
|
||||||
|
--entry-url https://1ms.run/console/personal/benefits/check-in \
|
||||||
|
--uin 100012108023 --skey xxxxxxxx --csrf xxxxxxxx --intl 1 \
|
||||||
|
[--referer https://1ms.run/] [--proxy http://user:pass@host:port]
|
||||||
|
|
||||||
|
cookie 获取: 浏览器 F12 -> Application -> Cookies -> https://turing.captcha.qcloud.com/
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
if str(ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
|
||||||
|
from tcaptcha.client import KIND_TEXT_CLICK, TencentCaptchaClient # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def run_once(tag: str, cookies, args) -> dict:
|
||||||
|
client = TencentCaptchaClient(
|
||||||
|
aid=args.aid,
|
||||||
|
entry_url=args.entry_url,
|
||||||
|
referer=args.referer,
|
||||||
|
wb=args.wb,
|
||||||
|
accept_kinds=(KIND_TEXT_CLICK,),
|
||||||
|
telemetry_mode=args.telemetry,
|
||||||
|
proxy=args.proxy,
|
||||||
|
cookies=cookies,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = client.solve(max_retries=args.retries, max_refreshes=8)
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
raw = result.get("raw_response") or {}
|
||||||
|
print(f"\n[{tag}] success={result['success']} "
|
||||||
|
f"errorCode={raw.get('errorCode')} errMessage={raw.get('errMessage', '')!r}")
|
||||||
|
print(f" error={result.get('error', '')}")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
ap = argparse.ArgumentParser(description="H3: captcha 域 cookie 单变量实验")
|
||||||
|
ap.add_argument("--aid", default="195672344")
|
||||||
|
ap.add_argument("--entry-url", default="https://1ms.run/console/personal/benefits/check-in")
|
||||||
|
ap.add_argument("--referer", default="https://1ms.run/")
|
||||||
|
ap.add_argument("--wb", default="1")
|
||||||
|
ap.add_argument("--retries", type=int, default=3)
|
||||||
|
ap.add_argument("--telemetry", default="python_keyed", choices=["python_keyed", "python"])
|
||||||
|
ap.add_argument("--proxy", default=None)
|
||||||
|
# 腾讯云登录 cookie (从浏览器取)
|
||||||
|
ap.add_argument("--uin", default="")
|
||||||
|
ap.add_argument("--skey", default="")
|
||||||
|
ap.add_argument("--csrf", dest="csrf", default="", help="qcmainCSRFToken")
|
||||||
|
ap.add_argument("--intl", default="1")
|
||||||
|
ap.add_argument("--both", action="store_true",
|
||||||
|
help="先跑 A(不带) 再跑 B(带); 默认只跑 A")
|
||||||
|
ap.add_argument("--only", choices=["a", "b"], default="a",
|
||||||
|
help="只跑 A(不带 cookie) 或 B(带 cookie)")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
cookies = {k: v for k, v in {
|
||||||
|
"uin": args.uin, "skey": args.skey, "qcmainCSRFToken": args.csrf, "intl": args.intl,
|
||||||
|
}.items() if v}
|
||||||
|
if args.only == "b" or args.both:
|
||||||
|
if not cookies:
|
||||||
|
print("[-] 跑 B 必须提供 uin/skey/csrf 等 cookie", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
plan = ["a", "b"] if args.both else [args.only]
|
||||||
|
summary = {}
|
||||||
|
for which in plan:
|
||||||
|
if which == "a":
|
||||||
|
r = run_once("A 不带 cookie", None, args)
|
||||||
|
else:
|
||||||
|
r = run_once("B 带 cookie ", cookies, args)
|
||||||
|
summary[which] = (r.get("raw_response") or {}).get("errorCode")
|
||||||
|
|
||||||
|
print("\n=== 判读 ===")
|
||||||
|
print(json.dumps({"A_no_cookie": summary.get("a"), "B_with_cookie": summary.get("b")},
|
||||||
|
ensure_ascii=False))
|
||||||
|
if args.both:
|
||||||
|
a, b = summary.get("a"), summary.get("b")
|
||||||
|
if str(a) == "12" and str(b) == "0":
|
||||||
|
print("=> H3 成立: 补上 captcha 域 cookie 即可通过")
|
||||||
|
elif str(a) == "12" and str(b) == "12":
|
||||||
|
print("=> H3 排除: 转 H1/H2 (真实验证码页面上下文 / 自动化检测)")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
+22
-11
@@ -108,6 +108,7 @@ class TencentCaptchaClient:
|
|||||||
inter_attempt_delay: float = 0.25,
|
inter_attempt_delay: float = 0.25,
|
||||||
telemetry_mode: str = "python_keyed",
|
telemetry_mode: str = "python_keyed",
|
||||||
proxy: Optional[str] = None,
|
proxy: Optional[str] = None,
|
||||||
|
cookies: Optional[dict] = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
telemetry_mode 决定 collect/eks 怎么来 (见 tcaptcha.crypto.generate_telemetry), 只走纯算:
|
telemetry_mode 决定 collect/eks 怎么来 (见 tcaptcha.crypto.generate_telemetry), 只走纯算:
|
||||||
@@ -123,6 +124,11 @@ class TencentCaptchaClient:
|
|||||||
9 分钟静默后单发, **全部都是 12**。所以 12 不是遥测/环境/IP 问题,
|
9 分钟静默后单发, **全部都是 12**。所以 12 不是遥测/环境/IP 问题,
|
||||||
不要再走"补环境"或"换 IP"的路; 真正的变量 (登录态 / 真实验证码页面上下文)
|
不要再走"补环境"或"换 IP"的路; 真正的变量 (登录态 / 真实验证码页面上下文)
|
||||||
尚未被单变量实验区分。
|
尚未被单变量实验区分。
|
||||||
|
|
||||||
|
cookies: 可选, 逐条写入 captcha 域 (turing.captcha.qcloud.com) 的 Cookie 头。
|
||||||
|
成功样本 (har/签到成功-1.har) 的 captcha 域请求带着腾讯云登录 cookie
|
||||||
|
(uin / skey / qcmainCSRFToken / intl), 而本客户端此前完全不带 ——
|
||||||
|
这是账本 §7.5 的 H3 单变量实验入口 (默认 None, 不改变原有行为)。
|
||||||
"""
|
"""
|
||||||
self.aid = str(aid)
|
self.aid = str(aid)
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
@@ -146,21 +152,26 @@ class TencentCaptchaClient:
|
|||||||
self._text_solver = text_solver
|
self._text_solver = text_solver
|
||||||
self.subsid = 1
|
self.subsid = 1
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"User-Agent": self.user_agent,
|
||||||
|
"Referer": self.referer,
|
||||||
|
"Accept-Language": "zh-CN,zh;q=0.9,fr;q=0.8,de;q=0.7,en;q=0.6",
|
||||||
|
"sec-ch-ua": '"Google Chrome";v="155", "Chromium";v="155", "Not(A:Brand";v="24"',
|
||||||
|
"sec-ch-ua-mobile": "?0",
|
||||||
|
"sec-ch-ua-platform": '"macOS"',
|
||||||
|
"Sec-Fetch-Dest": "empty",
|
||||||
|
"Sec-Fetch-Mode": "cors",
|
||||||
|
"Sec-Fetch-Site": "cross-site",
|
||||||
|
}
|
||||||
|
# 可选: 透传 captcha 域的腾讯云登录 cookie (账本 §7.5 的 H3 单变量实验入口)
|
||||||
|
if cookies:
|
||||||
|
headers["Cookie"] = "; ".join(f"{k}={v}" for k, v in cookies.items())
|
||||||
|
|
||||||
self.http_client = httpx.Client(
|
self.http_client = httpx.Client(
|
||||||
base_url=self.BASE_URL,
|
base_url=self.BASE_URL,
|
||||||
trust_env=False, # 屏蔽环境代理干扰
|
trust_env=False, # 屏蔽环境代理干扰
|
||||||
proxy=self.proxy,
|
proxy=self.proxy,
|
||||||
headers={
|
headers=headers,
|
||||||
"User-Agent": self.user_agent,
|
|
||||||
"Referer": self.referer,
|
|
||||||
"Accept-Language": "zh-CN,zh;q=0.9,fr;q=0.8,de;q=0.7,en;q=0.6",
|
|
||||||
"sec-ch-ua": '"Google Chrome";v="155", "Chromium";v="155", "Not(A:Brand";v="24"',
|
|
||||||
"sec-ch-ua-mobile": "?0",
|
|
||||||
"sec-ch-ua-platform": '"macOS"',
|
|
||||||
"Sec-Fetch-Dest": "empty",
|
|
||||||
"Sec-Fetch-Mode": "cors",
|
|
||||||
"Sec-Fetch-Site": "cross-site",
|
|
||||||
},
|
|
||||||
timeout=self.timeout,
|
timeout=self.timeout,
|
||||||
follow_redirects=True,
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -55,13 +55,16 @@ class OneMsAdapter:
|
|||||||
|
|
||||||
def __init__(self, cookie: Optional[str] = None, user_agent: str = DEFAULT_USER_AGENT,
|
def __init__(self, cookie: Optional[str] = None, user_agent: str = DEFAULT_USER_AGENT,
|
||||||
timeout: float = 15.0, proxy: Optional[str] = None,
|
timeout: float = 15.0, proxy: Optional[str] = None,
|
||||||
proxy_api: Optional[str] = None, telemetry_mode: str = "python_keyed"):
|
proxy_api: Optional[str] = None, telemetry_mode: str = "python_keyed",
|
||||||
|
captcha_cookies: Optional[Dict[str, str]] = None):
|
||||||
self.user_agent = user_agent
|
self.user_agent = user_agent
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
# 出口代理 (可选)。注意: 实测换出口 IP 并不能解决 errorCode=12 (见 ANALYSIS.md §7)
|
# 出口代理 (可选)。注意: 实测换出口 IP 并不能解决 errorCode=12 (见 ANALYSIS.md §7)
|
||||||
self.proxy = proxy
|
self.proxy = proxy
|
||||||
self.proxy_api = proxy_api
|
self.proxy_api = proxy_api
|
||||||
self.telemetry_mode = telemetry_mode
|
self.telemetry_mode = telemetry_mode
|
||||||
|
# 可选: 透传到 captcha 域的腾讯云登录 cookie (账本 §7.5 的 H3 实验入口)
|
||||||
|
self.captcha_cookies = captcha_cookies
|
||||||
headers = {
|
headers = {
|
||||||
"User-Agent": user_agent,
|
"User-Agent": user_agent,
|
||||||
"Origin": ONE_MS_BASE,
|
"Origin": ONE_MS_BASE,
|
||||||
@@ -130,6 +133,7 @@ class OneMsAdapter:
|
|||||||
accept_kinds=(KIND_TEXT_CLICK,),
|
accept_kinds=(KIND_TEXT_CLICK,),
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
telemetry_mode=self.telemetry_mode,
|
telemetry_mode=self.telemetry_mode,
|
||||||
|
cookies=self.captcha_cookies,
|
||||||
)
|
)
|
||||||
connectivity_error = False
|
connectivity_error = False
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user