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 f975373d..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; @@ -13,8 +14,11 @@ 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); + PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote if (!isDynamicModulesMode) { Router.loadModulesSafely(); Main.closeFilesBeforeForkNative(); @@ -22,6 +26,8 @@ public class BlackWhiteListProxy { } 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(); @@ -36,8 +42,14 @@ public class BlackWhiteListProxy { 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 + // 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(); @@ -45,6 +57,8 @@ public class BlackWhiteListProxy { } 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 462e9db2..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; @@ -17,6 +18,7 @@ public class NormalProxy { 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); @@ -40,6 +42,7 @@ public class NormalProxy { 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 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 CALLERS = new HashMap<>(); + + /** + * format for each row: {className, methodName, methodSig} + */ + private static final String[][] BOOT_IMAGE = { + // callers of Application#attach(Context) + {"android.app.Instrumentation", "newApplication", "(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Context;)Landroid/app/Application;"} + }; + + private static final String[][] SYSTEM_SERVER = {}; + + private static final String[][] SYSTEM_UI = {}; + + static { + CALLERS.put(KEY_BOOT_IMAGE, BOOT_IMAGE); + CALLERS.put(KEY_SYSTEM_SERVER, SYSTEM_SERVER); + CALLERS.put("com.android.systemui", SYSTEM_UI); + } + + public static HashMap getAll() { + return CALLERS; + } + + public static String[][] get(String where) { + return CALLERS.get(where); + } +} 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 new file mode 100644 index 00000000..24c00581 --- /dev/null +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/PrebuiltMethodsDeopter.java @@ -0,0 +1,39 @@ +package com.elderdrivers.riru.xposed.util; + +import com.elderdrivers.riru.xposed.Main; + +import java.util.Arrays; + +import de.robv.android.xposed.XposedHelpers; + +import static com.elderdrivers.riru.xposed.util.InlinedMethodCallers.KEY_BOOT_IMAGE; +import static com.elderdrivers.riru.xposed.util.InlinedMethodCallers.KEY_SYSTEM_SERVER; + +public class PrebuiltMethodsDeopter { + + public static void deoptMethods(String where, ClassLoader cl) { + String[][] callers = InlinedMethodCallers.get(where); + if (callers == null) { + return; + } + for (String[] caller : callers) { + try { + Object method = Main.findMethodNative( + XposedHelpers.findClass(caller[0], cl), caller[1], caller[2]); + if (method != null) { + Main.deoptMethodNative(method); + } + } catch (Throwable throwable) { + Utils.logE("error when deopting method: " + Arrays.toString(caller), throwable); + } + } + } + + public static void deoptBootMethods() { + deoptMethods(KEY_BOOT_IMAGE, null); + } + + public static void deoptSystemServerMethods(ClassLoader sysCL) { + deoptMethods(KEY_SYSTEM_SERVER, sysCL); + } +} diff --git a/Bridge/src/main/java/de/robv/android/xposed/callbacks/XCallback.java b/Bridge/src/main/java/de/robv/android/xposed/callbacks/XCallback.java index 56930fb4..4de87157 100644 --- a/Bridge/src/main/java/de/robv/android/xposed/callbacks/XCallback.java +++ b/Bridge/src/main/java/de/robv/android/xposed/callbacks/XCallback.java @@ -2,6 +2,9 @@ package de.robv.android.xposed.callbacks; import android.os.Bundle; +import com.elderdrivers.riru.xposed.entry.Router; +import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter; + import java.io.Serializable; import de.robv.android.xposed.XposedBridge; @@ -98,6 +101,14 @@ public abstract class XCallback implements Comparable { /** @hide */ public static void callAll(Param param) { + + if (param instanceof XC_LoadPackage.LoadPackageParam) { + // deopt methods in system apps or priv-apps, this would be not necessary + // only if we found out how to recompile their apks + XC_LoadPackage.LoadPackageParam lpp = (XC_LoadPackage.LoadPackageParam) param; + PrebuiltMethodsDeopter.deoptMethods(lpp.packageName, lpp.classLoader); + } + if (param.callbacks == null) throw new IllegalStateException("This object was not created for use with callAll");