dex文件格式------header_item解析

授人与鱼不如授人与渔,作为初学者,最重要的是学会查看官方文档,自主学习。

首先放上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;
    if (*str)
    {
        do {
        //左移7位加上之前的值
            value = (*str & 0X7F) << (index * 7) + value;  
            ++str;
            //判断下一个字符高位是否为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[]	//静态链接文件中使用的数据。本文档尚未指定本区段中数据的格式。此区段在未链接文件中为空,而运行时实现可能会在适当的情况下使用这些数据。

header_item格式

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 区段开头的偏移量。

有了header_item格式,那么我们就可以定义一个header_item的结构体来解析里面的内容

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\c++)