mirror of
https://github.com/LSPosed/LSPlant.git
synced 2025-05-04 20:42:02 +08:00
Modularize hook helper
This commit is contained in:
parent
39334baed3
commit
14b753e1dc
@ -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;
|
||||
|
@ -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__) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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) ==
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
16
lsplant/src/main/jni/include/utils/hook_helper.ixx
Normal file
16
lsplant/src/main/jni/include/utils/hook_helper.ixx
Normal 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
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user