From d6c7570588b9c1b6ff2393429d8f5c6a44c3e552 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Fri, 4 Dec 2020 00:20:08 +0800 Subject: [PATCH] Move inline hooks to post fork --- .../src/main/cpp/main/include/JNIHelper.h | 32 +++++----- .../main/include/art/runtime/class_linker.h | 59 ++++++++++--------- .../cpp/main/include/art/runtime/gc/heap.h | 51 +++++++++++++--- .../cpp/main/include/art/runtime/runtime.h | 23 +------- edxp-core/src/main/cpp/main/include/config.h | 11 ++++ .../src/main/cpp/main/src/edxp_context.cpp | 19 +++++- .../cpp/main/src/jni/art_class_linker.cpp | 7 +-- edxp-core/src/main/cpp/main/src/main.cpp | 1 - .../src/main/cpp/main/src/native_hook.cpp | 8 +-- 9 files changed, 125 insertions(+), 86 deletions(-) diff --git a/edxp-core/src/main/cpp/main/include/JNIHelper.h b/edxp-core/src/main/cpp/main/include/JNIHelper.h index f90162e1..84cec521 100644 --- a/edxp-core/src/main/cpp/main/include/JNIHelper.h +++ b/edxp-core/src/main/cpp/main/include/JNIHelper.h @@ -41,67 +41,67 @@ ALWAYS_INLINE static int ClearException(JNIEnv *env) { #define JNI_FindClass(env, name) \ env->FindClass(name); \ - if (ClearException(env)) LOGE("FindClass " #name); + if (ClearException(env)) LOGE("FindClass " #name) #define JNI_GetObjectClass(env, obj) \ env->GetObjectClass(obj); \ - if (ClearException(env)) LOGE("GetObjectClass " #obj); + if (ClearException(env)) LOGE("GetObjectClass " #obj) #define JNI_GetFieldID(env, class, name, sig) \ env->GetFieldID(class, name, sig); \ - if (ClearException(env)) LOGE("GetFieldID " #name); + if (ClearException(env)) LOGE("GetFieldID " #name) #define JNI_GetObjectField(env, class, fieldId) \ env->GetObjectField(class, fieldId); \ - if (ClearException(env)) LOGE("GetObjectField " #fieldId); + if (ClearException(env)) LOGE("GetObjectField " #fieldId) #define JNI_GetMethodID(env, class, name, sig) \ env->GetMethodID(class, name, sig); \ - if (ClearException(env)) LOGE("GetMethodID " #name); + if (ClearException(env)) LOGE("GetMethodID " #name) #define JNI_CallObjectMethod(env, obj, ...) \ env->CallObjectMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallObjectMethod " #obj " " #__VA_ARGS__); + if (ClearException(env)) LOGE("CallObjectMethod " #obj " " #__VA_ARGS__) #define JNI_CallVoidMethod(env, obj, ...) \ env->CallVoidMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallVoidMethod " #obj " " #__VA_ARGS__); + if (ClearException(env)) LOGE("CallVoidMethod " #obj " " #__VA_ARGS__) #define JNI_GetStaticFieldID(env, class, name, sig) \ env->GetStaticFieldID(class, name, sig); \ - if (ClearException(env)) LOGE("GetStaticFieldID " #name " " #sig); + if (ClearException(env)) LOGE("GetStaticFieldID " #name " " #sig) #define JNI_GetStaticObjectField(env, class, fieldId) \ env->GetStaticObjectField(class, fieldId); \ - if (ClearException(env)) LOGE("GetStaticObjectField " #fieldId); + if (ClearException(env)) LOGE("GetStaticObjectField " #fieldId) #define JNI_GetStaticMethodID(env, class, name, sig) \ env->GetStaticMethodID(class, name, sig); \ - if (ClearException(env)) LOGE("GetStaticMethodID " #name); + if (ClearException(env)) LOGE("GetStaticMethodID " #name) #define JNI_CallStaticVoidMethod(env, obj, ...) \ env->CallStaticVoidMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallStaticVoidMethod " #obj " " #__VA_ARGS__); + if (ClearException(env)) LOGE("CallStaticVoidMethod " #obj " " #__VA_ARGS__) #define JNI_CallStaticObjectMethod(env, obj, ...) \ env->CallStaticObjectMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallStaticObjectMethod " #obj " " #__VA_ARGS__); + if (ClearException(env)) LOGE("CallStaticObjectMethod " #obj " " #__VA_ARGS__) #define JNI_CallStaticIntMethod(env, obj, ...) \ env->CallStaticIntMethod(obj, __VA_ARGS__); \ - if (ClearException(env)) LOGE("CallStaticIntMethod " #obj " " #__VA_ARGS__); + if (ClearException(env)) LOGE("CallStaticIntMethod " #obj " " #__VA_ARGS__) #define JNI_GetArrayLength(env, array) \ env->GetArrayLength(array); \ - if (ClearException(env)) LOGE("GetArrayLength " #array); + if (ClearException(env)) LOGE("GetArrayLength " #array) #define JNI_NewObject(env, class, ...) \ env->NewObject(class, __VA_ARGS__); \ - if (ClearException(env)) LOGE("NewObject " #class " " #__VA_ARGS__); + if (ClearException(env)) LOGE("NewObject " #class " " #__VA_ARGS__) #define JNI_RegisterNatives(env, class, methods, size) \ env->RegisterNatives(class, methods, size); \ - if (ClearException(env)) LOGE("RegisterNatives " #class); + if (ClearException(env)) LOGE("RegisterNatives " #class) class JUTFString { public: 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 9a046e10..17039d15 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 @@ -46,7 +46,8 @@ namespace art { } } - CREATE_HOOK_STUB_ENTRIES(bool, ShouldUseInterpreterEntrypoint, void *art_method, const void* quick_code) { + CREATE_HOOK_STUB_ENTRIES(bool, ShouldUseInterpreterEntrypoint, void *art_method, + const void *quick_code) { // TODO check hooked bool hooked = false; if (hooked && quick_code != nullptr) { @@ -68,46 +69,47 @@ namespace art { // TODO: Maybe not compatible with Android 10- int api_level = edxp::GetAndroidApiLevel(); size_t OFFSET_classlinker; // Get offset from art::Runtime::RunRootClinits() call in IDA - switch(api_level) { + switch (api_level) { case __ANDROID_API_O__: + [[fallthrough]]; case __ANDROID_API_O_MR1__: -#ifdef __LP64__ - OFFSET_classlinker = 464 / 8; -#else - OFFSET_classlinker = 284 / 4; -#endif + if constexpr(edxp::is64) { + OFFSET_classlinker = 464; + } else { + OFFSET_classlinker = 284; + } break; case __ANDROID_API_P__: -#ifdef __LP64__ - OFFSET_classlinker = 528 / 8; -#else - OFFSET_classlinker = 336 / 4; -#endif + if constexpr(edxp::is64) { + OFFSET_classlinker = 528; + } else { + OFFSET_classlinker = 336; + } break; case __ANDROID_API_Q__: -#ifdef __LP64__ - OFFSET_classlinker = 480 / 8; -#else - OFFSET_classlinker = 280 / 4; -#endif + if constexpr(edxp::is64) { + OFFSET_classlinker = 480; + } else { + OFFSET_classlinker = 280; + } break; default: LOGE("No valid offset for art::Runtime::class_linker_ found. Using Android R."); + [[fallthrough]]; case __ANDROID_API_R__: -#ifdef __LP64__ - OFFSET_classlinker = 472 / 8; -#else - OFFSET_classlinker = 276 / 4; -#endif + if constexpr(edxp::is64) { + OFFSET_classlinker = 472; + } else { + OFFSET_classlinker = 276; + } break; } + void *thiz = *reinterpret_cast( + reinterpret_cast(Runtime::Current()->Get()) + OFFSET_classlinker); // ClassLinker* GetClassLinker() but inlined - void* cl = reinterpret_cast( - reinterpret_cast(Runtime::Current()->Get()) + OFFSET_classlinker - ); - LOGD("Classlinker object: %p", cl); - instance_ = new ClassLinker(cl); + LOGD("Classlinker object: %p", thiz); + instance_ = new ClassLinker(thiz); HOOK_FUNC(Constructor, "_ZN3art11ClassLinkerC2EPNS_11InternTableE", "_ZN3art11ClassLinkerC2EPNS_11InternTableEb"); // 10.0 @@ -119,7 +121,8 @@ namespace art { // Sandhook will hook ShouldUseInterpreterEntrypoint, so we just skip // edxp::Context::GetInstance()->GetVariant() will not work here, so we use smh dirty hack - if (api_level >= __ANDROID_API_R__ && access(edxp::kLibSandHookNativePath.c_str(), F_OK) == -1) { + if (api_level >= __ANDROID_API_R__ && + access(edxp::kLibSandHookNativePath.c_str(), F_OK) == -1) { LOGD("Not sandhook, installing _ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv"); HOOK_FUNC(ShouldUseInterpreterEntrypoint, "_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv"); diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/gc/heap.h b/edxp-core/src/main/cpp/main/include/art/runtime/gc/heap.h index fbd78f8b..1cba0282 100644 --- a/edxp-core/src/main/cpp/main/include/art/runtime/gc/heap.h +++ b/edxp-core/src/main/cpp/main/include/art/runtime/gc/heap.h @@ -5,6 +5,7 @@ #include "collector/gc_type.h" #include "gc_cause.h" #include "../thread.h" +#include "../runtime.h" namespace art { @@ -22,14 +23,6 @@ namespace art { return art::gc::collector::GcType::kGcTypeNone; } - CREATE_HOOK_STUB_ENTRIES(void, PreZygoteFork, void *thiz) { - if (instance_) - instance_->Reset(thiz); - else - instance_ = new Heap(thiz); - PreZygoteForkBackup(thiz); - } - public: Heap(void *thiz) : HookedObject(thiz) {} @@ -39,7 +32,47 @@ namespace art { // @ApiSensitive(Level.MIDDLE) static void Setup(void *handle, HookFunType hook_func) { - HOOK_FUNC(PreZygoteFork, "_ZN3art2gc4Heap13PreZygoteForkEv"); + int api_level = edxp::GetAndroidApiLevel(); + size_t OFFSET_classlinker; // Get offset from art::Runtime::RunRootClinits() call in IDA + switch (api_level) { + case __ANDROID_API_O__: + [[fallthrough]]; + case __ANDROID_API_O_MR1__: + if constexpr(edxp::is64) { + OFFSET_classlinker = 464; + } else { + OFFSET_classlinker = 284; + } + break; + case __ANDROID_API_P__: + if constexpr(edxp::is64) { + OFFSET_classlinker = 528; + } else { + OFFSET_classlinker = 336; + } + break; + case __ANDROID_API_Q__: + if constexpr(edxp::is64) { + OFFSET_classlinker = 480; + } else { + OFFSET_classlinker = 280; + } + break; + default: + LOGE("No valid offset for art::Runtime::class_linker_ found. Using Android R."); + [[fallthrough]]; + case __ANDROID_API_R__: + if constexpr(edxp::is64) { + OFFSET_classlinker = 392; + } else { + OFFSET_classlinker = 236; + } + break; + } + void *thiz = *reinterpret_cast( + reinterpret_cast(Runtime::Current()->Get()) + OFFSET_classlinker); + LOGD("HEAP object: %p", thiz); + instance_ = new Heap(thiz); RETRIEVE_FUNC_SYMBOL(WaitForGcToComplete, "_ZN3art2gc4Heap19WaitForGcToCompleteENS0_7GcCauseEPNS_6ThreadE"); } diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/runtime.h b/edxp-core/src/main/cpp/main/include/art/runtime/runtime.h index 2e3ddc35..3436396e 100644 --- a/edxp-core/src/main/cpp/main/include/art/runtime/runtime.h +++ b/edxp-core/src/main/cpp/main/include/art/runtime/runtime.h @@ -16,19 +16,6 @@ namespace art { DeoptimizeBootImageSym(thiz); } - CREATE_HOOK_STUB_ENTRIES(bool, Init, void *thiz, void *runtime_options) { - if (LIKELY(instance_)) - instance_->Reset(thiz); - else - instance_ = new Runtime(thiz); - bool success = InitBackup(thiz, runtime_options); - if (edxp::ConfigManager::GetInstance()->IsDeoptBootImageEnabled()) { - DeoptimizeBootImage(thiz); - LOGI("DeoptimizeBootImage done"); - } - return success; - } - public: Runtime(void *thiz) : HookedObject(thiz) {} @@ -38,19 +25,13 @@ namespace art { // @ApiSensitive(Level.LOW) static void Setup(void *handle, HookFunType hook_func) { - HOOK_FUNC(Init, "_ZN3art7Runtime4InitEONS_18RuntimeArgumentMapE"); RETRIEVE_FUNC_SYMBOL(DeoptimizeBootImage, "_ZN3art7Runtime19DeoptimizeBootImageEv"); - RETRIEVE_FIELD_SYMBOL(thiz, "_ZN3art7Runtime9instance_E"); + RETRIEVE_FIELD_SYMBOL(instance, "_ZN3art7Runtime9instance_E"); + void * thiz = *reinterpret_cast(instance); LOGD("_ZN3art7Runtime9instance_E = %p", thiz); instance_ = new Runtime(thiz); } - - ALWAYS_INLINE void DeoptimizeBootImage() const { - if (LIKELY(thiz_)) - DeoptimizeBootImage(thiz_); - } - }; } diff --git a/edxp-core/src/main/cpp/main/include/config.h b/edxp-core/src/main/cpp/main/include/config.h index fa0f9357..c01d4279 100644 --- a/edxp-core/src/main/cpp/main/include/config.h +++ b/edxp-core/src/main/cpp/main/include/config.h @@ -12,6 +12,17 @@ namespace edxp { //#define LOG_DISABLED //#define DEBUG + +inline bool constexpr Is64() { +#if defined(__LP64__) + return true; +#else + return false; +#endif +} + +inline constexpr bool is64 = Is64(); + #if defined(__LP64__) # define LP_SELECT(lp32, lp64) (lp64) #else diff --git a/edxp-core/src/main/cpp/main/src/edxp_context.cpp b/edxp-core/src/main/cpp/main/src/edxp_context.cpp index 0b5a296b..5f7ae944 100644 --- a/edxp-core/src/main/cpp/main/src/edxp_context.cpp +++ b/edxp-core/src/main/cpp/main/src/edxp_context.cpp @@ -18,6 +18,9 @@ #include #include "edxp_context.h" #include "config_manager.h" +#include "art/runtime/runtime.h" +#include "art/runtime/gc/heap.h" +#include "native_hook.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-value" @@ -242,9 +245,22 @@ namespace edxp { } - int Context::OnNativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { + int + Context::OnNativeForkSystemServerPost(JNIEnv *env, [[maybe_unused]] jclass clazz, jint res) { if (res == 0) { if (!skip_) { + if (void *buf = mmap(nullptr, 1, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANONYMOUS | MAP_PRIVATE, -1, + 0); + buf == MAP_FAILED) { + skip_ = true; + LOGE("skip injecting into android because sepolicy was not loaded properly"); + } else { + munmap(buf, 1); + } + } + if (!skip_) { + InstallInlineHooks(); PrepareJavaEnv(env); // only do work in child since FindAndCall would print log FindAndCall(env, "forkSystemServerPost", "(I)V", res); @@ -345,6 +361,7 @@ namespace edxp { if (res == 0) { const JUTFString process_name(env, nice_name_); if (!skip_) { + InstallInlineHooks(); PrepareJavaEnv(env); LOGD("Done prepare"); FindAndCall(env, "forkAndSpecializePost", diff --git a/edxp-core/src/main/cpp/main/src/jni/art_class_linker.cpp b/edxp-core/src/main/cpp/main/src/jni/art_class_linker.cpp index f234b49d..858a94d8 100644 --- a/edxp-core/src/main/cpp/main/src/jni/art_class_linker.cpp +++ b/edxp-core/src/main/cpp/main/src/jni/art_class_linker.cpp @@ -9,18 +9,17 @@ namespace edxp { - static std::vector deopted_methods; + static std::unordered_set deopted_methods; static void ClassLinker_setEntryPointsToInterpreter(JNI_START, jobject method) { void *reflected_method = getArtMethod(env, method); - if (std::find(deopted_methods.begin(), deopted_methods.end(), reflected_method) != - deopted_methods.end()) { + if (deopted_methods.count(reflected_method)) { LOGD("method %p has been deopted before, skip...", reflected_method); return; } LOGD("deoptimizing method: %p", reflected_method); art::ClassLinker::Current()->SetEntryPointsToInterpreter(reflected_method); - deopted_methods.push_back(reflected_method); + deopted_methods.insert(reflected_method); LOGD("method deoptimized: %p", reflected_method); } diff --git a/edxp-core/src/main/cpp/main/src/main.cpp b/edxp-core/src/main/cpp/main/src/main.cpp index 5119aedc..2c8a92a1 100644 --- a/edxp-core/src/main/cpp/main/src/main.cpp +++ b/edxp-core/src/main/cpp/main/src/main.cpp @@ -24,7 +24,6 @@ namespace edxp { // TODO exclude unrelated processes static void onModuleLoaded() { LOG(INFO) << "onModuleLoaded: welcome to EdXposed!"; - InstallInlineHooks(); } static int shouldSkipUid(int uid) { 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 c03bb8ab..6f4caccf 100644 --- a/edxp-core/src/main/cpp/main/src/native_hook.cpp +++ b/edxp-core/src/main/cpp/main/src/native_hook.cpp @@ -24,7 +24,7 @@ namespace edxp { static volatile bool installed = false; static volatile bool art_hooks_installed = false; - static HookFunType hook_func = nullptr; + static HookFunType hook_func = reinterpret_cast(DobbyHook); void InstallArtHooks(void *art_handle); @@ -42,8 +42,6 @@ namespace edxp { } LOGI("Using api level %d", api_level); InstallRiruHooks(); - hook_func = reinterpret_cast(DobbyHook); - // install ART hooks if (api_level >= __ANDROID_API_Q__) { // From Riru v22 we can't get ART handle by hooking dlopen, so we get libart.so from soinfo. @@ -73,9 +71,7 @@ namespace edxp { if (art_hooks_installed) { return; } - if (ConfigManager::GetInstance()->IsHiddenAPIBypassEnabled()) { - art::hidden_api::DisableHiddenApi(art_handle, hook_func); - } + art::hidden_api::DisableHiddenApi(art_handle, hook_func); art::Runtime::Setup(art_handle, hook_func); art::gc::Heap::Setup(art_handle, hook_func); art::ClassLinker::Setup(art_handle, hook_func);