diff --git a/core/src/main/cpp/main/include/art/runtime/jit/jit_code_cache.h b/core/src/main/cpp/main/include/art/runtime/jit/jit_code_cache.h new file mode 100644 index 00000000..73f2ec10 --- /dev/null +++ b/core/src/main/cpp/main/include/art/runtime/jit/jit_code_cache.h @@ -0,0 +1,52 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2021 LSPosed Contributors + */ + +#pragma once + +#include +#include +#include "jni/yahfa.h" + +namespace art { + namespace jit { + namespace jit_code_cache { + CREATE_MEM_FUNC_SYMBOL_ENTRY(void, MoveObsoleteMethod, void *thiz, + void *old_method, void *new_method) { + if (MoveObsoleteMethodSym) + [[likely]] MoveObsoleteMethodSym(thiz, old_method, new_method); + } + + CREATE_MEM_HOOK_STUB_ENTRIES( + "_ZN3art3jit12JitCodeCache19GarbageCollectCacheEPNS_6ThreadE", + void, GarbageCollectCache, (void * thiz, void * self), { + LOGD("Before jit cache gc, moving hooked methods"); + for (auto[target, backup] : lspd::getJitMovements()) { + MoveObsoleteMethod(thiz, target, backup); + } + backup(thiz, self); + }); + + inline void Setup(const SandHook::ElfImg &handle) { + RETRIEVE_MEM_FUNC_SYMBOL(MoveObsoleteMethod, + "_ZN3art3jit12JitCodeCache18MoveObsoleteMethodEPNS_9ArtMethodES3_"); + lspd::HookSyms(handle, GarbageCollectCache); + } + } + } +} diff --git a/core/src/main/cpp/main/include/art/runtime/jit/profiling_info.h b/core/src/main/cpp/main/include/art/runtime/jit/profiling_info.h deleted file mode 100644 index 5c0c2a25..00000000 --- a/core/src/main/cpp/main/include/art/runtime/jit/profiling_info.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of LSPosed. - * - * LSPosed is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LSPosed is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LSPosed. If not, see . - * - * Copyright (C) 2021 LSPosed Contributors - */ - -#pragma once - -#include -#include -#include -#include "utils.h" -#include "jni/pending_hooks.h" - -namespace art { - namespace profiling_info { - inline static size_t OFFSET_art_method = -1; - - CREATE_MEM_HOOK_STUB_ENTRIES( - "_ZN3art13ProfilingInfo13AddInvokeInfoEjPNS_6mirror5ClassE", - void, AddInvokeInfo, (void * thiz, uint32_t dex_pc, void * clazz_ptr), { - void *method = *reinterpret_cast( - reinterpret_cast(thiz) + OFFSET_art_method); - if (lspd::isHooked(method)) [[unlikely]] return; - backup(thiz, dex_pc, clazz_ptr); - }); - - static void Setup(const SandHook::ElfImg &handle) { - int api_level = lspd::GetAndroidApiLevel(); - switch (api_level) { - case __ANDROID_API_Q__: - OFFSET_art_method = 0; - break; - case __ANDROID_API_O_MR1__: - [[fallthrough]]; - case __ANDROID_API_P__: - [[fallthrough]]; - case __ANDROID_API_R__: - [[fallthrough]]; - case __ANDROID_API_S__: - if constexpr(lspd::is64) { - OFFSET_art_method = 8; - } else { - OFFSET_art_method = 4; - } - break; - } - if (OFFSET_art_method != size_t(-1)) - lspd::HookSyms(handle, AddInvokeInfo); - } - } -} diff --git a/core/src/main/cpp/main/src/jni/yahfa.cpp b/core/src/main/cpp/main/src/jni/yahfa.cpp index 49179ee7..213bd092 100644 --- a/core/src/main/cpp/main/src/jni/yahfa.cpp +++ b/core/src/main/cpp/main/src/jni/yahfa.cpp @@ -35,6 +35,9 @@ namespace lspd { namespace { std::unordered_set hooked_methods_; std::shared_mutex hooked_methods_lock_; + + std::vector> jit_movements_; + std::shared_mutex jit_movements_lock_; } bool isHooked(void *art_method) { @@ -47,7 +50,18 @@ namespace lspd { hooked_methods_.insert(art_method); } + void recordJitMovement(void *target, void* backup) { + std::unique_lock lk(jit_movements_lock_); + jit_movements_.emplace_back(target, backup); + } + + std::vector> getJitMovements() { + std::unique_lock lk(jit_movements_lock_); + return std::move(jit_movements_); + } + using namespace startop::dex; + LSP_DEF_NATIVE_METHOD(void, Yahfa, init, jint sdkVersion) { yahfa::init(env, clazz, sdkVersion); } @@ -59,16 +73,20 @@ namespace lspd { } LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, backupAndHookNative, jobject target, - jobject hook, jobject backup) { + jobject hook, jobject backup, jboolean is_proxy) { art::gc::ScopedGCCriticalSection section(art::Thread::Current().Get(), art::gc::kGcCauseDebugger, art::gc::kCollectorTypeDebugger); art::thread_list::ScopedSuspendAll suspend("Yahfa Hook", false); - return yahfa::backupAndHookNative(env, clazz, target, hook, backup); - } - - LSP_DEF_NATIVE_METHOD(void, Yahfa, recordHooked, jobject member) { - lspd::recordHooked(yahfa::getArtMethod(env, member)); + if (yahfa::backupAndHookNative(env, clazz, target, hook, backup)) { + auto *target_method = yahfa::getArtMethod(env, target); + auto *backup_method = yahfa::getArtMethod(env, backup); + recordHooked(target_method); + if (!is_proxy) [[likely]] recordJitMovement(target_method, backup_method); + return JNI_TRUE; + } else { + return JNI_FALSE; + } } LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, isHooked, jobject member) { @@ -80,7 +98,7 @@ namespace lspd { static auto *kInMemoryClassloader = JNI_NewGlobalRef(env, JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader")); static jmethodID kInitMid = JNI_GetMethodID(env, kInMemoryClassloader, "", - "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); + "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); DexBuilder dex_file; auto parameter_length = env->GetArrayLength(classes); @@ -116,10 +134,10 @@ namespace lspd { hook_builder.BuildNewArray(hook_params_array, TypeDescriptor::Object, tmp); for (size_t i = 0U, j = 0U; i < parameter_types.size(); ++i, ++j) { hook_builder.BuildBoxIfPrimitive(Value::Parameter(j), parameter_types[i], - Value::Parameter(j)); + Value::Parameter(j)); hook_builder.BuildConst(tmp, i); hook_builder.BuildAput(Instruction::Op::kAputObject, hook_params_array, - Value::Parameter(j), tmp); + Value::Parameter(j), tmp); if (parameter_types[i].is_wide()) ++j; } auto handle_hook_method{dex_file.GetOrDeclareMethod( @@ -171,10 +189,10 @@ namespace lspd { env->DeleteLocalRef(dex_buffer); static jmethodID kMid = JNI_GetMethodID(env, kInMemoryClassloader, "loadClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); + "(Ljava/lang/String;)Ljava/lang/Class;"); if (!kMid) { kMid = JNI_GetMethodID(env, kInMemoryClassloader, "findClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); + "(Ljava/lang/String;)Ljava/lang/Class;"); } auto target = JNI_CallObjectMethod(env, my_cl, kMid, env->NewStringUTF("LspHooker_")); // LOGD("Created %zd", image.size()); @@ -189,8 +207,7 @@ namespace lspd { LSP_NATIVE_METHOD(Yahfa, findMethodNative, "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Executable;"), LSP_NATIVE_METHOD(Yahfa, backupAndHookNative, - "(Ljava/lang/reflect/Executable;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"), - LSP_NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Executable;)V"), + "(Ljava/lang/reflect/Executable;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Z)Z"), LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Executable;)Z"), LSP_NATIVE_METHOD(Yahfa, buildHooker, "(Ljava/lang/ClassLoader;C[CLjava/lang/String;)Ljava/lang/Class;"), diff --git a/core/src/main/cpp/main/src/jni/yahfa.h b/core/src/main/cpp/main/src/jni/yahfa.h index b3c04203..fe16429f 100644 --- a/core/src/main/cpp/main/src/jni/yahfa.h +++ b/core/src/main/cpp/main/src/jni/yahfa.h @@ -21,12 +21,14 @@ #pragma once #include "jni.h" +#include +#include namespace lspd { bool isHooked(void* art_method); - void recordHooked(void* art_method); + std::vector> getJitMovements(); void RegisterYahfa(JNIEnv *); diff --git a/core/src/main/cpp/main/src/native_hook.cpp b/core/src/main/cpp/main/src/native_hook.cpp index f6cf1a13..7760fbcf 100644 --- a/core/src/main/cpp/main/src/native_hook.cpp +++ b/core/src/main/cpp/main/src/native_hook.cpp @@ -36,7 +36,7 @@ #include "art/runtime/instrumentation.h" #include "art/runtime/thread_list.h" #include "art/runtime/gc/scoped_gc_critical_section.h" -#include "art/runtime/jit/profiling_info.h" +#include "art/runtime/jit/jit_code_cache.h" namespace lspd { static std::atomic_bool installed = false; @@ -61,7 +61,7 @@ namespace lspd { art::instrumentation::DisableUpdateHookedMethodsCode(handle_libart); art::thread_list::ScopedSuspendAll::Setup(handle_libart); art::gc::ScopedGCCriticalSection::Setup(handle_libart); - art::profiling_info::Setup(handle_libart); + art::jit::jit_code_cache::Setup(handle_libart); art_img.reset(); LOGD("Inline hooks installed"); } diff --git a/core/src/main/java/org/lsposed/lspd/core/yahfa/HookMain.java b/core/src/main/java/org/lsposed/lspd/core/yahfa/HookMain.java index c89b189e..b31694e9 100644 --- a/core/src/main/java/org/lsposed/lspd/core/yahfa/HookMain.java +++ b/core/src/main/java/org/lsposed/lspd/core/yahfa/HookMain.java @@ -27,6 +27,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; @@ -51,10 +52,8 @@ public class HookMain { // backup is just a placeholder and the constraint could be less strict checkCompatibleMethods(target, backup, "Backup"); } - if(!Yahfa.backupAndHookNative(target, hook, backup)){ + if (!Yahfa.backupAndHookNative(target, hook, backup, Proxy.isProxyClass(target.getDeclaringClass()))) { throw new RuntimeException("Failed to hook " + target + " with " + hook); - } else { - Yahfa.recordHooked(target); } } diff --git a/core/src/main/java/org/lsposed/lspd/nativebridge/Yahfa.java b/core/src/main/java/org/lsposed/lspd/nativebridge/Yahfa.java index 4105f2f4..7dd091da 100644 --- a/core/src/main/java/org/lsposed/lspd/nativebridge/Yahfa.java +++ b/core/src/main/java/org/lsposed/lspd/nativebridge/Yahfa.java @@ -25,15 +25,13 @@ import java.lang.reflect.Method; public class Yahfa { - public static native boolean backupAndHookNative(Executable target, Method hook, Method backup); + public static native boolean backupAndHookNative(Executable target, Method hook, Method backup, boolean isProxy); // JNI.ToReflectedMethod() could return either Method or Constructor public static native Executable findMethodNative(Class targetClass, String methodName, String methodSig); public static native void init(int sdkVersion); - public static native void recordHooked(Executable member); - public static native boolean isHooked(Executable member); public static native Class buildHooker(ClassLoader appClassLoader, char returnType, char[] params, String methodName); diff --git a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java index 72cbd23d..2ef9d316 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java @@ -58,8 +58,6 @@ public class ConfigFileManager { DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(Utils.getZoneId()); @SuppressWarnings("FieldCanBeLocal") private static FileLocker locker = null; - @SuppressWarnings("FieldCanBeLocal") - private static AssetManager am = null; private static Resources res = null; private static ParcelFileDescriptor fd = null; @@ -119,7 +117,7 @@ public class ConfigFileManager { private static void loadRes() { if (res != null) return; try { - am = AssetManager.class.newInstance(); + var am = AssetManager.class.newInstance(); //noinspection JavaReflectionMemberAccess DiscouragedPrivateApi Method addAssetPath = AssetManager.class.getDeclaredMethod("addAssetPath", String.class); addAssetPath.setAccessible(true);