Avoid entrypoint overwritten in Android R
This commit is contained in:
parent
598d555e02
commit
2c3eee62c0
|
|
@ -19,4 +19,6 @@ public class Yahfa {
|
|||
public static native void recordHooked(Member member);
|
||||
|
||||
public static native boolean isHooked(Member member);
|
||||
|
||||
public static native void makeInitializedClassesVisiblyInitialized(long thread, boolean wait);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.os.Build;
|
|||
import com.elderdrivers.riru.edxp.art.ClassLinker;
|
||||
import com.elderdrivers.riru.edxp.art.Heap;
|
||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||
import com.elderdrivers.riru.edxp.util.ClassUtils;
|
||||
import com.elderdrivers.riru.edxp.util.Utils;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
|
@ -122,12 +123,13 @@ public class HookMain {
|
|||
}
|
||||
// make sure GC completed before hook
|
||||
Thread currentThread = Thread.currentThread();
|
||||
int lastGcType = Heap.waitForGcToComplete(
|
||||
XposedHelpers.getLongField(currentThread, "nativePeer"));
|
||||
long nativePeer = XposedHelpers.getLongField(currentThread, "nativePeer");
|
||||
int lastGcType = Heap.waitForGcToComplete(nativePeer);
|
||||
if (lastGcType < 0) {
|
||||
Utils.logW("waitForGcToComplete failed, using fallback");
|
||||
Runtime.getRuntime().gc();
|
||||
}
|
||||
|
||||
if (!Yahfa.backupAndHookNative(target, hook, backup)) {
|
||||
throw new RuntimeException("Failed to hook " + target + " with " + hook);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ void *getArtMethod(JNIEnv *env, jobject jmethod);
|
|||
|
||||
static void *getResolvedMethodsAddr(JNIEnv *, jobject);
|
||||
|
||||
// TODO: move to common utils instead of in YAHFA's code
|
||||
void *getEntryPoint(void* method);
|
||||
|
||||
void setEntryPoint(void* method, void* entry);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -136,6 +136,16 @@ bool setNativeFlag(void *method, bool isNative) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void *getEntryPoint(void* method) {
|
||||
return readAddr((char *) method + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod);
|
||||
}
|
||||
|
||||
void setEntryPoint(void* method, void* entry) {
|
||||
memcpy((char *) method + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
|
||||
&entry,
|
||||
pointer_size);
|
||||
}
|
||||
|
||||
static int replaceMethod(void *fromMethod, void *toMethod, int isBackup) {
|
||||
if (hookCount >= hookCap) {
|
||||
LOGI("not enough capacity. Allocating...");
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "edxp_context.h"
|
||||
#include "jni/edxp_pending_hooks.h"
|
||||
#include "utils.h"
|
||||
#include "HookMain.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
|
|
@ -37,7 +38,8 @@ namespace art {
|
|||
art::mirror::Class clazz(clazz_ptr);
|
||||
std::string storage;
|
||||
const char *desc = clazz.GetDescriptor(&storage);
|
||||
bool should_intercept = edxp::IsClassPending(desc) || std::string(desc).rfind("LEdHooker_") == 0;
|
||||
bool should_intercept =
|
||||
edxp::IsClassPending(desc) || std::string(desc).rfind("LEdHooker_") == 0;
|
||||
if (UNLIKELY(should_intercept)) {
|
||||
edxp::Context::GetInstance()->CallOnPreFixupStaticTrampolines(clazz_ptr);
|
||||
}
|
||||
|
|
@ -47,15 +49,30 @@ namespace art {
|
|||
}
|
||||
}
|
||||
|
||||
CREATE_FUNC_SYMBOL_ENTRY(void, MakeInitializedClassesVisiblyInitialized, void *thiz,
|
||||
void *self, bool wait) {
|
||||
if (LIKELY(MakeInitializedClassesVisiblyInitializedSym))
|
||||
MakeInitializedClassesVisiblyInitializedSym(thiz, self, wait);
|
||||
}
|
||||
|
||||
|
||||
CREATE_HOOK_STUB_ENTRIES(bool, ShouldUseInterpreterEntrypoint, void *art_method,
|
||||
const void *quick_code) {
|
||||
if (UNLIKELY(edxp::isHooked(art_method) && quick_code != nullptr)) {
|
||||
LOGD("Hooked method %p, no use interpreter", art_method);
|
||||
if (UNLIKELY(quick_code != nullptr && edxp::isEntryHooked(quick_code))) {
|
||||
return false;
|
||||
}
|
||||
return ShouldUseInterpreterEntrypointBackup(art_method, quick_code);
|
||||
}
|
||||
|
||||
CREATE_HOOK_STUB_ENTRIES(bool, IsQuickToInterpreterBridge, void *thiz,
|
||||
const void *quick_code) {
|
||||
if (quick_code != nullptr && UNLIKELY(edxp::isEntryHooked(quick_code))) {
|
||||
LOGD("Pretend to be quick to interpreter bridge %p", quick_code);
|
||||
return true;
|
||||
}
|
||||
return IsQuickToInterpreterBridgeBackup(thiz, quick_code);
|
||||
}
|
||||
|
||||
public:
|
||||
ClassLinker(void *thiz) : HookedObject(thiz) {}
|
||||
|
||||
|
|
@ -127,6 +144,23 @@ namespace art {
|
|||
HOOK_FUNC(ShouldUseInterpreterEntrypoint,
|
||||
"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv");
|
||||
}
|
||||
|
||||
// MakeInitializedClassesVisiblyInitialized will cause deadlock
|
||||
// IsQuickToInterpreterBridge cannot be hooked by Dobby yet
|
||||
// So we use GetSavedEntryPointOfPreCompiledMethod instead
|
||||
// if (api_level >= __ANDROID_API_R__) {
|
||||
// RETRIEVE_FUNC_SYMBOL(MakeInitializedClassesVisiblyInitialized,
|
||||
// "_ZN3art11ClassLinker40MakeInitializedClassesVisiblyInitializedEPNS_6ThreadEb");
|
||||
// HOOK_FUNC(IsQuickToInterpreterBridge,
|
||||
// "_ZNK3art11ClassLinker26IsQuickToInterpreterBridgeEPKv");
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void MakeInitializedClassesVisiblyInitialized(void *self, bool wait) const {
|
||||
LOGD("MakeInitializedClassesVisiblyInitialized start, thiz=%p, self=%p", thiz_, self);
|
||||
if (LIKELY(thiz_))
|
||||
MakeInitializedClassesVisiblyInitialized(thiz_, self, wait);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void SetEntryPointsToInterpreter(void *art_method) const {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/object.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
namespace jit {
|
||||
|
||||
CREATE_HOOK_STUB_ENTRIES(const void*, GetSavedEntryPointOfPreCompiledMethod, void *thiz,
|
||||
void *art_method) {
|
||||
if (UNLIKELY(edxp::isHooked(art_method))) {
|
||||
LOGD("Found hooked method, return entrypoint as jit entrypoint");
|
||||
return getEntryPoint(art_method);
|
||||
}
|
||||
return GetSavedEntryPointOfPreCompiledMethodBackup(thiz, art_method);
|
||||
}
|
||||
|
||||
static void HookJitCacheCode(void *handle, HookFunType hook_func) {
|
||||
const int api_level = edxp::GetAndroidApiLevel();
|
||||
// For android R, the invisibly initialization makes static methods initializes multiple
|
||||
// times in non-x86 devices. So we need to hook this function to make sure
|
||||
// our hooked entry point won't be overwritten.
|
||||
// This is for SandHook and YAHFA
|
||||
if (api_level >= __ANDROID_API_R__) {
|
||||
HOOK_FUNC(GetSavedEntryPointOfPreCompiledMethod,
|
||||
"_ZN3art3jit12JitCodeCache37GetSavedEntryPointOfPreCompiledMethodEPNS_9ArtMethodE");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#include <nativehelper/jni_macros.h>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include "HookMain.h"
|
||||
#include "jni.h"
|
||||
#include "native_util.h"
|
||||
#include "edxp_pending_hooks.h"
|
||||
|
|
@ -10,7 +11,7 @@ namespace edxp {
|
|||
|
||||
static std::set<std::string> class_descs_;
|
||||
|
||||
static std::set<void*> hooked_methods_;
|
||||
static std::set<const void*> hooked_methods_;
|
||||
|
||||
bool IsClassPending(const char *class_desc) {
|
||||
return class_descs_.find(class_desc) != class_descs_.end();
|
||||
|
|
@ -29,12 +30,16 @@ namespace edxp {
|
|||
REGISTER_EDXP_NATIVE_METHODS("de.robv.android.xposed.PendingHooks");
|
||||
}
|
||||
|
||||
bool isEntryHooked(const void* entry) {
|
||||
return hooked_methods_.count(entry);
|
||||
}
|
||||
|
||||
bool isHooked(void* art_method) {
|
||||
return hooked_methods_.count(art_method);
|
||||
return isEntryHooked(getEntryPoint(art_method));
|
||||
}
|
||||
|
||||
void recordHooked(void * art_method) {
|
||||
hooked_methods_.insert(art_method);
|
||||
hooked_methods_.insert(getEntryPoint(art_method));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ namespace edxp {
|
|||
|
||||
void RegisterPendingHooks(JNIEnv *);
|
||||
|
||||
bool isEntryHooked(const void* entry);
|
||||
|
||||
bool isHooked(void* art_method);
|
||||
|
||||
void recordHooked(void* art_method);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "native_util.h"
|
||||
#include "edxp_yahfa.h"
|
||||
#include "edxp_pending_hooks.h"
|
||||
#include "art/runtime/class_linker.h"
|
||||
|
||||
namespace edxp {
|
||||
|
||||
|
|
@ -58,6 +59,12 @@ namespace edxp {
|
|||
return edxp::isHooked(getArtMethod(env, member));
|
||||
}
|
||||
|
||||
static void
|
||||
Yahfa_makeInitializedClassesVisiblyInitialized(JNI_START, jlong thread, jboolean wait) {
|
||||
art::ClassLinker::Current()->MakeInitializedClassesVisiblyInitialized(
|
||||
reinterpret_cast<void *>(thread), wait);
|
||||
}
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
NATIVE_METHOD(Yahfa, init, "(I)V"),
|
||||
NATIVE_METHOD(Yahfa, findMethodNative,
|
||||
|
|
@ -68,6 +75,7 @@ namespace edxp {
|
|||
NATIVE_METHOD(Yahfa, setNativeFlag, "(Ljava/lang/reflect/Member;Z)Z"),
|
||||
NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Member;)V"),
|
||||
NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Member;)Z"),
|
||||
NATIVE_METHOD(Yahfa, makeInitializedClassesVisiblyInitialized, "(JZ)V"),
|
||||
};
|
||||
|
||||
void RegisterEdxpYahfa(JNIEnv *env) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "art/runtime/gc/heap.h"
|
||||
#include "art/runtime/hidden_api.h"
|
||||
#include "art/runtime/oat_file_manager.h"
|
||||
#include "art/runtime/jit/jit_code_cache.h"
|
||||
|
||||
std::vector<soinfo_t> linker_get_solist(); // Dobby but not in .h
|
||||
|
||||
|
|
@ -78,6 +79,7 @@ namespace edxp {
|
|||
art::mirror::Class::Setup(art_handle, hook_func);
|
||||
art::JNIEnvExt::Setup(art_handle, hook_func);
|
||||
art::oat_file_manager::DisableOnlyUseSystemOatFiles(art_handle, hook_func);
|
||||
art::jit::HookJitCacheCode(art_handle, hook_func);
|
||||
|
||||
art_hooks_installed = true;
|
||||
LOGI("ART hooks installed");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.elderdrivers.riru.edxp.yahfa.core;
|
||||
|
||||
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||
import com.elderdrivers.riru.edxp.proxy.BaseRouter;
|
||||
import com.elderdrivers.riru.edxp.yahfa.config.YahfaEdxpConfig;
|
||||
import com.elderdrivers.riru.edxp.yahfa.config.YahfaHookProvider;
|
||||
|
|
@ -10,6 +9,7 @@ import com.elderdrivers.riru.edxp.yahfa.dexmaker.DynamicBridge;
|
|||
public class YahfaRouter extends BaseRouter {
|
||||
|
||||
YahfaRouter() {
|
||||
// TODO: disable for better performance
|
||||
useXposedApi = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ public class HookerDexMaker {
|
|||
loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);
|
||||
}
|
||||
|
||||
mHookClass = loader.loadClass(className);
|
||||
mHookClass = Class.forName(className.replace("/", "."), true, loader);
|
||||
// Execute our newly-generated code in-process.
|
||||
mHookClass.getMethod(METHOD_NAME_SETUP, Member.class, XposedBridge.AdditionalHookInfo.class)
|
||||
.invoke(null, mMember, mHookInfo);
|
||||
|
|
|
|||
Loading…
Reference in New Issue