70 lines
3.1 KiB
Python
70 lines
3.1 KiB
Python
"""监视人工手指滑动: 你滑, 我抓结果。
|
|
检测: 活动离开 KwaiWebViewActivity => PASS(result=1); 图片 md5 变化 => 刷新(350014/350002); 不变 => 未滑或静默回弹。
|
|
"""
|
|
from __future__ import annotations
|
|
import subprocess
|
|
import time
|
|
import hashlib
|
|
from io import BytesIO
|
|
import numpy as np
|
|
from PIL import Image
|
|
|
|
PHOTO_LEFT, PHOTO_RIGHT = 66, 1013
|
|
PHOTO_TOP, PHOTO_BOT = 267, 819
|
|
|
|
|
|
def adb_text(*a):
|
|
r = subprocess.run(["adb", *a], capture_output=True, text=True,
|
|
encoding="utf-8", errors="replace", timeout=30)
|
|
return r.stdout
|
|
|
|
|
|
def state():
|
|
d = subprocess.run(["adb", "exec-out", "screencap", "-p"],
|
|
capture_output=True, timeout=30).stdout
|
|
img = np.asarray(Image.open(BytesIO(d)).convert("L"))
|
|
photo = img[PHOTO_TOP:PHOTO_BOT, PHOTO_LEFT:PHOTO_RIGHT]
|
|
md5 = hashlib.md5(photo.tobytes()).hexdigest()[:10]
|
|
top = adb_text("shell", "dumpsys", "activity", "activities")
|
|
act = next((l.strip() for l in top.splitlines() if "topResumedActivity" in l), "(none)")
|
|
on_captcha = "KwaiWebViewActivity" in act
|
|
return md5, on_captcha, act, img
|
|
|
|
|
|
def moved(a, b):
|
|
d = np.abs(b[PHOTO_TOP:PHOTO_BOT, PHOTO_LEFT:PHOTO_RIGHT].astype(np.int16)
|
|
- a[PHOTO_TOP:PHOTO_BOT, PHOTO_LEFT:PHOTO_RIGHT].astype(np.int16))
|
|
return int((d > 45).sum())
|
|
|
|
|
|
def main():
|
|
print("[watch] 开始监视 (~45s)。请现在用手指把拼图块滑进缺口。", flush=True)
|
|
base_md5, base_captcha, base_act, base_img = state()
|
|
print(f"[watch] baseline md5={base_md5} on_captcha={base_captcha}", flush=True)
|
|
t0 = time.monotonic(); last_print = 0; motion_seen = False
|
|
while time.monotonic() - t0 < 45:
|
|
time.sleep(0.6)
|
|
md5, on_captcha, act, img = state()
|
|
mv = moved(base_img, img)
|
|
if mv > 2000 and not motion_seen:
|
|
motion_seen = True
|
|
print(f"[watch] t={time.monotonic()-t0:.0f}s 检测到手指拖动(diff={mv})。等释放结果...", flush=True)
|
|
if not on_captcha:
|
|
print(f"\n>>> [PASS] t={time.monotonic()-t0:.0f}s 活动离开验证码页(人工滑动通过)。", flush=True)
|
|
print(f">>> top={act[:75]}", flush=True)
|
|
print(">>> 结论: 是自动化被检测(非号码标记)。路径=人工/root+Frida。", flush=True)
|
|
return 0
|
|
if md5 != base_md5 and motion_seen:
|
|
print(f"\n>>> [REFRESH] t={time.monotonic()-t0:.0f}s 图片变化(md5 {base_md5}->{md5})但仍在验证码页。", flush=True)
|
|
print(">>> 即人工正确滑动仍被拒 => 350014(号码被永久标记)。只能换号(新号不触发验证码)。", flush=True)
|
|
return 0
|
|
if time.monotonic() - last_print > 4:
|
|
print(f"[watch] t={time.monotonic()-t0:.0f}s 等待中... md5={'同' if md5==base_md5 else '变'} motion={'是' if motion_seen else '否'}", flush=True)
|
|
last_print = time.monotonic()
|
|
print("\n[watch] 超时未检测到明确结果。可能没滑/滑错回弹。再跑一次或告诉我结果。", flush=True)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|