[core] Refactor Yahfa
This commit is contained in:
parent
26a4decfeb
commit
a6b4ed548e
|
|
@ -3,26 +3,18 @@
|
|||
|
||||
#include <jni.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
namespace yahfa {
|
||||
void init(JNIEnv *env, jclass clazz, jint sdkVersion);
|
||||
|
||||
void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion);
|
||||
jobject findMethodNative(JNIEnv *env, jclass clazz,
|
||||
jclass targetClass, jstring methodName,
|
||||
jstring methodSig);
|
||||
|
||||
jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env, jclass clazz,
|
||||
jclass targetClass, jstring methodName,
|
||||
jstring methodSig);
|
||||
jboolean backupAndHookNative(JNIEnv *env, jclass clazz,
|
||||
jobject target, jobject hook,
|
||||
jobject backup);
|
||||
|
||||
jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass clazz,
|
||||
jobject target, jobject hook,
|
||||
jobject backup);
|
||||
|
||||
void setNonCompilable(void *method);
|
||||
|
||||
void *getArtMethodYahfa(JNIEnv *env, jobject jmethod);
|
||||
|
||||
#ifdef __cplusplus
|
||||
void *getArtMethod(JNIEnv *env, jobject jmethod);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HOOK_MAIN_H
|
||||
|
|
|
|||
|
|
@ -8,255 +8,201 @@
|
|||
#include "HookMain.h"
|
||||
|
||||
int SDKVersion;
|
||||
static size_t OFFSET_entry_point_from_interpreter_in_ArtMethod;
|
||||
size_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||
static size_t OFFSET_ArtMehod_in_Object;
|
||||
static size_t 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 uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;
|
||||
namespace {
|
||||
size_t ArtMethodSize;
|
||||
constexpr size_t OFFSET_access_flags_in_ArtMethod = 4;
|
||||
constexpr uint32_t kAccCompileDontBother = 0x02000000;
|
||||
constexpr uint32_t kAccPreCompiled = 0x00100000 | 0x00200000;
|
||||
constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic
|
||||
constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic
|
||||
constexpr uint32_t kAccProtected = 0x0004; // field, method, ic
|
||||
constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
|
||||
constexpr uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;
|
||||
|
||||
jfieldID fieldArtMethod = nullptr;
|
||||
|
||||
static jfieldID fieldArtMethod = nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_S__:
|
||||
classExecutable = env->FindClass("java/lang/reflect/Executable");
|
||||
fieldArtMethod = env->GetFieldID(classExecutable, "artMethod", "J");
|
||||
kAccCompileDontBother = 0x02000000;
|
||||
OFFSET_ArtMehod_in_Object = 0;
|
||||
OFFSET_access_flags_in_ArtMethod = 4;
|
||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
|
||||
roundUpToPtrSize(4 * 3 + 2 * 2) + pointer_size;
|
||||
ArtMethodSize = roundUpToPtrSize(4 * 3 + 2 * 2) + pointer_size * 2;
|
||||
break;
|
||||
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;
|
||||
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;
|
||||
constexpr inline uint32_t read32(void *addr) {
|
||||
return *((uint32_t *) addr);
|
||||
}
|
||||
|
||||
setupTrampoline();
|
||||
}
|
||||
|
||||
void setNonCompilable(void *method) {
|
||||
if (SDKVersion < __ANDROID_API_N__) {
|
||||
return;
|
||||
constexpr inline void write32(void *addr, uint32_t value) {
|
||||
*((uint32_t *) addr) = value;
|
||||
}
|
||||
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;
|
||||
constexpr inline void *readAddr(void *addr) {
|
||||
return *((void **) addr);
|
||||
}
|
||||
|
||||
constexpr inline void writeAddr(void *addr, void *value) {
|
||||
*((void **) addr) = value;
|
||||
}
|
||||
|
||||
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;
|
||||
access_flags &= ~kAccPreCompiled;
|
||||
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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int doBackupAndHook(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;
|
||||
}
|
||||
LOGI("Allocating done");
|
||||
|
||||
if (SDKVersion >= __ANDROID_API_Q__) {
|
||||
uint32_t access_flags = read32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod);
|
||||
// On API 29 whether to use the fast path or not is cached in the ART method structure
|
||||
access_flags &= ~kAccFastInterpreterToInterpreterInvoke;
|
||||
write32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||
}
|
||||
|
||||
LOGI("hook and backup done");
|
||||
hookCount += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
}
|
||||
|
||||
if (SDKVersion >= __ANDROID_API_Q__) {
|
||||
uint32_t access_flags = read32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod);
|
||||
// On API 29 whether to use the fast path or not is cached in the ART method structure
|
||||
access_flags &= ~kAccFastInterpreterToInterpreterInvoke;
|
||||
write32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||
}
|
||||
|
||||
LOGI("hook and backup done");
|
||||
hookCount += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *getArtMethodYahfa(JNIEnv *env, jobject jmethod) {
|
||||
void *artMethod = nullptr;
|
||||
namespace yahfa {
|
||||
|
||||
if (jmethod == nullptr) {
|
||||
return artMethod;
|
||||
void init(JNIEnv *env, [[maybe_unused]] jclass clazz, jint sdkVersion) {
|
||||
SDKVersion = sdkVersion;
|
||||
jclass classExecutable = env->FindClass("java/lang/reflect/Executable");
|
||||
fieldArtMethod = env->GetFieldID(classExecutable, "artMethod", "J");
|
||||
LOGI("init to SDK %d", sdkVersion);
|
||||
switch (sdkVersion) {
|
||||
case __ANDROID_API_S__:
|
||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
|
||||
roundUpToPtrSize(4 * 3 + 2 * 2) + pointer_size;
|
||||
ArtMethodSize = roundUpToPtrSize(4 * 3 + 2 * 2) + pointer_size * 2;
|
||||
break;
|
||||
case __ANDROID_API_R__:
|
||||
case __ANDROID_API_Q__:
|
||||
case __ANDROID_API_P__:
|
||||
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__:
|
||||
case __ANDROID_API_O__:
|
||||
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;
|
||||
default:
|
||||
LOGE("not compatible with SDK %d", sdkVersion);
|
||||
break;
|
||||
}
|
||||
|
||||
setupTrampoline();
|
||||
}
|
||||
|
||||
if (SDKVersion >= __ANDROID_API_R__) {
|
||||
artMethod = (void *) env->GetLongField(jmethod, fieldArtMethod);
|
||||
} else {
|
||||
artMethod = (void *) env->FromReflectedMethod(jmethod);
|
||||
}
|
||||
|
||||
LOGI("ArtMethod: %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);
|
||||
void *getArtMethod(JNIEnv *env, jobject jmethod) {
|
||||
if (jmethod == nullptr) {
|
||||
return nullptr;
|
||||
} else {
|
||||
env->ExceptionClear();
|
||||
return (void *) env->GetLongField(jmethod, fieldArtMethod);
|
||||
}
|
||||
}
|
||||
|
||||
env->ReleaseStringUTFChars(methodName, c_methodName);
|
||||
env->ReleaseStringUTFChars(methodSig, c_methodSig);
|
||||
return ret;
|
||||
}
|
||||
jobject findMethodNative(JNIEnv *env, [[maybe_unused]] 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;
|
||||
|
||||
extern "C" jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass clazz,
|
||||
jobject target, jobject hook,
|
||||
jobject backup) {
|
||||
|
||||
if (!doBackupAndHook(env,
|
||||
getArtMethodYahfa(env, target),
|
||||
getArtMethodYahfa(env, hook),
|
||||
getArtMethodYahfa(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;
|
||||
//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;
|
||||
}
|
||||
|
||||
jboolean backupAndHookNative(JNIEnv *env, [[maybe_unused]] 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace lspd {
|
|||
static std::unordered_set<void *> deopted_methods;
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(void, ClassLinker, setEntryPointsToInterpreter, jobject method) {
|
||||
void *reflected_method = getArtMethodYahfa(env, method);
|
||||
void *reflected_method = yahfa::getArtMethod(env, method);
|
||||
if (deopted_methods.contains(reflected_method)) {
|
||||
LOGD("method %p has been deopted before, skip...", reflected_method);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ namespace lspd {
|
|||
|
||||
LSP_DEF_NATIVE_METHOD(void, PendingHooks, recordPendingMethodNative, jobject method_ref, jclass class_ref){
|
||||
auto *class_ptr = art::Thread::Current().DecodeJObject(class_ref);
|
||||
auto *method = getArtMethodYahfa(env, method_ref);
|
||||
auto *method = yahfa::getArtMethod(env, method_ref);
|
||||
art::mirror::Class mirror_class(class_ptr);
|
||||
if (auto def = mirror_class.GetClassDef(); LIKELY(def)) {
|
||||
LOGD("record pending: %p (%s) with %p", class_ptr, mirror_class.GetDescriptor().c_str(),
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@
|
|||
namespace lspd {
|
||||
using namespace startop::dex;
|
||||
LSP_DEF_NATIVE_METHOD(void, Yahfa, init, jint sdkVersion) {
|
||||
Java_lab_galaxy_yahfa_HookMain_init(env, clazz, sdkVersion);
|
||||
yahfa::init(env, clazz, sdkVersion);
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jobject, Yahfa, findMethodNative, jclass targetClass,
|
||||
jstring methodName, jstring methodSig) {
|
||||
return Java_lab_galaxy_yahfa_HookMain_findMethodNative(env, clazz, targetClass, methodName,
|
||||
methodSig);
|
||||
return yahfa::findMethodNative(env, clazz, targetClass, methodName,
|
||||
methodSig);
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, backupAndHookNative, jobject target,
|
||||
|
|
@ -47,21 +47,23 @@ namespace lspd {
|
|||
art::gc::kGcCauseDebugger,
|
||||
art::gc::kCollectorTypeDebugger);
|
||||
art::thread_list::ScopedSuspendAll suspend("Yahfa Hook", false);
|
||||
return Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(env, clazz, target, hook, backup);
|
||||
return yahfa::backupAndHookNative(env, clazz, target, hook, backup);
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(void, Yahfa, recordHooked, jobject member) {
|
||||
lspd::recordHooked(getArtMethodYahfa(env, member));
|
||||
lspd::recordHooked(yahfa::getArtMethod(env, member));
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, isHooked, jobject member) {
|
||||
return lspd::isHooked(getArtMethodYahfa(env, member));
|
||||
return lspd::isHooked(yahfa::getArtMethod(env, member));
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jclass return_class, jobjectArray classes, jstring method_name) {
|
||||
static auto in_memory_classloader = (jclass)env->NewGlobalRef(env->FindClass( "dalvik/system/InMemoryDexClassLoader"));
|
||||
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jclass return_class,
|
||||
jobjectArray classes, jstring method_name) {
|
||||
static auto in_memory_classloader = (jclass) env->NewGlobalRef(
|
||||
env->FindClass("dalvik/system/InMemoryDexClassLoader"));
|
||||
static jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
||||
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||
DexBuilder dex_file;
|
||||
|
||||
auto parameter_length = env->GetArrayLength(classes);
|
||||
|
|
@ -69,7 +71,9 @@ namespace lspd {
|
|||
parameter_types.reserve(parameter_length);
|
||||
std::string storage;
|
||||
auto current_thread = art::Thread::Current();
|
||||
auto return_type = TypeDescriptor::FromDescriptor(art::mirror::Class(current_thread.DecodeJObject(return_class)).GetDescriptor(&storage));
|
||||
auto return_type = TypeDescriptor::FromDescriptor(
|
||||
art::mirror::Class(current_thread.DecodeJObject(return_class)).GetDescriptor(
|
||||
&storage));
|
||||
for (int i = 0; i < parameter_length; ++i) {
|
||||
auto param = (jclass) env->GetObjectArrayElement(classes, i);
|
||||
auto *param_ref = current_thread.DecodeJObject(param);
|
||||
|
|
@ -153,14 +157,15 @@ namespace lspd {
|
|||
|
||||
slicer::MemView image{dex_file.CreateImage()};
|
||||
|
||||
auto dex_buffer = env->NewDirectByteBuffer(const_cast<void*>(image.ptr()), image.size());
|
||||
auto dex_buffer = env->NewDirectByteBuffer(const_cast<void *>(image.ptr()), image.size());
|
||||
jobject my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
|
||||
dex_buffer, app_class_loader);
|
||||
|
||||
static jmethodID mid = JNI_GetMethodID(env, in_memory_classloader, "loadClass",
|
||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
if (!mid) {
|
||||
mid = JNI_GetMethodID(env, in_memory_classloader, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
mid = JNI_GetMethodID(env, in_memory_classloader, "findClass",
|
||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
}
|
||||
jobject target = env->CallObjectMethod(my_cl, mid, env->NewStringUTF("LspHooker_"));
|
||||
// LOGD("Created %zd", image.size());
|
||||
|
|
@ -178,7 +183,8 @@ namespace lspd {
|
|||
"(Ljava/lang/reflect/Executable;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"),
|
||||
LSP_NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Executable;)V"),
|
||||
LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Executable;)Z"),
|
||||
LSP_NATIVE_METHOD(Yahfa, buildHooker, "(Ljava/lang/ClassLoader;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Class;"),
|
||||
LSP_NATIVE_METHOD(Yahfa, buildHooker,
|
||||
"(Ljava/lang/ClassLoader;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Class;"),
|
||||
};
|
||||
|
||||
void RegisterYahfa(JNIEnv *env) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue