首页
社区
课程
招聘
[原创]Dex文件结构学习,顺便还写了一个解析Dex文件的小程序.
发表于: 2019-11-1 02:26 10890

[原创]Dex文件结构学习,顺便还写了一个解析Dex文件的小程序.

2019-11-1 02:26
10890

       2.0、理解DexStringId

       2.0、解析DexStringId

       3.0、理解DexTypeId

       3.0、解析DexTypeId

       4.0、理解DexFieldId

       4.0、解析DexFieldId

 5、DexMethodId

       5.0、理解DexMethodId

       5.0、解析DexMethodId

       6.0、理解DexProtoId

       6.0、解析DexProtoId

       7.0、理解DexClassDef

       7.0、理解DexClassDef

       8.待续....狗命要紧

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

格式:


图只是指示性的用两个字节表示。编码的每个字节有效部分只有低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




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

先来分析第一个type:



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

010


得到的值是 0x19F

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


0x19F = 415




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

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


010:



总结:



那么来到 824C



之前分析过type 这次 分析结构中的 type_idx

上图可见 type_idx的地址的值内容为 0x1D 也就是 29

可以看到 他们的值是一致的!

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

截图截多了!不好意思!



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



输出


010




   

描述的比较清晰



输出:



010






具体结构


010:



010:


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

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

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

目录:
 1、DexHeader
       1.0、理解DexHeader
       1.1、解析DexHeader
 2、 DexStringId

       2.0、理解DexStringId

       2.0、解析DexStringId

 3、DexTypeId

       3.0、理解DexTypeId

       3.0、解析DexTypeId

 4、DexFieldId

       4.0、理解DexFieldId

       4.0、解析DexFieldId

 5、DexMethodId

       5.0、理解DexMethodId

       5.0、解析DexMethodId

 6、DexProtoId

       6.0、理解DexProtoId

       6.0、解析DexProtoId

 7、DexClassDef

       7.0、理解DexClassDef

       7.0、理解DexClassDef

 8、DexLink

       8.待续....狗命要紧



 1、DexHeader

       1.0、理解DexHeader


       1.0、理解DexHeader



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


接下开始逐个分析Dex文件结构
我用是的android-6.0.0_r1的源码中DexFile.h
/*
 * Structure representing a DEX file.
 *
 * Code should regard DexFile as opaque, using the API calls provided here
 * to access specific structures.
 */
struct DexFile {
    directly-mapped "opt" header */
    //const DexOptHeader* pOptHeader;

    /*
        对应关系如下
        DexHeader*    pHeader   ---->struct header_item dex_header
        DexStringId*  pStringIds---->struct string_id_list dex_string_ids
        DexTypeId*    pTypeIds  ---->struct type_id_list dex_type_ids
        DexFieldId*   pFieldIds ---->struct field_id_list dex_field_ids
        DexMethodId*  pMethodIds---->struct method_id_list dex_method_ids
        DexProtoId*   pProtoIds ---->struct proto_id_list dex_proto_ids
        DexClassDef*  pClassDefs---->struct class_def_item_list dex_class_defs
        DexLink*      pLinkData ---->struct map_list_type dex_map_list
     */

    /* pointers to directly-mapped structs and arrays in base DEX */
     DexHeader*    pHeader;        //DEX 文件头,记录了一些当前文件的信息以及其他数据结构在文件中的偏移量
     DexStringId*  pStringIds;     //数组,元素类型为string_id_item,存储字符串相关的信息
     DexTypeId*    pTypeIds;       //数组,存储类型相关的信息
     DexFieldId*   pFieldIds;      //数组,存储成员变量信息,包括变量名和类型等
     DexMethodId*  pMethodIds;     //数组,存储成员函数信息包括函数名 参数和返回值类型
     DexProtoId*   pProtoIds;      //数组,函数原型数据索引,记录了方法声明的字符串,返回类型和参数列表
     DexClassDef*  pClassDefs;     //数组,存储类的信息
     DexLink*      pLinkData;      //Dex文件重要的数据内容都存在data区域内,一些数据结构会通过如 xx_off这样的成员变量只想文件的某个位置,从该位置开始,存储了对应的数据结构的内容,而xx_off的位置一般落在data区域内

    /*
     * These are mapped out of the "auxillary" section, and may not be
     * included in the file.
     */
    const DexClassLookup* pClassLookup;
    const void*         pRegisterMapPool;       // RegisterMapClassPool

    /* points to start of DEX file data */
    const u1*           baseAddr;

