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的研究 


免責聲明!

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



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