"""合成快手 captcha 的 captchaExtraParam (key1-key39) —— 含拖拽相关传感器。 逆向自 out/captcha_net/js_08_...deobf2.js 的 collectEnvInfo / sensorInstance / traceInstance: - sensorInstance.cdma(devicemotion): 每事件 push "count, t, ax, ay, az" 到 dma_acc_result, "count,t,gx,gy,gz" 到 dma_gravity_result, "count,t,ra,rb,rg" 到 dma_rate_result。 - sensorInstance.cdoa(deviceorientation): push "count,t,alpha,beta,gamma" 到 doa_result。 - traceInstance.cta(touchmove): push "teCount,1,t,clientX,clientY" 到 teResult,并 resetDoaAndDmaThrottle —— 所以传感器在拖拽期与触摸同步采样(这是服务端 350014 校验的相关性)。 - 值经 _(e)=parseFloat(e).toFixed(2);时间戳 t = Z()-start_time (ms)。 - 限流: dma_count_lmt=10, doa_count_lmt=10, throttle_lmt=2 (touchmove 重置)。 服务端 350014 = 校验 captchaExtraParam 里的传感器(key18/20/22/24)是否像真人拖拽。 本模块用真人手持滑动的传感器签名(见 memory: 重力 Z:2.96↔12.55/Y≤9.46, 线性加速度±0.5, 陀螺仪±0.5 rad/s)合成与 trajectory 时间相关的样本。 """ from __future__ import annotations import json import math import random from typing import Any def _f(v: float) -> str: """复刻 JS 的 _(e): parseFloat(e).toFixed(2),非法→-1。""" try: if v is None or (isinstance(v, float) and (math.isnan(v) or math.isinf(v))): return "-1" return f"{float(v):.2f}" except (TypeError, ValueError): return "-1" def _build_sensor_series( duration_ms: float, n_samples: int, *, rnd: random.Random, ) -> dict[str, list[str]]: """生成 doa/acc/gravity/rate 四组传感器样本(时间跨 duration_ms, 与拖拽同步)。 关键保真点(对齐 memory 真人签名 + 物理): - dma_gravity_result = accelerationIncludingGravity = 重力 + 线性加速度。 静态 |.|≈9.8;拖拽期 |.|>9.8(因含 linacc),memory 实测 Z:2.96↔12.55, Y≤9.46。 - dma_acc_result = 线性加速度(去重力):静态 ~0;拖拽中段阻尼振荡 ±0.5。 - dma_rate_result = 陀螺仪:静态 ~0;拖拽中段 ±0.5 rad/s。 - 噪声用高斯(非均匀),爆发与拖拽速度同相(余弦缓动导数)。 返回 {acc, gravity, rate, doa} 各为 "count,t,x,y,z" 字符串列表。 """ acc: list[str] = [] grav: list[str] = [] rate: list[str] = [] doa: list[str] = [] total = 0 # 拖拽中手倾斜: theta 在中段大幅摆动(重力向量在 Y/Z 间扫) tilt_amp = math.radians(55) for i in range(n_samples): frac = (i + 0.5) / n_samples t = round(frac * duration_ms) # 速度包络: 余弦缓动的导数 ~ sin(pi*frac),中段最大;静态两端 ~0 env = math.sin(math.pi * frac) # 倾斜角: 中段摆动 + 慢漂 theta = tilt_amp * math.sin(frac * math.pi * 2.0) * (0.5 + 0.5 * env) # 线性加速度(去重力): 中段阻尼振荡 ±0.5,与速度同相 ax = rnd.gauss(0, 0.12) + 0.45 * env * math.sin(frac * math.pi * 6 + rnd.random()) ay = rnd.gauss(0, 0.12) + 0.50 * env * math.cos(frac * math.pi * 5) az = rnd.gauss(0, 0.10) + 0.35 * env * math.sin(frac * math.pi * 4) # 重力向量(纯重力,|.|=9.8): 手机竖持基线 Y≈9.8,倾斜后向 Z 分流 gxg = rnd.gauss(0, 0.15) gyg = 9.8 * math.cos(theta) gzg = 9.8 * math.sin(theta) # accelerationIncludingGravity = 重力 + 线性加速度 (=> 拖拽期 |.|>9.8) gx = gxg + ax gy = gyg + ay gz = gzg + az # 陀螺仪(角速度):与倾斜变化率同相,±0.5 rad/s ra = rnd.gauss(0, 0.05) + 0.45 * env * math.sin(frac * math.pi * 5) rb = rnd.gauss(0, 0.05) + 0.40 * env * math.cos(frac * math.pi * 4) rg = rnd.gauss(0, 0.06) + 0.50 * env * math.sin(frac * math.pi * 6) # orientation:alpha 罗盘稳定;beta=倾斜角(度);gamma 随手持微动 alpha = 180 + rnd.gauss(0, 2) beta = math.degrees(theta) + rnd.gauss(0, 1.5) gamma = rnd.gauss(0, 2.0) * (0.3 + env) c = total acc.append(f"{c},{t},{_f(ax)},{_f(ay)},{_f(az)}") grav.append(f"{c},{t},{_f(gx)},{_f(gy)},{_f(gz)}") rate.append(f"{c},{t},{_f(ra)},{_f(rb)},{_f(rg)}") doa.append(f"{c},{t},{_f(alpha)},{_f(beta)},{_f(gamma)}") total += 1 return {"acc": acc, "gravity": grav, "rate": rate, "doa": doa} def _build_touch_series( start_x: float, drag: float, y: float, duration_ms: float, n_moves: int, *, rnd: random.Random, ) -> list[str]: """teResult: "teCount,1,t,clientX,clientY" 列表(最多 10 move), x 从 start_x 到 start_x+drag。""" out: list[str] = [] te_count = 0 for i in range(n_moves): frac = (i + 1) / n_moves ease = 0.5 * (1 - math.cos(math.pi * frac)) x = math.floor(start_x + drag * ease + rnd.uniform(-1.5, 1.5)) yy = math.floor(y + rnd.uniform(-2.0, 2.0)) t = round(frac * duration_ms) out.append(f"{te_count},1,{t},{x},{yy}") te_count += 1 return out # Android WebView 指纹常量(真机 OnePlus PJZ110 / Android 16) _UA = ( "Mozilla/5.0 (Linux; Android 16; PJZ110 Build/UKQ1.230917.001; wv) " "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.134 " "Mobile Safari/537.36" ) def build_captcha_extra_param( *, drag_physical: float, slider_x: float, slider_y: float, duration_ms: float = 820.0, seed: int | None = None, ) -> dict[str, Any]: """生成完整 key1-key39 captchaExtraParam, 传感器+触摸与拖拽时间相关。 drag_physical: 滑块按钮物理位移(像素, DPR=3 空间) —— 与 trajectory 终点一致。 slider_x/slider_y: 按钮起始物理坐标(clientX/Y 空间)。 duration_ms: 拖拽总时长(与 build_trajectory 对齐)。 """ rnd = random.Random(seed) n_sensor = 10 # dma/doa count_lmt n_touch = 10 # tmeCountLmt series = _build_sensor_series(duration_ms, n_sensor, rnd=rnd) touch = _build_touch_series(slider_x, drag_physical, slider_y, duration_ms, n_touch, rnd=rnd) # key1=G(version常量, 近似), key2=collect 时间戳, key35=canvas 指纹(近似), key36=U()async(近似) return { "key1": "0.2.0", "key2": 1785061273985, # Z() 收集时刻(近似, 服务端宽容) "key3": _UA, "key4": "20030107", # navigator.productSub (Chrome) "key5": "zh-cn", "key6": "Gecko", "key7": 1080, "key8": 2376, "key9": 1080, "key10": 2376, "key11": 915, "key12": 412, "key13": 2376, "key14": 1080, "key15": "", # handleXAttri (近似) "key16": 1, "key17": 1, # DeviceOrientation/DeviceMotion 支持 "key18": series["doa"], "key19": {}, "key20": series["acc"], "key21": {}, "key22": series["gravity"], "key23": {}, "key24": series["rate"], "key25": {}, "key26": { "key27": [], # mouseEventInfo "key28": touch, # touchEventInfo (拖拽轨迹) "key29": [], # pointerEventInfo "key30": [], # keyEventInfo "key31": {}, "key32": {}, "key33": {}, "key34": {}, }, "key35": "", # canvas 指纹 (近似) "key36": "", # U() async (近似) "key37": 3, # devicePixelRatio "key38": False, # webdriver "key39": 8, # hardwareConcurrency } if __name__ == "__main__": import sys ep = build_captcha_extra_param(drag_physical=706, slider_x=60, slider_y=840, seed=42) print(json.dumps(ep, ensure_ascii=False)[:800]) print("...") print("gravity samples:", ep["key22"][:3]) print("touch samples:", ep["key26"]["key28"][:3])