[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 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

View File

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

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,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) {