// // Created by Milk on 3/8/21. // #include #include "utils/Log.h" #include "Art.h" #include "ArtM.h" #include "JniHook.h" static struct { int api_level; jclass method_utils_class; jmethodID get_method_desc_id; jmethodID get_method_declaring_class_id; jmethodID get_method_name_id; } HookEnv; static const char *GetMethodDesc(JNIEnv *env, jobject javaMethod) { auto desc = reinterpret_cast(env->CallStaticObjectMethod(HookEnv.method_utils_class, HookEnv.get_method_desc_id, javaMethod)); return env->GetStringUTFChars(desc, JNI_FALSE); } static const char *GetMethodDeclaringClass(JNIEnv *env, jobject javaMethod) { auto desc = reinterpret_cast(env->CallStaticObjectMethod(HookEnv.method_utils_class, HookEnv.get_method_declaring_class_id, javaMethod)); return env->GetStringUTFChars(desc, JNI_FALSE); } static const char *GetMethodName(JNIEnv *env, jobject javaMethod) { auto desc = reinterpret_cast(env->CallStaticObjectMethod(HookEnv.method_utils_class, HookEnv.get_method_name_id, javaMethod)); return env->GetStringUTFChars(desc, JNI_FALSE); } bool CheckFlags(void *artMethod) { char *method = static_cast(artMethod); if (!ArtM::HasAccessFlag(method, kAccNative)) { ALOGE("not native method"); return false; } ArtM::ClearFastNativeFlag(method); return true; } void JniHook::HookJniFun(JNIEnv *env, jobject java_method, void *new_fun, void **orig_fun, bool is_static) { const char *class_name = GetMethodDeclaringClass(env, java_method); const char *method_name = GetMethodName(env, java_method); const char *sign = GetMethodDesc(env, java_method); HookJniFun(env, class_name, method_name, sign, new_fun, orig_fun, is_static); } void JniHook::HookJniFun(JNIEnv *env, const char *class_name, const char *method_name, const char *sign, void *new_fun, void **orig_fun, bool is_static) { if (ArtM::GetArtMethodNativeOffset() == 0) { return; } jclass clazz = env->FindClass(class_name); if (!clazz) { ALOGD("findClass fail: %s %s", class_name, method_name); env->ExceptionClear(); return; } jmethodID method; if (is_static) { method = env->GetStaticMethodID(clazz, method_name, sign); } else { method = env->GetMethodID(clazz, method_name, sign); } if (!method) { env->ExceptionClear(); ALOGD("get method id fail: %s %s", class_name, method_name); return; } JNINativeMethod gMethods[] = { {method_name, sign, (void *) new_fun}, }; auto artMethod = reinterpret_cast(ArtM::GetArtMethod(env, clazz, method)); if (!CheckFlags(artMethod)) { ALOGE("check flags error. class:%s, method:%s", class_name, method_name); return; } *orig_fun = reinterpret_cast(artMethod[ArtM::GetArtMethodNativeOffset()]); if (env->RegisterNatives(clazz, gMethods, 1) < 0) { ALOGE("jni hook error. class:%s, method:%s", class_name, method_name); return; } // FastNative if (HookEnv.api_level == __ANDROID_API_O__ || HookEnv.api_level == __ANDROID_API_O_MR1__) { ArtM::AddAccessFlag((char *) artMethod, kAccFastNative); } ALOGD("register class:%s, method:%s success!", class_name, method_name); } void JniHook::InitJniHook(JNIEnv *env, int api_level) { HookEnv.api_level = api_level; HookEnv.method_utils_class = env->FindClass("top/niunaijun/jnihook/MethodUtils"); HookEnv.get_method_desc_id = env->GetStaticMethodID(HookEnv.method_utils_class, "getDesc", "(Ljava/lang/reflect/Method;)Ljava/lang/String;"); HookEnv.get_method_declaring_class_id = env->GetStaticMethodID(HookEnv.method_utils_class, "getDeclaringClass", "(Ljava/lang/reflect/Method;)Ljava/lang/String;"); HookEnv.get_method_name_id = env->GetStaticMethodID(HookEnv.method_utils_class, "getMethodName", "(Ljava/lang/reflect/Method;)Ljava/lang/String;"); }