From a80eae7190d85515dbab3694b8ba8fe4ba638059 Mon Sep 17 00:00:00 2001 From: solohsu Date: Sun, 24 Feb 2019 19:01:36 +0800 Subject: [PATCH] Deoptimize boot image on runtime start. All framework methods should be hook-available now --- .../riru/xposed/util/MethodHookUtils.java | 36 --------- .../de/robv/android/xposed/XposedBridge.java | 3 - Core/jni/main/java_hook/java_hook.cpp | 2 +- Core/jni/main/main.cpp | 4 +- Core/jni/main/native_hook/native_hook.cpp | 75 +++++++++++++++++-- 5 files changed, 74 insertions(+), 46 deletions(-) delete mode 100644 Bridge/src/main/java/com/elderdrivers/riru/xposed/util/MethodHookUtils.java diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/MethodHookUtils.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/MethodHookUtils.java deleted file mode 100644 index 3183b696..00000000 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/MethodHookUtils.java +++ /dev/null @@ -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; - } - -} diff --git a/Bridge/src/main/java/de/robv/android/xposed/XposedBridge.java b/Bridge/src/main/java/de/robv/android/xposed/XposedBridge.java index 0ea04085..851d12ab 100644 --- a/Bridge/src/main/java/de/robv/android/xposed/XposedBridge.java +++ b/Bridge/src/main/java/de/robv/android/xposed/XposedBridge.java @@ -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, diff --git a/Core/jni/main/java_hook/java_hook.cpp b/Core/jni/main/java_hook/java_hook.cpp index 65c24299..043dcd03 100644 --- a/Core/jni/main/java_hook/java_hook.cpp +++ b/Core/jni/main/java_hook/java_hook.cpp @@ -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, diff --git a/Core/jni/main/main.cpp b/Core/jni/main/main.cpp index 9f6bcb82..58337bfc 100644 --- a/Core/jni/main/main.cpp +++ b/Core/jni/main/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #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"))) diff --git a/Core/jni/main/native_hook/native_hook.cpp b/Core/jni/main/native_hook/native_hook.cpp index bc988c49..71cb767d 100644 --- a/Core/jni/main/native_hook/native_hook.cpp +++ b/Core/jni/main/native_hook/native_hook.cpp @@ -2,14 +2,41 @@ #include #include #include +#include #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(&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(dlsym(artHandle, + "_ZN3art7Runtime19DeoptimizeBootImageEv")); + if (!deoptBootImage) { + LOGE("can't find deoptBootImageSym: %s", dlerror()); + return; + } + LOGI("start to hook runtimeInitSym"); + (*hookFun)(runtimeInitSym, reinterpret_cast(my_runtimeInit), + reinterpret_cast(&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"); }