查看原文
其他

Dex文件结构学习

iio 看雪学院 2021-03-07

本文为看雪论坛优秀文章
看雪论坛作者ID:iio

目 录

一、DexHeader

       1. 理解DexHeader

       2. 解析DexHeader

二、DexStringId

       1. 理解DexStringId

       2. 解析DexStringId

三、DexTypeId

       1. 理解DexTypeId

       2. 解析DexTypeId

四、DexFieldId

       1. 理解DexFieldId

       2. 解析DexFieldId

五、DexMethodId

       1. 理解DexMethodId

       2. 解析DexMethodId

六、DexProtoId

       1. 理解DexProtoId

       2. 解析DexProtoId

七、DexClassDef

       1. 理解DexClassDef

       2. 理解DexClassDef

八、DexLink

       待续....狗命要紧

最近在学习dex文件,把学习的过程记录一下!有什么不对的地方还各位多多指点,感激不尽!



一、DexHeader


>>>>

1. 理解DexHeader


随便拖入一个Dex文件到010,运行DEXTemplate.bt,就能看到下图中的Dex的结果。



接下开始逐个分析Dex文件结构,我用是android-6.0.0_r1的源码中DexFile.h:


通过上面的源码的注释知道了各个结构的大意与010的对应的关系,但是还不清楚这些结构的内部是什么样子的!

那么开始解析第一个结构 DexHeader:

