Fixed the hook of 64 bit getOatHeader
This commit is contained in:
parent
5f3dc9220d
commit
76f438f959
|
|
@ -9,21 +9,35 @@
|
|||
#include <HookMain.h>
|
||||
|
||||
namespace art {
|
||||
namespace art_method {
|
||||
|
||||
class ArtMethod : public edxp::HookedObject {
|
||||
|
||||
private:
|
||||
inline static size_t oat_header_length;
|
||||
inline static int32_t oat_header_code_length_offset;
|
||||
|
||||
CREATE_FUNC_SYMBOL_ENTRY(std::string, PrettyMethod, void *thiz, bool with_signature) {
|
||||
if (UNLIKELY(thiz == nullptr))
|
||||
return "null";
|
||||
if (LIKELY(PrettyMethodSym))
|
||||
return PrettyMethodSym(thiz, with_signature);
|
||||
else return "null sym";
|
||||
}
|
||||
|
||||
inline static std::string PrettyMethod(void *thiz) {
|
||||
return PrettyMethod(thiz, true);
|
||||
}
|
||||
|
||||
CREATE_HOOK_STUB_ENTRIES(void *, GetOatQuickMethodHeader, void *thiz, uintptr_t pc) {
|
||||
// LOGD("GetOatQuickMethodHeader called, thiz=%p", thiz);
|
||||
// This is a partial copy from AOSP. We only touch them if they are hooked.
|
||||
if (LIKELY(edxp::isHooked(thiz))) {
|
||||
uintptr_t original_ep = reinterpret_cast<uintptr_t>(getOriginalEntryPointFromTargetMethod(thiz));
|
||||
if(original_ep) {
|
||||
char* code_length_loc = reinterpret_cast<char *>(original_ep) + oat_header_code_length_offset;
|
||||
uint32_t code_length = *reinterpret_cast<uint32_t *>(code_length_loc) & ~0x80000000;
|
||||
LOGD("GetOatQuickMethodHeader: isHooked=true, original_ep=0x%x, code_length=0x%x, pc=0x%x", original_ep, code_length, pc);
|
||||
if (UNLIKELY(edxp::isHooked(thiz))) {
|
||||
uintptr_t original_ep = reinterpret_cast<uintptr_t>(getOriginalEntryPointFromTargetMethod(
|
||||
thiz));
|
||||
if (original_ep) {
|
||||
char *code_length_loc =
|
||||
reinterpret_cast<char *>(original_ep) + oat_header_code_length_offset;
|
||||
uint32_t code_length =
|
||||
*reinterpret_cast<uint32_t *>(code_length_loc) & ~0x80000000;
|
||||
LOGD("GetOatQuickMethodHeader: ArtMethod=%p (%s), isHooked=true, original_ep=0x%x, code_length=0x%x, pc=0x%x",
|
||||
thiz, PrettyMethod(thiz).c_str(), original_ep, code_length, pc);
|
||||
if (original_ep <= pc && pc <= original_ep + code_length)
|
||||
return reinterpret_cast<void *>(original_ep - oat_header_length);
|
||||
// If PC is not in range, we mark it as not found.
|
||||
|
|
@ -36,28 +50,38 @@ namespace art {
|
|||
return GetOatQuickMethodHeaderBackup(thiz, pc);
|
||||
}
|
||||
|
||||
public:
|
||||
// @ApiSensitive(Level.MIDDLE)
|
||||
static void Setup(void *handle, HookFunType hook_func) {
|
||||
LOGD("Classlinker hook setup, handle=%p", handle);
|
||||
int api_level = edxp::GetAndroidApiLevel();
|
||||
switch (api_level) {
|
||||
case __ANDROID_API_O__:
|
||||
[[fallthrough]];
|
||||
case __ANDROID_API_O_MR1__:
|
||||
[[fallthrough]];
|
||||
case __ANDROID_API_P__:
|
||||
oat_header_length = 24;
|
||||
oat_header_code_length_offset = -4;
|
||||
break;
|
||||
default:
|
||||
LOGW("No valid offset in SDK %d for oatHeaderLen, using offset from Android R", api_level);
|
||||
LOGW("No valid offset in SDK %d for oatHeaderLen, using offset from Android R",
|
||||
api_level);
|
||||
[[fallthrough]];
|
||||
case __ANDROID_API_Q__:
|
||||
[[fallthrough]];
|
||||
case __ANDROID_API_R__:
|
||||
oat_header_length = 8;
|
||||
oat_header_code_length_offset = -4;
|
||||
break;
|
||||
}
|
||||
HOOK_FUNC(GetOatQuickMethodHeader, "_ZN3art9ArtMethod23GetOatQuickMethodHeaderEj");
|
||||
if constexpr (edxp::is64) {
|
||||
HOOK_FUNC(GetOatQuickMethodHeader, "_ZN3art9ArtMethod23GetOatQuickMethodHeaderEm");
|
||||
} else {
|
||||
HOOK_FUNC(GetOatQuickMethodHeader, "_ZN3art9ArtMethod23GetOatQuickMethodHeaderEj");
|
||||
}
|
||||
|
||||
RETRIEVE_FUNC_SYMBOL(PrettyMethod,
|
||||
"_ZN3art9ArtMethod12PrettyMethodEb");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif //EDXPOSED_ART_METHOD_H
|
||||
|
|
|
|||
|
|
@ -35,13 +35,10 @@ namespace art {
|
|||
}
|
||||
|
||||
CREATE_HOOK_STUB_ENTRIES(void, FixupStaticTrampolines, void *thiz, void *clazz_ptr) {
|
||||
art::mirror::Class clazz(clazz_ptr);
|
||||
std::string storage;
|
||||
const char *desc = clazz.GetDescriptor(&storage);
|
||||
bool should_intercept =
|
||||
edxp::IsClassPending(desc) || std::string(desc).rfind("LEdHooker_") == 0;
|
||||
bool should_intercept = edxp::IsClassPending(clazz_ptr);
|
||||
FixupStaticTrampolinesBackup(thiz, clazz_ptr);
|
||||
if (UNLIKELY(should_intercept)) {
|
||||
LOGD("Pending hook for %p (%s)", clazz_ptr, art::mirror::Class(clazz_ptr).GetDescriptor(nullptr));
|
||||
edxp::Context::GetInstance()->CallOnPostFixupStaticTrampolines(clazz_ptr);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/object.h"
|
||||
#include "art/runtime/art_method.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
|
|
@ -9,7 +10,8 @@ namespace art {
|
|||
CREATE_HOOK_STUB_ENTRIES(const void*, GetSavedEntryPointOfPreCompiledMethod, void *thiz,
|
||||
void *art_method) {
|
||||
if (UNLIKELY(edxp::isHooked(art_method))) {
|
||||
LOGD("Found hooked method %p, return entrypoint as jit entrypoint", art_method);
|
||||
LOGD("Found hooked method %p (%s), return entrypoint as jit entrypoint", art_method,
|
||||
art::art_method::PrettyMethod(art_method).c_str());
|
||||
return getEntryPoint(art_method);
|
||||
}
|
||||
return GetSavedEntryPointOfPreCompiledMethodBackup(thiz, art_method);
|
||||
|
|
@ -25,7 +27,7 @@ namespace art {
|
|||
HOOK_FUNC(GetSavedEntryPointOfPreCompiledMethod,
|
||||
"_ZN3art3jit12JitCodeCache37GetSavedEntryPointOfPreCompiledMethodEPNS_9ArtMethodE");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,12 @@ namespace art {
|
|||
|
||||
const char *GetDescriptor(std::string *storage) {
|
||||
if (thiz_ && GetDescriptorSym) {
|
||||
return GetDescriptor(thiz_, storage);
|
||||
if (storage == nullptr) {
|
||||
std::string str;
|
||||
return GetDescriptor(thiz_, &str);
|
||||
} else {
|
||||
return GetDescriptor(thiz_, storage);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,29 @@
|
|||
|
||||
namespace art {
|
||||
|
||||
class Thread : public edxp::HookedObject {
|
||||
class Thread : public edxp::HookedObject {
|
||||
|
||||
CREATE_FUNC_SYMBOL_ENTRY(void *, DecodeJObject, void *thiz,
|
||||
jobject obj) {
|
||||
if (DecodeJObjectSym)
|
||||
return DecodeJObjectSym(thiz, obj);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
Thread(void *thiz) : HookedObject(thiz) {}
|
||||
|
||||
static void Setup(void *handle, HookFunType hook_func) {
|
||||
RETRIEVE_FUNC_SYMBOL(DecodeJObject,
|
||||
"_ZNK3art6Thread13DecodeJObjectEP8_jobject");
|
||||
}
|
||||
|
||||
void *DecodeJObject(jobject obj) {
|
||||
if (thiz_ && DecodeJObjectSym) {
|
||||
return DecodeJObject(thiz_, obj);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,40 +1,44 @@
|
|||
|
||||
#include <nativehelper/jni_macros.h>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include "HookMain.h"
|
||||
#include "jni.h"
|
||||
#include "native_util.h"
|
||||
#include "edxp_pending_hooks.h"
|
||||
#include "art/runtime/thread.h"
|
||||
#include "art/runtime/mirror/class.h"
|
||||
|
||||
namespace edxp {
|
||||
|
||||
static std::set<std::string> class_descs_;
|
||||
static std::unordered_set<const void *> pending_classes_;
|
||||
|
||||
static std::set<const void*> hooked_methods_;
|
||||
static std::unordered_set<const void *> hooked_methods_;
|
||||
|
||||
bool IsClassPending(const char *class_desc) {
|
||||
return class_descs_.find(class_desc) != class_descs_.end();
|
||||
bool IsClassPending(void *clazz) {
|
||||
return pending_classes_.count(clazz);
|
||||
}
|
||||
|
||||
static void PendingHooks_recordPendingMethodNative(JNI_START, jstring class_desc) {
|
||||
const char *class_desc_chars = env->GetStringUTFChars(class_desc, JNI_FALSE);
|
||||
class_descs_.insert(class_desc_chars);
|
||||
static void PendingHooks_recordPendingMethodNative(JNI_START, jlong thread, jclass class_ref) {
|
||||
art::Thread current_thread(reinterpret_cast<void *>(thread));
|
||||
auto *class_ptr = current_thread.DecodeJObject(class_ref);
|
||||
LOGD("record pending: %p (%s)", class_ptr, art::mirror::Class(class_ptr).GetDescriptor(nullptr));
|
||||
pending_classes_.insert(class_ptr);
|
||||
}
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
NATIVE_METHOD(PendingHooks, recordPendingMethodNative, "(Ljava/lang/String;)V"),
|
||||
NATIVE_METHOD(PendingHooks, recordPendingMethodNative, "(JLjava/lang/Class;)V"),
|
||||
};
|
||||
|
||||
void RegisterPendingHooks(JNIEnv *env) {
|
||||
REGISTER_EDXP_NATIVE_METHODS("de.robv.android.xposed.PendingHooks");
|
||||
}
|
||||
|
||||
bool isHooked(void* art_method) {
|
||||
bool isHooked(void *art_method) {
|
||||
return hooked_methods_.count(art_method);
|
||||
}
|
||||
|
||||
void recordHooked(void * art_method) {
|
||||
void recordHooked(void *art_method) {
|
||||
hooked_methods_.insert(art_method);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
namespace edxp {
|
||||
|
||||
bool IsClassPending(const char *);
|
||||
bool IsClassPending(void *);
|
||||
|
||||
void RegisterPendingHooks(JNIEnv *);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "art/runtime/hidden_api.h"
|
||||
#include "art/runtime/oat_file_manager.h"
|
||||
#include "art/runtime/jit/jit_code_cache.h"
|
||||
#include "art/runtime/art_method.h"
|
||||
|
||||
std::vector<soinfo_t> linker_get_solist(); // Dobby but not in .h
|
||||
|
||||
|
|
@ -76,7 +77,8 @@ namespace edxp {
|
|||
art::hidden_api::DisableHiddenApi(art_handle, hook_func);
|
||||
art::Runtime::Setup(art_handle, hook_func);
|
||||
art::gc::Heap::Setup(art_handle, hook_func);
|
||||
art::ArtMethod::Setup(art_handle, hook_func);
|
||||
art::art_method::Setup(art_handle, hook_func);
|
||||
art::Thread::Setup(art_handle, hook_func);
|
||||
art::ClassLinker::Setup(art_handle, hook_func);
|
||||
art::mirror::Class::Setup(art_handle, hook_func);
|
||||
art::JNIEnvExt::Setup(art_handle, hook_func);
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ fi
|
|||
[[ -f "${MODDIR}/sepolicy.rule" ]] || livePatch
|
||||
|
||||
# start_verbose_log_catcher
|
||||
start_log_cather all "EdXposed:V XSharedPreferences:V EdXposed-Bridge:V EdXposedManager:V XposedInstaller:V" true ${LOG_VERBOSE}
|
||||
start_log_cather all "EdXposed:V XSharedPreferences:V EdXposed-Bridge:V EdXposedManager:V XposedInstaller:V *:F" true ${LOG_VERBOSE}
|
||||
|
||||
# start_bridge_log_catcher
|
||||
start_log_cather error "XSharedPreferences:V EdXposed-Bridge:V" true true
|
||||
|
|
|
|||
|
|
@ -1,48 +1,52 @@
|
|||
package de.robv.android.xposed;
|
||||
|
||||
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
||||
import com.jaredrummler.apkparser.utils.Utils;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static de.robv.android.xposed.XposedBridge.hookMethodNative;
|
||||
import static de.robv.android.xposed.XposedBridge.log;
|
||||
|
||||
public final class PendingHooks {
|
||||
|
||||
// GuardedBy("PendingHooks.class")
|
||||
private static final ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo>
|
||||
sPendingHookMethods = new ConcurrentHashMap<>();
|
||||
// GuardedBy("PendingHooks.class")
|
||||
private static final HashSet<Member> sNonNativeMethods = new HashSet<>();
|
||||
private static final ConcurrentHashMap<Class<?>, ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo>>
|
||||
sPendingHooks = new ConcurrentHashMap<>();
|
||||
|
||||
public synchronized static void hookPendingMethod(Class clazz) {
|
||||
for (Member member : sPendingHookMethods.keySet()) {
|
||||
if (member.getDeclaringClass().equals(clazz)) {
|
||||
hookMethodNative(member, clazz, 0, sPendingHookMethods.get(member));
|
||||
public synchronized static void hookPendingMethod(Class<?> clazz) {
|
||||
if (sPendingHooks.containsKey(clazz)) {
|
||||
for (Map.Entry<Member, XposedBridge.AdditionalHookInfo> hook : sPendingHooks.get(clazz).entrySet()) {
|
||||
hookMethodNative(hook.getKey(), clazz, 0, hook.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static void recordPendingMethod(Member hookMethod,
|
||||
XposedBridge.AdditionalHookInfo additionalInfo) {
|
||||
if (!Modifier.isNative(hookMethod.getModifiers())) {
|
||||
// record non-native methods for later native flag temporary removing
|
||||
sNonNativeMethods.add(hookMethod);
|
||||
}
|
||||
sPendingHookMethods.put(hookMethod, additionalInfo);
|
||||
recordPendingMethodNative("L" +
|
||||
hookMethod.getDeclaringClass().getName().replace(".", "/") + ";");
|
||||
ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo> pending =
|
||||
sPendingHooks.computeIfAbsent(hookMethod.getDeclaringClass(),
|
||||
new Function<Class<?>, ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo>>() {
|
||||
@Override
|
||||
public ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo> apply(Class<?> aClass) {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
});
|
||||
|
||||
pending.put(hookMethod, additionalInfo);
|
||||
Thread currentThread = Thread.currentThread();
|
||||
long nativePeer = XposedHelpers.getLongField(currentThread, "nativePeer");
|
||||
recordPendingMethodNative(nativePeer, hookMethod.getDeclaringClass());
|
||||
}
|
||||
|
||||
public synchronized void cleanUp() {
|
||||
sPendingHookMethods.clear();
|
||||
// sNonNativeMethods should be cleared very carefully because their
|
||||
// pre-set native flag have to be removed if its hooking is cancelled
|
||||
// before its class is initialized
|
||||
// sNonNativeMethods.clear();
|
||||
sPendingHooks.clear();
|
||||
}
|
||||
|
||||
private static native void recordPendingMethodNative(String classDesc);
|
||||
private static native void recordPendingMethodNative(long thread, Class clazz);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue