Deoptimize methods in framework to make their callees hookable

This commit is contained in:
solohsu 2019-02-28 21:39:30 +08:00
parent 37bf64a1ee
commit 441106915f
7 changed files with 124 additions and 1 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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