/* * Direct-mapped "header_item" struct. */struct DexHeader { u1 magic[8]; //取值必须是字符串 "dex\n035\0" 或者字节byte数组 {0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00} u4 checksum; //文件内容的校验和,不包括magic和自己,主要用于检查文件是否损坏 u1 signature[kSHA1DigestLen]; //签名信息,不包括 magic\checksum和自己 u4 fileSize; //整个文件的长度,单位为字节,包括所有的内容 u4 headerSize; //默认是0x70个字节 u4 endianTag; //大小端标签,标准.dex文件为小端,此项一般固定为0x12345678常量 u4 linkSize; //链接数据的大小 u4 linkOff; //链接数据的偏移值 u4 mapOff; //map item的偏移地址,该item属于data区里的内容,值要大于等于dataOff的大小 u4 stringIdsSize; //DEX中用到的所有字符串内容的大小* u4 stringIdsOff; //DEX中用到的所有字符串内容的偏移量 u4 typeIdsSize; //DEX中类型数据结构的大小 u4 typeIdsOff; //DEX中类型数据结构的偏移值 u4 protoIdsSize; //DEX中的元数据信息数据结构的大小 u4 protoIdsOff; //DEX中的元数据信息数据结构的偏移值 u4 fieldIdsSize; //DEX中字段信息数据结构的大小 u4 fieldIdsOff; //DEX中字段信息数据结构的偏移值 u4 methodIdsSize; //DEX中方法信息数据结构的大小 u4 methodIdsOff; //DEX中方法信息数据结构的偏移值 u4 classDefsSize; //DEX中的类信息数据结构的大小 u4 classDefsOff; //DEX中的类信息数据结构的偏移值 u4 dataSize; //DEX中数据区域的结构信息的大小 u4 dataOff; //DEX中数据区域的结构信息的偏移值};

看010中对应的位置:


  u1  magic[8];       //取值必须是字符串 "dex\n035\0" 或者字节byte数组 {0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00}

下图选中magic时 会选中前八个字节magic[8]  u1代表字节 [8] 则是8个字节。

可以把magic看做dex文件的标识{0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00}

通过这个字段知道是该文件dex文件:



它是一个u4 代表4个字节的内容。

u4 checksum; //文件内容的校验和,不包括magic和自己,主要用于检查文件是否损坏



signature[kSHA1DigestLen]了,signature字段用于检验dex文件,其实就是把整个dex文件用SHA-1签名得到的一个值。这里占用20个字节。

u1 signature[kSHA1DigestLen]; //签名信息,不包括 magic\checksum和自己

kSHA1DigestLen:

/* * 160-bit SHA-1 digest. */enum { kSHA1DigestLen = 20, kSHA1DigestOutputLen = kSHA1DigestLen*2 +1 };

fileSize:文件长度:

u4 fileSize; //整个文件的长度,单位为字节,包括所有的内容



553820个字节大小。

表示 DexHeader 头结构的大小,占用4个字节。这里可以看到它一共占用了112个字节,112对应的16进制数为70h:

u4 headerSize; //默认是0x70个字节



 u4  endianTag;      //大小端标签,标准.dex文件为小端,此项一般固定为0x12345678常量


当多个class文件被编译到一个dex文件是,他们会用到link_size和link_off,通常为0。

u4 linkSize; //链接数据的大小 u4 linkOff; //链接数据的偏移值

mapOff字段了,它指定了 DexMapList 的文件偏移:

u4 mapOff; //map item的偏移地址,该item属于data区里的内容,值要大于等于dataOff的大小

stringIdsSize代表全dex文件的字符串的个数
stringIdsOff 代表全dex文件的字符串的偏移

u4 stringIdsSize; //DEX中用到的所有字符串内容的大小* u4 stringIdsOff; //DEX中用到的所有字符串内容的偏移量



有了这个知识 可以尝试分析第一个字符串:



已经找到第一个字符串的偏移是6e72c:


那么也就找到了第一个字符串的值,虽然看不明显但确实如此,以上的2个问题会在解析中给出答案

这指出了type类型的偏移和type的个数:

  u4  typeIdsSize;        //DEX中类型数据结构的大小 u4 typeIdsOff; //DEX中类型数据结构的偏移值

略...

u4 protoIdsSize; //DEX中的元数据信息数据结构的大小 u4 protoIdsOff; //DEX中的元数据信息数据结构的偏移值 u4 fieldIdsSize; //DEX中字段信息数据结构的大小 u4 fieldIdsOff; //DEX中字段信息数据结构的偏移值 u4 methodIdsSize; //DEX中方法信息数据结构的大小 u4 methodIdsOff; //DEX中方法信息数据结构的偏移值 u4 classDefsSize; //DEX中的类信息数据结构的大小 u4 classDefsOff; //DEX中的类信息数据结构的偏移值 u4 dataSize; //DEX中数据区域的结构信息的大小 u4 dataOff; //DEX中数据区域的结构信息的偏移值

>>>>

2. 解析DexHeader


那么就开始动手写的代码来解析吧~~用的Clion编译器:

首先 先写个一个读取文件的方法char* Read_File(string file_Path) { filebuf *pbuf; ifstream filestr; long size; char *buffer; // 要读入整个文件,必须采用二进制打开 filestr.open(file_Path, ios::binary); // 获取filestr对应buffer对象的指针 pbuf = filestr.rdbuf(); // 调用buffer对象方法获取文件大小 size = pbuf->pubseekoff(0, ios::end, ios::in); pbuf->pubseekpos(0, ios::in); // 分配内存空间 buffer = new char[size]; // 获取文件内容 pbuf->sgetn(buffer, size); filestr.close(); // 输出到标准输出 //cout.write (buffer,size); // delete []buffer; return buffer;}

main: //得到文件内存地址指针 char* fp = Read_File("/home/zlq/baidunetdiskdownload/android/classes.dex"); //强转为DexFile指针 DexFile *dex = (DexFile*)&fp; //解析struct header_item dex_header Read_DexHeader(dex->pHeader);

写的比较拙劣。

/** * 实现方法 * @param dexHeader */void Read_DexHeader(DexHeader* dexHeader){ //struct header_item dex_header printf("struct header_item dex_header\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t0x0"); printf("\t\t\t\t0x%x", (unsigned int)sizeof(DexHeader)); printf("\t\tDEX 文件头,记录了一些当前文件的信息以及其他数据结构在文件中的偏移量"); printf("\n\tmagic\t\t\t\t\t\t\t\t"); char* magic = (char*)dexHeader->magic; while(*magic != '\0') { if(*magic != '\n') { printf("%c ",(*magic)); } magic++; } printf("\t\t\t\t\t\t\t\t\t\t0x0\t\t\t\t0x%x\t\t\t取值必须是字符串 \"dex\\n035\\0\" 或者字节byte数组 {0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00}", (unsigned int)sizeof(dexHeader->magic)); printf("\n\tchecksum\t\t\t\t\t\t\t0x%X\t\t\t\t\t\t\t\t\t\t\t0x%x\t\t\t\t0x%x\t\t\t文件内容的校验和,不包括magic和自己,主要用于检查文件是否损坏" ,dexHeader->checksum ,(unsigned int)sizeof(dexHeader->magic) ,(unsigned int)sizeof(dexHeader->checksum)); .... 略

输出:



通过对比分析是一样的,这个比较简单,用结构指针接受一下就行了,只是输出有点麻烦。



二、DexStringId


>>>>

1. 理解DexStringId


 DEX中所有的字符串存储在这里:

 DexStringId*  pStringIds;     //数组,元素类型为string_id_item,存储字符串相关的信息

进去看看此结构。

它只有一个字段,那么它是如何解析dex字符串呢?

/* * Direct-mapped "string_id_item". */struct DexStringId { //用于指明 string_data_item 位置文件的位置 u4 stringDataOff; /* file offset to string_data_item */};

首先要知道的是它是一个数组类型,数组的大小取决于:

  DexHeader->stringIdsSize

那么就很好理解了,上面提示 一共 有3619个字符串,解析主要靠DexHeader中的stringIdsSize和stringIdsOff来解析。


>>>>

2. 解析DexStringId


又开始写代码了:

void Read_DexStringId(char* fp) { DexFile *dex = (DexFile*)&fp; printf("\n\nstruct string_id_list dex_string_ids\t%d\t\t\t\t\t\t\t\t\t\t\t\t0x%x", dex->pHeader->stringIdsSize,(unsigned int) sizeof(DexHeader)); printf("\t\t\t0x%x", (unsigned int) sizeof(DexStringId)); printf("\t\t\t数组,元素类型为string_id_item,存储字符串相关的信息"); //拿到字符串的偏移 int *p2 = (int*)(fp + dex->pHeader->stringIdsOff); ////stringIdsSize --> DEX中用到的所有字符串内容的大小 for (int i = 0; i < dex->pHeader->stringIdsSize; ++i) { printf("\n\tstruct string_id_item string_id[%d]:",i); // 文件首地址+字符串偏移就是字符串存放的位置的第一个数组 const u1* stringdata = (u1*)fp+*p2; //解码 /* * dex文件里采用了变长方式表示字符串长度。一个字符串的长度可能是一个字节(小于256)或者4个字节(1G大小以上)。字符串的长度大多数都是小于 256个字节,因此需要使用一种编码,既可以表示一个字节的长度,也可以表示4个字节的长度,并且1个字节的长度占绝大多数。能满足这种表示的编码方式有很多,但dex文件里采用的是uleb128方式。leb128编码是一种变长编码,每个字节采用7位来表达原来的数据,最高位用来表示是否有后继字节。若第一个 Byte 的最高位为 1 ,则表示还需要下一个 Byte 来描述 ,直至最后一个 Byte 的最高位为 0 。每个 Byte 的其余 Bit 用来表示数据 */ readUnsignedLeb128(&stringdata); //转码后该字符串为 内存数据为 \0 时 则结束 while(*stringdata != '\0') { //换行 if((*stringdata) != '\a' && (*stringdata) != '\b' &&(*stringdata) != '\t' && (*stringdata) != '\n' && (*stringdata) != '\v' && (*stringdata) != '\r' && (*stringdata) != '\f' ) { printf("%c",(*stringdata)); } //printf("%c",(*stringdata)); stringdata++; } p2++; }}

其中一部分的输出:



010:
 

总结:

* dex->pHeader->stringIdsOff 存储了整个dex的字符串的偏移的首地址 是一个数组类型 * dex->pHeader->stringIdsSize 存储字符串的个数 * 字符串偏移指向的地址的内容通过 readUnsignedLeb128 解码 字符串的长度通过判断'\0' 即可

编码:
LEB128即"Little-Endian Base 128",基于128的小印第安序编码格式,是对任意有符号或者无符号整型数的可变长度的编码。

也即,用LEB128编码的正数,会根据数字的大小改变所占字节数。在android的.dex文件中,他只用来编码32bits的整型数。

格式:


图只是指示性的用两个字节表示。编码的每个字节有效部分只有低7bits,每个字节的最高bit用来指示是否是最后一个字节。

非最高字节的bit7为0,最高字节的bit7为1。

将leb128编码的数字转换为可读数字的规则是:除去每个字节的bit7,将每个字节剩余的7个bits拼接在一起,即为数字。

比如:
LEB128编码的0x02b0 ---> 转换后的数字0x0130

转换过程:
0x02b0 ---> 0000 0010 1011 0000 -->去除最高位--> 000 0010 011 0000 -->按4bits重排 --> 00 0001 0011 0000 --> 0x130
引用 https://blog.csdn.net/Roland_Sun/article/details/46708061


三、DexTypeId


>>>>

1. 理解DexTypeId


type_ids 区索引了 .dex 文件里的所有数据类型 ,包括 class 类型 ,数组类型(array types)和基本类型(primitive types) 。 本区域里的元素格式为type_ids_item。

type_ids_item 里面 descriptor_idx 的值的意思 ,是 string_ids 里的 index 序号 ,是用来描述此type 的字符串。

  DexTypeId*    pTypeIds;       //数组,存储类型相关的信息

看结构,他也是一个字段,且看如何分析它

注意是一个数组的类型:

/* * Direct-mapped "type_id_item". */struct DexTypeId { u4 descriptorIdx; /* 指向string_ids的索引 */};

同时 还有2个很重要的字段在DexHeader中typeIdsSize和typeIdsOff 同样代表类型的偏移和大小。

先来分析第一个type:



得知第一个type所在位置是 00 00 38 FC。

010:



得到的值是 0x19F
根据描述   /* 指向string_ids的索引 */   知道这个值是字符串偏移的索引。

0x19F = 415



恰好是第一个类型的值 : B byte

如此就分析出第一个类型的位置和来源了。

   

>>>>

2. 解析DexTypeId 


又开始写代码:

void Read_DexTypeId(char* fp){ DexFile *dex = (DexFile*)&fp; printf("\n\nstruct struct type_id_list dex_type_ids\t%d\t\t\t\t\t\t\t\t\t\t\t\t0x%x", dex->pHeader->typeIdsSize,(unsigned int) sizeof(DexHeader)); printf("\t\t\t0x%x", (unsigned int) sizeof(DexTypeId)); printf("\t\t\t数组,存储类型相关的信息\n"); //拿到类型的偏移 int *typeIdsOff = (int*)(fp + dex->pHeader->typeIdsOff); for (int i = 0; i < dex->pHeader->typeIdsSize; ++i) { printf("\n\tstruct type_id_list dex_type_ids[%d]:",i); //先拿到字符串的偏移 int *stringIdsOff = (int*)(fp + dex->pHeader->stringIdsOff); //再根据类型的偏移去寻找字符串 const u1* stringdata = (u1*)fp+stringIdsOff[(*typeIdsOff)]; //解码 readUnsignedLeb128(&stringdata); while(*stringdata != '\0') { printf("%c",(*stringdata)); stringdata++; } //自加 获取下一个指针 int型指针自加 base += 1 typeIdsOff++; }}

输出:



010:



总结:

descriptor_idx 的值的意思 ,是 string_ids 里的 index 序号 ,是用来描述此type 的字符串 字符串指针[descriptor_idx地址内的值] = descriptor_idx值

四、DexFieldId


>>>>

1. 理解DexFieldId


大意:

 DexFieldId*   pFieldIds;      //数组,存储成员变量信息,包括变量名和类型等

具体结构:

struct DexFieldId { u2 classIdx; /* field所属的class类型,class_idx的值时type_ids的一个index,指向所属的类 */ u2 typeIdx; /* field的类型,值是type_ids的一个index */ u4 nameIdx; /* field的名称,它的值是string_ids的一个index */};

仍然从header看起 重要的数据还是保存在头部。

继续从010中尝试分析:



那么来到 824C:



之前分析过type 这次 分析结构中的 type_idx,上图可见 type_idx的地址的值内容为 0x1D 也就是 29。可以看到他们的值是一致的!

另外2个原理一直,可以自行分析。


截图截多了!不好意思!

>>>>

2. 解析DexFieldId


又开始写代码。

比较麻烦的是取值的过程需要通过其他属性来取值。

void Read_DexFieldId(char* fp){ DexFile *dex = (DexFile*)&fp; printf("\n\nstruct field_id_list dex_field_ids\t%d\t\t\t\t\t\t\t\t\t\t\t\t0x%x", dex->pHeader->fieldIdsSize,(unsigned int) sizeof(DexHeader)); printf("\t\t\t0x%x", (unsigned int) sizeof(DexFieldId)); printf("\t\t\t数组,存储类的相关的信息"); //转为DexFieldId结构体 DexFieldId *dexFieldId = (DexFieldId*)(fp + dex->pHeader->fieldIdsOff); for (int i = 0; i < dex->pHeader->fieldIdsSize; ++i) { printf("\n\tstruct field_id_item field_id[%d]",i); /** * 重点: * 要想找到field_id的typeIdx对应位置的值 要先找到 typeIdsOff偏移地址+typeIdx等于string_ids的索引,再通过string_ids来寻找值 * * [类型偏移首地址+typeIdx索引] = 字符串所在的索引位置 * 字符串偏移首地址[字符串所在的索引位置] = [类型偏移首地址+typeIdx索引]的具体值 */ //拿到类型的偏移首地址 int *typeIdsOff = (int*)(fp + dex->pHeader->typeIdsOff); //拿到字符串的偏移首地址 int *stringIdsOff = (int*)(fp + dex->pHeader->stringIdsOff); //类型偏移地址+索引=字符串索引 //字符串偏移+字符串索引=typeIdx位置具体的值 const u1* typeIdx_stringdata = (u1*)fp+stringIdsOff[(*(typeIdsOff+dexFieldId->typeIdx))]; //解码 readUnsignedLeb128(&typeIdx_stringdata); printf("\n\t\ttypeIdx --> "); while(*typeIdx_stringdata != '\0') { printf("%c",(*typeIdx_stringdata)); typeIdx_stringdata++; } //u2 classIdx; /* field所属的class类型,class_idx的值时type_ids的一个index,指向所属的类 */ const u1* classIdx_stringdata = (u1*)fp+stringIdsOff[(*(typeIdsOff+dexFieldId->classIdx))]; //解码 readUnsignedLeb128(&classIdx_stringdata); printf("\n\t\tclassIdx--> "); while(*classIdx_stringdata != '\0') { printf("%c",(*classIdx_stringdata)); classIdx_stringdata++; } //u4 nameIdx; /* field的名称,它的值是string_ids的一个index */ const u1* nameIdx_stringdata = (u1*)fp+stringIdsOff[(dexFieldId->nameIdx)]; //解码 readUnsignedLeb128(&nameIdx_stringdata); printf("\n\t\tnameIdx --> "); while(*nameIdx_stringdata != '\0') { printf("%c",(*nameIdx_stringdata)); nameIdx_stringdata++; } dexFieldId++; }}

输出:



010:




五、DexMethodId


 
>>>>

1. 理解DexMethodId 

   
DexMethodId* pMethodIds; //数组,存储成员函数信息包括函数名 参数和返回值类型<br>

查看具体结构。

描述的比较清晰:

struct DexMethodId { u2 classIdx; /* method所属的class类型,class_idx的值是type_ids的一个index,必须指向一个class类型 */ u2 protoIdx; /* method的原型,指向proto_ids的一个index */ u4 nameIdx; /* method的名称,值为string_ids的一个index */};

可以自己尝试手动分析了,这里就不过多的演示了..之前演示很多次了。

>>>>

2. 解析DexMethodId


又开始写代码了:

void Read_DexMethodId(char* fp){ DexFile *dex = (DexFile*)&fp; printf("\n\nstruct method_id_list dex_method_ids\t%d\t\t\t\t\t\t\t\t\t\t\t\t0x%x", dex->pHeader->methodIdsSize,(unsigned int) sizeof(DexHeader)); printf("\t\t\t0x%x", (unsigned int) sizeof(DexMethodId)); printf("\t\t\t数组,存储成员函数信息包括函数名 参数和返回值类型"); //转为DexMethodId结构体 DexMethodId *dexMethodId = (DexMethodId*)(fp + dex->pHeader->methodIdsOff); for (int i = 0; i < dex->pHeader->methodIdsSize; ++i) { printf("\n\tstruct method_id_item method_id[%d]", i); int *typeIdsOff = (int*)(fp + dex->pHeader->typeIdsOff); int *protoIdsOff = (int*)(fp + dex->pHeader->protoIdsOff); int *stringIdsOff = (int*)(fp + dex->pHeader->stringIdsOff); const u1* classIdx_stringdata = (u1*)fp+stringIdsOff[(*(typeIdsOff+dexMethodId->classIdx))]; //解码 readUnsignedLeb128(&classIdx_stringdata); printf("\n\t\tclassIdx--> "); while(*classIdx_stringdata != '\0') { printf("%c",(*classIdx_stringdata)); classIdx_stringdata++; } //method的原型,指向proto_ids的一个index DexProtoId* dexProtoId = (DexProtoId*) protoIdsOff+dexMethodId->protoIdx; const u1* protoIdx_stringdata = (u1*)fp+stringIdsOff[(*(typeIdsOff+dexProtoId->returnTypeIdx))]; readUnsignedLeb128(&protoIdx_stringdata); printf("\n\t\tprotoIdx--> "); while(*protoIdx_stringdata != '\0') { printf("%c",(*protoIdx_stringdata)); protoIdx_stringdata++; } const u1* nameIdx_stringdata = (u1*)fp+stringIdsOff[dexMethodId->nameIdx]; //解码 readUnsignedLeb128(&nameIdx_stringdata); printf("\n\t\tnameIdx --> "); while(*nameIdx_stringdata != '\0') { printf("%c",(*nameIdx_stringdata)); nameIdx_stringdata++; } dexMethodId++; }}

输出:



010:




六、DexProtoId


>>>>

1. 理解DexProtoId


  DexProtoId*   pProtoIds;      //数组,函数原型数据索引,记录了方法声明的字符串,返回类型和参数列表<br>

具体结构:

struct DexProtoId { u4 shortyIdx; /* 值为一个string_ids的index号,用来说明该method原型 */ u4 returnTypeIdx; /* 值为一个type_ids的index,表示该method原型的返回值类型 */ u4 parametersOff; /* 指定method原型的参数列表type_list,若method没有参数,则值为0. 参数的格式是type_list */};

>>>>

2. 解析DexProtoId


void Read_DexProtoId(char* fp){ DexFile *dex = (DexFile*)&fp; printf("\n\nstruct proto_id_list dex_proto_ids\t%d\t\t\t\t\t\t\t\t\t\t\t\t0x%x", dex->pHeader->protoIdsSize,(unsigned int) sizeof(DexHeader)); printf("\t\t\t0x%x", (unsigned int) sizeof(DexMethodId)); printf("\t\t\t数组,函数原型数据索引,记录了方法声明的字符串,返回类型和参数列表"); //转为DexMethodId结构体 DexProtoId *dexProtoId = (DexProtoId*)(fp + dex->pHeader->protoIdsOff); for (int i = 0; i < dex->pHeader->protoIdsSize; ++i) { printf("\n\tstruct proto_id_item proto_id[%d]", i); int *typeIdsOff = (int*)(fp + dex->pHeader->typeIdsOff); int *stringIdsOff = (int*)(fp + dex->pHeader->stringIdsOff); const u1* shortyIdx_stringdata = (u1*)fp+stringIdsOff[dexProtoId->shortyIdx]; //解码 readUnsignedLeb128(&shortyIdx_stringdata); printf("\n\t\tshortyIdx--> "); while(*shortyIdx_stringdata != '\0') { printf("%c",(*shortyIdx_stringdata)); shortyIdx_stringdata++; } const u1* returnTypeIdx_stringdata = (u1*)fp+stringIdsOff[(*(typeIdsOff+dexProtoId->returnTypeIdx))]; readUnsignedLeb128(&returnTypeIdx_stringdata); printf("\n\t\tprotoIdx--> "); while(*returnTypeIdx_stringdata != '\0') { printf("%c",(*returnTypeIdx_stringdata)); returnTypeIdx_stringdata++; } printf("\n\t\tparametersOff--> %d",dexProtoId->parametersOff); dexProtoId++; }}

输出:



010:




七、DexClassDef


>>>>

1. 理解DexClassDef


  DexClassDef*  pClassDefs;     //数组,存储类的信息

结构体:

struct DexClassDef { u4 classIdx; /* 描述具体的class类型,值是type_ids的一个index,值必须是一个class类型,不能是数组或者基本类型 */ u4 accessFlags; /* 描述class的访问类型,如public,final,static等 */ u4 superclassIdx; /* 描述父类的类型,值必须是一个class类型,不能是数组雷兴国或者基本类型 */ u4 interfacesOff; /* 值为偏移地址,被指向的数据结构为type_list,class若没有interfaces,值为0 */ u4 sourceFileIdx; /* 表示源代码文件的信息,值为string_ids的一个index。若此项信息丢失,此项赋值为NO_INDEX=0xFFFFFFFF */ u4 annotationsOff; /* 值为偏移地址,指向的内容是该class的注解,位置在data区,格式为annotations_directory_item,若没有此项,值为0 */ u4 classDataOff; /* 值为偏移地址,指向的内容是该class的使用到的数据,位置在data区,格式为class_data_item。无偶没有此项,则值为0 */ u4 staticValuesOff; /* 值为偏移地址,指向data区里的一个列表,格式为encoded_array_item。若没有此项,值为0. */};

解析:

void Read_DexClassDef(char* fp){ //u4 classIdx; /* 描述具体的class类型,值是type_ids的一个index,值必须是一个class类型,不能是数组或者基本类型 */ //u4 accessFlags; /* 描述class的访问类型,如public,final,static等 */ //u4 superclassIdx; /* 描述父类的类型,值必须是一个class类型,不能是数组或者基本类型 */ //u4 interfacesOff; /* 值为偏移地址,被指向的数据结构为type_list,class若没有interfaces,值为0 */ //u4 sourceFileIdx; /* 表示源代码文件的信息,值为string_ids的一个index。若此项信息丢失,此项赋值为NO_INDEX=0xFFFFFFFF */ //u4 annotationsOff; /* 值为偏移地址,指向的内容是该class的注解,位置在data区,格式为annotations_directory_item,若没有此项,值为0 */ //u4 classDataOff; /* 值为偏移地址,指向的内容是该class的使用到的数据,位置在data区,格式为class_data_item。无偶没有此项,则值为0 */ //u4 staticValuesOff; /* 值为偏移地址,指向data区里的一个列表,格式为encoded_array_item。若没有此项,值为0. */ DexFile *dex = (DexFile*)&fp; printf("\n\nstruct class_def_item_list dex_class_defs\t%d\t\t\t\t\t\t\t\t\t\t\t\t0x%x", dex->pHeader->classDefsSize,(unsigned int) sizeof(DexHeader)); printf("\t\t\t0x%x", (unsigned int) sizeof(DexMethodId)); printf("\t\t\t数组,存储类的信息"); //转为DexMethodId结构体 DexClassDef *dexClassDef = (DexClassDef*)(fp + dex->pHeader->classDefsOff); for (int i = 0; i < dex->pHeader->classDefsSize; ++i) { printf("\n\tstruct class_def_item class_def[%d]", i); int *typeIdsOff = (int*)(fp + dex->pHeader->typeIdsOff); int *stringIdsOff = (int*)(fp + dex->pHeader->stringIdsOff); const u1* classIdx_stringdata = (u1*)fp+stringIdsOff[(*(typeIdsOff+dexClassDef->classIdx))]; //解码 readUnsignedLeb128(&classIdx_stringdata); printf("\n\t\tclassIdx\t\t--> "); while(*classIdx_stringdata != '\0') { printf("%c",(*classIdx_stringdata)); classIdx_stringdata++; } printf("\n\t\taccessFlags\t\t-->\t%x",dexClassDef->accessFlags); //通过类型去寻找 const u1* superclassIdx_stringdata = (u1*)fp+stringIdsOff[(*(typeIdsOff+dexClassDef->superclassIdx))]; //解码 readUnsignedLeb128(&superclassIdx_stringdata); printf("\n\t\tsuperclassIdx\t--> "); while(*superclassIdx_stringdata != '\0') { printf("%c",(*superclassIdx_stringdata)); superclassIdx_stringdata++; } printf("\n\t\tinterfacesOff\t-->\t%d",dexClassDef->interfacesOff); if (dexClassDef->sourceFileIdx == -1) { printf("\n\t\tsourceFileIdx\t-->\tNO_INDEX"); } printf("\n\t\tannotationsOff\t-->\t%d",dexClassDef->annotationsOff); printf("\n\t\tclassDataOff\t-->\t%d",dexClassDef->classDataOff); printf("\n\t\tstaticValuesOff\t-->\t%d",dexClassDef->staticValuesOff); dexClassDef++; }}

输出:



010:


代码写的比较乱,在这次学习中收获挺多的..

各位大神看完了有什么错的那啥,轻点。



- End -




看雪ID:iio

https://bbs.pediy.com/user-816237.htm 

*本文由看雪论坛 iio 原创,转载请注明来自看雪社区。



推荐文章++++

CVE-2017-11882理论以及实战样本分析

恶意代码分析之 RC4 算法学习

CVE-2017-0101-Win32k提权分析笔记

ROPEmporium全解

实战栈溢出漏洞


好书推荐





公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com

“阅读原文”一起来充电吧!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存