授人与鱼不如授人与渔,作为初学者,最重要的是学会查看官方文档,自主学习。
首先放上google官方文档对dex解释的链接,里面相当详细的介绍了dex的格式的组成。
https://source.android.google.cn/devices/tech/dalvik/dex-format#header-item
类型指南
byte 8 位有符号整数
ubyte 8 位无符号整数
short 16 位有符号整数,采用小端字节序
ushort 16 位无符号整数,采用小端字节序
int 32 位有符号整数,采用小端字节序
uint 32 位无符号整数,采用小端字节序
long 64 位有符号整数,采用小端字节序
ulong 64 位无符号整数,采用小端字节序
sleb128 有符号 LEB128,可变长度(见下文)
uleb128 无符号 LEB128,可变长度(见下文)
uleb128p1 无符号 LEB128 加 1,可变长度(见下文)
LEB128
LEB128(“**L**ittle-**E**ndian **B**ase **128**”)表示任意有符号或无符号整数的可变长度编码。该格式借鉴了 [DWARF3](http://dwarfstd.org/Dwarf3Std.php) 规范。在 `.dex` 文件中,LEB128 仅用于对 32 位数字进行编码。
每个 LEB128 编码值均由 1-5 个字节组成,共同表示一个 32 位的值。每个字节均已设置其最高有效位(序列中的最后一个字节除外,其最高有效位已清除)。每个字节的剩余 7 位均为有效负荷,即第一个字节中有 7 个最低有效位,第二个字节中也是 7 个,依此类推。对于有符号 LEB128 (`sleb128`),序列中最后一个字节的最高有效负荷位会进行符号扩展,以生成最终值。在无符号情况 (`uleb128`) 下,任何未明确表示的位都会被解译为 `0`。
c语言对LEB128解码
void LEB128toInt(char* str)
{
DWORD value = 0;
WORD index = 0;
if (*str)
{
do {
//左移7位加上之前的值
value = (*str & 0X7F) << (index * 7) + value;
++index;
//判断下一个字符高位是否为1
} while (*str++ & 0x80);
return;
}
return ;
}
dex完整格式:
header header_item 标头
string_ids string_id_item[] //字符串标识符列表。这些是此文件使用的所有字符串的标识符,用于内部命名(例如类型描述符)或用作代码引用的常量对象。此列表必须使用 UTF-16 代码点值按字符串内容进行排序(不采用语言区域敏感方式),且不得包含任何重复条目。
type_ids type_id_item[] //类型标识符列表。这些是此文件引用的所有类型(类、数组或原始类型)的标识符(无论文件中是否已定义)。此列表必须按 string_id 索引进行排序,且不得包含任何重复条目。
proto_ids proto_id_item[] //方法原型标识符列表。这些是此文件引用的所有原型的标识符。此列表必须按返回类型(按 type_id 索引排序)主要顺序进行排序,然后按参数列表(按 type_id 索引排序的各个参数,采用字典排序方法)进行排序。该列表不得包含任何重复条目。
field_ids field_id_item[] //字段标识符列表。这些是此文件引用的所有字段的标识符(无论文件中是否已定义)。此列表必须进行排序,其中定义类型(按 type_id 索引排序)是主要顺序,字段名称(按 string_id 索引排序)是中间顺序,而类型(按 type_id 索引排序)是次要顺序。该列表不得包含任何重复条目。
method_ids method_id_item[] //方法标识符列表。这些是此文件引用的所有方法的标识符(无论文件中是否已定义)。此列表必须进行排序,其中定义类型(按 type_id 索引排序)是主要顺序,方法名称(按 string_id 索引排序)是中间顺序,而方法原型(按 proto_id 索引排序)是次要顺序。该列表不得包含任何重复条目。
class_defs class_def_item[] //类定义列表。这些类必须进行排序,以便所指定类的超类和已实现的接口比引用类更早出现在该列表中。此外,对于在该列表中多次出现的同名类,其定义是无效的。
call_site_ids call_site_id_item[] //调用站点标识符列表。这些是此文件引用的所有调用站点的标识符(无论文件中是否已定义)。此列表必须按 call_site_off 以升序进行排序。
method_handles method_handle_item[] //方法句柄列表。此文件引用的所有方法句柄的列表(无论文件中是否已定义)。此列表未进行排序,而且可能包含将在逻辑上对应于不同方法句柄实例的重复项。
data ubyte[]// 数据区,包含上面所列表格的所有支持数据。不同的项有不同的对齐要求;如有必要,则在每个项之前插入填充字节,以实现所需的对齐效果。
link_data ubyte[] //静态链接文件中使用的数据。本文档尚未指定本区段中数据的格式。此区段在未链接文件中为空,而运行时实现可能会在适当的情况下使用这些数据。
magic ubyte[8] = DEX_FILE_MAGIC //魔法值。如需了解详情,请参阅上文中“DEX_FILE_MAGIC”下的讨论。
checksum uint //文件剩余内容(除 magic 和此字段之外的所有内容)的 adler32 校验和;用于检测文件损坏情况
signature ubyte[20] //文件剩余内容(除 magic、checksum 和此字段之外的所有内容)的 SHA-1 签名(哈希);用于对文件进行唯一标识
file_size uint //整个文件(包括标头)的大小,以字节为单位
header_size uint = 0x70 //头文件(整个区段)的大小,以字节为单位。此项允许至少一定程度的向后/向前兼容性,而不会使格式失效。
endian_tag uint = ENDIAN_CONSTANT //字节序标记。如需了解详情,请参阅上文中“ENDIAN_CONSTANT 和 REVERSE_ENDIAN_CONSTANT”下的讨论。
link_size uint //链接区段的大小;如果此文件未进行静态链接,则该值为 0
link_off uint //从文件开头到链接区段的偏移量,如果 link_size == 0,则该值为 0。该偏移量(如果为非零值)应该是到 link_data 区段的偏移量。本文档未指定此处所指数据的格式;此标头字段(和之前的字段)会被保留为钩子,以供运行时实现使用。
map_off uint //从文件开头到映射项的偏移量。该偏移量(必须为非零值)应该是到 data 区段的偏移量,而数据应采用下文中“map_list”指定的格式。
string_ids_size uint //字符串标识符列表中的字符串数量
string_ids_off uint //从文件开头到字符串标识符列表的偏移量;如果 string_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 string_ids 区段开头的偏移量。
type_ids_size uint //类型标识符列表中的元素数量,最多为 65535
type_ids_off uint //从文件开头到类型标识符列表的偏移量;如果 type_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 type_ids 区段开头的偏移量。
proto_ids_size uint //原型标识符列表中的元素数量,最多为 65535
proto_ids_off uint //从文件开头到原型标识符列表的偏移量;如果 proto_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 proto_ids 区段开头的偏移量。
field_ids_size uint //字段标识符列表中的元素数量
field_ids_off uint //从文件开头到字段标识符列表的偏移量;如果 field_ids_size == 0,则该值为 0。该偏移量(如果为非零值)应该是到 field_ids 区段开头的偏移量。
method_ids_size uint //方法标识符列表中的元素数量
method_ids_off uint //从文件开头到方法标识符列表的偏移量;如果 method_ids_size == 0,则该值为 0。该偏移量(如果为非零值)应该是到 method_ids 区段开头的偏移量。
class_defs_size uint //类定义列表中的元素数量
class_defs_off uint //从文件开头到类定义列表的偏移量;如果 class_defs_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 class_defs 区段开头的偏移量。
data_size uint //data 区段的大小(以字节为单位)。该数值必须是 sizeof(uint) 的偶数倍。
data_off uint //从文件开头到 data 区段开头的偏移量。
windows头文件中类型的定义,此处为了方便,没有自己定义。
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
定义结构体
#define IMAGE_SIZEOF_DEX_FILE 8
#define IMAGE_SIZEOF_DEX_SIGNATURE 20
typedef struct _IMAGE_DEX_HEADER
{
BYTE magic[IMAGE_SIZEOF_DEX_FILE]; 。
DWORD checksum;
BYTE signature[IMAGE_SIZEOF_DEX_SIGNATURE];
DWORD file_size;
DWORD header_size;
DWORD endian_tag;
DWORD link_size;
DWORD link_off;
DWORD map_off;
DWORD string_ids_size;
DWORD string_ids_off;
DWORD type_ids_size;
DWORD type_ids_off;
DWORD proto_ids_size;
DWORD proto_ids_off;
DWORD field_ids_size;
DWORD field_ids_off;
DWORD method_ids_size;
DWORD method_ids_off;
DWORD class_defs_size;
DWORD class_defs_off;
DWORD data_size;
DWORD data_off;
}IMAGE_DEX_HEADER, * PIMAGE_DEX_HEADER;
定义好了结构体,接下来就可以解析了。
打开一个文件
//IpzFile为文件名字
FILE* pFile = fopen(IpzFile, "rb");
//定义一个结构体变量
IMAGE_DEX_HEADER dex_header;
if (!pFile)
{
MessageBox(0, TEXT("打开文件失败"), TEXT("信息"), 0);
return fileSize;
}
//此时header_item数据就已经读取到结构体中了
fread(&dex_header, sizeof(IMAGE_DEX_HEADER), 1, pFile);
//关闭文件
fclose(pFile);
此时要打印或者写到文件中任由你选择。如果这个你不会,那么你要去补c语言基础了。
说了那么多,其实总结起来就两步,第一步:定义结构体,第二步:读取文件,给结构体赋值。