zoukankan      html  css  js  c++  java
  • adbi学习:java hook实现机制

      adbi的java hook实现代码ddi不在之前下载的文件中,下载地址:https://github.com/crmulliner/ddi,具体的编译看readme里面很详细的介绍了。注意ddi代码不能单独使用要跟之前的adbi相结合,因为adbi提供了注入so。本文不对代码进行详细的剖析(你可以看参考资料的文章),分析下2个问题:java如何hook;如何执行自己的java函数。

      java hook

      其实在ddi的java hook和xposed的hook原理(不清楚的看我之前xposed的分析)是相同的,都是改变被hook函数的accessflag;将原本的java函数变成native函数,从而去执行自己定义的native函数;它实现的代码在dalvik_hook.c的dalvik_hook函数中。但不同于xposed是,它没有直接修改Mehtod结构的insns和nativeFunc而是调用了dvmUseJNIBridge函数来修改insns和nativeFunc。但在我看这个函数时发现其跟android源码中的dvmRegisterJNIMethod很相似:

    static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
        const char* signature, void* fnPtr)
    {
      // 解释下参数:
      // clazz:类名"shy/luo/jni/ClassWithJni";
      // methodName:需要注册的jni方法名 nanosleep;
      // signature:方法的签名 实质是方法的参数和返回值,区别不同参数的函数
      // fnPtr: jni方法函数地址 即shy_luo_jni_ClassWithJni_nanosleep函数;dalvik执行的就是这个函数,很重要哎
        Method* method;
        ......
        method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
        ......
        dvmUseJNIBridge(method, fnPtr);
        ......
    }

       dvmRegisterJNIMethod(关于此函数的解析,看dalvik浅析二:jni、so)函数中用dvmFindDirectMethodByDescriptor找到函数在dalvik中的Method结构,然后再dvmUseJNIBridge为Method注册native函数(在正常情况下,该Method的accessflag就是native所以无需修改)。下面来看ddi中的dalvik_hook函数实现

    void* dalvik_hook(struct dexstuff_t *dex, struct dalvik_hook_t *h)
    {    //dalvik_hook_t结构体中已包含函数所属的class、name、signature
        ......
        h->method = dex->dvmFindVirtualMethodHierByDescriptor_fnPtr(target_cls, h->method_name, h->method_sig);
        if (h->method == 0) {
            h->method = dex->dvmFindDirectMethodByDescriptor_fnPtr(target_cls, h->method_name, h->method_sig);
        }
            ......
            //保留method的属性带以后还原
            h->iss = h->method->insSize;
            h->rss = h->method->registersSize;
            h->oss = h->method->outsSize;
          // 这里要处理
            h->method->insSize = h->n_iss;
            h->method->registersSize = h->n_rss;
            h->method->outsSize = h->n_oss;
            
            h->method->jniArgInfo = 0x80000000; // <--- also important;这里很重要,下面要分析
            ......
            h->access_flags = h->method->a;        //保留原先函数的accessflag
            h->method->a = h->method->a | h->af; // make method native
            ......
            // bind function
            dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);
            ......
    }

      显而易见,dalvik_hook按dvmRegisterJNIMethod的逻辑来编写的,所以hook其实是自己为java函数注册了一个jni方法!(熟读源码理解实现机制,你会发现很多框架和工具都是基于源码的)ok,上面的知识点大部分都是熟悉,注意蓝色代码很重要的是函数使用的dalvik寄存器个数,xposed也对其进行修正但不同的是jniArgInfo属性,xposed没对它修改,看下对jniArgInfo的描述

     jniArgInfo:这个变量记录了一些预先计算好的信息,从而不需要在调用的时候再通过方法的参数和返回值实时计算了,方便了JNI的调用,提高了调用的速度。如果第一位为1(即0x80000000),则Dalvik虚拟机会忽略后面的所有信息,强制在调用时实时计算;

       可以这么理解jniArgInfo相当于jni方法的cache标识符,若jniArgInfo不为0x80000000则dalvik会按之前调用过的jni方法去执行。回到xposed和ddi的区别:

       xposed:在loadPackage时hook函数,而在此时绝对没有执行过函数的jni方法;

       ddi:在app运行时hook函数,我们可不敢肯定此时没有执行过jni方法啊

      执行自己的java函数

      在so中执行了上面的java hook后,app在调用被hook函数时就会执行我们的自定义的native函数。那我们又怎样去执行我们自己的java函数呢(难道不可以直接在native函数中实现逻辑?当然可以啊,但java开发android简单啊)?很简单,1.加载自己的dex(包含自定义java函数的文件);2.找到自定义java函数地址3.执java函数。下面对以上三步用到的知识点剖析。

    const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {  //  /dalvik/vm/native/dalvik_system_DexFile.cpp基于源码4.4
    520    { "openDexFileNative",  "(Ljava/lang/String;Ljava/lang/String;I)I",
    521        Dalvik_dalvik_system_DexFile_openDexFileNative },
    522    { "openDexFile",        "([B)I",
    523        Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
    524    { "closeDexFile",       "(I)V",
    525        Dalvik_dalvik_system_DexFile_closeDexFile },
    526    { "defineClassNative",  "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
    527        Dalvik_dalvik_system_DexFile_defineClassNative },
    528    { "getClassNameList",   "(I)[Ljava/lang/String;",
    529        Dalvik_dalvik_system_DexFile_getClassNameList },
    530    { "isDexOptNeeded",     "(Ljava/lang/String;)Z",
    531        Dalvik_dalvik_system_DexFile_isDexOptNeeded },
    532    { NULL, NULL, NULL },
    533};

      第一、二步的知识点都在上面的代码里。加载dex的知识我们在app加壳的文章中有所提及,ddi利用Dalvik_dalvik_system_DexFile_openDexFileNative直接加载目录里的dex文件。关于第二步找函数是利用Dalvik_dalvik_system_DexFile_defineClassNative来实现,具体可以看dalvik浅析三:类加载 这里我有一点不明白的是为什么调用了Dalvik_dalvik_system_DexFile_defineClassNative还要再去调用(*env)->FindClass去得到ClassObject,明明2者都是调用dvmFindClassNoInit去实现的啊,有谁知道告诉我!第三步更简单了,得到函数的ClassObject后直接找到methodID,再调用CallVoidMethodA就可以在native执行java函数啦。

       最后添幅图来理解下ddi的整个执行过程吧

      

      当然你也可以在my_init中hook到自定义native函数2中,省去native函数1的执行。

      ok,adbi的分析就到此为止啦。总结下adbi在hijack中利用ptrace在目标进程注入代码,该代码会加载so;而在so里面可以进行so和java hook:

      so hook:找到native函数的指令利用Inline hook去修改它,就可以在执行native函数时去执行自定义的函数;

      java hook:改变函数的accessflag去执行自己的native函数,而在native中去执行自定义的java函数

    参考资料:

      1 Android平台下Dalvik层hook框架ddi的研究 

  • 相关阅读:
    再谈低代码开发平台(200727)【转】
    JS 动态加载脚本的4种方法【转】
    mdadm Centos7 软RAID0安装配置
    Program to print all palindromes in a given range打印范围内的回文
    Python | Reverse Slicing of given string倒排并切割字符串
    Reverse string in Python (5 different ways)倒排字符串
    安装pip3
    yum命令的使用与createrepo自建仓库教程
    你了解Redis的持久化吗?
    Elasticsearch面试题汇总与解析
  • 原文地址:https://www.cnblogs.com/vendanner/p/5021890.html
Copyright © 2011-2022 走看看