Modularize hook helper

This commit is contained in:
LoveSy 2024-08-06 00:14:41 +08:00
parent 39334baed3
commit 14b753e1dc
No known key found for this signature in database
15 changed files with 545 additions and 576 deletions

View File

@ -3,7 +3,6 @@ module;
#include <parallel_hashmap/phmap.h> #include <parallel_hashmap/phmap.h>
#include "logging.hpp" #include "logging.hpp"
#include "utils/hook_helper.hpp"
export module clazz; export module clazz;
@ -11,23 +10,20 @@ import common;
import art_method; import art_method;
import thread; import thread;
import handle; import handle;
import hook_helper;
namespace lsplant::art::mirror { namespace lsplant::art::mirror {
export class Class { export class Class {
private: private:
CREATE_MEM_FUNC_SYMBOL_ENTRY(const char *, GetDescriptor, Class *thiz, std::string *storage) { inline static MemberFunction<
if (GetDescriptorSym) [[likely]] "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE",
return GetDescriptorSym(thiz, storage); Class, const char *(std::string *)>
else GetDescriptor_;
return "";
}
CREATE_MEM_FUNC_SYMBOL_ENTRY(const dex::ClassDef *, GetClassDef, Class *thiz) { inline static MemberFunction<"_ZN3art6mirror5Class11GetClassDefEv", Class,
if (GetClassDefSym) [[likely]] const dex::ClassDef *()>
return GetClassDefSym(thiz); GetClassDef_;
return nullptr;
}
using BackupMethods = phmap::flat_hash_map<art::ArtMethod *, void *>; using BackupMethods = phmap::flat_hash_map<art::ArtMethod *, void *>;
inline static phmap::flat_hash_map<const art::Thread *, inline static phmap::flat_hash_map<const art::Thread *,
@ -35,6 +31,8 @@ private:
backup_methods_; backup_methods_;
inline static std::mutex backup_methods_lock_; inline static std::mutex backup_methods_lock_;
inline static uint8_t initialized_status = 0;
static void BackupClassMethods(const dex::ClassDef *class_def, art::Thread *self) { static void BackupClassMethods(const dex::ClassDef *class_def, art::Thread *self) {
BackupMethods out; BackupMethods out;
if (!class_def) return; if (!class_def) return;
@ -64,62 +62,57 @@ private:
} }
} }
CREATE_HOOK_STUB_ENTRY( inline static Hooker<
"_ZN3art6mirror5Class9SetStatusENS_6HandleIS1_EENS_11ClassStatusEPNS_6ThreadE", void, "_ZN3art6mirror5Class9SetStatusENS_6HandleIS1_EENS_11ClassStatusEPNS_6ThreadE",
SetClassStatus, (TrivialHandle<Class> h, uint8_t new_status, Thread *self), { void(TrivialHandle<Class>, uint8_t, Thread *)>
SetClassStatus_ = +[](TrivialHandle<Class> h, uint8_t new_status, Thread *self) {
if (new_status == initialized_status) { if (new_status == initialized_status) {
BackupClassMethods(h->GetClassDef(), self); BackupClassMethods(GetClassDef_(h.Get()), self);
} }
return backup(h, new_status, self); return SetClassStatus_(h, new_status, self);
}); };
CREATE_HOOK_STUB_ENTRY( inline static Hooker<"_ZN3art6mirror5Class9SetStatusENS_6HandleIS1_EENS1_6StatusEPNS_6ThreadE",
"_ZN3art6mirror5Class9SetStatusENS_6HandleIS1_EENS1_6StatusEPNS_6ThreadE", void, SetStatus, void(Handle<Class>, int, Thread *)>
(Handle<Class> h, int new_status, Thread *self), { SetStatus_ = +[](Handle<Class> h, int new_status, Thread *self) {
if (new_status == static_cast<int>(initialized_status)) { if (new_status == static_cast<int>(initialized_status)) {
BackupClassMethods(h->GetClassDef(), self); BackupClassMethods(GetClassDef_(h.Get()), self);
} }
return backup(h, new_status, self); return SetStatus_(h, new_status, self);
}); };
CREATE_HOOK_STUB_ENTRY( inline static Hooker<"_ZN3art6mirror5Class9SetStatusENS_6HandleIS1_EENS1_6StatusEPNS_6ThreadE",
"_ZN3art6mirror5Class9SetStatusENS_6HandleIS1_EENS1_6StatusEPNS_6ThreadE", void, void(TrivialHandle<Class>, uint32_t, Thread *)>
TrivialSetStatus, (TrivialHandle<Class> h, uint32_t new_status, Thread *self), { TrivialSetStatus_ = +[](TrivialHandle<Class> h, uint32_t new_status, Thread *self) {
if (new_status == initialized_status) { if (new_status == initialized_status) {
BackupClassMethods(h->GetClassDef(), self); BackupClassMethods(GetClassDef_(h.Get()), self);
} }
return backup(h, new_status, self); return TrivialSetStatus_(h, new_status, self);
}); };
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art6mirror5Class9SetStatusENS1_6StatusEPNS_6ThreadE", void, inline static Hooker<"_ZN3art6mirror5Class9SetStatusENS1_6StatusEPNS_6ThreadE",
ClassSetStatus, (Class * thiz, int new_status, Thread *self), { void(Class *, int, Thread *)>
if (new_status == static_cast<int>(initialized_status)) { ClassSetStatus_ = +[](Class *thiz, int new_status, Thread *self) {
BackupClassMethods(thiz->GetClassDef(), self); if (new_status == static_cast<int>(initialized_status)) {
} BackupClassMethods(GetClassDef_(thiz), self);
return backup(thiz, new_status, self); }
}); return ClassSetStatus_(thiz, new_status, self);
};
inline static uint8_t initialized_status = 0;
public: public:
static bool Init(const HookHandler &handler) { static bool Init(const HookHandler &handler) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor, if (!handler.dlsym(GetDescriptor_) || !handler.dlsym(GetClassDef_)) {
"_ZN3art6mirror5Class13GetDescriptorEPNSt3__112"
"basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE")) {
return false;
}
if (!RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv")) {
return false; return false;
} }
int sdk_int = GetAndroidApiLevel(); int sdk_int = GetAndroidApiLevel();
if (sdk_int < __ANDROID_API_O__) { if (sdk_int < __ANDROID_API_O__) {
if (!HookSyms(handler, SetStatus, ClassSetStatus)) { if (!handler.hook(SetStatus_, ClassSetStatus_)) {
return false; return false;
} }
} else { } else {
if (!HookSyms(handler, SetClassStatus, TrivialSetStatus)) { if (!handler.hook(SetClassStatus_, TrivialSetStatus_)) {
return false; return false;
} }
} }
@ -137,22 +130,14 @@ public:
return true; return true;
} }
const char *GetDescriptor(std::string *storage) { const char *GetDescriptor(std::string *storage) { return GetDescriptor_(this, storage); }
if (GetDescriptorSym) {
return GetDescriptor(this, storage);
}
return "";
}
std::string GetDescriptor() { std::string GetDescriptor() {
std::string storage; std::string storage;
return GetDescriptor(&storage); return GetDescriptor(&storage);
} }
const dex::ClassDef *GetClassDef() { const dex::ClassDef *GetClassDef() { return GetClassDef_(this); }
if (GetClassDefSym) return GetClassDef(this);
return nullptr;
}
static auto PopBackup(const dex::ClassDef *class_def, art::Thread *self) { static auto PopBackup(const dex::ClassDef *class_def, art::Thread *self) {
BackupMethods methods; BackupMethods methods;

View File

@ -1,11 +1,14 @@
module; module;
#include <atomic>
#include <string>
#include "logging.hpp" #include "logging.hpp"
#include "utils/hook_helper.hpp"
export module art_method; export module art_method;
import common; import common;
import hook_helper;
namespace lsplant::art { namespace lsplant::art {
namespace mirror { namespace mirror {
@ -13,33 +16,43 @@ class Class;
} }
export class ArtMethod { export class ArtMethod {
CREATE_FUNC_SYMBOL_ENTRY(std::string, PrettyMethod, ArtMethod *thiz, bool with_signature) { inline static MemberFunction<"_ZN3art9ArtMethod12PrettyMethodEPS0_b", ArtMethod,
if (thiz == nullptr) [[unlikely]] std::string(bool)>
return "null"; PrettyMethod_;
else if (PrettyMethodSym) [[likely]]
return PrettyMethodSym(thiz, with_signature); inline static Function<"_ZN3art12PrettyMethodEPNS_9ArtMethodEb",
else std::string(ArtMethod *thiz, bool with_signature)>
return "null sym"; PrettyMethodStatic_;
inline static Function<"_ZN3art12PrettyMethodEPNS_6mirror9ArtMethodEb",
std::string(ArtMethod *thiz, bool with_signature)>
PrettyMethodMirror_;
inline static Function<"_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID",
const char *(JNIEnv *env, jmethodID method)>
GetMethodShortyL_;
inline static Function<"_ZN3art15GetMethodShortyEP7_JNIEnvP10_jmethodID",
const char *(JNIEnv *env, jmethodID mid)>
GetMethodShorty_;
inline static MemberFunction<"_ZN3art9ArtMethod24ThrowInvocationTimeErrorEv", ArtMethod, void()>
ThrowInvocationTimeError_;
inline static Function<"artInterpreterToCompiledCodeBridge", void()>
art_interpreter_to_compiled_code_bridge_;
inline void ThrowInvocationTimeError() {
if (ThrowInvocationTimeError_) {
[[likely]] ThrowInvocationTimeError_(this);
}
} }
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, ThrowInvocationTimeError, ArtMethod *thiz) {
if (thiz && ThrowInvocationTimeErrorSym) [[likely]]
return ThrowInvocationTimeErrorSym(thiz);
}
CREATE_FUNC_SYMBOL_ENTRY(const char *, GetMethodShorty, JNIEnv *env, jmethodID mid) {
if (GetMethodShortySym) [[likely]]
return GetMethodShortySym(env, mid);
return nullptr;
}
CREATE_FUNC_SYMBOL_ENTRY(void, art_interpreter_to_compiled_code_bridge) {}
inline void ThrowInvocationTimeError() { ThrowInvocationTimeError(this); }
public: public:
inline static const char *GetMethodShorty(JNIEnv *env, jobject method) { inline static const char *GetMethodShorty(JNIEnv *env, jobject method) {
return GetMethodShorty(env, env->FromReflectedMethod(method)); if (GetMethodShortyL_) {
return GetMethodShortyL_(env, env->FromReflectedMethod(method));
}
return GetMethodShorty_(env, env->FromReflectedMethod(method));
} }
void SetNonCompilable() { void SetNonCompilable() {
@ -101,7 +114,7 @@ public:
if (interpreter_entry_point_offset) [[unlikely]] { if (interpreter_entry_point_offset) [[unlikely]] {
*reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) + *reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) +
interpreter_entry_point_offset) = interpreter_entry_point_offset) =
reinterpret_cast<void *>(art_interpreter_to_compiled_code_bridgeSym); reinterpret_cast<void *>(&art_interpreter_to_compiled_code_bridge_);
} }
} }
@ -130,7 +143,11 @@ public:
} }
std::string PrettyMethod(bool with_signature = true) { std::string PrettyMethod(bool with_signature = true) {
return PrettyMethod(this, with_signature); if (PrettyMethod_) [[likely]]
return PrettyMethod_(this, with_signature);
if (PrettyMethodStatic_) return PrettyMethodStatic_(this, with_signature);
if (PrettyMethodMirror_) return PrettyMethodMirror_(this, with_signature);
return "null sym";
} }
mirror::Class *GetDeclaringClass() { mirror::Class *GetDeclaringClass() {
@ -229,7 +246,7 @@ public:
JNI_GetObjectField( JNI_GetObjectField(
env, env,
JNI_ToReflectedField(env, executable, JNI_ToReflectedField(env, executable,
JNI_GetFieldID(env, executable, name, sig), false), JNI_GetFieldID(env, executable, name, sig), false),
art_field_field), art_field_field),
field_offset); field_offset);
}; };
@ -256,17 +273,13 @@ public:
} }
if (sdk_int < __ANDROID_API_Q__) kAccFastInterpreterToInterpreterInvoke = 0; if (sdk_int < __ANDROID_API_Q__) kAccFastInterpreterToInterpreterInvoke = 0;
if (!RETRIEVE_FUNC_SYMBOL(GetMethodShorty, if (!handler.dlsym(GetMethodShortyL_, true) && !handler.dlsym(GetMethodShorty_)) {
"_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID", true) &&
!RETRIEVE_FUNC_SYMBOL(GetMethodShorty,
"_ZN3art15GetMethodShortyEP7_JNIEnvP10_jmethodID")) {
LOGE("Failed to find GetMethodShorty"); LOGE("Failed to find GetMethodShorty");
return false; return false;
} }
!RETRIEVE_FUNC_SYMBOL(PrettyMethod, "_ZN3art9ArtMethod12PrettyMethodEPS0_b") && handler.dlsym(PrettyMethod_) || handler.dlsym(PrettyMethodStatic_) ||
!RETRIEVE_FUNC_SYMBOL(PrettyMethod, "_ZN3art12PrettyMethodEPNS_9ArtMethodEb") && handler.dlsym(PrettyMethodMirror_);
!RETRIEVE_FUNC_SYMBOL(PrettyMethod, "_ZN3art12PrettyMethodEPNS_6mirror9ArtMethodEb");
if (sdk_int <= __ANDROID_API_O__) [[unlikely]] { if (sdk_int <= __ANDROID_API_O__) [[unlikely]] {
auto abstract_method_error = JNI_FindClass(env, "java/lang/AbstractMethodError"); auto abstract_method_error = JNI_FindClass(env, "java/lang/AbstractMethodError");
@ -281,8 +294,7 @@ public:
LOGE("Failed to find Executable.getName"); LOGE("Failed to find Executable.getName");
return false; return false;
} }
RETRIEVE_MEM_FUNC_SYMBOL(ThrowInvocationTimeError, handler.dlsym(ThrowInvocationTimeError_);
"_ZN3art9ArtMethod24ThrowInvocationTimeErrorEv");
auto abstract_method = FromReflectedMethod( auto abstract_method = FromReflectedMethod(
env, JNI_ToReflectedMethod(env, executable, executable_get_name, false).get()); env, JNI_ToReflectedMethod(env, executable, executable_get_name, false).get());
uint32_t access_flags = abstract_method->GetAccessFlags(); uint32_t access_flags = abstract_method->GetAccessFlags();
@ -301,8 +313,7 @@ public:
kAccCompileDontBother = 0; kAccCompileDontBother = 0;
} }
if (sdk_int <= __ANDROID_API_M__) [[unlikely]] { if (sdk_int <= __ANDROID_API_M__) [[unlikely]] {
if (!RETRIEVE_FUNC_SYMBOL(art_interpreter_to_compiled_code_bridge, if (!handler.dlsym(art_interpreter_to_compiled_code_bridge_)) {
"artInterpreterToCompiledCodeBridge")) {
return false; return false;
} }
if (sdk_int >= __ANDROID_API_L_MR1__) { if (sdk_int >= __ANDROID_API_L_MR1__) {

View File

@ -1,6 +1,7 @@
module; module;
#include "include/utils/hook_helper.hpp" #include <sys/types.h>
#include "logging.hpp" #include "logging.hpp"
export module class_linker; export module class_linker;
@ -10,29 +11,29 @@ import thread;
import common; import common;
import clazz; import clazz;
import handle; import handle;
import hook_helper;
namespace lsplant::art { namespace lsplant::art {
export class ClassLinker { export class ClassLinker {
private: private:
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, SetEntryPointsToInterpreter, ClassLinker *thiz, inline static MemberFunction<
ArtMethod *art_method) { "_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE", ClassLinker,
if (SetEntryPointsToInterpreterSym) [[likely]] { void(ArtMethod *)>
SetEntryPointsToInterpreterSym(thiz, art_method); SetEntryPointsToInterpreter_;
}
}
CREATE_HOOK_STUB_ENTRY( inline static Hooker<"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv",
"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv", bool, bool(ArtMethod *, const void *)>
ShouldUseInterpreterEntrypoint, (ArtMethod * art_method, const void *quick_code), { ShouldUseInterpreterEntrypoint_ = +[](ArtMethod *art_method, const void *quick_code) {
if (quick_code != nullptr && IsHooked(art_method)) [[unlikely]] { if (quick_code != nullptr && IsHooked(art_method)) [[unlikely]] {
return false; return false;
} }
return backup(art_method, quick_code); return ShouldUseInterpreterEntrypoint_(art_method, quick_code);
}); };
CREATE_FUNC_SYMBOL_ENTRY(void, art_quick_to_interpreter_bridge, void *) {} inline static Function<"art_quick_to_interpreter_bridge", void(void *)>
art_quick_to_interpreter_bridge_;
CREATE_FUNC_SYMBOL_ENTRY(void, art_quick_generic_jni_trampoline, void *) {} inline static Function<"art_quick_generic_jni_trampoline", void(void *)>
art_quick_generic_jni_trampoline_;
inline static art::ArtMethod *MayGetBackup(art::ArtMethod *method) { inline static art::ArtMethod *MayGetBackup(art::ArtMethod *method) {
if (auto backup = IsHooked(method); backup) [[unlikely]] { if (auto backup = IsHooked(method); backup) [[unlikely]] {
@ -42,44 +43,60 @@ private:
return method; return method;
} }
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<"_ZN3art6mirror9ArtMethod14RegisterNativeEPNS_6ThreadEPKvb",
"_ZN3art6mirror9ArtMethod14RegisterNativeEPNS_6ThreadEPKvb", void, RegisterNativeThread, ClassLinker, void(ArtMethod *, Thread *, const void *, bool)>
(art::ArtMethod * method, art::Thread *thread, const void *native_method, bool is_fast), RegisterNativeThread_ = +[](ClassLinker *thiz, ArtMethod *method, Thread *thread,
{ return backup(MayGetBackup(method), thread, native_method, is_fast); }); const void *native_method, bool is_fast) {
return RegisterNativeThread_(thiz, MayGetBackup(method), thread, native_method,
is_fast);
};
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art6mirror9ArtMethod16UnregisterNativeEPNS_6ThreadE", void, inline static MemberHooker<"_ZN3art6mirror9ArtMethod16UnregisterNativeEPNS_6ThreadE",
UnregisterNativeThread, ClassLinker, void(ArtMethod *, Thread *)>
(art::ArtMethod * method, art::Thread *thread), UnregisterNativeThread_ = +[](ClassLinker *thiz, ArtMethod *method, Thread *thread) {
{ return backup(MayGetBackup(method), thread); }); return UnregisterNativeThread_(thiz, MayGetBackup(method), thread);
};
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art9ArtMethod14RegisterNativeEPKvb", void, RegisterNativeFast, inline static MemberHooker<"_ZN3art9ArtMethod14RegisterNativeEPKvb", ClassLinker,
(art::ArtMethod * method, const void *native_method, bool is_fast), void(ArtMethod *, const void *, bool)>
{ return backup(MayGetBackup(method), native_method, is_fast); }); RegisterNativeFast_ =
+[](ClassLinker *thiz, ArtMethod *method, const void *native_method, bool is_fast) {
return RegisterNativeFast_(thiz, MayGetBackup(method), native_method, is_fast);
};
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art9ArtMethod16UnregisterNativeEv", void, UnregisterNativeFast, inline static MemberHooker<"_ZN3art9ArtMethod16UnregisterNativeEv", ClassLinker,
(art::ArtMethod * method), { return backup(MayGetBackup(method)); }); void(ArtMethod *)>
UnregisterNativeFast_ = +[](ClassLinker *thiz, ArtMethod *method) {
return UnregisterNativeFast_(thiz, MayGetBackup(method));
};
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art9ArtMethod14RegisterNativeEPKv", const void *, inline static MemberHooker<"_ZN3art9ArtMethod14RegisterNativeEPKv", ClassLinker,
RegisterNative, (art::ArtMethod * method, const void *native_method), const void *(ArtMethod *, const void *)>
{ return backup(MayGetBackup(method), native_method); }); RegisterNative_ = +[](ClassLinker *thiz, ArtMethod *method, const void *native_method) {
return RegisterNative_(thiz, MayGetBackup(method), native_method);
};
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art9ArtMethod16UnregisterNativeEv", const void *, inline static MemberHooker<"_ZN3art9ArtMethod16UnregisterNativeEv", ClassLinker,
UnregisterNative, (art::ArtMethod * method), const void *(ArtMethod *)>
{ return backup(MayGetBackup(method)); }); UnregisterNative_ = +[](ClassLinker *thiz, ArtMethod *method) {
return UnregisterNative_(thiz, MayGetBackup(method));
};
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<
"_ZN3art11ClassLinker14RegisterNativeEPNS_6ThreadEPNS_9ArtMethodEPKv", const void *, "_ZN3art11ClassLinker14RegisterNativeEPNS_6ThreadEPNS_9ArtMethodEPKv", ClassLinker,
RegisterNativeClassLinker, const void *(Thread *, ArtMethod *, const void *)>
(art::ClassLinker * thiz, art::Thread *self, art::ArtMethod *method, RegisterNativeClassLinker_ =
const void *native_method), +[](ClassLinker *thiz, Thread *self, ArtMethod *method, const void *native_method) {
{ return backup(thiz, self, MayGetBackup(method), native_method); }); return RegisterNativeClassLinker_(thiz, self, MayGetBackup(method), native_method);
};
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art11ClassLinker16UnregisterNativeEPNS_6ThreadEPNS_9ArtMethodE", inline static MemberHooker<"_ZN3art11ClassLinker16UnregisterNativeEPNS_6ThreadEPNS_9ArtMethodE",
const void *, UnregisterNativeClassLinker, ClassLinker, const void *(Thread *, ArtMethod *)>
(art::ClassLinker * thiz, art::Thread *self, art::ArtMethod *method), UnregisterNativeClassLinker_ = +[](ClassLinker *thiz, Thread *self, ArtMethod *method) {
{ return backup(thiz, self, MayGetBackup(method)); }); return UnregisterNativeClassLinker_(thiz, self, MayGetBackup(method));
};
static auto RestoreBackup(const dex::ClassDef *class_def, art::Thread *self) { static void RestoreBackup(const dex::ClassDef *class_def, art::Thread *self) {
auto methods = mirror::Class::PopBackup(class_def, self); auto methods = mirror::Class::PopBackup(class_def, self);
for (const auto &[art_method, old_trampoline] : methods) { for (const auto &[art_method, old_trampoline] : methods) {
auto new_trampoline = art_method->GetEntryPoint(); auto new_trampoline = art_method->GetEntryPoint();
@ -93,8 +110,8 @@ private:
backup_method->SetEntryPoint(new_trampoline); backup_method->SetEntryPoint(new_trampoline);
} }
} else if (deoptimized) { } else if (deoptimized) {
if (new_trampoline != art_quick_to_interpreter_bridge && if (new_trampoline != &art_quick_to_interpreter_bridge_ &&
new_trampoline != art_quick_generic_jni_trampoline) { new_trampoline != &art_quick_generic_jni_trampoline_) {
LOGV("re-deoptimize for %p", art_method); LOGV("re-deoptimize for %p", art_method);
SetEntryPointsToInterpreter(art_method); SetEntryPointsToInterpreter(art_method);
} }
@ -102,107 +119,105 @@ private:
} }
} }
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<
"_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE", void, "_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE", ClassLinker,
FixupStaticTrampolines, (ClassLinker * thiz, ObjPtr<mirror::Class> mirror_class), { void(ObjPtr<mirror::Class>)>
backup(thiz, mirror_class); FixupStaticTrampolines_ = +[](ClassLinker *thiz, ObjPtr<mirror::Class> mirror_class) {
FixupStaticTrampolines_(thiz, mirror_class);
RestoreBackup(mirror_class->GetClassDef(), nullptr); RestoreBackup(mirror_class->GetClassDef(), nullptr);
}); };
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<
"_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6ThreadENS_6ObjPtrINS_6mirror5ClassEEE", "_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6ThreadENS_6ObjPtrINS_6mirror5ClassEEE",
void, FixupStaticTrampolinesWithThread, ClassLinker, void(Thread *, ObjPtr<mirror::Class>)>
(ClassLinker * thiz, art::Thread *self, ObjPtr<mirror::Class> mirror_class), { FixupStaticTrampolinesWithThread_ =
backup(thiz, self, mirror_class); +[](ClassLinker *thiz, Thread *self, ObjPtr<mirror::Class> mirror_class) {
RestoreBackup(mirror_class->GetClassDef(), self); FixupStaticTrampolinesWithThread_(thiz, self, mirror_class);
}); RestoreBackup(mirror_class->GetClassDef(), self);
};
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6mirror5ClassE", inline static MemberHooker<"_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6mirror5ClassE",
void, FixupStaticTrampolinesRaw, ClassLinker, void(mirror::Class *)>
(ClassLinker * thiz, mirror::Class *mirror_class), { FixupStaticTrampolinesRaw_ = +[](ClassLinker *thiz, mirror::Class *mirror_class) {
backup(thiz, mirror_class); FixupStaticTrampolinesRaw_(thiz, mirror_class);
RestoreBackup(mirror_class->GetClassDef(), nullptr); RestoreBackup(mirror_class->GetClassDef(), nullptr);
}); };
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<
LP_SELECT( {"_ZN3art11ClassLinker26VisiblyInitializedCallback29AdjustThreadVisibilityCounterEPNS_6ThreadEi",
"_ZN3art11ClassLinker26VisiblyInitializedCallback29AdjustThreadVisibilityCounterEPNS_6ThreadEi", "_ZN3art11ClassLinker26VisiblyInitializedCallback29AdjustThreadVisibilityCounterEPNS_6ThreadEl"},
"_ZN3art11ClassLinker26VisiblyInitializedCallback29AdjustThreadVisibilityCounterEPNS_6ThreadEl"), ClassLinker, void(Thread *, ssize_t)>
void, AdjustThreadVisibilityCounter, (void *thiz, art::Thread *self, ssize_t adjustment), { AdjustThreadVisibilityCounter_ = +[](ClassLinker *thiz, Thread *self, ssize_t adjustment) {
backup(thiz, self, adjustment); AdjustThreadVisibilityCounter_(thiz, self, adjustment);
RestoreBackup(nullptr, self); RestoreBackup(nullptr, self);
}); };
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<
"_ZN3art11ClassLinker26VisiblyInitializedCallback22MarkVisiblyInitializedEPNS_6ThreadE", "_ZN3art11ClassLinker26VisiblyInitializedCallback22MarkVisiblyInitializedEPNS_6ThreadE",
void, MarkVisiblyInitialized, (void *thiz, Thread *self), { ClassLinker, void(Thread *)>
backup(thiz, self); MarkVisiblyInitialized_ = +[](ClassLinker *thiz, Thread *self) {
MarkVisiblyInitialized_(thiz, self);
RestoreBackup(nullptr, self); RestoreBackup(nullptr, self);
}); };
public: public:
static bool Init(const HookHandler &handler) { static bool Init(const HookHandler &handler) {
int sdk_int = GetAndroidApiLevel(); int sdk_int = GetAndroidApiLevel();
if (sdk_int >= __ANDROID_API_N__ && sdk_int < __ANDROID_API_T__) { if (sdk_int >= __ANDROID_API_N__ && sdk_int < __ANDROID_API_T__) {
HookSyms(handler, ShouldUseInterpreterEntrypoint); handler.hook(ShouldUseInterpreterEntrypoint_);
} }
if (!HookSyms(handler, FixupStaticTrampolinesWithThread, FixupStaticTrampolines, if (!handler.hook(FixupStaticTrampolinesWithThread_, FixupStaticTrampolines_,
FixupStaticTrampolinesRaw)) { FixupStaticTrampolinesRaw_)) {
return false; return false;
} }
if (!HookSyms(handler, RegisterNativeClassLinker, RegisterNative, RegisterNativeFast, if (!handler.hook(RegisterNativeClassLinker_, RegisterNative_, RegisterNativeFast_,
RegisterNativeThread) || RegisterNativeThread_) ||
!HookSyms(handler, UnregisterNativeClassLinker, UnregisterNative, UnregisterNativeFast, !handler.hook(UnregisterNativeClassLinker_, UnregisterNative_, UnregisterNativeFast_,
UnregisterNativeThread)) { UnregisterNativeThread_)) {
return false; return false;
} }
if (sdk_int >= __ANDROID_API_R__) { if (sdk_int >= __ANDROID_API_R__) {
if constexpr (kArch != Arch::kX86 && kArch != Arch::kX86_64) { if constexpr (kArch != Arch::kX86 && kArch != Arch::kX86_64) {
// fixup static trampoline may have been inlined // fixup static trampoline may have been inlined
HookSyms(handler, AdjustThreadVisibilityCounter, MarkVisiblyInitialized); handler.hook(AdjustThreadVisibilityCounter_, MarkVisiblyInitialized_);
} }
} }
if (!RETRIEVE_MEM_FUNC_SYMBOL( if (!handler.dlsym(SetEntryPointsToInterpreter_)) [[unlikely]] {
SetEntryPointsToInterpreter, if (!handler.dlsym(art_quick_to_interpreter_bridge_)) [[unlikely]] {
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE"))
[[unlikely]] {
if (!RETRIEVE_FUNC_SYMBOL(art_quick_to_interpreter_bridge,
"art_quick_to_interpreter_bridge")) [[unlikely]] {
return false; return false;
} }
if (!RETRIEVE_FUNC_SYMBOL(art_quick_generic_jni_trampoline, if (!handler.dlsym(art_quick_generic_jni_trampoline_)) [[unlikely]] {
"art_quick_generic_jni_trampoline")) [[unlikely]] {
return false; return false;
} }
LOGD("art_quick_to_interpreter_bridge = %p", art_quick_to_interpreter_bridgeSym); LOGD("art_quick_to_interpreter_bridge = %p", &art_quick_to_interpreter_bridge_);
LOGD("art_quick_generic_jni_trampoline = %p", art_quick_generic_jni_trampolineSym); LOGD("art_quick_generic_jni_trampoline = %p", &art_quick_generic_jni_trampoline_);
} }
return true; return true;
} }
[[gnu::always_inline]] static bool SetEntryPointsToInterpreter(ArtMethod *art_method) { [[gnu::always_inline]] static bool SetEntryPointsToInterpreter(ArtMethod *art_method) {
if (SetEntryPointsToInterpreterSym) [[likely]] { if (SetEntryPointsToInterpreter_) [[likely]] {
SetEntryPointsToInterpreter(nullptr, art_method); SetEntryPointsToInterpreter_(nullptr, art_method);
return true; return true;
} }
// Android 13 // Android 13
if (art_quick_to_interpreter_bridgeSym && art_quick_generic_jni_trampolineSym) [[likely]] { if (art_quick_to_interpreter_bridge_ && art_quick_generic_jni_trampoline_) [[likely]] {
if (art_method->GetAccessFlags() & ArtMethod::kAccNative) [[unlikely]] { if (art_method->GetAccessFlags() & ArtMethod::kAccNative) [[unlikely]] {
LOGV("deoptimize native method %s from %p to %p", LOGV("deoptimize native method %s from %p to %p",
art_method->PrettyMethod(true).data(), art_method->GetEntryPoint(), art_method->PrettyMethod(true).data(), art_method->GetEntryPoint(),
art_quick_generic_jni_trampolineSym); &art_quick_generic_jni_trampoline_);
art_method->SetEntryPoint( art_method->SetEntryPoint(
reinterpret_cast<void *>(art_quick_generic_jni_trampolineSym)); reinterpret_cast<void *>(&art_quick_generic_jni_trampoline_));
} else { } else {
LOGV("deoptimize method %s from %p to %p", art_method->PrettyMethod(true).data(), LOGV("deoptimize method %s from %p to %p", art_method->PrettyMethod(true).data(),
art_method->GetEntryPoint(), art_quick_to_interpreter_bridgeSym); art_method->GetEntryPoint(), &art_quick_to_interpreter_bridge_);
art_method->SetEntryPoint( art_method->SetEntryPoint(
reinterpret_cast<void *>(art_quick_to_interpreter_bridgeSym)); reinterpret_cast<void *>(&art_quick_to_interpreter_bridge_));
} }
return true; return true;
} }

View File

@ -5,11 +5,11 @@ module;
#include <vector> #include <vector>
#include "logging.hpp" #include "logging.hpp"
#include "utils/hook_helper.hpp"
export module dex_file; export module dex_file;
import common; import common;
import hook_helper;
namespace lsplant::art { namespace lsplant::art {
export class DexFile { export class DexFile {
@ -17,70 +17,59 @@ export class DexFile {
[[maybe_unused]] uint8_t magic_[8]; [[maybe_unused]] uint8_t magic_[8];
uint32_t checksum_; // See also location_checksum_ uint32_t checksum_; // See also location_checksum_
}; };
CREATE_FUNC_SYMBOL_ENTRY(std::unique_ptr<DexFile>, OpenMemory, const uint8_t* dex_file,
size_t size, const std::string& location, uint32_t location_checksum,
void* mem_map, const void* oat_dex_file, std::string* error_msg) {
if (OpenMemorySym) [[likely]] {
return OpenMemorySym(dex_file, size, location, location_checksum, mem_map, oat_dex_file,
error_msg);
}
if (error_msg) *error_msg = "null sym";
return nullptr;
}
CREATE_FUNC_SYMBOL_ENTRY(const DexFile*, OpenMemoryRaw, const uint8_t* dex_file, size_t size, inline static Function<
const std::string& location, uint32_t location_checksum, void* mem_map, {"_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_",
const void* oat_dex_file, std::string* error_msg) { "_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"},
if (OpenMemoryRawSym) [[likely]] { std::unique_ptr<DexFile>(const uint8_t* dex_file, size_t size, const std::string& location,
return OpenMemoryRawSym(dex_file, size, location, location_checksum, mem_map, uint32_t location_checksum, void* mem_map,
oat_dex_file, error_msg); const void* oat_dex_file, std::string* error_msg)>
} OpenMemory_;
if (error_msg) *error_msg = "null sym";
return nullptr;
}
CREATE_FUNC_SYMBOL_ENTRY(const DexFile*, OpenMemoryWithoutOdex, const uint8_t* dex_file, inline static Function<
size_t size, const std::string& location, uint32_t location_checksum, {"_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_7OatFileEPS9_",
void* mem_map, std::string* error_msg) { "_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_7OatFileEPS9_"},
if (OpenMemoryWithoutOdexSym) [[likely]] { const DexFile*(const uint8_t* dex_file, size_t size, const std::string& location,
return OpenMemoryWithoutOdexSym(dex_file, size, location, location_checksum, mem_map, uint32_t location_checksum, void* mem_map, const void* oat_dex_file,
error_msg); std::string* error_msg)>
} OpenMemoryRaw_;
if (error_msg) *error_msg = "null sym";
return nullptr;
}
CREATE_FUNC_SYMBOL_ENTRY(void, DexFile_setTrusted, JNIEnv* env, jclass clazz, inline static Function<
jobject j_cookie) { {"_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPS9_",
if (DexFile_setTrustedSym != nullptr) [[likely]] { "_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPS9_"},
DexFile_setTrustedSym(env, clazz, j_cookie); const DexFile*(const uint8_t* dex_file, size_t size, const std::string& location,
} uint32_t location_checksum, void* mem_map, std::string* error_msg)>
} OpenMemoryWithoutOdex_;
inline static Function<"_ZN3artL18DexFile_setTrustedEP7_JNIEnvP7_jclassP8_jobject",
void(JNIEnv* env, jclass clazz, jobject j_cookie)>
DexFile_setTrusted_;
public: public:
static const DexFile* OpenMemory(const void* dex_file, size_t size, std::string location, static const DexFile* OpenMemory(const uint8_t* dex_file, size_t size, std::string location,
std::string* error_msg) { std::string* error_msg) {
if (OpenMemorySym) [[likely]] { if (OpenMemory_) [[likely]] {
return OpenMemory(reinterpret_cast<const uint8_t*>(dex_file), size, location, return OpenMemory_(dex_file, size, location,
reinterpret_cast<const Header*>(dex_file)->checksum_, nullptr, reinterpret_cast<const Header*>(dex_file)->checksum_, nullptr,
nullptr, error_msg) nullptr, error_msg)
.release(); .release();
} else if (OpenMemoryRawSym) [[likely]] {
return OpenMemoryRaw(reinterpret_cast<const uint8_t*>(dex_file), size, location,
reinterpret_cast<const Header*>(dex_file)->checksum_, nullptr,
nullptr, error_msg);
} else if (OpenMemoryWithoutOdexSym) [[likely]] {
return OpenMemoryWithoutOdex(reinterpret_cast<const uint8_t*>(dex_file), size, location,
reinterpret_cast<const Header*>(dex_file)->checksum_,
nullptr, error_msg);
} else {
if (error_msg) *error_msg = "no sym";
return nullptr;
} }
if (OpenMemoryRaw_) [[likely]] {
return OpenMemoryRaw_(dex_file, size, location,
reinterpret_cast<const Header*>(dex_file)->checksum_, nullptr,
nullptr, error_msg);
}
if (OpenMemoryWithoutOdex_) [[likely]] {
return OpenMemoryWithoutOdex_(dex_file, size, location,
reinterpret_cast<const Header*>(dex_file)->checksum_,
nullptr, error_msg);
}
if (error_msg) *error_msg = "null sym";
return nullptr;
} }
jobject ToJavaDexFile(JNIEnv* env) const { jobject ToJavaDexFile(JNIEnv* env) const {
auto java_dex_file = env->AllocObject(dex_file_class); auto* java_dex_file = env->AllocObject(dex_file_class);
auto cookie = JNI_NewLongArray(env, dex_file_start_index + 1); auto cookie = JNI_NewLongArray(env, dex_file_start_index + 1);
if (dex_file_start_index != size_t(-1)) [[likely]] { if (dex_file_start_index != size_t(-1)) [[likely]] {
cookie[oat_file_index] = 0; cookie[oat_file_index] = 0;
@ -100,41 +89,23 @@ public:
} }
static bool SetTrusted(JNIEnv* env, jobject cookie) { static bool SetTrusted(JNIEnv* env, jobject cookie) {
if (!DexFile_setTrustedSym) return false; if (!DexFile_setTrusted_) return false;
DexFile_setTrusted(env, nullptr, cookie); DexFile_setTrusted_(env, nullptr, cookie);
return true; return true;
} }
static bool Init(JNIEnv* env, const HookHandler& handler) { static bool Init(JNIEnv* env, const HookHandler& handler) {
auto sdk_int = GetAndroidApiLevel(); auto sdk_int = GetAndroidApiLevel();
if (sdk_int >= __ANDROID_API_P__) [[likely]] { if (sdk_int >= __ANDROID_API_P__) [[likely]] {
if (!RETRIEVE_FUNC_SYMBOL(DexFile_setTrusted, if (!handler.dlsym(DexFile_setTrusted_, true)) {
"_ZN3artL18DexFile_setTrustedEP7_JNIEnvP7_jclassP8_jobject",
true)) {
LOGW("DexFile.setTrusted not found, MakeDexFileTrusted will not work."); LOGW("DexFile.setTrusted not found, MakeDexFileTrusted will not work.");
} }
} }
if (sdk_int >= __ANDROID_API_O__) [[likely]] { if (sdk_int >= __ANDROID_API_O__) [[likely]] {
return true; return true;
} }
if (!RETRIEVE_FUNC_SYMBOL( if (!handler.dlsym(OpenMemory_) && !handler.dlsym(OpenMemoryRaw_) &&
OpenMemory, !handler.dlsym(OpenMemoryWithoutOdex_)) [[unlikely]] {
LP_SELECT("_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_",
"_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_")) &&
!RETRIEVE_FUNC_SYMBOL(
OpenMemoryRaw,
LP_SELECT("_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_7OatFileEPS9_",
"_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_7OatFileEPS9_")) &&
!RETRIEVE_FUNC_SYMBOL(
OpenMemoryWithoutOdex,
LP_SELECT("_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPS9_",
"_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPS9_"))) [[unlikely]] {
LOGE("Failed to find OpenMemory"); LOGE("Failed to find OpenMemory");
return false; return false;
} }

View File

@ -1,11 +1,12 @@
module; module;
#include "utils/hook_helper.hpp" #include <android/api-level.h>
export module scope_gc_critical_section; export module scope_gc_critical_section;
import thread; import thread;
import common; import common;
import hook_helper;
namespace lsplant::art::gc { namespace lsplant::art::gc {
// Which types of collections are able to be performed. // Which types of collections are able to be performed.
@ -97,35 +98,30 @@ private:
}; };
export class ScopedGCCriticalSection { export class ScopedGCCriticalSection {
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, constructor, ScopedGCCriticalSection *thiz, Thread *self, inline static MemberFunction<
GcCause cause, CollectorType collector_type) { "_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_7GcCauseENS0_13CollectorTypeE",
if (thiz && constructorSym) [[likely]] ScopedGCCriticalSection, void(Thread *, GcCause, CollectorType)>
return constructorSym(thiz, self, cause, collector_type); constructor_;
} inline static MemberFunction<"_ZN3art2gc23ScopedGCCriticalSectionD2Ev", ScopedGCCriticalSection,
void()>
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, destructor, ScopedGCCriticalSection *thiz) { destructor_;
if (thiz && destructorSym) [[likely]]
destructorSym(thiz);
}
public: public:
ScopedGCCriticalSection(Thread *self, GcCause cause, CollectorType collector_type) { ScopedGCCriticalSection(Thread *self, GcCause cause, CollectorType collector_type) {
constructor(this, self, cause, collector_type); if (constructor_) {
constructor_(this, self, cause, collector_type);
}
} }
~ScopedGCCriticalSection() { destructor(this); } ~ScopedGCCriticalSection() {
if (destructor_) destructor_(this);
}
static bool Init(const HookHandler &handler) { static bool Init(const HookHandler &handler) {
// for Android M, it's safe to not found since we have suspendVM & resumeVM // for Android M, it's safe to not found since we have suspendVM & resumeVM
auto sdk_int = GetAndroidApiLevel(); auto sdk_int = GetAndroidApiLevel();
if (sdk_int >= __ANDROID_API_N__) [[likely]] { if (sdk_int >= __ANDROID_API_N__) [[likely]] {
if (!RETRIEVE_MEM_FUNC_SYMBOL(constructor, if (!handler.dlsym(constructor_) || !handler.dlsym(destructor_)) {
"_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_"
"7GcCauseENS0_13CollectorTypeE")) [[unlikely]] {
return false;
}
if (!RETRIEVE_MEM_FUNC_SYMBOL(destructor, "_ZN3art2gc23ScopedGCCriticalSectionD2Ev"))
[[unlikely]] {
return false; return false;
} }
} }

View File

@ -1,12 +1,12 @@
module; module;
#include "logging.hpp" #include "logging.hpp"
#include "utils/hook_helper.hpp"
export module instrumentation; export module instrumentation;
import art_method; import art_method;
import common; import common;
import hook_helper;
namespace lsplant::art { namespace lsplant::art {
@ -21,29 +21,32 @@ export class Instrumentation {
return art_method; return art_method;
} }
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<
"_ZN3art15instrumentation15Instrumentation40UpdateMethodsCodeToInterpreterEntryPointEPNS_9ArtMethodE", "_ZN3art15instrumentation15Instrumentation40UpdateMethodsCodeToInterpreterEntryPointEPNS_9ArtMethodE",
void, UpdateMethodsCodeToInterpreterEntryPoint, Instrumentation, void(ArtMethod *)>
(Instrumentation * thiz, ArtMethod *art_method), { UpdateMethodsCodeToInterpreterEntryPoint_ =
if (IsDeoptimized(art_method)) { +[](Instrumentation *thiz, ArtMethod *art_method) {
LOGV("skip update entrypoint on deoptimized method %s", if (IsDeoptimized(art_method)) {
art_method->PrettyMethod(true).c_str()); LOGV("skip update entrypoint on deoptimized method %s",
return; art_method->PrettyMethod(true).c_str());
} return;
backup(thiz, MaybeUseBackupMethod(art_method, nullptr)); }
}); UpdateMethodsCodeToInterpreterEntryPoint_(
thiz, MaybeUseBackupMethod(art_method, nullptr));
};
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<
"_ZN3art15instrumentation15Instrumentation21InitializeMethodsCodeEPNS_9ArtMethodEPKv", void, "_ZN3art15instrumentation15Instrumentation21InitializeMethodsCodeEPNS_9ArtMethodEPKv",
InitializeMethodsCode, Instrumentation, void(ArtMethod *, const void *)>
(Instrumentation * thiz, ArtMethod *art_method, const void *quick_code), { InitializeMethodsCode_ = +[](Instrumentation *thiz, ArtMethod *art_method,
const void *quick_code) {
if (IsDeoptimized(art_method)) { if (IsDeoptimized(art_method)) {
LOGV("skip update entrypoint on deoptimized method %s", LOGV("skip update entrypoint on deoptimized method %s",
art_method->PrettyMethod(true).c_str()); art_method->PrettyMethod(true).c_str());
return; return;
} }
backup(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code); InitializeMethodsCode_(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code);
}); };
public: public:
static bool Init(JNIEnv *env, const HookHandler &handler) { static bool Init(JNIEnv *env, const HookHandler &handler) {
@ -52,8 +55,7 @@ public:
} }
int sdk_int = GetAndroidApiLevel(); int sdk_int = GetAndroidApiLevel();
if (sdk_int >= __ANDROID_API_P__) [[likely]] { if (sdk_int >= __ANDROID_API_P__) [[likely]] {
if (!HookSyms(handler, InitializeMethodsCode, if (!handler.hook(InitializeMethodsCode_, UpdateMethodsCodeToInterpreterEntryPoint_)) {
UpdateMethodsCodeToInterpreterEntryPoint)) {
return false; return false;
} }
} }

View File

@ -1,13 +1,13 @@
module; module;
#include "logging.hpp" #include "logging.hpp"
#include "utils/hook_helper.hpp"
export module jit; export module jit;
import art_method; import art_method;
import common; import common;
import thread; import thread;
import hook_helper;
namespace lsplant::art::jit { namespace lsplant::art::jit {
enum class CompilationKind { enum class CompilationKind {
@ -17,38 +17,38 @@ enum class CompilationKind {
}; };
export class Jit { export class Jit {
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<
"_ZN3art3jit3Jit27EnqueueOptimizedCompilationEPNS_9ArtMethodEPNS_6ThreadE", void, "_ZN3art3jit3Jit27EnqueueOptimizedCompilationEPNS_9ArtMethodEPNS_6ThreadE", Jit,
EnqueueOptimizedCompilation, (Jit * thiz, ArtMethod *method, Thread *self), { void(ArtMethod *, Thread *)>
EnqueueOptimizedCompilation_ = +[](Jit *thiz, ArtMethod *method, Thread *self) {
if (auto target = IsBackup(method); target) [[unlikely]] { if (auto target = IsBackup(method); target) [[unlikely]] {
LOGD("Propagate enqueue compilation: %p -> %p", method, target); LOGD("Propagate enqueue compilation: %p -> %p", method, target);
method = target; method = target;
} }
return backup(thiz, method, self); return EnqueueOptimizedCompilation_(thiz, method, self);
}); };
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<
"_ZN3art3jit3Jit14AddCompileTaskEPNS_6ThreadEPNS_9ArtMethodENS_15CompilationKindEb", void, "_ZN3art3jit3Jit14AddCompileTaskEPNS_6ThreadEPNS_9ArtMethodENS_15CompilationKindEb", Jit,
AddCompileTask, void(Thread *, ArtMethod *, CompilationKind, bool)>
(Jit * thiz, Thread *self, ArtMethod *method, CompilationKind compilation_kind, AddCompileTask_ = +[](Jit *thiz, Thread *self, ArtMethod *method,
bool precompile), CompilationKind compilation_kind, bool precompile) {
{ if (compilation_kind == CompilationKind::kOptimized && !precompile) {
if (compilation_kind == CompilationKind::kOptimized && !precompile/* && in_enqueue*/) {
if (auto backup = IsHooked(method); backup) [[unlikely]] { if (auto backup = IsHooked(method); backup) [[unlikely]] {
LOGD("Propagate compile task: %p -> %p", method, backup); LOGD("Propagate compile task: %p -> %p", method, backup);
method = backup; method = backup;
} }
} }
return backup(thiz, self, method, compilation_kind, precompile); return AddCompileTask_(thiz, self, method, compilation_kind, precompile);
}); };
public: public:
static bool Init(const HookHandler &handler) { static bool Init(const HookHandler &handler) {
auto sdk_int = GetAndroidApiLevel(); auto sdk_int = GetAndroidApiLevel();
if (sdk_int <= __ANDROID_API_U__) [[likely]] { if (sdk_int <= __ANDROID_API_U__) [[likely]] {
HookSyms(handler, EnqueueOptimizedCompilation); handler.hook(EnqueueOptimizedCompilation_);
HookSyms(handler, AddCompileTask); handler.hook(AddCompileTask_);
} }
return true; return true;
} }

View File

@ -1,60 +1,57 @@
module; module;
#include "logging.hpp" #include "logging.hpp"
#include "utils/hook_helper.hpp"
export module jit_code_cache; export module jit_code_cache;
import art_method; import art_method;
import common; import common;
import thread; import thread;
import hook_helper;
namespace lsplant::art::jit { namespace lsplant::art::jit {
export class JitCodeCache { export class JitCodeCache {
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, MoveObsoleteMethod, JitCodeCache *thiz, inline static MemberFunction<"_ZN3art3jit12JitCodeCache18MoveObsoleteMethodEPNS_9ArtMethodES3_",
ArtMethod *old_method, ArtMethod *new_method) { JitCodeCache, void(ArtMethod *, ArtMethod *)>
if (MoveObsoleteMethodSym) [[likely]] { MoveObsoleteMethod_;
MoveObsoleteMethodSym(thiz, old_method, new_method);
} else {
// fallback to set data
new_method->SetData(old_method->GetData());
old_method->SetData(nullptr);
}
}
void MoveObsoleteMethods() { static void MoveObsoleteMethods(JitCodeCache *thiz) {
auto movements = GetJitMovements(); auto movements = GetJitMovements();
LOGD("Before jit cache collection, moving %zu hooked methods", movements.size()); LOGD("Before jit cache collection, moving %zu hooked methods", movements.size());
for (auto [target, backup] : movements) { for (auto [target, backup] : movements) {
MoveObsoleteMethod(this, target, backup); if (MoveObsoleteMethod_) [[likely]]
MoveObsoleteMethod_(thiz, target, backup);
else {
backup->SetData(backup->GetData());
target->SetData(nullptr);
}
} }
} }
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art3jit12JitCodeCache19GarbageCollectCacheEPNS_6ThreadE", void, inline static MemberHooker<"_ZN3art3jit12JitCodeCache19GarbageCollectCacheEPNS_6ThreadE",
GarbageCollectCache, (JitCodeCache * thiz, Thread *self), { JitCodeCache, void(Thread *)>
thiz->MoveObsoleteMethods(); GarbageCollectCache_ = +[](JitCodeCache *thiz, Thread *self) {
backup(thiz, self); MoveObsoleteMethods(thiz);
}); GarbageCollectCache_(thiz, self);
};
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art3jit12JitCodeCache12DoCollectionEPNS_6ThreadE", void, inline static MemberHooker<"_ZN3art3jit12JitCodeCache12DoCollectionEPNS_6ThreadE", JitCodeCache,
DoCollection, (JitCodeCache * thiz, Thread *self), { void(Thread *)>
thiz->MoveObsoleteMethods(); DoCollection_ = +[](JitCodeCache *thiz, Thread *self) {
backup(thiz, self); MoveObsoleteMethods(thiz);
}); DoCollection_(thiz, self);
};
public: public:
static bool Init(const HookHandler &handler) { static bool Init(const HookHandler &handler) {
auto sdk_int = GetAndroidApiLevel(); auto sdk_int = GetAndroidApiLevel();
if (sdk_int >= __ANDROID_API_O__) [[likely]] { if (sdk_int >= __ANDROID_API_O__) [[likely]] {
if (!RETRIEVE_MEM_FUNC_SYMBOL( if (!handler.dlsym(MoveObsoleteMethod_)) [[unlikely]] {
MoveObsoleteMethod,
"_ZN3art3jit12JitCodeCache18MoveObsoleteMethodEPNS_9ArtMethodES3_"))
[[unlikely]] {
return false; return false;
} }
} }
if (sdk_int >= __ANDROID_API_N__) [[likely]] { if (sdk_int >= __ANDROID_API_N__) [[likely]] {
if (!HookSyms(handler, GarbageCollectCache, DoCollection)) [[unlikely]] { if (!handler.hook(GarbageCollectCache_, DoCollection_)) [[unlikely]] {
return false; return false;
} }
} }

View File

@ -1,33 +1,35 @@
module; module;
#include "logging.hpp" #include "logging.hpp"
#include "utils/hook_helper.hpp"
export module jni_id_manager; export module jni_id_manager;
import art_method; import art_method;
import common; import common;
import handle; import handle;
import hook_helper;
namespace lsplant::art::jni { namespace lsplant::art::jni {
export class JniIdManager { export class JniIdManager {
private: private:
CREATE_MEM_HOOK_STUB_ENTRY( inline static MemberHooker<
"_ZN3art3jni12JniIdManager15EncodeGenericIdINS_9ArtMethodEEEmNS_16ReflectiveHandleIT_EE", "_ZN3art3jni12JniIdManager15EncodeGenericIdINS_9ArtMethodEEEmNS_16ReflectiveHandleIT_EE",
uintptr_t, EncodeGenericId, (JniIdManager * thiz, ReflectiveHandle<ArtMethod> method), { JniIdManager, uintptr_t(ReflectiveHandle<ArtMethod>)>
if (auto target = IsBackup(method.Get()); target) { EncodeGenericId_ =
LOGD("get generic id for %s", method.Get()->PrettyMethod().c_str()); +[](JniIdManager *thiz, ReflectiveHandle<ArtMethod> method) -> uintptr_t {
method.Set(target); if (auto target = IsBackup(method.Get()); target) {
} LOGD("get generic id for %s", method.Get()->PrettyMethod().c_str());
return backup(thiz, method); method.Set(target);
}); }
return EncodeGenericId_(thiz, method);
};
public: public:
static bool Init(JNIEnv *env, const HookHandler &handler) { static bool Init(JNIEnv *env, const HookHandler &handler) {
int sdk_int = GetAndroidApiLevel(); int sdk_int = GetAndroidApiLevel();
if (sdk_int >= __ANDROID_API_R__) { if (sdk_int >= __ANDROID_API_R__) {
if (IsJavaDebuggable(env) && !HookSyms(handler, EncodeGenericId)) { if (IsJavaDebuggable(env) && !handler.hook(EncodeGenericId_)) {
LOGW("Failed to hook EncodeGenericId, attaching debugger may crash the process"); LOGW("Failed to hook EncodeGenericId, attaching debugger may crash the process");
} }
} }

View File

@ -1,11 +1,13 @@
module; module;
#include <array>
#include "logging.hpp" #include "logging.hpp"
#include "utils/hook_helper.hpp"
export module runtime; export module runtime;
import common; import common;
import hook_helper;
namespace lsplant::art { namespace lsplant::art {
@ -27,55 +29,47 @@ public:
}; };
private: private:
inline static Runtime *instance_; inline static Field<"_ZN3art7Runtime9instance_E", Runtime *> instance_;
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, SetJavaDebuggable, void *thiz, bool value) { inline static MemberFunction<"_ZN3art7Runtime17SetJavaDebuggableEb", Runtime, void(bool)>
SetJavaDebuggableSym(thiz, value); SetJavaDebuggable_;
} inline static MemberFunction<"_ZN3art7Runtime20SetRuntimeDebugStateENS0_17RuntimeDebugStateE",
Runtime, void(RuntimeDebugState)>
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, SetRuntimeDebugState, void *thiz, RuntimeDebugState value) { SetRuntimeDebugState_;
SetRuntimeDebugStateSym(thiz, value);
}
inline static size_t debug_state_offset = 0U; inline static size_t debug_state_offset = 0U;
public: public:
inline static Runtime *Current() { return instance_; } inline static Runtime *Current() { return *instance_; }
void SetJavaDebuggable(RuntimeDebugState value) { void SetJavaDebuggable(RuntimeDebugState value) {
if (SetJavaDebuggableSym) { if (SetJavaDebuggable_) {
SetJavaDebuggable(this, value != RuntimeDebugState::kNonJavaDebuggable); SetJavaDebuggable_(this, value != RuntimeDebugState::kNonJavaDebuggable);
} else if (debug_state_offset > 0) { } else if (debug_state_offset > 0) {
*reinterpret_cast<RuntimeDebugState *>(reinterpret_cast<uintptr_t>(instance_) + *reinterpret_cast<RuntimeDebugState *>(reinterpret_cast<uintptr_t>(*instance_) +
debug_state_offset) = value; debug_state_offset) = value;
} }
} }
static bool Init(const HookHandler &handler) { static bool Init(const HookHandler &handler) {
int sdk_int = GetAndroidApiLevel(); int sdk_int = GetAndroidApiLevel();
if (void **instance; !RETRIEVE_FIELD_SYMBOL(instance, "_ZN3art7Runtime9instance_E")) { if (!handler.dlsym(instance_) || !*instance_) {
return false;
} else if (instance_ = reinterpret_cast<Runtime *>(*instance); !instance_) {
return false; return false;
} }
LOGD("runtime instance = %p", instance_); LOGD("runtime instance = %p", *instance_);
if (sdk_int >= __ANDROID_API_O__) { if (sdk_int >= __ANDROID_API_O__) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(SetJavaDebuggable, if (!handler.dlsym(SetJavaDebuggable_) && !handler.dlsym(SetRuntimeDebugState_)) {
"_ZN3art7Runtime17SetJavaDebuggableEb") &&
!RETRIEVE_MEM_FUNC_SYMBOL(
SetRuntimeDebugState,
"_ZN3art7Runtime20SetRuntimeDebugStateENS0_17RuntimeDebugStateE")) {
return false; return false;
} }
} }
if (SetRuntimeDebugStateSym) { if (SetRuntimeDebugState_) {
static constexpr size_t kLargeEnoughSizeForRuntime = 4096; static constexpr size_t kLargeEnoughSizeForRuntime = 4096;
std::array<uint8_t, kLargeEnoughSizeForRuntime> code; std::array<uint8_t, kLargeEnoughSizeForRuntime> code;
static_assert(static_cast<int>(RuntimeDebugState::kJavaDebuggable) != 0); static_assert(static_cast<int>(RuntimeDebugState::kJavaDebuggable) != 0);
static_assert(static_cast<int>(RuntimeDebugState::kJavaDebuggableAtInit) != 0); static_assert(static_cast<int>(RuntimeDebugState::kJavaDebuggableAtInit) != 0);
code.fill(uint8_t{0}); code.fill(uint8_t{0});
auto *const fake_runtime = reinterpret_cast<Runtime *>(code.data()); auto *const fake_runtime = reinterpret_cast<Runtime *>(code.data());
SetRuntimeDebugState(fake_runtime, RuntimeDebugState::kJavaDebuggable); SetRuntimeDebugState_(fake_runtime, RuntimeDebugState::kJavaDebuggable);
for (size_t i = 0; i < kLargeEnoughSizeForRuntime; ++i) { for (size_t i = 0; i < kLargeEnoughSizeForRuntime; ++i) {
if (*reinterpret_cast<RuntimeDebugState *>( if (*reinterpret_cast<RuntimeDebugState *>(
reinterpret_cast<uintptr_t>(fake_runtime) + i) == reinterpret_cast<uintptr_t>(fake_runtime) + i) ==

View File

@ -1,24 +1,22 @@
module; module;
#include "utils/hook_helper.hpp"
export module thread; export module thread;
import hook_helper;
namespace lsplant::art { namespace lsplant::art {
export class Thread { export class Thread {
CREATE_FUNC_SYMBOL_ENTRY(Thread *, CurrentFromGdb) { inline static Function<"_ZN3art6Thread14CurrentFromGdbEv", Thread *()> CurrentFromGdb_;
if (CurrentFromGdbSym) [[likely]]
return CurrentFromGdbSym();
else
return nullptr;
}
public: public:
static Thread *Current() { return CurrentFromGdb(); } static Thread *Current() {
if (CurrentFromGdb_) [[likely]]
return CurrentFromGdb_();
return nullptr;
}
static bool Init(const HookHandler &handler) { static bool Init(const HookHandler &handler) {
if (!RETRIEVE_FUNC_SYMBOL(CurrentFromGdb, "_ZN3art6Thread14CurrentFromGdbEv")) if (!handler.dlsym(CurrentFromGdb_)) [[unlikely]] {
[[unlikely]] {
return false; return false;
} }
return true; return true;

View File

@ -1,55 +1,43 @@
module; module;
#include "include/utils/hook_helper.hpp"
export module thread_list; export module thread_list;
import hook_helper;
namespace lsplant::art::thread_list { namespace lsplant::art::thread_list {
export class ScopedSuspendAll { export class ScopedSuspendAll {
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, constructor, ScopedSuspendAll *thiz, const char *cause, inline static MemberFunction<"_ZN3art16ScopedSuspendAllC2EPKcb", ScopedSuspendAll,
bool long_suspend) { void(const char *, bool)>
if (thiz && constructorSym) [[likely]] { constructor_;
return constructorSym(thiz, cause, long_suspend); inline static MemberFunction<"_ZN3art16ScopedSuspendAllD2Ev", ScopedSuspendAll, void()>
} else { destructor_;
SuspendVM();
}
}
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, destructor, ScopedSuspendAll *thiz) { inline static Function<"_ZN3art3Dbg9SuspendVMEv", void()> SuspendVM_;
if (thiz && destructorSym) [[likely]] { inline static Function<"_ZN3art3Dbg8ResumeVMEv", void()> ResumeVM_;
return destructorSym(thiz);
} else {
ResumeVM();
}
}
CREATE_FUNC_SYMBOL_ENTRY(void, SuspendVM) {
if (SuspendVMSym) [[likely]] {
SuspendVMSym();
}
}
CREATE_FUNC_SYMBOL_ENTRY(void, ResumeVM) {
if (ResumeVMSym) [[likely]] {
ResumeVMSym();
}
}
public: public:
ScopedSuspendAll(const char *cause, bool long_suspend) { ScopedSuspendAll(const char *cause, bool long_suspend) {
constructor(this, cause, long_suspend); if (constructor_) {
constructor_(this, cause, long_suspend);
} else if (SuspendVM_) {
SuspendVM_();
}
} }
~ScopedSuspendAll() { destructor(this); } ~ScopedSuspendAll() {
if (destructor_) {
destructor_(this);
} else if (ResumeVM_) {
ResumeVM_();
}
}
static bool Init(const HookHandler &handler) { static bool Init(const HookHandler &handler) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(constructor, "_ZN3art16ScopedSuspendAllC2EPKcb") && if (!handler.dlsym(constructor_) && !handler.dlsym(SuspendVM_)) [[unlikely]] {
!RETRIEVE_FUNC_SYMBOL(SuspendVM, "_ZN3art3Dbg9SuspendVMEv")) [[unlikely]] {
return false; return false;
} }
if (!RETRIEVE_MEM_FUNC_SYMBOL(destructor, "_ZN3art16ScopedSuspendAllD2Ev") && if (!handler.dlsym(destructor_) && !handler.dlsym(ResumeVM_)) [[unlikely]] {
!RETRIEVE_FUNC_SYMBOL(ResumeVM, "_ZN3art3Dbg8ResumeVMEv")) [[unlikely]] {
return false; return false;
} }
return true; return true;

View File

@ -7,61 +7,47 @@
#include "lsplant.hpp" #include "lsplant.hpp"
#include "type_traits.hpp" #include "type_traits.hpp"
#if defined(__LP64__)
#define LP_SELECT(lp32, lp64) lp64
#else
#define LP_SELECT(lp32, lp64) lp32
#endif
#define CREATE_HOOK_STUB_ENTRY(SYM, RET, FUNC, PARAMS, DEF) \
inline static struct : public lsplant::Hooker<RET PARAMS, SYM>{ \
inline static RET replace PARAMS DEF} FUNC
#define CREATE_MEM_HOOK_STUB_ENTRY(SYM, RET, FUNC, PARAMS, DEF) \
inline static struct : public lsplant::MemHooker<RET PARAMS, SYM>{ \
inline static RET replace PARAMS DEF} FUNC
#define RETRIEVE_FUNC_SYMBOL(name, ...) \
(name##Sym = reinterpret_cast<name##Type>(lsplant::Dlsym(handler, __VA_ARGS__)))
#define RETRIEVE_MEM_FUNC_SYMBOL(name, ...) \
(name##Sym = reinterpret_cast<name##Type::FunType>(lsplant::Dlsym(handler, __VA_ARGS__)))
#define RETRIEVE_FIELD_SYMBOL(name, ...) \
(name = reinterpret_cast<decltype(name)>(lsplant::Dlsym(handler, __VA_ARGS__)))
#define CREATE_FUNC_SYMBOL_ENTRY(ret, func, ...) \
typedef ret (*func##Type)(__VA_ARGS__); \
inline static ret (*func##Sym)(__VA_ARGS__); \
inline static ret func(__VA_ARGS__)
#define CREATE_MEM_FUNC_SYMBOL_ENTRY(ret, func, thiz, ...) \
using func##Type = lsplant::MemberFunction<ret(__VA_ARGS__)>; \
inline static func##Type func##Sym; \
inline static ret func(thiz, ##__VA_ARGS__)
namespace lsplant { namespace lsplant {
using HookHandler = InitInfo;
template <size_t N> template <size_t N>
struct FixedString { struct FixedString {
consteval inline FixedString(const char (&str)[N]) { std::copy_n(str, N, data); } consteval FixedString(const char (&str)[N]) { std::copy_n(str, N, data); }
#if defined(__LP64__)
template <size_t M>
consteval FixedString(const char (&)[M], const char (&str)[N]) : FixedString(str) {}
#else
template <size_t M>
consteval FixedString(const char (&str)[N], const char (&)[M]) : FixedString(str) {}
#endif
char data[N] = {}; char data[N] = {};
}; };
inline void *Dlsym(const HookHandler &handle, const char *name, bool match_prefix = false) { template <FixedString, typename>
if (auto match = handle.art_symbol_resolver(name); match) { struct Function;
return match;
} else if (match_prefix && handle.art_symbol_prefix_resolver) { template <FixedString, typename, typename>
return handle.art_symbol_prefix_resolver(name); struct MemberFunction;
}
return nullptr; template <FixedString, typename T>
} struct Field {
[[gnu::always_inline]] T *operator->() { return field_; }
[[gnu::always_inline]] T &operator*() { return *field_; }
[[gnu::always_inline]] operator bool() { return field_ != nullptr; }
private:
friend struct HookHandler;
T *field_;
};
template <FixedString, typename>
struct Hooker;
template <FixedString, typename, typename>
struct MemberHooker;
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> || std::is_same_v<Class, T>)
inline static auto memfun_cast(Return (*func)(T *, Args...)) { inline auto memfun_cast(Return (*func)(T *, Args...)) {
union { union {
Return (Class::*f)(Args...); Return (Class::*f)(Args...);
@ -79,102 +65,106 @@ inline auto memfun_cast(Return (*func)(T *, Args...)) {
return memfun_cast<T>(func); return memfun_cast<T>(func);
} }
template <typename, typename = void> struct HookHandler {
class MemberFunction; HookHandler(const InitInfo &info) : info_(info) {}
template <FixedString Sym, typename This, typename Ret, typename... Args>
[[gnu::always_inline]] bool dlsym(MemberFunction<Sym, This, Ret(Args...)> &function,
bool match_prefix = false) const {
return function.function_ = memfun_cast<This>(
reinterpret_cast<Ret (*)(This *, Args...)>(dlsym<Sym>(match_prefix)));
}
template <typename This, typename Return, typename... Args> template <FixedString Sym, typename Ret, typename... Args>
class MemberFunction<Return(Args...), This> { [[gnu::always_inline]] bool dlsym(Function<Sym, Ret(Args...)> &function,
using SelfType = MemberFunction<Return(This *, Args...), This>; bool match_prefix = false) const {
using ThisType = std::conditional_t<std::is_same_v<This, void>, SelfType, This>; return function.function_ = reinterpret_cast<Ret (*)(Args...)>(dlsym<Sym>(match_prefix));
using MemFunType = Return (ThisType::*)(Args...); }
public: template <FixedString Sym, typename T>
using FunType = Return (*)(This *, Args...); [[gnu::always_inline]] bool dlsym(Field<Sym, T> &field, bool match_prefix = false) const {
return field.field_ = reinterpret_cast<T *>(dlsym<Sym>(match_prefix));
}
template <FixedString Sym, typename Ret, typename... Args>
[[gnu::always_inline]] bool hook(Hooker<Sym, Ret(Args...)> &hooker) const {
return hooker.function_ = reinterpret_cast<Ret (*)(Args...)>(
hook(dlsym<Sym>(), reinterpret_cast<void *>(hooker.replace_)));
}
template <FixedString Sym, typename This, typename Ret, typename... Args>
[[gnu::always_inline]] bool hook(MemberHooker<Sym, This, Ret(Args...)> &hooker) const {
return hooker.function_ = memfun_cast<This>(reinterpret_cast<Ret (*)(This *, Args...)>(
hook(dlsym<Sym>(), reinterpret_cast<void *>(hooker.replace_))));
}
template <typename... T>
[[gnu::always_inline]] bool hook(T &...args) const {
return (hook(args) || ...);
}
private: private:
MemFunType f_ = nullptr; const InitInfo &info_;
public: template <FixedString Sym>
MemberFunction() = default; [[gnu::always_inline]] void *dlsym(bool match_prefix = false) const {
if (auto match = info_.art_symbol_resolver(Sym.data); match) {
MemberFunction(FunType f) : f_(memfun_cast<ThisType>(f)) {} return match;
MemberFunction(MemFunType f) : f_(f) {}
Return operator()(This *thiz, Args... args) {
return (reinterpret_cast<ThisType *>(thiz)->*f_)(std::forward<Args>(args)...);
}
inline operator bool() { return f_ != nullptr; }
};
// deduction guide
template <typename This, typename Return, typename... Args>
MemberFunction(Return (*f)(This *, Args...)) -> MemberFunction<Return(Args...), This>;
template <typename This, typename Return, typename... Args>
MemberFunction(Return (This::*f)(Args...)) -> MemberFunction<Return(Args...), This>;
template <typename, FixedString>
struct Hooker;
template <typename Ret, FixedString Sym, typename... Args>
struct Hooker<Ret(Args...), Sym> {
inline static Ret (*backup)(Args...) = nullptr;
inline static constexpr std::string_view sym = Sym.data;
};
template <typename, FixedString>
struct MemHooker;
template <typename Ret, typename This, FixedString Sym, typename... Args>
struct MemHooker<Ret(This, Args...), Sym> {
inline static MemberFunction<Ret(Args...)> backup;
inline static constexpr std::string_view sym = Sym.data;
};
template <typename T>
concept HookerType = requires(T a) {
a.backup;
a.replace;
};
template <HookerType T>
inline static bool HookSymNoHandle(const HookHandler &handler, void *original, T &arg) {
if (original) {
if constexpr (is_instance_v<decltype(arg.backup), MemberFunction>) {
void *backup = handler.inline_hooker(original, reinterpret_cast<void *>(arg.replace));
arg.backup = reinterpret_cast<typename decltype(arg.backup)::FunType>(backup);
} else {
arg.backup = reinterpret_cast<decltype(arg.backup)>(
handler.inline_hooker(original, reinterpret_cast<void *>(arg.replace)));
} }
return true; if (match_prefix && info_.art_symbol_prefix_resolver) {
} else { return info_.art_symbol_prefix_resolver(Sym.data);
return false; }
return nullptr;
} }
}
template <HookerType T> void *hook(void *original, void *replace) const {
inline static bool HookSym(const HookHandler &handler, T &arg) { if (original) {
auto original = handler.art_symbol_resolver(arg.sym); return info_.inline_hooker(original, replace);
return HookSymNoHandle(handler, original, arg); }
} return nullptr;
template <HookerType T, HookerType... Args>
inline static bool HookSyms(const HookHandler &handle, T &first, Args &...rest) {
if (!(HookSym(handle, first) || ... || HookSym(handle, rest))) {
__android_log_print(ANDROID_LOG_ERROR,
#ifdef LOG_TAG
LOG_TAG,
#else
"HookHelper",
#endif
"Hook Fails: %*s", static_cast<int>(first.sym.size()),
first.sym.data());
return false;
} }
return true; };
}
template <FixedString Sym, typename Ret, typename... Args>
struct Function<Sym, Ret(Args...)> {
[[gnu::always_inline]] constexpr Ret operator()(Args... args) { return function_(args...); }
[[gnu::always_inline]] operator bool() { return function_ != nullptr; }
auto operator&() const { return function_; }
private:
friend struct HookHandler;
Ret (*function_)(Args...) = nullptr;
};
template <FixedString Sym, typename This, typename Ret, typename... Args>
struct MemberFunction<Sym, This, Ret(Args...)> {
[[gnu::always_inline]] constexpr Ret operator()(This *thiz, Args... args) {
return (reinterpret_cast<ThisType *>(thiz)->*function_)(args...);
}
[[gnu::always_inline]] operator bool() { return function_ != nullptr; }
private:
friend struct HookHandler;
using ThisType = std::conditional_t<std::is_same_v<This, void>, MemberFunction, This>;
Ret (ThisType::*function_)(Args...) = nullptr;
};
template <FixedString Sym, typename Ret, typename... Args>
struct Hooker<Sym, Ret(Args...)> : Function<Sym, Ret(Args...)> {
[[gnu::always_inline]] constexpr Hooker(Ret (*replace)(Args...)) : replace_(replace) {};
private:
friend struct HookHandler;
[[maybe_unused]] Ret (*replace_)(Args...) = nullptr;
};
template <FixedString Sym, typename This, typename Ret, typename... Args>
struct MemberHooker<Sym, This, Ret(Args...)> : MemberFunction<Sym, This, Ret(Args...)> {
[[gnu::always_inline]] constexpr MemberHooker(Ret (*replace)(This *, Args...))
: replace_(replace) {};
private:
friend struct HookHandler;
[[maybe_unused]] Ret (*replace_)(This *, Args...) = nullptr;
};
} // namespace lsplant } // namespace lsplant

View File

@ -0,0 +1,16 @@
module;
#include "hook_helper.hpp"
export module hook_helper;
export namespace lsplant {
using lsplant::Field;
using lsplant::FixedString;
using lsplant::Function;
using lsplant::Hooker;
using lsplant::HookHandler;
using lsplant::MemberFunction;
using lsplant::MemberHooker;
using lsplant::memfun_cast;
} // namespace lsplant

View File

@ -1,14 +1,16 @@
#include <android/api-level.h> #include <android/api-level.h>
#include <bits/sysconf.h> #include <bits/sysconf.h>
#include <jni.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/system_properties.h> #include <sys/system_properties.h>
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <bit> #include <bit>
#include <string_view>
#include <tuple>
#include "logging.hpp" #include "logging.hpp"
#include "utils/hook_helper.hpp"
import dex_builder; import dex_builder;
import lsplant; import lsplant;
@ -26,6 +28,7 @@ import jit_code_cache;
import jni_id_manager; import jni_id_manager;
import dex_file; import dex_file;
import jit; import jit;
import hook_helper;
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas" #pragma clang diagnostic ignored "-Wunknown-pragmas"
@ -42,8 +45,8 @@ using art::Instrumentation;
using art::Runtime; using art::Runtime;
using art::Thread; using art::Thread;
using art::gc::ScopedGCCriticalSection; using art::gc::ScopedGCCriticalSection;
using art::jit::JitCodeCache;
using art::jit::Jit; using art::jit::Jit;
using art::jit::JitCodeCache;
using art::jni::JniIdManager; using art::jni::JniIdManager;
using art::mirror::Class; using art::mirror::Class;
using art::thread_list::ScopedSuspendAll; using art::thread_list::ScopedSuspendAll;
@ -256,9 +259,6 @@ inline void UpdateTrampoline(uint8_t offset) {
} }
bool InitNative(JNIEnv *env, const HookHandler &handler) { bool InitNative(JNIEnv *env, const HookHandler &handler) {
if (!handler.inline_hooker || !handler.inline_unhooker || !handler.art_symbol_resolver) {
return false;
}
if (!ArtMethod::Init(env, handler)) { if (!ArtMethod::Init(env, handler)) {
LOGE("Failed to init art method"); LOGE("Failed to init art method");
return false; return false;
@ -482,8 +482,8 @@ std::tuple<jclass, jfieldID, jmethodID, jmethodID> BuildDex(JNIEnv *env, jobject
mprotect(target, image.size(), PROT_READ); mprotect(target, image.size(), PROT_READ);
std::string err_msg; std::string err_msg;
const auto *dex = DexFile::OpenMemory( const auto *dex = DexFile::OpenMemory(
target, image.size(), generated_source_name.empty() ? "lsplant" : generated_source_name, reinterpret_cast<const uint8_t *>(target), image.size(),
&err_msg); generated_source_name.empty() ? "lsplant" : generated_source_name, &err_msg);
if (!dex) { if (!dex) {
LOGE("Failed to open memory dex: %s", err_msg.data()); LOGE("Failed to open memory dex: %s", err_msg.data());
} else { } else {
@ -694,6 +694,10 @@ inline namespace v2 {
using ::lsplant::IsHooked; using ::lsplant::IsHooked;
[[maybe_unused]] bool Init(JNIEnv *env, const InitInfo &info) { [[maybe_unused]] bool Init(JNIEnv *env, const InitInfo &info) {
if (!info.inline_hooker || !info.inline_unhooker || !info.art_symbol_resolver ||
!info.art_symbol_prefix_resolver) {
return false;
}
bool static kInit = InitConfig(info) && InitJNI(env) && InitNative(env, info); bool static kInit = InitConfig(info) && InitJNI(env) && InitNative(env, info);
return kInit; return kInit;
} }