From 5bab7a0e3894395131f0d8205c8c1fbb67a20f1b Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sat, 19 Feb 2022 03:47:26 +0800 Subject: [PATCH] Add clang-format and reformat all files --- library/jni/.clang-format | 18 + library/jni/Android.mk | 4 +- library/jni/art/mirror/class.hpp | 14 +- library/jni/art/runtime/art_method.hpp | 79 ++--- library/jni/art/runtime/class_linker.hpp | 99 +++--- library/jni/art/runtime/gc/collector_type.hpp | 2 +- library/jni/art/runtime/gc/gc_cause.hpp | 5 +- .../runtime/gc/scoped_gc_critical_section.hpp | 20 +- .../jni/art/runtime/jit/jit_code_cache.hpp | 30 +- library/jni/include/lsplant.hpp | 42 +-- library/jni/include/utils/hook_helper.hpp | 137 ++++---- library/jni/include/utils/jni_helper.hpp | 332 ++++++++---------- library/jni/lsplant.cc | 283 +++++++-------- 13 files changed, 504 insertions(+), 561 deletions(-) create mode 100644 library/jni/.clang-format diff --git a/library/jni/.clang-format b/library/jni/.clang-format new file mode 100644 index 0000000..e2c8b95 --- /dev/null +++ b/library/jni/.clang-format @@ -0,0 +1,18 @@ +--- +BasedOnStyle: Google +IndentWidth: 4 +UseCRLF: false +UseTab: false +--- +Language: Cpp +DerivePointerAlignment: true +PointerAlignment: Right +ColumnLimit: 100 +AlignEscapedNewlines: Right +Cpp11BracedListStyle: true +Standard: Latest +# IndentAccessModifiers: false +IndentCaseLabels: false +IndentExternBlock: false +AccessModifierOffset: -4 +# EmptyLineBeforeAccessModifier: true diff --git a/library/jni/Android.mk b/library/jni/Android.mk index c4b36b6..6bc9c81 100644 --- a/library/jni/Android.mk +++ b/library/jni/Android.mk @@ -3,7 +3,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := lsplant -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/external/dex_builder/include LOCAL_SRC_FILES := lsplant.cc LOCAL_EXPORT_C_INCLUDES:= $(LOCAL_PATH)/include LOCAL_SHARED_LIBRARIES := dex_builder @@ -16,7 +16,7 @@ include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := lsplant_static -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/external/dex_builder/include LOCAL_SRC_FILES := lsplant.cc LOCAL_EXPORT_C_INCLUDES:= $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := dex_builder_static diff --git a/library/jni/art/mirror/class.hpp b/library/jni/art/mirror/class.hpp index 6820b10..96f21c8 100644 --- a/library/jni/art/mirror/class.hpp +++ b/library/jni/art/mirror/class.hpp @@ -1,14 +1,13 @@ #pragma once +#include "art/thread.hpp" #include "common.hpp" namespace lsplant::art { namespace dex { -class ClassDef { - -}; -} +class ClassDef {}; +} // namespace dex namespace mirror { @@ -71,8 +70,7 @@ public: } const dex::ClassDef *GetClassDef() { - if (GetClassDefSym) - return GetClassDef(this); + if (GetClassDefSym) return GetClassDef(this); return nullptr; } @@ -94,5 +92,5 @@ private: inline static bool is_unsigned = false; }; -} -} +} // namespace mirror +} // namespace lsplant::art diff --git a/library/jni/art/runtime/art_method.hpp b/library/jni/art/runtime/art_method.hpp index 150fe54..b8dbd38 100644 --- a/library/jni/art/runtime/art_method.hpp +++ b/library/jni/art/runtime/art_method.hpp @@ -1,15 +1,18 @@ #pragma once -#include "common.hpp" #include "art/mirror/class.hpp" +#include "common.hpp" namespace lsplant::art { class ArtMethod { CREATE_MEM_FUNC_SYMBOL_ENTRY(std::string, PrettyMethod, ArtMethod *thiz, bool with_signature) { - if (thiz == nullptr) [[unlikely]] return "null"; - else if (PrettyMethodSym) [[likely]] return PrettyMethodSym(thiz, with_signature); - else return "null sym"; + if (thiz == nullptr) [[unlikely]] + return "null"; + else if (PrettyMethodSym) [[likely]] + return PrettyMethodSym(thiz, with_signature); + else + return "null sym"; } public: @@ -36,43 +39,35 @@ public: } } - bool IsStatic() { - return GetAccessFlags() & kAccStatic; - } + bool IsStatic() { return GetAccessFlags() & kAccStatic; } - bool IsNative() { - return GetAccessFlags() & kAccNative; - } + bool IsNative() { return GetAccessFlags() & kAccNative; } - void CopyFrom(const ArtMethod *other) { - memcpy(this, other, art_method_size); - } + void CopyFrom(const ArtMethod *other) { memcpy(this, other, art_method_size); } void SetEntryPoint(void *entry_point) { - *reinterpret_cast(reinterpret_cast(this) + - entry_point_offset) = entry_point; + *reinterpret_cast(reinterpret_cast(this) + entry_point_offset) = + entry_point; } void *GetEntryPoint() { - return *reinterpret_cast(reinterpret_cast(this) + - entry_point_offset); + return *reinterpret_cast(reinterpret_cast(this) + entry_point_offset); } void *GetData() { - return *reinterpret_cast(reinterpret_cast(this) + - data_offset); + return *reinterpret_cast(reinterpret_cast(this) + data_offset); } uint32_t GetAccessFlags() { - return (reinterpret_cast *>( - reinterpret_cast(this) + access_flags_offset))->load( - std::memory_order_relaxed); + return (reinterpret_cast *>(reinterpret_cast(this) + + access_flags_offset)) + ->load(std::memory_order_relaxed); } void SetAccessFlags(uint32_t flags) { - return (reinterpret_cast *>( - reinterpret_cast(this) + access_flags_offset))->store( - flags, std::memory_order_relaxed); + return (reinterpret_cast *>(reinterpret_cast(this) + + access_flags_offset)) + ->store(flags, std::memory_order_relaxed); } std::string PrettyMethod(bool with_signature = true) { @@ -90,8 +85,8 @@ public: return false; } - if (art_method_field = JNI_GetFieldID(env, executable, "artMethod", - "J"); !art_method_field) { + if (art_method_field = JNI_GetFieldID(env, executable, "artMethod", "J"); + !art_method_field) { LOGE("Failed to find artMethod field"); return false; } @@ -105,8 +100,8 @@ public: static_assert(std::is_same_v); jmethodID get_declared_constructors = JNI_GetMethodID(env, clazz, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;"); - auto constructors = JNI_Cast( - JNI_CallObjectMethod(env, throwable, get_declared_constructors)); + auto constructors = + JNI_Cast(JNI_CallObjectMethod(env, throwable, get_declared_constructors)); auto length = JNI_GetArrayLength(env, constructors); if (length < 2) { LOGE("Throwable has less than 2 constructors"); @@ -130,7 +125,7 @@ public: LOGD("ArtMethod::data offset: %zu", data_offset); if (auto access_flags_field = JNI_GetFieldID(env, executable, "accessFlags", "I"); - access_flags_field) { + access_flags_field) { uint32_t real_flags = JNI_GetIntField(env, first_ctor, access_flags_field); for (size_t i = 0; i < art_method_size; i += sizeof(uint32_t)) { if (*reinterpret_cast(reinterpret_cast(first) + i) == @@ -151,26 +146,24 @@ public: if (sdk_int < __ANDROID_API_Q__) kAccFastInterpreterToInterpreterInvoke = 0; get_method_shorty_symbol = GetArtSymbol( - info.art_symbol_resolver, - "_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID"); + info.art_symbol_resolver, "_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID"); if (!get_method_shorty_symbol) return false; return true; } static const char *GetMethodShorty(_JNIEnv *env, _jmethodID *method) { - if (get_method_shorty_symbol) [[likely]] return get_method_shorty_symbol(env, method); + if (get_method_shorty_symbol) [[likely]] + return get_method_shorty_symbol(env, method); return nullptr; } - static size_t GetEntryPointOffset() { - return entry_point_offset; - } + static size_t GetEntryPointOffset() { return entry_point_offset; } - constexpr static uint32_t kAccPublic = 0x0001; // class, field, method, ic - constexpr static uint32_t kAccPrivate = 0x0002; // field, method, ic + constexpr static uint32_t kAccPublic = 0x0001; // class, field, method, ic + constexpr static uint32_t kAccPrivate = 0x0002; // field, method, ic constexpr static uint32_t kAccProtected = 0x0004; // field, method, ic - constexpr static uint32_t kAccStatic = 0x0008; // field, method, ic - constexpr static uint32_t kAccNative = 0x0100; // method + constexpr static uint32_t kAccStatic = 0x0008; // field, method, ic + constexpr static uint32_t kAccNative = 0x0100; // method private: inline static jfieldID art_method_field = nullptr; @@ -182,8 +175,8 @@ private: inline static uint32_t kAccPreCompiled = 0x00200000; inline static uint32_t kAccCompileDontBother = 0x02000000; - inline static const char * - (*get_method_shorty_symbol)(_JNIEnv *env, _jmethodID *method) = nullptr; + inline static const char *(*get_method_shorty_symbol)(_JNIEnv *env, + _jmethodID *method) = nullptr; }; -} +} // namespace lsplant::art diff --git a/library/jni/art/runtime/class_linker.hpp b/library/jni/art/runtime/class_linker.hpp index ad2e5a0..bdb041f 100644 --- a/library/jni/art/runtime/class_linker.hpp +++ b/library/jni/art/runtime/class_linker.hpp @@ -1,24 +1,21 @@ #pragma once -#include "art/runtime/art_method.hpp" #include "art/mirror/class.hpp" +#include "art/runtime/art_method.hpp" #include "art/thread.hpp" #include "common.hpp" namespace lsplant::art { - class ClassLinker { - private: - CREATE_MEM_FUNC_SYMBOL_ENTRY( - void, SetEntryPointsToInterpreter, ClassLinker *thiz, ArtMethod *art_method) { + CREATE_MEM_FUNC_SYMBOL_ENTRY(void, SetEntryPointsToInterpreter, ClassLinker *thiz, + ArtMethod *art_method) { if (SetEntryPointsToInterpreterSym) [[likely]] { SetEntryPointsToInterpreterSym(thiz, art_method); } } - [[gnu::always_inline]] - static void MaybeDelayHook(mirror::Class *clazz) { + [[gnu::always_inline]] static void MaybeDelayHook(mirror::Class *clazz) { const auto *class_def = clazz->GetClassDef(); bool should_intercept = class_def && IsPending(class_def); if (should_intercept) [[unlikely]] { @@ -28,60 +25,58 @@ private: } CREATE_MEM_HOOK_STUB_ENTRY( - "_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE", - void, FixupStaticTrampolines, (ClassLinker * thiz, mirror::Class * clazz), { - backup(thiz, clazz); - MaybeDelayHook(clazz); - }); + "_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE", void, + FixupStaticTrampolines, (ClassLinker * thiz, mirror::Class *clazz), { + backup(thiz, clazz); + MaybeDelayHook(clazz); + }); CREATE_MEM_HOOK_STUB_ENTRY( - "_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6ThreadENS_6ObjPtrINS_6mirror5ClassEEE", - void, FixupStaticTrampolinesWithThread, - (ClassLinker * thiz, Thread * self, mirror::Class * clazz), { - backup(thiz, self, clazz); - MaybeDelayHook(clazz); - }); + "_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6ThreadENS_6ObjPtrINS_6mirror5ClassEEE", + void, FixupStaticTrampolinesWithThread, + (ClassLinker * thiz, Thread *self, mirror::Class *clazz), { + backup(thiz, self, clazz); + MaybeDelayHook(clazz); + }); CREATE_MEM_HOOK_STUB_ENTRY( - "_ZN3art11ClassLinker20MarkClassInitializedEPNS_6ThreadENS_6HandleINS_6mirror5ClassEEE", - void*, MarkClassInitialized, (ClassLinker * thiz, Thread * self, uint32_t * clazz_ptr), - { - void *result = backup(thiz, self, clazz_ptr); - auto clazz = reinterpret_cast(*clazz_ptr); - MaybeDelayHook(clazz); - return result; - }); + "_ZN3art11ClassLinker20MarkClassInitializedEPNS_6ThreadENS_6HandleINS_6mirror5ClassEEE", + void *, MarkClassInitialized, (ClassLinker * thiz, Thread *self, uint32_t *clazz_ptr), { + void *result = backup(thiz, self, clazz_ptr); + auto clazz = reinterpret_cast(*clazz_ptr); + MaybeDelayHook(clazz); + return result; + }); CREATE_HOOK_STUB_ENTRY( - "_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv", - bool, ShouldUseInterpreterEntrypoint, (ArtMethod * art_method, const void *quick_code), - { - if (quick_code != nullptr && - (IsHooked(art_method) || IsPending(art_method))) [[unlikely]] { - return false; - } - return backup(art_method, quick_code); - }); + "_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv", bool, + ShouldUseInterpreterEntrypoint, (ArtMethod * art_method, const void *quick_code), { + if (quick_code != nullptr && (IsHooked(art_method) || IsPending(art_method))) + [[unlikely]] { + return false; + } + return backup(art_method, quick_code); + }); - CREATE_FUNC_SYMBOL_ENTRY(void, art_quick_to_interpreter_bridge, void*) {} + CREATE_FUNC_SYMBOL_ENTRY(void, art_quick_to_interpreter_bridge, void *) {} - CREATE_FUNC_SYMBOL_ENTRY(void, art_quick_generic_jni_trampoline, void*) {} + CREATE_FUNC_SYMBOL_ENTRY(void, art_quick_generic_jni_trampoline, void *) {} - CREATE_HOOK_STUB_ENTRY( - "_ZN3art11interpreter29ShouldStayInSwitchInterpreterEPNS_9ArtMethodE", - bool, ShouldStayInSwitchInterpreter, (ArtMethod * art_method), { - if (IsHooked(art_method) || IsPending(art_method)) [[unlikely]] { - return false; - } - return backup(art_method); - }); + CREATE_HOOK_STUB_ENTRY("_ZN3art11interpreter29ShouldStayInSwitchInterpreterEPNS_9ArtMethodE", + bool, ShouldStayInSwitchInterpreter, (ArtMethod * art_method), { + if (IsHooked(art_method) || IsPending(art_method)) [[unlikely]] { + return false; + } + return backup(art_method); + }); public: static bool Init(const HookHandler &handler) { int api_level = GetAndroidApiLevel(); - if (!RETRIEVE_MEM_FUNC_SYMBOL(SetEntryPointsToInterpreter, - "_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE")) { + if (!RETRIEVE_MEM_FUNC_SYMBOL( + SetEntryPointsToInterpreter, + "_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE")) { return false; } @@ -123,15 +118,14 @@ public: return true; } - [[gnu::always_inline]] - static bool SetEntryPointsToInterpreter(ArtMethod *art_method) { + [[gnu::always_inline]] static bool SetEntryPointsToInterpreter(ArtMethod *art_method) { if (art_quick_to_interpreter_bridgeSym && art_quick_generic_jni_trampolineSym) [[likely]] { if (art_method->GetAccessFlags() & ArtMethod::kAccNative) [[unlikely]] { art_method->SetEntryPoint( - reinterpret_cast(art_quick_generic_jni_trampolineSym)); + reinterpret_cast(art_quick_generic_jni_trampolineSym)); } else { art_method->SetEntryPoint( - reinterpret_cast(art_quick_to_interpreter_bridgeSym)); + reinterpret_cast(art_quick_to_interpreter_bridgeSym)); } return true; } @@ -141,6 +135,5 @@ public: } return false; } - }; -} +} // namespace lsplant::art diff --git a/library/jni/art/runtime/gc/collector_type.hpp b/library/jni/art/runtime/gc/collector_type.hpp index 40958a6..74610e4 100644 --- a/library/jni/art/runtime/gc/collector_type.hpp +++ b/library/jni/art/runtime/gc/collector_type.hpp @@ -39,4 +39,4 @@ enum CollectorType { // Fake collector type for ScopedGCCriticalSection kCollectorTypeCriticalSection, }; -} // namespace gc +} // namespace lsplant::art::gc diff --git a/library/jni/art/runtime/gc/gc_cause.hpp b/library/jni/art/runtime/gc/gc_cause.hpp index 17ac51c..12d53fe 100644 --- a/library/jni/art/runtime/gc/gc_cause.hpp +++ b/library/jni/art/runtime/gc/gc_cause.hpp @@ -16,7 +16,8 @@ enum GcCause { kGcCauseForNativeAlloc, // GC triggered for a collector transition. kGcCauseCollectorTransition, - // Not a real GC cause, used when we disable moving GC (currently for GetPrimitiveArrayCritical). + // Not a real GC cause, used when we disable moving GC (currently for + // GetPrimitiveArrayCritical). kGcCauseDisableMovingGc, // Not a real GC cause, used when we trim the heap. kGcCauseTrim, @@ -41,4 +42,4 @@ enum GcCause { // GC cause for the profile saver. kGcCauseProfileSaver, }; -} // namespace art +} // namespace lsplant::art::gc diff --git a/library/jni/art/runtime/gc/scoped_gc_critical_section.hpp b/library/jni/art/runtime/gc/scoped_gc_critical_section.hpp index 8d91650..4136f98 100644 --- a/library/jni/art/runtime/gc/scoped_gc_critical_section.hpp +++ b/library/jni/art/runtime/gc/scoped_gc_critical_section.hpp @@ -1,10 +1,9 @@ #pragma once -#include "gc_cause.hpp" -#include "collector_type.hpp" #include "art/thread.hpp" +#include "collector_type.hpp" #include "common.hpp" - +#include "gc_cause.hpp" namespace lsplant::art::gc { @@ -17,13 +16,15 @@ private: class ScopedGCCriticalSection { CREATE_MEM_FUNC_SYMBOL_ENTRY(void, constructor, ScopedGCCriticalSection *thiz, Thread *self, GcCause cause, CollectorType collector_type) { - if (thiz == nullptr) [[unlikely]] return; + if (thiz == nullptr) [[unlikely]] + return; if (constructorSym) [[likely]] return constructorSym(thiz, self, cause, collector_type); } CREATE_MEM_FUNC_SYMBOL_ENTRY(void, destructor, ScopedGCCriticalSection *thiz) { - if (thiz == nullptr) [[unlikely]] return; + if (thiz == nullptr) [[unlikely]] + return; if (destructorSym) [[likely]] return destructorSym(thiz); } @@ -33,13 +34,12 @@ public: constructor(this, self, cause, collector_type); } - ~ScopedGCCriticalSection() { - destructor(this); - } + ~ScopedGCCriticalSection() { destructor(this); } static bool Init(const HookHandler &handler) { if (!RETRIEVE_MEM_FUNC_SYMBOL(constructor, - "_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_7GcCauseENS0_13CollectorTypeE")) { + "_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_" + "7GcCauseENS0_13CollectorTypeE")) { return false; } if (!RETRIEVE_MEM_FUNC_SYMBOL(destructor, "_ZN3art2gc23ScopedGCCriticalSectionD2Ev")) { @@ -52,4 +52,4 @@ private: [[maybe_unused]] GCCriticalSection critical_section_; [[maybe_unused]] const char *old_no_suspend_reason_; }; -} +} // namespace lsplant::art::gc diff --git a/library/jni/art/runtime/jit/jit_code_cache.hpp b/library/jni/art/runtime/jit/jit_code_cache.hpp index 26976de..ddd4d3c 100644 --- a/library/jni/art/runtime/jit/jit_code_cache.hpp +++ b/library/jni/art/runtime/jit/jit_code_cache.hpp @@ -5,25 +5,25 @@ namespace lsplant::art::jit { class JitCodeCache { CREATE_MEM_FUNC_SYMBOL_ENTRY(void, MoveObsoleteMethod, JitCodeCache *thiz, - ArtMethod * old_method, ArtMethod * new_method) { - if (MoveObsoleteMethodSym) - [[likely]] MoveObsoleteMethodSym(thiz, old_method, new_method); + ArtMethod *old_method, ArtMethod *new_method) { + if (MoveObsoleteMethodSym) [[likely]] + MoveObsoleteMethodSym(thiz, old_method, new_method); } - CREATE_MEM_HOOK_STUB_ENTRY( - "_ZN3art3jit12JitCodeCache19GarbageCollectCacheEPNS_6ThreadE", - void, GarbageCollectCache, (JitCodeCache * thiz, Thread * self), { - LOGD("Before jit cache gc, moving hooked methods"); - for (auto[target, backup] : GetJitMovements()) { - MoveObsoleteMethod(thiz, target, backup); - } - backup(thiz, self); - }); + CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art3jit12JitCodeCache19GarbageCollectCacheEPNS_6ThreadE", void, + GarbageCollectCache, (JitCodeCache * thiz, Thread *self), { + LOGD("Before jit cache gc, moving hooked methods"); + for (auto [target, backup] : GetJitMovements()) { + MoveObsoleteMethod(thiz, target, backup); + } + backup(thiz, self); + }); public: static bool Init(const HookHandler &handler) { - if (!RETRIEVE_MEM_FUNC_SYMBOL(MoveObsoleteMethod, - "_ZN3art3jit12JitCodeCache18MoveObsoleteMethodEPNS_9ArtMethodES3_")) { + if (!RETRIEVE_MEM_FUNC_SYMBOL( + MoveObsoleteMethod, + "_ZN3art3jit12JitCodeCache18MoveObsoleteMethodEPNS_9ArtMethodES3_")) { return false; } if (!HookSyms(handler, GarbageCollectCache)) { @@ -32,4 +32,4 @@ public: return true; } }; -} +} // namespace lsplant::art::jit diff --git a/library/jni/include/lsplant.hpp b/library/jni/include/lsplant.hpp index c48bee8..04fae6b 100644 --- a/library/jni/include/lsplant.hpp +++ b/library/jni/include/lsplant.hpp @@ -1,6 +1,7 @@ #pragma once #include + #include /// \namespace namespace of LSPlant @@ -58,8 +59,8 @@ struct InitInfo { /// \return Indicate whether initialization succeed. Behavior is undefined if calling other /// LSPlant interfaces before initialization or after a fail initialization. /// \see InitInfo. -[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] -bool Init(JNIEnv *env, const InitInfo &info); +[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] bool Init(JNIEnv *env, + const InitInfo &info); /// \brief Hook a Java method by providing the \p target_method together with the context object /// \p hooker_object and its callback \p callback_method. @@ -93,9 +94,10 @@ bool Init(JNIEnv *env, const InitInfo &info); /// simultaneously call on this function with the same \p target_method does not guarantee only one /// will success. If you call this with different \p hooker_object on the same target_method /// simultaneously, the behavior is undefined. -[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] -jobject -Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback_method); +[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] jobject Hook(JNIEnv *env, + jobject target_method, + jobject hooker_object, + jobject callback_method); /// \brief Unhook a Java function that is previously hooked. /// \param[in] env The Java environment. @@ -104,8 +106,8 @@ Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback /// \note Calling \p backup (the return method of #Hook()) after unhooking is undefined behavior. /// Please read #Hook()'s note for more details. /// \see Hook() -[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] -bool UnHook(JNIEnv *env, jobject target_method); +[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] bool UnHook(JNIEnv *env, + jobject target_method); /// \brief Check if a Java function is hooked by LSPlant or not /// \param[in] env The Java environment. @@ -113,33 +115,33 @@ bool UnHook(JNIEnv *env, jobject target_method); /// \return If \p method hooked, ture; otherwise, false. /// Please read #Hook()'s note for more details. /// \see Hook() -[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] -bool IsHooked(JNIEnv *env, jobject method); +[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] bool IsHooked(JNIEnv *env, + jobject method); /// \brief Deoptimize a method to avoid hooked callee not being called because of inline /// \param[in] env The Java environment. /// \param[in] method The method to deoptimize. By deoptimizing the method, the method will back all /// callee without inlining. For example, if you hooked a short method B that is invoked by method /// A, and you find that your callback to B is not invoked after hooking, then it may mean A has -/// inlined B inside its method body. To force A to call your hooked B, you can deoptimize A and then -/// your hook can take effect. Generally, you need to find all the callers of your hooked callee -/// and that can be hardly achieve. Use this function if you are sure the deoptimized callers +/// inlined B inside its method body. To force A to call your hooked B, you can deoptimize A and +/// then your hook can take effect. Generally, you need to find all the callers of your hooked +/// callee and that can be hardly achieve. Use this function if you are sure the deoptimized callers /// are all you need. Otherwise, it would be better to change the hook point or to deoptimize the /// whole app manually (by simple reinstall the app without uninstalled). /// \return Indicate whether the deoptimizing succeed or not. /// \note It is safe to call deoptimizing on a hooked method because the deoptimization will /// perform on the backup method instead. -[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] -bool Deoptimize(JNIEnv *env, jobject method); +[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] bool Deoptimize(JNIEnv *env, + jobject method); -/// \brief Get the registered native function pointer of a native function. It helps user to hook native -/// methods directly by backing up the native function pointer this function returns and +/// \brief Get the registered native function pointer of a native function. It helps user to hook +/// native methods directly by backing up the native function pointer this function returns and /// env->registerNatives another native function pointer. /// \param[in] env The Java environment. /// \param[in] method The native method to get the native function pointer. /// \return The native function pointer the \p method previously registered. If it has not been /// registered or it is not a native method, null is returned instead. -[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] -void *GetNativeFunction(JNIEnv *env, jobject method); -} -} // namespace lsplant +[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] void *GetNativeFunction( + JNIEnv *env, jobject method); +} // namespace v1 +} // namespace lsplant diff --git a/library/jni/include/utils/hook_helper.hpp b/library/jni/include/utils/hook_helper.hpp index bdb6139..b3daa9b 100644 --- a/library/jni/include/utils/hook_helper.hpp +++ b/library/jni/include/utils/hook_helper.hpp @@ -1,82 +1,74 @@ #pragma once -#include "logging.hpp" -#include "jni_helper.hpp" -#include "hook_helper.hpp" #include +#include "hook_helper.hpp" +#include "jni_helper.hpp" +#include "logging.hpp" + #define CONCATENATE(a, b) a##b -#define CREATE_HOOK_STUB_ENTRY(SYM, RET, FUNC, PARAMS, DEF) \ - inline static struct : public lsplant::Hooker{ \ - inline static RET replace PARAMS DEF \ - } FUNC +#define CREATE_HOOK_STUB_ENTRY(SYM, RET, FUNC, PARAMS, DEF) \ + inline static struct : public lsplant::Hooker{ \ + inline static RET replace PARAMS DEF} FUNC -#define CREATE_MEM_HOOK_STUB_ENTRY(SYM, RET, FUNC, PARAMS, DEF) \ - inline static struct : public lsplant::MemHooker{ \ - inline static RET replace PARAMS DEF \ - } FUNC +#define CREATE_MEM_HOOK_STUB_ENTRY(SYM, RET, FUNC, PARAMS, DEF) \ + inline static struct : public lsplant::MemHooker{ \ + inline static RET replace PARAMS DEF} FUNC -#define RETRIEVE_FUNC_SYMBOL(name, ...) \ - (name##Sym = reinterpret_cast( \ - lsplant::Dlsym(handler, __VA_ARGS__))) +#define RETRIEVE_FUNC_SYMBOL(name, ...) \ + (name##Sym = reinterpret_cast(lsplant::Dlsym(handler, __VA_ARGS__))) -#define RETRIEVE_MEM_FUNC_SYMBOL(name, ...) \ - (name##Sym = reinterpret_cast( \ - lsplant::Dlsym(handler, __VA_ARGS__))) +#define RETRIEVE_MEM_FUNC_SYMBOL(name, ...) \ + (name##Sym = reinterpret_cast(lsplant::Dlsym(handler, __VA_ARGS__))) -#define RETRIEVE_FIELD_SYMBOL(name, ...) \ - (name = reinterpret_cast(lsplant::Dlsym(handler, __VA_ARGS__))) +#define RETRIEVE_FIELD_SYMBOL(name, ...) \ + (name = reinterpret_cast(lsplant::Dlsym(handler, __VA_ARGS__))) -#define CREATE_FUNC_SYMBOL_ENTRY(ret, func, ...) \ - typedef ret (*func##Type)(__VA_ARGS__); \ - inline static ret (*func##Sym)(__VA_ARGS__); \ - inline static ret func(__VA_ARGS__) +#define CREATE_FUNC_SYMBOL_ENTRY(ret, func, ...) \ + typedef ret (*func##Type)(__VA_ARGS__); \ + inline static ret (*func##Sym)(__VA_ARGS__); \ + inline static ret func(__VA_ARGS__) -#define CREATE_MEM_FUNC_SYMBOL_ENTRY(ret, func, thiz, ...) \ - using func##Type = lsplant::MemberFunction; \ - inline static func##Type func##Sym; \ - inline static ret func(thiz, ## __VA_ARGS__) +#define CREATE_MEM_FUNC_SYMBOL_ENTRY(ret, func, thiz, ...) \ + using func##Type = lsplant::MemberFunction; \ + inline static func##Type func##Sym; \ + inline static ret func(thiz, ##__VA_ARGS__) namespace lsplant { using HookHandler = InitInfo; -template +template struct tstring : public std::integer_sequence { - inline constexpr static const char *c_str() { - return str_; - } + inline constexpr static const char *c_str() { return str_; } - inline constexpr operator std::string_view() const { - return { c_str(), sizeof...(chars) }; - } + inline constexpr operator std::string_view() const { return {c_str(), sizeof...(chars)}; } private: - inline static constexpr char str_[]{ chars..., '\0' }; + inline static constexpr char str_[]{chars..., '\0'}; }; -template -inline constexpr tstring operator ""_tstr() { +template +inline constexpr tstring operator""_tstr() { return {}; } - -template -inline constexpr tstring -operator+(const tstring &, const tstring &) { +template +inline constexpr tstring operator+(const tstring &, const tstring &) { return {}; } -template +template inline constexpr auto operator+(const std::string &a, const tstring &) { - char b[]{ as..., '\0' }; + char b[]{as..., '\0'}; return a + b; } -template +template inline constexpr auto operator+(const tstring &, const std::string &b) { - char a[]{ as..., '\0' }; + char a[]{as..., '\0'}; return a + b; } @@ -84,9 +76,9 @@ inline void *Dlsym(const HookHandler &handle, const char *name) { return handle.art_symbol_resolver(name); } -template -requires (std::is_same_v || std::is_same_v) -inline static auto memfun_cast(Return (*func)(T *, Args...)) { +template +requires(std::is_same_v || + std::is_same_v) inline static auto memfun_cast(Return (*func)(T *, Args...)) { union { Return (Class::*f)(Args...); @@ -94,28 +86,31 @@ inline static auto memfun_cast(Return (*func)(T *, Args...)) { decltype(func) p; std::ptrdiff_t adj; } data; - } u{ .data = { func, 0 }}; + } u{.data = {func, 0}}; static_assert(sizeof(u.f) == sizeof(u.data), "Try different T"); return u.f; } -template T, typename Return, typename... Args> +template T, typename Return, typename... Args> inline auto memfun_cast(Return (*func)(T *, Args...)) { return memfun_cast(func); } -template +template class MemberFunction; -template +template class MemberFunction { using SelfType = MemberFunction; using ThisType = std::conditional_t, SelfType, This>; - using MemFunType = Return(ThisType::*)(Args...); + using MemFunType = Return (ThisType::*)(Args...); + public: using FunType = Return (*)(This *, Args...); + private: MemFunType f_ = nullptr; + public: MemberFunction() = default; @@ -127,51 +122,49 @@ public: return (reinterpret_cast(thiz)->*f_)(std::forward(args)...); } - inline operator bool() { - return f_ != nullptr; - } + inline operator bool() { return f_ != nullptr; } }; // deduction guide -template -MemberFunction(Return(*f)(This *, Args...)) -> MemberFunction; +template +MemberFunction(Return (*f)(This *, Args...)) -> MemberFunction; -template -MemberFunction(Return(This::*f)(Args...)) -> MemberFunction; +template +MemberFunction(Return (This::*f)(Args...)) -> MemberFunction; -template +template struct Hooker; -template +template struct Hooker> { inline static Ret (*backup)(Args...) = nullptr; inline static constexpr std::string_view sym = tstring{}; }; -template +template struct MemHooker; -template +template struct MemHooker> { inline static MemberFunction backup; inline static constexpr std::string_view sym = tstring{}; }; -template +template concept HookerType = requires(T a) { a.backup; a.replace; }; -template +template inline static bool HookSymNoHandle(const HookHandler &handler, void *original, T &arg) { if (original) { - if constexpr(is_instance_v) { + if constexpr (is_instance_v) { void *backup = handler.inline_hooker(original, reinterpret_cast(arg.replace)); arg.backup = reinterpret_cast(backup); } else { - arg.backup = reinterpret_cast(handler.inline_hooker(original, - reinterpret_cast(arg.replace))); + arg.backup = reinterpret_cast( + handler.inline_hooker(original, reinterpret_cast(arg.replace))); } return true; } else { @@ -179,13 +172,13 @@ inline static bool HookSymNoHandle(const HookHandler &handler, void *original, T } } -template +template inline static bool HookSym(const HookHandler &handler, T &arg) { auto original = handler.art_symbol_resolver(arg.sym); return HookSymNoHandle(handler, original, arg); } -template +template inline static bool HookSyms(const HookHandler &handle, T &first, Args &...rest) { if (!(HookSym(handle, first) || ... || HookSym(handle, rest))) { LOGW("Hook Fails: %*s", static_cast(first.sym.size()), first.sym.data()); @@ -194,4 +187,4 @@ inline static bool HookSyms(const HookHandler &handle, T &first, Args &...rest) return true; } -} +} // namespace lsplant diff --git a/library/jni/include/utils/jni_helper.hpp b/library/jni/include/utils/jni_helper.hpp index 3e4e4d7..4ef9c47 100644 --- a/library/jni/include/utils/jni_helper.hpp +++ b/library/jni/include/utils/jni_helper.hpp @@ -4,49 +4,43 @@ #pragma once #include + #include + #include "logging.hpp" -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - void operator=(const TypeName&) = delete +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName &) = delete; \ + void operator=(const TypeName &) = delete namespace lsplant { -template class> -struct is_instance : public std::false_type { -}; +template class> +struct is_instance : public std::false_type {}; -template class U> -struct is_instance, U> : public std::true_type { -}; +template class U> +struct is_instance, U> : public std::true_type {}; -template class U> +template class U> inline constexpr bool is_instance_v = is_instance::value; -template +template concept JObject = std::is_base_of_v, std::remove_pointer_t>; -template +template class ScopedLocalRef { public: using BaseType [[maybe_unused]] = T; - ScopedLocalRef(JNIEnv *env, T localRef) : env_(env), local_ref_(localRef) { - } + ScopedLocalRef(JNIEnv *env, T localRef) : env_(env), local_ref_(localRef) {} - ScopedLocalRef(ScopedLocalRef &&s) noexcept: env_(s.env_), local_ref_(s.release()) { - } + ScopedLocalRef(ScopedLocalRef &&s) noexcept : env_(s.env_), local_ref_(s.release()) {} - template - ScopedLocalRef(ScopedLocalRef &&s) noexcept: env_(s.env_), local_ref_((T) s.release()) { - } + template + ScopedLocalRef(ScopedLocalRef &&s) noexcept : env_(s.env_), local_ref_((T)s.release()) {} - explicit ScopedLocalRef(JNIEnv *env) noexcept: env_(env), local_ref_(nullptr) { - } + explicit ScopedLocalRef(JNIEnv *env) noexcept : env_(env), local_ref_(nullptr) {} - ~ScopedLocalRef() { - reset(); - } + ~ScopedLocalRef() { reset(); } void reset(T ptr = nullptr) { if (ptr != local_ref_) { @@ -63,13 +57,9 @@ public: return localRef; } - T get() const { - return local_ref_; - } + T get() const { return local_ref_; } - operator T() const { - return local_ref_; - } + operator T() const { return local_ref_; } // We do not expose an empty constructor as it can easily lead to errors // using common idioms, e.g.: @@ -82,16 +72,12 @@ public: return *this; } - operator bool() const { - return local_ref_; - } + operator bool() const { return local_ref_; } - template - friend - class ScopedLocalRef; + template + friend class ScopedLocalRef; - friend - class JUTFString; + friend class JUTFString; private: JNIEnv *env_; @@ -99,70 +85,65 @@ private: DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); }; - class JNIScopeFrame { JNIEnv *env_; -public: - JNIScopeFrame(JNIEnv *env, jint size) : env_(env) { - env_->PushLocalFrame(size); - } - ~JNIScopeFrame() { - env_->PopLocalFrame(nullptr); - } +public: + JNIScopeFrame(JNIEnv *env, jint size) : env_(env) { env_->PushLocalFrame(size); } + + ~JNIScopeFrame() { env_->PopLocalFrame(nullptr); } }; -template +template concept ScopeOrRaw = std::is_convertible_v || - (is_instance_v, ScopedLocalRef> && - std::is_convertible_v::BaseType, U>); + (is_instance_v, ScopedLocalRef> + &&std::is_convertible_v::BaseType, U>); -template +template concept ScopeOrClass = ScopeOrRaw; -template +template concept ScopeOrObject = ScopeOrRaw; inline ScopedLocalRef ClearException(JNIEnv *env) { if (auto exception = env->ExceptionOccurred()) { env->ExceptionClear(); - static jclass log = (jclass) env->NewGlobalRef(env->FindClass("android/util/Log")); - static jmethodID toString = env->GetStaticMethodID(log, "getStackTraceString", - "(Ljava/lang/Throwable;)Ljava/lang/String;"); - auto str = (jstring) env->CallStaticObjectMethod(log, toString, exception); + static jclass log = (jclass)env->NewGlobalRef(env->FindClass("android/util/Log")); + static jmethodID toString = env->GetStaticMethodID( + log, "getStackTraceString", "(Ljava/lang/Throwable;)Ljava/lang/String;"); + auto str = (jstring)env->CallStaticObjectMethod(log, toString, exception); env->DeleteLocalRef(exception); - return { env, str }; + return {env, str}; } - return { env, nullptr }; + return {env, nullptr}; } -template -[[maybe_unused]] -inline auto UnwrapScope(T &&x) { - if constexpr(std::is_same_v, std::string_view>) +template +[[maybe_unused]] inline auto UnwrapScope(T &&x) { + if constexpr (std::is_same_v, std::string_view>) return x.data(); - else if constexpr(is_instance_v, ScopedLocalRef>) + else if constexpr (is_instance_v, ScopedLocalRef>) return x.get(); - else return std::forward(x); + else + return std::forward(x); } -template -[[maybe_unused]] -inline auto WrapScope(JNIEnv *env, T &&x) { - if constexpr(std::is_convertible_v) { +template +[[maybe_unused]] inline auto WrapScope(JNIEnv *env, T &&x) { + if constexpr (std::is_convertible_v) { return ScopedLocalRef(env, std::forward(x)); - } else return x; + } else + return x; } -template -[[maybe_unused]] -inline auto WrapScope(JNIEnv *env, std::tuple &&x, std::index_sequence) { +template +[[maybe_unused]] inline auto WrapScope(JNIEnv *env, std::tuple &&x, + std::index_sequence) { return std::make_tuple(WrapScope(env, std::forward(std::get(x)))...); } -template -[[maybe_unused]] -inline auto WrapScope(JNIEnv *env, std::tuple &&x) { +template +[[maybe_unused]] inline auto WrapScope(JNIEnv *env, std::tuple &&x) { return WrapScope(env, std::forward>(x), std::make_index_sequence()); } @@ -173,17 +154,17 @@ inline auto JNI_NewStringUTF(JNIEnv *env, std::string_view sv) { class JUTFString { public: - inline JUTFString(JNIEnv *env, jstring jstr) : JUTFString(env, jstr, nullptr) { - } + inline JUTFString(JNIEnv *env, jstring jstr) : JUTFString(env, jstr, nullptr) {} - inline JUTFString(const ScopedLocalRef &jstr) : JUTFString(jstr.env_, jstr.local_ref_, - nullptr) { - } + inline JUTFString(const ScopedLocalRef &jstr) + : JUTFString(jstr.env_, jstr.local_ref_, nullptr) {} - inline JUTFString(JNIEnv *env, jstring jstr, const char *default_cstr) : env_(env), - jstr_(jstr) { - if (env_ && jstr_) cstr_ = env_->GetStringUTFChars(jstr, nullptr); - else cstr_ = default_cstr; + inline JUTFString(JNIEnv *env, jstring jstr, const char *default_cstr) + : env_(env), jstr_(jstr) { + if (env_ && jstr_) + cstr_ = env_->GetStringUTFChars(jstr, nullptr); + else + cstr_ = default_cstr; } inline operator const char *() const { return cstr_; } @@ -199,13 +180,13 @@ public: } JUTFString(JUTFString &&other) - : env_(std::move(other.env_)), jstr_(std::move(other.jstr_)), - cstr_(std::move(other.cstr_)) { + : env_(std::move(other.env_)), + jstr_(std::move(other.jstr_)), + cstr_(std::move(other.cstr_)) { other.cstr_ = nullptr; } - JUTFString & - operator=(JUTFString &&other) { + JUTFString &operator=(JUTFString &&other) { if (&other != this) { env_ = std::move(other.env_); jstr_ = std::move(other.jstr_); @@ -225,11 +206,9 @@ private: JUTFString &operator=(const JUTFString &) = delete; }; - -template +template requires(std::is_function_v) -[[maybe_unused]] -inline auto JNI_SafeInvoke(JNIEnv *env, Func JNIEnv::*f, Args &&... args) { + [[maybe_unused]] inline auto JNI_SafeInvoke(JNIEnv *env, Func JNIEnv::*f, Args &&...args) { struct finally { finally(JNIEnv *env) : env_(env) {} @@ -242,185 +221,166 @@ inline auto JNI_SafeInvoke(JNIEnv *env, Func JNIEnv::*f, Args &&... args) { JNIEnv *env_; } _(env); - if constexpr(!std::is_same_v(args)))... >>) + if constexpr (!std::is_same_v(args)))...>>) return WrapScope(env, (env->*f)(UnwrapScope(std::forward(args))...)); - else (env->*f)(UnwrapScope(std::forward(args))...); + else + (env->*f)(UnwrapScope(std::forward(args))...); } -[[maybe_unused]] -inline auto JNI_FindClass(JNIEnv *env, std::string_view name) { +[[maybe_unused]] inline auto JNI_FindClass(JNIEnv *env, std::string_view name) { return JNI_SafeInvoke(env, &JNIEnv::FindClass, name); } -template -[[maybe_unused]] -inline auto JNI_GetObjectClass(JNIEnv *env, const Object &obj) { +template +[[maybe_unused]] inline auto JNI_GetObjectClass(JNIEnv *env, const Object &obj) { return JNI_SafeInvoke(env, &JNIEnv::GetObjectClass, obj); } -template -[[maybe_unused]] -inline auto -JNI_GetFieldID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) { +template +[[maybe_unused]] inline auto JNI_GetFieldID(JNIEnv *env, const Class &clazz, std::string_view name, + std::string_view sig) { return JNI_SafeInvoke(env, &JNIEnv::GetFieldID, clazz, name, sig); } -template -[[maybe_unused]] -inline auto -JNI_ToReflectedMethod(JNIEnv *env, const Class &clazz, jmethodID method, jboolean isStatic) { +template +[[maybe_unused]] inline auto JNI_ToReflectedMethod(JNIEnv *env, const Class &clazz, + jmethodID method, jboolean isStatic) { return JNI_SafeInvoke(env, &JNIEnv::ToReflectedMethod, clazz, method, isStatic); } -template -[[maybe_unused]] -inline auto JNI_GetObjectField(JNIEnv *env, const Object &obj, jfieldID fieldId) { +template +[[maybe_unused]] inline auto JNI_GetObjectField(JNIEnv *env, const Object &obj, jfieldID fieldId) { return JNI_SafeInvoke(env, &JNIEnv::GetObjectField, obj, fieldId); } -template -[[maybe_unused]] -inline auto JNI_GetLongField(JNIEnv *env, const Object &obj, jfieldID fieldId) { +template +[[maybe_unused]] inline auto JNI_GetLongField(JNIEnv *env, const Object &obj, jfieldID fieldId) { return JNI_SafeInvoke(env, &JNIEnv::GetLongField, obj, fieldId); } -template -[[maybe_unused]] -inline auto JNI_GetIntField(JNIEnv *env, const Object &obj, jfieldID fieldId) { +template +[[maybe_unused]] inline auto JNI_GetIntField(JNIEnv *env, const Object &obj, jfieldID fieldId) { return JNI_SafeInvoke(env, &JNIEnv::GetIntField, obj, fieldId); } -template -[[maybe_unused]] -inline auto -JNI_GetMethodID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) { +template +[[maybe_unused]] inline auto JNI_GetMethodID(JNIEnv *env, const Class &clazz, std::string_view name, + std::string_view sig) { return JNI_SafeInvoke(env, &JNIEnv::GetMethodID, clazz, name, sig); } -template -[[maybe_unused]] -inline auto -JNI_CallObjectMethod(JNIEnv *env, const Object &obj, jmethodID method, Args &&... args) { +template +[[maybe_unused]] inline auto JNI_CallObjectMethod(JNIEnv *env, const Object &obj, jmethodID method, + Args &&...args) { return JNI_SafeInvoke(env, &JNIEnv::CallObjectMethod, obj, method, std::forward(args)...); } -template -[[maybe_unused]] -inline auto JNI_CallIntMethod(JNIEnv *env, const Object &obj, jmethodID method, Args &&... args) { +template +[[maybe_unused]] inline auto JNI_CallIntMethod(JNIEnv *env, const Object &obj, jmethodID method, + Args &&...args) { return JNI_SafeInvoke(env, &JNIEnv::CallIntMethod, obj, method, std::forward(args)...); } -template -[[maybe_unused]] -inline auto JNI_CallLongMethod(JNIEnv *env, const Object &obj, Args &&... args) { +template +[[maybe_unused]] inline auto JNI_CallLongMethod(JNIEnv *env, const Object &obj, Args &&...args) { return JNI_SafeInvoke(env, &JNIEnv::CallLongMethod, obj, std::forward(args)...); } -template -[[maybe_unused]] -inline auto JNI_CallVoidMethod(JNIEnv *env, const Object &obj, Args &&...args) { +template +[[maybe_unused]] inline auto JNI_CallVoidMethod(JNIEnv *env, const Object &obj, Args &&...args) { return JNI_SafeInvoke(env, &JNIEnv::CallVoidMethod, obj, std::forward(args)...); } -template -[[maybe_unused]] -inline auto JNI_CallBooleanMethod(JNIEnv *env, const Object &obj, Args &&...args) { +template +[[maybe_unused]] inline auto JNI_CallBooleanMethod(JNIEnv *env, const Object &obj, Args &&...args) { return JNI_SafeInvoke(env, &JNIEnv::CallBooleanMethod, obj, std::forward(args)...); } -template -[[maybe_unused]] -inline auto -JNI_GetStaticFieldID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) { +template +[[maybe_unused]] inline auto JNI_GetStaticFieldID(JNIEnv *env, const Class &clazz, + std::string_view name, std::string_view sig) { return JNI_SafeInvoke(env, &JNIEnv::GetStaticFieldID, clazz, name, sig); } -template -[[maybe_unused]] -inline auto JNI_GetStaticObjectField(JNIEnv *env, const Class &clazz, jfieldID fieldId) { +template +[[maybe_unused]] inline auto JNI_GetStaticObjectField(JNIEnv *env, const Class &clazz, + jfieldID fieldId) { return JNI_SafeInvoke(env, &JNIEnv::GetStaticObjectField, clazz, fieldId); } -template -[[maybe_unused]] -inline auto JNI_GetStaticIntField(JNIEnv *env, const Class &clazz, jfieldID fieldId) { +template +[[maybe_unused]] inline auto JNI_GetStaticIntField(JNIEnv *env, const Class &clazz, + jfieldID fieldId) { return JNI_SafeInvoke(env, &JNIEnv::GetStaticIntField, clazz, fieldId); } -template -[[maybe_unused]] -inline auto -JNI_GetStaticMethodID(JNIEnv *env, const Class &clazz, std::string_view name, - std::string_view sig) { +template +[[maybe_unused]] inline auto JNI_GetStaticMethodID(JNIEnv *env, const Class &clazz, + std::string_view name, std::string_view sig) { return JNI_SafeInvoke(env, &JNIEnv::GetStaticMethodID, clazz, name, sig); } -template -[[maybe_unused]] -inline auto JNI_CallStaticVoidMethod(JNIEnv *env, const Class &clazz, Args &&...args) { +template +[[maybe_unused]] inline auto JNI_CallStaticVoidMethod(JNIEnv *env, const Class &clazz, + Args &&...args) { return JNI_SafeInvoke(env, &JNIEnv::CallStaticVoidMethod, clazz, std::forward(args)...); } -template -[[maybe_unused]] -inline auto JNI_CallStaticObjectMethod(JNIEnv *env, const Class &clazz, Args &&...args) { +template +[[maybe_unused]] inline auto JNI_CallStaticObjectMethod(JNIEnv *env, const Class &clazz, + Args &&...args) { return JNI_SafeInvoke(env, &JNIEnv::CallStaticObjectMethod, clazz, std::forward(args)...); } -template -[[maybe_unused]] -inline auto JNI_CallStaticIntMethod(JNIEnv *env, const Class &clazz, Args &&...args) { +template +[[maybe_unused]] inline auto JNI_CallStaticIntMethod(JNIEnv *env, const Class &clazz, + Args &&...args) { return JNI_SafeInvoke(env, &JNIEnv::CallStaticIntMethod, clazz, std::forward(args)...); } -template -[[maybe_unused]] -inline auto JNI_CallStaticBooleanMethod(JNIEnv *env, const Class &clazz, Args &&...args) { +template +[[maybe_unused]] inline auto JNI_CallStaticBooleanMethod(JNIEnv *env, const Class &clazz, + Args &&...args) { return JNI_SafeInvoke(env, &JNIEnv::CallStaticBooleanMethod, clazz, std::forward(args)...); } -template Array> -[[maybe_unused]] -inline auto JNI_GetArrayLength(JNIEnv *env, const Array &array) { +template Array> +[[maybe_unused]] inline auto JNI_GetArrayLength(JNIEnv *env, const Array &array) { return JNI_SafeInvoke(env, &JNIEnv::GetArrayLength, array); } -template Array> -[[maybe_unused]] -inline auto JNI_GetObjectArrayElement(JNIEnv *env, const Array &array, jsize idx) { +template Array> +[[maybe_unused]] inline auto JNI_GetObjectArrayElement(JNIEnv *env, const Array &array, jsize idx) { return JNI_SafeInvoke(env, &JNIEnv::GetObjectArrayElement, array, idx); } -template -[[maybe_unused]] -inline auto JNI_NewObject(JNIEnv *env, const Class &clazz, Args &&...args) { +template +[[maybe_unused]] inline auto JNI_NewObject(JNIEnv *env, const Class &clazz, Args &&...args) { return JNI_SafeInvoke(env, &JNIEnv::NewObject, clazz, std::forward(args)...); } -template -[[maybe_unused]] -inline auto -JNI_RegisterNatives(JNIEnv *env, const Class &clazz, const JNINativeMethod *methods, jint size) { +template +[[maybe_unused]] inline auto JNI_RegisterNatives(JNIEnv *env, const Class &clazz, + const JNINativeMethod *methods, jint size) { return JNI_SafeInvoke(env, &JNIEnv::RegisterNatives, clazz, methods, size); } -template -[[maybe_unused]] -inline auto JNI_NewGlobalRef(JNIEnv *env, Object &&x) { - return (decltype(UnwrapScope(std::forward(x)))) env->NewGlobalRef( - UnwrapScope(std::forward(x))); +template +[[maybe_unused]] inline auto JNI_NewGlobalRef(JNIEnv *env, Object &&x) { + return (decltype(UnwrapScope(std::forward(x))))env->NewGlobalRef( + UnwrapScope(std::forward(x))); } -template -[[maybe_unused]] -inline auto -JNI_Cast(ScopedLocalRef &&x)requires(std::is_convertible_v) { +template +[[maybe_unused]] inline auto JNI_Cast(ScopedLocalRef &&x) requires( + std::is_convertible_v) { return ScopedLocalRef(std::move(x)); } -} +} // namespace lsplant #undef DISALLOW_COPY_AND_ASSIGN diff --git a/library/jni/lsplant.cc b/library/jni/lsplant.cc index a9a6af3..4426660 100644 --- a/library/jni/lsplant.cc +++ b/library/jni/lsplant.cc @@ -1,20 +1,22 @@ #include "lsplant.hpp" -#include #include -#include -#include +#include #include -#include "utils/jni_helper.hpp" -#include "art/runtime/gc/scoped_gc_critical_section.hpp" -#include "art/thread.hpp" + +#include +#include + #include "art/instrumentation.hpp" -#include "art/runtime/jit/jit_code_cache.hpp" #include "art/runtime/art_method.hpp" -#include "art/thread_list.hpp" #include "art/runtime/class_linker.hpp" -#include "dex_builder.h" +#include "art/runtime/gc/scoped_gc_critical_section.hpp" +#include "art/runtime/jit/jit_code_cache.hpp" +#include "art/thread.hpp" +#include "art/thread_list.hpp" #include "common.hpp" +#include "dex_builder.h" +#include "utils/jni_helper.hpp" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunknown-pragmas" @@ -24,48 +26,45 @@ namespace lsplant { using art::ArtMethod; -using art::thread_list::ScopedSuspendAll; using art::ClassLinker; -using art::mirror::Class; -using art::Thread; using art::Instrumentation; +using art::Thread; using art::gc::ScopedGCCriticalSection; using art::jit::JitCodeCache; +using art::mirror::Class; +using art::thread_list::ScopedSuspendAll; namespace { -template -inline consteval auto operator ""_uarr() { - return std::array{ static_cast(chars)... }; +template +inline consteval auto operator""_uarr() { + return std::array{static_cast(chars)...}; } consteval inline auto GetTrampoline() { - if constexpr(kArch == Arch::kArm) { - return std::make_tuple( - "\x00\x00\x9f\xe5\x20\xf0\x90\xe5\x78\x56\x34\x12"_uarr, - // NOLINTNEXTLINE - uint8_t{ 32u }, uintptr_t{ 8u }); + if constexpr (kArch == Arch::kArm) { + return std::make_tuple("\x00\x00\x9f\xe5\x20\xf0\x90\xe5\x78\x56\x34\x12"_uarr, + // NOLINTNEXTLINE + uint8_t{32u}, uintptr_t{8u}); } - if constexpr(kArch == Arch::kArm64) { + if constexpr (kArch == Arch::kArm64) { return std::make_tuple( - "\x60\x00\x00\x58\x10\x00\x40\xf8\x00\x02\x1f\xd6\x78\x56\x34\x12\x78\x56\x34\x12"_uarr, - // NOLINTNEXTLINE - uint8_t{ 44u }, uintptr_t{ 12u }); + "\x60\x00\x00\x58\x10\x00\x40\xf8\x00\x02\x1f\xd6\x78\x56\x34\x12\x78\x56\x34\x12"_uarr, + // NOLINTNEXTLINE + uint8_t{44u}, uintptr_t{12u}); } - if constexpr(kArch == Arch::kX86) { - return std::make_tuple( - "\xb8\x78\x56\x34\x12\xff\x70\x20\xc3"_uarr, - // NOLINTNEXTLINE - uint8_t{ 56u }, uintptr_t{ 1u }); + if constexpr (kArch == Arch::kX86) { + return std::make_tuple("\xb8\x78\x56\x34\x12\xff\x70\x20\xc3"_uarr, + // NOLINTNEXTLINE + uint8_t{56u}, uintptr_t{1u}); } - if constexpr(kArch == Arch::kX8664) { - return std::make_tuple( - "\x48\xbf\x78\x56\x34\x12\x78\x56\x34\x12\xff\x77\x20\xc3"_uarr, - // NOLINTNEXTLINE - uint8_t{ 96u }, uintptr_t{ 2u }); + if constexpr (kArch == Arch::kX8664) { + return std::make_tuple("\x48\xbf\x78\x56\x34\x12\x78\x56\x34\x12\xff\x77\x20\xc3"_uarr, + // NOLINTNEXTLINE + uint8_t{96u}, uintptr_t{2u}); } } -auto[trampoline, entry_point_offset, art_method_offset] = GetTrampoline(); +auto [trampoline, entry_point_offset, art_method_offset] = GetTrampoline(); jmethodID method_get_name = nullptr; jmethodID method_get_declaring_class = nullptr; @@ -110,13 +109,14 @@ bool InitJNI(JNIEnv *env) { return false; } - if (method_get_name = JNI_GetMethodID(env, executable, "getName", - "()Ljava/lang/String;"); !method_get_name) { + if (method_get_name = JNI_GetMethodID(env, executable, "getName", "()Ljava/lang/String;"); + !method_get_name) { LOGE("Failed to find getName method"); return false; } - if (method_get_declaring_class = JNI_GetMethodID(env, executable, "getDeclaringClass", - "()Ljava/lang/Class;"); !method_get_declaring_class) { + if (method_get_declaring_class = + JNI_GetMethodID(env, executable, "getDeclaringClass", "()Ljava/lang/Class;"); + !method_get_declaring_class) { LOGE("Failed to find getName method"); return false; } @@ -126,15 +126,15 @@ bool InitJNI(JNIEnv *env) { return false; } - if (class_get_class_loader = JNI_GetMethodID(env, clazz, "getClassLoader", - "()Ljava/lang/ClassLoader;"); - !class_get_class_loader) { + if (class_get_class_loader = + JNI_GetMethodID(env, clazz, "getClassLoader", "()Ljava/lang/ClassLoader;"); + !class_get_class_loader) { LOGE("Failed to find getClassLoader"); return false; } if (class_get_name = JNI_GetMethodID(env, clazz, "getName", "()Ljava/lang/String;"); - !class_get_name) { + !class_get_name) { LOGE("Failed to find getName"); return false; } @@ -144,10 +144,10 @@ bool InitJNI(JNIEnv *env) { return false; } - in_memory_class_loader = JNI_NewGlobalRef(env, JNI_FindClass(env, - "dalvik/system/InMemoryDexClassLoader")); - in_memory_class_loader_init = JNI_GetMethodID(env, in_memory_class_loader, "", - "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); + in_memory_class_loader = + JNI_NewGlobalRef(env, JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader")); + in_memory_class_loader_init = JNI_GetMethodID( + env, in_memory_class_loader, "", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); load_class = JNI_GetMethodID(env, in_memory_class_loader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); @@ -161,7 +161,7 @@ bool InitJNI(JNIEnv *env) { return false; } if (set_accessible = JNI_GetMethodID(env, accessible_object, "setAccessible", "(Z)V"); - !set_accessible) { + !set_accessible) { LOGE("Failed to find AccessibleObject.setAccessible"); return false; } @@ -171,7 +171,7 @@ bool InitJNI(JNIEnv *env) { inline void UpdateTrampoline(uint8_t offset) { trampoline[entry_point_offset / CHAR_BIT] |= offset << (entry_point_offset % CHAR_BIT); trampoline[entry_point_offset / CHAR_BIT + 1] |= - offset >> (CHAR_BIT - entry_point_offset % CHAR_BIT); + offset >> (CHAR_BIT - entry_point_offset % CHAR_BIT); } bool InitNative(JNIEnv *env, const HookHandler &handler) { @@ -214,16 +214,17 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) { return true; } -std::tuple -BuildDex(JNIEnv *env, jobject class_loader, - std::string_view shorty, bool is_static, std::string_view method_name, - std::string_view hooker_class, std::string_view callback_name) { +std::tuple BuildDex(JNIEnv *env, jobject class_loader, + std::string_view shorty, bool is_static, + std::string_view method_name, + std::string_view hooker_class, + std::string_view callback_name) { // NOLINTNEXTLINE using namespace startop::dex; if (shorty.empty()) { LOGE("Invalid shorty"); - return { nullptr, nullptr, nullptr, nullptr }; + return {nullptr, nullptr, nullptr, nullptr}; } DexBuilder dex_file; @@ -232,66 +233,62 @@ BuildDex(JNIEnv *env, jobject class_loader, parameter_types.reserve(shorty.size() - 1); std::string storage; auto return_type = - shorty[0] == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor( - shorty[0]); - if (!is_static) parameter_types.push_back(TypeDescriptor::Object); // this object - for (const char ¶m: shorty.substr(1)) { - parameter_types.push_back( - param == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor( - static_cast(param))); + shorty[0] == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor(shorty[0]); + if (!is_static) parameter_types.push_back(TypeDescriptor::Object); // this object + for (const char ¶m : shorty.substr(1)) { + parameter_types.push_back(param == 'L' + ? TypeDescriptor::Object + : TypeDescriptor::FromDescriptor(static_cast(param))); } - ClassBuilder cbuilder{ dex_file.MakeClass(generated_class_name) }; + ClassBuilder cbuilder{dex_file.MakeClass(generated_class_name)}; if (!generated_source_name.empty()) cbuilder.set_source_file(generated_source_name); auto hooker_type = TypeDescriptor::FromClassname(hooker_class.data()); auto *hooker_field = cbuilder.CreateField(generated_field_name, hooker_type) - .access_flags(dex::kAccStatic) - .Encode(); + .access_flags(dex::kAccStatic) + .Encode(); - auto hook_builder{ cbuilder.CreateMethod( - generated_method_name == "{target}" ? method_name.data() : generated_method_name, - Prototype{ return_type, parameter_types }) }; + auto hook_builder{cbuilder.CreateMethod( + generated_method_name == "{target}" ? method_name.data() : generated_method_name, + Prototype{return_type, parameter_types})}; // allocate tmp first because of wide - auto tmp{ hook_builder.AllocRegister() }; + auto tmp{hook_builder.AllocRegister()}; hook_builder.BuildConst(tmp, static_cast(parameter_types.size())); - auto hook_params_array{ hook_builder.AllocRegister() }; + auto hook_params_array{hook_builder.AllocRegister()}; 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)); hook_builder.BuildConst(tmp, static_cast(i)); - hook_builder.BuildAput(Instruction::Op::kAputObject, hook_params_array, - Value::Parameter(j), tmp); + hook_builder.BuildAput(Instruction::Op::kAputObject, hook_params_array, Value::Parameter(j), + tmp); if (parameter_types[i].is_wide()) ++j; } - auto handle_hook_method{ dex_file.GetOrDeclareMethod( - hooker_type, callback_name.data(), - Prototype{ TypeDescriptor::Object, TypeDescriptor::Object.ToArray() }) }; + auto handle_hook_method{dex_file.GetOrDeclareMethod( + hooker_type, callback_name.data(), + Prototype{TypeDescriptor::Object, TypeDescriptor::Object.ToArray()})}; hook_builder.AddInstruction( - Instruction::GetStaticObjectField(hooker_field->decl->orig_index, tmp)); - hook_builder.AddInstruction(Instruction::InvokeVirtualObject( - handle_hook_method.id, tmp, tmp, hook_params_array)); + Instruction::GetStaticObjectField(hooker_field->decl->orig_index, tmp)); + hook_builder.AddInstruction( + Instruction::InvokeVirtualObject(handle_hook_method.id, tmp, tmp, hook_params_array)); if (return_type == TypeDescriptor::Void) { hook_builder.BuildReturn(); } else if (return_type.is_primitive()) { - auto box_type{ return_type.ToBoxType() }; + auto box_type{return_type.ToBoxType()}; const ir::Type *type_def = dex_file.GetOrAddType(box_type); - hook_builder.AddInstruction( - Instruction::Cast(tmp, Value::Type(type_def->orig_index))); + hook_builder.AddInstruction(Instruction::Cast(tmp, Value::Type(type_def->orig_index))); hook_builder.BuildUnBoxIfPrimitive(tmp, box_type, tmp); hook_builder.BuildReturn(tmp, false, return_type.is_wide()); } else { const ir::Type *type_def = dex_file.GetOrAddType(return_type); - hook_builder.AddInstruction( - Instruction::Cast(tmp, Value::Type(type_def->orig_index))); + hook_builder.AddInstruction(Instruction::Cast(tmp, Value::Type(type_def->orig_index))); hook_builder.BuildReturn(tmp, true); } auto *hook_method = hook_builder.Encode(); - auto backup_builder{ - cbuilder.CreateMethod("backup", Prototype{ return_type, parameter_types }) }; + auto backup_builder{cbuilder.CreateMethod("backup", Prototype{return_type, parameter_types})}; if (return_type == TypeDescriptor::Void) { backup_builder.BuildReturn(); } else if (return_type.is_wide()) { @@ -307,30 +304,32 @@ BuildDex(JNIEnv *env, jobject class_loader, } auto *backup_method = backup_builder.Encode(); - slicer::MemView image{ dex_file.CreateImage() }; + slicer::MemView image{dex_file.CreateImage()}; auto *dex_buffer = env->NewDirectByteBuffer(const_cast(image.ptr()), image.size()); - auto my_cl = JNI_NewObject(env, in_memory_class_loader, in_memory_class_loader_init, - dex_buffer, class_loader); + auto my_cl = JNI_NewObject(env, in_memory_class_loader, in_memory_class_loader_init, dex_buffer, + class_loader); env->DeleteLocalRef(dex_buffer); if (my_cl) { - auto *target_class = JNI_Cast( + auto *target_class = + JNI_Cast( JNI_CallObjectMethod(env, my_cl, load_class, - JNI_NewStringUTF(env, generated_class_name.data()))).release(); + JNI_NewStringUTF(env, generated_class_name.data()))) + .release(); if (target_class) { return { - target_class, - JNI_GetStaticFieldID(env, target_class, hooker_field->decl->name->c_str(), - hooker_field->decl->type->descriptor->c_str()), - JNI_GetStaticMethodID(env, target_class, hook_method->decl->name->c_str(), - hook_method->decl->prototype->Signature().data()), - JNI_GetStaticMethodID(env, target_class, backup_method->decl->name->c_str(), - backup_method->decl->prototype->Signature().data()), + target_class, + JNI_GetStaticFieldID(env, target_class, hooker_field->decl->name->c_str(), + hooker_field->decl->type->descriptor->c_str()), + JNI_GetStaticMethodID(env, target_class, hook_method->decl->name->c_str(), + hook_method->decl->prototype->Signature().data()), + JNI_GetStaticMethodID(env, target_class, backup_method->decl->name->c_str(), + backup_method->decl->prototype->Signature().data()), }; } } - return { nullptr, nullptr, nullptr, nullptr }; + return {nullptr, nullptr, nullptr, nullptr}; } static_assert(std::endian::native == std::endian::little, "Unsupported architecture"); @@ -338,16 +337,16 @@ static_assert(std::endian::native == std::endian::little, "Unsupported architect union Trampoline { public: uintptr_t address; - unsigned count: 12; + unsigned count : 12; }; static_assert(sizeof(Trampoline) == sizeof(uintptr_t), "Unsupported architecture"); static_assert(std::atomic_uintptr_t::is_always_lock_free, "Unsupported architecture"); -std::atomic_uintptr_t trampoline_pool{ 0 }; -std::atomic_flag trampoline_lock{ false }; +std::atomic_uintptr_t trampoline_pool{0}; +std::atomic_flag trampoline_lock{false}; constexpr size_t kTrampolineSize = RoundUpTo(sizeof(trampoline), kPointerSize); -constexpr size_t kPageSize = 4096; // assume +constexpr size_t kPageSize = 4096; // assume constexpr size_t kTrampolineNumPerPage = kPageSize / kTrampolineSize; constexpr uintptr_t kAddressMask = 0xFFFU; @@ -355,7 +354,7 @@ void *GenerateTrampolineFor(art::ArtMethod *hook) { unsigned count; uintptr_t address; while (true) { - auto tl = Trampoline{ .address = trampoline_pool.fetch_add(1, std::memory_order_release) }; + auto tl = Trampoline{.address = trampoline_pool.fetch_add(1, std::memory_order_release)}; count = tl.count; address = tl.address & ~kAddressMask; if (address == 0 || count >= kTrampolineNumPerPage) { @@ -378,7 +377,6 @@ void *GenerateTrampolineFor(art::ArtMethod *hook) { trampoline_pool.store(tl.address, std::memory_order_release); trampoline_lock.clear(std::memory_order_release); trampoline_lock.notify_all(); - } LOGV("trampoline: count = %u, address = %zx, target = %zx", count, address, address + count * kTrampolineSize); @@ -396,8 +394,7 @@ void *GenerateTrampolineFor(art::ArtMethod *hook) { } bool DoHook(ArtMethod *target, ArtMethod *hook, ArtMethod *backup) { - ScopedGCCriticalSection section(art::Thread::Current(), - art::gc::kGcCauseDebugger, + ScopedGCCriticalSection section(art::Thread::Current(), art::gc::kGcCauseDebugger, art::gc::kCollectorTypeDebugger); ScopedSuspendAll suspend("LSPlant Hook", false); LOGV("Hooking: target = %p, hook = %p, backup = %p", target, hook, backup); @@ -421,27 +418,25 @@ bool DoHook(ArtMethod *target, ArtMethod *hook, ArtMethod *backup) { backup->SetPrivate(); - LOGV("Done hook: target(%p:0x%x) -> %p; backup(%p:0x%x) -> %p; hook(%p:0x%x) -> %p", - target, target->GetAccessFlags(), target->GetEntryPoint(), - backup, backup->GetAccessFlags(), backup->GetEntryPoint(), - hook, hook->GetAccessFlags(), hook->GetEntryPoint()); + LOGV("Done hook: target(%p:0x%x) -> %p; backup(%p:0x%x) -> %p; hook(%p:0x%x) -> %p", target, + target->GetAccessFlags(), target->GetEntryPoint(), backup, backup->GetAccessFlags(), + backup->GetEntryPoint(), hook, hook->GetAccessFlags(), hook->GetEntryPoint()); return true; } } bool DoUnHook(ArtMethod *target, ArtMethod *backup) { - ScopedGCCriticalSection section(art::Thread::Current(), - art::gc::kGcCauseDebugger, + ScopedGCCriticalSection section(art::Thread::Current(), art::gc::kGcCauseDebugger, art::gc::kCollectorTypeDebugger); ScopedSuspendAll suspend("LSPlant Hook", false); LOGV("Unhooking: target = %p, backup = %p", target, backup); auto access_flags = target->GetAccessFlags(); target->CopyFrom(backup); target->SetAccessFlags(access_flags); - LOGV("Done unhook: target(%p:0x%x) -> %p; backup(%p:0x%x) -> %p;", - target, target->GetAccessFlags(), target->GetEntryPoint(), - backup, backup->GetAccessFlags(), backup->GetEntryPoint()); + LOGV("Done unhook: target(%p:0x%x) -> %p; backup(%p:0x%x) -> %p;", target, + target->GetAccessFlags(), target->GetEntryPoint(), backup, backup->GetAccessFlags(), + backup->GetEntryPoint()); return true; } @@ -458,16 +453,13 @@ inline namespace v1 { using ::lsplant::IsHooked; -[[maybe_unused]] -bool Init(JNIEnv *env, const InitInfo &info) { +[[maybe_unused]] bool Init(JNIEnv *env, const InitInfo &info) { bool static kInit = InitConfig(info) && InitJNI(env) && InitNative(env, info); return kInit; } - -[[maybe_unused]] -jobject -Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback_method) { +[[maybe_unused]] jobject Hook(JNIEnv *env, jobject target_method, jobject hooker_object, + jobject callback_method) { if (!env->IsInstanceOf(target_method, executable)) { LOGE("target method is not an executable"); return nullptr; @@ -481,8 +473,8 @@ Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback jmethodID backup_method = nullptr; jfieldID hooker_field = nullptr; - auto target_class = JNI_Cast( - JNI_CallObjectMethod(env, target_method, method_get_declaring_class)); + auto target_class = + JNI_Cast(JNI_CallObjectMethod(env, target_method, method_get_declaring_class)); bool is_proxy = JNI_CallBooleanMethod(env, target_class, class_is_proxy); auto *target = ArtMethod::FromReflectedMethod(env, target_method); bool is_static = target->IsStatic(); @@ -492,30 +484,26 @@ Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback return nullptr; } - ScopedLocalRef built_class{ env }; + ScopedLocalRef built_class{env}; { - auto callback_name = JNI_Cast( - JNI_CallObjectMethod(env, callback_method, method_get_name)); + auto callback_name = + JNI_Cast(JNI_CallObjectMethod(env, callback_method, method_get_name)); JUTFString method_name(callback_name); - auto callback_class = JNI_Cast(JNI_CallObjectMethod(env, callback_method, - method_get_declaring_class)); - auto callback_class_loader = JNI_CallObjectMethod(env, callback_class, - class_get_class_loader); - auto callback_class_name = JNI_Cast(JNI_CallObjectMethod(env, callback_class, - class_get_name)); + auto callback_class = JNI_Cast( + JNI_CallObjectMethod(env, callback_method, method_get_declaring_class)); + auto callback_class_loader = + JNI_CallObjectMethod(env, callback_class, class_get_class_loader); + auto callback_class_name = + JNI_Cast(JNI_CallObjectMethod(env, callback_class, class_get_name)); JUTFString class_name(callback_class_name); if (!env->IsInstanceOf(hooker_object, callback_class)) { LOGE("callback_method is not a method of hooker_object"); return nullptr; } - std::tie(built_class, hooker_field, hook_method, backup_method) = - WrapScope(env, BuildDex(env, callback_class_loader, - ArtMethod::GetMethodShorty(env, env->FromReflectedMethod( - target_method)), - is_static, - method_name.get(), - class_name.get(), - method_name.get())); + std::tie(built_class, hooker_field, hook_method, backup_method) = WrapScope( + env, BuildDex(env, callback_class_loader, + ArtMethod::GetMethodShorty(env, env->FromReflectedMethod(target_method)), + is_static, method_name.get(), class_name.get(), method_name.get())); if (!built_class || !hooker_field || !hook_method || !backup_method) { LOGE("Failed to generate hooker"); return nullptr; @@ -550,15 +538,15 @@ Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback if (DoHook(target, hook, backup)) { jobject global_backup = JNI_NewGlobalRef(env, reflected_backup); RecordHooked(target, global_backup); - if (!is_proxy) [[likely]] RecordJitMovement(target, backup); + if (!is_proxy) [[likely]] + RecordJitMovement(target, backup); return global_backup; } return nullptr; } -[[maybe_unused]] -bool UnHook(JNIEnv *env, jobject target_method) { +[[maybe_unused]] bool UnHook(JNIEnv *env, jobject target_method) { if (!env->IsInstanceOf(target_method, executable)) { LOGE("target method is not an executable"); return false; @@ -574,7 +562,7 @@ bool UnHook(JNIEnv *env, jobject target_method) { } { std::unique_lock lk(hooked_methods_lock_); - if (auto it = hooked_methods_.find(target);it != hooked_methods_.end()) { + if (auto it = hooked_methods_.find(target); it != hooked_methods_.end()) { reflected_backup = it->second; hooked_methods_.erase(it); } @@ -588,8 +576,7 @@ bool UnHook(JNIEnv *env, jobject target_method) { return DoUnHook(target, backup); } -[[maybe_unused]] -bool IsHooked(JNIEnv *env, jobject method) { +[[maybe_unused]] bool IsHooked(JNIEnv *env, jobject method) { if (!env->IsInstanceOf(method, executable)) { LOGE("method is not an executable"); return false; @@ -605,8 +592,7 @@ bool IsHooked(JNIEnv *env, jobject method) { return false; } -[[maybe_unused]] -bool Deoptimize(JNIEnv *env, jobject method) { +[[maybe_unused]] bool Deoptimize(JNIEnv *env, jobject method) { if (!env->IsInstanceOf(method, executable)) { LOGE("method is not an executable"); return false; @@ -626,8 +612,7 @@ bool Deoptimize(JNIEnv *env, jobject method) { return ClassLinker::SetEntryPointsToInterpreter(art_method); } -[[maybe_unused]] -void *GetNativeFunction(JNIEnv *env, jobject method) { +[[maybe_unused]] void *GetNativeFunction(JNIEnv *env, jobject method) { if (!env->IsInstanceOf(method, executable)) { LOGE("method is not an executable"); return nullptr;