[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>
#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 Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env, jclass clazz,
jobject findMethodNative(JNIEnv *env, jclass clazz,
jclass targetClass, jstring methodName,
jstring methodSig);
jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass clazz,
jboolean 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

View File

@ -8,113 +8,36 @@
#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) {
constexpr inline uint32_t read32(void *addr) {
return *((uint32_t *) addr);
}
static inline void write32(void *addr, uint32_t value) {
constexpr inline void write32(void *addr, uint32_t value) {
*((uint32_t *) addr) = value;
}
static inline void *readAddr(void *addr) {
constexpr inline void *readAddr(void *addr) {
return *((void **) addr);
}
static inline void writeAddr(void *addr, void *value) {
constexpr 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();
}
void setNonCompilable(void *method) {
if (SDKVersion < __ANDROID_API_N__) {
return;
@ -122,6 +45,7 @@ void setNonCompilable(void *method) {
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);
}
@ -136,7 +60,7 @@ void setPrivate(void *method) {
}
}
static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, void *backupMethod) {
int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMethod) {
if (hookCount >= hookCap) {
LOGI("not enough capacity. Allocating...");
if (doInitHookCap(DEFAULT_CAP)) {
@ -182,12 +106,6 @@ static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, vo
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
@ -200,25 +118,53 @@ static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, vo
return 0;
}
void *getArtMethodYahfa(JNIEnv *env, jobject jmethod) {
void *artMethod = nullptr;
}
namespace yahfa {
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) {
return artMethod;
}
if (SDKVersion >= __ANDROID_API_R__) {
artMethod = (void *) env->GetLongField(jmethod, fieldArtMethod);
return nullptr;
} else {
artMethod = (void *) env->FromReflectedMethod(jmethod);
return (void *) env->GetLongField(jmethod, fieldArtMethod);
}
}
LOGI("ArtMethod: %p", artMethod);
return artMethod;
}
extern "C" jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env, jclass clazz,
jclass targetClass, jstring methodName,
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);
@ -244,14 +190,13 @@ extern "C" jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env,
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 backup) {
if (!doBackupAndHook(env,
getArtMethodYahfa(env, target),
getArtMethodYahfa(env, hook),
getArtMethodYahfa(env, 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);
@ -260,3 +205,4 @@ extern "C" jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *e
return JNI_FALSE;
}
}
}

View File

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

View File

@ -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(),

View File

@ -32,12 +32,12 @@
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,
return yahfa::findMethodNative(env, clazz, targetClass, methodName,
methodSig);
}
@ -47,19 +47,21 @@ 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");
DexBuilder dex_file;
@ -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);
@ -160,7 +164,8 @@ namespace lspd {
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) {