安卓 dex 通用脱壳技术研究

来源:互联网 发布:张伯伦场均数据 编辑:程序博客网 时间:2024/05/17 09:35

0x01 背景介绍

安卓 APP 的保护一般分为下列几个方面:

  1. JAVA/C代码混淆

  2. dex文件加壳

  3. .so文件加壳

  4. 反动态调试技术

其中混淆和加壳是为了防止对应用的静态分析;代码混淆会增加攻击者的时间成本, 但并不能从根本上解决应用被逆向的问题;而加壳技术一旦被破解,其优势更是荡然无存;反调试用来对抗对APP的动态分析;

昨天看雪zyqqyz同学发了一个Android应用程序通用自动脱壳方法:DexHunter,详见Github;通过定制Dalvik虚拟机实现,并对目前国内6款主流加固产品的进行了测试,效果良好;下面我们会讲解Dalvik解释器实现原理,并分析DexHunter代码实现;

目前来看,要对抗这种脱壳方法,最好的办法应该是APP启动时hook被修改的几处函数,并还原之;对开发者来说,应当将涉及资产、创新的关键代码放入Native层实现,并通过.so加壳和反调试进行保护;

上次与梆梆的交流,他们提到梆梆3.0正在研发类似PC上的VMP保护壳技术,实现APK保护;但个人认为要在兼容性方面做的工作实在太多,短时间内估计很难实现了;

下面开始,分3个方面介绍通过定制Dalvik的通用脱壳方案:1.Dalvik 解释器原理分析,2.DexHunter代码分析,3.测试

0x02 Dalvik 解释器原理分析

解释器是Dalvik虚拟机的执行引擎,它负责解释执行Dalvik字节码。在字节码加载完毕后,Dalvik虚拟机调用解释器开始取指解释字节码,解释器跳转到解释程序处执行。目前安卓解释器有两种,Portable和Fast解释器,分别使用C和汇编实现;优势分别是兼容性和性能,具体使用哪个可以自己来指定,因此本着简单的原则,我们分析并使用Portable解释器;

获取字节码并分析与解释执行是Dalvik虚拟机解释器的主要工作。Dalvik虚拟机的入口函数是vm/interp下的dvmInterpret函数;外部通过调用dvmInterpret函数进入解释器执行,其流程为dvmCallMethod->dvmCallMethodV->dvmInterpret。

在外部函数调用解释器以后,解释器执行的主要流程有以下几个步骤。

  1. 初始化解释器执行环境

  2. 根据系统参数,选择使用Portable或Fast解释器

  3. 跳转到相应解释器执行

  4. 取指及指令检查

  5. 执行字节码对应程序段

dvmInterpret函数作为解释器的入口函数,主要完成整个流程的前三部分,执行流程如下:

由于前三部分与我们定制Dalvik无关,这里不做详细介绍;

根据解释器的功能,可以想像的到,最简单的模型就是一个大的switch语句,对每条指令进行判断,然后case到相应的代码进行解释,解释完成后又回到switch顶部,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while (insn) {
    switch (insn) {
        case NOP:
            break;
        case MOV:
            do something;
            break;
        ...
        case OP:
            do something;
            break;
        default:
            break;
    }
    取指;
}

然而当解释完成一条指令后,再重新判断指令类型是个昂贵的开销。因为对于每条指令,都将从switch顶部开始判断,也就是从NOP指令开始判断,直到找到相应的指令为止,这使得解释器的执行效率十分低下。

这类问题的解决方法就是空间换时间,Dalvik就采用了这个思路;它为每条指令分配一个对应的标签(Label),标签标示的是该指令解释程序的开始,每条指令的解释程序末尾,有取指动作,可以取下一条要执行指令;Dalvik具体使用GCC的Threaded Code技术来实现,它使用了一个静态的标签数组,用来存储各个字节码解释程序对应的标签地址,其具体以一个宏来定义:

dalvik/libdex/DexOpcodes.h

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
 * Macro used to generate a computed goto table for use in implementing
 * an interpreter in C.
 */
