Fixed the hook of 64 bit getOatHeader

This commit is contained in:
LoveSy 2020-12-20 14:47:03 +08:00 committed by Jim Wu
parent 5f3dc9220d
commit 76f438f959
10 changed files with 116 additions and 59 deletions

View File

@ -9,21 +9,35 @@
#include <HookMain.h> #include <HookMain.h>
namespace art { namespace art {
namespace art_method {
class ArtMethod : public edxp::HookedObject {
private:
inline static size_t oat_header_length; inline static size_t oat_header_length;
inline static int32_t oat_header_code_length_offset; 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) { 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. // This is a partial copy from AOSP. We only touch them if they are hooked.
if (LIKELY(edxp::isHooked(thiz))) { if (UNLIKELY(edxp::isHooked(thiz))) {
uintptr_t original_ep = reinterpret_cast<uintptr_t>(getOriginalEntryPointFromTargetMethod(thiz)); uintptr_t original_ep = reinterpret_cast<uintptr_t>(getOriginalEntryPointFromTargetMethod(
if(original_ep) { thiz));
char* code_length_loc = reinterpret_cast<char *>(original_ep) + oat_header_code_length_offset; if (original_ep) {
uint32_t code_length = *reinterpret_cast<uint32_t *>(code_length_loc) & ~0x80000000; char *code_length_loc =
LOGD("GetOatQuickMethodHeader: isHooked=true, original_ep=0x%x, code_length=0x%x, pc=0x%x", original_ep, code_length, pc); 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) if (original_ep <= pc && pc <= original_ep + code_length)
return reinterpret_cast<void *>(original_ep - oat_header_length); return reinterpret_cast<void *>(original_ep - oat_header_length);
// If PC is not in range, we mark it as not found. // If PC is not in range, we mark it as not found.
@ -36,28 +50,38 @@ namespace art {
return GetOatQuickMethodHeaderBackup(thiz, pc); return GetOatQuickMethodHeaderBackup(thiz, pc);
} }
public:
// @ApiSensitive(Level.MIDDLE)
static void Setup(void *handle, HookFunType hook_func) { static void Setup(void *handle, HookFunType hook_func) {
LOGD("Classlinker hook setup, handle=%p", handle); LOGD("Classlinker hook setup, handle=%p", handle);
int api_level = edxp::GetAndroidApiLevel(); int api_level = edxp::GetAndroidApiLevel();
switch (api_level) { switch (api_level) {
case __ANDROID_API_O__: case __ANDROID_API_O__:
[[fallthrough]];
case __ANDROID_API_O_MR1__: case __ANDROID_API_O_MR1__:
[[fallthrough]];
case __ANDROID_API_P__: case __ANDROID_API_P__:
oat_header_length = 24; oat_header_length = 24;
oat_header_code_length_offset = -4; oat_header_code_length_offset = -4;
break; break;
default: 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__: case __ANDROID_API_Q__:
[[fallthrough]];
case __ANDROID_API_R__: case __ANDROID_API_R__:
oat_header_length = 8; oat_header_length = 8;
oat_header_code_length_offset = -4; oat_header_code_length_offset = -4;
break; 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 #endif //EDXPOSED_ART_METHOD_H

View File

@ -35,13 +35,10 @@ namespace art {
} }
CREATE_HOOK_STUB_ENTRIES(void, FixupStaticTrampolines, void *thiz, void *clazz_ptr) { CREATE_HOOK_STUB_ENTRIES(void, FixupStaticTrampolines, void *thiz, void *clazz_ptr) {
art::mirror::Class clazz(clazz_ptr); bool should_intercept = edxp::IsClassPending(clazz_ptr);
std::string storage;
const char *desc = clazz.GetDescriptor(&storage);
bool should_intercept =
edxp::IsClassPending(desc) || std::string(desc).rfind("LEdHooker_") == 0;
FixupStaticTrampolinesBackup(thiz, clazz_ptr); FixupStaticTrampolinesBackup(thiz, clazz_ptr);
if (UNLIKELY(should_intercept)) { 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); edxp::Context::GetInstance()->CallOnPostFixupStaticTrampolines(clazz_ptr);
} }
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "base/object.h" #include "base/object.h"
#include "art/runtime/art_method.h"
namespace art { namespace art {
@ -9,7 +10,8 @@ namespace art {
CREATE_HOOK_STUB_ENTRIES(const void*, GetSavedEntryPointOfPreCompiledMethod, void *thiz, CREATE_HOOK_STUB_ENTRIES(const void*, GetSavedEntryPointOfPreCompiledMethod, void *thiz,
void *art_method) { void *art_method) {
if (UNLIKELY(edxp::isHooked(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 getEntryPoint(art_method);
} }
return GetSavedEntryPointOfPreCompiledMethodBackup(thiz, art_method); return GetSavedEntryPointOfPreCompiledMethodBackup(thiz, art_method);
@ -25,7 +27,7 @@ namespace art {
HOOK_FUNC(GetSavedEntryPointOfPreCompiledMethod, HOOK_FUNC(GetSavedEntryPointOfPreCompiledMethod,
"_ZN3art3jit12JitCodeCache37GetSavedEntryPointOfPreCompiledMethodEPNS_9ArtMethodE"); "_ZN3art3jit12JitCodeCache37GetSavedEntryPointOfPreCompiledMethodEPNS_9ArtMethodE");
} }
}; }
} }

View File

@ -64,7 +64,12 @@ namespace art {
const char *GetDescriptor(std::string *storage) { const char *GetDescriptor(std::string *storage) {
if (thiz_ && GetDescriptorSym) { if (thiz_ && GetDescriptorSym) {
return GetDescriptor(thiz_, storage); if (storage == nullptr) {
std::string str;
return GetDescriptor(thiz_, &str);
} else {
return GetDescriptor(thiz_, storage);
}
} }
return ""; return "";
} }

View File

@ -5,10 +5,29 @@
namespace art { 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: public:
Thread(void *thiz) : HookedObject(thiz) {} 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;
}
}; };
} }

View File

@ -1,40 +1,44 @@
#include <nativehelper/jni_macros.h> #include <nativehelper/jni_macros.h>
#include <set>
#include <string> #include <string>
#include <unordered_set>
#include "HookMain.h" #include "HookMain.h"
#include "jni.h" #include "jni.h"
#include "native_util.h" #include "native_util.h"
#include "edxp_pending_hooks.h" #include "edxp_pending_hooks.h"
#include "art/runtime/thread.h"
#include "art/runtime/mirror/class.h"
namespace edxp { 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) { bool IsClassPending(void *clazz) {
return class_descs_.find(class_desc) != class_descs_.end(); return pending_classes_.count(clazz);
} }
static void PendingHooks_recordPendingMethodNative(JNI_START, jstring class_desc) { static void PendingHooks_recordPendingMethodNative(JNI_START, jlong thread, jclass class_ref) {
const char *class_desc_chars = env->GetStringUTFChars(class_desc, JNI_FALSE); art::Thread current_thread(reinterpret_cast<void *>(thread));
class_descs_.insert(class_desc_chars); 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[] = { static JNINativeMethod gMethods[] = {
NATIVE_METHOD(PendingHooks, recordPendingMethodNative, "(Ljava/lang/String;)V"), NATIVE_METHOD(PendingHooks, recordPendingMethodNative, "(JLjava/lang/Class;)V"),
}; };
void RegisterPendingHooks(JNIEnv *env) { void RegisterPendingHooks(JNIEnv *env) {
REGISTER_EDXP_NATIVE_METHODS("de.robv.android.xposed.PendingHooks"); 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); return hooked_methods_.count(art_method);
} }
void recordHooked(void * art_method) { void recordHooked(void *art_method) {
hooked_methods_.insert(art_method); hooked_methods_.insert(art_method);
} }

View File

@ -5,7 +5,7 @@
namespace edxp { namespace edxp {
bool IsClassPending(const char *); bool IsClassPending(void *);
void RegisterPendingHooks(JNIEnv *); void RegisterPendingHooks(JNIEnv *);

View File

@ -19,6 +19,7 @@
#include "art/runtime/hidden_api.h" #include "art/runtime/hidden_api.h"
#include "art/runtime/oat_file_manager.h" #include "art/runtime/oat_file_manager.h"
#include "art/runtime/jit/jit_code_cache.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 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::hidden_api::DisableHiddenApi(art_handle, hook_func);
art::Runtime::Setup(art_handle, hook_func); art::Runtime::Setup(art_handle, hook_func);
art::gc::Heap::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::ClassLinker::Setup(art_handle, hook_func);
art::mirror::Class::Setup(art_handle, hook_func); art::mirror::Class::Setup(art_handle, hook_func);
art::JNIEnvExt::Setup(art_handle, hook_func); art::JNIEnvExt::Setup(art_handle, hook_func);

View File

@ -157,7 +157,7 @@ fi
[[ -f "${MODDIR}/sepolicy.rule" ]] || livePatch [[ -f "${MODDIR}/sepolicy.rule" ]] || livePatch
# start_verbose_log_catcher # 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_bridge_log_catcher
start_log_cather error "XSharedPreferences:V EdXposed-Bridge:V" true true start_log_cather error "XSharedPreferences:V EdXposed-Bridge:V" true true

View File

@ -1,48 +1,52 @@
package de.robv.android.xposed; package de.robv.android.xposed;
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal; import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
import com.jaredrummler.apkparser.utils.Utils;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; 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.hookMethodNative;
import static de.robv.android.xposed.XposedBridge.log;
public final class PendingHooks { public final class PendingHooks {
// GuardedBy("PendingHooks.class") // GuardedBy("PendingHooks.class")
private static final ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo> private static final ConcurrentHashMap<Class<?>, ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo>>
sPendingHookMethods = new ConcurrentHashMap<>(); sPendingHooks = new ConcurrentHashMap<>();
// GuardedBy("PendingHooks.class")
private static final HashSet<Member> sNonNativeMethods = new HashSet<>();
public synchronized static void hookPendingMethod(Class clazz) { public synchronized static void hookPendingMethod(Class<?> clazz) {
for (Member member : sPendingHookMethods.keySet()) { if (sPendingHooks.containsKey(clazz)) {
if (member.getDeclaringClass().equals(clazz)) { for (Map.Entry<Member, XposedBridge.AdditionalHookInfo> hook : sPendingHooks.get(clazz).entrySet()) {
hookMethodNative(member, clazz, 0, sPendingHookMethods.get(member)); hookMethodNative(hook.getKey(), clazz, 0, hook.getValue());
} }
} }
} }
public synchronized static void recordPendingMethod(Member hookMethod, public synchronized static void recordPendingMethod(Member hookMethod,
XposedBridge.AdditionalHookInfo additionalInfo) { XposedBridge.AdditionalHookInfo additionalInfo) {
if (!Modifier.isNative(hookMethod.getModifiers())) { ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo> pending =
// record non-native methods for later native flag temporary removing sPendingHooks.computeIfAbsent(hookMethod.getDeclaringClass(),
sNonNativeMethods.add(hookMethod); new Function<Class<?>, ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo>>() {
} @Override
sPendingHookMethods.put(hookMethod, additionalInfo); public ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo> apply(Class<?> aClass) {
recordPendingMethodNative("L" + return new ConcurrentHashMap<>();
hookMethod.getDeclaringClass().getName().replace(".", "/") + ";"); }
});
pending.put(hookMethod, additionalInfo);
Thread currentThread = Thread.currentThread();
long nativePeer = XposedHelpers.getLongField(currentThread, "nativePeer");
recordPendingMethodNative(nativePeer, hookMethod.getDeclaringClass());
} }
public synchronized void cleanUp() { public synchronized void cleanUp() {
sPendingHookMethods.clear(); sPendingHooks.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();
} }
private static native void recordPendingMethodNative(String classDesc); private static native void recordPendingMethodNative(long thread, Class clazz);
} }