Revert YAHFA to the version where backup are memcpied
This commit is contained in:
parent
7f08ddcc21
commit
b04d830fba
|
|
@ -17,21 +17,12 @@ jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass
|
||||||
jobject target, jobject hook,
|
jobject target, jobject hook,
|
||||||
jobject backup);
|
jobject backup);
|
||||||
|
|
||||||
void Java_lab_galaxy_yahfa_HookMain_ensureMethodCached(JNIEnv *env, jclass clazz,
|
|
||||||
jobject hook,
|
|
||||||
jobject backup);
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
void setNonCompilable(void *method);
|
void setNonCompilable(void *method);
|
||||||
|
|
||||||
void *getArtMethod(JNIEnv *env, jobject jmethod);
|
void *getArtMethod(JNIEnv *env, jobject jmethod);
|
||||||
|
|
||||||
// TODO: move to common utils instead of in YAHFA's code
|
#ifdef __cplusplus
|
||||||
void *getEntryPoint(void* method);
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// get original entrypoint from target ArtMethod
|
#endif // HOOK_MAIN_H
|
||||||
void *getOriginalEntryPointFromTargetMethod(void* method);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // HOOK_MAIN_H
|
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,17 @@
|
||||||
#define YAHFA_TAMPOLINE_H
|
#define YAHFA_TAMPOLINE_H
|
||||||
|
|
||||||
extern int SDKVersion;
|
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
|
||||||
|
|
||||||
extern unsigned char trampoline[];
|
extern unsigned char trampoline[];
|
||||||
extern unsigned char trampolineForBackup[];
|
|
||||||
|
|
||||||
void* doInitHookCap(size_t cap);
|
int doInitHookCap(unsigned int cap);
|
||||||
void setupTrampoline(uint8_t offset);
|
void setupTrampoline();
|
||||||
void *genTrampoline(void *toMethod, void *entrypoint);
|
void *genTrampoline(void *hookMethod);
|
||||||
|
|
||||||
|
#define DEFAULT_CAP 1 //size of each trampoline area would be no more than 4k Bytes(one page)
|
||||||
|
|
||||||
#endif //YAHFA_TAMPOLINE_H
|
#endif //YAHFA_TAMPOLINE_H
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,252 @@
|
||||||
|
#include "jni.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "trampoline.h"
|
||||||
|
#include "HookMain.h"
|
||||||
|
|
||||||
|
int SDKVersion;
|
||||||
|
static int OFFSET_entry_point_from_interpreter_in_ArtMethod;
|
||||||
|
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 uint32_t kAccCompileDontBother = 0x01000000;
|
||||||
|
static uint32_t kAccPublic = 0x0001; // class, field, method, ic
|
||||||
|
static uint32_t kAccPrivate = 0x0002; // field, method, ic
|
||||||
|
static uint32_t kAccProtected = 0x0004; // field, method, ic
|
||||||
|
static uint32_t kAccStatic = 0x0008; // field, method, ic
|
||||||
|
|
||||||
|
|
||||||
|
static jfieldID fieldArtMethod = NULL;
|
||||||
|
|
||||||
|
static inline uint32_t read32(void *addr) {
|
||||||
|
return *((uint32_t *) addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void write32(void *addr, uint32_t value) {
|
||||||
|
*((uint32_t *) addr) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *readAddr(void *addr) {
|
||||||
|
return *((void **) addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void writeAddr(void *addr, void *value) {
|
||||||
|
*((void **) addr) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion) {
|
||||||
|
SDKVersion = sdkVersion;
|
||||||
|
jclass classExecutable;
|
||||||
|
LOGI("init to SDK %d", sdkVersion);
|
||||||
|
switch (sdkVersion) {
|
||||||
|
case __ANDROID_API_R__:
|
||||||
|
classExecutable = (*env)->FindClass(env, "java/lang/reflect/Executable");
|
||||||
|
fieldArtMethod = (*env)->GetFieldID(env, classExecutable, "artMethod", "J");
|
||||||
|
case __ANDROID_API_Q__:
|
||||||
|
case __ANDROID_API_P__:
|
||||||
|
kAccCompileDontBother = 0x02000000;
|
||||||
|
OFFSET_ArtMehod_in_Object = 0;
|
||||||
|
OFFSET_access_flags_in_ArtMethod = 4;
|
||||||
|
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_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
|
||||||
|
// 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;
|
||||||
|
ArtMethodSize = roundUpToPtrSize(4 * 7) + pointer_size * 3;
|
||||||
|
break;
|
||||||
|
case __ANDROID_API_L_MR1__:
|
||||||
|
OFFSET_ArtMehod_in_Object = 4 * 2;
|
||||||
|
OFFSET_entry_point_from_interpreter_in_ArtMethod = roundUpToPtrSize(
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
ArtMethodSize = OFFSET_ArtMehod_in_Object + 4 * 4 + 8 * 4 + 4 * 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGE("not compatible with SDK %d", sdkVersion);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupTrampoline();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNonCompilable(void *method) {
|
||||||
|
if (SDKVersion < __ANDROID_API_N__) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
|
||||||
|
LOGI("setNonCompilable: access flags is 0x%x", access_flags);
|
||||||
|
access_flags |= kAccCompileDontBother;
|
||||||
|
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPrivate(void *method) {
|
||||||
|
uint32_t access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
|
||||||
|
if (!(access_flags & kAccStatic)) {
|
||||||
|
LOGI("setPrivate: access flags is 0x%x", access_flags);
|
||||||
|
access_flags |= kAccPrivate;
|
||||||
|
access_flags &= ~kAccProtected;
|
||||||
|
access_flags &= ~kAccPublic;
|
||||||
|
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, void *backupMethod) {
|
||||||
|
if (hookCount >= hookCap) {
|
||||||
|
LOGI("not enough capacity. Allocating...");
|
||||||
|
if (doInitHookCap(DEFAULT_CAP)) {
|
||||||
|
LOGE("cannot hook method");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
setPrivate(backupMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
newEntrypoint
|
||||||
|
);
|
||||||
|
if (newEntrypoint) {
|
||||||
|
writeAddr((char *) targetMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
|
||||||
|
newEntrypoint);
|
||||||
|
} else {
|
||||||
|
LOGE("failed to allocate space for trampoline of target method");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OFFSET_entry_point_from_interpreter_in_ArtMethod != 0) {
|
||||||
|
writeAddr((char *) targetMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod,
|
||||||
|
readAddr((char *) hookMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the target method to native so that Android O wouldn't invoke it with interpreter
|
||||||
|
if (SDKVersion >= __ANDROID_API_O__) {
|
||||||
|
// setNativeFlag(targetMethod, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGI("hook and backup done");
|
||||||
|
hookCount += 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *getArtMethod(JNIEnv *env, jobject jmethod) {
|
||||||
|
void *artMethod = NULL;
|
||||||
|
|
||||||
|
if (jmethod == NULL) {
|
||||||
|
return artMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SDKVersion == __ANDROID_API_R__) {
|
||||||
|
artMethod = (void *) (*env)->GetLongField(env, jmethod, fieldArtMethod);
|
||||||
|
} else {
|
||||||
|
artMethod = (void *) (*env)->FromReflectedMethod(env, jmethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGI("ArtMethod: %p", artMethod);
|
||||||
|
return artMethod;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env, jclass clazz,
|
||||||
|
jclass targetClass, jstring methodName,
|
||||||
|
jstring methodSig) {
|
||||||
|
const char *c_methodName = (*env)->GetStringUTFChars(env, methodName, NULL);
|
||||||
|
const char *c_methodSig = (*env)->GetStringUTFChars(env, methodSig, NULL);
|
||||||
|
jobject ret = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
//Try both GetMethodID and GetStaticMethodID -- Whatever works :)
|
||||||
|
jmethodID method = (*env)->GetMethodID(env, targetClass, c_methodName, c_methodSig);
|
||||||
|
if (!(*env)->ExceptionCheck(env)) {
|
||||||
|
ret = (*env)->ToReflectedMethod(env, targetClass, method, JNI_FALSE);
|
||||||
|
} else {
|
||||||
|
(*env)->ExceptionClear(env);
|
||||||
|
method = (*env)->GetStaticMethodID(env, targetClass, c_methodName, c_methodSig);
|
||||||
|
if (!(*env)->ExceptionCheck(env)) {
|
||||||
|
ret = (*env)->ToReflectedMethod(env, targetClass, method, JNI_TRUE);
|
||||||
|
} else {
|
||||||
|
(*env)->ExceptionClear(env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->ReleaseStringUTFChars(env, methodName, c_methodName);
|
||||||
|
(*env)->ReleaseStringUTFChars(env, methodSig, c_methodSig);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass clazz,
|
||||||
|
jobject target, jobject hook,
|
||||||
|
jobject backup) {
|
||||||
|
|
||||||
|
if (!doBackupAndHook(env,
|
||||||
|
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
|
||||||
|
if (backup) (*env)->NewGlobalRef(env, backup);
|
||||||
|
return JNI_TRUE;
|
||||||
|
} else {
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,257 +0,0 @@
|
||||||
#include "jni.h"
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "HookMain.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "trampoline.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
int SDKVersion;
|
|
||||||
namespace {
|
|
||||||
uint32_t OFFSET_entry_point_from_interpreter_in_ArtMethod;
|
|
||||||
uint32_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
|
||||||
uint32_t OFFSET_ArtMehod_in_Object;
|
|
||||||
uint32_t OFFSET_access_flags_in_ArtMethod;
|
|
||||||
uint32_t kAccCompileDontBother = 0x01000000;
|
|
||||||
uint32_t kAccNative = 0x0100;
|
|
||||||
uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;
|
|
||||||
|
|
||||||
jfieldID fieldArtMethod = nullptr;
|
|
||||||
|
|
||||||
std::unordered_map<void *, void *> replaced_entrypoint;
|
|
||||||
|
|
||||||
inline uint32_t read32(void *addr) {
|
|
||||||
return *((uint32_t *) addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void write32(void *addr, uint32_t value) {
|
|
||||||
*((uint32_t *) addr) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void *readAddr(void *addr) {
|
|
||||||
return *((void **) addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void writeAddr(void *addr, void *value) {
|
|
||||||
*((void **) addr) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion) {
|
|
||||||
SDKVersion = sdkVersion;
|
|
||||||
jclass classExecutable;
|
|
||||||
LOGI("init to SDK %d", sdkVersion);
|
|
||||||
switch (sdkVersion) {
|
|
||||||
case __ANDROID_API_R__:
|
|
||||||
classExecutable = env->FindClass("java/lang/reflect/Executable");
|
|
||||||
fieldArtMethod = env->GetFieldID(classExecutable, "artMethod", "J");
|
|
||||||
case __ANDROID_API_Q__:
|
|
||||||
case __ANDROID_API_P__:
|
|
||||||
kAccCompileDontBother = 0x02000000;
|
|
||||||
OFFSET_ArtMehod_in_Object = 0;
|
|
||||||
OFFSET_access_flags_in_ArtMethod = 4;
|
|
||||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
|
|
||||||
roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size;
|
|
||||||
break;
|
|
||||||
case __ANDROID_API_O_MR1__:
|
|
||||||
kAccCompileDontBother = 0x02000000;
|
|
||||||
case __ANDROID_API_O__:
|
|
||||||
OFFSET_ArtMehod_in_Object = 0;
|
|
||||||
OFFSET_access_flags_in_ArtMethod = 4;
|
|
||||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
|
|
||||||
roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 2;
|
|
||||||
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
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
break;
|
|
||||||
case __ANDROID_API_L_MR1__:
|
|
||||||
OFFSET_ArtMehod_in_Object = 4 * 2;
|
|
||||||
OFFSET_entry_point_from_interpreter_in_ArtMethod = roundUpToPtrSize(
|
|
||||||
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;
|
|
||||||
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;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOGE("not compatible with SDK %d", sdkVersion);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupTrampoline(OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t getFlags(void *method_) {
|
|
||||||
char *method = (char *) method_;
|
|
||||||
uint32_t access_flags = read32(method + OFFSET_access_flags_in_ArtMethod);
|
|
||||||
return access_flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setFlags(void *method_, uint32_t access_flags) {
|
|
||||||
char *method = (char *) method_;
|
|
||||||
write32(method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setNonCompilable(void *method) {
|
|
||||||
if (SDKVersion < __ANDROID_API_N__) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *getEntryPoint(void *method) {
|
|
||||||
return readAddr((char *) method + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int replaceMethod(void *fromMethod, void *toMethod, int isBackup) {
|
|
||||||
// replace entry point
|
|
||||||
void *newEntrypoint = nullptr;
|
|
||||||
void *fromEntrypoint =
|
|
||||||
(char *) fromMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
|
||||||
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, nullptr);
|
|
||||||
}
|
|
||||||
replaced_entrypoint[fromMethod] = readAddr(fromEntrypoint);
|
|
||||||
|
|
||||||
LOGI("replace entry point from %p to %p", readAddr(fromEntrypoint), newEntrypoint);
|
|
||||||
if (newEntrypoint) {
|
|
||||||
writeAddr(fromEntrypoint, newEntrypoint);
|
|
||||||
} else {
|
|
||||||
LOGE("failed to allocate space for trampoline of target method");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For pre Android M devices, should be not used by EdXposed.
|
|
||||||
if (OFFSET_entry_point_from_interpreter_in_ArtMethod != 0) {
|
|
||||||
void *interpEntrypoint = readAddr(
|
|
||||||
(char *) toMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod);
|
|
||||||
writeAddr(fromEntrypoint, interpEntrypoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
res += replaceMethod(targetMethod, hookMethod, 0);
|
|
||||||
|
|
||||||
LOGI("hook and backup done");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *getArtMethod(JNIEnv *env, jobject jmethod) {
|
|
||||||
void *artMethod = nullptr;
|
|
||||||
|
|
||||||
if (jmethod == nullptr) {
|
|
||||||
return artMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SDKVersion == __ANDROID_API_R__) {
|
|
||||||
artMethod = (void *) env->GetLongField(jmethod, fieldArtMethod);
|
|
||||||
} else {
|
|
||||||
artMethod = (void *) env->FromReflectedMethod(jmethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGI("HookMain: getArtMethod: %p", artMethod);
|
|
||||||
return artMethod;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env, jclass clazz,
|
|
||||||
jclass targetClass,
|
|
||||||
jstring methodName,
|
|
||||||
jstring methodSig) {
|
|
||||||
const char *c_methodName = env->GetStringUTFChars(methodName, nullptr);
|
|
||||||
const char *c_methodSig = env->GetStringUTFChars(methodSig, nullptr);
|
|
||||||
jobject ret = nullptr;
|
|
||||||
|
|
||||||
|
|
||||||
//Try both GetMethodID and GetStaticMethodID -- Whatever works :)
|
|
||||||
jmethodID method = env->GetMethodID(targetClass, c_methodName, c_methodSig);
|
|
||||||
if (!env->ExceptionCheck()) {
|
|
||||||
ret = env->ToReflectedMethod(targetClass, method, JNI_FALSE);
|
|
||||||
} else {
|
|
||||||
env->ExceptionClear();
|
|
||||||
method = env->GetStaticMethodID(targetClass, c_methodName, c_methodSig);
|
|
||||||
if (!env->ExceptionCheck()) {
|
|
||||||
ret = env->ToReflectedMethod(targetClass, method, JNI_TRUE);
|
|
||||||
} else {
|
|
||||||
env->ExceptionClear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
env->ReleaseStringUTFChars(methodName, c_methodName);
|
|
||||||
env->ReleaseStringUTFChars(methodSig, c_methodSig);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass clazz,
|
|
||||||
jobject target, jobject hook,
|
|
||||||
jobject backup) {
|
|
||||||
|
|
||||||
if (!doBackupAndHook(getArtMethod(env, target),
|
|
||||||
getArtMethod(env, hook),
|
|
||||||
getArtMethod(env, backup)
|
|
||||||
)) {
|
|
||||||
env->NewGlobalRef(hook); // keep a global ref so that the hook method would not be GCed
|
|
||||||
if (backup) env->NewGlobalRef(backup);
|
|
||||||
return JNI_TRUE;
|
|
||||||
} else {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *getOriginalEntryPointFromTargetMethod(void *method) {
|
|
||||||
return replaced_entrypoint[method];
|
|
||||||
}
|
|
||||||
|
|
@ -14,17 +14,15 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "trampoline.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
|
||||||
|
|
||||||
|
unsigned int hookCap = 0;
|
||||||
|
unsigned int hookCount = 0;
|
||||||
|
|
||||||
// trampoline:
|
// trampoline:
|
||||||
// 1. set eax/rdi/r0/x0 to the hook ArtMethod addr
|
// 1. set eax/r0/x0 to the hook ArtMethod addr
|
||||||
// 2. jump into its entry point
|
// 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__)
|
#if defined(__i386__)
|
||||||
// b8 78 56 34 12 ; mov eax, 0x12345678 (addr of the hook method)
|
// b8 78 56 34 12 ; mov eax, 0x12345678 (addr of the hook method)
|
||||||
// ff 70 20 ; push DWORD PTR [eax + 0x20]
|
// ff 70 20 ; push DWORD PTR [eax + 0x20]
|
||||||
|
|
@ -35,15 +33,6 @@ unsigned char trampoline[] = {
|
||||||
0xc3
|
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__)
|
#elif defined(__x86_64__)
|
||||||
// 48 bf 78 56 34 12 78 56 34 12 ; movabs rdi, 0x1234567812345678
|
// 48 bf 78 56 34 12 78 56 34 12 ; movabs rdi, 0x1234567812345678
|
||||||
// ff 77 20 ; push QWORD PTR [rdi + 0x20]
|
// ff 77 20 ; push QWORD PTR [rdi + 0x20]
|
||||||
|
|
@ -54,17 +43,6 @@ unsigned char trampoline[] = {
|
||||||
0xc3
|
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__)
|
#elif defined(__arm__)
|
||||||
// 00 00 9F E5 ; ldr r0, [pc, #0]
|
// 00 00 9F E5 ; ldr r0, [pc, #0]
|
||||||
// 20 F0 90 E5 ; ldr pc, [r0, 0x20]
|
// 20 F0 90 E5 ; ldr pc, [r0, 0x20]
|
||||||
|
|
@ -75,21 +53,6 @@ unsigned char trampoline[] = {
|
||||||
0x78, 0x56, 0x34, 0x12
|
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__)
|
#elif defined(__aarch64__)
|
||||||
// 60 00 00 58 ; ldr x0, 12
|
// 60 00 00 58 ; ldr x0, 12
|
||||||
// 10 00 40 F8 ; ldr x16, [x0, #0x00]
|
// 10 00 40 F8 ; ldr x16, [x0, #0x00]
|
||||||
|
|
@ -103,113 +66,75 @@ unsigned char trampoline[] = {
|
||||||
0x78, 0x56, 0x34, 0x12,
|
0x78, 0x56, 0x34, 0x12,
|
||||||
0x89, 0x67, 0x45, 0x23
|
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
|
#endif
|
||||||
|
static unsigned int trampolineSize = roundUpToPtrSize(sizeof(trampoline));
|
||||||
|
|
||||||
static inline void FlushCache(void *addr, size_t size) {
|
static inline void FlushCache(void *addr, size_t size) {
|
||||||
__builtin___clear_cache((char *) addr, (char *) ((uintptr_t) addr + size));
|
__builtin___clear_cache((char *) addr, (char *) ((uintptr_t) addr + size));
|
||||||
}
|
}
|
||||||
|
|
||||||
void *genTrampoline(void *toMethod, void *entrypoint) {
|
void *genTrampoline(void *hookMethod) {
|
||||||
size_t size = entrypoint == NULL ? sizeof(trampoline) : sizeof(trampolineForBackup);
|
void *targetAddr;
|
||||||
|
|
||||||
// TODO: make use of thread_local to avoid frequent memory allocate
|
targetAddr = trampolineCode + trampolineSize * hookCount;
|
||||||
char *targetAddr = doInitHookCap(size);
|
memcpy(targetAddr, trampoline,
|
||||||
if (targetAddr == NULL) return NULL;
|
sizeof(trampoline)); // do not use trampolineSize since it's a rounded size
|
||||||
|
|
||||||
if (entrypoint != NULL) {
|
|
||||||
memcpy(targetAddr, trampolineForBackup, size);
|
|
||||||
} else {
|
|
||||||
memcpy(targetAddr, trampoline, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace with the actual ArtMethod addr
|
// replace with the actual ArtMethod addr
|
||||||
#if defined(__i386__)
|
#if defined(__i386__)
|
||||||
if(entrypoint) {
|
memcpy(targetAddr+1, &hookMethod, pointer_size);
|
||||||
memcpy(targetAddr + 1, &toMethod, pointer_size);
|
|
||||||
memcpy(targetAddr + 6, &entrypoint, pointer_size);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
memcpy(targetAddr + 1, &toMethod, pointer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(__x86_64__)
|
#elif defined(__x86_64__)
|
||||||
if(entrypoint) {
|
memcpy((char*)targetAddr + 2, &hookMethod, pointer_size);
|
||||||
memcpy(targetAddr + 2, &entrypoint, pointer_size);
|
|
||||||
memcpy(targetAddr + 13, &toMethod, pointer_size);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
memcpy(targetAddr + 2, &toMethod, pointer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(__arm__)
|
#elif defined(__arm__)
|
||||||
if(entrypoint) {
|
memcpy(targetAddr+8, &hookMethod, pointer_size);
|
||||||
memcpy(targetAddr + 20, &entrypoint, pointer_size);
|
|
||||||
memcpy(targetAddr + 16, &toMethod, pointer_size);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
memcpy(targetAddr + 8, &toMethod, pointer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
if (entrypoint) {
|
memcpy(targetAddr + 12, &hookMethod, pointer_size);
|
||||||
memcpy(targetAddr + 20, &entrypoint, pointer_size);
|
|
||||||
memcpy(targetAddr + 12, &toMethod, pointer_size);
|
|
||||||
} else {
|
|
||||||
memcpy(targetAddr + 12, &toMethod, pointer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error Unsupported architecture
|
#error Unsupported architecture
|
||||||
#endif
|
#endif
|
||||||
FlushCache(targetAddr, size);
|
FlushCache(targetAddr, sizeof(trampoline));
|
||||||
|
|
||||||
return targetAddr;
|
return targetAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupTrampoline(uint8_t offset) {
|
void setupTrampoline() {
|
||||||
#if defined(__i386__)
|
#if defined(__i386__)
|
||||||
trampoline[7] = offset;
|
trampoline[7] = (unsigned char)OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||||
#elif defined(__x86_64__)
|
#elif defined(__x86_64__)
|
||||||
trampoline[12] = offset;
|
trampoline[12] = (unsigned char)OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||||
#elif defined(__arm__)
|
#elif defined(__arm__)
|
||||||
trampoline[4] = offset;
|
trampoline[4] = (unsigned char)OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
trampoline[5] |= offset << 4;
|
trampoline[5] |=
|
||||||
trampoline[6] |= offset >> 4;
|
((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;
|
||||||
#else
|
#else
|
||||||
#error Unsupported architecture
|
#error Unsupported architecture
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void *doInitHookCap(size_t size) {
|
int doInitHookCap(unsigned int cap) {
|
||||||
if (size == 0) {
|
if (cap == 0) {
|
||||||
LOGE("invalid capacity: %zx", size);
|
LOGE("invalid capacity: %d", cap);
|
||||||
return NULL;
|
return 1;
|
||||||
}
|
}
|
||||||
unsigned char *buf = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
if (hookCap) {
|
||||||
|
LOGI("allocating new space for trampoline code");
|
||||||
|
}
|
||||||
|
unsigned int allSize = trampolineSize * cap;
|
||||||
|
unsigned char *buf = mmap(NULL, allSize, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||||
if (buf == MAP_FAILED) {
|
if (buf == MAP_FAILED) {
|
||||||
LOGE("mmap failed, errno = %s", strerror(errno));
|
LOGE("mmap failed, errno = %s", strerror(errno));
|
||||||
return NULL;
|
return 1;
|
||||||
}
|
}
|
||||||
return buf;
|
hookCap = cap;
|
||||||
|
hookCount = 0;
|
||||||
|
trampolineCode = buf;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,17 @@ namespace art {
|
||||||
|
|
||||||
namespace jit {
|
namespace jit {
|
||||||
|
|
||||||
CREATE_MEM_HOOK_STUB_ENTRIES("_ZN3art3jit12JitCodeCache37GetSavedEntryPointOfPreCompiledMethodEPNS_9ArtMethodE",
|
// CREATE_MEM_HOOK_STUB_ENTRIES("_ZN3art3jit12JitCodeCache37GetSavedEntryPointOfPreCompiledMethodEPNS_9ArtMethodE",
|
||||||
const void*, GetSavedEntryPointOfPreCompiledMethod, (void *thiz,
|
// 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 (%s), 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());
|
// art::art_method::PrettyMethod(art_method).c_str());
|
||||||
return getEntryPoint(art_method);
|
// return getEntryPoint(art_method);
|
||||||
}
|
// }
|
||||||
return backup(thiz, art_method);
|
// return backup(thiz, art_method);
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
static void HookJitCacheCode(void *handle, HookFunType hook_func) {
|
static void HookJitCacheCode(void *handle, HookFunType hook_func) {
|
||||||
const int api_level = edxp::GetAndroidApiLevel();
|
const int api_level = edxp::GetAndroidApiLevel();
|
||||||
// For android R, the invisibly initialization makes static methods initializes multiple
|
// For android R, the invisibly initialization makes static methods initializes multiple
|
||||||
|
|
@ -25,7 +25,7 @@ namespace art {
|
||||||
// our hooked entry point won't be overwritten.
|
// our hooked entry point won't be overwritten.
|
||||||
// This is for SandHook and YAHFA
|
// This is for SandHook and YAHFA
|
||||||
if (api_level >= __ANDROID_API_R__) {
|
if (api_level >= __ANDROID_API_R__) {
|
||||||
edxp::HookSyms(handle, hook_func, GetSavedEntryPointOfPreCompiledMethod);
|
// edxp::HookSyms(handle, hook_func, GetSavedEntryPointOfPreCompiledMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ namespace art {
|
||||||
// RETRIEVE_FIELD_SYMBOL(mutator_lock_, "_ZN3art5Locks13mutator_lock_E");
|
// RETRIEVE_FIELD_SYMBOL(mutator_lock_, "_ZN3art5Locks13mutator_lock_E");
|
||||||
// LOGE("mutator_lock_: %p", mutator_lock_);
|
// LOGE("mutator_lock_: %p", mutator_lock_);
|
||||||
|
|
||||||
edxp::HookSyms(handle, hook_func, IsInSamePackage);
|
// edxp::HookSyms(handle, hook_func, IsInSamePackage);
|
||||||
|
|
||||||
// HOOK_FUNC(ClassForName,
|
// HOOK_FUNC(ClassForName,
|
||||||
// "_ZN3artL18Class_classForNameEP7_JNIEnvP7_jclassP8_jstringhP8_jobject");
|
// "_ZN3artL18Class_classForNameEP7_JNIEnvP7_jclassP8_jstringhP8_jobject");
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// Created by loves on 1/28/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef EDXPOSED_REFLECTION_H
|
||||||
|
#define EDXPOSED_REFLECTION_H
|
||||||
|
|
||||||
|
#include "base/object.h"
|
||||||
|
|
||||||
|
namespace art {
|
||||||
|
|
||||||
|
CREATE_HOOK_STUB_ENTRIES(
|
||||||
|
"_ZN3art12VerifyAccessENS_6ObjPtrINS_6mirror6ObjectEEENS0_INS1_5ClassEEEjS5_",
|
||||||
|
bool, VerifyAccess,
|
||||||
|
(void * obj, void * declaring_class, uint32_t access_flags, void * calling_class), {
|
||||||
|
auto calling_desc = art::mirror::Class(calling_class).GetDescriptor();
|
||||||
|
if (UNLIKELY(calling_desc.find("de/robv/android/xposed/LspHooker") !=
|
||||||
|
std::string::npos)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return backup(obj, declaring_class, access_flags, calling_class);
|
||||||
|
});
|
||||||
|
|
||||||
|
static void PermissiveAccessByReflection(void *handle, HookFunType hook_func) {
|
||||||
|
edxp::HookSym(handle, hook_func, VerifyAccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //EDXPOSED_REFLECTION_H
|
||||||
|
|
@ -24,20 +24,6 @@ namespace edxp {
|
||||||
return Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(env, clazz, target, hook, backup);
|
return Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(env, clazz, target, hook, backup);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Yahfa_setMethodNonCompilable(JNI_START, jobject member) {
|
|
||||||
if (!member) {
|
|
||||||
LOGE("setNonCompilableNative: member is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void *art_method = getArtMethod(env, member);
|
|
||||||
|
|
||||||
if (!art_method) {
|
|
||||||
LOGE("setNonCompilableNative: art_method is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setNonCompilable(art_method);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Yahfa_recordHooked(JNI_START, jobject member) {
|
static void Yahfa_recordHooked(JNI_START, jobject member) {
|
||||||
edxp::recordHooked(getArtMethod(env, member));
|
edxp::recordHooked(getArtMethod(env, member));
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +38,6 @@ namespace edxp {
|
||||||
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Member;"),
|
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Member;"),
|
||||||
NATIVE_METHOD(Yahfa, backupAndHookNative,
|
NATIVE_METHOD(Yahfa, backupAndHookNative,
|
||||||
"(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"),
|
"(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"),
|
||||||
//NATIVE_METHOD(Yahfa, setMethodNonCompilable, "(Ljava/lang/reflect/Member;)V"),
|
|
||||||
NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Member;)V"),
|
NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Member;)V"),
|
||||||
NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Member;)Z"),
|
NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Member;)Z"),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include "art/runtime/jit/jit_code_cache.h"
|
#include "art/runtime/jit/jit_code_cache.h"
|
||||||
#include "art/runtime/art_method.h"
|
#include "art/runtime/art_method.h"
|
||||||
#include "art/runtime/instrumentation.h"
|
#include "art/runtime/instrumentation.h"
|
||||||
|
#include "art/runtime/reflection.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
|
||||||
|
|
||||||
|
|
@ -86,6 +87,7 @@ namespace edxp {
|
||||||
// art::oat_file_manager::DisableOnlyUseSystemOatFiles(art_handle, hook_func);
|
// art::oat_file_manager::DisableOnlyUseSystemOatFiles(art_handle, hook_func);
|
||||||
// art::jit::HookJitCacheCode(art_handle, hook_func);
|
// art::jit::HookJitCacheCode(art_handle, hook_func);
|
||||||
art::instrumentation::DisableUpdateHookedMethodsCode(art_handle, hook_func);
|
art::instrumentation::DisableUpdateHookedMethodsCode(art_handle, hook_func);
|
||||||
|
art::PermissiveAccessByReflection(art_handle, hook_func);
|
||||||
|
|
||||||
art_hooks_installed = true;
|
art_hooks_installed = true;
|
||||||
LOGI("ART hooks installed");
|
LOGI("ART hooks installed");
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@ public class Yahfa {
|
||||||
|
|
||||||
public static native void init(int sdkVersion);
|
public static native void init(int sdkVersion);
|
||||||
|
|
||||||
//public static native void setMethodNonCompilable(Member member);
|
|
||||||
|
|
||||||
public static native void recordHooked(Member member);
|
public static native void recordHooked(Member member);
|
||||||
|
|
||||||
public static native boolean isHooked(Member member);
|
public static native boolean isHooked(Member member);
|
||||||
|
|
|
||||||
|
|
@ -10,87 +10,7 @@ import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class HookMain {
|
public class HookMain {
|
||||||
|
|
||||||
private static final Set<String> hookItemWhiteList = new HashSet<String>();
|
|
||||||
|
|
||||||
public static void addHookItemWhiteList(String className) {
|
|
||||||
hookItemWhiteList.add(className);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void doHookDefault(ClassLoader patchClassLoader, ClassLoader originClassLoader, String hookInfoClassName) {
|
|
||||||
try {
|
|
||||||
Class<?> hookInfoClass = Class.forName(hookInfoClassName, true, patchClassLoader);
|
|
||||||
String[] hookItemNames = (String[]) hookInfoClass.getField("hookItemNames").get(null);
|
|
||||||
for (String hookItemName : hookItemNames) {
|
|
||||||
doHookItemDefault(patchClassLoader, hookItemName, originClassLoader);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Utils.logE("error when hooking all in: " + hookInfoClassName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void doHookItemDefault(ClassLoader patchClassLoader, String hookItemName, ClassLoader originClassLoader) {
|
|
||||||
try {
|
|
||||||
Utils.logD("Start hooking with item " + hookItemName);
|
|
||||||
Class<?> hookItem = Class.forName(hookItemName, true, patchClassLoader);
|
|
||||||
|
|
||||||
String className = (String) hookItem.getField("className").get(null);
|
|
||||||
String methodName = (String) hookItem.getField("methodName").get(null);
|
|
||||||
String methodSig = (String) hookItem.getField("methodSig").get(null);
|
|
||||||
|
|
||||||
if (className == null || className.equals("")) {
|
|
||||||
Utils.logW("No target class. Skipping...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Class<?> clazz = null;
|
|
||||||
try {
|
|
||||||
clazz = Class.forName(className, true, originClassLoader);
|
|
||||||
} catch (ClassNotFoundException cnfe) {
|
|
||||||
Utils.logE(className + " not found in " + originClassLoader);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Modifier.isAbstract(clazz.getModifiers())) {
|
|
||||||
Utils.logW("Hook may fail for abstract class: " + className);
|
|
||||||
}
|
|
||||||
|
|
||||||
Method hook = null;
|
|
||||||
Method backup = null;
|
|
||||||
for (Method method : hookItem.getDeclaredMethods()) {
|
|
||||||
if (method.getName().equals("hook") && Modifier.isStatic(method.getModifiers())) {
|
|
||||||
hook = method;
|
|
||||||
} else if (method.getName().equals("backup") && Modifier.isStatic(method.getModifiers())) {
|
|
||||||
backup = method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hook == null) {
|
|
||||||
Utils.logE("Cannot find hook for " + methodName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
findAndBackupAndHook(clazz, methodName, methodSig, hook, backup);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (!hookItemWhiteList.contains(hookItemName)) {
|
|
||||||
Utils.logE("error when hooking " + hookItemName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void findAndHook(Class targetClass, String methodName, String methodSig, Method hook) {
|
|
||||||
hook(findMethod(targetClass, methodName, methodSig), hook);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void findAndBackupAndHook(Class targetClass, String methodName, String methodSig,
|
|
||||||
Method hook, Method backup) {
|
|
||||||
backupAndHook(findMethod(targetClass, methodName, methodSig), hook, backup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void hook(Member target, Method hook) {
|
|
||||||
backupAndHook(target, hook, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void backupAndHook(Member 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));
|
Utils.logD(String.format("target=%s, hook=%s, backup=%s", target, hook, backup));
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
|
|
@ -103,13 +23,13 @@ public class HookMain {
|
||||||
if (!Modifier.isStatic(hook.getModifiers())) {
|
if (!Modifier.isStatic(hook.getModifiers())) {
|
||||||
throw new IllegalArgumentException("Hook must be a static method: " + hook);
|
throw new IllegalArgumentException("Hook must be a static method: " + hook);
|
||||||
}
|
}
|
||||||
checkCompatibleMethods(target, hook, "Original", "Hook");
|
checkCompatibleMethods(target, hook, "Hook");
|
||||||
if (backup != null) {
|
if (backup != null) {
|
||||||
if (!Modifier.isStatic(backup.getModifiers())) {
|
if (!Modifier.isStatic(backup.getModifiers())) {
|
||||||
throw new IllegalArgumentException("Backup must be a static method: " + backup);
|
throw new IllegalArgumentException("Backup must be a static method: " + backup);
|
||||||
}
|
}
|
||||||
// backup is just a placeholder and the constraint could be less strict
|
// backup is just a placeholder and the constraint could be less strict
|
||||||
checkCompatibleMethods(target, backup, "Original", "Backup");
|
checkCompatibleMethods(target, backup, "Backup");
|
||||||
}
|
}
|
||||||
// make sure GC completed before hook
|
// make sure GC completed before hook
|
||||||
int lastGcType = Heap.waitForGcToComplete();
|
int lastGcType = Heap.waitForGcToComplete();
|
||||||
|
|
@ -118,28 +38,15 @@ public class HookMain {
|
||||||
Runtime.getRuntime().gc();
|
Runtime.getRuntime().gc();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Yahfa.backupAndHookNative(target, hook, backup)) {
|
if(!Yahfa.backupAndHookNative(target, hook, backup)){
|
||||||
throw new RuntimeException("Failed to hook " + target + " with " + hook);
|
throw new RuntimeException("Failed to hook " + target + " with " + hook);
|
||||||
} else {
|
} else {
|
||||||
Yahfa.recordHooked(target);
|
Yahfa.recordHooked(target);
|
||||||
Yahfa.recordHooked(backup);
|
// Yahfa.recordHooked(backup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Member findMethod(Class cls, String methodName, String methodSig) {
|
private static void checkCompatibleMethods(Object original, Method replacement, String replacementName) {
|
||||||
if (cls == null) {
|
|
||||||
throw new IllegalArgumentException("null class");
|
|
||||||
}
|
|
||||||
if (methodName == null) {
|
|
||||||
throw new IllegalArgumentException("null method name");
|
|
||||||
}
|
|
||||||
if (methodSig == null) {
|
|
||||||
throw new IllegalArgumentException("null method signature");
|
|
||||||
}
|
|
||||||
return Yahfa.findMethodNative(cls, methodName, methodSig);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkCompatibleMethods(Object original, Method replacement, String originalName, String replacementName) {
|
|
||||||
ArrayList<Class<?>> originalParams;
|
ArrayList<Class<?>> originalParams;
|
||||||
if (original instanceof Method) {
|
if (original instanceof Method) {
|
||||||
originalParams = new ArrayList<>(Arrays.asList(((Method) original).getParameterTypes()));
|
originalParams = new ArrayList<>(Arrays.asList(((Method) original).getParameterTypes()));
|
||||||
|
|
@ -165,7 +72,7 @@ public class HookMain {
|
||||||
|
|
||||||
if (original instanceof Method
|
if (original instanceof Method
|
||||||
&& !replacement.getReturnType().isAssignableFrom(((Method) original).getReturnType())) {
|
&& !replacement.getReturnType().isAssignableFrom(((Method) original).getReturnType())) {
|
||||||
throw new IllegalArgumentException("Incompatible return types. " + originalName + ": " + ((Method) original).getReturnType() + ", " + replacementName + ": " + replacement.getReturnType());
|
throw new IllegalArgumentException("Incompatible return types. " + "Original" + ": " + ((Method) original).getReturnType() + ", " + replacementName + ": " + replacement.getReturnType());
|
||||||
} else if (original instanceof Constructor) {
|
} else if (original instanceof Constructor) {
|
||||||
if (replacement.getReturnType().equals(Void.class)) {
|
if (replacement.getReturnType().equals(Void.class)) {
|
||||||
throw new IllegalArgumentException("Incompatible return types. " + "<init>" + ": " + "V" + ", " + replacementName + ": " + replacement.getReturnType());
|
throw new IllegalArgumentException("Incompatible return types. " + "<init>" + ": " + "V" + ", " + replacementName + ": " + replacement.getReturnType());
|
||||||
|
|
@ -173,12 +80,12 @@ public class HookMain {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (originalParams.size() != replacementParams.size()) {
|
if (originalParams.size() != replacementParams.size()) {
|
||||||
throw new IllegalArgumentException("Number of arguments don't match. " + originalName + ": " + originalParams.size() + ", " + replacementName + ": " + replacementParams.size());
|
throw new IllegalArgumentException("Number of arguments don't match. " + "Original" + ": " + originalParams.size() + ", " + replacementName + ": " + replacementParams.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < originalParams.size(); i++) {
|
for (int i = 0; i < originalParams.size(); i++) {
|
||||||
if (!replacementParams.get(i).isAssignableFrom(originalParams.get(i))) {
|
if (!replacementParams.get(i).isAssignableFrom(originalParams.get(i))) {
|
||||||
throw new IllegalArgumentException("Incompatible argument #" + i + ": " + originalName + ": " + originalParams.get(i) + ", " + replacementName + ": " + replacementParams.get(i));
|
throw new IllegalArgumentException("Incompatible argument #" + i + ": " + "Original" + ": " + originalParams.get(i) + ", " + replacementName + ": " + replacementParams.get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,6 @@ public abstract class BaseRouter implements Router {
|
||||||
|
|
||||||
protected volatile AtomicBoolean bootstrapHooked = new AtomicBoolean(false);
|
protected volatile AtomicBoolean bootstrapHooked = new AtomicBoolean(false);
|
||||||
|
|
||||||
protected static boolean useXposedApi = false;
|
|
||||||
|
|
||||||
public void initResourcesHook() {
|
public void initResourcesHook() {
|
||||||
XposedBridge.initXResources();
|
XposedBridge.initXResources();
|
||||||
}
|
}
|
||||||
|
|
@ -81,49 +79,27 @@ public abstract class BaseRouter implements Router {
|
||||||
public void startBootstrapHook(boolean isSystem) {
|
public void startBootstrapHook(boolean isSystem) {
|
||||||
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
||||||
ClassLoader classLoader = BaseRouter.class.getClassLoader();
|
ClassLoader classLoader = BaseRouter.class.getClassLoader();
|
||||||
if (useXposedApi) {
|
if (isSystem) {
|
||||||
if (isSystem) {
|
XposedHelpers.findAndHookMethod(SystemMainHooker.className, classLoader,
|
||||||
XposedHelpers.findAndHookMethod(SystemMainHooker.className, classLoader,
|
SystemMainHooker.methodName, new SystemMain());
|
||||||
SystemMainHooker.methodName, new SystemMain());
|
|
||||||
}
|
|
||||||
XposedHelpers.findAndHookMethod(HandleBindAppHooker.className, classLoader,
|
|
||||||
HandleBindAppHooker.methodName,
|
|
||||||
"android.app.ActivityThread$AppBindData",
|
|
||||||
new HandleBindApp());
|
|
||||||
XposedHelpers.findAndHookConstructor(LoadedApkConstructorHooker.className, classLoader,
|
|
||||||
ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class,
|
|
||||||
ClassLoader.class, boolean.class, boolean.class, boolean.class,
|
|
||||||
new LoadedApkCstr());
|
|
||||||
} else {
|
|
||||||
if (isSystem) {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
BaseRouter.class.getClassLoader(),
|
|
||||||
classLoader,
|
|
||||||
SysBootstrapHookInfo.class.getName());
|
|
||||||
} else {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
BaseRouter.class.getClassLoader(),
|
|
||||||
classLoader,
|
|
||||||
AppBootstrapHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
XposedHelpers.findAndHookMethod(HandleBindAppHooker.className, classLoader,
|
||||||
|
HandleBindAppHooker.methodName,
|
||||||
|
"android.app.ActivityThread$AppBindData",
|
||||||
|
new HandleBindApp());
|
||||||
|
XposedHelpers.findAndHookConstructor(LoadedApkConstructorHooker.className, classLoader,
|
||||||
|
ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class,
|
||||||
|
ClassLoader.class, boolean.class, boolean.class, boolean.class,
|
||||||
|
new LoadedApkCstr());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startSystemServerHook() {
|
public void startSystemServerHook() {
|
||||||
ClassLoader classLoader = BaseRouter.class.getClassLoader();
|
StartBootstrapServices sbsHooker = new StartBootstrapServices();
|
||||||
if (useXposedApi) {
|
Object[] paramTypesAndCallback = Versions.hasR() ?
|
||||||
StartBootstrapServices sbsHooker = new StartBootstrapServices();
|
new Object[]{"com.android.server.utils.TimingsTraceAndSlog", sbsHooker} :
|
||||||
Object[] paramTypesAndCallback = Versions.hasR() ?
|
new Object[]{sbsHooker};
|
||||||
new Object[]{"com.android.server.utils.TimingsTraceAndSlog", sbsHooker} :
|
XposedHelpers.findAndHookMethod(StartBootstrapServicesHooker.className,
|
||||||
new Object[]{sbsHooker};
|
SystemMain.systemServerCL,
|
||||||
XposedHelpers.findAndHookMethod(StartBootstrapServicesHooker.className,
|
StartBootstrapServicesHooker.methodName, paramTypesAndCallback);
|
||||||
SystemMain.systemServerCL,
|
|
||||||
StartBootstrapServicesHooker.methodName, paramTypesAndCallback);
|
|
||||||
} else {
|
|
||||||
HookMain.doHookDefault(
|
|
||||||
classLoader,
|
|
||||||
SystemMain.systemServerCL,
|
|
||||||
SysInnerHookInfo.class.getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import de.robv.android.xposed.XposedBridge;
|
||||||
public class SandHookRouter extends BaseRouter {
|
public class SandHookRouter extends BaseRouter {
|
||||||
|
|
||||||
public SandHookRouter() {
|
public SandHookRouter() {
|
||||||
useXposedApi = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean useSandHook = false;
|
private static boolean useSandHook = false;
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ import com.elderdrivers.riru.edxp.yahfa.dexmaker.DynamicBridge;
|
||||||
public class YahfaRouter extends BaseRouter {
|
public class YahfaRouter extends BaseRouter {
|
||||||
|
|
||||||
YahfaRouter() {
|
YahfaRouter() {
|
||||||
// TODO: disable for better performance
|
|
||||||
useXposedApi = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onEnterChildProcess() {
|
public void onEnterChildProcess() {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
package com.elderdrivers.riru.edxp.yahfa.dexmaker;
|
package com.elderdrivers.riru.edxp.yahfa.dexmaker;
|
||||||
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import de.robv.android.xposed.LspHooker;
|
import de.robv.android.xposed.LspHooker;
|
||||||
|
|
@ -17,7 +14,7 @@ import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
public final class DynamicBridge {
|
public final class DynamicBridge {
|
||||||
|
|
||||||
private static final HashMap<Member, LspHooker> hookedInfo = new HashMap<>();
|
private static final ConcurrentHashMap<Member, LspHooker> hookedInfo = new ConcurrentHashMap<>();
|
||||||
private static final HookerDexMaker dexMaker = new HookerDexMaker();
|
private static final HookerDexMaker dexMaker = new HookerDexMaker();
|
||||||
private static final AtomicBoolean dexPathInited = new AtomicBoolean(false);
|
private static final AtomicBoolean dexPathInited = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
|
@ -74,7 +71,10 @@ public final class DynamicBridge {
|
||||||
if (hooker == null) {
|
if (hooker == null) {
|
||||||
throw new IllegalStateException("method not hooked, cannot call original method.");
|
throw new IllegalStateException("method not hooked, cannot call original method.");
|
||||||
}
|
}
|
||||||
return hooker.callBackup(thisObject, args);
|
if (args == null) {
|
||||||
|
args = new Object[0];
|
||||||
|
}
|
||||||
|
return hooker.getBackup().invoke(thisObject, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,39 +18,12 @@ public class LspHooker {
|
||||||
this.backup = backup;
|
this.backup = backup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object callBackup(Object thisObject, Object[] args) throws InvocationTargetException, IllegalAccessException {
|
public Method getBackup() {
|
||||||
if (args == null) {
|
return backup;
|
||||||
args = new Object[0];
|
|
||||||
}
|
|
||||||
if (Modifier.isStatic(method.getModifiers())) {
|
|
||||||
return backup.invoke(null, args);
|
|
||||||
} else {
|
|
||||||
Object[] newArgs = new Object[args.length + 1];
|
|
||||||
newArgs[0] = thisObject;
|
|
||||||
System.arraycopy(args, 0, newArgs, 1, args.length);
|
|
||||||
return backup.invoke(null, newArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "RedundantSuppression"})
|
@SuppressWarnings({"unused", "RedundantSuppression"})
|
||||||
public Object handleHookedMethod(Object[] args) throws Throwable {
|
public Object handleHookedMethod(Object[] args) throws Throwable {
|
||||||
if (disableHooks) {
|
|
||||||
try {
|
|
||||||
return backup.invoke(null, args);
|
|
||||||
} catch (InvocationTargetException ite) {
|
|
||||||
throw ite.getCause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();
|
|
||||||
final int callbacksLength = callbacksSnapshot.length;
|
|
||||||
if (callbacksLength == 0) {
|
|
||||||
try {
|
|
||||||
return backup.invoke(null, args);
|
|
||||||
} catch (InvocationTargetException ite) {
|
|
||||||
throw ite.getCause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||||
|
|
||||||
param.method = method;
|
param.method = method;
|
||||||
|
|
@ -64,6 +37,23 @@ public class LspHooker {
|
||||||
System.arraycopy(args, 1, param.args, 0, args.length - 1);
|
System.arraycopy(args, 1, param.args, 0, args.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (disableHooks) {
|
||||||
|
try {
|
||||||
|
return backup.invoke(param.thisObject, param.args);
|
||||||
|
} catch (InvocationTargetException ite) {
|
||||||
|
throw ite.getCause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();
|
||||||
|
final int callbacksLength = callbacksSnapshot.length;
|
||||||
|
if (callbacksLength == 0) {
|
||||||
|
try {
|
||||||
|
return backup.invoke(param.thisObject, param.args);
|
||||||
|
} catch (InvocationTargetException ite) {
|
||||||
|
throw ite.getCause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// call "before method" callbacks
|
// call "before method" callbacks
|
||||||
int beforeIdx = 0;
|
int beforeIdx = 0;
|
||||||
do {
|
do {
|
||||||
|
|
@ -88,7 +78,7 @@ public class LspHooker {
|
||||||
// call original method if not requested otherwise
|
// call original method if not requested otherwise
|
||||||
if (!param.returnEarly) {
|
if (!param.returnEarly) {
|
||||||
try {
|
try {
|
||||||
param.setResult(callBackup(param.thisObject, param.args));
|
param.setResult(backup.invoke(param.thisObject, param.args));
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
param.setThrowable(e.getCause());
|
param.setThrowable(e.getCause());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue