百度加固逆向分析—dex還原--二代抽取殼

原文鏈接:https://bbs.pediy.com/thread-218891.htm

上回說過要再寫一篇文章,這裏跟大家分享一下百度殼DEX的dump與修復。

下面開始:

一、如何獲取dex

    首先,我們知道動態加載的dex必然會調用dalvik/vm/DvmDex.cpp中以下兩個函數任意一個:

    dvmDexFileOpenFromFd  從文件描述符獲取DexFile結構體

    dvmDexFileOpenPartial    從內存獲取DexFile結構體

    百度這裏用的是dvmDexFileOpenFromFd,通過這個函數我們得到了pDexFile

int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
{
    DvmDex* pDvmDex;
    DexFile* pDexFile;
    MemMapping memMap;
    int parseFlags = kDexParseDefault;
    int result = -1;
    if (gDvm.verifyDexChecksum)
        parseFlags |= kDexParseVerifyChecksum;
    if (lseek(fd, 0, SEEK_SET) < 0) {
        ALOGE("lseek rewind failed");
        goto bail;
    }
    if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
        ALOGE("Unable to map file");
        goto bail;
    }
    pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);//這裏獲取了pDexFile
    if (pDexFile == NULL) {
        ALOGE("DEX parse failed");
        sysReleaseShmem(&memMap);
        goto bail;
    }
    pDvmDex = allocateAuxStructures(pDexFile);
    if (pDvmDex == NULL) {
        dexFileFree(pDexFile);
        sysReleaseShmem(&memMap);
        goto bail;
    }
    /* tuck this into the DexFile so it gets released later */
    sysCopyMap(&pDvmDex->memMap, &memMap);
    pDvmDex->isMappedReadOnly = true;
    *ppDvmDex = pDvmDex;
    result = 0;
bail:
    return result;
struct DexFile {
    /* directly-mapped "opt" header */
    const DexOptHeader* pOptHeader;
    /* pointers to directly-mapped structs and arrays in base DEX */
    const DexHeader*    pHeader;
    const DexStringId*  pStringIds;
    const DexTypeId*    pTypeIds;
    const DexFieldId*   pFieldIds;
    const DexMethodId*  pMethodIds;
    const DexProtoId*   pProtoIds;
    const DexClassDef*  pClassDefs;
    const DexLink*      pLinkData;
    /*
     * These are mapped out of the "auxillary" section, and may not be
     * included in the file.
     */
    const DexClassLookup* pClassLookup;
    const void*         pRegisterMapPool;       // RegisterMapClassPool
    /* points to start of DEX file data */
    const u1*           baseAddr;
    /* track memory overhead for auxillary structures */
    int                 overhead;
    /* additional app-specific data structures associated with the DEX */
    //void*               auxData;
};

通過pDexFile->baseAddr 獲取到dex加載的基址。

struct DexHeader {
    u1  magic[8];           /* includes version number */
    u4  checksum;           /* adler32 checksum */
    u1  signature[kSHA1DigestLen]; /* SHA-1 hash */
    u4  fileSize;           /* length of entire file */
    u4  headerSize;         /* offset to start of next section */
    u4  endianTag;
    u4  linkSize;
    u4  linkOff;
    u4  mapOff;
    u4  stringIdsSize;
    u4  stringIdsOff;
    u4  typeIdsSize;
    u4  typeIdsOff;
    u4  protoIdsSize;
    u4  protoIdsOff;
    u4  fieldIdsSize;
    u4  fieldIdsOff;
    u4  methodIdsSize;
    u4  methodIdsOff;
    u4  classDefsSize;
    u4  classDefsOff;
    u4  dataSize;
    u4  dataOff;
};

通過pDexFile->pHeader->fileSize 獲取到dex文件大小。

int fd = open("/sdcard/dump.dex", O_CREAT | O_WRONLY, 0666);
write(fd, pDexFile->baseAddr, pDexFile->pHeader->fileSize);
close(fd);

這時我們已經得到dex了。

二、百度殼對dex做了什麼?

     1、修改DexClassDef中的classDataOff字段保存的偏移爲負偏移

2、將classdata數據清空

 三、這麼做如何讓系統正常解析?

       百度的把classdata的數據保存在/data/data/xxxx/.1/1.jar包中,會在加載dex之前先分配空間給jar包,所以他的偏移爲負值,內存結構如下圖:

 

四、還原DEX

      1、獲取到pDexFile後,我們遍歷pDexFile->pClassDefs調用dexGetClassData獲取到ClassData。將ClassData經過writeLeb128函數編碼,寫入到文件classdata並記錄每個class的大小(此處參考Dexhunter做法)

#define log(...) \
        {FILE *fp = fopen("/sdcard/dumpdex.log", "a+"); if (fp) {\
        fprintf(fp, __VA_ARGS__);\
        fclose(fp);}}
   //因爲log被hook了所以用寫文件的形式保存log
   void dump()      
   {
        const DexClassDef* pClassDefs = pDexFile->pClassDefs;
    u4 classDefsSize = pDexFile->pHeader->classDefsSize;
    DexMapList* pMaps = (DexMapList*)(g_pDexFile->baseAddr + pDexFile->pHeader->mapOff);
    u4 cls_dat_off = 0;
    for (u4 i = 0; i < pMaps->size; i++)
    {
        if (pMaps->list[i].type == 0x2000)   //0x2000代表classdata
        {
            cls_dat_off = pMaps->list[i].offset;  //獲取classdata起始偏移
            break;
        }
    }
    log("classdata_offset:0x%x\n", cls_dat_off);
    log("0,");     //記錄classdata的偏移用的log
    for (u4 i = 0; i<classDefsSize; i++)
    {
        const u1* data = dexGetClassData(g_pDexFile, &pClassDefs[i]);
        DexClassData *pData = ReadClassData(&data);
        if (!pData) {
            continue;
        }
        int fd = open("/sdcard/classdata", O_APPEND | O_CREAT | O_WRONLY, 0666);
        int class_data_len = 0;
        uint8_t *out = EncodeClassData(pData, class_data_len);
        log("%d,", class_data_len);//記錄classdata的偏移用的log
        cls_dat_off += class_data_len;
        write(fd, out, class_data_len);
        close(fd);
    }
    log("\n");
    }

以上ReadClassData、EncodeClassData函數用的Dexhunter的。感謝Dexhunter作者。

   2、此時我們已經有了dump.dex、classdata、dumpdex.log中保存的偏移。

 3、將classdata寫入到classdata偏移處。

  4、通過程序修復classdef中的偏移。

5、運行完後我們已經獲取到正確的dex,我們將out.dex拖入JEB 已經可以正常解析。

 

六、附件說明

      額 附件不讓上傳了,大家看下思路就好

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章