目录(?)[-]

  1. Hook Java的的一个改进版本
  2. 改进点一更简单地修改java方法为本地方法
  3. 改进点二方法回调避免线程安全问题
  4. 最后

Hook Java的的一个改进版本

《注入安卓进程,并Hook java世界的方法》这篇好文相信大家都看这,里面所提到的方法估计大家也都试过。不过里面的所用的方法,我发现有两个可以改进的地方。

改进点一:更简单地修改java方法为本地方法

[cpp] view plain copy
  1. ...  
  2.     // hook method  
  3.     int argsSize = calcMethodArgsSize(method->shorty);  
  4.     if (!dvmIsStaticMethod(method))  
  5.         argsSize++;  
  6.   
  7.   
  8.     SET_METHOD_FLAG(method, ACC_NATIVE);  
  9.     method->registersSize = method->insSize = argsSize;  
  10.     method->outsSize = 0;  
  11.     method->jniArgInfo = dvmComputeJniArgInfo(method->shorty);  
  12.   
  13.   
  14.     // save info to insns  
  15.     method->insns = (u2*)info;  
  16.   
  17.   
  18.     // bind the bridge func,only one line  
  19.     method->nativeFunc = method_handler;  
  20.     LOGI("[+] %s->%s was hooked\n", classDesc, methodName);  
  21.     ...  


直接把method->nativeFunc即可,无需重新调用JNIEnv的RegisterNatives方法,其中method_handler可以是下面两种形式之一:

[cpp] view plain copy
  1. typedef void (*DalvikBridgeFunc)(const u4* args, JValue* pResult, const Method* method, struct Thread* self);  
  2. typedef void (*DalvikNativeFunc)(const u4* args, JValue* pResult);  

这样有一个好处,就是所有java方法都可以统一指向同一个native func,而不需要像为每一个java method方法指定一个native func。

改进点二:方法回调避免线程安全问题

原来的方法,是这样的

[cpp] view plain copy
  1. //hook之前先拷贝  
  2.   uint mlen = sizeof(Method);  
  3.   Method *oldMeth = (Method*)malloc(mlen);  
  4.   memcpy(oldMeth,method,mlen);  
  5.   info->odlMethod = oldMeth;  
  6.   info->curMethod = method;  
  7.   
  8.   
  9.   //回调后再拷贝回来,再通过jni->callXXXXMethod调用,之后再重新hook  
  10.   memcpy(hi->curMethod,hi->odlMethod,mlen);  
  11.   jmethodID om = (jmethodID)hi->curMethod;  
  12.   jenv->CallVoidMethod(me,om,gDevice_Sensors);  
  13.   ClassMethodHook(jenv,&baiduhookInfos[0]);  


这个方法,其实是有线程安全问题的,其中在dalvik中,有很多方法可以直接调用Method对象,比如dvmCallMethod, dvmCallMethodA, dvmCallMethodV,dvmInvokeMethod等等。针对DalvikBridgeFunc和DalvikNativeFunc的参数,我最后选择使用dvmInvokeMethod,这个函数的原型是这样的:

[cpp] view plain copy
  1. Object* dvmInvokeMethod(Object* obj, const Method* method, ArrayObject* argList, ArrayObject* params, ClassObject* returnType, bool noAccessCheck)  

其中,obj是this或者null(如果是static方法),method可以直接使用hook之前copy的对象,比较麻烦是argList,params和returnType的获取。获取argList的方法,我在Proxy.cpp中到了现成的boxMethodArgs方法,而returnType通过Reflect.h中dvmGetBoxedReturnType的方法也可以获取,而剩下的params只能自己写代码了,下面是我的代码:

[cpp] view plain copy
  1. STATIC ArrayObject* dvmGetMethodParamTypes(const Method* method, const char* methodsig){  
  2.     /* count args */  
  3.     size_t argCount = dexProtoGetParameterCount(&method->prototype);  
  4.     STATIC ClassObject* java_lang_object_array = dvmFindSystemClass("[Ljava/lang/Object;");  
  5.   
  6.   
  7.     /* allocate storage */  
  8.     ArrayObject* argTypes = dvmAllocArrayByClass(java_lang_object_array, argCount, ALLOC_DEFAULT);  
  9.     if(argTypes == NULL){  
  10.         return NULL;  
  11.     }  
  12.   
  13.   
  14.     Object** argObjects = (Object**) argTypes->contents;  
  15.     const char *desc = (const char *)(strchr(methodsig, '(') + 1);  
  16.   
  17.   
  18.     /* 
  19.      * Fill in the array. 
  20.      */  
  21.     size_t desc_index = 0;  
  22.     size_t arg_index = 0;  
  23.     bool isArray = false;  
  24.     char descChar = desc[desc_index];  
  25.   
  26.   
  27.     while (descChar != ')') {  
  28.   
  29.   
  30.         switch (descChar) {  
  31.         case 'Z':  
  32.         case 'C':  
  33.         case 'F':  
  34.         case 'B':  
  35.         case 'S':  
  36.         case 'I':  
  37.         case 'D':  
  38.         case 'J':  
  39.             if(!isArray){  
  40.                 argObjects[arg_index++] = dvmFindPrimitiveClass(descChar);  
  41.                 isArray = false;  
  42.             }else{  
  43.                 char buf[3] = {0};  
  44.                 memcpy(buf, desc + desc_index - 1, 2);  
  45.                 argObjects[arg_index++] = dvmFindSystemClass(buf);  
  46.             }  
  47.   
  48.   
  49.             desc_index++;  
  50.             break;  
  51.   
  52.   
  53.         case '[':  
  54.             isArray = true;  
  55.             desc_index++;  
  56.             break;  
  57.   
  58.   
  59.         case 'L':  
  60.             int s_pos = desc_index, e_pos = desc_index;  
  61.             while(desc[++e_pos] != ';');  
  62.             s_pos = isArray ? s_pos - 1 : s_pos;  
  63.             isArray = false;  
  64.   
  65.   
  66.             size_t len = e_pos - s_pos + 1;  
  67.             char buf[128] = { 0 };  
  68.             memcpy((void *)buf, (const void *)(desc + s_pos), len);  
  69.             argObjects[arg_index++] = dvmFindClass(buf);  
  70.             desc_index = e_pos + 1;  
  71.             break;  
  72.         }  
  73.   
  74.   
  75.         descChar = desc[desc_index];  
  76.     }  
  77.   
  78.   
  79.     return argTypes;  
  80. }  


通过上面几个类型的获取之后,最后再看一下整个method hook的实现,过程其实大同小异,不过直接把上述提及的向种类型信息预先获取并保存到method->insns里头了:

[cpp] view plain copy
  1. extern int __attribute__ ((visibility ("hidden"))) dalvik_java_method_hook(JNIEnv* env, HookInfo *info) {  
  2.     const char* classDesc = info->classDesc;  
  3.     const char* methodName = info->methodName;  
  4.     const char* methodSig = info->methodSig;  
  5.     const bool isStaticMethod = info->isStaticMethod;  
  6.   
  7.   
  8.     jclass classObj = dvmFindJNIClass(env, classDesc);  
  9.     if (classObj == NULL) {  
  10.         LOGE("[-] %s class not found", classDesc);  
  11.         return -1;  
  12.     }  
  13.   
  14.   
  15.     jmethodID methodId =  
  16.             isStaticMethod ?  
  17.                     env->GetStaticMethodID(classObj, methodName, methodSig) :  
  18.                     env->GetMethodID(classObj, methodName, methodSig);  
  19.   
  20.   
  21.     if (methodId == NULL) {  
  22.         LOGE("[-] %s->%s method not found", classDesc, methodName);  
  23.         return -1;  
  24.     }  
  25.   
  26.   
  27.   
  28.   
  29.     // backup method  
  30.     Method* method = (Method*) methodId;  
  31.     if(method->nativeFunc == method_handler){  
  32.         LOGW("[*] %s->%s method had been hooked", classDesc, methodName);  
  33.         return -1;  
  34.     }  
  35.     Method* bakMethod = (Method*) malloc(sizeof(Method));  
  36.     memcpy(bakMethod, method, sizeof(Method));  
  37.   
  38.   
  39.     // init info  
  40.     info->originalMethod = (void *)bakMethod;  
  41.     info->returnType = (void *)dvmGetBoxedReturnType(bakMethod);  
  42.     info->paramTypes = dvmGetMethodParamTypes(bakMethod, info->methodSig);  
  43.   
  44.   
  45.     // hook method  
  46.     int argsSize = calcMethodArgsSize(method->shorty);  
  47.     if (!dvmIsStaticMethod(method))  
  48.         argsSize++;  
  49.   
  50.   
  51.     SET_METHOD_FLAG(method, ACC_NATIVE);  
  52.     method->registersSize = method->insSize = argsSize;  
  53.     method->outsSize = 0;  
  54.     method->jniArgInfo = dvmComputeJniArgInfo(method->shorty);  
  55.   
  56.   
  57.     // save info to insns  
  58.     method->insns = (u2*)info;  
  59.   
  60.   
  61.     // bind the bridge func,only one line  
  62.     method->nativeFunc = method_handler;  
  63.     LOGI("[+] %s->%s was hooked\n", classDesc, methodName);  
  64.   
  65.   
  66.     return 0;  
  67. }  