#define DEFINE_GOTO_TABLE(_name) \
    static const void* _name[kNumPackedOpcodes] = {                      \
        /* BEGIN(libdex-goto-table); GENERATED AUTOMATICALLY BY opcode-gen */ \
        H(OP_NOP),                                                            \
        H(OP_MOVE),                                                           \
        H(OP_MOVE_FROM16),                                                    \
        H(OP_MOVE_16),                                                        \
        H(OP_MOVE_WIDE),                                                      \
        ...
    }

下面看下H宏实现:

?
1
#define    H(_op)    &&op_##_op

那如何根据指令得到相应的Label地址呢?Dalvik中使用了索引号:

?
1
2
3
4
5
6
7
8
9
10
11
12
enum Opcode {
    // BEGIN(libdex-opcode-enum); GENERATED AUTOMATICALLY BY opcode-gen
    OP_NOP                          = 0x00,
    OP_MOVE                         = 0x01,
    OP_MOVE_FROM16                  = 0x02,
    OP_MOVE_16                      = 0x03,
    OP_MOVE_WIDE                    = 0x04,
    OP_MOVE_WIDE_FROM16             = 0x05,
    OP_MOVE_WIDE_16                 = 0x06,
    OP_MOVE_OBJECT                  = 0x07,
    ....
}

因此整个执行流程就是:取指令->取索引号->取Label得到解释程序地址->执行指令,并取下一条指令。

上面分析了解释器的基本模型,下面看Dalvik Portable的执行流程。其解析流程如图:

首先进行相关变量的声明,保存当前正在解释的方法curMethod、程序计数器pc、栈桢指针fp、当前指令inst、指令译码的相关部分包括保存寄存器值vsrc1,vsrc2,vdst、设置方法调用指针methodToCall等。

通过DEFINE_GOTO_TABLE(handlerTable)宏进行GOTO Label的绑定,获取并拷贝self->interpSave里保存的当前状态,包括方法method、程序计数器pc、堆栈帧curFrame、返回值retval、要分析的Dex文件的类对象信息curMethod->clazz->pDvmDex等已声明的变量。其代码如下:

dalvik/vm/mterp/out/InterpC-portable.cpp

?
1
2
3
4
5
6
    /* copy state in */
    curMethod = self->interpSave.method;
    pc = self->interpSave.pc;
    fp = self->interpSave.curFrame;
    retval = self->interpSave.retval;   /* only need for kInterpEntryReturn? */
    methodClassDex = curMethod->clazz->pDvmDex;

最后通过FINISH(0)来取得第一条指令开始执行字节码解析。

在Dalvik Portable中,解释程序是由一系列宏控制,以对应的Label来表示,以NOP操作为例,其定义如下:

dalvik/vm/mterp/out/InterpC-portable.cpp

?
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
/*--- start of opcodes ---*/
/* File: c/OP_NOP.cpp */
HANDLE_OPCODE(OP_NOP)
    FINISH(1);
OP_END
/* File: c/OP_MOVE.cpp */
HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
    vdst = INST_A(inst);
    vsrc1 = INST_B(inst);
    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
        (INST_INST(inst) == OP_MOVE) ? "" "-object", vdst, vsrc1,
        kSpacing, vdst, GET_REGISTER(vsrc1));
    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
    FINISH(1);
OP_END
/* File: c/OP_MOVE_FROM16.cpp */
HANDLE_OPCODE(OP_MOVE_FROM16 /*vAA, vBBBB*/)
    vdst = INST_AA(inst);
    vsrc1 = FETCH(1);
    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
        (INST_INST(inst) == OP_MOVE_FROM16) ? "" "-object", vdst, vsrc1,
        kSpacing, vdst, GET_REGISTER(vsrc1));
    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
    FINISH(2);
OP_END

HANDLE_OPCODE(OP_NOP)表示对应的是OP_NOP操作,紧接其后的是解释程序的具体实现。到OP_END结束。而在Portable中,所以有解释程序都由C语言编写。NOP操作中的HANDLE_OPCODE、FINISH和OP_END都是宏定义。其中HANDLE_OPCODE和OP_END是成对出现的,OP_END什么也不做:

?
1
#define    OP_END

所以

?
1
2
3
HANDLE_OPCODE(OP_NOP)
    FINISH(1);
