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