取證基礎知識--字節序
字節序(英文: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.