转https://awabest.com/thread-18663-1-1.html
一、如何獲取dex
首先,我們知道動態加載的dex必然會調用dalvik/vm/DvmDex.cpp中以下兩個函數任意一個:
dvmDexFileOpenFromFd 從文件描述符獲取DexFile結構體
dvmDexFileOpenPartial 從內存獲取DexFile結構體
百度這裡用的是dvmDexFileOpenFromFd,通過這個函數我們得到了pDexFile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36int 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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25struct 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加載的基址。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25struct 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文件大小。
1
2
3int 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做法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38#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.type == 0x2000) //0x2000代表classdata
{
cls_dat_off = pMaps->list.offset; //获取classdata起始偏移
break;
}
}
log("classdata_offset:0x%x\n", cls_dat_off);
log("0,"); //记录classdata的偏移用的log
for (u4 i = 0; i
{
const u1* data = dexGetClassData(g_pDexFile, &pClassDefs);
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 已經可以正常解析。