[core] Refactor Yahfa

This commit is contained in:
LoveSy 2021-03-20 15:10:55 +08:00 committed by LoveSy
parent 26a4decfeb
commit a6b4ed548e
5 changed files with 201 additions and 257 deletions

View File

@ -3,26 +3,18 @@
#include <jni.h> #include <jni.h>
#ifdef __cplusplus namespace yahfa {
extern "C" { void init(JNIEnv *env, jclass clazz, jint sdkVersion);
#endif
void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion); jobject findMethodNative(JNIEnv *env, jclass clazz,
jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env, jclass clazz,
jclass targetClass, jstring methodName, jclass targetClass, jstring methodName,
jstring methodSig); jstring methodSig);
jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass clazz, jboolean backupAndHookNative(JNIEnv *env, jclass clazz,
jobject target, jobject hook, jobject target, jobject hook,
jobject backup); jobject backup);
void setNonCompilable(void *method); void *getArtMethod(JNIEnv *env, jobject jmethod);
void *getArtMethodYahfa(JNIEnv *env, jobject jmethod);
#ifdef __cplusplus
} }
#endif
#endif // HOOK_MAIN_H #endif // HOOK_MAIN_H

View File

@ -8,124 +8,48 @@
#include "HookMain.h" #include "HookMain.h"
int SDKVersion; int SDKVersion;
static size_t OFFSET_entry_point_from_interpreter_in_ArtMethod;
size_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod; size_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
static size_t OFFSET_ArtMehod_in_Object; namespace {
static size_t OFFSET_access_flags_in_ArtMethod; size_t ArtMethodSize;
static size_t ArtMethodSize; constexpr size_t OFFSET_access_flags_in_ArtMethod = 4;
static uint32_t kAccCompileDontBother = 0x01000000; constexpr uint32_t kAccCompileDontBother = 0x02000000;
static uint32_t kAccPublic = 0x0001; // class, field, method, ic constexpr uint32_t kAccPreCompiled = 0x00100000 | 0x00200000;
static uint32_t kAccPrivate = 0x0002; // field, method, ic constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic
static uint32_t kAccProtected = 0x0004; // field, method, ic constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic
static uint32_t kAccStatic = 0x0008; // field, method, ic constexpr uint32_t kAccProtected = 0x0004; // field, method, ic
static uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000; constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
constexpr uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;
jfieldID fieldArtMethod = nullptr;
static jfieldID fieldArtMethod = nullptr; constexpr inline uint32_t read32(void *addr) {
static inline uint32_t read32(void *addr) {
return *((uint32_t *) 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;
} }
setupTrampoline(); constexpr inline void write32(void *addr, uint32_t value) {
} *((uint32_t *) addr) = value;
}
void setNonCompilable(void *method) { 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__) { if (SDKVersion < __ANDROID_API_N__) {
return; return;
} }
uint32_t access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod); uint32_t access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
LOGI("setNonCompilable: access flags is 0x%x", access_flags); LOGI("setNonCompilable: access flags is 0x%x", access_flags);
access_flags |= kAccCompileDontBother; access_flags |= kAccCompileDontBother;
access_flags &= ~kAccPreCompiled;
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags); write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
} }
void setPrivate(void *method) { void setPrivate(void *method) {
uint32_t access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod); uint32_t access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
if (!(access_flags & kAccStatic)) { if (!(access_flags & kAccStatic)) {
LOGI("setPrivate: access flags is 0x%x", access_flags); LOGI("setPrivate: access flags is 0x%x", access_flags);
@ -134,9 +58,9 @@ void setPrivate(void *method) {
access_flags &= ~kAccPublic; access_flags &= ~kAccPublic;
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags); write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
} }
} }
static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, void *backupMethod) { int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMethod) {
if (hookCount >= hookCap) { if (hookCount >= hookCap) {
LOGI("not enough capacity. Allocating..."); LOGI("not enough capacity. Allocating...");
if (doInitHookCap(DEFAULT_CAP)) { if (doInitHookCap(DEFAULT_CAP)) {
@ -182,12 +106,6 @@ static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, vo
return 1; 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__) { if (SDKVersion >= __ANDROID_API_Q__) {
uint32_t access_flags = read32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod); 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 // On API 29 whether to use the fast path or not is cached in the ART method structure
@ -198,27 +116,55 @@ static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, vo
LOGI("hook and backup done"); LOGI("hook and backup done");
hookCount += 1; hookCount += 1;
return 0; return 0;
}
} }
void *getArtMethodYahfa(JNIEnv *env, jobject jmethod) { namespace yahfa {
void *artMethod = nullptr;
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();
}
void *getArtMethod(JNIEnv *env, jobject jmethod) {
if (jmethod == nullptr) { if (jmethod == nullptr) {
return artMethod; return nullptr;
}
if (SDKVersion >= __ANDROID_API_R__) {
artMethod = (void *) env->GetLongField(jmethod, fieldArtMethod);
} else { } else {
artMethod = (void *) env->FromReflectedMethod(jmethod); return (void *) env->GetLongField(jmethod, fieldArtMethod);
}
} }
LOGI("ArtMethod: %p", artMethod); jobject findMethodNative(JNIEnv *env, [[maybe_unused]] jclass clazz,
return artMethod; jclass targetClass,
} jstring methodName,
extern "C" jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env, jclass clazz,
jclass targetClass, jstring methodName,
jstring methodSig) { jstring methodSig) {
const char *c_methodName = env->GetStringUTFChars(methodName, nullptr); const char *c_methodName = env->GetStringUTFChars(methodName, nullptr);
const char *c_methodSig = env->GetStringUTFChars(methodSig, nullptr); const char *c_methodSig = env->GetStringUTFChars(methodSig, nullptr);
@ -242,16 +188,15 @@ extern "C" jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env,
env->ReleaseStringUTFChars(methodName, c_methodName); env->ReleaseStringUTFChars(methodName, c_methodName);
env->ReleaseStringUTFChars(methodSig, c_methodSig); env->ReleaseStringUTFChars(methodSig, c_methodSig);
return ret; return ret;
} }
extern "C" jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass clazz, jboolean backupAndHookNative(JNIEnv *env, [[maybe_unused]] jclass clazz,
jobject target, jobject hook, jobject target, jobject hook,
jobject backup) { jobject backup) {
if (!doBackupAndHook(env, if (!doBackupAndHook(getArtMethod(env, target),
getArtMethodYahfa(env, target), getArtMethod(env, hook),
getArtMethodYahfa(env, hook), getArtMethod(env, backup)
getArtMethodYahfa(env, backup)
)) { )) {
env->NewGlobalRef(hook); // keep a global ref so that the hook method would not be GCed env->NewGlobalRef(hook); // keep a global ref so that the hook method would not be GCed
if (backup) env->NewGlobalRef(backup); if (backup) env->NewGlobalRef(backup);
@ -259,4 +204,5 @@ extern "C" jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *e
} else { } else {
return JNI_FALSE; return JNI_FALSE;
} }
}
} }