然后是method_handler的实现,这个方法是所有java方法的跳转函数,所以在这里可以注册callback,不过这部分逻辑我没有做上,有兴趣的朋友可以加上。

[cpp] view plain copy
  1. STATIC void method_handler(const u4* args, JValue* pResult, const Method* method, struct Thread* self){  
  2.     HookInfo* info = (HookInfo*)method->insns; //get hookinfo pointer from method-insns  
  3.     LOGI("entry %s->%s", info->classDesc, info->methodName);  
  4.   
  5.   
  6.     Method* originalMethod = reinterpret_cast(info->originalMethod);  
  7.     Object* thisObject = (Object*)args[0];  
  8.   
  9.   
  10.     ArrayObject* argTypes = dvmBoxMethodArgs(originalMethod, args + 1);   
  11.     pResult->l = (void *)dvmInvokeMethod(thisObject, originalMethod, argTypes, (ArrayObject *)info->paramTypes, (ClassObject *)info->returnType, true);  
  12.   
  13.   
  14.     dvmReleaseTrackedAlloc((Object *)argTypes, self);  
  15. }  


最后通过dvmInvokeMethod就可以直接调回原来的函数了。

最后

写这个代码,主要是因为我在工作中要注入到某个系统进程,然后要hook java中的某些方法,但用cydia和xposed感觉太笨重了,特别是xposed,里面的很多参数的boxed/unboxed都是通过jni模块自动转换的,整个框架已经离不开dex文件了。

所以才想自己实现一套纯本地的java hook代码,而《注入安卓进程,并Hook java世界的方法》所介绍的方法,我感觉用起来不太方便,跟cydia和xposed两个框架的主要区别就是缺少了一个“中转函数”,所以而有了本码。

代码我上传到github,目前只有java hook,我打算把目前的hook技术都集成到这里,包括inline hook, elf hook等等。


原文地址: http://blog.csdn.net/l173864930/article/details/39667355

更多相关文章

  1. Android之调用百度地图API规划当前位置到指定位置的路线
  2. Android系统启动阶段多种快速重启系统方法试验记录
  3. AsyncTask的使用和原理探究(一)
  4. Android(安卓)Studio apk打包,keystore.jks文件生成,根据keystore
  5. Android(安卓)View相关-事件分发机制详解-View
  6. 如何通过看google工程师的提交记录获取第一手Android资料
  7. Android蓝牙播放如何显示歌曲信息?
  8. 解锁Retrofit -- 浅析Retrofit源码
  9. Android如何判断当前手机是否正在播放音乐,并获取到正在播放的音

随机推荐

  1. Android基础 : Android(安卓)Service[转]
  2. Android加密
  3. 关于Android(安卓)NDK中调用第三方的动态
  4. Android(安卓)RecyclerView嵌套的滑动冲
  5. 程序时提示: No compatible targets were
  6. Android(安卓)OS 现身 HTC TyTN II
  7. Android(安卓)MVVM
  8. 你的Android(安卓)HTTPS真的安全吗?
  9. Android(安卓)TextView实现逐字动画
  10. Android自定义View之贝塞尔曲线图