Avoid entrypoint overwritten in Android R

This commit is contained in:
LoveSy 2020-12-10 17:44:59 +08:00 committed by 双草酸酯
parent 598d555e02
commit 2c3eee62c0
12 changed files with 112 additions and 10 deletions

View File

@ -19,4 +19,6 @@ public class Yahfa {
public static native void recordHooked(Member member);
public static native boolean isHooked(Member member);
public static native void makeInitializedClassesVisiblyInitialized(long thread, boolean wait);
}

View File

@ -5,6 +5,7 @@ import android.os.Build;
import com.elderdrivers.riru.edxp.art.ClassLinker;
import com.elderdrivers.riru.edxp.art.Heap;
import com.elderdrivers.riru.edxp.core.Yahfa;
import com.elderdrivers.riru.edxp.util.ClassUtils;
import com.elderdrivers.riru.edxp.util.Utils;
import java.lang.reflect.Constructor;
@ -122,12 +123,13 @@ public class HookMain {
}
// make sure GC completed before hook
Thread currentThread = Thread.currentThread();
int lastGcType = Heap.waitForGcToComplete(
XposedHelpers.getLongField(currentThread, "nativePeer"));
long nativePeer = XposedHelpers.getLongField(currentThread, "nativePeer");
int lastGcType = Heap.waitForGcToComplete(nativePeer);
if (lastGcType < 0) {
Utils.logW("waitForGcToComplete failed, using fallback");
Runtime.getRuntime().gc();
}
if (!Yahfa.backupAndHookNative(target, hook, backup)) {
throw new RuntimeException("Failed to hook " + target + " with " + hook);
} else {

View File

@ -29,6 +29,11 @@ void *getArtMethod(JNIEnv *env, jobject jmethod);
static void *getResolvedMethodsAddr(JNIEnv *, jobject);
// TODO: move to common utils instead of in YAHFA's code
void *getEntryPoint(void* method);
void setEntryPoint(void* method, void* entry);
#ifdef __cplusplus
}
#endif

View File

