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 e8eeda96..b90b4c2d 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java @@ -34,36 +34,32 @@ public class Main implements KeepAll { int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, - String appDataDir, boolean isBlackWhiteListMode, - boolean isDynamicModulesMode) { + String appDataDir) { if (BuildConfig.DEBUG) { forkAndSpecializePramsStr = String.format( "Zygote#forkAndSpecialize(%d, %d, %s, %d, %s, %d, %s, %s, %s, %s, %s, %s, %s)", uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits), mountExternal, seInfo, niceName, Arrays.toString(fdsToClose), - Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir, - isDynamicModulesMode); + Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir); } - if (isBlackWhiteListMode) { + if (isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, - instructionSet, appDataDir, isDynamicModulesMode); + instructionSet, appDataDir); } else { NormalProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, - appDataDir, isDynamicModulesMode); + appDataDir); } } - public static void forkAndSpecializePost(int pid, String appDataDir, - boolean isBlackWhiteListMode, - boolean isDynamicModulesMode) { + public static void forkAndSpecializePost(int pid, String appDataDir) { if (pid == 0) { Utils.logD(forkAndSpecializePramsStr + " = " + Process.myPid()); - if (isBlackWhiteListMode) { - BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir, isDynamicModulesMode); + if (isBlackWhiteListEnabled()) { + BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir); } else { - NormalProxy.forkAndSpecializePost(pid, appDataDir, isDynamicModulesMode); + NormalProxy.forkAndSpecializePost(pid, appDataDir); } } else { // in zygote process, res is child zygote pid @@ -72,30 +68,28 @@ public class Main implements KeepAll { } public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, - long permittedCapabilities, long effectiveCapabilities, - boolean isBlackWhiteListMode, boolean isDynamicModulesMode) { + long permittedCapabilities, long effectiveCapabilities) { if (BuildConfig.DEBUG) { forkSystemServerPramsStr = String.format("Zygote#forkSystemServer(%d, %d, %s, %d, %s, %d, %d)", uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits), permittedCapabilities, effectiveCapabilities); } - if (isBlackWhiteListMode) { + if (isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits, - permittedCapabilities, effectiveCapabilities, isDynamicModulesMode); + permittedCapabilities, effectiveCapabilities); } else { NormalProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits, - permittedCapabilities, effectiveCapabilities, isDynamicModulesMode); + permittedCapabilities, effectiveCapabilities); } } - public static void forkSystemServerPost(int pid, boolean isBlackWhiteListMode, - boolean isDynamicModulesMode) { + public static void forkSystemServerPost(int pid) { if (pid == 0) { Utils.logD(forkSystemServerPramsStr + " = " + Process.myPid()); - if (isBlackWhiteListMode) { - BlackWhiteListProxy.forkSystemServerPost(pid, isDynamicModulesMode); + if (isBlackWhiteListEnabled()) { + BlackWhiteListProxy.forkSystemServerPost(pid); } else { - NormalProxy.forkSystemServerPost(pid, isDynamicModulesMode); + NormalProxy.forkSystemServerPost(pid); } } else { // in zygote process, res is child zygote pid @@ -118,6 +112,10 @@ public class Main implements KeepAll { public static native String getInstallerPkgName(); + public static native boolean isBlackWhiteListEnabled(); + + public static native boolean isDynamicModulesEnabled(); + // 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(); @@ -125,4 +123,8 @@ public class Main implements KeepAll { public static native void reopenFilesAfterForkNative(); public static native void deoptMethodNative(Object object); + + public static native long suspendAllThreads(); + + public static native void resumeAllThreads(long obj); } diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/core/HookMain.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/core/HookMain.java index 21ad22f1..89413aed 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/core/HookMain.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/core/HookMain.java @@ -1,5 +1,6 @@ package com.elderdrivers.riru.xposed.core; +import com.elderdrivers.riru.xposed.Main; import com.elderdrivers.riru.xposed.entry.hooker.OnePlusWorkAroundHooker; import com.elderdrivers.riru.xposed.util.Utils; @@ -111,8 +112,13 @@ public class HookMain { if (backup != null) { HookMethodResolver.resolveMethod(hook, backup); } - if (!backupAndHookNative(target, hook, backup)) { - throw new RuntimeException("Failed to hook " + target + " with " + hook); + long obj = Main.suspendAllThreads(); + try { + if (!backupAndHookNative(target, hook, backup)) { + throw new RuntimeException("Failed to hook " + target + " with " + hook); + } + } finally { + Main.resumeAllThreads(obj); } } 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 73ef995f..7d358fb5 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 @@ -2,15 +2,19 @@ package com.elderdrivers.riru.xposed.entry; import android.text.TextUtils; -import com.elderdrivers.riru.xposed.config.InstallerChooser; +import com.elderdrivers.riru.xposed.Main; import com.elderdrivers.riru.xposed.core.HookMain; 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.hooker.SystemMainHooker; +import com.elderdrivers.riru.xposed.util.InlinedMethodCallers; import com.elderdrivers.riru.xposed.util.Utils; +import java.util.Arrays; + import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.XposedInit; public class Router { diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/hooker/SystemMainHooker.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/hooker/SystemMainHooker.java index 11f81373..1f51c3d2 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/hooker/SystemMainHooker.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/hooker/SystemMainHooker.java @@ -4,6 +4,7 @@ import android.app.ActivityThread; import com.elderdrivers.riru.common.KeepMembers; import com.elderdrivers.riru.xposed.entry.Router; +import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter; import de.robv.android.xposed.XposedBridge; @@ -30,6 +31,8 @@ public class SystemMainHooker implements KeepMembers { try { // get system_server classLoader systemServerCL = Thread.currentThread().getContextClassLoader(); + // deopt methods in SYSTEMSERVERCLASSPATH + PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL); Router.startSystemServerHook(); } catch (Throwable t) { logE("error when hooking systemMain", t); 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 b246c648..d361b38a 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 @@ -3,6 +3,7 @@ package com.elderdrivers.riru.xposed.proxy.yahfa; import com.elderdrivers.riru.xposed.Main; 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.util.FileUtils.getDataPathPrefix; @@ -12,16 +13,22 @@ public class BlackWhiteListProxy { int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, - String appDataDir, boolean isDynamicModulesMode) { + 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); + PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote if (!isDynamicModulesMode) { Router.loadModulesSafely(); Main.closeFilesBeforeForkNative(); } } - public static void forkAndSpecializePost(int pid, String appDataDir, - boolean isDynamicModulesMode) { + 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(); } @@ -34,16 +41,25 @@ public class BlackWhiteListProxy { public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, long permittedCapabilities, - long effectiveCapabilities, - boolean isDynamicModulesMode) { + 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 + // 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(); } } - public static void forkSystemServerPost(int pid, boolean isDynamicModulesMode) { + 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 + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); if (!isDynamicModulesMode) { Main.reopenFilesAfterForkNative(); } diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java index e967d633..20988054 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java @@ -4,6 +4,7 @@ import com.elderdrivers.riru.xposed.Main; import com.elderdrivers.riru.xposed.config.ConfigManager; import com.elderdrivers.riru.xposed.dexmaker.DynamicBridge; import com.elderdrivers.riru.xposed.entry.Router; +import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter; import static com.elderdrivers.riru.xposed.util.FileUtils.getDataPathPrefix; @@ -13,9 +14,11 @@ public class NormalProxy { int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, - String appDataDir, boolean isDynamicModulesMode) { + String appDataDir) { + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); Main.appDataDir = appDataDir; ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote Router.prepare(false); // install bootstrap hooks for secondary zygote Router.installBootstrapHooks(false); @@ -24,7 +27,7 @@ public class NormalProxy { Main.closeFilesBeforeForkNative(); } - public static void forkAndSpecializePost(int pid, String appDataDir, boolean isDynamicModulesMode) { + public static void forkAndSpecializePost(int pid, String appDataDir) { // TODO consider processes without forkAndSpecializePost called Main.reopenFilesAfterForkNative(); Router.onEnterChildProcess(); @@ -34,11 +37,12 @@ public class NormalProxy { } public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, - long permittedCapabilities, long effectiveCapabilities, - boolean isDynamicModulesMode) { + long permittedCapabilities, long effectiveCapabilities) { + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); Main.appDataDir = getDataPathPrefix() + "android"; ConfigManager.setDynamicModulesMode(isDynamicModulesMode); Router.prepare(true); + PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote // install bootstrap hooks for main zygote as early as possible // in case we miss some processes not forked via forkAndSpecialize // for instance com.android.phone @@ -50,7 +54,7 @@ public class NormalProxy { Main.closeFilesBeforeForkNative(); } - public static void forkSystemServerPost(int pid, boolean isDynamicModulesMode) { + public static void forkSystemServerPost(int pid) { // in system_server process Main.reopenFilesAfterForkNative(); Router.onEnterChildProcess(); diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/InlinedMethodCallers.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/InlinedMethodCallers.java new file mode 100644 index 00000000..eb8a29cc --- /dev/null +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/InlinedMethodCallers.java @@ -0,0 +1,49 @@ +package com.elderdrivers.riru.xposed.util; + +import java.util.HashMap; + +/** + * Providing a whitelist of methods which are the callers of the target methods we want to hook. + * Because the target methods are inlined into the callers, we deoptimize the callers to + * run in intercept mode to make target methods hookable. + *
+ * Only for methods which are included in pre-compiled framework codes.
+ * TODO recompile system apps and priv-apps since their original dex files are available
+ */
+public class InlinedMethodCallers {
+
+ public static final String KEY_BOOT_IMAGE = "boot_image";
+ public static final String KEY_SYSTEM_SERVER = "system_server";
+
+ /**
+ * Key should be {@link #KEY_BOOT_IMAGE}, {@link #KEY_SYSTEM_SERVER}, or a package name
+ * of system apps or priv-apps i.e. com.android.systemui
+ */
+ private static final HashMap