安卓 dex 通用脱壳技术研究(二)

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

代码未完,下一篇继续;


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值