mirror of
https://github.com/LSPosed/LSPlant.git
synced 2025-05-04 20:42:02 +08:00
parent
8c3c6e6b2c
commit
ce08314ca3
@ -2,6 +2,7 @@ module;
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "logging.hpp"
|
||||
|
||||
@ -98,6 +99,18 @@ public:
|
||||
SetAccessFlags(access_flags);
|
||||
}
|
||||
|
||||
void SetNative() {
|
||||
auto access_flags = GetAccessFlags();
|
||||
access_flags |= kAccNative;
|
||||
SetAccessFlags(access_flags);
|
||||
}
|
||||
|
||||
void SetNonNative() {
|
||||
auto access_flags = GetAccessFlags();
|
||||
access_flags &= ~kAccNative;
|
||||
SetAccessFlags(access_flags);
|
||||
}
|
||||
|
||||
bool IsPrivate() { return GetAccessFlags() & kAccPrivate; }
|
||||
bool IsProtected() { return GetAccessFlags() & kAccProtected; }
|
||||
bool IsPublic() { return GetAccessFlags() & kAccPublic; }
|
||||
@ -155,6 +168,12 @@ public:
|
||||
reinterpret_cast<uintptr_t>(this) + declaring_class_offset));
|
||||
}
|
||||
|
||||
std::unique_ptr<ArtMethod> Clone() {
|
||||
auto *method = reinterpret_cast<ArtMethod*>(::operator new(art_method_size));
|
||||
method->CopyFrom(this);
|
||||
return std::unique_ptr<ArtMethod>(method);
|
||||
}
|
||||
|
||||
void BackupTo(ArtMethod *backup) {
|
||||
SetNonCompilable();
|
||||
|
||||
|
@ -12,6 +12,7 @@ import common;
|
||||
import clazz;
|
||||
import handle;
|
||||
import hook_helper;
|
||||
import runtime;
|
||||
|
||||
namespace lsplant::art {
|
||||
export class ClassLinker {
|
||||
@ -35,6 +36,9 @@ private:
|
||||
inline static Function<"art_quick_generic_jni_trampoline", void(void *)>
|
||||
art_quick_generic_jni_trampoline_;
|
||||
|
||||
inline static Function<"_ZN3art15instrumentationL19GetOptimizedCodeForEPNS_9ArtMethodE",
|
||||
void *(ArtMethod *)> GetOptimizedCodeFor_;
|
||||
|
||||
inline static art::ArtMethod *MayGetBackup(art::ArtMethod *method) {
|
||||
if (auto backup = IsHooked(method); backup) [[unlikely]] {
|
||||
method = backup;
|
||||
@ -161,7 +165,7 @@ private:
|
||||
};
|
||||
|
||||
public:
|
||||
static bool Init(const HookHandler &handler) {
|
||||
static bool Init(JNIEnv *env, const HookHandler &handler) {
|
||||
int sdk_int = GetAndroidApiLevel();
|
||||
|
||||
if (sdk_int >= __ANDROID_API_N__ && sdk_int < __ANDROID_API_T__) {
|
||||
@ -187,16 +191,32 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (!handler.dlsym(SetEntryPointsToInterpreter_)) [[unlikely]] {
|
||||
if (handler.dlsym(GetOptimizedCodeFor_, true)) [[likely]] {
|
||||
auto obj = JNI_FindClass(env, "java/lang/Object");
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
auto method = JNI_GetMethodID(env, obj, "equals", "(Ljava/lang/Object;)Z");
|
||||
if (!method) {
|
||||
return false;
|
||||
}
|
||||
auto dummy = ArtMethod::FromReflectedMethod(
|
||||
env, JNI_ToReflectedMethod(env, obj, method, false).get())->Clone();
|
||||
JavaDebuggableGuard guard;
|
||||
dummy->SetNonNative();
|
||||
art_quick_to_interpreter_bridge_ = GetOptimizedCodeFor_(dummy.get());
|
||||
dummy->SetNative();
|
||||
art_quick_generic_jni_trampoline_ = GetOptimizedCodeFor_(dummy.get());
|
||||
} else if (!handler.dlsym(SetEntryPointsToInterpreter_)) [[unlikely]] {
|
||||
if (!handler.dlsym(art_quick_to_interpreter_bridge_)) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
if (!handler.dlsym(art_quick_generic_jni_trampoline_)) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "logging.hpp"
|
||||
|
||||
@ -87,4 +88,56 @@ public:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
export struct JavaDebuggableGuard {
|
||||
JavaDebuggableGuard() {
|
||||
while (true) {
|
||||
size_t expected = 0;
|
||||
if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel,
|
||||
std::memory_order_acquire)) {
|
||||
Runtime::Current()->SetJavaDebuggable(
|
||||
Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
|
||||
count.fetch_add(1, std::memory_order_release);
|
||||
count.notify_all();
|
||||
break;
|
||||
}
|
||||
if (expected == 1) {
|
||||
count.wait(expected, std::memory_order_acquire);
|
||||
continue;
|
||||
}
|
||||
if (count.compare_exchange_strong(expected, expected + 1, std::memory_order_acq_rel,
|
||||
std::memory_order_relaxed)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~JavaDebuggableGuard() {
|
||||
while (true) {
|
||||
size_t expected = 2;
|
||||
if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel,
|
||||
std::memory_order_acquire)) {
|
||||
Runtime::Current()->SetJavaDebuggable(
|
||||
Runtime::RuntimeDebugState::kNonJavaDebuggable);
|
||||
count.fetch_sub(1, std::memory_order_release);
|
||||
count.notify_all();
|
||||
break;
|
||||
}
|
||||
if (expected == 1) {
|
||||
count.wait(expected, std::memory_order_acquire);
|
||||
continue;
|
||||
}
|
||||
if (count.compare_exchange_strong(expected, expected - 1, std::memory_order_acq_rel,
|
||||
std::memory_order_relaxed)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
inline static std::atomic_size_t count{0};
|
||||
static_assert(std::atomic_size_t::is_always_lock_free, "Unsupported architecture");
|
||||
static_assert(std::is_same_v<std::atomic_size_t::value_type, size_t>,
|
||||
"Unsupported architecture");
|
||||
};
|
||||
} // namespace lsplant::art
|
||||
|
@ -129,6 +129,10 @@ 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_; }
|
||||
Function &operator=(void *function) {
|
||||
function_ = reinterpret_cast<decltype(function_)>(function);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct HookHandler;
|
||||
|
@ -50,6 +50,7 @@ using art::jit::JitCodeCache;
|
||||
using art::jni::JniIdManager;
|
||||
using art::mirror::Class;
|
||||
using art::thread_list::ScopedSuspendAll;
|
||||
using art::JavaDebuggableGuard;
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
@ -272,7 +273,11 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) {
|
||||
LOGE("Failed to init mirror class");
|
||||
return false;
|
||||
}
|
||||
if (!ClassLinker::Init(handler)) {
|
||||
if (!Runtime::Init(handler)) {
|
||||
LOGE("Failed to init runtime");
|
||||
return false;
|
||||
}
|
||||
if (!ClassLinker::Init(env, handler)) {
|
||||
LOGE("Failed to init class linker");
|
||||
return false;
|
||||
}
|
||||
@ -304,10 +309,6 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) {
|
||||
LOGE("Failed to init jni id manager");
|
||||
return false;
|
||||
}
|
||||
if (!Runtime::Init(handler)) {
|
||||
LOGE("Failed to init runtime");
|
||||
return false;
|
||||
}
|
||||
|
||||
// This should always be the last one
|
||||
if (IsJavaDebuggable(env)) {
|
||||
@ -318,58 +319,6 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct JavaDebuggableGuard {
|
||||
JavaDebuggableGuard() {
|
||||
while (true) {
|
||||
size_t expected = 0;
|
||||
if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel,
|
||||
std::memory_order_acquire)) {
|
||||
Runtime::Current()->SetJavaDebuggable(
|
||||
Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
|
||||
count.fetch_add(1, std::memory_order_release);
|
||||
count.notify_all();
|
||||
break;
|
||||
}
|
||||
if (expected == 1) {
|
||||
count.wait(expected, std::memory_order_acquire);
|
||||
continue;
|
||||
}
|
||||
if (count.compare_exchange_strong(expected, expected + 1, std::memory_order_acq_rel,
|
||||
std::memory_order_relaxed)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~JavaDebuggableGuard() {
|
||||
while (true) {
|
||||
size_t expected = 2;
|
||||
if (count.compare_exchange_strong(expected, 1, std::memory_order_acq_rel,
|
||||
std::memory_order_acquire)) {
|
||||
Runtime::Current()->SetJavaDebuggable(
|
||||
Runtime::RuntimeDebugState::kNonJavaDebuggable);
|
||||
count.fetch_sub(1, std::memory_order_release);
|
||||
count.notify_all();
|
||||
break;
|
||||
}
|
||||
if (expected == 1) {
|
||||
count.wait(expected, std::memory_order_acquire);
|
||||
continue;
|
||||
}
|
||||
if (count.compare_exchange_strong(expected, expected - 1, std::memory_order_acq_rel,
|
||||
std::memory_order_relaxed)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
inline static std::atomic_size_t count{0};
|
||||
static_assert(std::atomic_size_t::is_always_lock_free, "Unsupported architecture");
|
||||
static_assert(std::is_same_v<std::atomic_size_t::value_type, size_t>,
|
||||
"Unsupported architecture");
|
||||
};
|
||||
|
||||
std::tuple<jclass, jfieldID, jmethodID, jmethodID> BuildDex(JNIEnv *env, jobject class_loader,
|
||||
std::string_view shorty, bool is_static,
|
||||
std::string_view method_name,
|
||||
|
Loading…
x
Reference in New Issue
Block a user