MethodHooker-Hook分析

xiaoxiao2021-02-28  13

转自:http://weihe6666.iteye.com/blog/2224141

Hook的原理是修改java层的method属性,然后注册jni函数,但是实现起来还是有点复杂,具体看下面的函数。

Java代码   int Hook(){      init();      void* handle = dlopen("/data/local/libTest.so",RTLD_NOW);      const char *dlopen_error = dlerror();      if(!handle){          DEBUG_PRINT("cannt load plugin :%s",dlopen_error);          return -1;      }      SetupFunc setup = (SetupFunc)dlsym(handle,"getpHookInfo");      const char *dlsym_error = dlerror();      if (dlsym_error) {          DEBUG_PRINT("Cannot load symbol 'getpHookInfo' :%s" , dlsym_error);          dlclose(handle);          return 1;      }        HookInfo *hookInfo;      setup(&hookInfo);        DEBUG_PRINT("Target Class:%s",hookInfo[0].tClazz);      DEBUG_PRINT("Target Method:%s",hookInfo[0].tMethod);        ClassMethodHook(hookInfo[0]);      return 0;  }   1. init初始化虚拟机环境 2. 加载要注册jni函数的so文件 3. 注册jni函数,并修改java层对应的method方法属性 一、init函数 Java代码   void init()  {      g_bAttatedT = false;      g_JavaVM = android::AndroidRuntime::getJavaVM();  }   这里通过android运行环境获得虚拟机句柄或者称之为虚拟机指针,思考一下为何能够通过android::AndroidRuntime::getJavaVM();获取虚拟机指针,为什么正常情况下写的jni函数,此语句无法通过编译。 android系统在启动时会通过init.rc启动zygote进程,而zygote进行会初始化android运行时环境,并且初始化jvm相关的环境,而所有的app程序都是通过zygote fork出来的,相应的android运行时环境和jvm环境也会与zygote相同。 那么在任意一个app的native层,都可以通过android::AndroidRuntime::getJavaVM();获取jvm指针,navite层本来就是linux系统进程的概念,可以笼统的把jvm看做是linux进程中运行的一个线程,这么一想就通了。 二、so动态库加载 linux有通用的函数dlopen、dlsym、dlclose函数来处理so动态库的加载,这里也必然使用这几个函数来加载我们需要的so库。 dlopen就不多说了,看一下dlsym SetupFunc setup = (SetupFunc)dlsym(handle,"getpHookInfo"); 这里是获取so库中getpHookInfo符号的地址,也就是获取此函数的地址,那么SetupFunc setup怎么理解呢?如果C基础比较扎实的话,这不是个问题,看一下声明:typedef int (*SetupFunc)(HookInfo**);这里定义了一个函数指针,函数指针的行为为(*函数名)(参数); HookInfo *hookInfo; setup(&hookInfo); 这一步就是执行函数,或者说函数的调用。 三、java层函数的hook Java代码   bool ClassMethodHook(HookInfo info){        JNIEnv *jenv = GetEnv();        jclass clazzTarget = jenv->FindClass(info.tClazz);      if (ClearException(jenv)) {          DEBUG_PRINT("ClassMethodHook[Can't find class:%s in bootclassloader",info.tClazz);            clazzTarget = findAppClass(jenv,info.tClazz);          if(clazzTarget == NULL){              DEBUG_PRINT("%s","Error in findAppClass");              return false;          }      }        jmethodID method = jenv->GetMethodID(clazzTarget,info.tMethod,info.tMeihodSig);      if(method==NULL){          DEBUG_PRINT("ClassMethodHook[Can't find method:%s",info.tMethod);          return false;      }        if(isArt()){          HookArtMethod(jenv,method);      }else{          HookDalvikMethod(method);      }        JNINativeMethod gMethod[] = {          {info.tMethod, info.tMeihodSig, info.handleFunc},      };        if(info.handleFunc != NULL){          if (jenv->RegisterNatives(clazzTarget, gMethod, 1) < 0) {              DEBUG_PRINT("err");              return false;          }      }        DetachCurrent();      return true;  }   这个函数是重点,这里如何hook以及如何在so中注册jni函数呢? 想要hook java层函数,先要获取此函数的Method指针,然后在对此指针进行操作。我们看一看如何获取Method句柄。 想要获取类的一些信息,比如想要获取和jvm进行沟通的渠道,这个渠道就是JNIEnv,jni句柄, Java代码   static JNIEnv *GetEnv()  {      int status;      JNIEnv *envnow = NULL;      status = g_JavaVM->GetEnv((void **)&envnow, JNI_VERSION_1_4);      if(status < 0)      {          status = g_JavaVM->AttachCurrentThread(&envnow, NULL);          if(status < 0)          {              return NULL;          }          g_bAttatedT = true;      }      return envnow;  }   通过jvm句柄获取g_JavaVM->GetEnv((void **)&envnow, JNI_VERSION_1_4);这里有一个关键的处理就是绑定线程AttachCurrentThread(&envnow, NULL); 获取到JNIEnv后,便可以通过findClass获取类对象的句柄:jclass clazzTarget = jenv->FindClass(info.tClazz); 这是一个常用的方法,不多解。 jenv->GetMethodID(clazzTarget,info.tMethod,info.tMeihodSig) 这句是从class对象获取method属性,然后修改此method对应的属性。 四、修改Method属性 Java代码   bool HookDalvikMethod(jmethodID jmethod){      DEBUG_PRINT("HookDalvikMethod");      Method *method = (Method*)jmethod;      SET_METHOD_FLAG(method, ACC_NATIVE);        int argsSize = dvmComputeMethodArgsSize(method);      if (!dvmIsStaticMethod(method))          argsSize++;        method->registersSize = method->insSize = argsSize;        if (dvmIsNativeMethod(method)) {          method->nativeFunc = dvmResolveNativeMethod;          method->jniArgInfo = computeJniArgInfo(&method->prototype);      }      return true;  }   1. 设置method 为native属性:SET_METHOD_FLAG(method, ACC_NATIVE); 2. 计算method的参数个数,如果不是static,需要添加this参数 3. 设置nativeFunc地址函数 3. 设置jniArgInfo参数信息 这里重要的点是computeJniArgInfo(&method->prototype);来计算输入参数和返回类型。
转载请注明原文地址: https://www.6miu.com/read-1099983.html

最新回复(0)