Stripe pending hook (#1751)
This commit is contained in:
parent
c6e58731ed
commit
e15d39672a
|
|
@ -25,7 +25,6 @@
|
||||||
#ifndef LSPOSED_ART_METHOD_H
|
#ifndef LSPOSED_ART_METHOD_H
|
||||||
#define LSPOSED_ART_METHOD_H
|
#define LSPOSED_ART_METHOD_H
|
||||||
|
|
||||||
#include "jni/pending_hooks.h"
|
|
||||||
#include <HookMain.h>
|
#include <HookMain.h>
|
||||||
|
|
||||||
namespace art {
|
namespace art {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "jni_env_ext.h"
|
#include "jni_env_ext.h"
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "jni/pending_hooks.h"
|
|
||||||
#include "jni/yahfa.h"
|
#include "jni/yahfa.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "HookMain.h"
|
#include "HookMain.h"
|
||||||
|
|
@ -45,57 +44,11 @@ namespace art {
|
||||||
SetEntryPointsToInterpreterSym(thiz, art_method);
|
SetEntryPointsToInterpreterSym(thiz, art_method);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
|
||||||
static void MaybeDelayHook(void *clazz_ptr) {
|
|
||||||
art::mirror::Class mirror_class(clazz_ptr);
|
|
||||||
auto class_def = mirror_class.GetClassDef();
|
|
||||||
bool should_intercept = class_def && lspd::IsClassPending(class_def);
|
|
||||||
if (should_intercept) [[unlikely]] {
|
|
||||||
LOGD("Pending hook for %p (%s)", clazz_ptr,
|
|
||||||
art::mirror::Class(clazz_ptr).GetDescriptor().c_str());
|
|
||||||
lspd::Context::GetInstance()->CallOnPostFixupStaticTrampolines(clazz_ptr);
|
|
||||||
lspd::DonePendingHook(class_def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CREATE_MEM_HOOK_STUB_ENTRIES(
|
|
||||||
"_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE",
|
|
||||||
void, FixupStaticTrampolines, (void * thiz, void * clazz_ptr), {
|
|
||||||
backup(thiz, clazz_ptr);
|
|
||||||
MaybeDelayHook(clazz_ptr);
|
|
||||||
});
|
|
||||||
|
|
||||||
CREATE_MEM_HOOK_STUB_ENTRIES(
|
|
||||||
"_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6ThreadENS_6ObjPtrINS_6mirror5ClassEEE",
|
|
||||||
void, FixupStaticTrampolinesWithThread,
|
|
||||||
(void * thiz, void * self, void * clazz_ptr), {
|
|
||||||
backup(thiz, self, clazz_ptr);
|
|
||||||
MaybeDelayHook(clazz_ptr);
|
|
||||||
});
|
|
||||||
|
|
||||||
CREATE_MEM_HOOK_STUB_ENTRIES(
|
|
||||||
"_ZN3art11ClassLinker20MarkClassInitializedEPNS_6ThreadENS_6HandleINS_6mirror5ClassEEE",
|
|
||||||
void*, MarkClassInitialized, (void * thiz, void * self, uint32_t * clazz_ptr), {
|
|
||||||
void *result = backup(thiz, self, clazz_ptr);
|
|
||||||
auto ptr = reinterpret_cast<void *>(*clazz_ptr);
|
|
||||||
MaybeDelayHook(ptr);
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, MakeInitializedClassesVisiblyInitialized, void *thiz,
|
|
||||||
void *self, bool wait) {
|
|
||||||
if (MakeInitializedClassesVisiblyInitializedSym) [[likely]]
|
|
||||||
MakeInitializedClassesVisiblyInitializedSym(thiz, self, wait);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CREATE_HOOK_STUB_ENTRIES(
|
CREATE_HOOK_STUB_ENTRIES(
|
||||||
"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv",
|
"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv",
|
||||||
bool, ShouldUseInterpreterEntrypoint, (void * art_method,
|
bool, ShouldUseInterpreterEntrypoint, (void * art_method,
|
||||||
const void *quick_code), {
|
const void *quick_code), {
|
||||||
if (quick_code != nullptr &&
|
if (quick_code != nullptr && lspd::isHooked(art_method)) [[unlikely]] {
|
||||||
(lspd::isHooked(art_method) ||
|
|
||||||
lspd::IsMethodPending(art_method))) [[unlikely]] {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return backup(art_method, quick_code);
|
return backup(art_method, quick_code);
|
||||||
|
|
@ -106,7 +59,7 @@ namespace art {
|
||||||
|
|
||||||
CREATE_HOOK_STUB_ENTRIES("_ZN3art11interpreter29ShouldStayInSwitchInterpreterEPNS_9ArtMethodE",
|
CREATE_HOOK_STUB_ENTRIES("_ZN3art11interpreter29ShouldStayInSwitchInterpreterEPNS_9ArtMethodE",
|
||||||
bool, ShouldStayInSwitchInterpreter ,(void* art_method), {
|
bool, ShouldStayInSwitchInterpreter ,(void* art_method), {
|
||||||
if (lspd::isHooked(art_method) || lspd::IsMethodPending(art_method)) [[unlikely]] {
|
if (lspd::isHooked(art_method)) [[unlikely]] {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return backup(art_method);
|
return backup(art_method);
|
||||||
|
|
@ -121,8 +74,6 @@ namespace art {
|
||||||
|
|
||||||
// @ApiSensitive(Level.MIDDLE)
|
// @ApiSensitive(Level.MIDDLE)
|
||||||
inline static void Setup(const SandHook::ElfImg &handle) {
|
inline static void Setup(const SandHook::ElfImg &handle) {
|
||||||
int api_level = lspd::GetAndroidApiLevel();
|
|
||||||
|
|
||||||
instance_ = new ClassLinker(nullptr); // make it nullptr
|
instance_ = new ClassLinker(nullptr); // make it nullptr
|
||||||
|
|
||||||
RETRIEVE_MEM_FUNC_SYMBOL(SetEntryPointsToInterpreter,
|
RETRIEVE_MEM_FUNC_SYMBOL(SetEntryPointsToInterpreter,
|
||||||
|
|
@ -130,26 +81,6 @@ namespace art {
|
||||||
|
|
||||||
lspd::HookSyms(handle, ShouldUseInterpreterEntrypoint, ShouldStayInSwitchInterpreter);
|
lspd::HookSyms(handle, ShouldUseInterpreterEntrypoint, ShouldStayInSwitchInterpreter);
|
||||||
|
|
||||||
if (api_level >= __ANDROID_API_R__) {
|
|
||||||
// In android R, FixupStaticTrampolines won't be called unless it's marking it as
|
|
||||||
// visiblyInitialized.
|
|
||||||
// So we miss some calls between initialized and visiblyInitialized.
|
|
||||||
// Therefore we hook the new introduced MarkClassInitialized instead
|
|
||||||
// This only happens on non-x86 devices
|
|
||||||
lspd::HookSyms(handle, MarkClassInitialized);
|
|
||||||
lspd::HookSyms(handle, FixupStaticTrampolinesWithThread, FixupStaticTrampolines);
|
|
||||||
} else {
|
|
||||||
lspd::HookSyms(handle, FixupStaticTrampolines);
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeInitializedClassesVisiblyInitialized will cause deadlock
|
|
||||||
// IsQuickToInterpreterBridge is inlined
|
|
||||||
// So we use GetSavedEntryPointOfPreCompiledMethod instead
|
|
||||||
// if (api_level >= __ANDROID_API_R__) {
|
|
||||||
// RETRIEVE_FUNC_SYMBOL(MakeInitializedClassesVisiblyInitialized,
|
|
||||||
// "_ZN3art11ClassLinker40MakeInitializedClassesVisiblyInitializedEPNS_6ThreadEb");
|
|
||||||
// }
|
|
||||||
|
|
||||||
RETRIEVE_FUNC_SYMBOL(art_quick_to_interpreter_bridge, "art_quick_to_interpreter_bridge");
|
RETRIEVE_FUNC_SYMBOL(art_quick_to_interpreter_bridge, "art_quick_to_interpreter_bridge");
|
||||||
RETRIEVE_FUNC_SYMBOL(art_quick_generic_jni_trampoline, "art_quick_generic_jni_trampoline");
|
RETRIEVE_FUNC_SYMBOL(art_quick_generic_jni_trampoline, "art_quick_generic_jni_trampoline");
|
||||||
|
|
||||||
|
|
@ -157,14 +88,6 @@ namespace art {
|
||||||
LOGD("art_quick_generic_jni_trampoline = %p", art_quick_generic_jni_trampolineSym);
|
LOGD("art_quick_generic_jni_trampoline = %p", art_quick_generic_jni_trampolineSym);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
|
||||||
void MakeInitializedClassesVisiblyInitialized(void *self, bool wait) const {
|
|
||||||
LOGD("MakeInitializedClassesVisiblyInitialized start, thiz=%p, self=%p", thiz_, self);
|
|
||||||
if (thiz_) [[likely]]
|
|
||||||
MakeInitializedClassesVisiblyInitialized(thiz_, self, wait);
|
|
||||||
else LOGW("Classlinker is nullptr");
|
|
||||||
}
|
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
void SetEntryPointsToInterpreter(void *art_method) const {
|
void SetEntryPointsToInterpreter(void *art_method) const {
|
||||||
if (art_quick_to_interpreter_bridgeSym && art_quick_generic_jni_trampolineSym) [[likely]] {
|
if (art_quick_to_interpreter_bridgeSym && art_quick_generic_jni_trampolineSym) [[likely]] {
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,28 @@ namespace art {
|
||||||
|
|
||||||
CREATE_MEM_HOOK_STUB_ENTRIES(
|
CREATE_MEM_HOOK_STUB_ENTRIES(
|
||||||
"_ZN3art15instrumentation15Instrumentation21UpdateMethodsCodeImplEPNS_9ArtMethodEPKv",
|
"_ZN3art15instrumentation15Instrumentation21UpdateMethodsCodeImplEPNS_9ArtMethodEPKv",
|
||||||
void, UpdateMethodsCode, (void * thiz, void * art_method, const void *quick_code), {
|
void, UpdateMethodsCodeImpl, (void * thiz, void * art_method, const void *quick_code), {
|
||||||
if (lspd::isHooked(art_method)) [[unlikely]] {
|
if (auto backup = lspd::isHooked(art_method);
|
||||||
LOGD("Skip update method code for hooked method %s",
|
backup && yahfa::getEntryPoint(art_method) != quick_code) [[unlikely]] {
|
||||||
|
LOGD("redirect update method code for hooked method %s to its backup",
|
||||||
art_method::PrettyMethod(art_method).c_str());
|
art_method::PrettyMethod(art_method).c_str());
|
||||||
return;
|
art_method = backup;
|
||||||
|
}
|
||||||
|
backup(thiz, art_method, quick_code);
|
||||||
|
});
|
||||||
|
|
||||||
|
CREATE_MEM_HOOK_STUB_ENTRIES(
|
||||||
|
"_ZN3art15instrumentation15Instrumentation17UpdateMethodsCodeEPNS_9ArtMethodEPKv",
|
||||||
|
void, UpdateMethodsCode, (void * thiz, void * art_method, const void *quick_code), {
|
||||||
|
if (auto backup = lspd::isHooked(art_method);
|
||||||
|
backup && yahfa::getEntryPoint(art_method) != quick_code) [[unlikely]] {
|
||||||
|
LOGD("redirect update method code for hooked method %s to its backup",
|
||||||
|
art_method::PrettyMethod(art_method).c_str());
|
||||||
|
art_method = backup;
|
||||||
|
}
|
||||||
|
// avoid calling our hook again
|
||||||
|
if (UpdateMethodsCodeImpl.backup) {
|
||||||
|
UpdateMethodsCodeImpl.backup(thiz, art_method, quick_code);
|
||||||
} else {
|
} else {
|
||||||
backup(thiz, art_method, quick_code);
|
backup(thiz, art_method, quick_code);
|
||||||
}
|
}
|
||||||
|
|
@ -40,6 +57,7 @@ namespace art {
|
||||||
|
|
||||||
inline void DisableUpdateHookedMethodsCode(const SandHook::ElfImg &handle) {
|
inline void DisableUpdateHookedMethodsCode(const SandHook::ElfImg &handle) {
|
||||||
lspd::HookSym(handle, UpdateMethodsCode);
|
lspd::HookSym(handle, UpdateMethodsCode);
|
||||||
|
lspd::HookSym(handle, UpdateMethodsCodeImpl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@
|
||||||
#include "jni/yahfa.h"
|
#include "jni/yahfa.h"
|
||||||
#include "jni/resources_hook.h"
|
#include "jni/resources_hook.h"
|
||||||
#include <art/runtime/jni_env_ext.h>
|
#include <art/runtime/jni_env_ext.h>
|
||||||
#include "jni/pending_hooks.h"
|
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "native_hook.h"
|
#include "native_hook.h"
|
||||||
#include "jni/native_api.h"
|
#include "jni/native_api.h"
|
||||||
|
|
@ -49,20 +48,6 @@ namespace lspd {
|
||||||
static constexpr uid_t kAidInjected = INJECTED_AID;
|
static constexpr uid_t kAidInjected = INJECTED_AID;
|
||||||
static constexpr uid_t kAidInet = 3003;
|
static constexpr uid_t kAidInet = 3003;
|
||||||
|
|
||||||
void Context::CallOnPostFixupStaticTrampolines(void *class_ptr) {
|
|
||||||
if (!class_ptr || !class_linker_class_ || !post_fixup_static_mid_) [[unlikely]] {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEnv *env;
|
|
||||||
vm_->GetEnv((void **) (&env), JNI_VERSION_1_4);
|
|
||||||
art::JNIEnvExt env_ext(env);
|
|
||||||
ScopedLocalRef clazz(env, env_ext.NewLocalRefer(class_ptr));
|
|
||||||
if (clazz) {
|
|
||||||
JNI_CallStaticVoidMethod(env, class_linker_class_, post_fixup_static_mid_, clazz.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Context::PreloadedDex::PreloadedDex(int fd, std::size_t size) {
|
Context::PreloadedDex::PreloadedDex(int fd, std::size_t size) {
|
||||||
LOGD("Context::PreloadedDex::PreloadedDex: fd=%d, size=%zu", fd, size);
|
LOGD("Context::PreloadedDex::PreloadedDex: fd=%d, size=%zu", fd, size);
|
||||||
auto *addr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
auto *addr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
|
@ -106,21 +91,12 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
env->DeleteLocalRef(dex_buffer);
|
env->DeleteLocalRef(dex_buffer);
|
||||||
|
|
||||||
env->GetJavaVM(&vm_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::Init() {
|
void Context::Init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::Init(JNIEnv *env) {
|
void Context::Init(JNIEnv *env) {
|
||||||
if (auto class_linker_class = FindClassFromCurrentLoader(env, kClassLinkerClassName)) {
|
|
||||||
class_linker_class_ = JNI_NewGlobalRef(env, class_linker_class);
|
|
||||||
}
|
|
||||||
post_fixup_static_mid_ = JNI_GetStaticMethodID(env, class_linker_class_,
|
|
||||||
"onPostFixupStaticTrampolines",
|
|
||||||
"(Ljava/lang/Class;)V");
|
|
||||||
|
|
||||||
if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(),
|
if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(),
|
||||||
kEntryClassName)) {
|
kEntryClassName)) {
|
||||||
entry_class_ = JNI_NewGlobalRef(env, entry_class);
|
entry_class_ = JNI_NewGlobalRef(env, entry_class);
|
||||||
|
|
@ -129,7 +105,6 @@ namespace lspd {
|
||||||
RegisterResourcesHook(env);
|
RegisterResourcesHook(env);
|
||||||
RegisterArtClassLinker(env);
|
RegisterArtClassLinker(env);
|
||||||
RegisterYahfa(env);
|
RegisterYahfa(env);
|
||||||
RegisterPendingHooks(env);
|
|
||||||
RegisterNativeAPI(env);
|
RegisterNativeAPI(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,6 @@ namespace lspd {
|
||||||
|
|
||||||
inline jobject GetCurrentClassLoader() const { return inject_class_loader_; }
|
inline jobject GetCurrentClassLoader() const { return inject_class_loader_; }
|
||||||
|
|
||||||
void CallOnPostFixupStaticTrampolines(void *class_ptr);
|
|
||||||
|
|
||||||
inline ScopedLocalRef<jclass>
|
inline ScopedLocalRef<jclass>
|
||||||
FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const {
|
FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const {
|
||||||
return FindClassFromLoader(env, GetCurrentClassLoader(), className);
|
return FindClassFromLoader(env, GetCurrentClassLoader(), className);
|
||||||
|
|
@ -65,9 +63,6 @@ namespace lspd {
|
||||||
inline static std::unique_ptr<Context> instance_ = std::make_unique<Context>();
|
inline static std::unique_ptr<Context> instance_ = std::make_unique<Context>();
|
||||||
jobject inject_class_loader_ = nullptr;
|
jobject inject_class_loader_ = nullptr;
|
||||||
jclass entry_class_ = nullptr;
|
jclass entry_class_ = nullptr;
|
||||||
JavaVM *vm_ = nullptr;
|
|
||||||
jclass class_linker_class_ = nullptr;
|
|
||||||
jmethodID post_fixup_static_mid_ = nullptr;
|
|
||||||
bool skip_ = false;
|
bool skip_ = false;
|
||||||
|
|
||||||
struct PreloadedDex {
|
struct PreloadedDex {
|
||||||
|
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of LSPosed.
|
|
||||||
*
|
|
||||||
* LSPosed is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* LSPosed is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <shared_mutex>
|
|
||||||
#include "HookMain.h"
|
|
||||||
#include "jni.h"
|
|
||||||
#include "native_util.h"
|
|
||||||
#include "pending_hooks.h"
|
|
||||||
#include "art/runtime/thread.h"
|
|
||||||
#include "art/runtime/mirror/class.h"
|
|
||||||
|
|
||||||
namespace lspd {
|
|
||||||
namespace {
|
|
||||||
std::unordered_set<const void *> pending_classes_;
|
|
||||||
std::shared_mutex pending_classes_lock_;
|
|
||||||
|
|
||||||
std::unordered_set<const void *> pending_methods_;
|
|
||||||
std::shared_mutex pending_methods_lock_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsClassPending(void *clazz) {
|
|
||||||
std::shared_lock lk(pending_classes_lock_);
|
|
||||||
return pending_classes_.contains(clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsMethodPending(void *art_method) {
|
|
||||||
bool result;
|
|
||||||
{
|
|
||||||
std::shared_lock lk(pending_methods_lock_);
|
|
||||||
result = pending_methods_.contains(art_method);
|
|
||||||
}
|
|
||||||
if (result) {
|
|
||||||
std::unique_lock lk(pending_methods_lock_);
|
|
||||||
pending_methods_.erase(art_method);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DonePendingHook(void *clazz) {
|
|
||||||
std::unique_lock lk(pending_classes_lock_);
|
|
||||||
pending_classes_.erase(clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(void, PendingHooks, recordPendingMethodNative, jobject method_ref, jclass class_ref){
|
|
||||||
auto *class_ptr = art::Thread::Current().DecodeJObject(class_ref);
|
|
||||||
auto *method = yahfa::getArtMethod(env, method_ref);
|
|
||||||
art::mirror::Class mirror_class(class_ptr);
|
|
||||||
if (auto def = mirror_class.GetClassDef(); def) [[likely]] {
|
|
||||||
LOGD("record pending: %p (%s) with %p", class_ptr, mirror_class.GetDescriptor().c_str(),
|
|
||||||
method);
|
|
||||||
// Add it for ShouldUseInterpreterEntrypoint
|
|
||||||
{
|
|
||||||
std::unique_lock lk(pending_methods_lock_);
|
|
||||||
pending_methods_.insert(method);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::unique_lock lk(pending_classes_lock_);
|
|
||||||
pending_classes_.insert(def);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOGW("fail to record pending for : %p (%s)", class_ptr,
|
|
||||||
mirror_class.GetDescriptor().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static JNINativeMethod gMethods[] = {
|
|
||||||
LSP_NATIVE_METHOD(PendingHooks, recordPendingMethodNative,
|
|
||||||
"(Ljava/lang/reflect/Method;Ljava/lang/Class;)V"),
|
|
||||||
};
|
|
||||||
|
|
||||||
void RegisterPendingHooks(JNIEnv *env) {
|
|
||||||
REGISTER_LSP_NATIVE_METHODS(PendingHooks);
|
|
||||||
}
|
|
||||||
} // namespace lspd
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of LSPosed.
|
|
||||||
*
|
|
||||||
* LSPosed is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* LSPosed is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "jni.h"
|
|
||||||
|
|
||||||
namespace lspd {
|
|
||||||
|
|
||||||
bool IsClassPending(void *);
|
|
||||||
|
|
||||||
void RegisterPendingHooks(JNIEnv *);
|
|
||||||
|
|
||||||
void DonePendingHook(void *clazz);
|
|
||||||
|
|
||||||
bool IsMethodPending(void* art_method);
|
|
||||||
|
|
||||||
} // namespace lspd
|
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
#include "yahfa.h"
|
#include "yahfa.h"
|
||||||
#include "HookMain.h"
|
#include "HookMain.h"
|
||||||
#include "native_util.h"
|
#include "native_util.h"
|
||||||
#include "pending_hooks.h"
|
|
||||||
#include "art/runtime/class_linker.h"
|
#include "art/runtime/class_linker.h"
|
||||||
#include "art/runtime/thread_list.h"
|
#include "art/runtime/thread_list.h"
|
||||||
#include "art/runtime/thread.h"
|
#include "art/runtime/thread.h"
|
||||||
|
|
@ -33,21 +32,24 @@
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
namespace {
|
namespace {
|
||||||
std::unordered_set<const void *> hooked_methods_;
|
std::unordered_map<const void *, void*> hooked_methods_;
|
||||||
std::shared_mutex hooked_methods_lock_;
|
std::shared_mutex hooked_methods_lock_;
|
||||||
|
|
||||||
std::vector<std::pair<void *, void *>> jit_movements_;
|
std::vector<std::pair<void *, void *>> jit_movements_;
|
||||||
std::shared_mutex jit_movements_lock_;
|
std::shared_mutex jit_movements_lock_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isHooked(void *art_method) {
|
void* isHooked(void *art_method) {
|
||||||
std::shared_lock lk(hooked_methods_lock_);
|
std::shared_lock lk(hooked_methods_lock_);
|
||||||
return hooked_methods_.contains(art_method);
|
if (auto found = hooked_methods_.find(art_method); found != hooked_methods_.end()) {
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void recordHooked(void *art_method) {
|
void recordHooked(void *art_method, void *backup) {
|
||||||
std::unique_lock lk(hooked_methods_lock_);
|
std::unique_lock lk(hooked_methods_lock_);
|
||||||
hooked_methods_.insert(art_method);
|
hooked_methods_.emplace(art_method, backup);
|
||||||
}
|
}
|
||||||
|
|
||||||
void recordJitMovement(void *target, void *backup) {
|
void recordJitMovement(void *target, void *backup) {
|
||||||
|
|
@ -81,16 +83,15 @@ namespace lspd {
|
||||||
if (yahfa::backupAndHookNative(env, clazz, target, hook, backup)) {
|
if (yahfa::backupAndHookNative(env, clazz, target, hook, backup)) {
|
||||||
auto *target_method = yahfa::getArtMethod(env, target);
|
auto *target_method = yahfa::getArtMethod(env, target);
|
||||||
auto *backup_method = yahfa::getArtMethod(env, backup);
|
auto *backup_method = yahfa::getArtMethod(env, backup);
|
||||||
recordHooked(target_method);
|
recordHooked(target_method, backup_method);
|
||||||
if (!is_proxy) [[likely]] recordJitMovement(target_method, backup_method);
|
if (!is_proxy) [[likely]] recordJitMovement(target_method, backup_method);
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
} else {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
}
|
||||||
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, isHooked, jobject member) {
|
LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, isHooked, jobject member) {
|
||||||
return lspd::isHooked(yahfa::getArtMethod(env, member));
|
return lspd::isHooked(yahfa::getArtMethod(env, member)) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jchar return_class,
|
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jchar return_class,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
|
|
||||||
bool isHooked(void* art_method);
|
void* isHooked(void* art_method);
|
||||||
|
|
||||||
std::vector<std::pair<void*, void*>> getJitMovements();
|
std::vector<std::pair<void*, void*>> getJitMovements();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of LSPosed.
|
|
||||||
*
|
|
||||||
* LSPosed is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* LSPosed is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.robv.android.xposed;
|
|
||||||
|
|
||||||
import static org.lsposed.lspd.nativebridge.PendingHooks.recordPendingMethodNative;
|
|
||||||
|
|
||||||
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
|
||||||
|
|
||||||
import java.lang.reflect.Executable;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public final class PendingHooks {
|
|
||||||
private static final ConcurrentHashMap<Class<?>, ConcurrentHashMap<Executable, XposedBridge.AdditionalHookInfo>>
|
|
||||||
sPendingHooks = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public static void hookPendingMethod(Class<?> clazz) {
|
|
||||||
var record = sPendingHooks.remove(clazz);
|
|
||||||
if (record != null) {
|
|
||||||
for (var hook : record.entrySet()) {
|
|
||||||
YahfaHooker.hookMethod(hook.getKey(), hook.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void recordPendingMethod(Method hookMethod,
|
|
||||||
XposedBridge.AdditionalHookInfo additionalInfo) {
|
|
||||||
var pending = sPendingHooks.computeIfAbsent(hookMethod.getDeclaringClass(), aClass -> new ConcurrentHashMap<>());
|
|
||||||
|
|
||||||
pending.put(hookMethod, additionalInfo);
|
|
||||||
recordPendingMethodNative(hookMethod, hookMethod.getDeclaringClass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -231,11 +231,7 @@ public final class XposedBridge {
|
||||||
|
|
||||||
if (newMethod) {
|
if (newMethod) {
|
||||||
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks);
|
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks);
|
||||||
if (!YahfaHooker.shouldDelayHook(targetMethod)) {
|
|
||||||
YahfaHooker.hookMethod(targetMethod, additionalInfo);
|
YahfaHooker.hookMethod(targetMethod, additionalInfo);
|
||||||
} else {
|
|
||||||
PendingHooks.recordPendingMethod((Method) hookMethod, additionalInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback.new Unhook(hookMethod);
|
return callback.new Unhook(hookMethod);
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,7 @@ package org.lsposed.lspd.nativebridge;
|
||||||
|
|
||||||
import java.lang.reflect.Executable;
|
import java.lang.reflect.Executable;
|
||||||
|
|
||||||
import de.robv.android.xposed.PendingHooks;
|
|
||||||
|
|
||||||
public class ClassLinker {
|
public class ClassLinker {
|
||||||
|
|
||||||
public static native void setEntryPointsToInterpreter(Executable method);
|
public static native void setEntryPointsToInterpreter(Executable method);
|
||||||
|
|
||||||
public static void onPostFixupStaticTrampolines(Class<?> clazz) {
|
|
||||||
// native flags will be re-set in hooking logic
|
|
||||||
PendingHooks.hookPendingMethod(clazz);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of LSPosed.
|
|
||||||
*
|
|
||||||
* LSPosed is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* LSPosed is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.lsposed.lspd.nativebridge;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
public class PendingHooks {
|
|
||||||
public static native void recordPendingMethodNative(Method hookMethod, Class clazz);
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of LSPosed.
|
|
||||||
*
|
|
||||||
* LSPosed is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* LSPosed is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.lsposed.lspd.util;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import java.lang.reflect.Member;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
|
|
||||||
public class ClassUtils {
|
|
||||||
private static int getClassStatus(Class<?> clazz, boolean isUnsigned) {
|
|
||||||
if (clazz == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int status = XposedHelpers.getIntField(clazz, "status");
|
|
||||||
if (isUnsigned) {
|
|
||||||
status = (int) (Integer.toUnsignedLong(status) >> (32 - 4));
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 5.0-8.0: kInitialized = 10 int
|
|
||||||
* 8.1: kInitialized = 11 int
|
|
||||||
* 9.0+: kInitialized = 14 uint8_t
|
|
||||||
* 11.0+: kInitialized = 14 uint8_t
|
|
||||||
* kVisiblyInitialized = 15 uint8_t
|
|
||||||
*/
|
|
||||||
private static boolean isInitialized(Class<?> clazz) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
return getClassStatus(clazz, true) >= 14;
|
|
||||||
} else {
|
|
||||||
return getClassStatus(clazz, false) == 11;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean shouldDelayHook(Member hookMethod) {
|
|
||||||
if (!(hookMethod instanceof Method)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Class<?> declaringClass = hookMethod.getDeclaringClass();
|
|
||||||
return Modifier.isStatic(hookMethod.getModifiers())
|
|
||||||
&& !ClassUtils.isInitialized(declaringClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -23,7 +23,6 @@ import android.os.Build;
|
||||||
|
|
||||||
import org.lsposed.lspd.nativebridge.ClassLinker;
|
import org.lsposed.lspd.nativebridge.ClassLinker;
|
||||||
import org.lsposed.lspd.nativebridge.Yahfa;
|
import org.lsposed.lspd.nativebridge.Yahfa;
|
||||||
import org.lsposed.lspd.util.ClassUtils;
|
|
||||||
import org.lsposed.lspd.yahfa.dexmaker.DynamicBridge;
|
import org.lsposed.lspd.yahfa.dexmaker.DynamicBridge;
|
||||||
|
|
||||||
import java.lang.reflect.Executable;
|
import java.lang.reflect.Executable;
|
||||||
|
|
@ -43,10 +42,6 @@ public class YahfaHooker {
|
||||||
DynamicBridge.hookMethod(method, additionalInfo);
|
DynamicBridge.hookMethod(method, additionalInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean shouldDelayHook(Executable member) {
|
|
||||||
return ClassUtils.shouldDelayHook(member);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object invokeOriginalMethod(Executable method, Object thisObject, Object[] args) throws Throwable {
|
public static Object invokeOriginalMethod(Executable method, Object thisObject, Object[] args) throws Throwable {
|
||||||
return DynamicBridge.invokeOriginalMethod(method, thisObject, args);
|
return DynamicBridge.invokeOriginalMethod(method, thisObject, args);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue