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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,61 +7,47 @@
#include "lsplant.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 {
using HookHandler = InitInfo;
template <size_t N>
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] = {};
};
inline void *Dlsym(const HookHandler &handle, const char *name, bool match_prefix = false) {
if (auto match = handle.art_symbol_resolver(name); match) {
return match;
} else if (match_prefix && handle.art_symbol_prefix_resolver) {
return handle.art_symbol_prefix_resolver(name);
}
return nullptr;
}
template <FixedString, typename>
struct Function;
template <FixedString, typename, typename>
struct MemberFunction;
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>
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 {
Return (Class::*f)(Args...);
@ -79,102 +65,106 @@ inline auto memfun_cast(Return (*func)(T *, Args...)) {
return memfun_cast<T>(func);
}
template <typename, typename = void>
class MemberFunction;
struct HookHandler {
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>
class MemberFunction<Return(Args...), This> {
using SelfType = MemberFunction<Return(This *, Args...), This>;
using ThisType = std::conditional_t<std::is_same_v<This, void>, SelfType, This>;
using MemFunType = Return (ThisType::*)(Args...);
template <FixedString Sym, typename Ret, typename... Args>
[[gnu::always_inline]] bool dlsym(Function<Sym, Ret(Args...)> &function,
bool match_prefix = false) const {
return function.function_ = reinterpret_cast<Ret (*)(Args...)>(dlsym<Sym>(match_prefix));
}
public:
using FunType = Return (*)(This *, Args...);
template <FixedString Sym, typename T>
[[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:
MemFunType f_ = nullptr;
const InitInfo &info_;
public:
MemberFunction() = default;
MemberFunction(FunType f) : f_(memfun_cast<ThisType>(f)) {}
MemberFunction(MemFunType f) : f_(f) {}
Return operator()(This *thiz, Args... args) {
return (reinterpret_cast<ThisType *>(thiz)->*f_)(std::forward<Args>(args)...);
template <FixedString Sym>
[[gnu::always_inline]] void *dlsym(bool match_prefix = false) const {
if (auto match = info_.art_symbol_resolver(Sym.data); match) {
return match;
}
if (match_prefix && info_.art_symbol_prefix_resolver) {
return info_.art_symbol_prefix_resolver(Sym.data);
}
return nullptr;
}
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) {
void *hook(void *original, void *replace) const {
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;
} else {
return false;
return info_.inline_hooker(original, replace);
}
return nullptr;
}
};
template <HookerType T>
inline static bool HookSym(const HookHandler &handler, T &arg) {
auto original = handler.art_symbol_resolver(arg.sym);
return HookSymNoHandle(handler, original, arg);
}
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_; }
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;
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

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