From bb4fcffe200d95d681d1ea987317442d89c35164 Mon Sep 17 00:00:00 2001 From: solohsu Date: Sat, 2 Mar 2019 16:37:23 +0800 Subject: [PATCH] Fix potential bootloop on OnePlus devices when using black/white list mode --- .../com/elderdrivers/riru/xposed/Main.java | 2 + .../riru/xposed/entry/Router.java | 8 ++ .../entry/bootstrap/WorkAroundHookInfo.java | 10 ++ .../proxy/yahfa/BlackWhiteListProxy.java | 93 +++++++++++-------- .../xposed/util/PrebuiltMethodsDeopter.java | 1 + Core/jni/main/inject/config_manager.cpp | 2 +- Core/jni/main/inject/config_manager.h | 2 +- Core/jni/main/inject/framework_hook.cpp | 6 -- Core/jni/main/java_hook/java_hook.cpp | 5 +- 9 files changed, 81 insertions(+), 48 deletions(-) create mode 100644 Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/bootstrap/WorkAroundHookInfo.java diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java index b90b4c2d..2674b3d0 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java @@ -116,6 +116,8 @@ public class Main implements KeepAll { public static native boolean isDynamicModulesEnabled(); + public static native boolean isAppNeedHook(String appDataDir); + // prevent from fatal error caused by holding not whitelisted file descriptors when forking zygote // https://github.com/rovo89/Xposed/commit/b3ba245ad04cd485699fb1d2ebde7117e58214ff public static native void closeFilesBeforeForkNative(); diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/Router.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/Router.java index 5aa1d10c..9dc758b4 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/Router.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/Router.java @@ -7,6 +7,7 @@ import com.elderdrivers.riru.xposed.dexmaker.DynamicBridge; import com.elderdrivers.riru.xposed.entry.bootstrap.AppBootstrapHookInfo; import com.elderdrivers.riru.xposed.entry.bootstrap.SysBootstrapHookInfo; import com.elderdrivers.riru.xposed.entry.bootstrap.SysInnerHookInfo; +import com.elderdrivers.riru.xposed.entry.bootstrap.WorkAroundHookInfo; import com.elderdrivers.riru.xposed.entry.hooker.SystemMainHooker; import com.elderdrivers.riru.xposed.util.Utils; @@ -81,6 +82,13 @@ public class Router { SysInnerHookInfo.class.getName()); } + public static void startWorkAroundHook() { + HookMain.doHookDefault( + Router.class.getClassLoader(), + XposedBridge.BOOTCLASSLOADER, + WorkAroundHookInfo.class.getName()); + } + public static void onEnterChildProcess() { forkCompleted = true; DynamicBridge.onForkPost(); diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/bootstrap/WorkAroundHookInfo.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/bootstrap/WorkAroundHookInfo.java new file mode 100644 index 00000000..58ffcb99 --- /dev/null +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/bootstrap/WorkAroundHookInfo.java @@ -0,0 +1,10 @@ +package com.elderdrivers.riru.xposed.entry.bootstrap; + +import com.elderdrivers.riru.common.KeepMembers; +import com.elderdrivers.riru.xposed.entry.hooker.OnePlusWorkAroundHooker; + +public class WorkAroundHookInfo implements KeepMembers { + public static String[] hookItemNames = { + OnePlusWorkAroundHooker.class.getName() + }; +} diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java index 63a73723..8695709f 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java @@ -5,6 +5,7 @@ import com.elderdrivers.riru.xposed.config.ConfigManager; import com.elderdrivers.riru.xposed.entry.Router; import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter; +import static com.elderdrivers.riru.xposed.Main.isAppNeedHook; import static com.elderdrivers.riru.xposed.util.FileUtils.getDataPathPrefix; public class BlackWhiteListProxy { @@ -14,63 +15,77 @@ public class BlackWhiteListProxy { String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) { - // always enter here, make sure secondary zygote's modules is loaded only once - // when isDynamicModulesMode is not on final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); - // call this to ensure the flag is set to false ASAP - Router.prepare(false); - PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote - if (!isDynamicModulesMode) { - Router.loadModulesSafely(); - Main.closeFilesBeforeForkNative(); + if (isDynamicModulesMode) { + // should never happen + return; } + // only enter here when isDynamicModulesMode is off + onForkPreForNonDynamicMode(false); } public static void forkAndSpecializePost(int pid, String appDataDir) { - // when this process is in white list or not in black list - // installBootstrapHooks -> loadModules if needed - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - if (!isDynamicModulesMode) { - Main.reopenFilesAfterForkNative(); - } - Main.appDataDir = appDataDir; - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); - Router.onEnterChildProcess(); - Router.installBootstrapHooks(false); - Router.loadModulesSafely(); + onForkPostCommon(false, appDataDir); } public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { - // we always enter here whether black/white list is on or not final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); - PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote - // set startsSystemServer flag used when loadModules - Router.prepare(true); - // we never install bootstrap hooks here in black/white list mode - // because installed hooks would be propagated to all child processes of main zygote - // hence we cannot install hooks for processes like com.android.phone process who are - // not from forkAndSpecialize as a side effect - if (!isDynamicModulesMode) { - Router.loadModulesSafely(); - Main.closeFilesBeforeForkNative(); + if (isDynamicModulesMode) { + // should never happen + return; } + // only enter here when isDynamicModulesMode is off + onForkPreForNonDynamicMode(true); } public static void forkSystemServerPost(int pid) { - // should only here when system_server is in white list or not in black list - // installBootstrapHooks -> loadModules if needed + onForkPostCommon(true, getDataPathPrefix() + "android"); + } + + /** + * Some details are different between main zygote and secondary zygote. + */ + private static void onForkPreForNonDynamicMode(boolean isSystemServer) { + ConfigManager.setDynamicModulesMode(false); + // deoptBootMethods once for all child processes of zygote + PrebuiltMethodsDeopter.deoptBootMethods(); + // set startsSystemServer flag used when loadModules + Router.prepare(isSystemServer); + // we never install bootstrap hooks here in black/white list mode except workaround hooks + // because installed hooks would be propagated to all child processes of zygote + Router.startWorkAroundHook(); + // loadModules once for all child processes of zygote + Router.loadModulesSafely(); + // at last close all fds + Main.closeFilesBeforeForkNative(); + } + + private static void onForkPostCommon(boolean isSystemServer, String appDataDir) { final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); + // set common flags + ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + Main.appDataDir = appDataDir; + Router.onEnterChildProcess(); + if (!isDynamicModulesMode) { + // initial stuffs have been done in forkSystemServerPre Main.reopenFilesAfterForkNative(); } - Main.appDataDir = getDataPathPrefix() + "android"; - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); - Router.onEnterChildProcess(); - Router.installBootstrapHooks(true); - Router.loadModulesSafely(); + + if (!isAppNeedHook(Main.appDataDir)) { + // if is blacklisted, just stop here + return; + } + + if (isDynamicModulesMode) { + // nothing has been done in forkSystemServerPre, we have to do the same here + // except some workarounds specific for forkSystemServerPre + PrebuiltMethodsDeopter.deoptBootMethods(); + Router.prepare(isSystemServer); + Router.loadModulesSafely(); + } + Router.installBootstrapHooks(isSystemServer); } } diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/PrebuiltMethodsDeopter.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/PrebuiltMethodsDeopter.java index 24c00581..7ef0fff6 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/PrebuiltMethodsDeopter.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/PrebuiltMethodsDeopter.java @@ -30,6 +30,7 @@ public class PrebuiltMethodsDeopter { } public static void deoptBootMethods() { + // todo check if has been done before deoptMethods(KEY_BOOT_IMAGE, null); } diff --git a/Core/jni/main/inject/config_manager.cpp b/Core/jni/main/inject/config_manager.cpp index 5af64060..af4476d7 100644 --- a/Core/jni/main/inject/config_manager.cpp +++ b/Core/jni/main/inject/config_manager.cpp @@ -87,7 +87,7 @@ static void init_once() { static char package_name[256]; -bool is_app_need_hook(JNIEnv *env, jstring appDataDir) { +bool is_app_need_hook(JNIEnv *env, jclass, jstring appDataDir) { init_once(); if (!black_white_list_enabled) { return true; diff --git a/Core/jni/main/inject/config_manager.h b/Core/jni/main/inject/config_manager.h index 8fb59259..a5349b3c 100644 --- a/Core/jni/main/inject/config_manager.h +++ b/Core/jni/main/inject/config_manager.h @@ -7,7 +7,7 @@ #include -bool is_app_need_hook(JNIEnv *env, jstring appDataDir); +bool is_app_need_hook(JNIEnv *env, jclass, jstring appDataDir); bool is_black_white_list_enabled(); diff --git a/Core/jni/main/inject/framework_hook.cpp b/Core/jni/main/inject/framework_hook.cpp index 4309ac98..bd6392d9 100644 --- a/Core/jni/main/inject/framework_hook.cpp +++ b/Core/jni/main/inject/framework_hook.cpp @@ -57,9 +57,6 @@ void onNativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t uid, gid_t gid int onNativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { if (res == 0) { - if (!is_app_need_hook(env, sAppDataDir)) { - return 0; - } prepareJavaEnv(env); // only do work in child since findAndCall would print log findAndCall(env, "forkSystemServerPost", "(I)V", res); @@ -100,9 +97,6 @@ void onNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, int onNativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { if (res == 0) { - if (!is_app_need_hook(env, sAppDataDir)) { - return 0; - } prepareJavaEnv(env); findAndCall(env, "forkAndSpecializePost", "(ILjava/lang/String;)V", res, sAppDataDir); } else { diff --git a/Core/jni/main/java_hook/java_hook.cpp b/Core/jni/main/java_hook/java_hook.cpp index 749b50a1..94747a4e 100644 --- a/Core/jni/main/java_hook/java_hook.cpp +++ b/Core/jni/main/java_hook/java_hook.cpp @@ -79,6 +79,9 @@ static JNINativeMethod hookMethods[] = { { "isDynamicModulesEnabled", "()Z", (void *) is_dynamic_modules_enabled }, + { + "isAppNeedHook", "(Ljava/lang/String;)Z", (void *) is_app_need_hook + }, { "getInstallerPkgName", "()Ljava/lang/String;", (void *) get_installer_pkg_name }, @@ -134,7 +137,7 @@ void loadDexAndInit(JNIEnv *env, const char *dexPath) { jclass entry_class = findClassFromLoader(env, myClassLoader, ENTRY_CLASS_NAME); if (NULL != entry_class) { LOGD("HookEntry Class %p", entry_class); - env->RegisterNatives(entry_class, hookMethods, 12); + env->RegisterNatives(entry_class, hookMethods, 13); isInited = true; LOGD("RegisterNatives succeed for HookEntry."); } else {