OP_END

可以翻译为:

?
1
2
op_OP_NOP:
    FINISH(1);

对于NOP指令,其完成的工作就是什么也不做。因此,对应的解释程序就是直接取下一条将要执行的指令,也就是FINISH(1)所完成的工作。在FINISH()宏里,虚拟机获取下一条指令,并从指令中提取操作码号,根据该操作码号到指令解释程序查找表中得到相应的标签,然后跳转到该处理程序执行。其定义如下:

1
2
3
4
5
6
7
8
# define FINISH(_offset) {                                                  \
        ADJUST_PC(_offset);                                                 \
        inst = FETCH(0);                                                    \
        if (self->interpBreak.ctl.subMode) {                                \
            dvmCheckBefore(pc, fp, self);                                   \
        }                                                                   \
        goto *handlerTable[INST_INST(inst)];                                \
    }

0x03 DexHunter代码分析

DexHunter 实现中,只需要修改一处文件:dalvik\vm\native\dalvik_system_DexFile.cpp

下面是BeyondCompare比对:

首先看一下DexHunter的设计原理:

APP 启动时,通过freature string定位dex在内存中位置,并读取classdef块之前的内存为part1,读取classdef之后的内存为data。遍历class_def_item结构,生成文件classdef,并通过code_item_off判断具体的类方法是否在dex范围内,若不在,则写extra文件。

描述几个问题:

  • 从哪里dump出dex文件

dex文件打开时

类加载时

类初始化时

类方法调用时

DexHunter中,我们关注,ClassLoader.loadClass->Dalvik_dalvik_system_DexFile_defineClassNative这个函数,它实现了类的加载,实现过程如下:

选择脱壳的时机应是在APP的第一个类加载的时候,为什么呢?

  1. 类加载之前,类的内容是在内存当中的

  2. 当类初始化时,该内存的内容可能会被动态修改

  3. 在一个类方法被调用前,code_item或指令肯定是可用的

那如何做呢?

我们要主动加载并初始化所有的类;

因此,我们代码的注入点,应该是Dalvik_dalvik_system_DexFile_defineClassNative()函数的clazz = dvmDefineClass(pDvmDex, descriptor, loader);语句之前;即在APP加载第一个类之前完成;通过dvmDefineClass主动遍历class_def_item加载每个类,并调用dvmIsClassInitialized和dvmInitClass函数初始化之。

初始化完成之后,内存中的就是将执行的代码,像梆梆加固针对每个方法进行的加密,会在运行时解密、运行完成后清理内存并再次加密,通过这种方法就可以过掉;因为我们模拟了这样一次调用过程;

下面是我加入注释的代码:

?
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
//------------------------added begin----------------------//
 
#include <asm/siginfo.h>
#include "libdex/DexClass.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
 
static char dexname[100]={0};   //feature string
static char dumppath[100]={0};  //dump的文件路径
static bool readable=true;
 
static pthread_mutex_t read_mutex;
static bool flag=true;
static pthread_mutex_t mutex;
static bool timer_flag=true;
static timer_t timerId;
 
struct arg{
    DvmDex* pDvmDex;
    Object * loader;
}param;
 
void timer_thread(sigval_t)
{
    timer_flag=false;
    timer_delete(timerId);
    ALOGI("GOT IT time up");
}
 
void* ReadThread(void *arg){
    FILE *fp = NULL;
    while (dexname[0]==0||dumppath[0]==0) {
        fp=fopen("/data/dexname""r");
        if (fp==NULL) {
            sleep(1);
            continue;
        }
        fgets(dexname,99,fp);   //读feature string
        dexname[strlen(dexname)-1]=0;
        fgets(dumppath,99,fp);
        dumppath[strlen(dumppath)-1]=0;//取dump路径 
        fclose(fp);
        fp=NULL;
    }
 
    struct sigevent sev;
 
    sev.sigev_notify=SIGEV_THREAD;
    sev.sigev_value.sival_ptr=&timerId;
    sev.sigev_notify_function=timer_thread;
    sev.sigev_notify_attributes = NULL;
 
    timer_create(CLOCK_REALTIME,&sev,&timerId);
 
    struct itimerspec ts;
    ts.it_value.tv_sec=5;
    ts.it_value.tv_nsec=0;
    ts.it_interval.tv_sec=0;
    ts.it_interval.tv_nsec=0;
 
    timer_settime(timerId,0,&ts,NULL);
 
    return NULL;
}
 
