Fix native crashes in R

This commit is contained in:
LoveSy 2020-12-10 20:07:26 +08:00 committed by 双草酸酯
parent 1cc8751c8c
commit b577d4c2b3
10 changed files with 68 additions and 37 deletions

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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<Object> 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");
}

View File

@ -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);
}

View File

@ -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");

View File

@ -10,6 +10,8 @@ namespace edxp {
static std::set<std::string> class_descs_;
static std::set<void*> 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);
}
}

View File

@ -9,4 +9,8 @@ namespace edxp {
void RegisterPendingHooks(JNIEnv *);
bool isHooked(void* art_method);
void recordHooked(void* art_method);
} // namespace edxp

View File

@ -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) {

View File

@ -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);