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\n")

        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\n", h->clname)

        void *target_cls = dex->dvmFindLoadedClass_fnPtr(h->clname);
        if (h->debug_me)
                log("class = 0x%x\n", 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\n")
                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\n", h->method_name, h->method_sig, h->method)

        if (h->method) {
                h->insns = h->method->insns;

                if (h->debug_me) {
                        log("nativeFunc %x\n", 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\n", h->method->shorty)
                        log("name %s\n", h->method->name)
                        log("arginfo %x\n", h->method->jniArgInfo)
                }
                h->method->jniArgInfo = 0x80000000; // <--- also important
                if (h->debug_me) {
                        log("noref %c\n", h->method->noRef)
                        log("access %x\n", 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\n", h->method->a)

                dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);

                if (h->debug_me)
                        log("patched %s to: 0x%x\n", h->method_name, h->native_func)

                return (void*)1;
        }
        else {
                if (h->debug_me)
                        log("could NOT patch %s\n", 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\n", cookie)
        if (!cookie)
                log("libsmsdispatch: make sure /data/dalvik-cache/ is world writable and delete data@local@tmp@ddiclasses.dex\n")
        void *clazz = dexstuff_defineclass(&d, "org/mulliner/ddiexample/SMSDispatch", cookie);
        log("libsmsdispatch: clazz = 0x%x\n", 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\n", obj)

                if (!obj)
                        log("libsmsdispatch: failed to create smsdispatch class, FATAL!\n")
        }
        else {
                log("libsmsdispatch: constructor not found!\n")
        }

        // 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\n", 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\n", 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\n", 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\n", h->method->shorty)
        //log("name %s\n", h->method->name)
        //log("arginfo %x\n", h->method->jniArgInfo)
        h->method->jniArgInfo = 0x80000000;
        //log("noref %c\n", h->method->noRef)
        //log("access %x\n", h->method->a)
        h->access_flags = h->method->a;
        h->method->a = h->method->a | h->af;
        //log("access %x\n", h->method->a)

        dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);

        if (h->debug_me)
                log("patched BACK %s to: 0x%x\n", h->method_name, h->native_func)
}
dalvik_postcall 函數跟 dalvik_hook 函數后半部分一致,通過對目標 Method 對象的成員進行賦值實現掛鈎



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM