取证基础知识--字节序
字节序(英文:byte-order 或Endianness)是指计算机存储和传输链路中,多字节数据的字节排列顺序。
Endianness一词来源于Jonathan Swift的小说《格列佛游记》,小说中两派人因为吃煮鸡蛋先打破小头还是大头争论不休。丹尼·科恩(Danny Cohen)在1980年发表的一篇互联网实验笔记中,将大端和小端这两个术语引入计算机科学,用于描述字节排列顺序。
多字节数据字节间排列方式规则如下:
大端序(Big-Endian)将数据的低位字节存放在内存的高位地址,高位字节存放在低位地址。这种排列方式与数据用字节表示时的书写顺序一致,符合人类的阅读习惯。除了计算机的内部处理,其他的场合几乎都是大端字节序,比如网络传输和文件储存。
小端序(Little-Endian),将多字节数据的低位放在较小的地址处,高位放在较大的地址处,则称小端序。小端序与人类的阅读习惯相反,但更符合计算机读取内存的方式,因为CPU读取内存中的数据时,是从低地址向高地址方向进行读取的。
举个具体的例子:
十六进制数0x12345678,如果按大端序存储,沿内存增长方向顺序存放。
如果按小端序存储,将得到下图结果。
这里要注意,小端序每个字节内部还是按大端序存储的。也就是说,0x12345678的小端序存储为0x78563412,而不是0x87654321
计算机内部字节序往往由CPU架构决定。许多历史和现存的处理器采用大端内存表示。有些混合使用两种格式,称为中端、混合端或pdp-11端。
lx86,MOS Technology 6502,Z80,VAX,PDP-11等处理器为Little endian。
lMotorola 6800,Motorola 68000,PowerPC 970,System/370,SPARC(除V9外)等处理器为Big endian。
lARM, PowerPC (除PowerPC 970外), DEC Alpha, SPARC V9, MIPS, PA-RISC and IA64的字节序是可配置的。
为什么小端序不太好理解,还一定要用呢?据说是因为计算是从低位开始的,CPU采用小端序的方式处理数据效率更高,因此一般计算机内部处理很多都选择小端字节序。 但是人类更容易理解大端序,网络传输和文件储存往往采用大端序。其实一般用户不用太关心字节序的问题,程序员则要关心计算机内部字节序和外部字节序之间的转换问题。
你有没有注意过,最朴实的文本编辑器在保存文件时候,也可以选择不同的编码。
五种编码相信大家都挺熟悉了。我们在文本编辑器中写入“让我们测试一下吧”几个汉字,然后分别存为这五种编码。然后用winhex打开。
比较一下上图UTF16-LE和UTF16-BE两个文件,文件头分别是FFFE和FEFF。因为是UTF-16编码,每个汉字占两个字节,存在字节序问题。看上图里面的字节位置,UTF-16LE是小端序,UTF-16BE是大端序
还有一个奇怪编码,就是这个”带有BOM的UTF-8”。BOM(byte order mark)实际上就是字节序标记的意思。但是我们都知道UTF-8不存在字节序问题,为什么还要加BOM呢?BOM是为 UTF-16 和 UTF-32 准备的,用于标记字节序(byte order)。微软在 UTF-8 中使用 BOM 是因为这样可以把 UTF-8 和 ASCII 等编码明确区分开,但这样的文件在 Windows 之外的操作系统里会带来问题。「UTF-8」和「带 BOM 的 UTF-8」的区别就是有没有 BOM。即文件开头有没有FFBBEF。
大家如果仔细看会发现,同样的数据,如果解析选择的字节序和数据不匹配就会是乱码,可见数据就在那里,怎么解读数据很重要。
再看一个例子:
现代PC绝大多数采用小端字节序,我们一般成为主机字节序。而TCP/IP协议在RFC1700中规定使用“大端”字节序为网络字节序,这与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。
当多台电脑进行通信时,如果双方字节序不匹配,会造成通信解析错误。因此如果高于8位的数据要进行网络传输,需要先将数据转换为大端再进行发送,对于接收到的数据,根据接收机器自身的存储方式进行大小端转换后再使用。
BSD Socket提供了封装好的转换接口,包括四个函数,分别是htons(把unsigned short类型从主机序转换到网络序)、htonl (把unsigned long类型从主机序转换到网络序)、ntohs (把unsigned short类型从网络序转换到主机序)和ntohl (把unsigned long类型从网络序转换到主机序)。
这里我们看一个wireshark的抓到的ICMP协议报文。其中Identifier(BE)、Identifier(LE)、Sequence number(BE)、Sequence number(LE)分别代表什么含义呢?
Identifier(BE)指的是标示符(大端顺序):1(0x0100);
Identifier(LE)指的是标示符(小端顺序):256(0x0100);
Sequence number(BE)指的是序列号(大端顺序):31(0x001f)
Sequence number(LE)指的是序列号(小端顺序):7936(0x1f00)
为什么要搞这么复杂呢?原因是wireshark考虑到window系统与Linux系统发出的ping报文(主要指ping应用字段而非包含IP头的ping包)字节顺序不同,windows发出的报文是小端序LE,Linux为大端序BE),所以wireshark这里给出了两种字节序数据解析结果,实际上原始数据都是一样的。
实验:判断主机字节序的方法
确定一个多字节的值(下面使用的是4字节的整数),将其写入内存(即赋值给一个变量),然后用指针取其首地址所对应的字节(即低地址的一个字节),判断该字节存放的是高位还是低位,高位说明是大端序,低位说明是小端序。
#include
int main ()
{
unsigned int x = 0x12345678;
char *c = (char*)&x;
if (*c == 0x78) {
printf("Little endian");
} else {
printf("Big endian");
}
return 0;
}
参考:
理解字节序 大端字节序和小端字节序 - gremount - 博客园 (cnblogs.com)
Errata Security: How to teach endian
理解字节序 - 阮一峰的网络日志 (ruanyifeng.com)
“字节序”是个什么鬼?- 知乎 (zhihu.com)
Cohen, Danny (1980-04-01). On Holy Wars and a Plea for Peace. IETF. IEN 137. ...which bit should travel first, the bit from the little end of the word, or the bit from the big end of the word? The followers of the former approach are called the Little-Endians, and the followers of the latter are called the Big-Endians. Also published at IEEE Computer, October 1981 issue.