安卓 dex 通用脱壳技术研究
Posted gm-201705
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓 dex 通用脱壳技术研究相关的知识,希望对你有一定的参考价值。
0x03 DexHunter代码分析
DexHunter 实现中,只需要修改一处文件:dalvikvm ativedalvik_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的第一个类加载的时候,为什么呢?
-
类加载之前,类的内容是在内存当中的
-
当类初始化时,该内存的内容可能会被动态修改
-
在一个类方法被调用前,code_item或指令肯定是可用的
那如何做呢?
我们要主动加载并初始化所有的类;
因此,我们代码的注入点,应该是Dalvik_dalvik_system_DexFile_defineClassNative()函数的clazz = dvmDefineClass(pDvmDex, descriptor, loader);语句之前;即在APP加载第一个类之前完成;通过dvmDefineClass主动遍历class_def_item加载每个类,并调用dvmIsClassInitialized和dvmInitClass函数初始化之。
初始化完成之后,内存中的就是将执行的代码,像梆梆加固针对每个方法进行的加密,会在运行时解密、运行完成后清理内存并再次加密,通过这种方法就可以过掉;因为我们模拟了这样一次调用过程;
下面是我加入注释的代码:
//------------------------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); }
代码未完,下一篇继续;
以上是关于安卓 dex 通用脱壳技术研究的主要内容,如果未能解决你的问题,请参考以下文章