通用格式设计
1 序
二进制文件的格式一般采用结构 文件头 + 核心内容。
- 文件头:标识文件的基本信息,其设计方法基本一致。
- 核心内容:这部分是真正的文件数据内容。
二进制文件的格式一般采用结构 文件头 + 核心内容。
描述了文件的整体信息,常见的字段有魔数、检验码、版本号、文件大小等。
顾名思义,即是将文件的整体信息通常放在文件的开头。
在少数情况下,也会将文件头放在文件的尾部,也就是‘文件尾’,但是一般还是叫做 文件头。
魔数作为文件格式的标识,一般用于识别文件是否程序所能处理的文件。其值可以随意选取,主要看设计者自身的喜好。
例如 zip 格式的魔数就是 “PK\x03\x04”,其中 PK 就是设计者 Philip Katz 的名字首字母。
一般程序在分析文件时,会先判断魔数的值是否匹配,不匹配就表明文件格式不正确。
需要注意的是,假如魔数正确,文件格式并非一定能够读取正确,还需要进一步判断。
文件头通常还会有个检验码,用于检验文件是否完整并且没有经过修改的。这个检验码可以使用 crc, 可以使用 md5,也可以使用其它算法。
这也是检查文件格式的正确性中的重要一步,因为即便是魔数相同,校验码所采用的算法不同,和校验码所作的位置不同,所计算的校验码,基本上是不一的,所以进一步就,能区分出文件格式是否正确。
文件头通常还会包含版本号。
版本号不同,就表明文件格式发生变化,可能变化很小,也可能变化很大;可能是某些字段的值在解释上发生变化,也可能是直接添加了一些结构。所以导致文件的读取方式可能会有所不同。
版本号有时只是单独一个数字,不断往上递增。有时也会拆分成两个数字,为主版本号和次版本号。主版本号修改,通常表示文件格式发生大变动。而次版本号修改,通常只是表示添加了一些小功能。
字节顺序在文件格式设计中至关重要。
字节顺序分为大端字节序和小端字节序。不同的机器字节序有可能不同,设计文件格式时需要考虑文件用什么字节序保存数据的。
读取和存储的字节序不一致就会导致程序出错。
通用结构定义如下:
struct FileHeader{
uint8 mMagic[4]; //魔数
uint8 mHash[16]; //检验码
uint32 mEndian; //字节序
uint32 mVersion; //文件版本
...
};
分段(或叫分节,后续默认叫法为分段)的二进制文件格式是比较常见的,比如编译器 GCC 输出的目标文件一般为 ELF 的分段文件。
这类文件一般采用 文件头 + 分段 的结构:
file header
section 0
section 1
....
section N
文件头设计方法在 文件头 中介绍,本章节主要描述分段式存储数据的方法。
一般有两种结构:
分散式分结构在分区的头部的描述分段的大小等信息,然后各个分段首尾相连的,分段写入。
分段的每一个段的结构通常是:
tag + length
section data
tag 和 length 合起来是分区头部,描述分段中的数据标识,后面紧跟着体数据。
tag 一般是一个整数,用来标识分区的类型。不同的分区用不同的 tag 值表示,不同的分区种类可以使用不同的读取方式。
整体结构如下:
file header
section 0
tag //section 0 的标识
length //section 0 的数据长度
data[] //section 0 的数据
section 1
....
section N
段表式的结构是有一个描述分段信息的段表,后面存储各个分区的数据。
主要优点就是,可以通过段描述表(简称段表)快速的访问分段的内容,其格式如下。
section table
section 0
section 1
....
section N
段表一般描述分段的类型、数据长度、相对偏移等,结构如下。
kind
length
offset
C 语言定义如下:
struct SegTabItem{
uint32 kind; //类型
uint32 length; //数据长度
uint32 offset; //相对偏移
};
整体结构如下:
file header
section table
kind //section 0 的类型
length //section 0 的数据长度
offset //section 0 的相对偏移
kind //section 1 的类型
length //section 1 的数据长度
offset //section 1 的相对偏移
...
kind //section N 的类型
length //section N 的数据长度
offset //section N 的相对偏移
section 0
section 1
....
section N
TLV 是一种可变的格式,由三个域构成:标识域(Tag)+长度域(Length)+值域(Value),简称TLV格式。
其中:
T 和 L 的长度固定,一般是2或4个字节,V 的长度由 Length 指定。
data 0
T = 1
L = 1
V = 1
data 1
T = 1
L = 1
V = 2
...
data N
T = 1
L = 1
V = n
当然V中的数据也是可以嵌套,至于嵌套几层看设计者的规定的。
结构如下:
data 0
T = 1
L = 3
V =[
data x
T = 1
L = 1
V = 1
]
data 1
T = 1
L = 1
V = 2
...
data N
T = 1
L = 1
V = n