MethodHooker-Hook分析

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

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


Java代码   收藏代码
  1. int Hook(){  
  2.     init();  
  3.     void* handle = dlopen("/data/local/libTest.so",RTLD_NOW);  
  4.     const char *dlopen_error = dlerror();  
  5.     if(!handle){  
  6.         DEBUG_PRINT("cannt load plugin :%s",dlopen_error);  
  7.         return -1;  
  8.     }  
  9.     SetupFunc setup = (SetupFunc)dlsym(handle,"getpHookInfo");  
  10.     const char *dlsym_error = dlerror();  
  11.     if (dlsym_error) {  
  12.         DEBUG_PRINT("Cannot load symbol 'getpHookInfo' :%s" , dlsym_error);  
  13.         dlclose(handle);  
  14.         return 1;  
  15.     }  
  16.   
  17.     HookInfo *hookInfo;  
  18.     setup(&hookInfo);  
  19.   
  20.     DEBUG_PRINT("Target Class:%s",hookInfo[0].tClazz);  
  21.     DEBUG_PRINT("Target Method:%s",hookInfo[0].tMethod);  
  22.   
  23.     ClassMethodHook(hookInfo[0]);  
  24.     return 0;  
  25. }  


1. init初始化虚拟机环境
2. 加载要注册jni函数的so文件
3. 注册jni函数,并修改java层对应的method方法属性

一、init函数
Java代码   收藏代码
  1. void init()  
  2. {  
  3.     g_bAttatedT = false;  
  4.     g_JavaVM = android::AndroidRuntime::getJavaVM();  
  5. }  


这里通过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代码   收藏代码
  1. bool ClassMethodHook(HookInfo info){  
  2.   
  3.     JNIEnv *jenv = GetEnv();  
  4.   
  5.     jclass clazzTarget = jenv->FindClass(info.tClazz);  
  6.     if (ClearException(jenv)) {  
  7.         DEBUG_PRINT("ClassMethodHook[Can't find class:%s in bootclassloader",info.tClazz);  
  8.   
  9.         clazzTarget = findAppClass(jenv,info.tClazz);  
  10.         if(clazzTarget == NULL){  
  11.             DEBUG_PRINT("%s","Error in findAppClass");  
  12.             return false;  
  13.         }  
  14.     }  
  15.   
  16.     jmethodID method = jenv->GetMethodID(clazzTarget,info.tMethod,info.tMeihodSig);  
  17.     if(method==NULL){  
  18.         DEBUG_PRINT("ClassMethodHook[Can't find method:%s",info.tMethod);  
  19.         return false;  
  20.     }  
  21.   
  22.     if(isArt()){  
  23.         HookArtMethod(jenv,method);  
  24.     }else{  
  25.         HookDalvikMethod(method);  
  26.     }  
  27.   
  28.     JNINativeMethod gMethod[] = {  
  29.         {info.tMethod, info.tMeihodSig, info.handleFunc},  
  30.     };  
  31.   
  32.     if(info.handleFunc != NULL){  
  33.         if (jenv->RegisterNatives(clazzTarget, gMethod, 1) < 0) {  
  34.             DEBUG_PRINT("err");  
  35.             return false;  
  36.         }  
  37.     }  
  38.   
  39.     DetachCurrent();  
  40.     return true;  
  41. }  


这个函数是重点,这里如何hook以及如何在so中注册jni函数呢?

想要hook java层函数,先要获取此函数的Method指针,然后在对此指针进行操作。我们看一看如何获取Method句柄。

想要获取类的一些信息,比如想要获取和jvm进行沟通的渠道,这个渠道就是JNIEnv,jni句柄,

Java代码   收藏代码
  1. static JNIEnv *GetEnv()  
  2. {  
  3.     int status;  
  4.     JNIEnv *envnow = NULL;  
  5.     status = g_JavaVM->GetEnv((void **)&envnow, JNI_VERSION_1_4);  
  6.     if(status < 0)  
  7.     {  
  8.         status = g_JavaVM->AttachCurrentThread(&envnow, NULL);  
  9.         if(status < 0)  
  10.         {  
  11.             return NULL;  
  12.         }  
  13.         g_bAttatedT = true;  
  14.     }  
  15.     return envnow;  
  16. }  


通过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代码   收藏代码
  1. bool HookDalvikMethod(jmethodID jmethod){  
  2.     DEBUG_PRINT("HookDalvikMethod");  
  3.     Method *method = (Method*)jmethod;  
  4.     SET_METHOD_FLAG(method, ACC_NATIVE);  
  5.   
  6.     int argsSize = dvmComputeMethodArgsSize(method);  
  7.     if (!dvmIsStaticMethod(method))  
  8.         argsSize++;  
  9.   
  10.     method->registersSize = method->insSize = argsSize;  
  11.   
  12.     if (dvmIsNativeMethod(method)) {  
  13.         method->nativeFunc = dvmResolveNativeMethod;  
  14.         method->jniArgInfo = computeJniArgInfo(&method->prototype);  
  15.     }  
  16.     return true;  
  17. }  


1. 设置method 为native属性:SET_METHOD_FLAG(method, ACC_NATIVE);
2. 计算method的参数个数,如果不是static,需要添加this参数
3. 设置nativeFunc地址函数
3. 设置jniArgInfo参数信息

这里重要的点是computeJniArgInfo(&method->prototype);来计算输入参数和返回类型。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值