Fix potential bootloop on OnePlus devices when using black/white list mode

This commit is contained in:
solohsu 2019-03-02 16:37:23 +08:00
parent ff595542c4
commit bb4fcffe20
9 changed files with 81 additions and 48 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -30,6 +30,7 @@ public class PrebuiltMethodsDeopter {
}
public static void deoptBootMethods() {
// todo check if has been done before
deoptMethods(KEY_BOOT_IMAGE, null);
}

View File

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

View File

@ -7,7 +7,7 @@
#include <jni.h>
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();

View File

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

View File

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