153 lines
5.6 KiB
Python
153 lines
5.6 KiB
Python
"""v5: 几何瞄准 + 慢速滑动(避免 flick 被拒) + logcat 读 verify 响应。
|
|
|
|
关键修正(v4 的 photo_changed=False 根因):
|
|
1. 瞄准用几何 home_true=183(disX=24, scale=947/686), 不用拟合的有偏截距。
|
|
target = handle_home + (notch - 183) / R, R=1.27(标定斜率, 无偏)
|
|
2. 慢速 swipe(2500ms)。v4 用 650ms(~946px/s) => flick 被滑块忽略 => 不触发 verify。
|
|
标定的慢扫(3500ms)能触发 verify(错位刷新), 证明慢速 OK。
|
|
|
|
R=1.27 几何自洽: 轨道[137,~740]=603 宽, 图片 reach[183,~945]=762, 762/603=1.26。
|
|
真机 adb touch = 内核 MotionEvent(与人手不可区分); PC 人性化拖拽仍 350014 =>
|
|
350014 是指纹(canvas/WebGL), 非拖拽模式 => 真机慢速 swipe 能过(若号码未被标记)。
|
|
|
|
判定:
|
|
activity 关闭(KwaiWebViewActivity 消失) => result=1, 真机指纹过! 路径=真机/模拟器。
|
|
刷新 + logcat 350014 => 号码被永久标记(真机也过不去)。
|
|
刷新 + logcat 350002 => 缺口错(微调再试)。
|
|
无变化 => 滑动未触发 verify(速度/落点问题)。
|
|
"""
|
|
from __future__ import annotations
|
|
import subprocess
|
|
import time
|
|
from io import BytesIO
|
|
import numpy as np
|
|
from PIL import Image
|
|
|
|
PHOTO_LEFT, PHOTO_RIGHT = 66, 1013
|
|
PHOTO_TOP, PHOTO_BOT = 267, 819
|
|
SCALE = (PHOTO_RIGHT - PHOTO_LEFT) / 686.0 # 947/686 = 1.379
|
|
HOME_TRUE = PHOTO_LEFT + (24 + 122 / 2.0) * SCALE # = 183.2 (disX=24, cutPic/2=61)
|
|
R = 1.27 # piece位移/handle位移 (标定斜率, 无偏)
|
|
TRACK_Y = 950
|
|
|
|
|
|
def adb_text(*a):
|
|
r = subprocess.run(["adb", *a], capture_output=True, text=True,
|
|
encoding="utf-8", errors="replace", timeout=60)
|
|
return r.stdout
|
|
|
|
|
|
def cap():
|
|
d = subprocess.run(["adb", "exec-out", "screencap", "-p"],
|
|
capture_output=True, timeout=30).stdout
|
|
return np.asarray(Image.open(BytesIO(d)).convert("L"))
|
|
|
|
|
|
def photo(img):
|
|
return img[PHOTO_TOP:PHOTO_BOT, PHOTO_LEFT:PHOTO_RIGHT]
|
|
|
|
|
|
def detect_notch(img, x_min_local=230, col_thr=25):
|
|
reg = photo(img)
|
|
col = (reg < 90).sum(axis=0)
|
|
col_r = col[x_min_local:]
|
|
hot = np.where(col_r > col_thr)[0]
|
|
if len(hot) == 0:
|
|
return None
|
|
br = np.where(np.diff(hot) > 12)[0]
|
|
segs = [s for s in np.split(hot, br + 1) if len(s) > 10]
|
|
seg = max(segs, key=lambda s: col_r[s].sum())
|
|
segL = x_min_local + int(seg[0]); segR = x_min_local + int(seg[-1])
|
|
ys, xs = np.where((reg[:, segL:segR + 1] < 95))
|
|
if len(xs) == 0:
|
|
return None
|
|
return PHOTO_LEFT + int(round(np.average(xs + segL)))
|
|
|
|
|
|
def detect_handle(img, lo=80, hi=340):
|
|
band = img[932:968, lo:hi]
|
|
col = (band < 200).sum(axis=0).astype(float)
|
|
hot = np.where(col > 12)[0]
|
|
if len(hot) == 0:
|
|
return None
|
|
br = np.where(np.diff(hot) > 6)[0]
|
|
cands = [s for s in np.split(hot, br + 1) if 10 <= len(s) <= 80]
|
|
if not cands:
|
|
return None
|
|
seg = max(cands, key=lambda s: col[s].max()) # 峰值密度最高=实心按钮
|
|
w = col[seg[0]:seg[-1] + 1]
|
|
return lo + int(round(np.average(seg, weights=w)))
|
|
|
|
|
|
def top_activity():
|
|
out = adb_text("shell", "dumpsys", "activity", "activities")
|
|
return next((l.strip() for l in out.splitlines() if "topResumedActivity" in l), "(none)")
|
|
|
|
|
|
def solve_once(home=None):
|
|
home = home if home is not None else cap()
|
|
notch = detect_notch(home)
|
|
hh = detect_handle(home)
|
|
print(f"[init] notch={notch} handle_home={hh} (HOME_TRUE={HOME_TRUE:.0f} R={R})", flush=True)
|
|
if notch is None or hh is None:
|
|
print("[!] 检测失败"); return "err"
|
|
|
|
target = hh + (notch - HOME_TRUE) / R
|
|
tgt = int(round(target))
|
|
print(f"[plan] target = {hh} + ({notch}-{HOME_TRUE:.0f})/{R} = {tgt}", flush=True)
|
|
|
|
adb_text("logcat", "-c")
|
|
# 慢速 swipe => 触发 verify(避免 flick 被忽略)
|
|
subprocess.run(["adb", "shell", "input", "swipe",
|
|
str(hh), str(TRACK_Y), str(tgt), str(TRACK_Y), "2500"], timeout=30)
|
|
time.sleep(4.0)
|
|
aft = cap()
|
|
after = top_activity()
|
|
closed = "KwaiWebViewActivity" not in after
|
|
changed = not np.array_equal(photo(home), photo(aft))
|
|
print(f"[post] closed={closed} photo_changed={changed} top={after[:60]}", flush=True)
|
|
|
|
# logcat 找 verify 响应码
|
|
code = None
|
|
hits = []
|
|
for ln in adb_text("logcat", "-d").splitlines():
|
|
low = ln.lower()
|
|
if any(k in low for k in ("350014", "350002", "350005", "\"result\"", "ksecretapi", "captcha/sliding")):
|
|
hits.append(ln[:170])
|
|
for tok in ("350014", "350002", "350005"):
|
|
if tok in ln:
|
|
code = tok
|
|
print("----- logcat hits -----", flush=True)
|
|
for h in hits[:25]:
|
|
print(h, flush=True)
|
|
|
|
if closed:
|
|
return "PASS"
|
|
if code == "350014":
|
|
return "350014"
|
|
if code == "350002":
|
|
return "350002"
|
|
if changed:
|
|
return "refresh(nocode)"
|
|
return "nochange"
|
|
|
|
|
|
def main():
|
|
r = solve_once()
|
|
print(f"\n>>> VERDICT: {r}", flush=True)
|
|
if r == "PASS":
|
|
print(">>> result=1: 真机 WebView 指纹过验证! 可行路径=真机/模拟器。", flush=True)
|
|
elif r == "350014":
|
|
print(">>> 号码被永久标记: 真机正确落点也 350014 => 纯算+真机都过不去, 只能换号。", flush=True)
|
|
elif r == "350002":
|
|
print(">>> 缺口错位(350002): 微调 target 再试。", flush=True)
|
|
elif r == "refresh(nocode)":
|
|
print(">>> 刷新但 logcat 无码: 落点可能正确(350014)或微偏(350002), 需 bracket。", flush=True)
|
|
else:
|
|
print(">>> 滑动未触发 verify: 检查速度/落点。", flush=True)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|