首页
论坛
专栏
课程

[原创]百度加固逆向分析—dex还原

2017-6-30 13:49 11664

[原创]百度加固逆向分析—dex还原

2017-6-30 13:49
11664

上回说过要再写一篇文章,这里跟大家分享一下百度壳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 已经可以正常解析。

六、附件说明

      额 附件不让上传了,大家看下思路就好




[招聘]欢迎市场人员加入看雪学院团队!

最新回复 (45)
ddddddfx 2017-6-30 14:13
2
0
我顶啊 
scxc 2 2017-6-30 14:16
3
0
ddddddfx 我顶啊
你这太划水了吧。。
地狱怪客 2 2017-6-30 14:33
4
0
太厉害了
zfdyq 1 2017-6-30 14:35
5
0
这个厉害了!膜拜!
不知世事 1 2017-6-30 14:39
6
0
不错,赞
hanhaochi 2017-6-30 14:47
7
0
厉害了……等我涉及到这些,我天天去烦你……
scxc 2 2017-6-30 14:51
8
0
hanhaochi 厉害了……等我涉及到这些,我天天去烦你……[em_19]
韩大神最近在玩什么?
Youngs 2017-6-30 14:58
9
0
厉害了!叼叼叼!!
欧阳峰峰 2017-6-30 15:18
10
0
大神,onCreate流程研究了没?  invoke-static  {v1,  p0,  v0},  Lcom/baidu/protect/A;-&gt;V(ILjava/lang/Object;[Ljava/lang/Object;)V,这个函数,流程混淆的没法看了
clumsybirda 2017-6-30 15:47
11
0
天气不错啊
scxc 2 2017-6-30 16:48
12
0
欧阳峰峰 大神,onCreate流程研究了没? invoke-static {v1, p0, v0}, Lcom/baidu/protect/A;->V(ILjava/lang/Object;[Ljava ...
这个函数是在我分析的idb中的0x2CA4D。混淆我没看,可以hook  malloc函数看看他放了什么进去。
Loopher 2017-7-1 11:07
13
0
叼叼叼
Lostself 2017-7-1 16:55
14
0
66666
scxc 2 2017-7-3 11:42
15
0
欧阳峰峰 大神,onCreate流程研究了没? invoke-static {v1, p0, v0}, Lcom/baidu/protect/A;->V(ILjava/lang/Object;[Ljava ...
你可以分析下0x3292C到0x329DC中间内容。其他都没用的。。
smehod 2017-7-3 14:47
16
0
厉害!
欧阳峰峰 2017-7-3 21:16
17
0
谢大神,onCreate差不多分析完了
scxc 2 2017-7-4 11:06
18
0
欧阳峰峰 谢大神,onCreate差不多分析完了
别叫大神了,我是渣渣。看雪的大佬太多。
王小东 2017-7-4 15:05
19
0
谢大神,onCreate差不多分析完了
ugene 2017-7-5 14:32
20
0
太厉害了
繁华皆成空 2017-7-6 18:01
21
0
看来百度用了2代壳+vmp
scxc 2 2017-7-6 19:25
22
0
繁华皆成空 [em_4][em_4]看来百度用了2代壳+vmp
没有vmp只有混淆
欧阳锋锋 2017-7-6 20:00
23
0
有vmp有混淆,跟360的一样,通过jni解释的
scxc 2 2017-7-6 20:41
24
0




欧阳锋锋

有vmp有混淆,跟360的一样,通过jni解释的[em_48]

好吧  我还没分析里面流程  没看到呢 dalvik指令的解释器不能算vmp吧? 这种跟邦邦企业版的vmp差的还挺大吧

欧阳锋锋 2017-7-6 23:16
25
0
先前分析棒棒企业版的,就是内存加载加类抽取加这种vmp,各大加固厂商宣传的vmp也是这种实现原理,没看到哪家是不依赖dalvik,真正自己实现一套指令的,难道现在棒棒实现了?企业版和免费的,在dex的加载执行上区别是不大的,无非是在对so的处理上,保护强度大些
scxc 2 2017-7-7 00:01
26
0




欧阳锋锋

先前分析棒棒企业版的,就是内存加载加类抽取加这种vmp,各大加固厂商宣传的vmp也是这种实现原理,没看到哪家是不依赖dalvik,真正自己实现一套指令的,难道现在棒棒实现了?企业版和免费的,在dex的 ...

有没有样本呀。我看看跟我看的一样吗。libdexvmp.so?

无边 2017-7-7 01:34
27
0
大哥,来个测试样本apk啊。
cugtaotao 2017-7-7 17:32
28
0
支持一个,分析的很不错。看来类抽取的加固方案也不是很安全。目前正在实现类抽取的加固方案,有兴趣的加我QQ409842321一起交流研究。
houjingyi 9 2017-9-10 00:04
29
0
6666666666666
看雪猫猫 2017-9-13 14:22
30
0
大神用的什么工具~~~透露下啊
clumsybirda 2017-9-13 18:15
31
0
天气不错啊
scxc 2 2017-9-14 17:23
32
0
我是很水 爱看看 不看滚 谢谢
clumsybirda 2017-9-15 09:31
33
0




天气不错啊
clumsybirda 2017-9-15 09:35
34
0
天气不错啊!
clumsybirda 2017-9-15 09:38
35
0




天气不错啊!
clumsybirda 2017-9-15 09:42
36
0




天气不错啊!
clumsybirda 2017-9-15 09:42
37
0
天气不错啊
clumsybirda 2017-9-15 09:53
38
0
天气不错啊
scxc 2 2017-9-15 10:59
39
0
.
niansi 2018-2-10 10:34
40
0
大神,我用的jeb版本是225,jdk版本是1.7(也试过1.8),但是按q键编译时提示“No  decompiler  available”,求解答
DarkMoonCO 2018-4-8 15:49
41
0
楼主能给个附件么?
淡泊人生year 2018-4-11 12:16
42
0
我想收藏这篇文章
兔仙人会发光 2018-5-2 15:08
43
0
大神你用的什么模拟器呢?
miaoerst 2018-5-24 16:38
44
0
大神,  请问多个dex的app的dexfile  使用dvmDexFileOpenPartial都可以拿到吗
scxc 2 2018-5-24 16:46
45
0
miaoerst 大神, 请问多个dex的app的dexfile 使用dvmDexFileOpenPartial都可以拿到吗
现在脱壳在art下更好拿到dex了。参考defcon2017进行修改  https://github.com/CheckPointSW/android_unpacker
tigerwood 2018-10-23 10:06
46
0
确实好文章,好好学习,感谢分享,感觉自己现在落伍了。。。
游客
登录 | 注册 方可回帖
返回