docs: 绕过配方状态更正 (bypass_all 未验证, 以原版三脚本为准) + 三层健康检查工具

This commit is contained in:
yml2213
2026-08-29 11:44:54 +08:00
parent 5fa1f6d082
commit 1a6711c180
11 changed files with 504 additions and 24 deletions
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""bypass_all vs 原版三脚本 — 三项标准 + Activity 轨迹对比."""
import subprocess, sys, time, re
from pathlib import Path
import frida
ADB="5dd8c93f"; PKG="com.duowan.kiwi"
RE=Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
sys.path.insert(0,str(Path(__file__).resolve().parent))
from bypass_loader import load_bypass
def sh(*a): return subprocess.run(list(a),capture_output=True,text=True).stdout
def pid_of():
for l in sh("adb","-s",ADB,"shell","ps","-A").splitlines():
if l.rstrip().endswith(PKG): return l.split()[1]
def top_act():
out=sh("adb","-s",ADB,"shell","dumpsys","activity","activities")
for line in out.splitlines():
if "Hist #0" in line and PKG in line:
m=re.search(r"u0 ([\w.$]+) t\d+", line)
return m.group(1) if m else "?"
return None
def run(label, loader, attempts=2, watch=40):
for a in range(1,attempts+1):
sh("adb","-s",ADB,"shell","am","force-stop",PKG); time.sleep(4)
sh("adb","-s",ADB,"logcat","-c")
d=frida.get_device_manager().add_remote_device("127.0.0.1:31878")
pid0=d.spawn([PKG]); s=d.attach(pid0)
loader(s)
d.resume(pid0)
trail=[]; die=None; t0=time.time()
while time.time()-t0<watch:
time.sleep(4)
p=pid_of()
if p is None: die=f"{time.time()-t0:.0f}s"; break
act=top_act()
if act and (not trail or trail[-1]!=act): trail.append(act)
log=sh("adb","-s",ADB,"logcat","-d")
anr = "ANR in com.duowan.kiwi" in log
fatal = "FATAL EXCEPTION" in log and PKG in log
entered = any("Splash" not in x for x in trail)
tag = f"{label}#{a}"
print(f"{tag}: die={die or 'no'} ANR={anr} FATAL={fatal} entered={entered}")
print(f" trail: {' -> '.join(trail) if trail else '(none)'}")
try: s.detach()
except: pass
def load_merged(s):
load_bypass(s, patch_guard_delay_ms=11000)
def load_original(s):
s.create_script((RE/"evidence/scripts/bypass_msaoaid_maps_art_callsite.js").read_text()).load()
s.create_script((RE/"evidence/scripts/mask_frida_maps_only.js").read_text()).load()
time.sleep(11)
s.create_script((RE/"evidence/scripts/patch_guard_block_termination.js").read_text()).load()
if __name__=="__main__":
which=sys.argv[1] if len(sys.argv)>1 else "both"
if which in ("merged","both"): run("merged", load_merged)
if which in ("original","both"): run("original", load_original)
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/env python3
"""通用 bypass 健康检查器 — 三层面标准 (唯一判定工具).
层面1 进程: pid 不被杀、不换新 (换新 = 被静默杀后重启)
层面2 界面: Activity 轨迹持续推进, 通过 Splash 且最终停在主界面 (轨迹停滞 = 卡死)
层面3 崩溃: logcat 无 FATAL / 无 ANR
用法:
bypass_healthcheck.py [attempts] [--recipe merged|original] [--watch 秒]
判定: 每次 attempt 三层全过 → 该次 PASS; 所有 attempt 都 PASS → 总 PASS.
"""
import subprocess, sys, time, re
from pathlib import Path
import frida
ADB="5dd8c93f"; PKG="com.duowan.kiwi"
RE=Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
sys.path.insert(0,str(Path(__file__).resolve().parent))
from bypass_loader import load_bypass
def sh(*a): return subprocess.run(list(a),capture_output=True,text=True).stdout
def pid_of():
for l in sh("adb","-s",ADB,"shell","ps","-A").splitlines():
if l.rstrip().endswith(PKG): return l.split()[1]
return None
def main_thread_frozen(pid):
"""6s 采样主线程 utime 增量: 0 = 冻结 (前台卡死铁证)"""
if not pid: return None
def utime():
out=sh("adb","-s",ADB,"shell","su","-c",f"cat /proc/{pid}/task/{pid}/stat")
f=out.split()
return int(f[13])+int(f[14]) if len(f)>14 else None
a=utime()
if a is None: return None
time.sleep(6)
b=utime()
return (b is None) or (b==a)
def input_anr_warning():
log=sh("adb","-s",ADB,"logcat","-d")
return ("Input dispatching timed out" in log) or ("ANR in com.duowan.kiwi" in log)
def top_act():
out=sh("adb","-s",ADB,"shell","dumpsys","activity","activities")
for line in out.splitlines():
if "Hist #0" in line and PKG in line:
m=re.search(r"u0 ([\w.$]+) t\d+", line)
return m.group(1) if m else "?"
return None
def load_merged(s, delay=11000):
load_bypass(s, patch_guard_delay_ms=delay)
def load_original(s):
s.create_script((RE/"evidence/scripts/bypass_msaoaid_maps_art_callsite.js").read_text()).load()
s.create_script((RE/"evidence/scripts/mask_frida_maps_only.js").read_text()).load()
time.sleep(11)
s.create_script((RE/"evidence/scripts/patch_guard_block_termination.js").read_text()).load()
def check_once(recipe="merged", watch=45, guard_delay=11000):
sh("adb","-s",ADB,"shell","am","force-stop",PKG); time.sleep(4)
sh("adb","-s",ADB,"logcat","-c")
d=frida.get_device_manager().add_remote_device("127.0.0.1:31878")
pid0=d.spawn([PKG]); s=d.attach(pid0)
(load_merged if recipe=="merged" else load_original)(s, guard_delay) if recipe=="merged" else load_original(s)
d.resume(pid0)
L1_ok=True; trail=[]; die=None
t0=time.time()
while time.time()-t0<watch:
time.sleep(4)
p=pid_of()
if p is None: L1_ok=False; die=f"{time.time()-t0:.0f}s"; break
if p!=pid0: L1_ok=False; die=f"replaced@{time.time()-t0:.0f}s"; break
act=top_act()
if act and (not trail or trail[-1]!=act): trail.append(act)
log=sh("adb","-s",ADB,"logcat","-d")
L3_ok = not (("ANR in com.duowan.kiwi" in log) or ("FATAL EXCEPTION" in log and PKG in log))
entered = any("Splash" not in x for x in trail)
final = trail[-1] if trail else "(none)"
frozen = main_thread_frozen(pid_of()) if not die else None
warn = input_anr_warning()
# 层面2: 通过 splash + 主线程 utime 有增量 (首页没冻死) + 无 input ANR 前兆
L2_ok = entered and (frozen is False) and not warn
r={"L1_pid":L1_ok, "L2_ui":L2_ok, "L3_crash":L3_ok, "die":die, "final_act":final, "trail":trail, "main_frozen":frozen, "input_anr":warn}
r["PASS"]=L1_ok and L2_ok and L3_ok
try: s.detach()
except Exception: pass
return r
def main():
args=sys.argv[1:]
attempts=1; recipe="merged"; watch=45; delay=11000
i=0
while i<len(args):
if args[i]=="--recipe": recipe=args[i+1]; i+=2
elif args[i]=="--watch": watch=int(args[i+1]); i+=2
elif args[i]=="--delay": delay=int(args[i+1]); i+=2
else: attempts=int(args[i]); i+=1
results=[check_once(recipe, watch, delay) for _ in range(attempts)]
for n,r in enumerate(results,1):
print(f"attempt{n}: L1_pid={r['L1_pid']} L2_ui={r['L2_ui']} L3_crash={r['L3_crash']} die={r['die']} final={r['final_act']} frozen={r['main_frozen']} inputANR={r['input_anr']}")
print(f" trail: {' -> '.join(r['trail']) or '(none)'}")
total=all(r["PASS"] for r in results)
print(f"TOTAL: {'PASS ✅' if total else 'FAIL ❌'} ({recipe} x{attempts})")
sys.exit(0 if total else 1)
if __name__=="__main__": main()
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env python3
"""统一 bypass 加载器 — 所有 runner 共用.
用法:
from bypass_loader import load_bypass
s = d.attach(pid) # spawn 后、resume 前
load_bypass(s) # 默认 patch_guard 3s
load_bypass(s, patch_guard_delay_ms=5000, persist=True)
d.resume(pid)
参数经 globalThis.BYPASS_OPTS 注入 hook (见 tools/frida/bypass_all.js 头注释).
"""
from __future__ import annotations
import json
from pathlib import Path
HERE = Path(__file__).resolve().parent.parent
BYPASS_SRC = (HERE / "tools/frida/bypass_all.js").read_text()
def load_bypass(script_session, patch_guard_delay_ms: int = 3000, persist: bool = False):
"""spawn 挂起态调用: 单脚本装载三层绕过 (maps 掩盖立即生效, 终止拦截延迟生效)."""
src = (
"globalThis.BYPASS_OPTS = "
+ json.dumps({"patchGuardDelayMs": patch_guard_delay_ms, "persist": persist})
+ ";\n" + BYPASS_SRC
)
sc = script_session.create_script(src)
sc.load()
return sc
+2 -3
View File
@@ -21,9 +21,8 @@ def main():
time.sleep(1.5)
pid=d.spawn([PACKAGE]); print(f"[*] spawn pid={pid}",flush=True)
s=d.attach(pid)
try:
b=s.create_script((RE/"evidence/scripts/bypass_msaoaid_maps_skip_cleanup.js").read_text()); b.load()
except Exception as e: print(f"[*] bypass err {e}",flush=True)
from bypass_loader import load_bypass
load_bypass(s)
d.resume(pid)
result={}; got={"post":False,"out":False}
def on(m,_):
+2 -3
View File
@@ -21,9 +21,8 @@ def main():
time.sleep(1.5)
pid=d.spawn([PACKAGE]); print(f"[*] spawn pid={pid}",flush=True)
s=d.attach(pid)
try:
b=s.create_script((RE/"evidence/scripts/bypass_msaoaid_maps_skip_cleanup.js").read_text()); b.load()
except Exception as e: print(f"[*] bypass err {e}",flush=True)
from bypass_loader import load_bypass
load_bypass(s)
d.resume(pid)
result={}; got={"post":False,"out":False}
seen_fns=set()
+2 -3
View File
@@ -21,9 +21,8 @@ def main():
time.sleep(1.5)
pid=d.spawn([PACKAGE]); print(f"[*] spawn pid={pid}",flush=True)
s=d.attach(pid)
try:
b=s.create_script((RE/"evidence/scripts/bypass_msaoaid_maps_skip_cleanup.js").read_text()); b.load()
except Exception as e: print(f"[*] bypass err {e}",flush=True)
from bypass_loader import load_bypass
load_bypass(s)
d.resume(pid)
result={}; got={"post":False,"out":False}
seen_fns=set()
+2 -3
View File
@@ -21,9 +21,8 @@ def main():
time.sleep(1.5)
pid=d.spawn([PACKAGE]); print(f"[*] spawn pid={pid}",flush=True)
s=d.attach(pid)
try:
b=s.create_script((RE/"evidence/scripts/bypass_msaoaid_maps_skip_cleanup.js").read_text()); b.load()
except Exception as e: print(f"[*] bypass err {e}",flush=True)
from bypass_loader import load_bypass
load_bypass(s)
d.resume(pid)
result={}; state={"post":False,"key":False}
def on(m,_):
+3 -7
View File
@@ -16,14 +16,10 @@ def main():
pid=d.spawn([PACKAGE])
print(f"[*] spawned {pid}",flush=True)
s=d.attach(pid)
# 双重防护: callsite bypass + maps 掩盖
s.create_script((RE/"evidence/scripts/bypass_msaoaid_maps_art_callsite.js").read_text()).load()
s.create_script((RE/"evidence/scripts/mask_frida_maps_only.js").read_text()).load()
from bypass_loader import load_bypass
load_bypass(s, patch_guard_delay_ms=3000)
d.resume(pid)
print("[*] resumed, 稳定 11s...",flush=True)
time.sleep(11)
s.create_script((RE/"evidence/scripts/patch_guard_block_termination.js").read_text()).load()
print("[*] patch_guard on",flush=True)
print("[*] resumed (bypass_all: A层立即 + B层3s patch_guard)",flush=True)
result={}; state={"udb":0}
def on(m,_):
+2 -5
View File
@@ -41,11 +41,8 @@ def main():
pid = d.spawn([PACKAGE])
print(f"[*] spawn pid={pid}", flush=True)
s = d.attach(pid)
try:
b = s.create_script((RE / "evidence/scripts/bypass_msaoaid_maps_skip_cleanup.js").read_text())
b.load()
except Exception as e:
print(f"[*] bypass err {e}", flush=True)
from bypass_loader import load_bypass
load_bypass(s)
d.resume(pid)
result: dict = {}