186 lines
7.8 KiB
Python
186 lines
7.8 KiB
Python
"""合成快手 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])
|