107 lines
4.8 KiB
Python
107 lines
4.8 KiB
Python
"""全生命周期人工滑动监控 (GBK 安全, 无 emoji)。
|
|
|
|
Phase 1: 等 KwaiWebViewActivity 出现 (= 验证码弹起). 最多等 APPEAR_TIMEOUT 秒.
|
|
若一直不出现 => 该号在新设备上没触发验证码 (可能直接发短信 / 号码被放行).
|
|
Phase 2: 验证码已弹起 => 记基线 photo md5, 提示人工滑动. 监听:
|
|
- 活动离开 KwaiWebViewActivity => PASS (result=1, 人工手指过 => 自动化被检测, 非号码标记)
|
|
- 检测到拖动后图片 md5 变化 (新缺口) 但仍在验证码页 => REFRESH (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
|
|
APPEAR_TIMEOUT = 180 # 等验证码弹起
|
|
WATCH_TIMEOUT = 120 # 验证码弹起后等滑动结果
|
|
|
|
|
|
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 top():
|
|
out = adb_text("shell", "dumpsys", "activity", "activities")
|
|
return next((l.strip() for l in out.splitlines() if "topResumedActivity" in l), "(none)")
|
|
|
|
|
|
def on_captcha(act):
|
|
# 验证面出现过3种 host, 统一认 WebViewActivity (含 KwaiWebViewActivity / KwaiYodaWebViewActivity)
|
|
# + FaceRecognitionActivity + Verify*. 别死写死一个 Activity 名。
|
|
return (("WebViewActivity" in act)
|
|
or ("FaceRecognitionActivity" in act)
|
|
or ("Verify" in act or "verify" in act))
|
|
|
|
|
|
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 moved(a, b):
|
|
d = np.abs(photo(b).astype(np.int16) - photo(a).astype(np.int16))
|
|
return int((d > 45).sum())
|
|
|
|
|
|
def main():
|
|
print("[watch] Phase1: 等待验证页弹起 (KwaiWebViewActivity / FaceRecognitionActivity / Verify...)...", flush=True)
|
|
t0 = time.monotonic(); seen = False
|
|
while time.monotonic() - t0 < APPEAR_TIMEOUT:
|
|
act = top()
|
|
if on_captcha(act):
|
|
seen = True
|
|
print(f"\n========== 验证码已弹起 (t={time.monotonic()-t0:.0f}s) ==========", flush=True)
|
|
print("========== 请现在用手指把拼图块滑进缺口! ==========\n", flush=True)
|
|
break
|
|
if int(time.monotonic() - t0) % 10 == 0:
|
|
print(f"[wait] t={time.monotonic()-t0:.0f}s 还没弹验证码. top={act[:60]}", flush=True)
|
|
time.sleep(1.0)
|
|
if not seen:
|
|
print("\n>>> [NO-CAPTCHA] 等了 %ds 没弹验证码." % APPEAR_TIMEOUT, flush=True)
|
|
print(">>> 该号在清数据后的新设备上可能未触发验证码 (直接发短信/放行)。", flush=True)
|
|
print(">>> top=%s" % top()[:70], flush=True)
|
|
return 0
|
|
|
|
base = cap(); base_md5 = hashlib.md5(photo(base).tobytes()).hexdigest()[:10]
|
|
print(f"[watch] baseline photo md5={base_md5}", flush=True)
|
|
w0 = time.monotonic(); motion_seen = False; last_print = 0
|
|
while time.monotonic() - w0 < WATCH_TIMEOUT:
|
|
time.sleep(0.6)
|
|
act = top(); img = cap()
|
|
if not on_captcha(act):
|
|
print(f"\n>>> [PASS] t={time.monotonic()-w0:.0f}s 活动离开验证码页 (人工手指通过)!", flush=True)
|
|
print(f">>> top={act[:70]}", flush=True)
|
|
print(">>> 结论: 自动化被检测 (非号码标记). 可行路径 = 人工 in-loop / 根设备+Frida 注入真事件.", flush=True)
|
|
return 0
|
|
mv = moved(base, img)
|
|
if mv > 2000 and not motion_seen:
|
|
motion_seen = True
|
|
print(f"[watch] t={time.monotonic()-w0:.0f}s 检测到手指拖动 (diff={mv}). 等释放结果...", flush=True)
|
|
md5 = hashlib.md5(photo(img).tobytes()).hexdigest()[:10]
|
|
if motion_seen and md5 != base_md5:
|
|
print(f"\n>>> [REFRESH] t={time.monotonic()-w0:.0f}s 图片刷新 (md5 {base_md5}->{md5}) 但仍在验证码页.", flush=True)
|
|
print(">>> 即人工正确滑动仍被拒 => 350014 (号码被永久标记). 只能换号 (新号不触发验证码).", flush=True)
|
|
return 0
|
|
if time.monotonic() - last_print > 8:
|
|
print(f"[watch] t={time.monotonic()-w0:.0f}s 等待... motion={'是' if motion_seen else '否'} md5={'同' if md5==base_md5 else '变'}", flush=True)
|
|
last_print = time.monotonic()
|
|
print("\n>>> [TIMEOUT] 验证码弹起后 %ds 无明确结果." % WATCH_TIMEOUT, flush=True)
|
|
print(">>> 可能远错位静默回弹 (没发 verify) 或没滑. 可重跑.", flush=True)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|