zoukankan      html  css  js  c++  java
  • android hook 框架 ADBI 如何实现dalvik函数挂钩

    Android so注入-libinject2 简介、编译、运行

    Android so注入-libinject2  如何实现so注入

    Android so注入-Libinject 如何实现so注入

    Android so注入挂钩-Adbi 框架简介、编译、运行

    Android so注入挂钩-Adbi 框架如何实现so注入

    Android so注入挂钩-Adbi 框架如何实现so函数挂钩

    Android so注入挂钩-Adbi 框架如何实现dalvik函数挂钩

    Android dalvik挂钩-Xposed框架如何实现注入

    Android dalvik挂钩-Xposed框架如何实现挂钩

    前面几篇分析已经能做到注入一个so到目标进程并用so里的函数挂钩目标进程的函数,如果对这个实现不了解,请返回去阅读  android hook 框架 ADBI 简介、编译、运行  、 android hook 框架 ADBI 如何实现so注入 、android hook 框架 ADBI 如何实现so函数挂钩, so函数的挂钩只能影响native世界,没有影响到java虚拟机内部,而android绝大部分逻辑都是跑在虚拟机内部的。所以这篇接着分析 adbi 剩下的部分代码,看它如何实现挂钩dalvik虚拟机内部函数,使得目标进程执行java函数时能执行到我们注入的 dex 文件里的函数,这部分代码在 adbi 项目的 ddi 目录下。

    其中,dalvikhook目录存放具体的 dalvik 挂钩实现,examples 目录存放两个挂钩java函数的例子。我们使用第一个例子, smsdispatch 来分析这个过程。

    一、 smsdispatch 例子主要是两个文件, smsdispatch.c 和  SMSDispatch.java , 前者编译成一个so,利用 android hook 框架 ADBI 如何实现so注入  的方法注入到目标进程,并执行挂钩操作。后者编译成一个dex 文件,smsdispatch.c 挂钩前会先加载这个Dex文件获取用来挂钩的函数。

    void __attribute__ ((constructor)) my_init(void);
    
    void my_init(void)
    {
            log("libsmsdispatch: started
    ")
    
            debug = 1;
            // set log function for  libbase (very important!)
            set_logfunction(my_log2);
            // set log function for libdalvikhook (very important!)
            dalvikhook_set_logfunction(my_log2);
    
            hook(&eph, getpid(), "libc.", "epoll_wait", my_epoll_wait, 0);
    }

    同前面的分析,my_init函数由于带有 __attribute__ ((constructor)) 修饰,smsdispatch.so 被目标进程加载后自动执行my_init, 该函数执行hook操作,挂钩目标进程的 libc.so 的 epoll_wait 函数。其实这里可以直接执行 dalvik 的挂钩的。

    static int my_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
    {
            int (*orig_epoll_wait)(int epfd, struct epoll_event *events, int maxevents, int timeout);
            orig_epoll_wait = (void*)eph.orig;
            // remove hook for epoll_wait
            hook_precall(&eph);
    
            // resolve symbols from DVM
            dexstuff_resolv_dvm(&d);
    
            // hook
            dalvik_hook_setup(&dpdu, "Lcom/android/internal/telephony/SMSDispatcher;", "dispatchPdus", "([[B)V", 2, my_dispatch);
            dalvik_hook(&d, &dpdu);
    
            // call original function
            int res = orig_epoll_wait(epfd, events, maxevents, timeout);
            return res;
    }

    目标进程epoll_wait被调用的时候,实际执行的是  my_epoll_wait , 这个函数与之前的例子相比,增加了 dexstuff_resolv_dvm、 dalvik_hook_setup、 dalvik_hook 三个函数,即执行了 dalvik的挂钩操作,被挂钩的是  com/android/internal/telephony/SMSDispatcher 类的  dispatchPdus 函数,挂成了 my_dispatch 函数。其实这几句可以直接放到  my_init 里,当so被注入时直接执行的。涉及到几个数据结构:

    static struct dexstuff_t d;   
    static struct dalvik_hook_t dpdu;
    struct dalvik_hook_t
    {
            char clname[256];
            char clnamep[256];
            char method_name[256];
            char method_sig[256];
    
            Method *method;
            int sm; // static method
    
            // original values, saved before patching
            int iss;
            int rss;
            int oss;
            int access_flags;
            void *insns; // dalvik code
    
            // native values
            int n_iss; // == n_rss
            int n_rss; // num argument (+ 1, if non-static method) 
            int n_oss; // 0
            void *native_func;
    
            int af; // access flags modifier
    
            int resolvm;
    
            // for the call
            jclass cls;
            jmethodID mid;
    
            // debug stuff
            int dump;      // call dvmDumpClass() while patching
            int debug_me;  // print debug while operating on this method
    };
    struct dalvik_hook_t 结构描述了一次dalvik hook操作需要记录的信息,包括被挂钩的java函数和用于挂钩的java 函数的信息。其中, Method *method; 成员指向被挂钩java函数的Method结构体,通过动态替换这个指针指向的结构体内部成员的值,可以影响该java函数(一个java函数其实就是一个 Method 结构体,包括真实执行的native 函数地址及其他描述信息)。

    struct dexstuff_t
    {
            void *dvm_hand;
    
            dvmCreateStringFromCstr_func dvmStringFromCStr_fnPtr;
            dvmGetSystemClassLoader_func dvmGetSystemClassLoader_fnPtr;
            dvmThreadSelf_func dvmThreadSelf_fnPtr;
    
            dvmIsClassInitialized_func dvmIsClassInitialized_fnPtr;
            dvmInitClass_func dvmInitClass_fnPtr;
            dvmFindVirtualMethodHierByDescriptor_func dvmFindVirtualMethodHierByDescriptor_fnPtr;
            dvmFindDirectMethodByDescriptor_func dvmFindDirectMethodByDescriptor_fnPtr;
            dvmIsStaticMethod_func dvmIsStaticMethod_fnPtr;
            dvmAllocObject_func dvmAllocObject_fnPtr;
            dvmCallMethodV_func dvmCallMethodV_fnPtr;
            dvmCallMethodA_func dvmCallMethodA_fnPtr;
            dvmAddToReferenceTable_func dvmAddToReferenceTable_fnPtr;
            dvmDecodeIndirectRef_func dvmDecodeIndirectRef_fnPtr;
            dvmUseJNIBridge_func dvmUseJNIBridge_fnPtr;
            dvmFindInstanceField_func dvmFindInstanceField_fnPtr;
            dvmFindLoadedClass_func dvmFindLoadedClass_fnPtr;
            dvmDumpAllClasses_func dvmDumpAllClasses_fnPtr;
    
            dvmGetCurrentJNIMethod_func dvmGetCurrentJNIMethod_fnPtr;
            dvmLinearSetReadWrite_func dvmLinearSetReadWrite_fnPtr;
    
            dvmSetNativeFunc_func dvmSetNativeFunc_fnPtr;
            dvmCallJNIMethod_func dvmCallJNIMethod_fnPtr;
    
            dvmHashTableLock_func dvmHashTableLock_fnPtr;
            dvmHashTableUnlock_func dvmHashTableUnlock_fnPtr;
            dvmHashForeach_func dvmHashForeach_fnPtr;
    
            dvmDumpClass_func dvmDumpClass_fnPtr;
            dvmInstanceof_func dvmInstanceof_fnPtr;
    
            DalvikNativeMethod *dvm_dalvik_system_DexFile;
            DalvikNativeMethod *dvm_java_lang_Class;
    
            void *gDvm; // dvm globals !
    
            int done;
    };
    struct dexstuff_t 结构体包含了dalvik 虚拟机的重要的函数指针,这些函数指针的值从libdvm.so动态库里获取,有了这些指针,就可以在 native 环境里操纵java世界(比如加载类,生成对象,调用java函数,设置java类或函数属性等等),dexstuff_resolv_dvm 函数干的事情就是加载 libdvm.so 动态库并填充 dexstuff_t 结构体。
    
    
    int dalvik_hook_setup(struct dalvik_hook_t *h, char *cls, char *meth, char *sig, int ns, void *func)
    {
            if (!h)
                    return 0;
    
            strcpy(h->clname, cls);
            strncpy(h->clnamep, cls+1, strlen(cls)-2);
            strcpy(h->method_name, meth);
            strcpy(h->method_sig, sig);
            h->n_iss = ns;
            h->n_rss = ns;
            h->n_oss = 0;
            h->native_func = func;
    
            h->sm = 0; // set by hand if needed
    
            h->af = 0x0100; // native, modify by hand if needed
    
            h->resolvm = 0; // don't resolve method on-the-fly, change by hand if needed
    
            h->debug_me = 0;
    
            return 1;
    }
    dalvik_hook_setup 做dalvik函数挂钩的准备工作,包括把目标类和目标函数的名字保存下来,把用于挂钩的native 函数的地址保存下来,设置 dalvik_hook_t 结构对象的初始值等。 
    
    
    int dalvik_hook_setup(struct dalvik_hook_t *h, char *cls, char *meth, char *sig, int ns, void *func)
    {
    
            strcpy(h->method_name, meth);
            strcpy(h->method_sig, sig);
            h->n_iss = ns;
            h->n_rss = ns;
            h->n_oss = 0;
            h->native_func = func;
    
            h->sm = 0; // set by hand if needed
    
            h->af = 0x0100; // native, modify by hand if needed
    
            h->resolvm = 0; // don't resolve method on-the-fly, change by hand if needed
    
            h->debug_me = 0;
    
            return 1;
    }
    {
            if (h->debug_me)
                    log("dalvik_hook: class %s
    ", h->clname)
    
            void *target_cls = dex->dvmFindLoadedClass_fnPtr(h->clname);
            if (h->debug_me)
                    log("class = 0x%x
    ", target_cls)
    
            // print class in logcat
            if (h->dump && dex && target_cls)
                    dex->dvmDumpClass_fnPtr(target_cls, (void*)1);
    
            if (!target_cls) {
                    if (h->debug_me)
                            log("target_cls == 0
    ")
                    return (void*)0;
            }
    
            if (h->method == 0) {
            }
    
            // constrcutor workaround, see "dalvik_prepare" below
            if (!h->resolvm) {
                    h->cls = target_cls;
                    h->mid = (void*)h->method;
            }
    
            if (h->debug_me)
                    log("%s(%s) = 0x%x
    ", h->method_name, h->method_sig, h->method)
    
            if (h->method) {
                    h->insns = h->method->insns;
    
                    if (h->debug_me) {
                            log("nativeFunc %x
    ", h->method->nativeFunc)
    
                    }
    
                    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;
    
                    if (h->debug_me) {
                            log("shorty %s
    ", h->method->shorty)
                            log("name %s
    ", h->method->name)
                            log("arginfo %x
    ", h->method->jniArgInfo)
                    }
                    h->method->jniArgInfo = 0x80000000; // <--- also important
                    if (h->debug_me) {
                            log("noref %c
    ", h->method->noRef)
                            log("access %x
    ", h->method->a)
                    }
                    h->access_flags = h->method->a;
                    h->method->a = h->method->a | h->af; // make method native
                    if (h->debug_me)
                            log("access %x
    ", h->method->a)
    
                    dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);
    
                    if (h->debug_me)
                            log("patched %s to: 0x%x
    ", h->method_name, h->native_func)
    
                    return (void*)1;
            }
            else {
                    if (h->debug_me)
                            log("could NOT patch %s
    ", h->method_name)
            }
    
            return (void*)0;
    }

    dalvik_hook 函数实现dalvik函数挂钩。分为几步:

    step1, void *target_cls = dex->dvmFindLoadedClass_fnPtr(h->clname);  调用 dvmFindLoadedClass_fnPtr 函数获取目标类在虚拟机内部的指针,dvmFindLoadedClass_fnPtr这个函数是

    dexstuff_resolv_dvm  获取的。
    step2, h->method = dex->dvmFindVirtualMethodHierByDescriptor_fnPtr(target_cls, h->method_name, h->method_sig); 根据函数名获取目标dalvik函数对象指针
    step3,
    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;

    保存目标Dalvik 函数寄存器环境,设置新的寄存器环境

    step4, 

                    h->method->jniArgInfo = 0x80000000; // <--- also important
                    h->access_flags = h->method->a;
                    h->method->a = h->method->a | h->af; // make method native
                    dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);

    设置目标函数对象类型 access_flags 为 native ,并通过  dvmUseJNIBridge_fnPtr 函数将挂钩函数 h->native_func 设置为目标dalvik 函数对象真正执行的函数。这一步之后,虚拟机内部执行到目标java函数时,就会调用到 native 函数  my_dispatch,挂钩已经完成。

    static void my_dispatch(JNIEnv *env, jobject obj, jobjectArray pdu)
    {
            // load dex classes
            int cookie = dexstuff_loaddex(&d, "/data/local/tmp/ddiclasses.dex");
            log("libsmsdispatch: loaddex res = %x
    ", cookie)
            if (!cookie)
                    log("libsmsdispatch: make sure /data/dalvik-cache/ is world writable and delete data@local@tmp@ddiclasses.dex
    ")
            void *clazz = dexstuff_defineclass(&d, "org/mulliner/ddiexample/SMSDispatch", cookie);
            log("libsmsdispatch: clazz = 0x%x
    ", clazz)
    
            // call constructor and passin the pdu
            jclass smsd = (*env)->FindClass(env, "org/mulliner/ddiexample/SMSDispatch");
            jmethodID constructor = (*env)->GetMethodID(env, smsd, "<init>", "([[B)V");
            if (constructor) {
                    jvalue args[1];
                    args[0].l = pdu;
    
                    jobject obj = (*env)->NewObjectA(env, smsd, constructor, args);
                    log("libsmsdispatch: new obj = 0x%x
    ", obj)
    
                    if (!obj)
                            log("libsmsdispatch: failed to create smsdispatch class, FATAL!
    ")
            }
            else {
                    log("libsmsdispatch: constructor not found!
    ")
            }
    
            // call original SMS dispatch method
            jvalue args[1];
            args[0].l = pdu;
            dalvik_prepare(&d, &dpdu, env);
            (*env)->CallVoidMethodA(env, obj, dpdu.mid, args);
            log("success calling : %s
    ", dpdu.method_name)
            dalvik_postcall(&d, &dpdu);
    }
    my_dispatch 函数分几步,
    step1, 加载我们自定义的 SMSDispatch dex 文件,dexstuff_defineclass 函数实现了这个过程
    step2, 调用 JNIEnv 的 FindClass和GetMethodID函数,搜寻到step1加载的dex文件里的 SMSDispatch 类的构造函数, 再调用 NewObjectA 函数在目标 JNIEnv 环境里生成一个 SMSDispatch 实例
    step3, 先调用 dalvik_prepare 函数将前面 hook 的函数回退,这样接下去自定义的 SMSDispatch 类可以执行真正的 com/android/internal/telephony/SMSDispatcher 类的 dispatchPdus 函数
    step4, 调用 JNIEnv 的 CallVoidMethodA 函数,执行自定义的 SMSDispatch 类的函数
    step5, 再调用 dalvik_postcall 函数恢复对 com/android/internal/telephony/SMSDispatcher 类的 dispatchPdus 函数的挂载


    int dalvik_prepare(struct dexstuff_t *dex, struct dalvik_hook_t *h, JNIEnv *env)
    {
    
            // this seems to crash when hooking "constructors"
    
            if (h->resolvm) {
                    h->cls = (*env)->FindClass(env, h->clnamep);
                    if (h->debug_me)
                            log("cls = 0x%x
    ", h->cls)
                    if (!h->cls)
                            return 0;
                    if (h->sm)
                            h->mid = (*env)->GetStaticMethodID(env, h->cls, h->method_name, h->method_sig);
                    else
                            h->mid = (*env)->GetMethodID(env, h->cls, h->method_name, h->method_sig);
                    if (h->debug_me)
                            log("mid = 0x%x
    ", h-> mid)
                    if (!h->mid)
                            return 0;
            }
    
            h->method->insSize = h->iss;
            h->method->registersSize = h->rss;
            h->method->outsSize = h->oss;
            h->method->a = h->access_flags;
            h->method->jniArgInfo = 0;
            h->method->insns = h->insns;
    }
    dalvik_prepare 函数比较简单,将 dalvik_hook_t  保存的寄存器值、Method access_flags 标记、 insns 函数指针重新赋值回去,即恢复到了挂钩之前的状态

    void dalvik_postcall(struct dexstuff_t *dex, struct dalvik_hook_t *h)
    {
            h->method->insSize = h->n_iss;
            h->method->registersSize = h->n_rss;
            h->method->outsSize = h->n_oss;
    
            //log("shorty %s
    ", h->method->shorty)
            //log("name %s
    ", h->method->name)
            //log("arginfo %x
    ", h->method->jniArgInfo)
            h->method->jniArgInfo = 0x80000000;
            //log("noref %c
    ", h->method->noRef)
            //log("access %x
    ", h->method->a)
            h->access_flags = h->method->a;
            h->method->a = h->method->a | h->af;
            //log("access %x
    ", h->method->a)
    
            dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);
    
            if (h->debug_me)
                    log("patched BACK %s to: 0x%x
    ", h->method_name, h->native_func)
    }
    dalvik_postcall 函数跟 dalvik_hook 函数后半部分一致,通过对目标 Method 对象的成员进行赋值实现挂钩


  • 相关阅读:
    第三次博客作业
    多项式求导--三次作业小结
    Python实现批量修改文件名
    汉字编程 —— 第一次个人编程作业
    PAT甲级代码仓库
    谈谈自己 —— 第一次博客作业
    爬取豆瓣网图书TOP250的信息
    HDU1862
    HDU1408
    HDU1302
  • 原文地址:https://www.cnblogs.com/jiayy/p/4303446.html
Copyright © 2011-2022 走看看