mirror of
https://github.com/LSPosed/LSPlant.git
synced 2025-05-04 20:42:02 +08:00
Workaround UpdateMethodsCode inlined (#9)
This commit is contained in:
parent
3d54cde6aa
commit
e49bdad9b0
@ -1,69 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
#include "runtime/art_method.hpp"
|
||||
|
||||
namespace lsplant::art {
|
||||
|
||||
class Instrumentation {
|
||||
inline static ArtMethod *MaybeUseBackupMethod(ArtMethod *art_method, const void *quick_code) {
|
||||
std::shared_lock lk(hooked_methods_lock_);
|
||||
if (auto found = hooked_methods_.find(art_method);
|
||||
found != hooked_methods_.end() && art_method->GetEntryPoint() != quick_code)
|
||||
[[unlikely]] {
|
||||
LOGD("Propagate update method code %p for hooked method %s to its backup", quick_code,
|
||||
art_method->PrettyMethod().c_str());
|
||||
return found->second.second;
|
||||
}
|
||||
return art_method;
|
||||
}
|
||||
|
||||
CREATE_MEM_HOOK_STUB_ENTRY(
|
||||
"_ZN3art15instrumentation15Instrumentation21UpdateMethodsCodeImplEPNS_9ArtMethodEPKv", void,
|
||||
UpdateMethodsCodeImpl,
|
||||
(Instrumentation * thiz, ArtMethod *art_method, const void *quick_code),
|
||||
{ backup(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code); });
|
||||
|
||||
CREATE_MEM_HOOK_STUB_ENTRY(
|
||||
"_ZN3art15instrumentation15Instrumentation17UpdateMethodsCodeEPNS_9ArtMethodEPKv", void,
|
||||
UpdateMethodsCode, (Instrumentation * thiz, ArtMethod *art_method, const void *quick_code),
|
||||
{
|
||||
if (UpdateMethodsCodeImpl.backup) {
|
||||
UpdateMethodsCodeImpl.backup(thiz, MaybeUseBackupMethod(art_method, quick_code),
|
||||
quick_code);
|
||||
} else {
|
||||
backup(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code);
|
||||
}
|
||||
});
|
||||
CREATE_MEM_HOOK_STUB_ENTRY(
|
||||
"_ZN3art15instrumentation15Instrumentation17UpdateMethodsCodeEPNS_6mirror9ArtMethodEPKvS6_b",
|
||||
void, UpdateMethodsCodeWithProtableCode,
|
||||
(Instrumentation * thiz, ArtMethod *art_method, const void *quick_code,
|
||||
const void *portable_code, bool have_portable_code),
|
||||
{
|
||||
backup(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code, portable_code,
|
||||
have_portable_code);
|
||||
});
|
||||
|
||||
public:
|
||||
static bool Init(const HookHandler &handler) {
|
||||
int sdk_int = GetAndroidApiLevel();
|
||||
if (sdk_int < __ANDROID_API_M__) [[unlikely]] {
|
||||
if (!HookSyms(handler, UpdateMethodsCodeWithProtableCode)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!HookSyms(handler, UpdateMethodsCode)) {
|
||||
return false;
|
||||
}
|
||||
if (sdk_int >= __ANDROID_API_N__) [[likely]] {
|
||||
if (!HookSyms(handler, UpdateMethodsCodeImpl)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace lsplant::art
|
60
lsplant/src/main/jni/art/mirror/class.hpp
Normal file
60
lsplant/src/main/jni/art/mirror/class.hpp
Normal file
@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
namespace lsplant::art {
|
||||
|
||||
namespace dex {
|
||||
class ClassDef {};
|
||||
} // namespace dex
|
||||
|
||||
namespace mirror {
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(const dex::ClassDef *, GetClassDef, Class *thiz) {
|
||||
if (GetClassDefSym) [[likely]]
|
||||
return GetClassDefSym(thiz);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *GetDescriptor(std::string *storage) {
|
||||
if (GetDescriptorSym) {
|
||||
return GetDescriptor(this, storage);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string GetDescriptor() {
|
||||
std::string storage;
|
||||
return GetDescriptor(&storage);
|
||||
}
|
||||
|
||||
const dex::ClassDef *GetClassDef() {
|
||||
if (GetClassDefSym) return GetClassDef(this);
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mirror
|
||||
} // namespace lsplant::art
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "art/mirror/class.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
namespace lsplant::art {
|
||||
@ -125,6 +126,11 @@ public:
|
||||
return PrettyMethod(this, with_signature);
|
||||
}
|
||||
|
||||
mirror::Class *GetDeclaringClass() {
|
||||
return reinterpret_cast<mirror::Class *>(*reinterpret_cast<uint32_t *>(
|
||||
reinterpret_cast<uintptr_t>(this) + declaring_class_offset));
|
||||
}
|
||||
|
||||
static art::ArtMethod *FromReflectedMethod(JNIEnv *env, jobject method) {
|
||||
if (art_method_field) [[likely]] {
|
||||
return reinterpret_cast<art::ArtMethod *>(
|
||||
@ -221,6 +227,8 @@ public:
|
||||
field_offset);
|
||||
};
|
||||
access_flags_offset = get_offset_from_art_method("accessFlags", "I");
|
||||
declaring_class_offset =
|
||||
get_offset_from_art_method("declaringClass", "Ljava/lang/Class;");
|
||||
if (sdk_int == __ANDROID_API_L__) {
|
||||
entry_point_offset =
|
||||
get_offset_from_art_method("entryPointFromQuickCompiledCode", "J");
|
||||
@ -229,6 +237,7 @@ public:
|
||||
data_offset = get_offset_from_art_method("entryPointFromJni", "J");
|
||||
}
|
||||
}
|
||||
LOGD("ArtMethod::declaring_class offset: %zu", declaring_class_offset);
|
||||
LOGD("ArtMethod::entrypoint offset: %zu", entry_point_offset);
|
||||
LOGD("ArtMethod::data offset: %zu", data_offset);
|
||||
LOGD("ArtMethod::access_flags offset: %zu", access_flags_offset);
|
||||
@ -279,7 +288,7 @@ public:
|
||||
kAccCompileDontBother = kAccDefaultConflict;
|
||||
}
|
||||
}
|
||||
if (sdk_int <= __ANDROID_API_N__) {
|
||||
if (sdk_int < __ANDROID_API_N__) {
|
||||
kAccCompileDontBother = 0;
|
||||
}
|
||||
if (sdk_int <= __ANDROID_API_M__) [[unlikely]] {
|
||||
@ -312,6 +321,7 @@ private:
|
||||
inline static size_t interpreter_entry_point_offset = 0;
|
||||
inline static size_t data_offset = 0;
|
||||
inline static size_t access_flags_offset = 0;
|
||||
inline static size_t declaring_class_offset = 0;
|
||||
inline static uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;
|
||||
inline static uint32_t kAccPreCompiled = 0x00200000;
|
||||
inline static uint32_t kAccCompileDontBother = 0x02000000;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "art/runtime/art_method.hpp"
|
||||
#include "art/runtime/obj_ptr.h"
|
||||
#include "art/thread.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
@ -35,6 +36,59 @@ private:
|
||||
return backup(art_method);
|
||||
});
|
||||
|
||||
static auto GetBackupMethods(mirror::Class *mirror_class) {
|
||||
std::list<std::tuple<art::ArtMethod *, void *>> out;
|
||||
auto class_def = mirror_class->GetClassDef();
|
||||
if (!class_def) return out;
|
||||
std::shared_lock lk(hooked_classes_lock_);
|
||||
if (auto found = hooked_classes_.find(class_def); found != hooked_classes_.end()) {
|
||||
LOGD("Before fixup %s, backup hooked methods' trampoline",
|
||||
mirror_class->GetDescriptor().c_str());
|
||||
for (auto method : found->second) {
|
||||
out.emplace_back(method, method->GetEntryPoint());
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void FixTrampoline(const std::list<std::tuple<art::ArtMethod *, void *>> &methods) {
|
||||
std::shared_lock lk(hooked_methods_lock_);
|
||||
for (const auto &[art_method, old_trampoline] : methods) {
|
||||
if (auto found = hooked_methods_.find(art_method); found != hooked_methods_.end()) {
|
||||
if (auto new_trampoline = art_method->GetEntryPoint();
|
||||
new_trampoline != old_trampoline) {
|
||||
found->second.second->SetEntryPoint(new_trampoline);
|
||||
art_method->SetEntryPoint(old_trampoline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CREATE_MEM_HOOK_STUB_ENTRY(
|
||||
"_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE", void,
|
||||
FixupStaticTrampolines, (ClassLinker * thiz, ObjPtr<mirror::Class> mirror_class), {
|
||||
auto backup_methods = GetBackupMethods(mirror_class);
|
||||
backup(thiz, mirror_class);
|
||||
FixTrampoline(backup_methods);
|
||||
});
|
||||
|
||||
CREATE_MEM_HOOK_STUB_ENTRY(
|
||||
"_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6ThreadENS_6ObjPtrINS_6mirror5ClassEEE",
|
||||
void, FixupStaticTrampolinesWithThread,
|
||||
(ClassLinker * thiz, art::Thread *self, ObjPtr<mirror::Class> mirror_class), {
|
||||
auto backup_methods = GetBackupMethods(mirror_class);
|
||||
backup(thiz, self, mirror_class);
|
||||
FixTrampoline(backup_methods);
|
||||
});
|
||||
|
||||
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6mirror5ClassE",
|
||||
void, FixupStaticTrampolinesRaw,
|
||||
(ClassLinker * thiz, mirror::Class *mirror_class), {
|
||||
auto backup_methods = GetBackupMethods(mirror_class);
|
||||
backup(thiz, mirror_class);
|
||||
FixTrampoline(backup_methods);
|
||||
});
|
||||
|
||||
public:
|
||||
static bool Init(const HookHandler &handler) {
|
||||
int sdk_int = GetAndroidApiLevel();
|
||||
@ -46,6 +100,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (!HookSyms(handler, FixupStaticTrampolinesWithThread, FixupStaticTrampolines,
|
||||
FixupStaticTrampolinesRaw)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RETRIEVE_MEM_FUNC_SYMBOL(
|
||||
SetEntryPointsToInterpreter,
|
||||
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE"))
|
||||
|
12
lsplant/src/main/jni/art/runtime/obj_ptr.h
Normal file
12
lsplant/src/main/jni/art/runtime/obj_ptr.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
template <typename MirrorType>
|
||||
class ObjPtr {
|
||||
public:
|
||||
inline MirrorType* operator->() const { return Ptr(); }
|
||||
inline MirrorType* Ptr() const { return reference_; }
|
||||
inline operator MirrorType*() const { return Ptr(); }
|
||||
|
||||
private:
|
||||
MirrorType* reference_;
|
||||
};
|
@ -5,17 +5,6 @@
|
||||
namespace lsplant::art {
|
||||
|
||||
class Thread {
|
||||
struct ObjPtr {
|
||||
void *data;
|
||||
};
|
||||
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(ObjPtr, DecodeJObject, Thread *thiz, jobject obj) {
|
||||
if (DecodeJObjectSym) [[likely]]
|
||||
return DecodeJObjectSym(thiz, obj);
|
||||
else
|
||||
return {.data = nullptr};
|
||||
}
|
||||
|
||||
CREATE_FUNC_SYMBOL_ENTRY(Thread *, CurrentFromGdb) {
|
||||
if (CurrentFromGdbSym) [[likely]]
|
||||
return CurrentFromGdbSym();
|
||||
@ -27,22 +16,11 @@ public:
|
||||
static Thread *Current() { return CurrentFromGdb(); }
|
||||
|
||||
static bool Init(const HookHandler &handler) {
|
||||
if (!RETRIEVE_MEM_FUNC_SYMBOL(DecodeJObject, "_ZNK3art6Thread13DecodeJObjectEP8_jobject"))
|
||||
[[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
if (!RETRIEVE_FUNC_SYMBOL(CurrentFromGdb, "_ZN3art6Thread14CurrentFromGdbEv"))
|
||||
[[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void *DecodeJObject(jobject obj) {
|
||||
if (DecodeJObjectSym) [[likely]] {
|
||||
return DecodeJObject(this, obj).data;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
} // namespace lsplant::art
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <shared_mutex>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "logging.hpp"
|
||||
#include "lsplant.hpp"
|
||||
@ -56,6 +57,12 @@ inline static constexpr auto kPointerSize = sizeof(void *);
|
||||
|
||||
namespace art {
|
||||
class ArtMethod;
|
||||
namespace dex {
|
||||
class ClassDef;
|
||||
}
|
||||
namespace mirror {
|
||||
class Class;
|
||||
}
|
||||
} // namespace art
|
||||
|
||||
namespace {
|
||||
@ -65,6 +72,10 @@ inline std::shared_mutex hooked_methods_lock_;
|
||||
|
||||
inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> jit_movements_;
|
||||
inline std::shared_mutex jit_movements_lock_;
|
||||
|
||||
inline std::unordered_map<const art::dex::ClassDef *, std::unordered_set<art::ArtMethod *>>
|
||||
hooked_classes_;
|
||||
inline std::shared_mutex hooked_classes_lock_;
|
||||
} // namespace
|
||||
|
||||
inline bool IsHooked(art::ArtMethod *art_method) {
|
||||
@ -77,9 +88,16 @@ inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> GetJitMovements(
|
||||
return std::move(jit_movements_);
|
||||
}
|
||||
|
||||
inline void RecordHooked(art::ArtMethod *target, jobject reflected_backup, art::ArtMethod *backup) {
|
||||
inline void RecordHooked(art::ArtMethod *target, const art::dex::ClassDef *class_def,
|
||||
jobject reflected_backup, art::ArtMethod *backup) {
|
||||
{
|
||||
std::unique_lock lk(hooked_methods_lock_);
|
||||
hooked_methods_.emplace(target, std::make_pair(reflected_backup, backup));
|
||||
hooked_methods_[target] = {reflected_backup, backup};
|
||||
}
|
||||
{
|
||||
std::unique_lock lk(hooked_classes_lock_);
|
||||
hooked_classes_[class_def].emplace(target);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RecordJitMovement(art::ArtMethod *target, art::ArtMethod *backup) {
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "art/instrumentation.hpp"
|
||||
#include "art/mirror/class.hpp"
|
||||
#include "art/runtime/art_method.hpp"
|
||||
#include "art/runtime/class_linker.hpp"
|
||||
#include "art/runtime/dex_file.hpp"
|
||||
@ -29,10 +29,10 @@ namespace lsplant {
|
||||
using art::ArtMethod;
|
||||
using art::ClassLinker;
|
||||
using art::DexFile;
|
||||
using art::Instrumentation;
|
||||
using art::Thread;
|
||||
using art::gc::ScopedGCCriticalSection;
|
||||
using art::jit::JitCodeCache;
|
||||
using art::mirror::Class;
|
||||
using art::thread_list::ScopedSuspendAll;
|
||||
|
||||
namespace {
|
||||
@ -226,8 +226,8 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) {
|
||||
LOGE("Failed to init class linker");
|
||||
return false;
|
||||
}
|
||||
if (!Instrumentation::Init(handler)) {
|
||||
LOGE("Failed to init instrumentation");
|
||||
if (!Class::Init(handler)) {
|
||||
LOGE("Failed to init mirror class");
|
||||
return false;
|
||||
}
|
||||
if (!ScopedSuspendAll::Init(handler)) {
|
||||
@ -580,7 +580,7 @@ using ::lsplant::IsHooked;
|
||||
|
||||
if (DoHook(target, hook, backup)) {
|
||||
jobject global_backup = JNI_NewGlobalRef(env, reflected_backup);
|
||||
RecordHooked(target, global_backup, backup);
|
||||
RecordHooked(target, target->GetDeclaringClass()->GetClassDef(), global_backup, backup);
|
||||
if (!is_proxy) [[likely]] {
|
||||
RecordJitMovement(target, backup);
|
||||
}
|
||||
@ -605,6 +605,16 @@ using ::lsplant::IsHooked;
|
||||
hooked_methods_.erase(it);
|
||||
}
|
||||
}
|
||||
{
|
||||
std::unique_lock lk(hooked_classes_lock_);
|
||||
if (auto it = hooked_classes_.find(target->GetDeclaringClass()->GetClassDef());
|
||||
it != hooked_classes_.end()) {
|
||||
it->second.erase(target);
|
||||
if (it->second.empty()) {
|
||||
hooked_classes_.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reflected_backup == nullptr) {
|
||||
LOGE("Unable to unhook a method that is not hooked");
|
||||
return false;
|
||||
@ -619,11 +629,8 @@ using ::lsplant::IsHooked;
|
||||
return false;
|
||||
}
|
||||
auto *art_method = ArtMethod::FromReflectedMethod(env, method);
|
||||
|
||||
if (std::shared_lock lk(hooked_methods_lock_); hooked_methods_.contains(art_method)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
std::shared_lock lk(hooked_methods_lock_);
|
||||
return hooked_methods_.contains(art_method);
|
||||
}
|
||||
|
||||
[[maybe_unused]] bool Deoptimize(JNIEnv *env, jobject method) {
|
||||
|
@ -107,7 +107,7 @@ public class UnitTest {
|
||||
|
||||
Hooker hooker = Hooker.hook(staticMethod, staticMethodReplacement, null);
|
||||
Assert.assertNotNull(hooker);
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
for (int i = 0; i < 5000; ++i) {
|
||||
Assert.assertTrue("Iter " + i, (Boolean) callStaticMethod.invoke(null));
|
||||
Assert.assertFalse("Iter " + i, (boolean) hooker.backup.invoke(null));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user