/*
    这里是class_data_item的前4项,称为ClassDataHeader
    Dex File->class_defs->class_def_item(class_data_offset)->class_data_item->ClassDataHeader 
*/
void ReadClassDataHeader(const uint8_t** pData, DexClassDataHeader *pHeader) 
{
    pHeader->staticFieldsSize = readUnsignedLeb128(pData);
    pHeader->instanceFieldsSize = readUnsignedLeb128(pData);
    pHeader->directMethodsSize = readUnsignedLeb128(pData);
    pHeader->virtualMethodsSize = readUnsignedLeb128(pData);
}
 
/*
    下面两个函数,分别读class_data_item Header下的内容,分Field和Method
*/
void ReadClassDataField(const uint8_t** pData, DexField* pField) 
{
    pField->fieldIdx = readUnsignedLeb128(pData);
    pField->accessFlags = readUnsignedLeb128(pData);
}
 
void ReadClassDataMethod(const uint8_t** pData, DexMethod* pMethod) 
{
    pMethod->methodIdx = readUnsignedLeb128(pData);
    pMethod->accessFlags = readUnsignedLeb128(pData);
    pMethod->codeOff = readUnsignedLeb128(pData);
}
 
/*
    解析class_data_item结构,使用到上面3个函数,分别解析,Header、Field和Method部分
*/
DexClassData* ReadClassData(const uint8_t** pData) 
{
 
    DexClassDataHeader header;
 
    if (*pData == NULL) {
        return NULL;
    }
 
    //读取 class_data_item的Header
    ReadClassDataHeader(pData, &header);
 
    size_t resultSize = sizeof(DexClassData) + (header.staticFieldsSize * sizeof(DexField)) + (header.instanceFieldsSize * sizeof(DexField)) + (header.directMethodsSize * sizeof(DexMethod)) + (header.virtualMethodsSize * sizeof(DexMethod));
 
    DexClassData* result = (DexClassData*) malloc(resultSize); //result指向class_data_item并返回
 
    if (result == NULL) {
        return NULL;
    }
 
    uint8_t* ptr = ((uint8_t*) result) + sizeof(DexClassData);  //指向class_data_item的staic_fields偏移
 
    result->header = header;
 
    //以下依次读class_data_item的staticFields,instanceFields,directMethods和virtualMethods域大小————————begain
    if (header.staticFieldsSize != 0) {
        result->staticFields = (DexField*) ptr;
        ptr += header.staticFieldsSize * sizeof(DexField);
    else {
        result->staticFields = NULL;
    }
 
    if (header.instanceFieldsSize != 0) {
        result->instanceFields = (DexField*) ptr;
        ptr += header.instanceFieldsSize * sizeof(DexField);
    else {
        result->instanceFields = NULL;
    }
 
    if (header.directMethodsSize != 0) {
        result->directMethods = (DexMethod*) ptr;
        ptr += header.directMethodsSize * sizeof(DexMethod);
    else {
        result->directMethods = NULL;
    }
 
    if (header.virtualMethodsSize != 0) {
        result->virtualMethods = (DexMethod*) ptr;
    else {
        result->virtualMethods = NULL;
    }
    //以下依次读class_data_item的staticFields,instanceFields,directMethods和virtualMethods域大小————————end
     
     
 
    //以下依次读staticFields,instanceFields,directMethods,virtualMethods域内容————————begain
    for (uint32_t i = 0; i < header.staticFieldsSize; i++) {
        ReadClassDataField(pData, &result->staticFields[i]);
    }
 
    for (uint32_t i = 0; i < header.instanceFieldsSize; i++) {
        ReadClassDataField(pData, &result->instanceFields[i]);
    }
 
    for (uint32_t i = 0; i < header.directMethodsSize; i++) {
        ReadClassDataMethod(pData, &result->directMethods[i]);
    }
 
    for (uint32_t i = 0; i < header.virtualMethodsSize; i++) {
        ReadClassDataMethod(pData, &result->virtualMethods[i]);
    }
    //以下依次读staticFields,instanceFields,directMethods,virtualMethods域内容————————end
 
    return result;
}
 
 
/*
    class_data_item中的一些域是用LEB128算法编码的
*/
void writeLeb128(uint8_t ** ptr, uint32_t data)
{
    while (true) {
        uint8_t out = data & 0x7f;
        if (out != data) {
            *(*ptr)++ = out | 0x80;
            data >>= 7;
        else {
            *(*ptr)++ = out;
            break;
        }
    }
}
 
/*
    此函数读取class_data_item,并将内容用writeLeb128转码后返回
*/
uint8_t* EncodeClassData(DexClassData *pData, int& len)
{
    len=0;
 
    len+=unsignedLeb128Size(pData->header.staticFieldsSize);
    len+=unsignedLeb128Size(pData->header.instanceFieldsSize);
    len+=unsignedLeb128Size(pData->header.directMethodsSize);
    len+=unsignedLeb128Size(pData->header.virtualMethodsSize);
 
    if (pData->staticFields) {
        for (uint32_t i = 0; i < pData->header.staticFieldsSize; i++) {
            len+=unsignedLeb128Size(pData->staticFields[i].fieldIdx);
            len+=unsignedLeb128Size(pData->staticFields[i].accessFlags);
        }
    }
 
    if (pData->instanceFields) {
        for (uint32_t i = 0; i < pData->header.instanceFieldsSize; i++) {
            len+=unsignedLeb128Size(pData->instanceFields[i].fieldIdx);
            len+=unsignedLeb128Size(pData->instanceFields[i].accessFlags);
        }
    }
 
    if (pData->directMethods) {
        for (uint32_t i=0; i<pData->header.directMethodsSize; i++) {
            len+=unsignedLeb128Size(pData->directMethods[i].methodIdx);
            len+=unsignedLeb128Size(pData->directMethods[i].accessFlags);
            len+=unsignedLeb128Size(pData->directMethods[i].codeOff);
        }
    }
 
    if (pData->virtualMethods) {
        for (uint32_t i=0; i<pData->header.virtualMethodsSize; i++) {
            len+=unsignedLeb128Size(pData->virtualMethods[i].methodIdx);
            len+=unsignedLeb128Size(pData->virtualMethods[i].accessFlags);
            len+=unsignedLeb128Size(pData->virtualMethods[i].codeOff);
        }
    }
 
    uint8_t * store = (uint8_t *) malloc(len);
 
    if (!store) {
        return NULL;
    }
 
    uint8_t * result=store;
 
    writeLeb128(&store,pData->header.staticFieldsSize);
    writeLeb128(&store,pData->header.instanceFieldsSize);
    writeLeb128(&store,pData->header.directMethodsSize);
    writeLeb128(&store,pData->header.virtualMethodsSize);
 
    if (pData->staticFields) {
        for (uint32_t i = 0; i < pData->header.staticFieldsSize; i++) {
            writeLeb128(&store,pData->staticFields[i].fieldIdx);
            writeLeb128(&store,pData->staticFields[i].accessFlags);
        }
    }
 
    if (pData->instanceFields) {
        for (uint32_t i = 0; i < pData->header.instanceFieldsSize; i++) {
            writeLeb128(&store,pData->instanceFields[i].fieldIdx);
            writeLeb128(&store,pData->instanceFields[i].accessFlags);
        }
    }
 
    if (pData->directMethods) {
        for (uint32_t i=0; i<pData->header.directMethodsSize; i++) {
            writeLeb128(&store,pData->directMethods[i].methodIdx);
            writeLeb128(&store,pData->directMethods[i].accessFlags);
            writeLeb128(&store,pData->directMethods[i].codeOff);
        }
    }
 
    if (pData->virtualMethods) {
        for (uint32_t i=0; i<pData->header.virtualMethodsSize; i++) {
            writeLeb128(&store,pData->virtualMethods[i].methodIdx);
            writeLeb128(&store,pData->virtualMethods[i].accessFlags);
            writeLeb128(&store,pData->virtualMethods[i].codeOff);
        }
    }
 
    free(pData);
    return result;
}
 
uint8_t* codeitem_end(const u1** pData)
{
    uint32_t num_of_list = readUnsignedLeb128(pData);
    for (;num_of_list>0;num_of_list--) {
        int32_t num_of_handlers=readSignedLeb128(pData);
        int num=num_of_handlers;
        if (num_of_handlers<=0) {
            num=-num_of_handlers;
        }
        for (; num > 0; num--) {
            readUnsignedLeb128(pData);
            readUnsignedLeb128(pData);
        }
        if (num_of_handlers<=0) {
            readUnsignedLeb128(pData);
        }
    }
    return (uint8_t*)(*pData);
}
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/*
    此为DexHunter实现的主要功能,进行内存dump,将class_def_items中dump出classdef和extra部分
*/
void* DumpClass(void *parament)
{
  while (timer_flag) {
      sleep(5);
  }
 
  DvmDex* pDvmDex=((struct arg*)parament)->pDvmDex; //pDvmDex代表一个dex文件
  Object *loader=((struct arg*)parament)->loader;
  DexFile* pDexFile=pDvmDex->pDexFile;
  MemMapping * mem=&pDvmDex->memMap;
 
  u4 time=dvmGetRelativeTimeMsec();
  ALOGI("GOT IT begin: %d ms",time);
 
  char *path = new char[100];
  strcpy(path,dumppath);
  strcat(path,"classdef");  //构造classdef文件路径 
  FILE *fp = fopen(path, "wb+");
 
  strcpy(path,dumppath);
  strcat(path,"extra"); //构造extra文件路径 
  FILE *fp1 = fopen(path,"wb+");
 
  uint32_t mask=0x3ffff;
  char padding=0;
  const char* header="Landroid";
  unsigned int num_class_defs=pDexFile->pHeader->classDefsSize; //dex文件的Header域,class_defs_size,标示了class_def域有几个class_def_item
  uint32_t total_pointer = mem->length-uint32_t(pDexFile->baseAddr-(const u1*)mem->addr);
  uint32_t rec=total_pointer;
 
  while (total_pointer&3) {
      total_pointer++;
  }
 
  int inc=total_pointer-rec;
  uint32_t start = pDexFile->pHeader->classDefsOff+sizeof(DexClassDef)*num_class_defs;
  uint32_t end = (uint32_t)((const u1*)mem->addr+mem->length-pDexFile->baseAddr);
 
  for (size_t i=0;i<num_class_defs;i++) //遍历所有class_def_item
  {
      bool need_extra=false;
      ClassObject * clazz=NULL;
      const u1* data=NULL;
      DexClassData* pData = NULL;
      bool pass=false;
      const DexClassDef *pClassDef = dexGetClassDef(pDvmDex->pDexFile, i);
      const char *descriptor = dexGetClassDescriptor(pDvmDex->pDexFile,pClassDef);
 
      if(!strncmp(header,descriptor,8)||!pClassDef->classDataOff)
      {
          pass=true;
          goto classdef;
      }
 
      clazz = dvmDefineClass(pDvmDex, descriptor, loader);  //First Step:class加载
 
      if (!clazz) {
         continue;
      }
 
      ALOGI("GOT IT class: %s",descriptor);
 
      if (!dvmIsClassInitialized(clazz)) {  //Second Step:class初始化,遍历所有类进行初始化
          if(dvmInitClass(clazz)){  //与dvmIsClassInitialized()函数共同完成初始化
              ALOGI("GOT IT init: %s",descriptor);
          }
      }
            
      if(pClassDef->classDataOff<start || pClassDef->classDataOff>end)
      {
          need_extra=true;  //是不是需要extra这一块,当存在class_data_off不在dex范围内时,就需要
      }
 
      data=dexGetClassData(pDexFile, pClassDef);
      pData = ReadClassData(&data);
 
      if (!pData) {
          continue;
      }
 
      if (pData->directMethods) {
          for (uint32_t i=0; i<pData->header.directMethodsSize; i++) {
              Method *method = &(clazz->directMethods[i]);
              uint32_t ac = (method->accessFlags) & mask;
 
              ALOGI("GOT IT direct method name %s.%s",descriptor,method->name);
 
              if (!method->insns||ac&ACC_NATIVE) {
                  if (pData->directMethods[i].codeOff) {
                      need_extra = true;
                      pData->directMethods[i].accessFlags=ac;
                      pData->directMethods[i].codeOff=0;
                  }
                  continue;
              }
 
              u4 codeitem_off = u4((const u1*)method->insns-16-pDexFile->baseAddr);
 
              if (ac != pData->directMethods[i].accessFlags)
              {
                  ALOGI("GOT IT method ac");
                  need_extra=true;
                  pData->directMethods[i].accessFlags=ac;
              }
 
              if (codeitem_off!=pData->directMethods[i].codeOff&&((codeitem_off>=start&&codeitem_off<=end)||codeitem_off==0)) {
                  ALOGI("GOT IT method code");
                  need_extra=true;
                  pData->directMethods[i].codeOff=codeitem_off;
              }
 
              if ((codeitem_off<start || codeitem_off>end) && codeitem_off!=0) {
                  //如果code_item_off不在dex文件范围内,则写入extra; fp1为extra的句柄
                  //这里使用的是slider.pptx,中的第二种方案,见p42
                  need_extra=true;
                  pData->directMethods[i].codeOff = total_pointer;
                  DexCode *code = (DexCode*)((const u1*)method->insns-16);
                  uint8_t *item=(uint8_t *) code;
                  int code_item_len = 0;
                  if (code->triesSize) {
                      const u1 * handler_data = dexGetCatchHandlerData(code);
                      const u1** phandler=(const u1**)&handler_data;
                      uint8_t * tail=codeitem_end(phandler);
                      code_item_len = (int)(tail-item);
                  }else{
                      code_item_len = 16+code->insnsSize*2;
                  }
 
                  ALOGI("GOT IT method code changed");
 
                  fwrite(item,1,code_item_len,fp1);
                  fflush(fp1);
                  total_pointer+=code_item_len;
                  while (total_pointer&3) {
                      fwrite(&padding,1,1,fp1);
                      fflush(fp1);
                      total_pointer++;
                  }
              }
          }
      }
 
      if (pData->virtualMethods) {
          for (uint32_t i=0; i<pData->header.virtualMethodsSize; i++) {
              Method *method = &(clazz->virtualMethods[i]);
              uint32_t ac = (method->accessFlags) & mask;
 
              ALOGI("GOT IT virtual method name %s.%s",descriptor,method->name);
 
              if (!method->insns||ac&ACC_NATIVE) {
                  if (pData->virtualMethods[i].codeOff) {
                      need_extra = true;
                      pData->virtualMethods[i].accessFlags=ac;
                      pData->virtualMethods[i].codeOff=0;
                  }
                  continue;
              }
 
              u4 codeitem_off = u4((const u1 *)method->insns - 16 - pDexFile->baseAddr);
 
              if (ac != pData->virtualMethods[i].accessFlags)
              {
                  ALOGI("GOT IT method ac");
                  need_extra=true;
                  pData->virtualMethods[i].accessFlags=ac;
              }
 
              if (codeitem_off!=pData->virtualMethods[i].codeOff&&((codeitem_off>=start&&codeitem_off<=end)||codeitem_off==0)) {
                  ALOGI("GOT IT method code");
                  need_extra=true;
                  pData->virtualMethods[i].codeOff=codeitem_off;
              }
 
              if ((codeitem_off<start || codeitem_off>end)&&codeitem_off!=0) {
                  need_extra=true;
                  pData->virtualMethods[i].codeOff = total_pointer;
                  DexCode *code = (DexCode*)((const u1*)method->insns-16);
                  uint8_t *item=(uint8_t *) code;
                  int code_item_len = 0;
                  if (code->triesSize) {
                      const u1 *handler_data = dexGetCatchHandlerData(code);
                      const u1** phandler=(const u1**)&handler_data;
                      uint8_t * tail=codeitem_end(phandler);
                      code_item_len = (int)(tail-item);
                  }else{
                      code_item_len = 16+code->insnsSize*2;
                  }
 
                  ALOGI("GOT IT method code changed");
 
                  fwrite(item,1,code_item_len,fp1);
                  fflush(fp1);
                  total_pointer+=code_item_len;
                  while (total_pointer&3) {
                      fwrite(&padding,1,1,fp1);
                      fflush(fp1);
                      total_pointer++;
                  }
              }
          }
      }
 
classdef:
       DexClassDef temp=*pClassDef;
       uint8_t *p = (uint8_t *)&temp;
 
       if (need_extra) {
           ALOGI("GOT IT classdata before");
           int class_data_len = 0;
           uint8_t *out = EncodeClassData(pData,class_data_len);
           if (!out) {
               continue;
           }
           temp.classDataOff = total_pointer;
           fwrite(out,1,class_data_len,fp1);
           fflush(fp1);
           total_pointer+=class_data_len;
           while (total_pointer&3) {
               fwrite(&padding,1,1,fp1);
               fflush(fp1);
               total_pointer++;
           }
           free(out);
           ALOGI("GOT IT classdata written");
       }else{
           if (pData) {
               free(pData);
           }
       }
 
       if (pass) {
           temp.classDataOff=0;
           temp.annotationsOff=0;
       }
 
       ALOGI("GOT IT classdef");
       fwrite(p, sizeof(DexClassDef), 1, fp);
       fflush(fp);
  }
 
  fclose(fp1);
  fclose(fp);
    //目前已经准备好了part1,classdef,data和extra 4部分文件
  strcpy(path,dumppath);
  strcat(path,"whole.dex"); //组合4部分,并写入whole.dex
  fp = fopen(path,"wb+");
  rewind(fp);
 
  int fd=-1;
  int r=-1;
  int len=0;  
  char *addr=NULL;
  struct stat st;
 
  strcpy(path,dumppath);
  strcat(path,"part1");
 
  fd=open(path,O_RDONLY,0666);
  if (fd==-1) {
      return NULL;
  }
 
  r=fstat(fd,&st);  
  if(r==-1){  
      close(fd);  
      return NULL;
  }
 
  len=st.st_size;
  addr=(char*)mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
  fwrite(addr,1,len,fp);
  fflush(fp);
  munmap(addr,len);
  close(fd);
 
  strcpy(path,dumppath);
  strcat(path,"classdef");
 
  fd=open(path,O_RDONLY,0666);
  if (fd==-1) {
      return NULL;
  }
 
  r=fstat(fd,&st);  
  if(r==-1){  
      close(fd);  
      return NULL;
  }
 
  len=st.st_size;
  addr=(char*)mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
  fwrite(addr,1,len,fp);
  fflush(fp);
  munmap(addr,len);
  close(fd);
 
  strcpy(path,dumppath);
  strcat(path,"data");
 
  fd=open(path,O_RDONLY,0666);
  if (fd==-1) {
      return NULL;
  }
 
  r=fstat(fd,&st);  
  if(r==-1){  
      close(fd);  
      return NULL;
  }
 
  len=st.st_size;
  addr=(char*)mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
  fwrite(addr,1,len,fp);
  fflush(fp);
  munmap(addr,len);
  close(fd);
 
  while (inc>0) {
      fwrite(&padding,1,1,fp);
      fflush(fp);
      inc--;
  }
 
  strcpy(path,dumppath);
  strcat(path,"extra");
 
  fd=open(path,O_RDONLY,0666);
  if (fd==-1) {
      return NULL;
  }
 
  r=fstat(fd,&st);  
  if(r==-1){  
      close(fd);  
      return NULL;
  }
 
  len=st.st_size;
  addr=(char*)mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
  fwrite(addr,1,len,fp);
  fflush(fp);
  munmap(addr,len);
  close(fd);
 
  fclose(fp);
  delete path;
 
  time=dvmGetRelativeTimeMsec();
  ALOGI("GOT IT end: %d ms",time);
 
  return NULL;
}
//------------------------added end----------------------//

?
0 0
原创粉丝点击