DexHunter
从Android运行时出发,打造我们的脱壳神器
https://juejin.im/post/5aa117e9f265da23945f0f0e
DexHunter的原理分析和使用说明(一)
https://blog.csdn.net/QQ1084283172/article/details/53710357
DexHunter的原理分析和使用说明(二)
https://blog.csdn.net/QQ1084283172/article/details/53715325
DexHunter在Dalvik虚拟机模式下的脱壳原理分析
https://blog.csdn.net/qq1084283172/article/details/78494671
DexHunter在ART虚拟机模式下的脱壳原理分析
https://blog.csdn.net/QQ1084283172/article/details/78494620
换一个帅一点姿势实现DexHunter
https://bbs.pediy.com/thread-225427.htm
[原创]Xdex(百度版)脱壳工具基本原理
https://bbs.pediy.com/thread-206441.htm
Dalvik模式下,dexHunter的实现
//------------------------added begin----------------------//#include
#include "libdex/DexClass.h"
#include
#include
#include // 保存加固类型的特征字符串,其实是由pDexOrJar->fileName决定的
static char dexname[100]={0};
// 保存需要脱壳的apk文件的内存dump出来的part1、data等文件的保存路径
static char dumppath[100]={0};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;/**** dexname-----特征字符串,由pDexOrJar->fileName决定的(the feature string)* dumppath----内存dump文件的保存路径(the data path of the target app )* /data/dexname文件的示例:* /data/data/com.example.seventyfour.tencenttest/files/libmobisecy1.zip* /data/data/com.example.seventyfour.tencenttest/* * dexname(the feature string)的引用,具体参见作者zyq8709给出的slide.pptx* 重要的提醒:* Its line ending should be in the style of Unix/Linux* 意思就是/data/dexname文件中的字符串的结尾换行必须是Unix/Linux格式0A的不能是windows的0D0A***/struct arg
{DvmDex* pDvmDex;Object * loader;
} param;/*******删除定时器********/
void timer_thread(sigval_t)
{timer_flag = false;timer_delete(timerId);ALOGI("GOT IT time up");
}/*****读取配置文件/data/dexname中的数据信息并创建初始化定时器******/
void* ReadThread(void *arg)
{FILE *fp = NULL;while (dexname[0]==0 || dumppath[0]==0) {// 打开脱壳的配置文件/data/dexname// 脱壳的时,需要adb push dexname /data到Andrid系统里// 配置文件/data/dexname是可以自定义的,一些加固如梆梆可能会检测这个路径fp=fopen("/data/dexname", "r");if (fp==NULL) {sleep(1);continue;}// 读取文件中的第1行字符串---加固的特征字符串fgets(dexname, 99, fp);dexname[strlen(dexname)-1]=0;// 读取文件中的第2行字符串---需要脱壳的apk文件的数据路径fgets(dumppath,99,fp);dumppath[strlen(dumppath)-1]=0;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;
}/****获取一个类中静态字段,实例字段,直接方法和虚方法的个数*****/
void ReadClassDataHeader(const uint8_t** pData,DexClassDataHeader *pHeader)
{// 静态成员变量的个数pHeader->staticFieldsSize = readUnsignedLeb128(pData);// 实例成员变量的个数pHeader->instanceFieldsSize = readUnsignedLeb128(pData);// 直接成员方法的个数pHeader->directMethodsSize = readUnsignedLeb128(pData);// 虚成员方法的个数pHeader->virtualMethodsSize = readUnsignedLeb128(pData);
}/****获取一个类中成员变量的信息****/
void ReadClassDataField(const uint8_t** pData, DexField* pField)
{// 成员变量的DexFieldId索引pField->fieldIdx = readUnsignedLeb128(pData);// 成员变量的访问权限 pField->accessFlags = readUnsignedLeb128(pData);
}/***获取一个类中成员方法的信息****/
void ReadClassDataMethod(const uint8_t** pData, DexMethod* pMethod)
{// 成员方法的DexMethodId索引pMethod->methodIdx = readUnsignedLeb128(pData);// 成员方法的访问权限pMethod->accessFlags = readUnsignedLeb128(pData);// 成员方法的实现字节码DexCode的数据结构pMethod->codeOff = readUnsignedLeb128(pData);
}/***获取一个类的DexClassData和所有成员变量以及个数、成员方法以及个数的结构体的信息*****/
DexClassData* ReadClassData(const uint8_t** pData)
{// 类信息的数据头DexClassDataHeader header;if (*pData == NULL) {return NULL;}// 读取类的数据头信息DexClassDataHeaderReadClassDataHeader(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);if (result == NULL) {return NULL;}// 申请的内存中,指向类的实际存储成员变量DexField和成员方法DexMethod的内存起始地址uint8_t* ptr = ((uint8_t*) result) + sizeof(DexClassData);// 内存浅拷贝获取类的数据头信息result->header = header;// 判断类是否有静态成员变量if (header.staticFieldsSize != 0) {// 设置保存静态成员变量的DexField的起始地址result->staticFields = (DexField*) ptr;ptr += header.staticFieldsSize * sizeof(DexField);} else {result->staticFields = NULL;}// 判断类是否有实例成员变量if (header.instanceFieldsSize != 0) {// 设置保存实例成员变量的DexField的起始地址result->instanceFields = (DexField*) ptr;ptr += header.instanceFieldsSize * sizeof(DexField);} else {result->instanceFields = NULL;}// 判断类是否有直接成员方法if (header.directMethodsSize != 0) {// 设置保存直接成员方法的DexMethod的起始地址result->directMethods = (DexMethod*) ptr;ptr += header.directMethodsSize * sizeof(DexMethod);}else {result->directMethods = NULL;}// 判断类是否有虚成员方法if (header.virtualMethodsSize != 0) {// 设置保存虚成员方法的DexMethod的起始地址result->virtualMethods = (DexMethod*) ptr;} else {result->virtualMethods = NULL;}// 从内存中读取类的所有的静态成员变量的DexFieldfor (uint32_t i = 0; i < header.staticFieldsSize; i++) {ReadClassDataField(pData, &result->staticFields[i]);}// 从内存中读取类的所有的实例成员变量的DexFieldfor (uint32_t i = 0; i < header.instanceFieldsSize; i++) {ReadClassDataField(pData, &result->instanceFields[i]);}// 从内存中读取类的所有的直接成员方法的DexMethodfor (uint32_t i = 0; i < header.directMethodsSize; i++) {ReadClassDataMethod(pData, &result->directMethods[i]);}// 从内存中读取类的所有的虚成员方法的DexMethodfor (uint32_t i = 0; i < header.virtualMethodsSize; i++) {ReadClassDataMethod(pData, &result->virtualMethods[i]);}return result;
}/*****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;}}
}/****获取一个类的所有成员变量以及个数、成员方法以及个数的结构体的信息(自定义数据保存格式)*******/
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);// 继续获取保存类的静态成员变量的DexField的大小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);}}// 继续获取保存类的实例成员变量的DexField的大小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);}}// 继续获取保存类的直接成员方法的DexMethod的大小if (pData->directMethods) {for (uint32_t i=0; iheader.directMethodsSize; i++) {len+=unsignedLeb128Size(pData->directMethods[i].methodIdx);len+=unsignedLeb128Size(pData->directMethods[i].accessFlags);len+=unsignedLeb128Size(pData->directMethods[i].codeOff);}}// 继续获取保存类的虚成员方法的DexMethod的大小if (pData->virtualMethods) {for (uint32_t i=0; iheader.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);// 保存整个类所有的静态成员变量的DexFieldif (pData->staticFields) {for (uint32_t i = 0; i < pData->header.staticFieldsSize; i++) {writeLeb128(&store,pData->staticFields[i].fieldIdx);writeLeb128(&store,pData->staticFields[i].accessFlags);}}// 保存整个类所有的实例成员变量的DexFieldif (pData->instanceFields) {for (uint32_t i = 0; i < pData->header.instanceFieldsSize; i++) {// 指向成员变量索引表中的一个表项,即一个DexFieldId的数据结构writeLeb128(&store,pData->instanceFields[i].fieldIdx);// 访问权限writeLeb128(&store,pData->instanceFields[i].accessFlags);}}// 保存整个类所有的直接成员方法的DexMethodif (pData->directMethods) {for (uint32_t i=0; iheader.directMethodsSize; i++) {// 指向成员方法索引表中的一个表项,即一个DexMethodId数据结构writeLeb128(&store,pData->directMethods[i].methodIdx);// 访问权限writeLeb128(&store,pData->directMethods[i].accessFlags);// 成员方法的指向一个DexCode的数据结构writeLeb128(&store,pData->directMethods[i].codeOff);}}// 保存整个类所有的静虚成员方法的DexMethodif (pData->virtualMethods) {for (uint32_t i=0; iheader.virtualMethodsSize; i++) {writeLeb128(&store,pData->virtualMethods[i].methodIdx);writeLeb128(&store,pData->virtualMethods[i].accessFlags);writeLeb128(&store,pData->virtualMethods[i].codeOff);}}// 释放内存空间free(pData);return result;
}/***这个函数暂时还是不太理解,与try/catch语句解析有关***/
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);
}/*** Use this to keep track of mapped segments.
struct MemMapping
{void* addr; //start of datasize_t length; //length of datavoid* baseAddr; //page-aligned base addresssize_t baseLength; //length of mapping
};
***//***
// /dalvik/vm/DvmDex.h
// DvmDex代表一个dex文件//Some additional VM data structures that are associated with the DEX file.
struct DvmDex
{DexFile* pDexFile; //pointer to the DexFile we're associated with ,odex文件的信息const DexHeader* pHeader; //clone of pDexFile->pHeader (it's used frequently enough),dex文件头相关信息struct StringObject** pResStrings; //interned strings; parallel to "stringIds" ,字符串struct ClassObject** pResClasses; //resolved classes; parallel to "typeIds" ,通过DexFile里的ClassDefItem构造出来的一个结构体(类信息)struct Method** pResMethods; //resolved methods; parallel to "methodIds" ,通过Method_Item构造出来的结构体(方法信息)struct Field** pResFields; //resolved instance fields; parallel to "fieldIds",(this holds both InstField and StaticField)struct AtomicCache* pInterfaceCache; //interface method lookup cache//shared memory region with file contentsbool isMappedReadOnly;MemMapping memMap; // memMap->addr为odex文件的内存位置,memMap->length为odex文件的长度jobject dex_object;//lock ensuring mutual exclusion during updatespthread_mutex_t modLock;
};
****//*******
// /dalvik/libdex/DexFile.h//DexFile结构体存储了odex文件的一些信息。
//Structure representing a DEX file.struct DexFile
{//directly-mapped "opt" header const DexOptHeader* pOptHeader;//pointers to directly-mapped structs and arrays in base DEXconst 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; // RegisterMapClassPoolconst u1* baseAddr; // points to start of DEX file dataint overhead; // track memory overhead for auxillary structures//void* auxData; // additional app-specific data structures associated with the DEX
};//Direct-mapped "class_def_item".
struct DexClassDef {u4 classIdx; // index into typeIds for this class u4 accessFlags;u4 superclassIdx; // index into typeIds for superclass u4 interfacesOff; // file offset to DexTypeList u4 sourceFileIdx; // index into stringIds for source file name u4 annotationsOff; // file offset to annotations_directory_item u4 classDataOff; // file offset to class_data_item u4 staticValuesOff; // file offset to DexEncodedArray
};//Direct-mapped "code_item".
//The "catches" table is used when throwing an exception,
//"debugInfo" is used when displaying an exception stack trace or
//debugging. An offset of zero indicates that there are no entries.
struct DexCode {u2 registersSize;u2 insSize;u2 outsSize;u2 triesSize;u4 debugInfoOff; // file offset to debug info stream u4 insnsSize; // size of the insns array, in u2 units u2 insns[1];// followed by optional u2 padding // followed by try_item[triesSize] // followed by uleb128 handlersSize // followed by catch_handler_item[handlersSize]
};
*****//****
// dalvik/vm/oo/Object.h//Class objects have many additional fields. This is used for both classes and interfaces, including synthesized classes (arrays and primitive types).
//
struct ClassObject : Object {u4 instanceData[CLASS_FIELD_SLOTS]; const char* descriptor;char* descriptorAlloc;u4 accessFlags;u4 serialNumber;DvmDex* pDvmDex;ClassStatus status;ClassObject* verifyErrorClass;u4 initThreadId;size_t objectSize;ClassObject* elementClass;int arrayDim;PrimitiveType primitiveType;ClassObject* super;Object* classLoader;InitiatingLoaderList initiatingLoaderList;int interfaceCount;ClassObject** interfaces;int directMethodCount; //static, private, and methodsMethod* directMethods;int virtualMethodCount; // virtual methods defined in this class; invoked through vtableMethod* virtualMethods;int vtableCount;Method** vtable;int iftableCount;InterfaceEntry* iftable;int ifviPoolCount;int* ifviPool;int ifieldCount;int ifieldRefCount;InstField* ifields;u4 refOffsets;const char* sourceFile;int sfieldCount;StaticField sfields[0];
};struct Method {ClassObject* clazz; //the class we are a part ofu4 accessFlags; //access flags; low 16 bits are defined by spec (could be u2?)//For concrete virtual methods, this is the offset of the method in "vtable".//For abstract methods in an interface class, this is the offset of the method in "iftable[n]->methodIndexArray".u2 methodIndex;// Method bounds; not needed for an abstract method.// For a native method, we compute the size of the argument list, and set "insSize" and "registerSize" equal to it.u2 registersSize; // ins + locals u2 outsSize;u2 insSize;// method name, e.g. "" or "eatLunch" const char* name;//Method prototype descriptor string (return and argument types).// TODO: This currently must specify the DexFile as well as the proto_ids// index, because generated Proxy classes don't have a DexFile. We can// remove the DexFile* and reduce the size of this struct if we generate// a DEX for proxies.DexProto prototype;// short-form method descriptor stringconst char* shorty;// The remaining items are not used for abstract or native methods.// (JNI is currently hijacking "insns" as a function pointer, set// after the first call. For internal-native this stays null.)//// the actual code const u2* insns; // instructions, in memory-mapped .dex // JNI: cached argument and return-type hints int jniArgInfo;// JNI: native method ptr; could be actual function or a JNI bridge. We// don't currently discriminate between DalvikBridgeFunc and// DalvikNativeFunc; the former takes an argument superset (i.e. two// extra args) which will be ignored. If necessary we can use// insns==NULL to detect JNI bridge vs. internal native.DalvikBridgeFunc nativeFunc;// JNI: true if this static non-synchronized native method (that has no// reference arguments) needs a JNIEnv* and jclass/jobject. Libcore// uses this.bool fastJni;// JNI: true if this method has no reference arguments. This lets the JNI// bridge avoid scanning the shorty for direct pointers that need to be converted to local references.// TODO: replace this with a list of indexes of the reference arguments.bool noRef;// JNI: true if we should log entry and exit. This is the only way// developers can log the local references that are passed into their code.// Used for debugging JNI problems in third-party code.bool shouldTrace;// Register map data, if available. This will point into the DEX file// if the data was computed during pre-verification, or into the// linear alloc area if not.const RegisterMap* registerMap;// set if method was called during method profilingbool inProfile;
};
***///处理class_defs部分的线程函数
/*
创建原生线程,遍历被脱壳Android应用odex文件的 DexClassDef 数据数组,基于Android运行时进行被脱壳Android应用类的描述数据 DexClassDef 、DexClassData 和类方法的实现数据 DexCode 的收集和内存dump,将类的定义描述数据 DexClassDef内存dump保存到文件/data/data/pakegname/classdef中,将类的实际描述结构体数据DexClassData 和类方法的实现数据DexCode内存dump保存到文件/data/data/pakegname/extra中,然后将内存dump的odex文件的4部分数据 进行重组,得到能够反编译odex文件,其中pakegname为被脱壳Android应用的包名。
*/
void* DumpClass(void *parament)
{// 休眠一段时间while (timer_flag) {sleep(5);}// 获取odex文件的内存描述结构DvmDexDvmDex* pDvmDex=((struct arg*)parament)->pDvmDex;Object *loader=((struct arg*)parament)->loader;// 获取指向odex文件的指针DexFile* pDexFile = pDvmDex->pDexFile;// 获取内存存放odex文件的内存地址信息描述结构体MemMapping * mem = &pDvmDex->memMap;// 获取当前时间u4 time = dvmGetRelativeTimeMsec();ALOGI("GOT IT begin: %d ms", time);// 申请内存空间char *path = new char[100];// 获取被脱壳的apk的dump数据的文件保存路径strcpy(path, dumppath);// 拼接字符串(得到存放classdef的文件路径)//xxxx/classdef文件中保存着内存dump的类DexClassDefstrcat(path, "classdef");// 打开文件xxxx/classdefFILE *fp = fopen(path, "wb+");strcpy(path, dumppath);// 拼接字符串(得到存放extra数据的文件路径)//xxxx/extra文件中保存着内存dump的类DexClassData数据与DexCode,保存格式如下:// |{[DexCode#DexCode....]DexClassData}|{[DexCode#DexCode....]DexClassData}|{[DexCode#DexCode....]DexClassData}...strcat(path, "extra");// 打开文件xxxx/extraFILE *fp1 = fopen(path,"wb+");//被脱壳Android应用的odex文件中的类数据以Android运行时的DexClassData的实际填充数据为基准进行内存dump,意思就是内存dump的类描述结构体数据以dex文件加载到内存后转换为ClassObject *中的类实际填充数据为基准进行内存dump,//为什么要这样做呢? 因为,Android加固会对被保护的dex文件进行加固处理,对被保护的dex文件 中的类数据偏移 classDataOff 进行修改并将类数据 DexClassData 进行加密处理,在类被执行时先对被加密的类数据 DexClassData 进行解密处理,//然后修正类数据的偏移classDataOff 使其指向解密后正确的DexClassData数据(一般情况,被解密的DexClassData数据会存放在odex文件文件头之前的内存区域 或者 在odex文件文件尾之后的内存区域);//还有一些Android加固采取的加固粒度更细,对被保护dex文件的 codeOff 进行修改并对类方法实现 DexCode 的数据进行加密处理,在类方法执行时,先解密被加密的类方法实现 DexCode的数据,然后修正指向DexCode数据的偏移 codeOff //(一般情况,解密后的DexCode数据会被存放在在odex文件文件头之前的内 存区域 或者 在odex文件文件尾之后的内存区域)。由于当dex文件中类被执行时,类相关的描述数据都会被解密后正确填充,因此基于Android运行时进行dex文件类数据的dump。uint32_t mask = 0x3ffff;char padding = 0;//Android系统的类库是以android开头的类(用于过滤)const char* header = "Landroid";// 获取dex文件的DexClassDef的数量classDefsSizeunsigned int num_class_defs = pDexFile->pHeader->classDefsSize;// pDexFile->baseAddr为存放dex文件的内存地址,//mem->addr为存放odex的文件的数据的内存地址,mem->length为存放的odex文件的长度 // 获取dex文件起始到odex文件结束在内存区域的长度(因为偏移量是以dex文件起始地址来计算的,extra文件内容是添加到odex尾部)uint32_t total_pointer = mem->length - uint32_t(pDexFile->baseAddr-(const u1*)mem->addr);// 保存dex文件起始到odex文件结束在内存区域的长度uint32_t rec = total_pointer;//鉴于Android进程中内存数据4字节对齐的要求(目的是为了提高内存数据访问的效率),因此需要对被脱壳Android进程的odex文件的内存数据进行 4 字节对齐处理,不足4字节进行 0 的数据填充,//也是为了后面odex文件重组时正确的设置odex文件类数据DexClassData和DexCode的偏移。// 鉴于dex文件在内存中的4字节对齐问题(4字节对齐)while (total_pointer&3) {total_pointer++;}// 增加的内存字节数inc(4字节内存对齐导致的填充字节数)int inc = total_pointer - rec;//为了判断被脱壳Android应用dex文件的DexClassData数据是否被Android加固所加固处理,因此需要对DexClassData数据的偏移 classDataOff 进行边界的判断,//正常的DexClassData数据保存的起始文件偏移是dex文件存放DexClassDef段数据结束的位置,DexClassData数据保存的结束文件偏移是整个odex文件结束的位置。//遍历dex文件的每个类定义描述结构体DexClassDef,基于Android运行时的类数据DexClassData和DexCode的收集;在进行dex文件类数据收集时,排除过滤掉 "Landroid" 开头的Android系统类和空类的类数据DexClassData和DexCode的收集。// 在内存中dex文件存放DexClassData的起始文件相对偏移地址(即存放DexClassDef的结束地址)uint32_t start = pDexFile->pHeader->classDefsOff+sizeof(DexClassDef)*num_class_defs;// 在内存中dex文件起始内存地址到整个odex文件结束在内存区域的长度uint32_t end = (uint32_t)((const u1*)mem->addr+mem->length - pDexFile->baseAddr);// 遍历dex文件的所有的DexClassDeffor (size_t i = 0; i < num_class_defs; i++) //DexClassDef Loop start ----------{const u1* data = NULL;DexClassData* pData = NULL;//设置初始值bool need_extra = false;bool pass = false;// 获取dex文件的第i个DexClassDef的结构体信息const DexClassDef *pClassDef = dexGetClassDef(pDvmDex->pDexFile, i);// 获取类的描述符信息即类类型字符串如:Landroid/xxx/yyy;const char *descriptor = dexGetClassDescriptor(pDvmDex->pDexFile, pClassDef);// 判断该类是否是Landroid开头的系统类,是否是一个没数据的无效的类if(!strncmp(header, descriptor, 8) || !pClassDef->classDataOff){// 设置跳过过滤标签pass = true;// ******是系统类或者当前类不是有效的类,直接跳转******goto classdef;}//调用函数dvmDefineClass加载当前类descriptorClassObject * clazz=NULL;// ########加载类描述符指定的类#####################clazz = dvmDefineClass(pDvmDex, descriptor, loader);if (!clazz) {continue;}//#################################################// 打印加载的类描述符信息ALOGI("GOT IT 加载class: %s", descriptor);// 判断加载的指定的类是否已经初始化完成if (!dvmIsClassInitialized(clazz)) {if(dvmInitClass(clazz)){ALOGI("GOT IT init: %s", descriptor);}}// 判断类的classData的偏移classDataOff是否在正常的内存范围// pClassDef->classDataOff < start 解密的DexClassData数据存放在odex文件文件头之前的内存区域情况// pClassDef->classDataOff > end 解密的DexClassData数据存放在在odex文件文件尾之后的内存区域情况if(pClassDef->classDataOff < start || pClassDef->classDataOff > end){need_extra = true;}// 获取dex文件的一个类的类数据DexClassData的内存地址data = dexGetClassData(pDexFile, pClassDef);// 获取dex文件的一个类的类数据DexClassData以及成员变量DexField和成员方法DexMethod的结构pData = ReadClassData(&data);if (!pData) {// 获取失败,继续下次循环continue;}// 获取类成员直接方法if (pData->directMethods) {for (uint32_t i = 0; i < pData->header.directMethodsSize; i++) // directMethods looop start++++++++++++++++++++++++++++{// 获取类成员的直接方法的指针Method *method = &(clazz->directMethods[i]);// 用于判断函数是否是native函数相关uint32_t ac = (method->accessFlags) & mask;// 打印类成员直接方法的名称ALOGI("GOT IT direct method name %s.%s", descriptor, method->name);// method->insns为Dalvik虚拟机自带的Native函数(Internal Native)则值为Null// method->insns为普通的Native函数则为指向JNI实际函数机器码的首地址if (!method->insns || ac&ACC_NATIVE) {// 获取指向类成员直接方法字节码的指针if (pData->directMethods[i].codeOff) {// 设置需要额外数据的标志need_extra = true;// 设置类成员直接方法的访问权限值pData->directMethods[i].accessFlags=ac;// 设置类成员直接方法的字节码指针为0pData->directMethods[i].codeOff=0;}// 继续下次循环continue;}/**** method->insns不是Native方法的情况,则为指向方法具体的Dalvik指令的指针(指向的是实际加载到内存中的Dalvik指令,而不是在Dex文件中的)****/// 获取method->insns不是Native方法的情况下,方法的字节码实际相对偏移codeOffu4 codeitem_off = u4((const u1*)method->insns-16-pDexFile->baseAddr);// 修正method->insns不是Native方法的accessFlagsif (ac != pData->directMethods[i].accessFlags){ALOGI("GOT IT method ac");need_extra=true;pData->directMethods[i].accessFlags=ac;}// 修正method->insns不是Native方法的字节码正常相对偏移codeOffif (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;}// 修正method->insns不是Native方法但是codeOff被app加固修改的情况下的codeOff// 此种情况的方法的dex文件在内存中被分开存放了,codeOff的相对偏移值超出了正常dex文件的内存范围if ((codeitem_off < start || codeitem_off > end) && codeitem_off != 0) {need_extra=true;// 设置类方法的codeOff值为total_pointer#############pData->directMethods[i].codeOff = total_pointer;// 获取类方法的信息结构体DexCodeDexCode *code = (DexCode*)((const u1*)method->insns-16);// item保存类方法的字节码指令集内存指针uint8_t *item=(uint8_t *) code;// 获取类方法的实际字节码的长度int code_item_len = 0;if (code->triesSize) {// 类方法中有Try/Catch语句存在的情况// 获取DexCode中的try/catch语句的hander数据信息const u1 * handler_data = dexGetCatchHandlerData(code);const u1** phandler=(const u1**)&handler_data;// 计算DexCode结构体中insns字节码指令集的结束地址偏移uint8_t * tail = codeitem_end(phandler);// 获取到类方法的DexCode中的字节码指令集的长度code_item_len = (int)(tail-item);}else{// 计算DexCode中的字节码指令集的长度code_item_len = 16+code->insnsSize*2;}ALOGI("GOT IT method code changed");// 将类方法的字节码指令集method->insns写入文件xxxx/extra中fwrite(item, 1, code_item_len, fp1);// 刷新文件流fflush(fp1);// 设置下一个method->insns的内存地址total_pointer+=code_item_len;// method->insns的4字节对齐问题while (total_pointer&3){// 写入0填充,处理4字节的对齐的问题fwrite(&padding, 1, 1, fp1);// 刷新文件流fflush(fp1);total_pointer++;}}} // directMethods looop over++++++++++++++++++++++++++++}// 获取类成员虚方法的method->insns字节码指令保存到文件xxxx/extraif (pData->virtualMethods) {for (uint32_t i=0; iheader.virtualMethodsSize; i++) //virtualMethods looop start========================{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;}// 类成员方法的codeOff被加固修改了,dex文件在内存中分成了2份if ((codeitem_off < start || codeitem_off > end) && codeitem_off!=0) {need_extra = true;// 将这部分类方法的DexCode数据保存到odex文件末尾的后面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");// 将这部分类方法的DexCode数据暂时保存到文件ata/data/xxxx.xxxx.xxx/extra中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++;}}//virtualMethods looop over========================}} /*
对于是Android系统(Landroid开头的)的类和空类,need_extra为 false 即不对 Landroid开头 的Android系统类和空类进行类实现数据DexClassData的收集,并设置这两种情况的类DexClassDef的成员变量 classDataOff 和 annotationsOff 的文件偏移值为 0 ,还有对于这两种情况的类,只保存 类定义的数据DexClassDef 到文件 /data/data/pakegname/classdef 中。这里还需要对标志 need_extra 和 pass 的意思进行说明一下,标志need_extra 的意思是是否保存类的实现数据 DexClassData 到文件/data/data/pakegname/extra 中;标志 pass的意思是 是否设置类定义DexClassDef 的成员变量 classDataOff 和 annotationsOff 的文件偏移值为 0,标志 need_extra 和 pass 的bool值总是相反的,其中pakegname为被脱壳Android应用的包名。
*/// 当android系统类或者当前类是无数据的空类的情况下,need_extra = false, pass = true
// 针对非android系统有效类的情况下,need_extra = true, pass = false
classdef:// 获取dex文件的第i个DexClassDef结构体的信息DexClassDef temp = *pClassDef;uint8_t *p = (uint8_t *)&temp;// 判断该类是否需要额外保存的类的数据DexClassData和类方法的DexCode数据信息// 这种情况下,需要保存DexClassData结构体的数据信息if (need_extra) {// 将类的DexCode和DexClassData数据信息保存到xxxx/extra文件中,保存格式如下:// |{[DexCode#DexCode....]DexClassData}|{[DexCode#DexCode....]DexClassData}|{[DexCode#DexCode....]DexClassData}...// xxxx/extra文件中DexCode的文件偏移保存在DexClassData结构体的pData->virtualMethods[i].codeOff和pData->directMethods[i].codeOff中// xxxx/extra文件中DexClassData的文件偏移保存在xxxx/classdef文件中的DexClassDef->classDataOff中ALOGI("GOT IT classdata before");int class_data_len = 0;// pData = ReadClassData(&data);// 将DexClassData结构体的数据信息leb128编码后保存到申请的内存空间中// out为指向DexClassData结构体的数据存放指针,class_data_len为DexClassData结构体数据的长度uint8_t *out = EncodeClassData(pData, class_data_len);if (!out) {// 保存到申请的内存中失败continue;}// 设置类的DexClassDef的成员classDataOff(指向DexClassData)的相对文件偏移temp.classDataOff = total_pointer;// 将类的DexClassData的信息写入xxxx/extra文件中fwrite(out, 1, class_data_len, fp1);// 刷新文件流fflush(fp1);// 更新文件偏移地址total_pointer += class_data_len;// 处理dex文件在内存中4字节对齐的问题while (total_pointer&3) {// 写入填充的数据0fwrite(&padding,1,1,fp1);// 刷新文件流fflush(fp1);total_pointer++;}free(out);ALOGI("GOT IT classdata written");}else{// pData = ReadClassData(&data);if (pData) {free(pData);}}// 对系统类或者当前类不是有效的类的情况,修改classDataOff和annotationsOff值为0if (pass) {// 设置类信息结构体的classDataOff和annotationsOff为0temp.classDataOff = 0;temp.annotationsOff = 0;}ALOGI("GOT IT classdef");// 将DexClassDef的第i个DexClassDef结构体的信息写入到文件xxxx/classdef中fwrite(p, sizeof(DexClassDef), 1, fp);// 刷新文件流fflush(fp);} //DexClassDef Loop over ----------// 关闭文件fclose(fp1);fclose(fp);strcpy(path, dumppath);// 拼接字符串strcat(path, "whole.dex");// 打开文件xxxx/whole.dexfp = 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");// 打开文件xxxx/part1fd = 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);// 将odex文件的开头到dex文件classDefsOff之间的文件数据写入到文件xxxx/whole.dex中fwrite(addr, 1, len, fp);// 刷新文件流fflush(fp);// 取消内存映射munmap(addr, len);// 关闭文件close(fd);// ******************************************strcpy(path, dumppath);// 拼接字符串strcat(path,"classdef");// 打开文件xxxx/classdeffd = 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);// 将addr中的数据写入到文件xxxx/whole.dex中fwrite(addr, 1, len, fp);// 刷新文件流fflush(fp);// 取消内存映射munmap(addr, len);// 关闭文件close(fd);// *********************************************strcpy(path, dumppath);// 拼接字符串strcat(path, "data");// 打开文件xxxx/datafd = 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);// 将内存odex文件中存放DexClassDef结构体以后的所有的文件数据写入到文件xxxx/whole.dex中fwrite(addr, 1, len, fp);// 刷新文件流fflush(fp);// 取消内存映射munmap(addr,len);// 关闭文件close(fd);// 4字节对齐导致的0填充while (inc > 0) {// 向文件中写入填充数据0fwrite(&padding, 1, 1, fp);// 刷新文件流fflush(fp);inc--;}// ******************************************strcpy(path,dumppath);// 拼接字符串strcat(path,"extra");// 打开文件xxxx/extrafd=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);// 将addr中的数据写入到文件xxxx/whole.dex中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----------------------///****
typedef struct DexOrJar {char* fileName; //被加载dex文件或者jar文件的路径字符串, 表示由Dalvik_dalvik_system_DexFile_openDexFile_bytearray打开bool isDex; //是dex文件还是jar包bool okayToFree;RawDexFile* pRawDexFile; //如果是dex,则指向dex内存区,由Dalvik_dalvik_system_DexFile_openDexFileNative打开JarFile* pJarFile; //如果是jar包,则指向JarFile结构,由Dalvik_dalvik_system_DexFile_openDexFileNative打开u1* pDexMemory; //指向dex内存区,由Dalvik_dalvik_system_DexFile_openDexFile_bytearray打开
} DexOrJar;
***/static void Dalvik_dalvik_system_DexFile_defineClassNative(const u4* args,JValue* pResult)
{StringObject* nameObj = (StringObject*) args[0];Object* loader = (Object*) args[1];int cookie = args[2];ClassObject* clazz = NULL;DexOrJar* pDexOrJar = (DexOrJar*) cookie;DvmDex* pDvmDex;char* name;char* descriptor;name = dvmCreateCstrFromString(nameObj);descriptor = dvmDotToDescriptor(name);ALOGV("--- Explicit class load '%s' l=%p c=0x%08x",descriptor, loader, cookie);free(name);if (!validateCookie(cookie))RETURN_VOID();if (pDexOrJar->isDex)pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);elsepDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);/* once we load something, we can't unmap the storage */pDexOrJar->okayToFree = false;//------------------------added begin----------------------//int uid = getuid();// 排除掉Android系统进程(因为系统进程的uid为0)if (uid) {if (readable) {// 创建互斥信号通量pthread_mutex_lock(&read_mutex);if (readable) {readable = false;// 释放互斥信号通量pthread_mutex_unlock(&read_mutex);pthread_t read_thread;// 创建线程,读取脱壳的配置文件/data/dexname以及等待定时器timerpthread_create(&read_thread, NULL, ReadThread, NULL);}else{// 释放互斥信号通量pthread_mutex_unlock(&read_mutex);}}}// 非Android系统进程且特征字符串不为空的情况下if(uid && strcmp(dexname, "")){// 判断当前进程的apk是否是需要被脱壳的apkchar * res = strstr(pDexOrJar->fileName, dexname);if (res&&flag) {// 创建互斥信号通量pthread_mutex_lock(&mutex);if (flag) {// 设置脱壳操作的开关为闭flag = false;// 释放互斥信号通量pthread_mutex_unlock(&mutex);// 获取内存中odex文件的结构信息DexFile* pDexFile = pDvmDex->pDexFile;// 获取odex文件在内存中的存放地址信息结构体MemMapping * mem = &pDvmDex->memMap;char * temp = new char[100];//-----------------第1步-----------------------// 获取当前进程apk的数据目录路径/data/data/xxxx/strcpy(temp, dumppath);// 拼接字符串strcat(temp, "part1");// 打开文件/data/data/xxxx/part1FILE *fp = fopen(temp, "wb+");// 获取odex文件在内存中的存放指针const u1 *addr = (const u1*)mem->addr;// 获取odex文件的开头到dex文件classDefsOff之间的文件数据的长度int length = int(pDexFile->baseAddr+pDexFile->pHeader->classDefsOff-addr);// 将这部分文件数据写入到文件/data/data/xxxx/part1中fwrite(addr,1,length,fp);// 刷新文件流fflush(fp);// 关闭文件fclose(fp);//-----------------第2步-----------------------// 获取当前进程apk的数据目录路径/data/data/xxxx/strcpy(temp, dumppath);// 拼接字符串strcat(temp,"data");// 打开文件/data/data/xxxx/datafp = fopen(temp, "wb+");// 获取内存中dex文件存放DexClassDef结构体的结束内存地址addr = pDexFile->baseAddr+pDexFile->pHeader->classDefsOff+sizeof(DexClassDef)*pDexFile->pHeader->classDefsSize;// 获取内存中odex文件存放DexClassDef结构体结尾到odex文件结尾的数据长度length = int((const u1*)mem->addr+mem->length-addr);// 将这部分数据写入到文件/data/data/xxxx/data中fwrite(addr, 1, length, fp);// 刷新文件流fflush(fp);// 关闭文件fclose(fp);// 删除申请的内存空间delete temp;//-----------------第3步-----------------------// 用于保存线程idpthread_t dumpthread;// 构建线程的传入参数param.loader = loader;param.pDvmDex = pDvmDex;// 创建线程进行类class的内存dumpdvmCreateInternalThread(&dumpthread, "ClassDumper", DumpClass, (void*)param); }else{// 释放互斥信号通量pthread_mutex_unlock(&mutex);}}}
//------------------------added end----------------------//clazz = dvmDefineClass(pDvmDex, descriptor, loader);Thread* self = dvmThreadSelf();if (dvmCheckException(self)) {/** If we threw a "class not found" exception, stifle it, since the* contract in the higher method says we simply return null if* the class is not found.*/Object* excep = dvmGetException(self);if (strcmp(excep->clazz->descriptor,"Ljava/lang/ClassNotFoundException;") == 0 ||strcmp(excep->clazz->descriptor,"Ljava/lang/NoClassDefFoundError;") == 0){dvmClearException(self);}clazz = NULL;}free(descriptor);RETURN_PTR(clazz);
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