    /* track memory overhead for auxillary structures */
    int                 overhead;

    /* additional app-specific data structures associated with the DEX */
    //void*               auxData;
};

/*
 * Structure representing a DEX file.
 *
 * Code should regard DexFile as opaque, using the API calls provided here
 * to access specific structures.
 */
struct DexFile {
    directly-mapped "opt" header */
    //const DexOptHeader* pOptHeader;

    /*
        对应关系如下
        DexHeader*    pHeader   ---->struct header_item dex_header
        DexStringId*  pStringIds---->struct string_id_list dex_string_ids
        DexTypeId*    pTypeIds  ---->struct type_id_list dex_type_ids
        DexFieldId*   pFieldIds ---->struct field_id_list dex_field_ids
        DexMethodId*  pMethodIds---->struct method_id_list dex_method_ids
        DexProtoId*   pProtoIds ---->struct proto_id_list dex_proto_ids
        DexClassDef*  pClassDefs---->struct class_def_item_list dex_class_defs
        DexLink*      pLinkData ---->struct map_list_type dex_map_list
     */

    /* pointers to directly-mapped structs and arrays in base DEX */
     DexHeader*    pHeader;        //DEX 文件头,记录了一些当前文件的信息以及其他数据结构在文件中的偏移量
     DexStringId*  pStringIds;     //数组,元素类型为string_id_item,存储字符串相关的信息
     DexTypeId*    pTypeIds;       //数组,存储类型相关的信息
     DexFieldId*   pFieldIds;      //数组,存储成员变量信息,包括变量名和类型等
     DexMethodId*  pMethodIds;     //数组,存储成员函数信息包括函数名 参数和返回值类型
     DexProtoId*   pProtoIds;      //数组,函数原型数据索引,记录了方法声明的字符串,返回类型和参数列表
     DexClassDef*  pClassDefs;     //数组,存储类的信息
     DexLink*      pLinkData;      //Dex文件重要的数据内容都存在data区域内,一些数据结构会通过如 xx_off这样的成员变量只想文件的某个位置,从该位置开始,存储了对应的数据结构的内容,而xx_off的位置一般落在data区域内

    /*
     * These are mapped out of the "auxillary" section, and may not be
     * included in the file.
     */
    const DexClassLookup* pClassLookup;
    const void*         pRegisterMapPool;       // RegisterMapClassPool

    /* points to start of DEX file data */
    const u1*           baseAddr;

    /* track memory overhead for auxillary structures */
    int                 overhead;

    /* additional app-specific data structures associated with the DEX */
    //void*               auxData;
};



通过上面的源码的注释知道了各个结构的大意与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}
 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和自己,主要用于检查文件是否损坏

  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常量
0

当多个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中数据区域的结构信息的偏移值

       1.1、解析DexHeader

那么就开始动手写的代码来解析吧~~
用的Clion编译器
1.
首先 先写个一个读取文件的方法
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;
}

2.
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);

3.
写的比较拙劣,,,,
/**
 * 实现方法
 * @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));
    .... 略

输出


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


 2、 DexStringId

       2.0、理解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.0、解析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



 3、DexTypeId

       3.0、理解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

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

       3.0、解析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值


 4、DexFieldId

       4.0、理解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个原理一直,可以自行分析

截图截多了!不好意思!



       4.1、解析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




5、DexMethodId

       5.0、理解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 */
};


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

       5.1、解析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






 6、DexProtoId

       6.0、理解DexProtoId

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

具体结构

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

       6.0、解析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:



 7、DexClassDef

       7.0、理解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:


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

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

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常量
0

当多个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中数据区域的结构信息的偏移值

       1.1、解析DexHeader

那么就开始动手写的代码来解析吧~~
用的Clion编译器
1.
首先 先写个一个读取文件的方法
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;
}

2.
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);

3.
写的比较拙劣,,,,
/**
 * 实现方法
 * @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));
    .... 略

输出


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


 2、 DexStringId

       2.0、理解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.0、解析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



 3、DexTypeId

       3.0、理解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

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

       3.0、解析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值


 4、DexFieldId

       4.0、理解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个原理一直,可以自行分析

截图截多了!不好意思!



       4.1、解析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




5、DexMethodId

       5.0、理解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 */
};


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

       5.1、解析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






 6、DexProtoId

       6.0、理解DexProtoId

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

具体结构

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

       6.0、解析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:



 7、DexClassDef

       7.0、理解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:


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

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

