Deoptimize boot image on runtime start.

All framework methods should be hook-available now
This commit is contained in:
solohsu 2019-02-24 19:01:36 +08:00
parent 7c08e34821
commit a80eae7190
5 changed files with 74 additions and 46 deletions

View File

@ -1,36 +0,0 @@
package com.elderdrivers.riru.xposed.util;
import android.app.Application;
import android.content.Context;
import android.content.ContextWrapper;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
public class MethodHookUtils {
/**
* FIXME
* Some methods, for instance Application#attach(Context) in framework would be inlined,
* which makes our hooking of them not working.
* Actually we can append --debuggable option to dexoat args to avoid inlining,
* but it has significant impact on app's performance.
* So here is just a temporary workaround.
*/
public static Member preCheck(Member target) {
try {
if (target instanceof Method) {
Method method = (Method) target;
if (method.getDeclaringClass().equals(Application.class)
&& method.getName().equals("attach")) {
Utils.logW("replacing Application#attch to ContextWrapper#attachBaseContext, this is error-prone!");
return ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
}
}
} catch (Throwable throwable) {
Utils.logE("error when preCheck " + target, throwable);
}
return target;
}
}

View File

@ -6,7 +6,6 @@ import android.util.Log;
import com.elderdrivers.riru.xposed.core.HookMain;
import com.elderdrivers.riru.xposed.dexmaker.DynamicBridge;
import com.elderdrivers.riru.xposed.dexmaker.MethodInfo;
import com.elderdrivers.riru.xposed.util.MethodHookUtils;
import java.io.File;
import java.io.IOException;
@ -155,7 +154,6 @@ public final class XposedBridge {
* @see #hookAllConstructors
*/
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
hookMethod = MethodHookUtils.preCheck(hookMethod);
if (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor<?>)) {
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
} else if (hookMethod.getDeclaringClass().isInterface()) {
@ -402,7 +400,6 @@ public final class XposedBridge {
private synchronized static void hookMethodNative(final Member method, Class<?> declaringClass,
int slot, final Object additionalInfoObj) {
DynamicBridge.hookMethod(method, (AdditionalHookInfo) additionalInfoObj);
}
private static Object invokeOriginalMethodNative(Member method, int methodId,

View File

@ -68,7 +68,7 @@ void loadDexAndInit(JNIEnv *env, const char *dexPath) {
if (isInited) {
return;
}
install_inline_hooks();
// install_inline_hooks();
jclass clzClassLoader = env->FindClass("java/lang/ClassLoader");
LOGD("java/lang/ClassLoader: %p", clzClassLoader);
jmethodID mdgetSystemClassLoader = env->GetStaticMethodID(clzClassLoader,

View File

@ -11,6 +11,7 @@
#include <utility>
#include <string>
#include <inject/framework_hook.h>
#include <native_hook/native_hook.h>
#include "include/logging.h"
#include "include/misc.h"
@ -42,7 +43,8 @@ __attribute__((visibility("default"))) int nativeForkAndSpecializePost(JNIEnv *e
}
__attribute__((visibility("default"))) void onModuleLoaded() {
LOGI("onModuleLoaded: welcome to EdXposed!");
install_inline_hooks();
}
__attribute__((visibility("default")))

View File

@ -2,14 +2,41 @@
#include <dlfcn.h>
#include <include/android_build.h>
#include <string>
#include <vector>
#include "include/logging.h"
#include "native_hook.h"
static bool inlineHooksInstalled = false;
static const char *(*getDesc)(void *, std::string *);
static bool (*isInSamePackageBackup)(void *, void *) = nullptr;
void *runtime_ = nullptr;
void (*deoptBootImage)(void *runtime) = nullptr;
bool (*runtimeInitBackup)(void *runtime, void *mapAddr) = nullptr;
bool my_runtimeInit(void *runtime, void *mapAddr) {
if (!runtimeInitBackup) {
LOGE("runtimeInitBackup is null");
return false;
}
LOGI("runtimeInit starts");
bool result = (*runtimeInitBackup)(runtime, mapAddr);
if (!deoptBootImage) {
LOGE("deoptBootImageSym is null, skip deoptBootImage");
} else {
LOGI("deoptBootImage starts");
(*deoptBootImage)(runtime);
LOGI("deoptBootImage finishes");
}
LOGI("runtimeInit finishes");
return result;
}
static bool onIsInSamePackageCalled(void *thiz, void *that) {
std::string storage1, storage2;
const char *thisDesc = (*getDesc)(thiz, &storage1);
@ -70,8 +97,8 @@ static bool disable_HiddenAPIPolicyImpl(int api_level, void *artHandle,
return symbol != nullptr;
}
static void hook_IsInSamePackage(int api_level, void *artHandle,
void (*hookFun)(void *, void *, void **)) {
static void hookIsInSamePackage(int api_level, void *artHandle,
void (*hookFun)(void *, void *, void **)) {
// 5.0 - 7.1
const char *isInSamePackageSym = "_ZN3art6mirror5Class15IsInSamePackageEPS1_";
const char *getDescriptorSym = "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE";
@ -94,11 +121,45 @@ static void hook_IsInSamePackage(int api_level, void *artHandle,
reinterpret_cast<void **>(&isInSamePackageBackup));
}
void hookRuntime(int api_level, void *artHandle, void (*hookFun)(void *, void *, void **)) {
void *runtimeInitSym = nullptr;
if (api_level >= ANDROID_O) {
// only oreo has deoptBootImageSym in Runtime
runtime_ = dlsym(artHandle, "_ZN3art7Runtime9instance_E");
if (!runtime_) { LOGW("runtime instance not found"); }
runtimeInitSym = dlsym(artHandle, "_ZN3art7Runtime4InitEONS_18RuntimeArgumentMapE");
if (!runtimeInitSym) {
LOGE("can't find runtimeInitSym: %s", dlerror());
return;
}
deoptBootImage = reinterpret_cast<void (*)(void *)>(dlsym(artHandle,
"_ZN3art7Runtime19DeoptimizeBootImageEv"));
if (!deoptBootImage) {
LOGE("can't find deoptBootImageSym: %s", dlerror());
return;
}
LOGI("start to hook runtimeInitSym");
(*hookFun)(runtimeInitSym, reinterpret_cast<void *>(my_runtimeInit),
reinterpret_cast<void **>(&runtimeInitBackup));
LOGI("runtimeInitSym hooked");
} else {
// TODO support deoptBootImage for Android 7.1 and before?
LOGI("hooking Runtime skipped");
}
}
void install_inline_hooks() {
int api_level = GetAndroidApiLevel();
if (api_level < ANDROID_LOLLIPOP) {
if (inlineHooksInstalled) {
LOGI("inline hooks installed, skip");
return;
}
LOGI("start to install inline hooks");
int api_level = GetAndroidApiLevel();
if (api_level < ANDROID_LOLLIPOP) {
LOGE("api level not supported: %d, skip", api_level);
return;
}
LOGI("using api level %d", api_level);
void *whaleHandle = dlopen(kLibWhalePath, RTLD_LAZY | RTLD_GLOBAL);
if (!whaleHandle) {
LOGE("can't open libwhale: %s", dlerror());
@ -116,11 +177,15 @@ void install_inline_hooks() {
LOGE("can't open libart: %s", dlerror());
return;
}
hook_IsInSamePackage(api_level, artHandle, hookFun);
hookIsInSamePackage(api_level, artHandle, hookFun);
hookRuntime(api_level, artHandle, hookFun);
if (disable_HiddenAPIPolicyImpl(api_level, artHandle, hookFun)) {
LOGI("disable_HiddenAPIPolicyImpl done.");
} else {
LOGE("disable_HiddenAPIPolicyImpl failed.");
}
dlclose(whaleHandle);
dlclose(artHandle);
LOGI("install inline hooks done");
}