Deoptimize methods in framework to make their callees hookable
This commit is contained in:
parent
37bf64a1ee
commit
441106915f
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
* <p>
|
||||
* 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<String, String[][]> 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<String, String[][]> getAll() {
|
||||
return CALLERS;
|
||||
}
|
||||
|
||||
public static String[][] get(String where) {
|
||||
return CALLERS.get(where);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<XCallback> {
|
|||
|
||||
/** @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");
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue