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 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.core.HookMain;
|
||||||
import com.elderdrivers.riru.xposed.entry.bootstrap.AppBootstrapHookInfo;
|
import com.elderdrivers.riru.xposed.entry.bootstrap.AppBootstrapHookInfo;
|
||||||
import com.elderdrivers.riru.xposed.entry.bootstrap.SysBootstrapHookInfo;
|
import com.elderdrivers.riru.xposed.entry.bootstrap.SysBootstrapHookInfo;
|
||||||
import com.elderdrivers.riru.xposed.entry.bootstrap.SysInnerHookInfo;
|
import com.elderdrivers.riru.xposed.entry.bootstrap.SysInnerHookInfo;
|
||||||
import com.elderdrivers.riru.xposed.entry.hooker.SystemMainHooker;
|
import com.elderdrivers.riru.xposed.entry.hooker.SystemMainHooker;
|
||||||
|
import com.elderdrivers.riru.xposed.util.InlinedMethodCallers;
|
||||||
import com.elderdrivers.riru.xposed.util.Utils;
|
import com.elderdrivers.riru.xposed.util.Utils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
import de.robv.android.xposed.XposedInit;
|
import de.robv.android.xposed.XposedInit;
|
||||||
|
|
||||||
public class Router {
|
public class Router {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import android.app.ActivityThread;
|
||||||
|
|
||||||
import com.elderdrivers.riru.common.KeepMembers;
|
import com.elderdrivers.riru.common.KeepMembers;
|
||||||
import com.elderdrivers.riru.xposed.entry.Router;
|
import com.elderdrivers.riru.xposed.entry.Router;
|
||||||
|
import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
|
|
@ -30,6 +31,8 @@ public class SystemMainHooker implements KeepMembers {
|
||||||
try {
|
try {
|
||||||
// get system_server classLoader
|
// get system_server classLoader
|
||||||
systemServerCL = Thread.currentThread().getContextClassLoader();
|
systemServerCL = Thread.currentThread().getContextClassLoader();
|
||||||
|
// deopt methods in SYSTEMSERVERCLASSPATH
|
||||||
|
PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);
|
||||||
Router.startSystemServerHook();
|
Router.startSystemServerHook();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logE("error when hooking systemMain", 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.Main;
|
||||||
import com.elderdrivers.riru.xposed.config.ConfigManager;
|
import com.elderdrivers.riru.xposed.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.xposed.entry.Router;
|
import com.elderdrivers.riru.xposed.entry.Router;
|
||||||
|
import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter;
|
||||||
|
|
||||||
import static com.elderdrivers.riru.xposed.util.FileUtils.getDataPathPrefix;
|
import static com.elderdrivers.riru.xposed.util.FileUtils.getDataPathPrefix;
|
||||||
|
|
||||||
|
|
@ -13,8 +14,11 @@ public class BlackWhiteListProxy {
|
||||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
||||||
boolean startChildZygote, String instructionSet,
|
boolean startChildZygote, String instructionSet,
|
||||||
String appDataDir) {
|
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();
|
final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
|
||||||
ConfigManager.setDynamicModulesMode(isDynamicModulesMode);
|
ConfigManager.setDynamicModulesMode(isDynamicModulesMode);
|
||||||
|
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
||||||
if (!isDynamicModulesMode) {
|
if (!isDynamicModulesMode) {
|
||||||
Router.loadModulesSafely();
|
Router.loadModulesSafely();
|
||||||
Main.closeFilesBeforeForkNative();
|
Main.closeFilesBeforeForkNative();
|
||||||
|
|
@ -22,6 +26,8 @@ public class BlackWhiteListProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forkAndSpecializePost(int pid, String appDataDir) {
|
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();
|
final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
|
||||||
if (!isDynamicModulesMode) {
|
if (!isDynamicModulesMode) {
|
||||||
Main.reopenFilesAfterForkNative();
|
Main.reopenFilesAfterForkNative();
|
||||||
|
|
@ -36,8 +42,14 @@ public class BlackWhiteListProxy {
|
||||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags,
|
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags,
|
||||||
int[][] rlimits, long permittedCapabilities,
|
int[][] rlimits, long permittedCapabilities,
|
||||||
long effectiveCapabilities) {
|
long effectiveCapabilities) {
|
||||||
|
// we always enter here whether black/white list is on or not
|
||||||
final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
|
final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
|
||||||
ConfigManager.setDynamicModulesMode(isDynamicModulesMode);
|
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) {
|
if (!isDynamicModulesMode) {
|
||||||
Router.loadModulesSafely();
|
Router.loadModulesSafely();
|
||||||
Main.closeFilesBeforeForkNative();
|
Main.closeFilesBeforeForkNative();
|
||||||
|
|
@ -45,6 +57,8 @@ public class BlackWhiteListProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forkSystemServerPost(int pid) {
|
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();
|
final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
|
||||||
if (!isDynamicModulesMode) {
|
if (!isDynamicModulesMode) {
|
||||||
Main.reopenFilesAfterForkNative();
|
Main.reopenFilesAfterForkNative();
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.elderdrivers.riru.xposed.Main;
|
||||||
import com.elderdrivers.riru.xposed.config.ConfigManager;
|
import com.elderdrivers.riru.xposed.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.xposed.dexmaker.DynamicBridge;
|
import com.elderdrivers.riru.xposed.dexmaker.DynamicBridge;
|
||||||
import com.elderdrivers.riru.xposed.entry.Router;
|
import com.elderdrivers.riru.xposed.entry.Router;
|
||||||
|
import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter;
|
||||||
|
|
||||||
import static com.elderdrivers.riru.xposed.util.FileUtils.getDataPathPrefix;
|
import static com.elderdrivers.riru.xposed.util.FileUtils.getDataPathPrefix;
|
||||||
|
|
||||||
|
|
@ -17,6 +18,7 @@ public class NormalProxy {
|
||||||
final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
|
final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
|
||||||
Main.appDataDir = appDataDir;
|
Main.appDataDir = appDataDir;
|
||||||
ConfigManager.setDynamicModulesMode(isDynamicModulesMode);
|
ConfigManager.setDynamicModulesMode(isDynamicModulesMode);
|
||||||
|
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
||||||
Router.prepare(false);
|
Router.prepare(false);
|
||||||
// install bootstrap hooks for secondary zygote
|
// install bootstrap hooks for secondary zygote
|
||||||
Router.installBootstrapHooks(false);
|
Router.installBootstrapHooks(false);
|
||||||
|
|
@ -40,6 +42,7 @@ public class NormalProxy {
|
||||||
Main.appDataDir = getDataPathPrefix() + "android";
|
Main.appDataDir = getDataPathPrefix() + "android";
|
||||||
ConfigManager.setDynamicModulesMode(isDynamicModulesMode);
|
ConfigManager.setDynamicModulesMode(isDynamicModulesMode);
|
||||||
Router.prepare(true);
|
Router.prepare(true);
|
||||||
|
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote
|
||||||
// install bootstrap hooks for main zygote as early as possible
|
// install bootstrap hooks for main zygote as early as possible
|
||||||
// in case we miss some processes not forked via forkAndSpecialize
|
// in case we miss some processes not forked via forkAndSpecialize
|
||||||
// for instance com.android.phone
|
// 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 android.os.Bundle;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.xposed.entry.Router;
|
||||||
|
import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
@ -98,6 +101,14 @@ public abstract class XCallback implements Comparable<XCallback> {
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public static void callAll(Param param) {
|
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)
|
if (param.callbacks == null)
|
||||||
throw new IllegalStateException("This object was not created for use with callAll");
|
throw new IllegalStateException("This object was not created for use with callAll");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue