Merge with latest Yahfa
This commit is contained in:
parent
d49757b5ad
commit
e36c3ed314
|
|
@ -7,8 +7,6 @@ public class Yahfa {
|
|||
|
||||
public static native boolean backupAndHookNative(Object target, Method hook, Method backup);
|
||||
|
||||
public static native void ensureMethodCached(Method hook, Method backup);
|
||||
|
||||
// JNI.ToReflectedMethod() could return either Method or Constructor
|
||||
public static native Object findMethodNative(Class targetClass, String methodName, String methodSig);
|
||||
|
||||
|
|
|
|||
|
|
@ -121,11 +121,6 @@ public class HookMain {
|
|||
// backup is just a placeholder and the constraint could be less strict
|
||||
checkCompatibleMethods(target, backup, "Original", "Backup");
|
||||
}
|
||||
if (backup != null) {
|
||||
HookMethodResolver.resolveMethod(hook, backup, target);
|
||||
} else {
|
||||
Utils.logD("wanna resolve backup method, but it's null, target: " + target);
|
||||
}
|
||||
// make sure GC completed before hook
|
||||
Thread currentThread = Thread.currentThread();
|
||||
int lastGcType = Heap.waitForGcToComplete(
|
||||
|
|
|
|||
|
|
@ -1,157 +0,0 @@
|
|||
package com.elderdrivers.riru.edxp.core.yahfa;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||
import com.elderdrivers.riru.edxp.util.Utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* create by Swift Gan on 14/01/2019
|
||||
* To ensure method in resolved cache
|
||||
*/
|
||||
|
||||
public class HookMethodResolver {
|
||||
|
||||
public static Class artMethodClass;
|
||||
|
||||
public static Field resolvedMethodsField;
|
||||
public static Field dexCacheField;
|
||||
public static Field dexMethodIndexField;
|
||||
public static Field artMethodField;
|
||||
|
||||
public static boolean canResolvedInJava = false;
|
||||
public static boolean isArtMethod = false;
|
||||
|
||||
public static long resolvedMethodsAddress = 0;
|
||||
public static int dexMethodIndex = 0;
|
||||
|
||||
public static Method testMethod;
|
||||
public static Object testArtMethod;
|
||||
|
||||
public static void init() {
|
||||
checkSupport();
|
||||
}
|
||||
|
||||
private static void checkSupport() {
|
||||
try {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
isArtMethod = false;
|
||||
canResolvedInJava = false;
|
||||
return;
|
||||
}
|
||||
|
||||
testMethod = HookMethodResolver.class.getDeclaredMethod("init");
|
||||
artMethodField = getField(Method.class, "artMethod");
|
||||
|
||||
testArtMethod = artMethodField.get(testMethod);
|
||||
|
||||
if (hasJavaArtMethod() && testArtMethod.getClass() == artMethodClass) {
|
||||
checkSupportForArtMethod();
|
||||
isArtMethod = true;
|
||||
} else if (testArtMethod instanceof Long) {
|
||||
checkSupportForArtMethodId();
|
||||
isArtMethod = false;
|
||||
} else {
|
||||
canResolvedInJava = false;
|
||||
}
|
||||
|
||||
} catch (Throwable throwable) {
|
||||
Utils.logE("error when checkSupport", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
// may 5.0
|
||||
private static void checkSupportForArtMethod() throws Exception {
|
||||
dexMethodIndexField = getField(artMethodClass, "dexMethodIndex");
|
||||
dexCacheField = getField(Class.class, "dexCache");
|
||||
Object dexCache = dexCacheField.get(testMethod.getDeclaringClass());
|
||||
resolvedMethodsField = getField(dexCache.getClass(), "resolvedMethods");
|
||||
if (resolvedMethodsField.get(dexCache) instanceof Object[]) {
|
||||
canResolvedInJava = true;
|
||||
}
|
||||
}
|
||||
|
||||
// may 6.0
|
||||
private static void checkSupportForArtMethodId() throws Exception {
|
||||
dexMethodIndexField = getField(Method.class, "dexMethodIndex");
|
||||
dexMethodIndex = (int) dexMethodIndexField.get(testMethod);
|
||||
dexCacheField = getField(Class.class, "dexCache");
|
||||
Object dexCache = dexCacheField.get(testMethod.getDeclaringClass());
|
||||
resolvedMethodsField = getField(dexCache.getClass(), "resolvedMethods");
|
||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
||||
if (resolvedMethods instanceof Long) {
|
||||
canResolvedInJava = false;
|
||||
resolvedMethodsAddress = (long) resolvedMethods;
|
||||
} else if (resolvedMethods instanceof long[]) {
|
||||
canResolvedInJava = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void resolveMethod(Method hook, Method backup, Object target) {
|
||||
if (canResolvedInJava && artMethodField != null) {
|
||||
// in java
|
||||
try {
|
||||
resolveInJava(hook, backup, target);
|
||||
} catch (Exception e) {
|
||||
// in native
|
||||
resolveInNative(hook, backup, target);
|
||||
}
|
||||
} else {
|
||||
// in native
|
||||
resolveInNative(hook, backup, target);
|
||||
}
|
||||
}
|
||||
|
||||
private static void resolveInJava(Method hook, Method backup, Object target) throws Exception {
|
||||
Utils.logD("start to resolve in java. target: " + target);
|
||||
Object dexCache = dexCacheField.get(hook.getDeclaringClass());
|
||||
if (isArtMethod) {
|
||||
Object artMethod = artMethodField.get(backup);
|
||||
int dexMethodIndex = (int) dexMethodIndexField.get(artMethod);
|
||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
||||
((Object[]) resolvedMethods)[dexMethodIndex] = artMethod;
|
||||
} else {
|
||||
int dexMethodIndex = (int) dexMethodIndexField.get(backup);
|
||||
Object resolvedMethods = resolvedMethodsField.get(dexCache);
|
||||
long artMethod = (long) artMethodField.get(backup);
|
||||
((long[]) resolvedMethods)[dexMethodIndex] = artMethod;
|
||||
}
|
||||
}
|
||||
|
||||
private static void resolveInNative(Method hook, Method backup, Object target) {
|
||||
Utils.logD("start to resolve in native. target: " + target);
|
||||
Yahfa.ensureMethodCached(hook, backup);
|
||||
}
|
||||
|
||||
public static Field getField(Class topClass, String fieldName) throws NoSuchFieldException {
|
||||
while (topClass != null && topClass != Object.class) {
|
||||
try {
|
||||
Field field = topClass.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
topClass = topClass.getSuperclass();
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
public static boolean hasJavaArtMethod() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return false;
|
||||
}
|
||||
if (artMethodClass != null)
|
||||
return true;
|
||||
try {
|
||||
artMethodClass = Class.forName("java.lang.reflect.ArtMethod");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -10,16 +10,13 @@
|
|||
|
||||
int SDKVersion;
|
||||
static int OFFSET_entry_point_from_interpreter_in_ArtMethod;
|
||||
int OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||
static int OFFSET_dex_method_index_in_ArtMethod;
|
||||
static int OFFSET_dex_cache_resolved_methods_in_ArtMethod;
|
||||
static int OFFSET_array_in_PointerArray;
|
||||
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 size_t ArtMethodSize;
|
||||
static int kAccNative = 0x0100;
|
||||
static int kAccCompileDontBother = 0x01000000;
|
||||
static int kAccFastInterpreterToInterpreterInvoke = 0x40000000;
|
||||
static int kAccPreCompiled = 0x00200000;
|
||||
|
||||
static jfieldID fieldArtMethod = NULL;
|
||||
|
||||
|
|
@ -49,47 +46,32 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
|
|||
kAccCompileDontBother = 0x02000000;
|
||||
OFFSET_ArtMehod_in_Object = 0;
|
||||
OFFSET_access_flags_in_ArtMethod = 4;
|
||||
OFFSET_dex_method_index_in_ArtMethod = 4 * 3;
|
||||
OFFSET_array_in_PointerArray = 0;
|
||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
|
||||
roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size;
|
||||
ArtMethodSize = roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 2;
|
||||
break;
|
||||
case __ANDROID_API_O_MR1__:
|
||||
kAccCompileDontBother = 0x02000000;
|
||||
case __ANDROID_API_O__:
|
||||
OFFSET_ArtMehod_in_Object = 0;
|
||||
OFFSET_access_flags_in_ArtMethod = 4;
|
||||
OFFSET_dex_method_index_in_ArtMethod = 4 * 3;
|
||||
OFFSET_dex_cache_resolved_methods_in_ArtMethod = roundUpToPtrSize(4 * 4 + 2 * 2);
|
||||
OFFSET_array_in_PointerArray = 0;
|
||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
|
||||
roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 2;
|
||||
ArtMethodSize = roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 3;
|
||||
break;
|
||||
case __ANDROID_API_N_MR1__:
|
||||
case __ANDROID_API_N__:
|
||||
OFFSET_ArtMehod_in_Object = 0;
|
||||
OFFSET_access_flags_in_ArtMethod = 4; // sizeof(GcRoot<mirror::Class>) = 4
|
||||
OFFSET_dex_method_index_in_ArtMethod = 4 * 3;
|
||||
OFFSET_dex_cache_resolved_methods_in_ArtMethod = roundUpToPtrSize(4 * 4 + 2 * 2);
|
||||
OFFSET_array_in_PointerArray = 0;
|
||||
|
||||
// ptr_sized_fields_ is rounded up to pointer_size in ArtMethod
|
||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
|
||||
roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 3;
|
||||
|
||||
ArtMethodSize = roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 4;
|
||||
break;
|
||||
case __ANDROID_API_M__:
|
||||
OFFSET_ArtMehod_in_Object = 0;
|
||||
OFFSET_entry_point_from_interpreter_in_ArtMethod = roundUpToPtrSize(4 * 7);
|
||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
|
||||
OFFSET_entry_point_from_interpreter_in_ArtMethod + pointer_size * 2;
|
||||
OFFSET_dex_method_index_in_ArtMethod = 4 * 5;
|
||||
OFFSET_dex_cache_resolved_methods_in_ArtMethod = 4;
|
||||
OFFSET_array_in_PointerArray = 4 * 3;
|
||||
ArtMethodSize = roundUpToPtrSize(4 * 7) + pointer_size * 3;
|
||||
break;
|
||||
case __ANDROID_API_L_MR1__:
|
||||
OFFSET_ArtMehod_in_Object = 4 * 2;
|
||||
|
|
@ -97,42 +79,44 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
|
|||
OFFSET_ArtMehod_in_Object + 4 * 7);
|
||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
|
||||
OFFSET_entry_point_from_interpreter_in_ArtMethod + pointer_size * 2;
|
||||
OFFSET_dex_method_index_in_ArtMethod = OFFSET_ArtMehod_in_Object + 4 * 5;
|
||||
OFFSET_dex_cache_resolved_methods_in_ArtMethod = OFFSET_ArtMehod_in_Object + 4;
|
||||
OFFSET_array_in_PointerArray = 12;
|
||||
ArtMethodSize = OFFSET_entry_point_from_interpreter_in_ArtMethod + pointer_size * 3;
|
||||
break;
|
||||
case __ANDROID_API_L__:
|
||||
OFFSET_ArtMehod_in_Object = 4 * 2;
|
||||
OFFSET_entry_point_from_interpreter_in_ArtMethod = OFFSET_ArtMehod_in_Object + 4 * 4;
|
||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
|
||||
OFFSET_entry_point_from_interpreter_in_ArtMethod + 8 * 2;
|
||||
OFFSET_dex_method_index_in_ArtMethod =
|
||||
OFFSET_ArtMehod_in_Object + 4 * 4 + 8 * 4 + 4 * 2;
|
||||
OFFSET_dex_cache_resolved_methods_in_ArtMethod = OFFSET_ArtMehod_in_Object + 4;
|
||||
OFFSET_array_in_PointerArray = 12;
|
||||
ArtMethodSize = OFFSET_ArtMehod_in_Object + 4 * 4 + 8 * 4 + 4 * 4;
|
||||
break;
|
||||
default:
|
||||
LOGE("not compatible with SDK %d", sdkVersion);
|
||||
break;
|
||||
}
|
||||
|
||||
setupTrampoline();
|
||||
setupTrampoline(OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod);
|
||||
}
|
||||
|
||||
static uint32_t getFlags(char *method) {
|
||||
uint32_t access_flags = read32(method + OFFSET_access_flags_in_ArtMethod);
|
||||
return access_flags;
|
||||
}
|
||||
|
||||
static void setFlags(char *method, uint32_t access_flags) {
|
||||
write32(method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||
}
|
||||
|
||||
void setNonCompilable(void *method) {
|
||||
if (SDKVersion < __ANDROID_API_N__) {
|
||||
return;
|
||||
}
|
||||
int access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
|
||||
LOGI("setNonCompilable: access flags is 0x%x", access_flags);
|
||||
int access_flags = getFlags(method);
|
||||
int old_flags = access_flags;
|
||||
access_flags |= kAccCompileDontBother;
|
||||
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||
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 = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
|
||||
int access_flags = getFlags(method);
|
||||
int old_flags = access_flags;
|
||||
LOGI("setNativeFlag: access flags is 0x%x", access_flags);
|
||||
int old_access_flags = access_flags;
|
||||
if (isNative) {
|
||||
|
|
@ -145,13 +129,14 @@ bool setNativeFlag(void *method, bool isNative) {
|
|||
access_flags &= ~kAccNative;
|
||||
}
|
||||
if (access_flags != old_access_flags) {
|
||||
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||
setFlags(method, access_flags);
|
||||
LOGI("change access flags from 0x%x to 0x%x", old_flags, access_flags);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, void *backupMethod) {
|
||||
static int replaceMethod(void *fromMethod, void *toMethod, int isBackup) {
|
||||
if (hookCount >= hookCap) {
|
||||
LOGI("not enough capacity. Allocating...");
|
||||
if (doInitHookCap(DEFAULT_CAP)) {
|
||||
|
|
@ -161,35 +146,26 @@ static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, vo
|
|||
LOGI("Allocating done");
|
||||
}
|
||||
|
||||
LOGI("target method is at %p, hook method is at %p, backup method is at %p",
|
||||
targetMethod, hookMethod, backupMethod);
|
||||
|
||||
|
||||
// set kAccCompileDontBother for a method we do not want the compiler to compile
|
||||
// so that we don't need to worry about hotness_count_
|
||||
if (SDKVersion >= __ANDROID_API_N__) {
|
||||
setNonCompilable(targetMethod);
|
||||
setNonCompilable(hookMethod);
|
||||
}
|
||||
|
||||
if (backupMethod) {// do method backup
|
||||
// have to copy the whole target ArtMethod here
|
||||
// if the target method calls other methods which are to be resolved
|
||||
// then ToDexPC would be invoked for the caller(origin method)
|
||||
// in which case ToDexPC would use the entrypoint as a base for mapping pc to dex offset
|
||||
// so any changes to the target method's entrypoint would result in a wrong dex offset
|
||||
// and artQuickResolutionTrampoline would fail for methods called by the origin method
|
||||
memcpy(backupMethod, targetMethod, ArtMethodSize);
|
||||
}
|
||||
LOGI("replace method from %p to %p", fromMethod, toMethod);
|
||||
|
||||
// replace entry point
|
||||
void *newEntrypoint = genTrampoline(hookMethod);
|
||||
LOGI("origin ep is %p, new ep is %p",
|
||||
readAddr((char *) targetMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod),
|
||||
void *newEntrypoint = NULL;
|
||||
if(isBackup) {
|
||||
void *originEntrypoint = readAddr((char *) toMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod);
|
||||
// entry point hardcoded
|
||||
newEntrypoint = genTrampoline(toMethod, originEntrypoint);
|
||||
}
|
||||
else {
|
||||
// entry point from ArtMethod struct
|
||||
newEntrypoint = genTrampoline(toMethod, NULL);
|
||||
}
|
||||
|
||||
LOGI("replace entry point from %p to %p",
|
||||
readAddr((char *) fromMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod),
|
||||
newEntrypoint
|
||||
);
|
||||
if (newEntrypoint) {
|
||||
memcpy((char *) targetMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
|
||||
memcpy((char *) fromMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
|
||||
&newEntrypoint,
|
||||
pointer_size);
|
||||
} else {
|
||||
|
|
@ -198,69 +174,46 @@ static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, vo
|
|||
}
|
||||
|
||||
if (OFFSET_entry_point_from_interpreter_in_ArtMethod != 0) {
|
||||
memcpy((char *) targetMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod,
|
||||
(char *) hookMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod,
|
||||
memcpy((char *) fromMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod,
|
||||
(char *) toMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod,
|
||||
pointer_size);
|
||||
}
|
||||
|
||||
// set the target method to native so that Android O wouldn't invoke it with interpreter
|
||||
if (SDKVersion >= __ANDROID_API_O__) {
|
||||
setNativeFlag(targetMethod, true);
|
||||
setNativeFlag(fromMethod, true);
|
||||
}
|
||||
|
||||
LOGI("hook and backup done");
|
||||
hookCount += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ensureMethodCached(void *hookMethod, void *backupMethod,
|
||||
void *hookClassResolvedMethods) {
|
||||
if (!backupMethod) {
|
||||
LOGE("ensureMethodCached: backupMethod is null");
|
||||
return;
|
||||
}
|
||||
void *dexCacheResolvedMethods;
|
||||
// then we get the dex method index of the static backup method
|
||||
int methodIndex = read32(
|
||||
(void *) ((char *) backupMethod + OFFSET_dex_method_index_in_ArtMethod));
|
||||
static int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMethod) {
|
||||
LOGI("target method is at %p, hook method is at %p, backup method is at %p",
|
||||
targetMethod, hookMethod, backupMethod);
|
||||
|
||||
// todo fixme
|
||||
if (methodIndex >= 512) {
|
||||
LOGW("methodIndex = %d", methodIndex);
|
||||
int res = 0;
|
||||
|
||||
// set kAccCompileDontBother for a method we do not want the compiler to compile
|
||||
// so that we don't need to worry about hotness_count_
|
||||
if (SDKVersion >= __ANDROID_API_N__) {
|
||||
setNonCompilable(targetMethod);
|
||||
// setNonCompilable(hookMethod);
|
||||
if(backupMethod) setNonCompilable(backupMethod);
|
||||
}
|
||||
|
||||
// update the cached method manually
|
||||
// first we find the array of cached methods
|
||||
dexCacheResolvedMethods = hookClassResolvedMethods;
|
||||
|
||||
if (!dexCacheResolvedMethods) {
|
||||
LOGE("dexCacheResolvedMethods is null");
|
||||
return;
|
||||
if (backupMethod) {// do method backup
|
||||
// we use the same way as hooking target method
|
||||
// hook backup method and redirect back to the original target method
|
||||
// the only difference is that the entry point is now hardcoded
|
||||
// instead of reading from ArtMethod struct since it's overwritten
|
||||
res += replaceMethod(backupMethod, targetMethod, 1);
|
||||
}
|
||||
|
||||
// finally the addr of backup method is put at the corresponding location in cached methods array
|
||||
if (SDKVersion >= __ANDROID_API_O_MR1__) {
|
||||
// array of MethodDexCacheType is used as dexCacheResolvedMethods in Android 8.1
|
||||
// struct:
|
||||
// struct NativeDexCachePair<T> = { T*, size_t idx }
|
||||
// MethodDexCachePair = NativeDexCachePair<ArtMethod> = { ArtMethod*, size_t idx }
|
||||
// MethodDexCacheType = std::atomic<MethodDexCachePair>
|
||||
memcpy((char *) dexCacheResolvedMethods + OFFSET_array_in_PointerArray +
|
||||
pointer_size * 2 * methodIndex,
|
||||
(&backupMethod),
|
||||
pointer_size
|
||||
);
|
||||
memcpy((char *) dexCacheResolvedMethods + OFFSET_array_in_PointerArray +
|
||||
pointer_size * 2 * methodIndex + pointer_size,
|
||||
&methodIndex,
|
||||
pointer_size
|
||||
);
|
||||
} else {
|
||||
memcpy((char *) dexCacheResolvedMethods + OFFSET_array_in_PointerArray +
|
||||
pointer_size * methodIndex,
|
||||
(&backupMethod),
|
||||
pointer_size);
|
||||
}
|
||||
res += replaceMethod(targetMethod, hookMethod, 0);
|
||||
|
||||
LOGI("hook and backup done");
|
||||
return res;
|
||||
}
|
||||
|
||||
void *getArtMethod(JNIEnv *env, jobject jmethod) {
|
||||
|
|
@ -312,13 +265,11 @@ jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass
|
|||
jobject target, jobject hook,
|
||||
jobject backup) {
|
||||
|
||||
if (!doBackupAndHook(env,
|
||||
getArtMethod(env, target),
|
||||
if (!doBackupAndHook(getArtMethod(env, target),
|
||||
getArtMethod(env, hook),
|
||||
getArtMethod(env, backup)
|
||||
)) {
|
||||
(*env)->NewGlobalRef(env,
|
||||
hook); // keep a global ref so that the hook method would not be GCed
|
||||
(*env)->NewGlobalRef(env, hook); // keep a global ref so that the hook method would not be GCed
|
||||
if (backup) (*env)->NewGlobalRef(env, backup);
|
||||
return JNI_TRUE;
|
||||
} else {
|
||||
|
|
@ -326,14 +277,6 @@ jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass
|
|||
}
|
||||
}
|
||||
|
||||
void Java_lab_galaxy_yahfa_HookMain_ensureMethodCached(JNIEnv *env, jclass clazz,
|
||||
jobject hook,
|
||||
jobject backup) {
|
||||
ensureMethodCached(getArtMethod(env, hook),
|
||||
getArtMethod(env, backup),
|
||||
getResolvedMethodsAddr(env, hook));
|
||||
}
|
||||
|
||||
static void *getResolvedMethodsAddr(JNIEnv *env, jobject hook) {
|
||||
// get backup class
|
||||
jclass methodClass = (*env)->FindClass(env, "java/lang/reflect/Method");
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
#include "common.h"
|
||||
#include "trampoline.h"
|
||||
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
static unsigned char *trampolineCode; // place where trampolines are saved
|
||||
static unsigned int trampolineSize; // trampoline size required for each hook
|
||||
|
||||
|
|
@ -21,8 +23,13 @@ unsigned int hookCap = 0;
|
|||
unsigned int hookCount = 0;
|
||||
|
||||
// trampoline:
|
||||
// 1. set eax/r0/x0 to the hook ArtMethod addr
|
||||
// 1. set eax/rdi/r0/x0 to the hook ArtMethod addr
|
||||
// 2. jump into its entry point
|
||||
|
||||
// trampoline for backup:
|
||||
// 1. set eax/rdi/r0/x0 to the target ArtMethod addr
|
||||
// 2. ret to the hardcoded original entry point
|
||||
|
||||
#if defined(__i386__)
|
||||
// b8 78 56 34 12 ; mov eax, 0x12345678 (addr of the hook method)
|
||||
// ff 70 20 ; push DWORD PTR [eax + 0x20]
|
||||
|
|
@ -33,6 +40,15 @@ unsigned char trampoline[] = {
|
|||
0xc3
|
||||
};
|
||||
|
||||
// b8 78 56 34 12 ; mov eax, 0x12345678 (addr of the target method)
|
||||
// 68 78 56 34 12 ; push 0x12345678 (original entry point of the target method)
|
||||
// c3 ; ret
|
||||
unsigned char trampolineForBackup[] = {
|
||||
0xb8, 0x78, 0x56, 0x34, 0x12,
|
||||
0x68, 0x78, 0x56, 0x34, 0x12,
|
||||
0xc3
|
||||
};
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
// 48 bf 78 56 34 12 78 56 34 12 ; movabs rdi, 0x1234567812345678
|
||||
// ff 77 20 ; push QWORD PTR [rdi + 0x20]
|
||||
|
|
@ -43,6 +59,17 @@ unsigned char trampoline[] = {
|
|||
0xc3
|
||||
};
|
||||
|
||||
// 48 bf 78 56 34 12 78 56 34 12 ; movabs rdi, 0x1234567812345678
|
||||
// 57 ; push rdi (original entry point of the target method)
|
||||
// 48 bf 78 56 34 12 78 56 34 12 ; movabs rdi, 0x1234567812345678 (addr of the target method)
|
||||
// c3 ; ret
|
||||
unsigned char trampolineForBackup[] = {
|
||||
0x48, 0xbf, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
|
||||
0x57,
|
||||
0x48, 0xbf, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
|
||||
0xc3
|
||||
};
|
||||
|
||||
#elif defined(__arm__)
|
||||
// 00 00 9F E5 ; ldr r0, [pc, #0]
|
||||
// 20 F0 90 E5 ; ldr pc, [r0, 0x20]
|
||||
|
|
@ -53,6 +80,21 @@ unsigned char trampoline[] = {
|
|||
0x78, 0x56, 0x34, 0x12
|
||||
};
|
||||
|
||||
// 0c 00 9F E5 ; ldr r0, [pc, #12]
|
||||
// 01 00 2d e9 ; push {r0}
|
||||
// 00 00 9F E5 ; ldr r0, [pc, #0]
|
||||
// 00 80 bd e8 ; pop {pc}
|
||||
// 78 56 34 12 ; 0x12345678 (addr of the hook method)
|
||||
// 78 56 34 12 ; 0x12345678 (original entry point of the target method)
|
||||
unsigned char trampolineForBackup[] = {
|
||||
0x0c, 0x00, 0x9f, 0xe5,
|
||||
0x01, 0x00, 0x2d, 0xe9,
|
||||
0x00, 0x00, 0x9f, 0xe5,
|
||||
0x00, 0x80, 0xbd, 0xe8,
|
||||
0x78, 0x56, 0x34, 0x12,
|
||||
0x78, 0x56, 0x34, 0x12
|
||||
};
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
// 60 00 00 58 ; ldr x0, 12
|
||||
// 10 00 40 F8 ; ldr x16, [x0, #0x00]
|
||||
|
|
@ -66,28 +108,74 @@ unsigned char trampoline[] = {
|
|||
0x78, 0x56, 0x34, 0x12,
|
||||
0x89, 0x67, 0x45, 0x23
|
||||
};
|
||||
|
||||
// 60 00 00 58 ; ldr x0, 12
|
||||
// 90 00 00 58 ; ldr x16, 16
|
||||
// 00 02 1f d6 ; br x16
|
||||
// 78 56 34 12
|
||||
// 89 67 45 23 ; 0x2345678912345678 (addr of the hook method)
|
||||
// 78 56 34 12
|
||||
// 89 67 45 23 ; 0x2345678912345678 (original entry point of the target method)
|
||||
unsigned char trampolineForBackup[] = {
|
||||
0x60, 0x00, 0x00, 0x58,
|
||||
0x90, 0x00, 0x00, 0x58,
|
||||
0x00, 0x02, 0x1f, 0xd6,
|
||||
0x78, 0x56, 0x34, 0x12,
|
||||
0x89, 0x67, 0x45, 0x23,
|
||||
0x78, 0x56, 0x34, 0x12,
|
||||
0x89, 0x67, 0x45, 0x23
|
||||
};
|
||||
|
||||
#endif
|
||||
static unsigned int trampolineSize = roundUpToPtrSize(sizeof(trampoline));
|
||||
static unsigned int trampolineSize = roundUpToPtrSize(MAX(sizeof(trampoline), sizeof(trampolineForBackup)));
|
||||
|
||||
void *genTrampoline(void *hookMethod) {
|
||||
void *targetAddr;
|
||||
void *genTrampoline(void *toMethod, void *entrypoint) {
|
||||
unsigned char *targetAddr = trampolineCode + trampolineSize * hookCount;
|
||||
|
||||
targetAddr = trampolineCode + trampolineSize * hookCount;
|
||||
memcpy(targetAddr, trampoline,
|
||||
sizeof(trampoline)); // do not use trampolineSize since it's a rounded size
|
||||
if(entrypoint != NULL) {
|
||||
memcpy(targetAddr, trampolineForBackup, sizeof(trampolineForBackup));
|
||||
}
|
||||
else {
|
||||
memcpy(targetAddr, trampoline,
|
||||
sizeof(trampoline)); // do not use trampolineSize since it's a rounded size
|
||||
}
|
||||
|
||||
// replace with the actual ArtMethod addr
|
||||
#if defined(__i386__)
|
||||
memcpy(targetAddr+1, &hookMethod, pointer_size);
|
||||
if(entrypoint) {
|
||||
memcpy(targetAddr + 1, &toMethod, pointer_size);
|
||||
memcpy(targetAddr + 6, &entrypoint, pointer_size);
|
||||
}
|
||||
else {
|
||||
memcpy(targetAddr + 1, &toMethod, pointer_size);
|
||||
}
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
memcpy((char*)targetAddr + 2, &hookMethod, pointer_size);
|
||||
if(entrypoint) {
|
||||
memcpy(targetAddr + 2, &entrypoint, pointer_size);
|
||||
memcpy(targetAddr + 13, &toMethod, pointer_size);
|
||||
}
|
||||
else {
|
||||
memcpy(targetAddr + 2, &toMethod, pointer_size);
|
||||
}
|
||||
|
||||
#elif defined(__arm__)
|
||||
memcpy(targetAddr+8, &hookMethod, pointer_size);
|
||||
if(entrypoint) {
|
||||
memcpy(targetAddr + 20, &entrypoint, pointer_size);
|
||||
memcpy(targetAddr + 16, &toMethod, pointer_size);
|
||||
}
|
||||
else {
|
||||
memcpy(targetAddr + 8, &toMethod, pointer_size);
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
memcpy(targetAddr + 12, &hookMethod, pointer_size);
|
||||
if(entrypoint) {
|
||||
memcpy(targetAddr + 20, &entrypoint, pointer_size);
|
||||
memcpy(targetAddr + 12, &toMethod, pointer_size);
|
||||
}
|
||||
else {
|
||||
memcpy(targetAddr + 12, &toMethod, pointer_size);
|
||||
}
|
||||
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
|
|
@ -96,18 +184,16 @@ void *genTrampoline(void *hookMethod) {
|
|||
return targetAddr;
|
||||
}
|
||||
|
||||
void setupTrampoline() {
|
||||
void setupTrampoline(uint8_t offset) {
|
||||
#if defined(__i386__)
|
||||
trampoline[7] = (unsigned char)OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||
trampoline[7] = offset;
|
||||
#elif defined(__x86_64__)
|
||||
trampoline[12] = (unsigned char)OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||
trampoline[12] = offset;
|
||||
#elif defined(__arm__)
|
||||
trampoline[4] = (unsigned char)OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||
trampoline[4] = offset;
|
||||
#elif defined(__aarch64__)
|
||||
trampoline[5] |=
|
||||
((unsigned char) OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod) << 4;
|
||||
trampoline[6] |=
|
||||
((unsigned char) OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod) >> 4;
|
||||
trampoline[5] |= offset << 4;
|
||||
trampoline[6] |= offset >> 4;
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#define YAHFA_TAMPOLINE_H
|
||||
|
||||
extern int SDKVersion;
|
||||
extern int OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||
|
||||
extern unsigned int hookCap; // capacity for trampolines
|
||||
extern unsigned int hookCount; // current count of used trampolines
|
||||
|
|
@ -14,8 +13,8 @@ extern unsigned int hookCount; // current count of used trampolines
|
|||
extern unsigned char trampoline[];
|
||||
|
||||
int doInitHookCap(unsigned int cap);
|
||||
void setupTrampoline();
|
||||
void *genTrampoline(void *hookMethod);
|
||||
void setupTrampoline(uint8_t offset);
|
||||
void *genTrampoline(void *toMethod, void *entrypoint);
|
||||
|
||||
#define DEFAULT_CAP 1 //size of each trampoline area would be no more than 4k Bytes(one page)
|
||||
|
||||
|
|
|
|||
|
|
@ -344,6 +344,7 @@ namespace edxp {
|
|||
[](auto i) {
|
||||
return GetFrameworkPath(i);
|
||||
});
|
||||
LOGI("Got base config path: %s", misc_path_.c_str());
|
||||
} catch (const RirudSocket::RirudSocketException &e) {
|
||||
LOGE("%s", e.what());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,6 @@ namespace edxp {
|
|||
return Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(env, clazz, target, hook, backup);
|
||||
}
|
||||
|
||||
static void Yahfa_ensureMethodCached(JNI_START, jobject hook, jobject backup) {
|
||||
Java_lab_galaxy_yahfa_HookMain_ensureMethodCached(env, clazz, hook, backup);
|
||||
}
|
||||
|
||||
static void Yahfa_setMethodNonCompilable(JNI_START, jobject member) {
|
||||
if (!member) {
|
||||
LOGE("setNonCompilableNative: member is null");
|
||||
|
|
@ -59,8 +55,6 @@ namespace edxp {
|
|||
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;"),
|
||||
NATIVE_METHOD(Yahfa, backupAndHookNative,
|
||||
"(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"),
|
||||
NATIVE_METHOD(Yahfa, ensureMethodCached,
|
||||
"(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V"),
|
||||
NATIVE_METHOD(Yahfa, setMethodNonCompilable, "(Ljava/lang/reflect/Member;)V"),
|
||||
NATIVE_METHOD(Yahfa, setNativeFlag, "(Ljava/lang/reflect/Member;Z)Z"),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -169,5 +169,5 @@ chcon -R u:object_r:system_file:s0 "${MODDIR}"
|
|||
chcon -R ${PATH_CONTEXT} "${LOG_PATH}"
|
||||
chown -R ${PATH_OWNER} "${LOG_PATH}"
|
||||
chmod -R 666 "${LOG_PATH}"
|
||||
[[ -z "$MISC_PATH" ]] && chcon -R u:object_r:magisk_file:s0 "$BASE_PATH"
|
||||
[[ -z "$MISC_PATH" ]] || chcon -R u:object_r:magisk_file:s0 "$BASE_PATH"
|
||||
rm -f /data/adb/edxp/new_install
|
||||
|
|
@ -6,7 +6,6 @@ import com.elderdrivers.riru.edxp.core.BaseEdxpImpl;
|
|||
import com.elderdrivers.riru.edxp.core.EdxpImpl;
|
||||
import com.elderdrivers.riru.edxp.core.Main;
|
||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||
import com.elderdrivers.riru.edxp.core.yahfa.HookMethodResolver;
|
||||
import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge;
|
||||
|
||||
public class SandHookEdxpImpl extends BaseEdxpImpl {
|
||||
|
|
@ -32,7 +31,6 @@ public class SandHookEdxpImpl extends BaseEdxpImpl {
|
|||
@Override
|
||||
public void init() {
|
||||
Yahfa.init(Build.VERSION.SDK_INT);
|
||||
HookMethodResolver.init();
|
||||
getRouter().injectConfig();
|
||||
SandHookXposedBridge.init();
|
||||
setInitialized();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package com.elderdrivers.riru.edxp.service;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.BroadcastReceiver;
|
||||
|
|
@ -98,9 +96,11 @@ public class PackageReceiver {
|
|||
return result;
|
||||
}
|
||||
|
||||
private void updateModuleList(int uid) {
|
||||
private void updateModuleList(int uid, String packageName) {
|
||||
Map<String, String> enabledModules = loadEnabledModules(uid);
|
||||
|
||||
if(packageName != null && !enabledModules.containsKey(packageName)) return;
|
||||
|
||||
try {
|
||||
File moduleListFile = new File(CONFIG_PATH, uid + "/" + MODULES_LIST_FILENAME);
|
||||
moduleListFile.createNewFile();
|
||||
|
|
@ -155,7 +155,7 @@ public class PackageReceiver {
|
|||
for (Object uh : (List<Object>) m.invoke(um)) {
|
||||
int uid = (int) uh.getClass().getDeclaredField("id").get(uh);
|
||||
Utils.logI("updating uid: " + uid);
|
||||
updateModuleList(uid);
|
||||
updateModuleList(uid, pkgInfo == null ? null : packageName);
|
||||
}
|
||||
Toast.makeText(context, "EdXposed: Updated " + packageName, Toast.LENGTH_SHORT).show();
|
||||
} catch (Throwable e) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import com.elderdrivers.riru.edxp.core.EdxpImpl;
|
|||
import com.elderdrivers.riru.edxp.core.Main;
|
||||
import com.elderdrivers.riru.edxp.core.Proxy;
|
||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||
import com.elderdrivers.riru.edxp.core.yahfa.HookMethodResolver;
|
||||
import com.elderdrivers.riru.edxp.proxy.NormalProxy;
|
||||
import com.elderdrivers.riru.edxp.proxy.Router;
|
||||
|
||||
|
|
@ -29,7 +28,6 @@ public class YahfaEdxpImpl extends BaseEdxpImpl {
|
|||
@Override
|
||||
public void init() {
|
||||
Yahfa.init(Build.VERSION.SDK_INT);
|
||||
HookMethodResolver.init();
|
||||
getRouter().injectConfig();
|
||||
setInitialized();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue