#!/usr/bin/env python3 """模拟器 attach+三件套 对照实验: 1) 干净启动 App → 自动走到账号密码登录页 → 填表 → 登录 → 观察验证码是否加载(基线) 2) attach frida 三件套(bypass_msaoaid_maps_skip_cleanup + mask_frida_maps_only + patch_guard) → 观察存活 → 重新走登录 → 观察验证码是否加载(对照) 用 RE 仓库 .venv 环境与 RE 仓库 evidence/scripts 三件套。""" from pathlib import Path import frida, time, subprocess, json, sys REMOTE = "127.0.0.1:31878" ADB = ["adb", "-s", "127.0.0.1:5555"] PACKAGE = "com.duowan.kiwi" RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0") OUT = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("/tmp/emu_attach_captcha.json") TRIO = [ (RE / "evidence/scripts/bypass_msaoaid_maps_skip_cleanup.js", "bypass_main"), (RE / "evidence/scripts/mask_frida_maps_only.js", "mask_frida"), (RE / "evidence/scripts/patch_guard_block_termination.js", "patch_guard"), ] def sh(*args, **kw): return subprocess.run(args, capture_output=True, text=True, **kw) def adb(*args): return sh(*ADB, *args) def force_stop(): adb("shell", "am", "force-stop", PACKAGE) time.sleep(1.5) def launch(): adb("shell", "monkey", "-p", PACKAGE, "-c", "android.intent.category.LAUNCHER", "1") time.sleep(6) def wait_pid(timeout=20): for _ in range(timeout): r = adb("shell", "pidof", PACKAGE) if r.stdout.strip(): return int(r.stdout.strip().split()[0]) time.sleep(1) return None def ui_dump(): adb("shell", "uiautomator", "dump", "/data/local/tmp/ui.xml") r = adb("shell", "cat", "/data/local/tmp/ui.xml") return r.stdout def find_bounds(xml, text): import re m = re.search(r']*text="' + re.escape(text) + r'"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"', xml) if m: x1, y1, x2, y2 = map(int, m.groups()) return (x1 + x2) // 2, (y1 + y2) // 2 return None def tap(x, y): adb("shell", "input", "tap", str(x), str(y)) def input_text(s): adb("shell", "input", "text", s) def go_login_page(): """从首页走到账号密码登录页. 返回 True 若成功.""" xml = ui_dump() # 我的 b = find_bounds(xml, "我的") if not b: return False tap(*b); time.sleep(2.5) xml = ui_dump() b = find_bounds(xml, "立即登录") if not b: return False tap(*b); time.sleep(3) xml = ui_dump() b = find_bounds(xml, "账号密码登录") if not b: # 已经是验证码登录页, 尝试切换 print(" [!] 未找到账号密码登录tab", flush=True) return False tap(*b); time.sleep(2) return True def fill_and_submit(): xml = ui_dump() b = find_bounds(xml, "手机号/虎牙号") if not b: b = find_bounds(xml, "请填写手机号码") if not b: return False tap(*b); time.sleep(0.4); input_text("13800138000") xml = ui_dump() b = find_bounds(xml, "密码") if b: tap(*b); time.sleep(0.4); input_text("test12345678") time.sleep(0.6) xml = ui_dump() b = find_bounds(xml, "立即登录") if not b: return False tap(*b); time.sleep(2) # 协议弹窗 xml = ui_dump() b = find_bounds(xml, "同意并继续") if b: tap(*b); time.sleep(4) return True def captcha_status(): xml = ui_dump() nodes = xml.replace(">\n