Deoptimize boot image on runtime start.
All framework methods should be hook-available now
This commit is contained in:
parent
7c08e34821
commit
a80eae7190
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,6 @@ import android.util.Log;
|
||||||
import com.elderdrivers.riru.xposed.core.HookMain;
|
import com.elderdrivers.riru.xposed.core.HookMain;
|
||||||
import com.elderdrivers.riru.xposed.dexmaker.DynamicBridge;
|
import com.elderdrivers.riru.xposed.dexmaker.DynamicBridge;
|
||||||
import com.elderdrivers.riru.xposed.dexmaker.MethodInfo;
|
import com.elderdrivers.riru.xposed.dexmaker.MethodInfo;
|
||||||
import com.elderdrivers.riru.xposed.util.MethodHookUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -155,7 +154,6 @@ public final class XposedBridge {
|
||||||
* @see #hookAllConstructors
|
* @see #hookAllConstructors
|
||||||
*/
|
*/
|
||||||
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
|
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
|
||||||
hookMethod = MethodHookUtils.preCheck(hookMethod);
|
|
||||||
if (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor<?>)) {
|
if (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor<?>)) {
|
||||||
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
|
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
|
||||||
} else if (hookMethod.getDeclaringClass().isInterface()) {
|
} else if (hookMethod.getDeclaringClass().isInterface()) {
|
||||||
|
|
@ -402,7 +400,6 @@ public final class XposedBridge {
|
||||||
private synchronized static void hookMethodNative(final Member method, Class<?> declaringClass,
|
private synchronized static void hookMethodNative(final Member method, Class<?> declaringClass,
|
||||||
int slot, final Object additionalInfoObj) {
|
int slot, final Object additionalInfoObj) {
|
||||||
DynamicBridge.hookMethod(method, (AdditionalHookInfo) additionalInfoObj);
|
DynamicBridge.hookMethod(method, (AdditionalHookInfo) additionalInfoObj);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object invokeOriginalMethodNative(Member method, int methodId,
|
private static Object invokeOriginalMethodNative(Member method, int methodId,
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ void loadDexAndInit(JNIEnv *env, const char *dexPath) {
|
||||||
if (isInited) {
|
if (isInited) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
install_inline_hooks();
|
// install_inline_hooks();
|
||||||
jclass clzClassLoader = env->FindClass("java/lang/ClassLoader");
|
jclass clzClassLoader = env->FindClass("java/lang/ClassLoader");
|
||||||
LOGD("java/lang/ClassLoader: %p", clzClassLoader);
|
LOGD("java/lang/ClassLoader: %p", clzClassLoader);
|
||||||
jmethodID mdgetSystemClassLoader = env->GetStaticMethodID(clzClassLoader,
|
jmethodID mdgetSystemClassLoader = env->GetStaticMethodID(clzClassLoader,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <inject/framework_hook.h>
|
#include <inject/framework_hook.h>
|
||||||
|
#include <native_hook/native_hook.h>
|
||||||
|
|
||||||
#include "include/logging.h"
|
#include "include/logging.h"
|
||||||
#include "include/misc.h"
|
#include "include/misc.h"
|
||||||
|
|
@ -42,7 +43,8 @@ __attribute__((visibility("default"))) int nativeForkAndSpecializePost(JNIEnv *e
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((visibility("default"))) void onModuleLoaded() {
|
__attribute__((visibility("default"))) void onModuleLoaded() {
|
||||||
|
LOGI("onModuleLoaded: welcome to EdXposed!");
|
||||||
|
install_inline_hooks();
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((visibility("default")))
|
__attribute__((visibility("default")))
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,41 @@
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <include/android_build.h>
|
#include <include/android_build.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "include/logging.h"
|
#include "include/logging.h"
|
||||||
#include "native_hook.h"
|
#include "native_hook.h"
|
||||||
|
|
||||||
|
static bool inlineHooksInstalled = false;
|
||||||
|
|
||||||
static const char *(*getDesc)(void *, std::string *);
|
static const char *(*getDesc)(void *, std::string *);
|
||||||
|
|
||||||
static bool (*isInSamePackageBackup)(void *, void *) = nullptr;
|
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) {
|
static bool onIsInSamePackageCalled(void *thiz, void *that) {
|
||||||
std::string storage1, storage2;
|
std::string storage1, storage2;
|
||||||
const char *thisDesc = (*getDesc)(thiz, &storage1);
|
const char *thisDesc = (*getDesc)(thiz, &storage1);
|
||||||
|
|
@ -70,8 +97,8 @@ static bool disable_HiddenAPIPolicyImpl(int api_level, void *artHandle,
|
||||||
return symbol != nullptr;
|
return symbol != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hook_IsInSamePackage(int api_level, void *artHandle,
|
static void hookIsInSamePackage(int api_level, void *artHandle,
|
||||||
void (*hookFun)(void *, void *, void **)) {
|
void (*hookFun)(void *, void *, void **)) {
|
||||||
// 5.0 - 7.1
|
// 5.0 - 7.1
|
||||||
const char *isInSamePackageSym = "_ZN3art6mirror5Class15IsInSamePackageEPS1_";
|
const char *isInSamePackageSym = "_ZN3art6mirror5Class15IsInSamePackageEPS1_";
|
||||||
const char *getDescriptorSym = "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE";
|
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));
|
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() {
|
void install_inline_hooks() {
|
||||||
int api_level = GetAndroidApiLevel();
|
if (inlineHooksInstalled) {
|
||||||
if (api_level < ANDROID_LOLLIPOP) {
|
LOGI("inline hooks installed, skip");
|
||||||
return;
|
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);
|
void *whaleHandle = dlopen(kLibWhalePath, RTLD_LAZY | RTLD_GLOBAL);
|
||||||
if (!whaleHandle) {
|
if (!whaleHandle) {
|
||||||
LOGE("can't open libwhale: %s", dlerror());
|
LOGE("can't open libwhale: %s", dlerror());
|
||||||
|
|
@ -116,11 +177,15 @@ void install_inline_hooks() {
|
||||||
LOGE("can't open libart: %s", dlerror());
|
LOGE("can't open libart: %s", dlerror());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hook_IsInSamePackage(api_level, artHandle, hookFun);
|
hookIsInSamePackage(api_level, artHandle, hookFun);
|
||||||
|
hookRuntime(api_level, artHandle, hookFun);
|
||||||
if (disable_HiddenAPIPolicyImpl(api_level, artHandle, hookFun)) {
|
if (disable_HiddenAPIPolicyImpl(api_level, artHandle, hookFun)) {
|
||||||
LOGI("disable_HiddenAPIPolicyImpl done.");
|
LOGI("disable_HiddenAPIPolicyImpl done.");
|
||||||
} else {
|
} else {
|
||||||
LOGE("disable_HiddenAPIPolicyImpl failed.");
|
LOGE("disable_HiddenAPIPolicyImpl failed.");
|
||||||
}
|
}
|
||||||
|
dlclose(whaleHandle);
|
||||||
|
dlclose(artHandle);
|
||||||
|
LOGI("install inline hooks done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue