text: //
  // Created by Milk on 3/8/21.
  //
  
  #include <jni.h>
  #import <jnihook/JniHook.h>
  #include "utils/Log.h"
  #include "Art.h"
  #include "ArtM.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<jstring>(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<jstring>(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<jstring>(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<char *>(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<uintptr_t *>(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<void *>(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;");
  }
  
  