http://my.oschina.net/cve2015/blog/508605


摘要  本文剖析安卓 Dalvik 解释器 Portable 的原理与实现,并通过修改 Dalvik 虚拟机实现 dex 文件的通用脱壳方案;本方法同样适应于 ART 虚拟机;

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);
}

代码未完,下一篇继续;


Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