/*
 * 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常量
0

当多个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中数据区域的结构信息的偏移值

       1.1、解析DexHeader

那么就开始动手写的代码来解析吧~~
用的Clion编译器
1.
首先 先写个一个读取文件的方法
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;
}

2.
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);

3.
写的比较拙劣,,,,
/**
 * 实现方法
 * @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));
    .... 略

输出


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


 2、 DexStringId

       2.0、理解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.0、解析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



 3、DexTypeId

       3.0、理解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

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

       3.0、解析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值


 4、DexFieldId

       4.0、理解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个原理一直,可以自行分析

截图截多了!不好意思!



       4.1、解析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




5、DexMethodId

       5.0、理解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 */
};


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

       5.1、解析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






 6、DexProtoId

       6.0、理解DexProtoId

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

具体结构

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

       6.0、解析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:



 7、DexClassDef

       7.0、理解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:


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

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

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


553820个字节大小

表示 DexHeader 头结构的大小,占用4个字节。这里可以看到它一共占用了112个字节,112对应的16进制数为70h
u4  headerSize;     //默认是0x70个字节



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

当多个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中数据区域的结构信息的偏移值

       1.1、解析DexHeader

那么就开始动手写的代码来解析吧~~
用的Clion编译器
1.
首先 先写个一个读取文件的方法
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;
}

2.
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);

3.
写的比较拙劣,,,,
/**
 * 实现方法
 * @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));
    .... 略

输出


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


 2、 DexStringId

       2.0、理解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.0、解析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



 3、DexTypeId

       3.0、理解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

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

       3.0、解析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值


 4、DexFieldId

       4.0、理解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个原理一直,可以自行分析

截图截多了!不好意思!



       4.1、解析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




5、DexMethodId

       5.0、理解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 */
};


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

       5.1、解析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






 6、DexProtoId

       6.0、理解DexProtoId

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

具体结构

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

       6.0、解析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:



 7、DexClassDef

       7.0、理解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:


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

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

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



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

当多个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中数据区域的结构信息的偏移值

       1.1、解析DexHeader

那么就开始动手写的代码来解析吧~~
用的Clion编译器
1.
首先 先写个一个读取文件的方法
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;
}

2.
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);

3.
写的比较拙劣,,,,
/**
 * 实现方法
 * @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));
    .... 略

输出


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


 2、 DexStringId

       2.0、理解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.0、解析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



 3、DexTypeId

       3.0、理解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

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

       3.0、解析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值


 4、DexFieldId

       4.0、理解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个原理一直,可以自行分析

截图截多了!不好意思!



       4.1、解析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




5、DexMethodId

       5.0、理解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 */
};


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

       5.1、解析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






 6、DexProtoId

       6.0、理解DexProtoId

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

具体结构

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

       6.0、解析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:



 7、DexClassDef

       7.0、理解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:


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

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

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

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2019-11-1 13:02 被iio编辑 ,原因: 代码排版问题
上传的附件:
收藏
免费 9
支持
分享
最新回复 (13)
雪    币: 1385
活跃值: (5609)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
2
2019-11-1 09:14
0
雪    币: 26205
活跃值: (63302)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
3
感谢分享!
2019-11-1 09:19
0
雪    币: 6573
活跃值: (3893)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
4
写的挺好,能把代码在重新排版吗
2019-11-1 09:56
0
雪    币: 7
活跃值: (263)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
赞一下
2019-11-1 10:51
0
雪    币: 783
活跃值: (1121)
能力值: ( LV5,RANK:78 )
在线值:
发帖
回帖
粉丝
6
文章是真不错...mark
2019-11-1 12:08
0
雪    币: 317
活跃值: (146)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
iio
7
LowRebSwrd 写的挺好,能把代码在重新排版吗
我已排版好~~
2019-11-1 13:03
0
雪    币: 6257
活跃值: (1177)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
不错,设置想打个赏
2019-11-5 09:54
0
雪    币: 80
活跃值: (175)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
不错!适合学习
2019-11-5 19:27
0
雪    币: 1636
活跃值: (653)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
2019-11-10 10:00
0
雪    币: 8195
活跃值: (6419)
能力值: ( LV12,RANK:207 )
在线值:
发帖
回帖
粉丝
11
很详细,非常适合安卓逆向新手
2020-3-1 22:41
0
雪    币: 303
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
感谢
2022-11-13 18:34
0
雪    币: 922
活跃值: (1813)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
13
牛逼
2023-1-30 18:45
0
雪    币: 33
活跃值: (1481)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
精彩
2023-1-31 15:04
0
游客
登录 | 注册 方可回帖
返回
//