From 2c3eee62c0de64303d2a532d308eb1c67d663ee5 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Thu, 10 Dec 2020 17:44:59 +0800 Subject: [PATCH] Avoid entrypoint overwritten in Android R --- .../elderdrivers/riru/edxp/core/Yahfa.java | 2 + .../riru/edxp/core/yahfa/HookMain.java | 6 ++- .../cpp/external/yahfa/include/HookMain.h | 5 +++ .../main/cpp/external/yahfa/src/HookMain.c | 10 +++++ .../main/include/art/runtime/class_linker.h | 40 +++++++++++++++++-- .../include/art/runtime/jit/jit_code_cache.h | 32 +++++++++++++++ .../cpp/main/src/jni/edxp_pending_hooks.cpp | 11 +++-- .../cpp/main/src/jni/edxp_pending_hooks.h | 2 + .../src/main/cpp/main/src/jni/edxp_yahfa.cpp | 8 ++++ .../src/main/cpp/main/src/native_hook.cpp | 2 + .../riru/edxp/yahfa/core/YahfaRouter.java | 2 +- .../edxp/yahfa/dexmaker/HookerDexMaker.java | 2 +- 12 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 edxp-core/src/main/cpp/main/include/art/runtime/jit/jit_code_cache.h diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java index 2fa3c8c1..5d48d8a5 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java @@ -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); } diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/yahfa/HookMain.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/yahfa/HookMain.java index bebb7d18..352bea2a 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/yahfa/HookMain.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/yahfa/HookMain.java @@ -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 { diff --git a/edxp-core/src/main/cpp/external/yahfa/include/HookMain.h b/edxp-core/src/main/cpp/external/yahfa/include/HookMain.h index 8ab013d9..81f93f77 100644 --- a/edxp-core/src/main/cpp/external/yahfa/include/HookMain.h +++ b/edxp-core/src/main/cpp/external/yahfa/include/HookMain.h @@ -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 diff --git a/edxp-core/src/main/cpp/external/yahfa/src/HookMain.c b/edxp-core/src/main/cpp/external/yahfa/src/HookMain.c index eb7c05a0..831d96f0 100644 --- a/edxp-core/src/main/cpp/external/yahfa/src/HookMain.c +++ b/edxp-core/src/main/cpp/external/yahfa/src/HookMain.c @@ -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..."); diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h b/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h index c364a1cc..2ec73009 100644 --- a/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h +++ b/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h @@ -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 { diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/jit/jit_code_cache.h b/edxp-core/src/main/cpp/main/include/art/runtime/jit/jit_code_cache.h new file mode 100644 index 00000000..6c6471ed --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/art/runtime/jit/jit_code_cache.h @@ -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"); + } + }; + + } + +} diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.cpp b/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.cpp index ea82e1b9..8b05556b 100644 --- a/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.cpp +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.cpp @@ -2,6 +2,7 @@ #include #include #include +#include "HookMain.h" #include "jni.h" #include "native_util.h" #include "edxp_pending_hooks.h" @@ -10,7 +11,7 @@ namespace edxp { static std::set class_descs_; - static std::set hooked_methods_; + static std::set 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)); } } \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.h b/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.h index b952a2c5..09071675 100644 --- a/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.h +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.h @@ -9,6 +9,8 @@ namespace edxp { void RegisterPendingHooks(JNIEnv *); + bool isEntryHooked(const void* entry); + bool isHooked(void* art_method); void recordHooked(void* art_method); diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp b/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp index 0dda4147..31bc7447 100644 --- a/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp @@ -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(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) { diff --git a/edxp-core/src/main/cpp/main/src/native_hook.cpp b/edxp-core/src/main/cpp/main/src/native_hook.cpp index 6f4caccf..f73e3144 100644 --- a/edxp-core/src/main/cpp/main/src/native_hook.cpp +++ b/edxp-core/src/main/cpp/main/src/native_hook.cpp @@ -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 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"); diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/YahfaRouter.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/YahfaRouter.java index 2f4d0526..548724cb 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/YahfaRouter.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/YahfaRouter.java @@ -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; } diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java index 43017818..7ded6af1 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java @@ -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);