View File

@ -31,7 +31,7 @@ namespace lspd {
static std::unordered_set<void *> deopted_methods; static std::unordered_set<void *> deopted_methods;
LSP_DEF_NATIVE_METHOD(void, ClassLinker, setEntryPointsToInterpreter, jobject method) { 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)) { if (deopted_methods.contains(reflected_method)) {
LOGD("method %p has been deopted before, skip...", reflected_method); LOGD("method %p has been deopted before, skip...", reflected_method);
return; return;

View File

@ -65,7 +65,7 @@ namespace lspd {
LSP_DEF_NATIVE_METHOD(void, PendingHooks, recordPendingMethodNative, jobject method_ref, jclass class_ref){ LSP_DEF_NATIVE_METHOD(void, PendingHooks, recordPendingMethodNative, jobject method_ref, jclass class_ref){
auto *class_ptr = art::Thread::Current().DecodeJObject(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); art::mirror::Class mirror_class(class_ptr);
if (auto def = mirror_class.GetClassDef(); LIKELY(def)) { if (auto def = mirror_class.GetClassDef(); LIKELY(def)) {
LOGD("record pending: %p (%s) with %p", class_ptr, mirror_class.GetDescriptor().c_str(), LOGD("record pending: %p (%s) with %p", class_ptr, mirror_class.GetDescriptor().c_str(),

View File

@ -32,12 +32,12 @@
namespace lspd { namespace lspd {
using namespace startop::dex; using namespace startop::dex;
LSP_DEF_NATIVE_METHOD(void, Yahfa, init, jint sdkVersion) { 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, LSP_DEF_NATIVE_METHOD(jobject, Yahfa, findMethodNative, jclass targetClass,
jstring methodName, jstring methodSig) { jstring methodName, jstring methodSig) {
return Java_lab_galaxy_yahfa_HookMain_findMethodNative(env, clazz, targetClass, methodName, return yahfa::findMethodNative(env, clazz, targetClass, methodName,
methodSig); methodSig);
} }
@ -47,19 +47,21 @@ namespace lspd {
art::gc::kGcCauseDebugger, art::gc::kGcCauseDebugger,
art::gc::kCollectorTypeDebugger); art::gc::kCollectorTypeDebugger);
art::thread_list::ScopedSuspendAll suspend("Yahfa Hook", false); 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) { 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) { 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) { LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jclass return_class,
static auto in_memory_classloader = (jclass)env->NewGlobalRef(env->FindClass( "dalvik/system/InMemoryDexClassLoader")); 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>", 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; DexBuilder dex_file;
@ -69,7 +71,9 @@ namespace lspd {
parameter_types.reserve(parameter_length); parameter_types.reserve(parameter_length);
std::string storage; std::string storage;
auto current_thread = art::Thread::Current(); 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) { for (int i = 0; i < parameter_length; ++i) {
auto param = (jclass) env->GetObjectArrayElement(classes, i); auto param = (jclass) env->GetObjectArrayElement(classes, i);
auto *param_ref = current_thread.DecodeJObject(param); auto *param_ref = current_thread.DecodeJObject(param);
@ -153,14 +157,15 @@ namespace lspd {
slicer::MemView image{dex_file.CreateImage()}; 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, jobject my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
dex_buffer, app_class_loader); dex_buffer, app_class_loader);
static jmethodID mid = JNI_GetMethodID(env, in_memory_classloader, "loadClass", static jmethodID mid = JNI_GetMethodID(env, in_memory_classloader, "loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;"); "(Ljava/lang/String;)Ljava/lang/Class;");
if (!mid) { 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_")); jobject target = env->CallObjectMethod(my_cl, mid, env->NewStringUTF("LspHooker_"));
// LOGD("Created %zd", image.size()); // LOGD("Created %zd", image.size());
@ -178,7 +183,8 @@ namespace lspd {
"(Ljava/lang/reflect/Executable;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"), "(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, recordHooked, "(Ljava/lang/reflect/Executable;)V"),
LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Executable;)Z"), 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) { void RegisterYahfa(JNIEnv *env) {