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:
@@ -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 会循环 SEGV;cap=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.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<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/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<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();
|
||||
|
||||
Binary file not shown.
14
reverse/scripts/_scan_tng_vhv.py
Normal file
14
reverse/scripts/_scan_tng_vhv.py
Normal 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('/', '.')}")
|
||||
24
reverse/scripts/_scan_tng_vhv_bl.py
Normal file
24
reverse/scripts/_scan_tng_vhv_bl.py
Normal 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)
|
||||
20
reverse/scripts/parse_ui_dump.py
Normal file
20
reverse/scripts/parse_ui_dump.py
Normal 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}")
|
||||
13
reverse/scripts/parse_ui_dump_all.py
Normal file
13
reverse/scripts/parse_ui_dump_all.py
Normal 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}")
|
||||
110
reverse/scripts/test_tng_register_flow.py
Normal file
110
reverse/scripts/test_tng_register_flow.py
Normal 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())
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user