chore(tng): 清理死代码并停止跟踪 reverse/dumps

精简 login-first 不可达 Hook,忽略调试产物目录,从仓库移除已入库的 dumps 文件。
This commit is contained in:
mars
2026-08-04 13:32:57 +08:00
parent 5378a34f58
commit bc51fb35cc
85 changed files with 69 additions and 53722 deletions

View File

@@ -13,7 +13,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
@@ -45,20 +44,9 @@ public final class TngRootBypassHook {
/** Promon 混淆包名1.9.10 为 vhvlnqgy旧版为 xwwqazamx。 */
private static final String[] PROMON_PKG_PREFIXES = {"vhvlnqgy", "xwwqazamx"};
/**
* 登录优先:少 Hook、少短路 native-bridge避免 JNI DeleteLocalRef 损坏 → Runtime abort。
* 能进 UserLogin 后再逐步打开诊断 Hook。
*/
private static final boolean LOGIN_FIRST_MINIMAL = true;
private static final String SECURITY_ERROR_ACTIVITY =
"my.com.tngdigital.common.security.ui.SecurityErrorActivity";
private static final String[] BOOLEAN_HOOK_CLASSES = {
"my.com.tngdigital.common.jailbrkendetect.JailBrokenApiImpl",
"my.com.tngdigital.common.jailbrkendetect.JailBrokenManager",
};
private static final String[] BLOCKED_SUPPORT_MARKERS = {
"36616543382169-rooting",
"36616508159769-emulator",
@@ -71,8 +59,6 @@ public final class TngRootBypassHook {
};
private static volatile long lastBlockedSuicideAt = 0L;
private static final long SOFT_CRASH_GUARD_MS = 10000L;
/** 用户已进入注册/登录后续页时,禁止 Splash 强拉回 Login。 */
private static volatile boolean registrationFlowActive = false;
private static final String[] REGISTRATION_FLOW_MARKERS = {
"GuideActivity",
@@ -90,8 +76,6 @@ public final class TngRootBypassHook {
"EmailOtpVerificationActivity",
};
private static volatile int captchaDialogShowCount = 0;
private static final String[] REGISTRATION_RPC_MARKERS = {
"phonecheck", "com.abl.wallet.phone", "com.abl.wallet.otp",
"customer.registration", "customer.verify", "customer.login",
@@ -106,7 +90,52 @@ public final class TngRootBypassHook {
return PACKAGE.equals(packageName);
}
private static final ThreadLocal<String> CURRENT_REQUEST_URL = new ThreadLocal<>();
public static void install(XC_LoadPackage.LoadPackageParam lpparam) {
if (!isTargetPackage(lpparam.packageName)) {
return;
}
XposedBridge.log(TAG + " install for " + lpparam.packageName
+ " pid=" + Process.myPid()
+ " proc=" + getProcessName());
hookEarlyAttachLog(lpparam);
hookConscryptStatsLogGuard();
// 登录优先:藏 root + 拦自杀 Java 链 + bl#a/b不碰 native-bridge 探测短路。
hookSplashForceLogin(lpparam);
hookLoginDismissSplash(lpparam);
// 点区号会走 onCountryClick → i7.l loading真 show → HWUI setName/gralloc abort
hookLoadingDialogSkip();
// setName 一律 noopA16+Zygisk 下 native setName→dlopen libandroid 易 ART abort
// 区号 Compose 必须 HW软件绘制会 IllegalArgumentException(hardware bitmaps)。
hookHardwareRendererSetNameNoop();
hookCallingCodeAllowHwSurface(lpparam);
RootBypassHelper.hookFileExists(lpparam);
RootBypassHelper.hookRuntimeExec(lpparam);
RootBypassHelper.hookSystemGetProperty(lpparam);
ProcMapsFilterHook.install(lpparam);
hookAntiSuicide();
hookPromonSuicideUpstream(lpparam);
hookUncaughtPromonException(lpparam);
hookKillApplicationHandler(lpparam);
hookBlockSecurityErrorLaunch(lpparam);
hookPromonNativeGuard(lpparam);
hookPromonLifecycle(lpparam);
hookActivityThreadExit(lpparam);
hookFinishAllActivityAndKillApp(lpparam);
hookSecurityUrlOpeners(lpparam);
hookJailBroken(lpparam);
hookJailBrokenRpc(lpparam);
hookAppSecurityManager(lpparam);
hookAppSecurityCallbacks(lpparam);
hookSecurityErrorActivity(lpparam);
// seccomp 开着时必须 stub TigerTally init否则 fork getprop 永不退出 → App.onCreate ANR
hookTigerTally(lpparam);
hookTigerTallyAppWrappers(lpparam);
XposedBridge.log(TAG + " login-first hooks armed");
}
/** 进程启动最早打点,便于确认 LSPosed 是否注入(注册闪退常因 hook 未生效)。 */
private static void hookEarlyAttachLog(XC_LoadPackage.LoadPackageParam lpparam) {
@@ -130,13 +159,6 @@ public final class TngRootBypassHook {
/** Splash 优先生效;新 schedule 会取消旧 RunnableApplication 兜底 vs Splash 2000ms。 */
private static final Handler FORCE_LOGIN_HANDLER = new Handler(Looper.getMainLooper());
private static Runnable pendingForceLoginRunnable;
/** Promon native-bridge short-circuit 重入保护,避免 __cxa_guard_acquire 递归 abort。 */
private static final ThreadLocal<Integer> PROMON_BRIDGE_DEPTH = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
private static void scheduleForceLoginToUserLogin(
final Context appCtx, final Activity splashAct, final String reason, final long delayMs) {
@@ -152,7 +174,7 @@ public final class TngRootBypassHook {
public void run() {
pendingForceLoginRunnable = null;
try {
if (registrationFlowActive || isTopActivityRegistrationFlow(appCtx)) {
if (isTopActivityRegistrationFlow(appCtx)) {
XposedBridge.log(TAG + " skip force login (" + reason + ", registration flow)");
return;
}
@@ -224,106 +246,6 @@ public final class TngRootBypassHook {
return state.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
}
private static final String[] HTTP_LOG_MARKERS = {
"otp", "verify", "pin", "register", "auth", "login", "sms", "mobile",
"risk", "token", "error", "code", "unexpected", "reference",
"captcha", "nc_token", "nvc", "slide", "security",
};
public static void install(XC_LoadPackage.LoadPackageParam lpparam) {
if (!isTargetPackage(lpparam.packageName)) {
return;
}
XposedBridge.log(TAG + " install for " + lpparam.packageName
+ " pid=" + Process.myPid()
+ " proc=" + getProcessName()
+ " minimal=" + LOGIN_FIRST_MINIMAL);
hookEarlyAttachLog(lpparam);
hookConscryptStatsLogGuard();
if (LOGIN_FIRST_MINIMAL) {
installLoginFirstMinimal(lpparam);
return;
}
hookRegistrationFlowGuard(lpparam);
hookSplashForceLogin(lpparam);
hookLoginDismissSplash(lpparam);
hookHwuiBySurfacePolicy(lpparam);
hookLoadingDialogSkip();
hookBottomSelectDialogDiag(lpparam);
hookCaptchaDiag(lpparam);
hookActivityLifecycleDiag(lpparam);
hookPromonApService(lpparam);
hookPromonBroadcastReceiver(lpparam);
hookAppAttachForceLogin(lpparam);
RootBypassHelper.hookFileExists(lpparam);
RootBypassHelper.hookRuntimeExec(lpparam);
RootBypassHelper.hookSystemGetProperty(lpparam);
ProcMapsFilterHook.install(lpparam);
hookAntiSuicide();
hookPromonSuicideUpstream(lpparam);
hookUncaughtPromonException(lpparam);
hookKillApplicationHandler(lpparam);
hookBlockSecurityErrorLaunch(lpparam);
hookPromonNativeGuard(lpparam);
hookPromonLifecycle(lpparam);
hookJnicLibrary(lpparam);
hookTigerTally(lpparam);
hookActivityThreadExit(lpparam);
hookForceExitFlow(lpparam);
hookFinishAllActivityAndKillApp(lpparam);
hookShowSecurityScreenForState(lpparam);
hookSecurityUrlOpeners(lpparam);
hookJailBroken(lpparam);
hookJailBrokenRpc(lpparam);
hookAppSecurityManager(lpparam);
hookAppSecurityCallbacks(lpparam);
hookPromonNativeBridge(lpparam);
hookSecurityBooleanChecks(lpparam);
hookSecurityErrorActivity(lpparam);
hookNetworkDiag(lpparam);
hookWebViewErrorDiag(lpparam);
}
/** 最小集:藏 root + 拦自杀 Java 链 + bl#a/b不碰 native-bridge 探测短路。 */
private static void installLoginFirstMinimal(XC_LoadPackage.LoadPackageParam lpparam) {
hookSplashForceLogin(lpparam);
hookLoginDismissSplash(lpparam);
// 点区号会走 onCountryClick → i7.l loading真 show → HWUI setName/gralloc abort
hookLoadingDialogSkip();
// setName 一律 noopA16+Zygisk 下 native setName→dlopen libandroid 易 ART abort
// 区号 Compose 必须 HW软件绘制会 IllegalArgumentException(hardware bitmaps)。
hookHardwareRendererSetNameNoop();
hookCallingCodeAllowHwSurface(lpparam);
RootBypassHelper.hookFileExists(lpparam);
RootBypassHelper.hookRuntimeExec(lpparam);
RootBypassHelper.hookSystemGetProperty(lpparam);
ProcMapsFilterHook.install(lpparam);
hookAntiSuicide();
hookPromonSuicideUpstream(lpparam);
hookUncaughtPromonException(lpparam);
hookKillApplicationHandler(lpparam);
hookBlockSecurityErrorLaunch(lpparam);
hookPromonNativeGuard(lpparam);
hookPromonLifecycle(lpparam);
hookActivityThreadExit(lpparam);
hookFinishAllActivityAndKillApp(lpparam);
hookSecurityUrlOpeners(lpparam);
hookJailBroken(lpparam);
hookJailBrokenRpc(lpparam);
hookAppSecurityManager(lpparam);
hookAppSecurityCallbacks(lpparam);
hookSecurityErrorActivity(lpparam);
// seccomp 开着时必须 stub TigerTally init否则 fork getprop 永不退出 → App.onCreate ANR
hookTigerTally(lpparam);
hookTigerTallyAppWrappers(lpparam);
XposedBridge.log(TAG + " login-first minimal hooks armed");
}
/**
* Zygisk/命名空间下 android.util.StatsLog native 常 UnsatisfiedLinkError
* Conscrypt TLS 指标线程一写就炸 → ART fatal。直接 noop 指标写入。
@@ -365,276 +287,6 @@ public final class TngRootBypassHook {
XposedBridge.log(TAG + " conscrypt/StatsLog guard hooked n=" + hooked);
}
private static void logActivityDiag(String phase, Activity activity) {
if (activity == null) {
return;
}
String name = activity.getClass().getName();
String lower = name.toLowerCase(Locale.US);
if (lower.contains("userpin")
|| lower.contains("userlogin")
|| lower.contains("registration")
|| lower.contains("otp")
|| lower.contains("verify")
|| lower.contains("sms")
|| lower.contains("webview")
|| lower.contains("issue")
|| lower.contains("guide")
|| lower.contains("error")
|| lower.contains("dialog")) {
XposedBridge.log(TAG + " ACT " + phase + " " + name);
}
}
/** 记录 OTP/登录相关 HTTP 请求与响应体,定位验证码提交失败原因。 */
private static void hookNetworkDiag(XC_LoadPackage.LoadPackageParam lpparam) {
try {
XposedHelpers.findAndHookMethod(
"okhttp3.Request$Builder",
lpparam.classLoader,
"build",
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
try {
Object url = XposedHelpers.callMethod(param.getResult(), "url");
if (url != null) {
CURRENT_REQUEST_URL.set(String.valueOf(url));
}
} catch (Throwable ignored) {
}
}
});
} catch (Throwable t) {
XposedBridge.log(TAG + " diag Request.Builder.build failed: " + t.getMessage());
}
try {
XposedHelpers.findAndHookMethod(
"okhttp3.ResponseBody",
lpparam.classLoader,
"string",
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
String body = (String) param.getResult();
String url = CURRENT_REQUEST_URL.get();
CURRENT_REQUEST_URL.remove();
if (body == null) {
return;
}
if (!shouldLogHttp(url, body)) {
return;
}
String snippet = body.length() > 800
? body.substring(0, 800) + "..." : body;
XposedBridge.log(TAG + " HTTP rsp"
+ (url != null ? " " + url : "")
+ " body=" + snippet);
}
});
} catch (Throwable t) {
XposedBridge.log(TAG + " diag ResponseBody.string failed: " + t.getMessage());
}
XC_MethodHook callRequestHook = new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
try {
Object req = XposedHelpers.callMethod(param.thisObject, "request");
logHttpRequest(lpparam.classLoader, req);
} catch (Throwable ignored) {
}
}
};
XC_MethodHook enqueueHook = new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
try {
Object req = XposedHelpers.callMethod(param.thisObject, "request");
logHttpRequest(lpparam.classLoader, req);
} catch (Throwable ignored) {
}
}
};
for (String className : new String[]{
"okhttp3.RealCall", "okhttp3.internal.connection.RealCall"}) {
try {
XposedHelpers.findAndHookMethod(
className, lpparam.classLoader, "execute", callRequestHook);
XposedHelpers.findAndHookMethod(
className, lpparam.classLoader, "enqueue",
"okhttp3.Callback", enqueueHook);
} catch (Throwable ignored) {
}
}
XposedBridge.log(TAG + " hooked network diag (okhttp)");
}
private static void logHttpRequest(ClassLoader loader, Object req) {
if (req == null) {
return;
}
Object url = XposedHelpers.callMethod(req, "url");
if (url == null) {
return;
}
String urlStr = String.valueOf(url);
if (!shouldLogHttpUrl(urlStr)) {
return;
}
XposedBridge.log(TAG + " HTTP req " + urlStr);
Object body = XposedHelpers.callMethod(req, "body");
if (body != null) {
logRequestBodySnippet(loader, body);
}
}
private static boolean shouldLogHttpUrl(String url) {
if (url == null) {
return false;
}
String lower = url.toLowerCase(Locale.US);
return lower.contains("tngdigital")
|| lower.contains("ebuckler")
|| lower.contains("imgw")
|| lower.contains("abl.wallet")
|| lower.contains("alipay")
|| lower.contains("aliyun")
|| lower.contains("captcha")
|| lower.contains("dun.163")
|| lower.contains("nc_token")
|| lower.contains("otp")
|| lower.contains("verify")
|| lower.contains("register")
|| lower.contains("auth")
|| lower.contains("login")
|| lower.contains("pin")
|| lower.contains("sms");
}
private static boolean shouldLogHttp(String url, String body) {
if (shouldLogHttpUrl(url)) {
return true;
}
String lower = body.toLowerCase(Locale.US);
for (String marker : HTTP_LOG_MARKERS) {
if (lower.contains(marker)) {
return true;
}
}
return lower.contains("\"code\"") || lower.contains("reference");
}
private static void logRequestBodySnippet(ClassLoader loader, Object body) {
try {
Class<?> bufferClass = XposedHelpers.findClass("okio.Buffer", loader);
Object buffer = XposedHelpers.newInstance(bufferClass);
XposedHelpers.callMethod(body, "writeTo", buffer);
String text = (String) XposedHelpers.callMethod(buffer, "readUtf8");
if (text == null || text.isEmpty()) {
return;
}
String snippet = text.length() > 500 ? text.substring(0, 500) + "..." : text;
XposedBridge.log(TAG + " HTTP req body=" + snippet);
} catch (Throwable ignored) {
}
}
private static void hookWebViewErrorDiag(XC_LoadPackage.LoadPackageParam lpparam) {
try {
XposedHelpers.findAndHookMethod(
"android.webkit.WebViewClient",
lpparam.classLoader,
"onReceivedError",
"android.webkit.WebView",
"android.webkit.WebResourceRequest",
"android.webkit.WebResourceError",
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
try {
Object error = param.args[2];
Object code = XposedHelpers.callMethod(error, "getDescription");
Object url = XposedHelpers.callMethod(param.args[1], "getUrl");
XposedBridge.log(TAG + " WebView error url=" + url + " desc=" + code);
} catch (Throwable t) {
XposedBridge.log(TAG + " WebView error: " + t.getMessage());
}
}
});
XposedBridge.log(TAG + " hooked WebViewClient.onReceivedError");
} catch (Throwable t) {
XposedBridge.log(TAG + " WebView error hook failed: " + t.getMessage());
}
}
/** 仅记录注册/登录链 Activity 生命周期,便于 logcat 定位卡点。 */
private static void hookActivityLifecycleDiag(XC_LoadPackage.LoadPackageParam lpparam) {
try {
XposedHelpers.findAndHookMethod(Application.class, "onCreate", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
Context ctx = (Context) param.thisObject;
if (ctx == null || !PACKAGE.equals(ctx.getPackageName())) {
return;
}
Application app = (Application) param.thisObject;
app.registerActivityLifecycleCallbacks(
new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
logActivityDiag("onCreate", activity);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
logActivityDiag("onResume", activity);
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(
Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
XposedBridge.log(TAG + " registered activity lifecycle diag");
}
});
} catch (Throwable t) {
XposedBridge.log(TAG + " activity lifecycle diag failed: " + t.getMessage());
}
}
/**
* Android 16 HWUI 策略(经 APK 逆向核对):
* - i7.l一律 skip show真 show → gralloc ABRT
* - Login / Splash / 普通 View 页:清 HW flag + 拦 enableHW软件绘制
* - UserSearchCallingCodeActivityCompose CallingListScreen/LazyColumn
* 软件绘制 = 黑屏;必须完整 HW与 BottomSelect 相同)
* - BottomSelect完整 HW关了会黑
* - captcha软件层弹窗不放行 enableHW
*/
private static final ThreadLocal<Boolean> blockSetNameForDialog = new ThreadLocal<>();
/** BottomSelect Dialog.show 期间放行 enableHW。 */
private static final ThreadLocal<Boolean> allowHwForDialog = new ThreadLocal<>();
/** 区号 BottomSelect 弹窗可见。 */
private static volatile boolean bottomSelectDialogActive = false;
/**
* 区号 Compose Activity 生命周期标记。
* 必须在 execStart/onCreate 前设为 true供 enableHardwareAcceleration 放行。
@@ -669,223 +321,6 @@ public final class TngRootBypassHook {
return action != null && action.contains("CallingCode");
}
/**
* Compose 区号页 + BottomSelect 必须放行 enableHW。
* 其它页面拦 enableHW 且清 FLAG避免半开 HW 黑屏。
*/
private static boolean shouldAllowHwSurface() {
return callingCodeSurfaceActive
|| bottomSelectDialogActive
|| Boolean.TRUE.equals(allowHwForDialog.get());
}
private static boolean shouldBlockSetNameOnMain() {
if (shouldAllowHwSurface()) {
return false;
}
// 区号页绘制期也不拦 setName软件层下 setName 较少);仅 Dialog 窗口期拦
return Boolean.TRUE.equals(blockSetNameForDialog.get());
}
/** 必须在 ViewRoot 附着前清 flag否则拦 enableHW 会黑屏。 */
private static void clearHardwareAcceleratedFlag(Activity activity) {
try {
Window window = activity.getWindow();
if (window != null) {
window.clearFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}
} catch (Throwable t) {
XposedBridge.log(TAG + " clear HW flag failed: " + t.getMessage());
}
}
/**
* 区号 BottomSelect须 hook 父类 DialogFragment.show/onStartKotlin 子类不 override show
* 勿关 BottomSheet HW 加速Android 16 黑屏);可见期间放行主线程 setName。
*/
private static void hookBottomSelectDialogDiag(XC_LoadPackage.LoadPackageParam lpparam) {
int hooked = 0;
try {
Class<?> df = XposedHelpers.findClass(
"androidx.fragment.app.DialogFragment", lpparam.classLoader);
Class<?> fm = XposedHelpers.findClass(
"androidx.fragment.app.FragmentManager", lpparam.classLoader);
XposedHelpers.findAndHookMethod(df, "show", fm, String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
String owner = param.thisObject.getClass().getName();
if (!owner.contains("BottomSelect")) {
return;
}
bottomSelectDialogActive = true;
XposedBridge.log(TAG + " BottomSelect show " + owner);
}
});
hooked++;
XposedHelpers.findAndHookMethod(df, "onStart", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
String owner = param.thisObject.getClass().getName();
if (!owner.contains("BottomSelect")) {
return;
}
bottomSelectDialogActive = true;
XposedBridge.log(TAG + " BottomSelect onStart " + owner);
}
});
hooked++;
XC_MethodHook deactivateHook = new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
String owner = param.thisObject.getClass().getName();
if (!owner.contains("BottomSelect")) {
return;
}
bottomSelectDialogActive = false;
XposedBridge.log(TAG + " BottomSelect inactive "
+ param.method.getName() + " " + owner);
}
};
XposedHelpers.findAndHookMethod(df, "onStop", deactivateHook);
XposedHelpers.findAndHookMethod(df, "onDestroyView", deactivateHook);
hooked += 2;
} catch (Throwable t) {
XposedBridge.log(TAG + " DialogFragment BottomSelect hook failed: " + t.getMessage());
}
try {
Class<?> sheet = XposedHelpers.findClass(
"com.google.android.material.bottomsheet.BottomSheetDialog", lpparam.classLoader);
XposedHelpers.findAndHookMethod(sheet, "show", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
bottomSelectDialogActive = true;
XposedBridge.log(TAG + " BottomSheetDialog.show active");
}
});
XposedHelpers.findAndHookMethod(sheet, "dismiss", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
bottomSelectDialogActive = false;
XposedBridge.log(TAG + " BottomSheetDialog.dismiss inactive");
}
});
hooked += 2;
} catch (Throwable t) {
XposedBridge.log(TAG + " BottomSheetDialog hook skip: " + t.getMessage());
}
XposedBridge.log(TAG + " hooked BottomSelect lifecycle n=" + hooked);
}
/**
* 安全验证诊断:只 hook 关键类 + WebView避免全量 58 方法拖垮主线程。
*/
private static void hookCaptchaDiag(XC_LoadPackage.LoadPackageParam lpparam) {
int total = 0;
for (String className : new String[]{
"com.aliyun.captcha.Captcha",
"com.aliyun.captcha.CaptchaWebViewDialog",
}) {
total += hookAllConcreteMethods(lpparam.classLoader, className, "captcha");
try {
Class<?> clazz = XposedHelpers.findClass(className, lpparam.classLoader);
for (Class<?> inner : clazz.getDeclaredClasses()) {
String n = inner.getSimpleName();
if (!n.contains("Callback") && !n.contains("Listener")
&& !n.contains("Completion")) {
continue;
}
total += hookAllConcreteMethodsOnClass(inner, "captcha");
XposedBridge.log(TAG + " captcha inner " + inner.getName());
}
} catch (Throwable t) {
XposedBridge.log(TAG + " captcha inner scan " + className + ": " + t.getMessage());
}
}
hookCaptchaWebView(lpparam);
XposedBridge.log(TAG + " captcha diag hooked methods n=" + total);
}
private static int hookAllConcreteMethods(
ClassLoader loader, String className, String tag) {
try {
Class<?> clazz = XposedHelpers.findClass(className, loader);
return hookAllConcreteMethodsOnClass(clazz, tag);
} catch (Throwable t) {
XposedBridge.log(TAG + " " + tag + " class skip " + className + ": " + t.getMessage());
return 0;
}
}
private static int hookAllConcreteMethodsOnClass(Class<?> clazz, String tag) {
int hooked = 0;
final String simple = clazz.getName();
for (Method method : clazz.getDeclaredMethods()) {
if (Modifier.isAbstract(method.getModifiers())) {
continue;
}
// 跳过高频噪声
String name = method.getName();
if ("toString".equals(name) || "hashCode".equals(name)
|| "equals".equals(name) || "clone".equals(name)) {
continue;
}
final String hookLabel = simple + "#" + name;
final Method hookedMethod = method;
try {
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log(TAG + " " + tag + " CALL " + hookLabel
+ " args=" + describeHookArgs(param.args));
}
@Override
protected void afterHookedMethod(MethodHookParam param) {
if (param.hasThrowable()) {
XposedBridge.log(TAG + " " + tag + " ERR " + hookLabel
+ " " + param.getThrowable().getClass().getSimpleName()
+ ": " + param.getThrowable().getMessage());
return;
}
Object result = param.getResult();
if (result == null
&& (hookedMethod.getReturnType() == void.class
|| hookedMethod.getReturnType() == Void.class)) {
return;
}
XposedBridge.log(TAG + " " + tag + " RET " + hookLabel
+ " -> " + describeHookResult(result));
}
});
hooked++;
} catch (Throwable t) {
XposedBridge.log(TAG + " " + tag + " hook fail " + hookLabel
+ ": " + t.getMessage());
}
}
if (hooked > 0) {
XposedBridge.log(TAG + " " + tag + " hooked " + hooked + " on " + simple);
}
return hooked;
}
private static String describeHookResult(Object result) {
if (result == null) {
return "null";
}
if (result instanceof String) {
String text = (String) result;
return "String(" + (text.length() > 200 ? text.substring(0, 200) + "..." : text) + ")";
}
if (result instanceof byte[]) {
return "byte[" + ((byte[]) result).length + "]";
}
if (result instanceof Boolean || result instanceof Number) {
return String.valueOf(result);
}
return result.getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(result));
}
private static String shortStack(int maxFrames) {
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
StringBuilder sb = new StringBuilder();
@@ -910,124 +345,13 @@ public final class TngRootBypassHook {
return sb.length() == 0 ? "(empty)" : sb.toString();
}
/** Captcha WebView页面加载/错误/JS 回调全打点。 */
private static void hookCaptchaWebView(XC_LoadPackage.LoadPackageParam lpparam) {
try {
XposedHelpers.findAndHookMethod(
"android.webkit.WebView", lpparam.classLoader, "loadUrl", String.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
String url = (String) param.args[0];
if (url == null) {
return;
}
String lower = url.toLowerCase(Locale.US);
if (lower.contains("captcha") || lower.contains("aliyun")
|| lower.contains("nvc") || lower.contains("nc_")
|| lower.contains("slide") || lower.startsWith("javascript:")) {
XposedBridge.log(TAG + " captcha WebView.loadUrl "
+ (url.length() > 300 ? url.substring(0, 300) + "..." : url));
}
}
});
XposedBridge.log(TAG + " hooked WebView.loadUrl (captcha filter)");
} catch (Throwable t) {
XposedBridge.log(TAG + " WebView.loadUrl captcha hook failed: " + t.getMessage());
}
try {
XposedHelpers.findAndHookMethod(
"android.webkit.WebView", lpparam.classLoader,
"evaluateJavascript", String.class,
"android.webkit.ValueCallback",
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
String script = (String) param.args[0];
if (script == null) {
return;
}
String lower = script.toLowerCase(Locale.US);
if (lower.contains("captcha") || lower.contains("token")
|| lower.contains("verify") || lower.contains("nc")
|| lower.contains("slide") || lower.contains("callback")) {
XposedBridge.log(TAG + " captcha WebView.evalJS "
+ (script.length() > 240 ? script.substring(0, 240) + "..." : script));
}
}
});
XposedBridge.log(TAG + " hooked WebView.evaluateJavascript (captcha filter)");
} catch (Throwable t) {
XposedBridge.log(TAG + " WebView.evalJS captcha hook failed: " + t.getMessage());
}
try {
XposedHelpers.findAndHookMethod(
"android.webkit.WebViewClient",
lpparam.classLoader,
"onPageFinished",
"android.webkit.WebView",
String.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
String url = (String) param.args[1];
if (url == null) {
return;
}
String lower = url.toLowerCase(Locale.US);
if (lower.contains("captcha") || lower.contains("aliyun")
|| lower.contains("nvc") || lower.contains("nc_")) {
XposedBridge.log(TAG + " captcha WebView.onPageFinished " + url);
}
}
});
} catch (Throwable t) {
XposedBridge.log(TAG + " WebViewClient.onPageFinished captcha hook failed: "
+ t.getMessage());
}
// 已有 onReceivedError diag再补 captcha 专用标记
try {
XposedHelpers.findAndHookMethod(
"android.webkit.WebViewClient",
lpparam.classLoader,
"onReceivedError",
"android.webkit.WebView",
"android.webkit.WebResourceRequest",
"android.webkit.WebResourceError",
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
try {
Object req = param.args[1];
Object err = param.args[2];
Object url = XposedHelpers.callMethod(req, "getUrl");
Object desc = XposedHelpers.callMethod(err, "getDescription");
String urlStr = String.valueOf(url);
if (urlStr.toLowerCase(Locale.US).contains("captcha")
|| urlStr.toLowerCase(Locale.US).contains("aliyun")
|| urlStr.toLowerCase(Locale.US).contains("nvc")) {
XposedBridge.log(TAG + " captcha WebView.error url="
+ urlStr + " desc=" + desc);
}
} catch (Throwable ignored) {
}
}
});
} catch (Throwable ignored) {
}
}
private static int hookCaptchaCallbackClass(Class<?> clazz, String label) {
return hookAllConcreteMethodsOnClass(clazz, "captcha");
}
/** 已 skip show 的 loading Dialog让 isShowing=true 避免业务层卡死。 */
private static final Set<Dialog> skippedLoadingDialogs =
Collections.newSetFromMap(new WeakHashMap<>());
/**
* i7.l 等 loading一律 skip show + 伪装 isShowing。
* 不在此处开 HW 白名单——区号列表由 startActivity 标记
* Captcha 弹窗清 HW flag避免 Android 16 gralloc abort
*/
private static void hookLoadingDialogSkip() {
try {
@@ -1039,42 +363,21 @@ public final class TngRootBypassHook {
if (isLoadingDialog(owner)) {
skippedLoadingDialogs.add(dialog);
XposedBridge.log(TAG + " Dialog.show skip loading " + owner
+ " callingCode=" + callingCodeSurfaceActive
+ " regFlow=" + registrationFlowActive);
+ " callingCode=" + callingCodeSurfaceActive);
param.setResult(null);
return;
}
if (owner.contains("BottomSelect") || owner.contains("BottomSheet")) {
bottomSelectDialogActive = true;
allowHwForDialog.set(Boolean.TRUE);
} else if (isCaptchaDialog(owner)) {
// Android 16captcha 放行 enableHW → gralloc ABRT 黑屏闪退
// 改为软件层弹窗,不进白名单
if (isCaptchaDialog(owner)) {
try {
Dialog captchaDialog = (Dialog) param.thisObject;
Window cw = captchaDialog.getWindow();
Window cw = dialog.getWindow();
if (cw != null) {
cw.clearFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}
} catch (Throwable ignored) {
}
captchaDialogShowCount++;
XposedBridge.log(TAG + " Dialog.show CaptchaWebViewDialog #"
+ captchaDialogShowCount
+ " owner=" + owner
+ " hw=software"
+ " regFlow=" + registrationFlowActive
+ " stack=" + shortStack(8));
} else if (!callingCodeSurfaceActive) {
blockSetNameForDialog.set(Boolean.TRUE);
XposedBridge.log(TAG + " Dialog.show captcha software " + owner);
}
}
@Override
protected void afterHookedMethod(MethodHookParam param) {
blockSetNameForDialog.remove();
allowHwForDialog.remove();
}
});
XposedBridge.log(TAG + " hooked Dialog.show (skip loading only)");
} catch (Throwable t) {
@@ -1098,9 +401,6 @@ public final class TngRootBypassHook {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
String owner = param.thisObject.getClass().getName();
if (owner.contains("BottomSelect") || owner.contains("BottomSheet")) {
bottomSelectDialogActive = false;
}
if (isLoadingDialog(owner)) {
skippedLoadingDialogs.remove(param.thisObject);
}
@@ -1229,322 +529,6 @@ public final class TngRootBypassHook {
XposedBridge.log(TAG + " callingCode allow-HW surface armed");
}
/**
* 非区号:清 HW flag + 拦 enableHW成对防半开黑屏
* 区号 Compose / BottomSelect完整 HW软件绘制 Compose 必黑)。
* Dialog.show 窗口期可拦 setName。
*/
private static void hookHwuiBySurfacePolicy(XC_LoadPackage.LoadPackageParam lpparam) {
// setName 一律 noopA16 Zygisk 下 native setName 会 abort
hookHardwareRendererSetNameNoop();
try {
Class<?> vri = XposedHelpers.findClass("android.view.ViewRootImpl", null);
int hooked = 0;
for (Method method : vri.getDeclaredMethods()) {
if (!"enableHardwareAcceleration".equals(method.getName())) {
continue;
}
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
if (shouldAllowHwSurface()) {
return;
}
param.setResult(null);
}
});
hooked++;
}
XposedBridge.log(TAG + " hooked ViewRootImpl.enableHardwareAcceleration n=" + hooked);
} catch (Throwable t) {
XposedBridge.log(TAG + " ViewRootImpl HW hook failed: " + t.getMessage());
}
XC_MethodHook markCallingCodeLaunch = new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
Intent intent = extractIntent(param.args);
if (isCallingCodeIntent(intent)) {
callingCodeSurfaceActive = true;
XposedBridge.log(TAG + " callingCode surface ON (launch, HW allowed for Compose)");
}
}
};
try {
XposedHelpers.findAndHookMethod(
Activity.class, "startActivity", Intent.class, markCallingCodeLaunch);
XposedHelpers.findAndHookMethod(
Activity.class, "startActivity", Intent.class, Bundle.class, markCallingCodeLaunch);
} catch (Throwable t) {
XposedBridge.log(TAG + " startActivity callingCode mark failed: " + t.getMessage());
}
try {
XposedHelpers.findAndHookMethod(
Instrumentation.class,
"execStartActivity",
Context.class,
android.os.IBinder.class,
android.os.IBinder.class,
Activity.class,
Intent.class,
int.class,
Bundle.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
Intent intent = (Intent) param.args[4];
if (isCallingCodeIntent(intent)) {
callingCodeSurfaceActive = true;
XposedBridge.log(TAG
+ " callingCode surface ON (execStart, HW allowed for Compose)");
}
}
});
} catch (Throwable t) {
XposedBridge.log(TAG + " execStartActivity callingCode mark failed: " + t.getMessage());
}
try {
XposedHelpers.findAndHookMethod(
Instrumentation.class,
"callActivityOnCreate",
Activity.class,
Bundle.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
Activity activity = (Activity) param.args[0];
if (activity == null) {
return;
}
String name = activity.getClass().getName();
if (isCallingCodeActivity(name)) {
callingCodeSurfaceActive = true;
XposedBridge.log(TAG
+ " callingCode onCreate BEFORE — keep HW for Compose");
// Compose 禁止清 FLAG
return;
}
if (!shouldAllowHwSurface()) {
clearHardwareAcceleratedFlag(activity);
}
}
@Override
protected void afterHookedMethod(MethodHookParam param) {
Activity activity = (Activity) param.args[0];
if (activity == null) {
return;
}
String name = activity.getClass().getName();
if (isCallingCodeActivity(name)) {
boolean hw = false;
try {
View decor = activity.getWindow() != null
? activity.getWindow().getDecorView()
: null;
hw = decor != null && decor.isHardwareAccelerated();
} catch (Throwable ignored) {
}
XposedBridge.log(TAG
+ " callingCode onCreate AFTER hwAccelerated=" + hw
+ " allow=" + shouldAllowHwSurface());
return;
}
if (shouldAllowHwSurface()) {
return;
}
clearHardwareAcceleratedFlag(activity);
XposedBridge.log(TAG + " clear HW flag "
+ activity.getClass().getSimpleName());
}
});
// Android 16 可能走 PersistableBundle 重载
try {
Class<?> persistable = Class.forName("android.os.PersistableBundle");
XposedHelpers.findAndHookMethod(
Instrumentation.class,
"callActivityOnCreate",
Activity.class,
Bundle.class,
persistable,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
Activity activity = (Activity) param.args[0];
if (activity == null) {
return;
}
if (isCallingCodeActivity(activity.getClass().getName())) {
callingCodeSurfaceActive = true;
XposedBridge.log(TAG
+ " callingCode onCreate(Persistable) — keep HW");
return;
}
if (!shouldAllowHwSurface()) {
clearHardwareAcceleratedFlag(activity);
}
}
});
} catch (Throwable ignored) {
}
XposedHelpers.findAndHookMethod(
Instrumentation.class,
"callActivityOnDestroy",
Activity.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
Activity activity = (Activity) param.args[0];
if (activity == null) {
return;
}
if (isCallingCodeActivity(activity.getClass().getName())) {
callingCodeSurfaceActive = false;
XposedBridge.log(TAG + " callingCode surface OFF (onDestroy)");
}
}
});
XposedBridge.log(TAG + " hooked HWUI surface policy (Compose callingCode=HW)");
} catch (Throwable t) {
XposedBridge.log(TAG + " HWUI surface policy failed: " + t.getMessage());
}
}
private static String describeHookArgs(Object[] args) {
if (args == null || args.length == 0) {
return "[]";
}
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < args.length; i++) {
if (i > 0) {
sb.append(", ");
}
Object arg = args[i];
if (arg == null) {
sb.append("null");
} else if (arg instanceof String) {
String text = (String) arg;
sb.append("String(").append(text.length() > 120
? text.substring(0, 120) + "..." : text).append(")");
} else if (arg instanceof byte[]) {
sb.append("byte[").append(((byte[]) arg).length).append("]");
} else {
sb.append(arg.getClass().getSimpleName());
}
}
sb.append("]");
return sb.toString();
}
/** 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 {
final String svcName = svc.getName();
XposedHelpers.findAndHookMethod(svc, "onCreate", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
XposedBridge.log(TAG + " " + svcName + " onCreate pid=" + Process.myPid()
+ " proc=" + getProcessName());
}
});
for (Method method : svc.getDeclaredMethods()) {
if (!"onStartCommand".equals(method.getName())) {
continue;
}
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log(TAG + " " + svcName + " onStartCommand pid="
+ Process.myPid());
}
});
}
XposedBridge.log(TAG + " hooked " + svcName + " Service");
} catch (Throwable t) {
XposedBridge.log(TAG + " Promon ap Service hook failed: " + t.getMessage());
}
}
/** 标记注册链 Activity 活跃,防止 Splash 强拉 Login 清栈。 */
private static void hookRegistrationFlowGuard(XC_LoadPackage.LoadPackageParam lpparam) {
try {
XC_MethodHook flowGuardHook = new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
Activity activity = (Activity) param.args[0];
if (activity == null) {
return;
}
String name = activity.getClass().getName();
if (isRegistrationFlowActivity(name)) {
registrationFlowActive = true;
if (pendingForceLoginRunnable != null) {
FORCE_LOGIN_HANDLER.removeCallbacks(pendingForceLoginRunnable);
pendingForceLoginRunnable = null;
}
XposedBridge.log(TAG + " registration flow active: " + name);
} else if (name.endsWith(".SplashActivity")) {
registrationFlowActive = false;
callingCodeSurfaceActive = false;
} else if (name.endsWith(".UserLoginActivity")) {
registrationFlowActive = false;
}
}
};
XposedHelpers.findAndHookMethod(
Instrumentation.class,
"callActivityOnCreate",
Activity.class,
Bundle.class,
flowGuardHook);
XposedHelpers.findAndHookMethod(
Instrumentation.class,
"callActivityOnResume",
Activity.class,
flowGuardHook);
hookLoginOptionsDiag(lpparam);
XposedBridge.log(TAG + " hooked registration flow guard");
} catch (Throwable t) {
XposedBridge.log(TAG + " registration flow guard failed: " + t.getMessage());
}
}
/** Login 页 startLoginOptions 是进入注册/登录选项的网关 RPC 入口。 */
private static void hookLoginOptionsDiag(XC_LoadPackage.LoadPackageParam lpparam) {
final String login = "my.com.tngdigital.user.view.UserLoginActivity";
try {
Class<?> clazz = XposedHelpers.findClass(login, lpparam.classLoader);
for (Method method : clazz.getDeclaredMethods()) {
String name = method.getName();
if (!name.contains("LoginOptions") && !name.contains("loginOptions")
&& !name.contains("startLogin")) {
continue;
}
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log(TAG + " UserLoginActivity#" + name + " enter");
}
@Override
protected void afterHookedMethod(MethodHookParam param) {
XposedBridge.log(TAG + " UserLoginActivity#" + name + " done");
}
});
}
XposedBridge.log(TAG + " hooked UserLoginActivity login-options diag");
} catch (Throwable t) {
XposedBridge.log(TAG + " login-options diag failed: " + t.getMessage());
}
}
private static boolean isRegistrationFlowActivity(String className) {
if (className == null) {
return false;
@@ -1590,35 +574,6 @@ public final class TngRootBypassHook {
return false;
}
/** 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 {
int hooked = 0;
for (Method method : clazz.getDeclaredMethods()) {
if (!"onReceive".equals(method.getName())) {
continue;
}
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
param.setResult(null);
}
});
hooked++;
}
if (hooked > 0) {
XposedBridge.log(TAG + " hooked " + clazz.getName() + " onReceive x" + hooked);
}
} catch (Throwable t) {
XposedBridge.log(TAG + " Promon N hook failed: " + t.getMessage());
}
}
/**
* 强拉 Login 后系统 Splash 遮罩常挂在 UserLogin 上windows=Splash Screen
* 导致复进「未响应」。onCreate/onResume 强制 dismiss。
@@ -1754,33 +709,6 @@ public final class TngRootBypassHook {
}
}
/** Application.onCreate 兜底Splash beforeHook 未触发时仍强拉 Login跳过纯 Service 进程)。 */
private static void hookAppAttachForceLogin(XC_LoadPackage.LoadPackageParam lpparam) {
try {
XposedHelpers.findAndHookMethod(
android.app.Application.class,
"onCreate",
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
final Context appCtx = (Context) param.thisObject;
if (appCtx == null || !PACKAGE.equals(appCtx.getPackageName())) {
return;
}
String proc = getProcessName();
if (proc != null && proc.contains(":")) {
return;
}
XposedBridge.log(TAG + " Application.onCreate — schedule force login fallback");
scheduleForceLoginToUserLogin(appCtx, null, "Application/onCreate", 3500L);
}
});
XposedBridge.log(TAG + " hooked Application.onCreate force→UserLogin fallback");
} catch (Throwable t) {
XposedBridge.log(TAG + " Application force hook failed: " + t.getMessage());
}
}
private static volatile long lastSuicideLogAt = 0L;
private static volatile int suicideBlockCount = 0;
@@ -2128,20 +1056,14 @@ public final class TngRootBypassHook {
* 登录优先onActivity* / onApplication* 全部 noop不调原 native。
*/
private static void hookPromonLifecycle(XC_LoadPackage.LoadPackageParam lpparam) {
Class<?> promonExc = findPromonClass(lpparam.classLoader, "W");
if (promonExc == null) {
promonExc = findPromonClass(lpparam.classLoader, "bd");
}
final Class<?> promonExcFinal = promonExc;
for (String simple : new String[]{"w", "u"}) {
hookPromonLifecycleClass(lpparam, simple, promonExcFinal);
hookPromonLifecycleClass(lpparam, simple);
}
}
private static void hookPromonLifecycleClass(
XC_LoadPackage.LoadPackageParam lpparam,
String simpleName,
Class<?> promonExc) {
String simpleName) {
Class<?> lifecycleClass = findPromonClass(lpparam.classLoader, simpleName);
if (lifecycleClass == null) {
return;
@@ -2154,41 +1076,19 @@ public final class TngRootBypassHook {
if (!name.startsWith("onActivity") && !name.startsWith("onApplication")) {
continue;
}
if (LOGIN_FIRST_MINIMAL) {
// 不调原 native避免 JNI DeleteLocalRef → ART abort
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
setSafeHookResult(param, method);
}
});
} else {
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
if (!param.hasThrowable()) {
return;
}
Throwable t = param.getThrowable();
if (isPromonThrowable(t, promonExc)) {
XposedBridge.log(TAG + " swallowed " + t.getClass().getSimpleName()
+ " in " + lifecycleName + "#" + method.getName());
param.setThrowable(null);
}
}
});
}
// 不调原 native避免 JNI DeleteLocalRef → ART abort
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
setSafeHookResult(param, method);
}
});
hooked++;
}
if (hooked > 0) {
XposedBridge.log(TAG + " hooked " + hooked + " " + lifecycleName
+ " lifecycle ("
+ (LOGIN_FIRST_MINIMAL ? "noop (login-first)" : "afterHook only")
+ ")");
}
XposedBridge.log(TAG + " hooked " + hooked + " " + lifecycleName
+ " lifecycle (noop)");
} catch (Throwable t) {
XposedBridge.log(TAG + " " + lifecycleName + " lifecycle hook failed: "
+ t.getMessage());
XposedBridge.log(TAG + " skip lifecycle " + lifecycleName + ": " + t.getMessage());
}
}
@@ -2262,17 +1162,6 @@ public final class TngRootBypassHook {
}
}
private static void fixNullPromonResult(XC_MethodHook.MethodHookParam param, Method method) {
if (param.getResult() != null) {
return;
}
Class<?> returnType = method.getReturnType();
if (returnType == Integer.class || returnType == int.class) {
XposedBridge.log(TAG + " fixed null bl#" + method.getName() + " -> 0");
setSafeHookResult(param, method);
}
}
/** Promon 后台 Runnablebl#b 检测线程beforeHook 直接 noop禁止跑 native。 */
private static void hookPromonRunnable(XC_LoadPackage.LoadPackageParam lpparam) {
Class<?> runnableClass = findPromonClass(lpparam.classLoader, "a");
@@ -2327,10 +1216,6 @@ public final class TngRootBypassHook {
}
}
private static boolean isPromonException(Throwable t, Class<?> promonExc) {
return isPromonThrowable(t, promonExc);
}
private static boolean isPromonThrowable(Throwable t, Class<?> promonExc) {
if (t == null) {
return false;
@@ -2398,122 +1283,6 @@ public final class TngRootBypassHook {
return null;
}
/** SecurityGuard探测命令 stub10101 init + 104xx/105xx sign/verify 走真实 native。 */
private static void hookJnicLibrary(XC_LoadPackage.LoadPackageParam lpparam) {
try {
Class<?> clazz = XposedHelpers.findClass(
"com.hzchengdun.securityguard.adapter.JNICLibrary",
lpparam.classLoader);
XposedHelpers.findAndHookMethod(
clazz,
"doCommand",
int.class,
Object[].class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
int cmd = (Integer) param.args[0];
if (shouldStubJnicCmd(cmd)) {
Object[] payload = (Object[]) param.args[1];
Object stub = safeJnicReturn(cmd, payload);
XposedBridge.log(TAG + " stub JNICLibrary.doCommand cmd=" + cmd
+ " -> " + describeJnicResult(stub));
param.setResult(stub);
return;
}
if (shouldLogJnicCmd(cmd)) {
XposedBridge.log(TAG + " JNIC passthrough call cmd=" + cmd
+ " args=" + describeJnicArgs((Object[]) param.args[1]));
}
}
@Override
protected void afterHookedMethod(MethodHookParam param) {
int cmd = (Integer) param.args[0];
if (shouldStubJnicCmd(cmd)) {
return;
}
if (!shouldLogJnicCmd(cmd)) {
return;
}
if (param.hasThrowable()) {
logJnicThrowable(cmd, param.getThrowable());
return;
}
XposedBridge.log(TAG + " JNIC passthrough cmd=" + cmd
+ " -> " + describeJnicResult(param.getResult()));
}
});
XposedBridge.log(TAG + " hooked JNICLibrary.doCommand (probe stub + init/verify passthrough)");
} catch (Throwable t) {
XposedBridge.log(TAG + " JNICLibrary hook failed: " + t.getMessage());
}
}
/** 仅 stub 低号 env/root 探测10101 init 与 104xx/105xx 必须 passthrough。 */
private static boolean shouldStubJnicCmd(int cmd) {
if (cmd == 10101 || cmd == 10102 || cmd == 10103 || cmd == 10104) {
return false;
}
if (cmd >= 10000) {
return false;
}
return true;
}
private static boolean shouldLogJnicCmd(int cmd) {
return cmd == 10101 || cmd == 10102 || cmd == 10103 || cmd == 10104
|| cmd == 10401 || cmd == 10501 || cmd == 10603
|| (cmd >= 10400 && cmd < 10700);
}
private static void logJnicThrowable(int cmd, Throwable t) {
StringBuilder sb = new StringBuilder();
sb.append(TAG).append(" JNIC passthrough cmd=").append(cmd)
.append(" err ").append(t.getClass().getName());
String msg = t.getMessage();
if (msg != null && !msg.isEmpty()) {
sb.append(" msg=").append(msg);
}
try {
Object code = XposedHelpers.callMethod(t, "getErrorCode");
if (code != null) {
sb.append(" errorCode=").append(code);
}
} catch (Throwable ignored) {
}
StackTraceElement[] stack = t.getStackTrace();
if (stack != null && stack.length > 0) {
sb.append(" at ").append(stack[0]);
}
XposedBridge.log(sb.toString());
}
private static String describeJnicArgs(Object[] args) {
if (args == null) {
return "null";
}
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < args.length; i++) {
if (i > 0) {
sb.append(", ");
}
Object arg = args[i];
if (arg == null) {
sb.append("null");
} else if (arg instanceof byte[]) {
sb.append("byte[").append(((byte[]) arg).length).append("]");
} else if (arg instanceof String) {
String s = (String) arg;
sb.append("String(").append(s.length() > 40 ? s.substring(0, 40) + "..." : s).append(")");
} else {
sb.append(arg.getClass().getSimpleName()).append("=").append(arg);
}
}
sb.append("]");
return sb.toString();
}
/**
* Aliyun TigerTally与 Promon 并行的设备指纹/风控 SDKlibtiger_tally.so
*
@@ -2638,27 +1407,6 @@ public final class TngRootBypassHook {
}
}
private static Object safeJnicReturn(int cmd, Object[] args) {
if (args != null) {
for (Object arg : args) {
if (arg instanceof byte[]) {
return new byte[0];
}
}
}
return Integer.valueOf(0);
}
private static String describeJnicResult(Object result) {
if (result == null) {
return "null";
}
if (result instanceof byte[]) {
return "byte[" + ((byte[]) result).length + "]";
}
return result.getClass().getSimpleName() + "=" + result;
}
/** 拦截 ActivityThread / Handler 触发的应用退出Promon 常走 native→H.exit。 */
private static void hookActivityThreadExit(XC_LoadPackage.LoadPackageParam lpparam) {
hookActivityThreadExitMethods(null);
@@ -2826,93 +1574,6 @@ public final class TngRootBypassHook {
}
}
/** 拦截把 UnhandledEvent 导航成杀进程的入口。 */
private static void hookShowSecurityScreenForState(XC_LoadPackage.LoadPackageParam lpparam) {
try {
Class<?> clazz = XposedHelpers.findClass(
"my.com.tngdigital.common.security.shielding.AppSecurityManager",
lpparam.classLoader);
Class<?> unhandled = null;
try {
unhandled = XposedHelpers.findClass(
"my.com.tngdigital.common.security.model.UnhandledEvent",
lpparam.classLoader);
} catch (Throwable ignored) {
// optional
}
final Class<?> unhandledFinal = unhandled;
int hooked = 0;
for (Method method : clazz.getDeclaredMethods()) {
if (!"showSecurityScreenForState".equals(method.getName())) {
continue;
}
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
Object state = param.args != null && param.args.length > 1
? param.args[1] : null;
if (state != null && unhandledFinal != null) {
try {
Object eventInfo = XposedHelpers.callMethod(state, "getEventInfo");
if (unhandledFinal.isInstance(eventInfo)) {
XposedBridge.log(TAG + " blocked showSecurityScreenForState UnhandledEvent");
param.setResult(null);
return;
}
} catch (Throwable ignored) {
// fall through to blanket block
}
}
XposedBridge.log(TAG + " blocked showSecurityScreenForState");
param.setResult(null);
}
});
hooked++;
}
if (hooked > 0) {
XposedBridge.log(TAG + " hooked " + hooked + " showSecurityScreenForState");
}
} catch (Throwable t) {
XposedBridge.log(TAG + " showSecurityScreenForState hook failed: " + t.getMessage());
}
}
private static void hookForceExitFlow(XC_LoadPackage.LoadPackageParam lpparam) {
String[] classes = {
"my.com.tngdigital.common.security.ui.SecurityErrorBaseActivity",
"my.com.tngdigital.common.security.ui.SecurityErrorActivity",
"my.com.tngdigital.common.security.SecurityForceExitCountdownPolicyKt",
"my.com.tngdigital.common.security.shielding.AppSecurityManager",
};
for (String className : classes) {
try {
Class<?> clazz = XposedHelpers.findClass(className, lpparam.classLoader);
int hooked = 0;
for (Method method : clazz.getDeclaredMethods()) {
String name = method.getName();
String lower = name.toLowerCase(Locale.US);
if (!lower.contains("forceexit")
&& !lower.contains("exitcountdown")) {
continue;
}
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log(TAG + " blocked " + className + "#" + name);
setSafeHookResult(param, method);
}
});
hooked++;
}
if (hooked > 0) {
XposedBridge.log(TAG + " hooked " + hooked + " force-exit/queue method(s) in " + className);
}
} catch (Throwable t) {
XposedBridge.log(TAG + " skip " + className + ": " + t.getMessage());
}
}
}
/** 消费 Promon Java 层安全回调,避免检测后走 SecurityError / native fallback 退出链。 */
private static void hookAppSecurityCallbacks(XC_LoadPackage.LoadPackageParam lpparam) {
try {
@@ -2936,62 +1597,6 @@ public final class TngRootBypassHook {
}
}
/** Promon native 桥接类:强制 int/boolean 检测返回安全值。 */
private static void hookPromonNativeBridge(XC_LoadPackage.LoadPackageParam lpparam) {
String[] simpleNames = {
"F", "bg", "b", "c", "d", "h", "k", "l", "m", "o", "s", "t", "z",
};
int total = 0;
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(Class<?> clazz) {
int count = 0;
try {
for (Method method : clazz.getDeclaredMethods()) {
Class<?> returnType = method.getReturnType();
if (returnType != boolean.class && returnType != Boolean.class
&& returnType != int.class && returnType != Integer.class) {
continue;
}
if (method.getParameterTypes().length > 6) {
continue;
}
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
int depth = PROMON_BRIDGE_DEPTH.get();
if (depth > 0) {
return;
}
PROMON_BRIDGE_DEPTH.set(depth + 1);
try {
if (returnType == boolean.class || returnType == Boolean.class) {
param.setResult(false);
} else {
param.setResult(0);
}
} finally {
PROMON_BRIDGE_DEPTH.set(depth);
}
}
});
count++;
}
} catch (Throwable ignored) {
// class may be absent in this process
}
return count;
}
private static void hookSecurityUrlOpeners(XC_LoadPackage.LoadPackageParam lpparam) {
String[] classes = {
"my.com.tngdigital.common.security.shielding.AppSecurityManager",
@@ -3051,17 +1656,6 @@ public final class TngRootBypassHook {
}
}
private static void hookMethodNoop(Method method, String label) {
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log(TAG + " blocked " + label);
setSafeHookResult(param, method);
}
});
XposedBridge.log(TAG + " hooked " + label);
}
/** 高频回调(如 handleTapjackingCallback禁止逐次打 log避免复进主线程 ANR。 */
private static void hookMethodNoopSilent(Method method) {
XposedBridge.hookMethod(method, new XC_MethodHook() {
@@ -3491,12 +2085,6 @@ public final class TngRootBypassHook {
"isShowErrorScreen");
}
private static void hookSecurityBooleanChecks(XC_LoadPackage.LoadPackageParam lpparam) {
for (String className : BOOLEAN_HOOK_CLASSES) {
RootBypassHelper.hookSecurityClass(lpparam, className);
}
}
private static void hookSecurityErrorActivity(XC_LoadPackage.LoadPackageParam lpparam) {
hookSecurityErrorLaunch(lpparam);
hookGenericSecurityErrorFinish();