From 18ae42ec63ce5d3d4e8a0a1944a86b4661f7facc Mon Sep 17 00:00:00 2001 From: mars <3361409208@qq.com> Date: Mon, 3 Aug 2026 11:02:46 +0800 Subject: [PATCH] =?UTF-8?q?fix(tng):=20=E9=80=82=E9=85=8D=20Promon=201.9.1?= =?UTF-8?q?0=20vhvlnqgy=20=E5=8C=85=E5=90=8D=E5=B9=B6=E5=8A=A0=E5=9B=BA=20?= =?UTF-8?q?Zygisk=20=E4=BF=A1=E5=8F=B7=E8=B7=B3=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.9.10 将 xwwqazamx 重命名为 vhvlnqgy,Login 闪退为 vhvlnqgy.bd:16;Xposed 双包名解析并 short-circuit R/bl、拦截 bd 异常。Zygisk 扩展 non-promon SEGV pc+4 与 ABRT/TRAP 一律 pc+4,修复 Login 稳定;注册页 stack_chk abort 待续攻。 --- magisk-modules/tng_exit_guard/jni/main.cpp | 83 ++++----- .../tng_exit_guard/zygisk/arm64-v8a.so | Bin 186384 -> 186384 bytes reverse/scripts/_scan_tng_vhv.py | 14 ++ reverse/scripts/_scan_tng_vhv_bl.py | 24 +++ reverse/scripts/parse_ui_dump.py | 20 +++ reverse/scripts/parse_ui_dump_all.py | 13 ++ reverse/scripts/test_tng_register_flow.py | 110 ++++++++++++ .../xposed/hook/TngRootBypassHook.java | 160 ++++++++++++------ 8 files changed, 329 insertions(+), 95 deletions(-) create mode 100644 reverse/scripts/_scan_tng_vhv.py create mode 100644 reverse/scripts/_scan_tng_vhv_bl.py create mode 100644 reverse/scripts/parse_ui_dump.py create mode 100644 reverse/scripts/parse_ui_dump_all.py create mode 100644 reverse/scripts/test_tng_register_flow.py diff --git a/magisk-modules/tng_exit_guard/jni/main.cpp b/magisk-modules/tng_exit_guard/jni/main.cpp index 78f71c9..8ad34ec 100644 --- a/magisk-modules/tng_exit_guard/jni/main.cpp +++ b/magisk-modules/tng_exit_guard/jni/main.cpp @@ -42,8 +42,14 @@ static std::atomic g_stack_chk{0}; static std::atomic g_promon_segv{0}; static std::atomic g_promon_start{0}; static std::atomic g_promon_end{0}; -/* 隔离进程 :goacqowmmt 会循环 SEGV;cap=16 打满后 freeze 反拖累主进程 */ -static constexpr int kMaxPromonSegvSkip = 0; /* 0 = unlimited LR-return skip */ +static std::atomic g_main_tid{0}; +static std::atomic g_abrt_swallow{0}; +static std::atomic g_abrt_last_tid{0}; +static std::atomic g_abrt_last_pc{0}; +static std::atomic g_abrt_streak{0}; +/* 隔离进程 :goacqowmmt 会循环 SEGV;主进程 worker 也狂刷。cap 后 freeze 该线程 */ +static constexpr int kMaxPromonSegvSkip = 200; +static constexpr int kMaxAbrtStreak = 3; static std::atomic g_soft_sig_logged{0}; static void freeze_forever() { @@ -119,18 +125,30 @@ static void promon_segv_handler(int sig, siginfo_t *info, void *ctx) { LOGI("promon SIGSEGV tid=%d n=%d — cap hit, freeze", (int)gettid(), n); freeze_forever(); } - signal(SIGSEGV, SIG_DFL); - raise(SIGSEGV); + /* 1.9.10:Login 后出现 Promon so 外 SEGV → 旧逻辑 re-raise 直接闪退 */ + { + int n = ++g_promon_segv; + pid_t tid = gettid(); + if (n <= 5 || n % 50 == 0) { + LOGI("non-promon SIGSEGV tid=%d pc=%lx lr=%lx n=%d — pc+4", + (int)tid, (unsigned long)pc, (unsigned long)lr, n); + } + if (n > 200 && tid != g_main_tid.load()) { + LOGI("non-promon SEGV storm tid=%d — freeze", (int)tid); + freeze_forever(); + } + if (pc != 0) { + uc->uc_mcontext.pc = pc + 4; + return; + } + } + freeze_forever(); } -static std::atomic g_main_tid{0}; -static std::atomic g_abrt_swallow{0}; -static std::atomic g_abrt_last_tid{0}; -static std::atomic g_abrt_last_pc{0}; -static std::atomic g_abrt_streak{0}; -static constexpr int kMaxAbrtStreak = 3; - -/** ABRT/TRAP:pc+4 或远距 LR;同 tid+pc 连触发则 freeze,避免 LR 自旋死循环。 */ +/** + * ABRT/TRAP:一律 pc+4。跳远距 LR 会弄坏主线程 Looper(闪退观感)。 + * 工作线程同 PC 连 abort 超限 → freeze;主线程始终 pc+4。 + */ static void fatal_skip_handler(int sig, siginfo_t *info, void *ctx) { (void)info; ucontext_t *uc = reinterpret_cast(ctx); @@ -148,39 +166,26 @@ static void fatal_skip_handler(int sig, siginfo_t *info, void *ctx) { g_abrt_last_pc.store(pc); g_abrt_streak.store(1); } - if (streak > kMaxAbrtStreak) { - if (tid == g_main_tid.load()) { - LOGI("ABRT main-thread streak cap tid=%d pc=%lx — pc+4 bail", (int)tid, - (unsigned long)pc); - uc->uc_mcontext.pc = pc + 4; - g_abrt_streak.store(0); - return; - } - LOGI("ABRT streak cap tid=%d pc=%lx n=%d — freeze thread", (int)tid, + if (streak > kMaxAbrtStreak && tid != g_main_tid.load()) { + LOGI("ABRT streak cap tid=%d pc=%lx n=%d — freeze worker", (int)tid, (unsigned long)pc, streak); freeze_forever(); } - uintptr_t delta = (pc > lr) ? (pc - lr) : (lr - pc); - uintptr_t target = pc + 4; - /* lr 距 pc 很近时仍在 abort/epilogue 内,跳 LR 会 instant 再 ABRT */ - if (lr != 0 && delta > 64) { - target = lr; - } int n = ++g_abrt_swallow; - if (n <= 3 || n % 100 == 0) { - LOGI("ABRT skip tid=%d pc=%lx lr=%lx streak=%d -> %lx", (int)tid, - (unsigned long)pc, (unsigned long)lr, streak, - (unsigned long)target); + if (n <= 5 || n % 50 == 0) { + LOGI("ABRT pc+4 tid=%d pc=%lx lr=%lx streak=%d", (int)tid, + (unsigned long)pc, (unsigned long)lr, streak); } - uc->uc_mcontext.pc = target; + uc->uc_mcontext.pc = pc + 4; return; } if (sig == SIGTRAP) { - uintptr_t target = pc != 0 ? pc + 4 : lr; - LOGI("TRAP skip tid=%d pc=%lx -> %lx", (int)tid, (unsigned long)pc, - (unsigned long)target); - uc->uc_mcontext.pc = target; + int n = ++g_abrt_swallow; + if (n <= 5 || n % 50 == 0) { + LOGI("TRAP pc+4 tid=%d pc=%lx", (int)tid, (unsigned long)pc); + } + uc->uc_mcontext.pc = pc != 0 ? pc + 4 : lr; return; } #endif @@ -194,7 +199,7 @@ static void install_fatal_skip_handlers() { sigemptyset(&sa.sa_mask); sigaction(SIGABRT, &sa, nullptr); sigaction(SIGTRAP, &sa, nullptr); - LOGI("fatal skip handlers (ABRT pc+4/streak-freeze + TRAP pc+4)"); + LOGI("fatal skip handlers (ABRT/TRAP always pc+4)"); } static void install_promon_segv_handler() { @@ -220,7 +225,7 @@ static void install_soft_signals() { sigaction(SIGABRT, &sa, nullptr); sigaction(SIGTRAP, &sa, nullptr); if (g_soft_sig_logged.fetch_add(1) == 0) { - LOGI("soft signals (ABRT streak-freeze + TRAP pc+4)"); + LOGI("soft signals (ABRT/TRAP always pc+4)"); } } @@ -386,7 +391,7 @@ static void *phase_thread(void *) { static void install_all(zygisk::Api *api) { g_main_tid.store(gettid()); - LOGI("install pid=%d main_tid=%d (PLT+ABRT-streak-freeze+TRAP+pc==lr-SEGV+exit_group@400ms)", + LOGI("install pid=%d main_tid=%d (PLT+ABRT-pc+4+SEGV-all-skip+exit_group@400ms)", getpid(), (int)g_main_tid.load()); install_fatal_skip_handlers(); install_soft_signals(); diff --git a/magisk-modules/tng_exit_guard/zygisk/arm64-v8a.so b/magisk-modules/tng_exit_guard/zygisk/arm64-v8a.so index e58a6538dcfa92ea732e83cebd5c0f65de2a82c0..af139d929df8c4701dc2b4e0a8bf2b044b80267a 100644 GIT binary patch delta 66929 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 Q0gC13RKvRypJR>xAO5|+W&i*H diff --git a/reverse/scripts/_scan_tng_vhv.py b/reverse/scripts/_scan_tng_vhv.py new file mode 100644 index 0000000..fae195c --- /dev/null +++ b/reverse/scripts/_scan_tng_vhv.py @@ -0,0 +1,14 @@ +import zipfile +import re + +z = zipfile.ZipFile(r"reverse/dumps/tng_1.9.10_base.apk") +names = [n for n in z.namelist() if n.endswith(".dex")] +pat = re.compile(rb"Lvhvlnqgy/([^;\s]{1,80});") +found = set() +for n in names: + data = z.read(n) + for m in pat.findall(data): + found.add(m.decode("ascii", errors="ignore")) +print("vhvlnqgy classes", len(found)) +for c in sorted(found): + print(f"vhvlnqgy.{c.replace('/', '.')}") diff --git a/reverse/scripts/_scan_tng_vhv_bl.py b/reverse/scripts/_scan_tng_vhv_bl.py new file mode 100644 index 0000000..43ae0cb --- /dev/null +++ b/reverse/scripts/_scan_tng_vhv_bl.py @@ -0,0 +1,24 @@ +"""Dump vhvlnqgy bl/R/bd method refs from TNG 1.9.10 dex.""" +import re +import zipfile + +APK = r"reverse/dumps/tng_1.9.10_base.apk" +with zipfile.ZipFile(APK) as z: + data = b"".join(z.read(n) for n in z.namelist() if n.endswith(".dex")) + +for cls in ["Lvhvlnqgy/bl;", "Lvhvlnqgy/R;", "Lvhvlnqgy/bd;", "Lvhvlnqgy/w;", "Lvhvlnqgy/W;", "Lvhvlnqgy/a;"]: + print("===", cls, "===") + refs = sorted(set(re.findall(cls.encode() + rb"->[^\x00]{1,80}", data))) + for r in refs[:30]: + print(r.decode("ascii", "ignore")) + print() + +print("=== R callers (who invokes R.a/R.b) ===") +for m in sorted(set(re.findall(rb"Lvhvlnqgy/[^;]+;->[a-zA-Z]+[^\x00]{0,40}Lvhvlnqgy/R;", data))): + print(m.decode("ascii", "ignore")) + +print("\n=== bd throw sites ===") +for m in sorted(set(re.findall(rb"[^\x00]{0,40}Lvhvlnqgy/bd;", data))): + s = m.decode("ascii", "ignore") + if "vhvlnqgy" in s: + print(s) diff --git a/reverse/scripts/parse_ui_dump.py b/reverse/scripts/parse_ui_dump.py new file mode 100644 index 0000000..18ab60a --- /dev/null +++ b/reverse/scripts/parse_ui_dump.py @@ -0,0 +1,20 @@ +import sys +import xml.etree.ElementTree as ET + +path = sys.argv[1] if len(sys.argv) > 1 else "reverse/dumps/ui_login.xml" +root = ET.parse(path).getroot() +keywords = [ + "register", "sign", "login", "mobile", "phone", "continue", "next", + "create", "otp", "skip", "started", "email", "password", "pin", +] +for node in root.iter("node"): + text = node.get("text", "") + desc = node.get("content-desc", "") + clickable = node.get("clickable", "") + bounds = node.get("bounds", "") + label = (text or desc).strip() + if not label and clickable != "true": + continue + hay = (text + " " + desc).lower() + if clickable == "true" or any(k in hay for k in keywords): + print(f"{label!r} bounds={bounds} clickable={clickable}") diff --git a/reverse/scripts/parse_ui_dump_all.py b/reverse/scripts/parse_ui_dump_all.py new file mode 100644 index 0000000..2b73907 --- /dev/null +++ b/reverse/scripts/parse_ui_dump_all.py @@ -0,0 +1,13 @@ +import sys +import xml.etree.ElementTree as ET + +path = sys.argv[1] +root = ET.parse(path).getroot() +for node in root.iter("node"): + text = (node.get("text") or "").strip() + desc = (node.get("content-desc") or "").strip() + rid = node.get("resource-id") or "" + bounds = node.get("bounds") or "" + clickable = node.get("clickable") or "false" + if text or desc or "login" in rid.lower() or "register" in rid.lower(): + print(f"text={text!r} desc={desc!r} id={rid} bounds={bounds} click={clickable}") diff --git a/reverse/scripts/test_tng_register_flow.py b/reverse/scripts/test_tng_register_flow.py new file mode 100644 index 0000000..410b830 --- /dev/null +++ b/reverse/scripts/test_tng_register_flow.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +"""Automate TNG login/register tap test and report crashes.""" +import re +import subprocess +import sys +import time + +PKG = "my.com.tngdigital.ewallet" +TAGS = re.compile( + r"FATAL EXCEPTION|AndroidRuntime.*Process: " + PKG + + r"|has died|exited due to signal|vhvlnqgy\.bd|blocked killProcess|blocked System\.exit" + + r"|UserRegistration|UserOtp|UserLogin|Displayed.*tngdigital|ACT on(Create|Resume)" + + r"|TngExitGuard.*ABRT|TngRoot hooked 6 vhvlnqgy\.R", + re.I, +) + +TAPS = [ + ("register_continue", 540, 959, 8), + ("maybe_otp_continue", 540, 959, 6), + ("back_to_login", None, None, 2), + ("login_tab", 540, 1168, 5), +] + + +def adb(*args, timeout=30): + cmd = ["adb"] + list(args) + r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) + out = (r.stdout or "") + (r.stderr or "") + return r.returncode, out.strip() + + +def pid_alive(): + code, out = adb("shell", "pidof", PKG) + return code == 0 and out.strip().split() + + +def top_activity(): + code, out = adb("shell", "dumpsys", "activity", "activities") + if code != 0: + return "?" + for line in out.splitlines(): + if "topResumedActivity=" in line: + m = re.search(r"/([^/]+)$", line.strip()) + if m: + return m.group(1).rstrip("}") + return line.strip() + return "?" + + +def logcat_since(start): + code, out = adb("shell", "logcat", "-d", "-t", start) + hits = [] + if code == 0: + for line in out.splitlines(): + if TAGS.search(line): + hits.append(line) + return hits + + +def main(): + adb("logcat", "-c") + adb("shell", "am", "force-stop", PKG) + time.sleep(1) + adb("shell", "am", "start", "-n", f"{PKG}/.ui.SplashActivity") + time.sleep(12) + print("=== after cold start ===") + print("pids:", pid_alive()) + print("activity:", top_activity()) + + # dismiss country picker if open + adb("shell", "input", "keyevent", "KEYCODE_BACK") + time.sleep(1) + + results = [] + for name, x, y, wait_s in TAPS: + if x is not None: + adb("shell", "input", "tap", str(x), str(y)) + print(f"\n=== tap {name} ({x},{y}) ===") + else: + adb("shell", "input", "keyevent", "KEYCODE_BACK") + print(f"\n=== {name} ===") + time.sleep(wait_s) + pids = pid_alive() + act = top_activity() + crash_buf = adb("shell", "logcat", "-d", "-b", "crash", "-t", "50")[1] + fatal = [l for l in crash_buf.splitlines() if PKG in l or "vhvlnqgy" in l] + results.append((name, pids, act, fatal)) + print("pids:", pids or "DEAD") + print("activity:", act) + if fatal: + print("CRASH:", fatal[-3:]) + + print("\n=== summary ===") + ok = True + for name, pids, act, fatal in results: + status = "OK" if pids and not fatal else "FAIL" + if status == "FAIL": + ok = False + print(f"{status} {name}: pids={pids} activity={act} crash_lines={len(fatal)}") + + hits = logcat_since("2000") + print(f"\n=== key log lines ({len(hits)}) ===") + for line in hits[-40:]: + print(line) + + return 0 if ok else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/xposed-module/src/main/java/com/miraclegarden/smsmessage/xposed/hook/TngRootBypassHook.java b/xposed-module/src/main/java/com/miraclegarden/smsmessage/xposed/hook/TngRootBypassHook.java index 3efbf55..652d5bd 100644 --- a/xposed-module/src/main/java/com/miraclegarden/smsmessage/xposed/hook/TngRootBypassHook.java +++ b/xposed-module/src/main/java/com/miraclegarden/smsmessage/xposed/hook/TngRootBypassHook.java @@ -36,6 +36,9 @@ public final class TngRootBypassHook { public static final String PACKAGE = "my.com.tngdigital.ewallet"; private static final String TAG = "notiMessageHook/TngRoot"; + /** Promon 混淆包名:1.9.10 为 vhvlnqgy,旧版为 xwwqazamx。 */ + private static final String[] PROMON_PKG_PREFIXES = {"vhvlnqgy", "xwwqazamx"}; + private static final String SECURITY_ERROR_ACTIVITY = "my.com.tngdigital.common.security.ui.SecurityErrorActivity"; @@ -604,12 +607,17 @@ public final class TngRootBypassHook { /** Promon 隔离 Service;打点确认 :goacqowmmt 进程 hook 已注入。 */ private static void hookPromonApService(XC_LoadPackage.LoadPackageParam lpparam) { + Class svc = findPromonClass(lpparam.classLoader, "ap"); + if (svc == null) { + XposedBridge.log(TAG + " Promon ap Service not found"); + return; + } try { - Class svc = XposedHelpers.findClass("xwwqazamx.ap", lpparam.classLoader); + final String svcName = svc.getName(); XposedHelpers.findAndHookMethod(svc, "onCreate", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) { - XposedBridge.log(TAG + " xwwqazamx.ap onCreate pid=" + Process.myPid() + XposedBridge.log(TAG + " " + svcName + " onCreate pid=" + Process.myPid() + " proc=" + getProcessName()); } }); @@ -620,14 +628,14 @@ public final class TngRootBypassHook { XposedBridge.hookMethod(method, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { - XposedBridge.log(TAG + " xwwqazamx.ap onStartCommand pid=" + XposedBridge.log(TAG + " " + svcName + " onStartCommand pid=" + Process.myPid()); } }); } - XposedBridge.log(TAG + " hooked xwwqazamx.ap Service"); + XposedBridge.log(TAG + " hooked " + svcName + " Service"); } catch (Throwable t) { - XposedBridge.log(TAG + " xwwqazamx.ap hook failed: " + t.getMessage()); + XposedBridge.log(TAG + " Promon ap Service hook failed: " + t.getMessage()); } } @@ -746,10 +754,14 @@ public final class TngRootBypassHook { return false; } - /** Promon USB 广播 xwwqazamx.N 跑在主线程,复进时拖死 Looper。 */ + /** Promon USB 广播 N 跑在主线程,复进时拖死 Looper。 */ private static void hookPromonBroadcastReceiver(XC_LoadPackage.LoadPackageParam lpparam) { + Class clazz = findPromonClass(lpparam.classLoader, "N"); + if (clazz == null) { + XposedBridge.log(TAG + " Promon N receiver not found"); + return; + } try { - Class clazz = XposedHelpers.findClass("xwwqazamx.N", lpparam.classLoader); int hooked = 0; for (Method method : clazz.getDeclaredMethods()) { if (!"onReceive".equals(method.getName())) { @@ -764,10 +776,10 @@ public final class TngRootBypassHook { hooked++; } if (hooked > 0) { - XposedBridge.log(TAG + " hooked xwwqazamx.N onReceive x" + hooked); + XposedBridge.log(TAG + " hooked " + clazz.getName() + " onReceive x" + hooked); } } catch (Throwable t) { - XposedBridge.log(TAG + " xwwqazamx.N hook failed: " + t.getMessage()); + XposedBridge.log(TAG + " Promon N hook failed: " + t.getMessage()); } } @@ -1179,11 +1191,12 @@ public final class TngRootBypassHook { || lower.contains("emulator") || lower.contains("malware"); } - /** 阻止 Promon 混淆层抛出 W:16 并触发浏览器 fallback。 */ + /** 阻止 Promon 混淆层抛出 W/bd:16 并触发浏览器 fallback。 */ private static void hookPromonNativeGuard(XC_LoadPackage.LoadPackageParam lpparam) { hookPromonBlSwallowExceptions(lpparam); - hookPromonExceptionClass(lpparam, "xwwqazamx.W"); - hookPromonExceptionClass(lpparam, "xwwqazamx.A"); + for (String simple : new String[]{"W", "A", "bd"}) { + hookPromonExceptionClass(lpparam, simple); + } hookPromonRunnable(lpparam); } @@ -1191,14 +1204,18 @@ public final class TngRootBypassHook { * Promon lifecycle:只吞异常,不 short-circuit——全拦会拖死 Splash→Login。 */ private static void hookPromonLifecycle(XC_LoadPackage.LoadPackageParam lpparam) { + Class lifecycleClass = findPromonClass(lpparam.classLoader, "w"); + if (lifecycleClass == null) { + XposedBridge.log(TAG + " Promon w lifecycle not found"); + return; + } + Class promonExc = findPromonClass(lpparam.classLoader, "W"); + if (promonExc == null) { + promonExc = findPromonClass(lpparam.classLoader, "bd"); + } + final Class promonExcFinal = promonExc; + final String lifecycleName = lifecycleClass.getName(); try { - Class lifecycleClass = XposedHelpers.findClass("xwwqazamx.w", lpparam.classLoader); - Class promonExc = null; - try { - promonExc = XposedHelpers.findClass("xwwqazamx.W", lpparam.classLoader); - } catch (Throwable ignored) { - } - final Class promonExcFinal = promonExc; int hooked = 0; for (Method method : lifecycleClass.getDeclaredMethods()) { String name = method.getName(); @@ -1214,7 +1231,7 @@ public final class TngRootBypassHook { Throwable t = param.getThrowable(); if (isPromonThrowable(t, promonExcFinal)) { XposedBridge.log(TAG + " swallowed " + t.getClass().getSimpleName() - + " in xwwqazamx.w#" + method.getName()); + + " in " + lifecycleName + "#" + method.getName()); param.setThrowable(null); } } @@ -1223,10 +1240,10 @@ public final class TngRootBypassHook { } if (hooked > 0) { XposedBridge.log(TAG + " hooked " + hooked - + " xwwqazamx.w lifecycle method(s) (afterHook only)"); + + " " + lifecycleName + " lifecycle method(s) (afterHook only)"); } } catch (Throwable t) { - XposedBridge.log(TAG + " xwwqazamx.w lifecycle hook failed: " + t.getMessage()); + XposedBridge.log(TAG + " Promon w lifecycle hook failed: " + t.getMessage()); } } @@ -1256,15 +1273,24 @@ public final class TngRootBypassHook { } /** - * bl 全方法 short-circuit:a/b 之外的方法仍会跑 native,~40s 后 stack_chk/SEGV。 - * xwwqazamx.a.run 是 bl#b 后台 Runnable,必须 beforeHook 直接 return。 + * bl/R 全方法 short-circuit:a/b 之外的方法仍会跑 native,~40s 后 stack_chk/SEGV。 + * a.run 是 bl#b 后台 Runnable,必须 beforeHook 直接 return。 */ private static void hookPromonBlSwallowExceptions(XC_LoadPackage.LoadPackageParam lpparam) { + for (String simple : new String[]{"bl", "R"}) { + hookPromonClassShortCircuit(lpparam, simple); + } + } + + private static void hookPromonClassShortCircuit( + XC_LoadPackage.LoadPackageParam lpparam, String simpleName) { + Class clazz = findPromonClass(lpparam.classLoader, simpleName); + if (clazz == null) { + return; + } try { - Class blClass = XposedHelpers.findClass("xwwqazamx.bl", lpparam.classLoader); int hooked = 0; - for (Method method : blClass.getDeclaredMethods()) { - String name = method.getName(); + for (Method method : clazz.getDeclaredMethods()) { XposedBridge.hookMethod(method, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { @@ -1274,10 +1300,12 @@ public final class TngRootBypassHook { hooked++; } if (hooked > 0) { - XposedBridge.log(TAG + " hooked " + hooked + " bl method(s), all short-circuit"); + XposedBridge.log(TAG + " hooked " + hooked + " " + clazz.getName() + + " method(s), all short-circuit"); } } catch (Throwable t) { - XposedBridge.log(TAG + " xwwqazamx.bl hook failed: " + t.getMessage()); + XposedBridge.log(TAG + " " + clazz.getName() + " short-circuit failed: " + + t.getMessage()); } } @@ -1294,24 +1322,32 @@ public final class TngRootBypassHook { /** Promon 后台 Runnable(bl#b 检测线程),beforeHook 直接 noop,禁止跑 native。 */ private static void hookPromonRunnable(XC_LoadPackage.LoadPackageParam lpparam) { + Class runnableClass = findPromonClass(lpparam.classLoader, "a"); + if (runnableClass == null) { + XposedBridge.log(TAG + " Promon a runnable not found"); + return; + } try { - Class runnableClass = XposedHelpers.findClass("xwwqazamx.a", lpparam.classLoader); XposedHelpers.findAndHookMethod(runnableClass, "run", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { param.setResult(null); } }); - XposedBridge.log(TAG + " hooked xwwqazamx.a.run (short-circuit)"); + XposedBridge.log(TAG + " hooked " + runnableClass.getName() + ".run (short-circuit)"); } catch (Throwable t) { - XposedBridge.log(TAG + " xwwqazamx.a.run hook failed: " + t.getMessage()); + XposedBridge.log(TAG + " Promon a.run hook failed: " + t.getMessage()); } } private static void hookPromonExceptionClass( - XC_LoadPackage.LoadPackageParam lpparam, String className) { + XC_LoadPackage.LoadPackageParam lpparam, String simpleName) { + Class promonExc = findPromonClass(lpparam.classLoader, simpleName); + if (promonExc == null) { + return; + } + final String className = promonExc.getName(); try { - Class promonExc = XposedHelpers.findClass(className, lpparam.classLoader); for (Method method : promonExc.getDeclaredMethods()) { XposedBridge.hookMethod(method, new XC_MethodHook() { @Override @@ -1332,6 +1368,7 @@ public final class TngRootBypassHook { } catch (Throwable ignored) { // constructor overload may differ } + XposedBridge.log(TAG + " hooked Promon exception " + className); } catch (Throwable t) { XposedBridge.log(TAG + " " + className + " hook failed: " + t.getMessage()); } @@ -1355,16 +1392,38 @@ public final class TngRootBypassHook { if (className == null) { return false; } - if (!className.startsWith("xwwqazamx.")) { + if (!isPromonPackageClass(className)) { return false; } - // W/A 等单字母 Promon 异常;排除 bl/w/bg 等功能类 + // W/A/bd 等 Promon 异常;排除 bl/w/bg/R 等功能类 int dot = className.lastIndexOf('.'); if (dot < 0) { return false; } String simple = className.substring(dot + 1); - return simple.length() <= 2; + if ("bd".equals(simple)) { + return true; + } + return simple.length() == 1; + } + + private static boolean isPromonPackageClass(String className) { + for (String prefix : PROMON_PKG_PREFIXES) { + if (className.startsWith(prefix + ".")) { + return true; + } + } + return false; + } + + private static Class findPromonClass(ClassLoader loader, String simpleName) { + for (String prefix : PROMON_PKG_PREFIXES) { + try { + return XposedHelpers.findClass(prefix + "." + simpleName, loader); + } catch (Throwable ignored) { + } + } + return null; } /** SecurityGuard:探测命令 stub;10101 init + 104xx/105xx sign/verify 走真实 native。 */ @@ -1866,35 +1925,24 @@ public final class TngRootBypassHook { /** Promon native 桥接类:强制 int/boolean 检测返回安全值。 */ private static void hookPromonNativeBridge(XC_LoadPackage.LoadPackageParam lpparam) { - String[] classes = { - "xwwqazamx.F", - "xwwqazamx.bg", - "xwwqazamx.b", - "xwwqazamx.c", - "xwwqazamx.d", - "xwwqazamx.h", - "xwwqazamx.k", - "xwwqazamx.l", - "xwwqazamx.m", - "xwwqazamx.o", - "xwwqazamx.s", - "xwwqazamx.t", - "xwwqazamx.z", + String[] simpleNames = { + "F", "bg", "b", "c", "d", "h", "k", "l", "m", "o", "s", "t", "z", }; int total = 0; - for (String className : classes) { - total += hookPromonIntBooleanMethods(lpparam, className); + for (String simpleName : simpleNames) { + Class clazz = findPromonClass(lpparam.classLoader, simpleName); + if (clazz != null) { + total += hookPromonIntBooleanMethods(clazz); + } } if (total > 0) { XposedBridge.log(TAG + " Promon native-bridge total hooks=" + total); } } - private static int hookPromonIntBooleanMethods( - XC_LoadPackage.LoadPackageParam lpparam, String className) { + private static int hookPromonIntBooleanMethods(Class clazz) { int count = 0; try { - Class clazz = XposedHelpers.findClass(className, lpparam.classLoader); for (Method method : clazz.getDeclaredMethods()) { Class returnType = method.getReturnType(); if (returnType != boolean.class && returnType != Boolean.class