feat(验证): 纯算链路在真实 aid 上端到端出 ticket —— 归因 1ms 的 12 为 aid 级风控
做了什么 - 新增 scripts/check_real_tencent_aid.py: 用**可反复测**的真实 aid 回归纯算链路 (默认 aid=190758559 / mliev.login.wang 短信登录; 也可 --aid 195672344 对照)。 - 账本 har/1ms_dianxuan/PROGRESS.md: 已知 #26~#28、已排除新增三行、下一步重写为"已闭合结论 + 可复现靶子"。 为什么 (AGENTS §2.1 第 0 问 / §5.7) - 用户提供可重复的真机登录抓包 har/原始抓包/登录抓包-点选.har, 得以把"12 到底是不是我们的问题" 用一个能成功的目标做单变量反证。 怎么验证 (2026-10-11 实测) - aid 190758559: 本客户端 solve() => errorCode=0 + 真 ticket, 连续 4 次: tr03lxdviLWtn4uXGjWjmrLQ / tr03A5zTSQ9bCog51sDm / tr039KeeQxXX-OdduJoY / tr03fGUOpcJTZWXWmZG7Uioc0Y8GlHPn 其中 **httpx + 无 cookie + 无任何头部保真** 也拿到 0 —— 证明纯算链路本就完好。 - aid 195672344 (1ms): 在 cookie / curl_cffi Chrome 指纹 / 逐 endpoint 头保真 / collect 内容 逐字节同构 / 带签名 identity_context 先行注册 —— 全部仍 12。 - 结论: 1ms 的 12 是该 aid 自身风控不放行"非真机页面"会话; 请求侧已无自变量, 纯协议侧无解, 只能真机桥接或换入口。
This commit is contained in:
@@ -56,6 +56,9 @@
|
||||
| 23 | **纯 Python 密钥能解密 1ms 真实成功样本的 collect** | 用 `har/签到成功-1.har` 里那次 ec=0 之前的 tdc.js 提密钥 → 解出该次 collect 明文 `{"cd":[2560,"MacIntel",...` 1353B;且与 `models/success_collect_template.b64` **逐字节同构**(仅轨迹/时间戳/盐被回填)| 确证 |
|
||||
| 24 | **1ms 真实成功链路已完整解析** | prehandle(带腾讯云登录 cookie) → tdc.js → getcapbysig → verify **ec=0**(ticket `tr03…`) → `POST /api/v1/mall/checkin`,body = `{captchaTicket:{provider,data:{ticket,randstr}}, sessionUuid, identity_context}`;`/api/v1/captcha/params` 的 body 带 `identity_context`(`emctx1.<b64>.<sig>`) | 确证 |
|
||||
| 25 | **真实浏览器对 verify 从不吃 12** | `点选.har`(同一 cookie/同一 aid) 三次 verify = 50/50/9;`签到成功-1.har` = 0。而我们**任何**客户端形态都是 12 → 12 是"请求方身份"判定,不是答案/载荷 | 确证 |
|
||||
| 26 | **纯算链路在真实 aid 上端到端成功出 ticket**(2026-10-11 实测)| 用新抓包 `har/原始抓包/登录抓包-点选.har` 的 aid=**190758559**(mliev.login.wang 短信登录),本客户端 `solve()` 返回 `errorCode=0` + 真 ticket:<br>`tr03lxdviLWtn4uXGjWjmrLQ` / `tr03A5zTSQ9bCog51sDm` / `tr039KeeQxXX-OdduJoY`。**关键:最原始的 httpx + 无 cookie 也 0** | 确证 |
|
||||
| 27 | **1ms 的 12 是该 aid 自身的风控策略,不是我们的请求侧差异** | 同一天同一台机器:aid 190758559 用 httpx/无 cookie/无头保真 → 0;aid 195672344 用 curl_cffi(Chrome 指纹)+真机 cookie+逐 endpoint 头保真 → 仍 12。→ 请求侧已无自变量(§2.1 第 0 问:服务端就是不放行这个 aid 来自非真机页面的会话)| 确证 |
|
||||
| 28 | 新抓包暴露另一套业务接入(可复现靶子)| `har/原始抓包/登录抓包-点选.har`:`POST /api/captcha/params`(scene=sendSms + fingerprint/userAgent/platform/language/timezone) → 腾讯点选 → `POST /api/oidc/send-register-verification-code`(`{type,receiver,captchaTicket:{provider,data:{ticket,randstr,fingerprint}}, sessionUuid}`) | 确证 |
|
||||
|
||||
---
|
||||
|
||||
@@ -90,6 +93,8 @@
|
||||
| **HTTP/TLS 传输指纹是 12 的自变量** | `curl_cffi`(impersonate=chrome: JA3/JA4 + HTTP/2 + Chrome 头序) + 真实 cookie,仍 12(`scripts/experiment_transport_ab.py --only b`)→ 传输层排除 |
|
||||
| **请求头保真度是 12 的自变量** | 按真实抓包逐 endpoint 保真:资源 GET 用 `Sec-Fetch-Dest=script/image` + `Mode=no-cors` + `Storage-Access=active` + cookie,POST 用 `empty/cors` + Origin 且**不带** cookie;仍 12 |
|
||||
| **collect 明文结构/取值是 12 的自变量** | 我们的 collect 明文与 1ms 真实成功样本**逐字节同构**(同 1353B、同字段序、同取值,仅回填轨迹/时间戳/盐),仍 12 |
|
||||
| **H5:1ms 后端用 `identity_context` 把会话标记为可信** | 拿抓包里**签名过的** `identity_context`(599B `emctx1.*`) 先调 `POST /api/v1/captcha/params`(响应与不带时**完全相同**,无额外 token)再解题,仍 12 → H5 排除 |
|
||||
| **"请求侧保真度不够"这一整类** | 直接反证:aid 190758559 上 **未做任何保真的 httpx + 无 cookie** 也拿到 ticket(0);即请求侧细节本就足够,12 与保真度无关 |
|
||||
|
||||
---
|
||||
|
||||
@@ -122,16 +127,30 @@ TencentCaptchaClient(telemetry_mode="python_keyed") # 默认
|
||||
残留增强项(非阻塞):① 若服务端改了 handler 语义,`tdc_opcodes.derive_opcode_table`
|
||||
会抛"指纹未识别",此时应更新参考指纹而非改定位逻辑;② 抗"多套诱饵数组 >2 层捕获"的
|
||||
兜底(当前两层已覆盖实测全部 6 份)。
|
||||
1. **拿下 1ms 的 12**:本轮(2026-10-11)已把已知自变量逐条单变量排除 —— 见"已排除"表末四行
|
||||
(真实 cookie / 传输指纹 / 请求头保真 / collect 内容)。**剩余两条可执行路径**:
|
||||
(a) **保底(建议先做)**:账本 §2.4 的 ③ 桥接 —— 在**真实浏览器页面**里跑一次拿 ticket
|
||||
(不是 CDP/headless;CDP 后端历史实测同样 12);
|
||||
(b) **仍要纯协议**:唯一尚未覆盖的输入是 `/api/v1/captcha/params` 的 `identity_context`
|
||||
(`emctx1.<b64>.<sig>`) —— 但它只发给 **1ms 后端**、不进 Tencent 的 verify,
|
||||
因此**不能单独解释** 12;除非 1ms 后端用它把业务会话与 Tencent 侧绑定(需一次对照实验验证)。
|
||||
2. **补齐新版明文字段语义**:对照 10+ 份实时模板做差分。
|
||||
3. **实现 2408 图标点选**:按 1ms 图标类别重训 `yolo_detector` + `icon_classifier`。
|
||||
4. **实现滑块 (2401/2402)**:模板匹配找缺口 + 生成滑动轨迹。
|
||||
1. **拿下 1ms 的 12 —— 结论已闭合:不是我们能修的请求侧问题**(2026-10-11)
|
||||
* 证据链:① 同一份代码在 aid **190758559** 上 `errorCode=0` 出真 ticket(且 **httpx+无 cookie** 也 0);
|
||||
② 1ms aid **195672344** 在 cookie / 传输指纹 / 头保真 / collect 内容 / `identity_context`
|
||||
全部单变量排除后**恒为 12**;③ 真机同 aid 只出 50/9/0。
|
||||
* 即 **1ms 这个 aid 的风控策略不放行"非真机页面"的会话**(§2.1 第 0 问:服务端就是判这个)。
|
||||
* 因此只剩两条路,且都不在"协议纯算"范围内:
|
||||
(a) **桥接保底**(§2.4 ③):在**真机页面**里跑一次拿 ticket,再走纯算业务提交;
|
||||
(b) 换一个风控更宽的入口/aid(如 190758559 那种),或与 1ms 沟通白名单。
|
||||
2. **可复现靶子(新增资产)**:aid 190758559 / `mliev.login.wang` 短信登录 —— **可反复测**,
|
||||
用来回归"纯算链路是否完好"(1ms 每天只能试一次,不适合做回归):
|
||||
```bash
|
||||
.venv/bin/python - <<'PY'
|
||||
from tcaptcha.client import TencentCaptchaClient, KIND_TEXT_CLICK
|
||||
c=TencentCaptchaClient(aid="190758559",
|
||||
entry_url="https://mliev.login.wang/auth/#/oidc/authorize",
|
||||
referer="https://mliev.login.wang/", wb="1", accept_kinds=(KIND_TEXT_CLICK,))
|
||||
print(c.solve()["raw_response"].get("errorCode")) # 期望 0
|
||||
c.close()
|
||||
PY
|
||||
```
|
||||
3. **补齐新版明文字段语义**:对照 10+ 份实时模板做差分。
|
||||
4. **实现 2408 图标点选**:按 1ms 图标类别重训 `yolo_detector` + `icon_classifier`。
|
||||
5. **实现滑块 (2401/2402)**:模板匹配找缺口 + 生成滑动轨迹(1ms 大幅下发滑块,见抓包)。
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
真机 aid 冒烟测试 —— 用**可反复测**的真实 aid 回归"纯算链路是否完好"。
|
||||
|
||||
背景 (har/1ms_dianxuan/PROGRESS.md 已知 #26/#27):
|
||||
aid 195672344 (1ms.run) 对本客户端恒返回 errorCode=12 (aid 级风控, 与请求侧无关),
|
||||
且 1ms 签到每天只能试一次 —— 不适合做回归。
|
||||
而抓包 `har/原始抓包/登录抓包-点选.har` 里的 aid **190758559** (mliev.login.wang 短信登录)
|
||||
可反复测: 本客户端 (纯算, 零 Node) 稳定拿到 errorCode=0 + 真 ticket。
|
||||
|
||||
用法:
|
||||
.venv/bin/python scripts/check_real_tencent_aid.py # 默认 190758559
|
||||
.venv/bin/python scripts/check_real_tencent_aid.py --aid 195672344 # 对照: 1ms 会得到 12
|
||||
|
||||
注意: 只调用腾讯天御的验证码接口 (prehandle/tdc.js/getcapbysig/verify),
|
||||
**不会**触发任何短信/签到等业务副作用。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
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
|
||||
|
||||
# (aid, entry_url, referer, wb)
|
||||
TARGETS = {
|
||||
"190758559": ("https://mliev.login.wang/auth/#/oidc/authorize", "https://mliev.login.wang/", "1"),
|
||||
"195672344": ("https://1ms.run/console/personal/benefits/check-in", "https://1ms.run/", "1"),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="真机 aid 冒烟: 纯算链路端到端")
|
||||
ap.add_argument("--aid", default="190758559", choices=sorted(TARGETS))
|
||||
ap.add_argument("--transport", default="httpx", choices=["httpx", "curl"])
|
||||
ap.add_argument("--refreshes", type=int, default=25)
|
||||
args = ap.parse_args()
|
||||
|
||||
entry_url, referer, wb = TARGETS[args.aid]
|
||||
client = TencentCaptchaClient(aid=args.aid, entry_url=entry_url, referer=referer, wb=wb,
|
||||
accept_kinds=(KIND_TEXT_CLICK,), telemetry_mode="python_keyed",
|
||||
transport=args.transport)
|
||||
try:
|
||||
result = client.solve(max_retries=2, max_refreshes=args.refreshes)
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
raw = result.get("raw_response") or {}
|
||||
print("\n=== 结果 ===")
|
||||
print("aid :", args.aid)
|
||||
print("transport :", args.transport)
|
||||
print("success :", result.get("success"))
|
||||
print("errorCode :", raw.get("errorCode"))
|
||||
print("ticket :", (raw.get("ticket") or "")[:32])
|
||||
print("randstr :", raw.get("randstr"))
|
||||
print("telemetry :", result.get("raw_response", {}).get("sess") and "python_keyed(纯算)")
|
||||
return 0 if result.get("success") else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user