From b577d4c2b3b9243cb7cdba8f1c25dbd22a983ac9 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Thu, 10 Dec 2020 20:07:26 +0800 Subject: [PATCH] Fix native crashes in R --- .../_hooker/yahfa/HandleBindAppHooker.java | 2 ++ .../riru/edxp/config/BaseHookProvider.java | 3 +- .../elderdrivers/riru/edxp/core/Yahfa.java | 6 +++- .../riru/edxp/core/yahfa/HookMain.java | 20 ++++++------- .../main/cpp/external/yahfa/src/HookMain.c | 29 +++++++++---------- .../main/include/art/runtime/class_linker.h | 11 ++++--- .../cpp/main/src/jni/edxp_pending_hooks.cpp | 10 +++++++ .../cpp/main/src/jni/edxp_pending_hooks.h | 4 +++ .../src/main/cpp/main/src/jni/edxp_yahfa.cpp | 13 ++++++++- .../edxp/yahfa/dexmaker/HookerDexMaker.java | 7 +++-- 10 files changed, 68 insertions(+), 37 deletions(-) diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/_hooker/yahfa/HandleBindAppHooker.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/_hooker/yahfa/HandleBindAppHooker.java index 2174fff8..1105fe72 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/_hooker/yahfa/HandleBindAppHooker.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/_hooker/yahfa/HandleBindAppHooker.java @@ -2,6 +2,8 @@ package com.elderdrivers.riru.edxp._hooker.yahfa; import com.elderdrivers.riru.common.KeepMembers; import com.elderdrivers.riru.edxp._hooker.impl.HandleBindApp; +import com.elderdrivers.riru.edxp.core.Yahfa; +import com.elderdrivers.riru.edxp.util.Hookers; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.annotation.ApiSensitive; diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java index 543656c7..382fd030 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java @@ -6,6 +6,7 @@ import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter; import com.elderdrivers.riru.edxp.hook.HookProvider; import java.lang.reflect.Member; +import java.lang.reflect.Method; public abstract class BaseHookProvider implements HookProvider { @@ -47,7 +48,7 @@ public abstract class BaseHookProvider implements HookProvider { @Override public boolean methodHooked(Member target) { - return HookMain.hooked(target); + return Yahfa.isHooked((Method)target); } @Override diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java index 8405ada1..2fa3c8c1 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java @@ -8,11 +8,15 @@ public class Yahfa { public static native boolean backupAndHookNative(Object target, Method hook, Method backup); // JNI.ToReflectedMethod() could return either Method or Constructor - public static native Object findMethodNative(Class targetClass, String methodName, String methodSig); + public static native Member findMethodNative(Class targetClass, String methodName, String methodSig); public static native void init(int sdkVersion); public static native void setMethodNonCompilable(Member member); public static native boolean setNativeFlag(Member member, boolean isNative); + + public static native void recordHooked(Member member); + + public static native boolean isHooked(Member member); } diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/yahfa/HookMain.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/yahfa/HookMain.java index 5fa6972e..bebb7d18 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/yahfa/HookMain.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/yahfa/HookMain.java @@ -1,5 +1,8 @@ package com.elderdrivers.riru.edxp.core.yahfa; +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.Utils; @@ -15,6 +18,8 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import de.robv.android.xposed.PendingHooks; +import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; public class HookMain { @@ -25,12 +30,6 @@ public class HookMain { hookItemWhiteList.add(className); } - private static List hookedList = new CopyOnWriteArrayList(); - - public static boolean hooked(Member target) { - return hookedList.contains(target); - } - public static void doHookDefault(ClassLoader patchClassLoader, ClassLoader originClassLoader, String hookInfoClassName) { try { Class hookInfoClass = Class.forName(hookInfoClassName, true, patchClassLoader); @@ -97,11 +96,11 @@ public class HookMain { backupAndHook(findMethod(targetClass, methodName, methodSig), hook, backup); } - public static void hook(Object target, Method hook) { + public static void hook(Member target, Method hook) { backupAndHook(target, hook, null); } - public static void backupAndHook(Object target, Method hook, Method backup) { + public static void backupAndHook(Member target, Method hook, Method backup) { Utils.logD(String.format("target=%s, hook=%s, backup=%s", target, hook, backup)); if (target == null) { throw new IllegalArgumentException("null target method"); @@ -132,11 +131,12 @@ public class HookMain { if (!Yahfa.backupAndHookNative(target, hook, backup)) { throw new RuntimeException("Failed to hook " + target + " with " + hook); } else { - hookedList.add(target); + Yahfa.recordHooked(target); + Yahfa.recordHooked(backup); } } - public static Object findMethod(Class cls, String methodName, String methodSig) { + public static Member findMethod(Class cls, String methodName, String methodSig) { if (cls == null) { throw new IllegalArgumentException("null class"); } diff --git a/edxp-core/src/main/cpp/external/yahfa/src/HookMain.c b/edxp-core/src/main/cpp/external/yahfa/src/HookMain.c index 640f0e59..eb7c05a0 100644 --- a/edxp-core/src/main/cpp/external/yahfa/src/HookMain.c +++ b/edxp-core/src/main/cpp/external/yahfa/src/HookMain.c @@ -13,10 +13,10 @@ static int OFFSET_entry_point_from_interpreter_in_ArtMethod; static int OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod; static int OFFSET_ArtMehod_in_Object; static int OFFSET_access_flags_in_ArtMethod; -static int kAccNative = 0x0100; -static int kAccCompileDontBother = 0x01000000; -static int kAccFastInterpreterToInterpreterInvoke = 0x40000000; -static int kAccPreCompiled = 0x00200000; +static uint32_t kAccNative = 0x0100; +static uint32_t kAccCompileDontBother = 0x01000000; +static uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000; +static uint32_t kAccPreCompiled = 0x00200000; static jfieldID fieldArtMethod = NULL; @@ -107,30 +107,26 @@ void setNonCompilable(void *method) { if (SDKVersion < __ANDROID_API_N__) { return; } - int access_flags = getFlags(method); - int old_flags = access_flags; + uint32_t access_flags = getFlags(method); + uint32_t old_flags = access_flags; access_flags |= kAccCompileDontBother; setFlags(method, access_flags); LOGI("setNonCompilable: change access flags from 0x%x to 0x%x", old_flags, access_flags); } bool setNativeFlag(void *method, bool isNative) { - int access_flags = getFlags(method); - int old_flags = access_flags; + uint32_t access_flags = getFlags(method); + uint32_t old_flags = access_flags; LOGI("setNativeFlag: access flags is 0x%x", access_flags); - int old_access_flags = access_flags; + uint32_t old_access_flags = access_flags; if (isNative) { - // TODO: Temporally disable native for compatible with Android R, - // but we should keep this and MarkInitializedClassVisiblyInitialized - if (SDKVersion < __ANDROID_API_R__) - access_flags |= kAccNative; + access_flags |= kAccNative; if (SDKVersion >= __ANDROID_API_Q__) { // On API 29 whether to use the fast path or not is cached in the ART method structure access_flags &= ~kAccFastInterpreterToInterpreterInvoke; } } else { - if (SDKVersion < __ANDROID_API_R__) - access_flags &= ~kAccNative; + access_flags &= ~kAccNative; } if (access_flags != old_access_flags) { setFlags(method, access_flags); @@ -184,7 +180,8 @@ static int replaceMethod(void *fromMethod, void *toMethod, int isBackup) { } // set the target method to native so that Android O wouldn't invoke it with interpreter - if (SDKVersion >= __ANDROID_API_O__) { + // for Q or above, we use ShouldUseInterpreterEntrypoint + if (SDKVersion >= __ANDROID_API_O__ && SDKVersion < __ANDROID_API_Q__) { setNativeFlag(fromMethod, true); } diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h b/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h index 2a2dbf2d..c364a1cc 100644 --- a/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h +++ b/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h @@ -37,7 +37,7 @@ namespace art { art::mirror::Class clazz(clazz_ptr); std::string storage; const char *desc = clazz.GetDescriptor(&storage); - bool should_intercept = edxp::IsClassPending(desc); + bool should_intercept = edxp::IsClassPending(desc) || std::string(desc).rfind("LEdHooker_") == 0; if (UNLIKELY(should_intercept)) { edxp::Context::GetInstance()->CallOnPreFixupStaticTrampolines(clazz_ptr); } @@ -49,9 +49,8 @@ namespace art { CREATE_HOOK_STUB_ENTRIES(bool, ShouldUseInterpreterEntrypoint, void *art_method, const void *quick_code) { - // TODO check hooked - bool hooked = false; - if (hooked && quick_code != nullptr) { + if (UNLIKELY(edxp::isHooked(art_method) && quick_code != nullptr)) { + LOGD("Hooked method %p, no use interpreter", art_method); return false; } return ShouldUseInterpreterEntrypointBackup(art_method, quick_code); @@ -122,8 +121,8 @@ namespace art { // Sandhook will hook ShouldUseInterpreterEntrypoint, so we just skip // edxp::Context::GetInstance()->GetVariant() will not work here, so we use smh dirty hack - if (api_level >= __ANDROID_API_R__ && - access(edxp::kLibSandHookNativePath.c_str(), F_OK) == -1) { + if (api_level >= __ANDROID_API_Q__ && + edxp::path_exists(edxp::kLibSandHookNativePath)) { LOGD("Not sandhook, installing _ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv"); HOOK_FUNC(ShouldUseInterpreterEntrypoint, "_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv"); diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.cpp b/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.cpp index 398a5d95..ea82e1b9 100644 --- a/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.cpp +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.cpp @@ -10,6 +10,8 @@ namespace edxp { static std::set class_descs_; + static std::set hooked_methods_; + bool IsClassPending(const char *class_desc) { return class_descs_.find(class_desc) != class_descs_.end(); } @@ -27,4 +29,12 @@ namespace edxp { REGISTER_EDXP_NATIVE_METHODS("de.robv.android.xposed.PendingHooks"); } + bool isHooked(void* art_method) { + return hooked_methods_.count(art_method); + } + + void recordHooked(void * art_method) { + hooked_methods_.insert(art_method); + } + } \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.h b/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.h index 51c40d45..b952a2c5 100644 --- a/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.h +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.h @@ -9,4 +9,8 @@ namespace edxp { void RegisterPendingHooks(JNIEnv *); + bool isHooked(void* art_method); + + void recordHooked(void* art_method); + } // namespace edxp diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp b/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp index 4d705b22..0dda4147 100644 --- a/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp @@ -4,6 +4,7 @@ #include "jni.h" #include "native_util.h" #include "edxp_yahfa.h" +#include "edxp_pending_hooks.h" namespace edxp { @@ -49,14 +50,24 @@ namespace edxp { return (jboolean) setNativeFlag(art_method, is_native); } + static void Yahfa_recordHooked(JNI_START, jobject member) { + edxp::recordHooked(getArtMethod(env, member)); + } + + static jboolean Yahfa_isHooked(JNI_START, jobject member) { + return edxp::isHooked(getArtMethod(env, member)); + } + static JNINativeMethod gMethods[] = { NATIVE_METHOD(Yahfa, init, "(I)V"), NATIVE_METHOD(Yahfa, findMethodNative, - "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;"), + "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Member;"), NATIVE_METHOD(Yahfa, backupAndHookNative, "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"), NATIVE_METHOD(Yahfa, setMethodNonCompilable, "(Ljava/lang/reflect/Member;)V"), 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"), }; void RegisterEdxpYahfa(JNIEnv *env) { diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java index c184bf4b..43017818 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java @@ -7,6 +7,7 @@ import android.text.TextUtils; import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.core.yahfa.HookMain; import com.elderdrivers.riru.edxp.util.ProxyClassLoader; +import com.elderdrivers.riru.edxp.yahfa.BuildConfig; import java.io.File; import java.lang.reflect.Constructor; @@ -183,11 +184,11 @@ public class HookerDexMaker { mAppClassLoader = appClassLoader; mAppClassLoader = new ProxyClassLoader(mAppClassLoader, getClass().getClassLoader()); } - doMake(); + doMake(member.getDeclaringClass().getName()); } @TargetApi(Build.VERSION_CODES.O) - private void doMake() throws Exception { + private void doMake(String hookedClassName) throws Exception { final boolean useInMemoryCl = TextUtils.isEmpty(mDexDirPath); mDexMaker = new DexMaker(); ClassLoader loader; @@ -209,6 +210,8 @@ public class HookerDexMaker { loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), className); } else { // do everything in memory + if(BuildConfig.DEBUG) + className = className + hookedClassName.replace(".", "/"); doGenerate(className); byte[] dexBytes = mDexMaker.generate(); loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);