fix(tng): 适配 Promon 1.9.10 vhvlnqgy 包名并加固 Zygisk 信号跳过

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 待续攻。
This commit is contained in:
mars
2026-08-03 11:02:46 +08:00
parent 818b2f4f51
commit 18ae42ec63
8 changed files with 329 additions and 95 deletions

View File

@@ -42,8 +42,14 @@ static std::atomic<int> g_stack_chk{0};
static std::atomic<int> g_promon_segv{0};
static std::atomic<uintptr_t> g_promon_start{0};
static std::atomic<uintptr_t> g_promon_end{0};
/* 隔离进程 :goacqowmmt 会循环 SEGVcap=16 打满后 freeze 反拖累主进程 */
static constexpr int kMaxPromonSegvSkip = 0; /* 0 = unlimited LR-return skip */
static std::atomic<pid_t> g_main_tid{0};
static std::atomic<int> g_abrt_swallow{0};
static std::atomic<pid_t> g_abrt_last_tid{0};
static std::atomic<uintptr_t> g_abrt_last_pc{0};
static std::atomic<int> g_abrt_streak{0};
/* 隔离进程 :goacqowmmt 会循环 SEGV主进程 worker 也狂刷。cap 后 freeze 该线程 */
static constexpr int kMaxPromonSegvSkip = 200;
static constexpr int kMaxAbrtStreak = 3;
static std::atomic<int> 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.10Login 后出现 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<pid_t> g_main_tid{0};
static std::atomic<int> g_abrt_swallow{0};
static std::atomic<pid_t> g_abrt_last_tid{0};
static std::atomic<uintptr_t> g_abrt_last_pc{0};
static std::atomic<int> g_abrt_streak{0};
static constexpr int kMaxAbrtStreak = 3;
/** ABRT/TRAPpc+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<ucontext_t *>(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();

View File

@@ -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('/', '.')}")

View File

@@ -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)

View File

@@ -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}")

View File

@@ -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}")

View File

@@ -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())

View File

@@ -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-circuita/b 之外的方法仍会跑 native~40s 后 stack_chk/SEGV。
* xwwqazamx.a.run 是 bl#b 后台 Runnable必须 beforeHook 直接 return。
* bl/R 全方法 short-circuita/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 后台 Runnablebl#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探测命令 stub10101 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