@ -136,6 +136,16 @@ bool setNativeFlag(void *method, bool isNative) {
return false;
}
void *getEntryPoint(void* method) {
return readAddr((char *) method + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod);
}
void setEntryPoint(void* method, void* entry) {
memcpy((char *) method + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
&entry,
pointer_size);
}
static int replaceMethod(void *fromMethod, void *toMethod, int isBackup) {
if (hookCount >= hookCap) {
LOGI("not enough capacity. Allocating...");

View File

@ -11,6 +11,7 @@
#include "edxp_context.h"
#include "jni/edxp_pending_hooks.h"
#include "utils.h"
#include "HookMain.h"
namespace art {
@ -37,7 +38,8 @@ namespace art {
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(desc) || std::string(desc).rfind("LEdHooker_") == 0;
if (UNLIKELY(should_intercept)) {
edxp::Context::GetInstance()->CallOnPreFixupStaticTrampolines(clazz_ptr);
}
@ -47,15 +49,30 @@ namespace art {
}
}
CREATE_FUNC_SYMBOL_ENTRY(void, MakeInitializedClassesVisiblyInitialized, void *thiz,
void *self, bool wait) {
if (LIKELY(MakeInitializedClassesVisiblyInitializedSym))
MakeInitializedClassesVisiblyInitializedSym(thiz, self, wait);
}
CREATE_HOOK_STUB_ENTRIES(bool, ShouldUseInterpreterEntrypoint, void *art_method,
const void *quick_code) {
if (UNLIKELY(edxp::isHooked(art_method) && quick_code != nullptr)) {
LOGD("Hooked method %p, no use interpreter", art_method);
if (UNLIKELY(quick_code != nullptr && edxp::isEntryHooked(quick_code))) {
return false;
}
return ShouldUseInterpreterEntrypointBackup(art_method, quick_code);
}
CREATE_HOOK_STUB_ENTRIES(bool, IsQuickToInterpreterBridge, void *thiz,
const void *quick_code) {
if (quick_code != nullptr && UNLIKELY(edxp::isEntryHooked(quick_code))) {
LOGD("Pretend to be quick to interpreter bridge %p", quick_code);
return true;
}
return IsQuickToInterpreterBridgeBackup(thiz, quick_code);
}
public:
ClassLinker(void *thiz) : HookedObject(thiz) {}
@ -127,6 +144,23 @@ namespace art {
HOOK_FUNC(ShouldUseInterpreterEntrypoint,
"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv");
}
// MakeInitializedClassesVisiblyInitialized will cause deadlock
// IsQuickToInterpreterBridge cannot be hooked by Dobby yet
// So we use GetSavedEntryPointOfPreCompiledMethod instead
// if (api_level >= __ANDROID_API_R__) {
// RETRIEVE_FUNC_SYMBOL(MakeInitializedClassesVisiblyInitialized,
// "_ZN3art11ClassLinker40MakeInitializedClassesVisiblyInitializedEPNS_6ThreadEb");
// HOOK_FUNC(IsQuickToInterpreterBridge,
// "_ZNK3art11ClassLinker26IsQuickToInterpreterBridgeEPKv");
// }
}
ALWAYS_INLINE void MakeInitializedClassesVisiblyInitialized(void *self, bool wait) const {
LOGD("MakeInitializedClassesVisiblyInitialized start, thiz=%p, self=%p", thiz_, self);
if (LIKELY(thiz_))
MakeInitializedClassesVisiblyInitialized(thiz_, self, wait);
}
ALWAYS_INLINE void SetEntryPointsToInterpreter(void *art_method) const {

View File

@ -0,0 +1,32 @@
#pragma once
#include "base/object.h"
namespace art {
namespace jit {
CREATE_HOOK_STUB_ENTRIES(const void*, GetSavedEntryPointOfPreCompiledMethod, void *thiz,
void *art_method) {
if (UNLIKELY(edxp::isHooked(art_method))) {
LOGD("Found hooked method, return entrypoint as jit entrypoint");
return getEntryPoint(art_method);
}
return GetSavedEntryPointOfPreCompiledMethodBackup(thiz, art_method);
}
static void HookJitCacheCode(void *handle, HookFunType hook_func) {
const int api_level = edxp::GetAndroidApiLevel();
// For android R, the invisibly initialization makes static methods initializes multiple
// times in non-x86 devices. So we need to hook this function to make sure
// our hooked entry point won't be overwritten.
// This is for SandHook and YAHFA
if (api_level >= __ANDROID_API_R__) {
HOOK_FUNC(GetSavedEntryPointOfPreCompiledMethod,
"_ZN3art3jit12JitCodeCache37GetSavedEntryPointOfPreCompiledMethodEPNS_9ArtMethodE");
}
};
}
}

View File

@ -2,6 +2,7 @@
#include <nativehelper/jni_macros.h>
#include <set>
#include <string>
#include "HookMain.h"
#include "jni.h"
#include "native_util.h"
#include "edxp_pending_hooks.h"
@ -10,7 +11,7 @@ namespace edxp {
static std::set<std::string> class_descs_;
static std::set<void*> hooked_methods_;
static std::set<const void*> hooked_methods_;
bool IsClassPending(const char *class_desc) {
return class_descs_.find(class_desc) != class_descs_.end();
@ -29,12 +30,16 @@ namespace edxp {
REGISTER_EDXP_NATIVE_METHODS("de.robv.android.xposed.PendingHooks");
}
bool isEntryHooked(const void* entry) {
return hooked_methods_.count(entry);
}
bool isHooked(void* art_method) {
return hooked_methods_.count(art_method);
return isEntryHooked(getEntryPoint(art_method));
}
void recordHooked(void * art_method) {
hooked_methods_.insert(art_method);
hooked_methods_.insert(getEntryPoint(art_method));
}
}

View File

@ -9,6 +9,8 @@ namespace edxp {
void RegisterPendingHooks(JNIEnv *);
bool isEntryHooked(const void* entry);
bool isHooked(void* art_method);
void recordHooked(void* art_method);

View File

@ -5,6 +5,7 @@
#include "native_util.h"
#include "edxp_yahfa.h"
#include "edxp_pending_hooks.h"
#include "art/runtime/class_linker.h"
namespace edxp {
@ -58,6 +59,12 @@ namespace edxp {
return edxp::isHooked(getArtMethod(env, member));
}
static void
Yahfa_makeInitializedClassesVisiblyInitialized(JNI_START, jlong thread, jboolean wait) {
art::ClassLinker::Current()->MakeInitializedClassesVisiblyInitialized(
reinterpret_cast<void *>(thread), wait);
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Yahfa, init, "(I)V"),
NATIVE_METHOD(Yahfa, findMethodNative,
@ -68,6 +75,7 @@ namespace edxp {
NATIVE_METHOD(Yahfa, setNativeFlag, "(Ljava/lang/reflect/Member;Z)Z"),
NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Member;)V"),
NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Member;)Z"),
NATIVE_METHOD(Yahfa, makeInitializedClassesVisiblyInitialized, "(JZ)V"),
};
void RegisterEdxpYahfa(JNIEnv *env) {

View File

@ -17,6 +17,7 @@
#include "art/runtime/gc/heap.h"
#include "art/runtime/hidden_api.h"
#include "art/runtime/oat_file_manager.h"
#include "art/runtime/jit/jit_code_cache.h"
std::vector<soinfo_t> linker_get_solist(); // Dobby but not in .h
@ -78,6 +79,7 @@ namespace edxp {
art::mirror::Class::Setup(art_handle, hook_func);
art::JNIEnvExt::Setup(art_handle, hook_func);
art::oat_file_manager::DisableOnlyUseSystemOatFiles(art_handle, hook_func);
art::jit::HookJitCacheCode(art_handle, hook_func);
art_hooks_installed = true;
LOGI("ART hooks installed");

View File

@ -1,7 +1,6 @@
package com.elderdrivers.riru.edxp.yahfa.core;
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
import com.elderdrivers.riru.edxp.core.Yahfa;
import com.elderdrivers.riru.edxp.proxy.BaseRouter;
import com.elderdrivers.riru.edxp.yahfa.config.YahfaEdxpConfig;
import com.elderdrivers.riru.edxp.yahfa.config.YahfaHookProvider;
@ -10,6 +9,7 @@ import com.elderdrivers.riru.edxp.yahfa.dexmaker.DynamicBridge;
public class YahfaRouter extends BaseRouter {
YahfaRouter() {
// TODO: disable for better performance
useXposedApi = true;
}

View File

@ -217,7 +217,7 @@ public class HookerDexMaker {
loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);
}
mHookClass = loader.loadClass(className);
mHookClass = Class.forName(className.replace("/", "."), true, loader);
// Execute our newly-generated code in-process.
mHookClass.getMethod(METHOD_NAME_SETUP, Member.class, XposedBridge.AdditionalHookInfo.class)
.invoke(null, mMember, mHookInfo);