複合文檔文件格式研究node
前 言數組
複合文檔(Compound Document) 是一種不只包含文本並且包括圖形、電子表格數據、聲音、視頻圖象以及其它信息的文檔。能夠把複合文檔想象成一個全部者,它裝着文本、圖形以及多媒體信息如 聲音和圖象。目前創建複合文檔的趨勢是使用面向對象技術,在這裏,非標準信息如圖像和聲音能夠做爲獨立的、自包含式對象包含在文檔中。Microsoft Windows就是使用這種技術,叫作「OLE2 storage file format」或「Microsoft Office compatible storage file format」。 安全
固然Excel、Word等都是用這種格式存儲的。本文主要研究複合文檔的二進制結構。 數據結構
目 錄ide
第一章 倉庫與流(Storages and Streams)學習
第二章 扇區與扇區鏈(Sectors and Sector Chains)spa
第三章 複合文檔頭(Compound Document Header).net
第四章 扇區配置(Sector Allocation)code
第五章 短流(Short-Streams)orm
第六章 目錄(Directory)
第七章 Excel文件實例剖析
第一章 倉庫與流
複合文檔的原理就像一個文件系統(文件系統:如FAT與NTFS)。複合文檔將數據分紅許多流(Streams),這些流又存儲在不一樣的倉庫(Storages)裏。將複合文檔想象成你的D盤,D盤用的是NTFS(NT File System)格式,流就至關於D盤裏的文件,倉庫就至關於D盤裏的文件夾。
流和倉庫的命名規則與文件系統類似,同一個倉庫下的流及倉庫不能重名,不一樣倉庫下能夠有同名的流。每一個複合文檔都有一個根倉庫(root storage)。
例:
第二章 扇區與扇區鏈
2.1 扇區與扇區標識
全部的流又分紅更小的數據塊,叫作數據扇區(sectors)。Sectors 可能包含控制數據或用戶數據。
整個文件由一個頭(Header)結構以及其後的全部Sectors組成。Sectors的大小在頭中肯定,且每一個Sectors的大小都相同。
如下爲示意圖:
HEADER
SECTOR 0
SECTOR 1
SECTOR 2
SECTOR 3
SECTOR 4
SECTOR 5
SECTOR 6
┆
Sectors 簡單的以其在文件中的順序列舉,一個扇區的索引(從0開始)叫作扇區標識(SID:sector identifier)。SID是一個有符號的32位的整型值。
若是一個SID的值非負,就表示真正存在的那個Sector;若是爲負,就表示特殊的含義。下表給出有效的特殊SID:
SID Name Meaning
–1 Free SID 空閒sector,可存在於文件中,但不是任何流的組成部分。
–2 End Of Chain SID SID鏈的結束標記 (見2.2節)
–3 SAT SID 此Sector用於存放扇區配置表(SAT)(見4.2節)
–4 MSAT SID 此Sector用於存放主扇區配置表(MSAT)(見4.1節)
2.2 扇區鏈與扇區標識鏈
用於存儲流數據的全部Sectors的列表叫作扇區鏈(Sector Chain)。這些Sectors能夠是無序的。所以用於指定一個流的Sectors的順序的SID數組就稱爲SID chain。一個SID chain老是以End Of Chain SID(-2)爲結束標記。
例:一個流由4個Sector組成,其SID鏈爲[1, 6, 3, 5, –2]。
流的SID鏈是經過扇區配置表構建的(見4.2節),但短流和如下兩種內部流除外:
1.主扇區配置表,其從自身構建SID鏈(每一個扇區包含下一個扇區的SID)。
2.扇區配置表,其經過主扇區配置表構建SID鏈。
第三章 複合文檔頭
3.1 複合文檔頭的內容
複合文檔頭在文件的開始,且其大小一定爲512字節。這意味着第一個Sector的開始相對文件的偏移量爲512字節。
複合文檔頭的結構以下:
Offset Size Contents
0 8 複合文檔文件標識:D0H CFH 11H E0H A1H B1H 1AH E1H
8 16 此文件的惟一標識(不重要, 可所有爲0)
24 2 文件格式修訂號 (通常爲003EH)
26 2 文件格式版本號(通常爲0003H)
28 2 字節順序規則標識(見3.2)::FEH FFH = Little-Endian FFH FEH = Big-Endian
30 2 複合文檔中sector的大小(ssz),以2的冪形式存儲, sector實際大小爲s_size= 2ssz 字節
(通常爲9即512字節, 最小值爲7即128字節)
32 2 short-sector的大小(見5.1),以2的冪形式存儲, short-sector實際大
小爲s_s_size = 2sssz 字節(通常爲6即64字節,最大爲sector的大小)
34 10 Not used
44 4 用於存放扇區配置表(SAT)的sector總數
48 4 用於存放目錄流的第一個sector的SID (見6)
52 4 Not used
56 4 標準流的最小大小(通常爲4096 bytes), 小於此值的流即爲短流。
60 4 用於存放短扇區配置表(SSAT)的第一個sector的SID (見5.2),或爲–2 (End Of Chain SID)如不存在。
64 4 用於存放短扇區配置表(SSAT)的sector總數
68 4 用於存放主扇區配置表(MSAT)的第一個sector的SID (見4.1),或爲–2 (End Of Chain SID) 若無附加的sectors。
72 4 用於存放主扇區配置表(MSAT)的sector總數
76 436 存放主扇區配置表(MSAT)的第一部分,包含109個SID。
3.2 字節順序(Byte Order)
文件數據的二進制存儲有兩種方法Little-Endian 和 Big-Endian,但實際應用中只使用Little-Endian方法即:低位8字節存放在地址的低位,高位8字節存放在地址的高位。
例:一個32位的整數13579BDFH(轉爲十進制即324508639),以Little-Endian存放爲DFH 9BH 57H13H,以Big-Endian 存放爲 13H 57H 9BH DFH。(H下標表示十六進制)
3.3 扇區偏移量
從頭中的信息能夠計算出一個sector的偏移量(offset),公式爲:
sec_pos(SID) = 512 + SID ∙ s_size = 512 + SID ∙ 2 ssz
例:ssz = 10 and SID = 5:
sec_pos(SID) = 512 + SID ∙ 2 ssz = 512 + 5 ∙ 210 = 512 + 5 ∙ 1024 = 5632.
第四章 扇區配置
4.1 主扇區配置表
主扇區配置表(MSAT:master sector allocation table)是一個SID數組,指明瞭全部用於存放扇區配置表(SAT:sector allocation table)的sector的SID。MSAT的大小(SID個數)就等於存放SAT的sector數,在頭中指明。
MSAT的前109個SID也存放於頭中,若是一個MSAT的SID數多餘109個,那麼多出來的SID將存放於sector中,頭中已經指明瞭用於存放MSAT的第一個sector的SID。在用於存放MSAT的sector中的最後一個SID指向下一個用於存放MSAT的sector,若是沒有下一個則爲End Of Chain SID(-2)。
存放MSAT的sector的內容:(s_size表示sector的大小)
Offset Size Contents
0 s_size-4 MSAT的(s_size-4) / 4個SID的數組
s_size-4 4 下一個用於存放MSAT的sector的SID,或-2(已爲最後一個)
最後一個存放MSAT的sector可能未被徹底填滿,空閒的地方將被填上Free SID(-1)。
例:一個複合文檔須要300個sector用於存放SAT,頭中指定sector的大小爲512字節,這說明一個sector可存放128個SID。MAST有300個SID,前109個放於頭中,其他的191個將要佔用2個sector來存放。此例假定第一個存放MSAT的sector爲sector 1,則sector 1包含127個SID。第128個SID指向一個用於存放MSAT的sector,假定爲sector 6,則sector 6包含剩下的64個SID(最後一個SID爲-2,其餘的值爲-1)。
4.2 扇區配置表
扇區配置表(SAT:sector allocation table)是一個SID數組,包含全部用戶流(短流除外)和內部控制流(the short-stream container stream, 見5.1, the short-sector allocation table, 見5.2, and the directory, 見7)的SID鏈。SAT的大小(SID個數)就等於複合文檔中所存在的sector的個數。
SAT的創建就是經過按順序讀取MSAT中指定的sector中的內容。
存放SAT的sector的內容:(s_size表示sector的大小)
Offset Size Contents
0 s_size SAT的s_size / 4個SID的數組
當經過SAT爲一個流建立SID鏈時,SAT數組的當前位置(array index)表示的就是當前的sector,而該位置存放的SID則指向下一個sector。
SAT可能在任意位置包含Free SID(-1),這些sector將不被流使用。若是該位置包含End Of Chain SID(-2)表示一個流的結束。若是sector用於存放SAT則爲SAT SID(-3),一樣用於存放MSAT則爲MSAT SID(-4)。
一個SID鏈的起點從用戶流的目錄入口(directory entry,見6.2節)或頭(內部控制流)或目錄流自己得到。
例:一個複合文檔包含一個用於存放SAT的sector(sector 1)和2個流。
Sector 1的內容以下圖:
在位置1其值爲-3,代表Sector 1是SAT的一部分。
其中一個流爲內部目錄流,假定頭中指定其開始爲Sector 0,SAT中位置0的值爲2,位置2的值爲3,位置3 的值爲-2。所以目錄流的SID鏈爲[0, 2, 3, –2],即此目錄流存放於3個sector中。
目錄中包含一個用戶流的入口假定爲sector 10,從圖中可看出此流的SID鏈爲[10, 6, 7, 8, 9, –2]。
第五章 短流
5.1 短流存放流
當一個流的大小小於指定的值(在頭中指定),就稱爲短流(short-stream)。
短流並非直接使用sector存放數據,而是內含在一種特殊的內部控制流——短流存放流(short-stream container stream)中。
短流存放流象其餘的用戶流同樣:先從目錄中的根倉庫入口(root storage entry)得到第一個使用的sector,其SID鏈從SAT中得到。而後此流將其所佔用的sectors分紅short-sector,以便用來存放短流。此處也許較難理解,咱們來打個比方:既然流組成符合文檔,而短流組成短流存放流,這二者是類似的。把短流存放流看成複合文檔,那麼短流對應流,short-sector對應sector,惟一的不一樣是複合文檔有一個頭結構,而短流存放流沒有。short-sector的大小在頭中已經指定,所以可根據SID計算short-sector相對於短流存放流的偏移量(offset)。公式爲:
short_s_pos(SID) = SID ∙ short_s_size = SID ∙ 2 sssz
例:sssz = 6 and SID = 5:
short_s_pos(SID) = SID ∙ 2 sssz = 5 ∙ 26 = 5 ∙ 64 = 320.
5.2 短扇區配置表
短扇區配置表(SSAT:short-sector allocation table)是一個SID數組,包含全部短流的SID鏈。與SAT很類似。
用於存放SSAT的第一個sector的SID在頭中指定,其他的SID鏈從SAT中得到。
存放SSAT的sector的內容:(s_size表示sector的大小)
Offset Size Contents
0 s_size SSAT的s_size / 4個SID的數組
SSAT的用法與SAT相似,不一樣的是其SID鏈引用的是short-sector。
第六章 目錄
6.1 目錄結構
目錄(directory)是一種內部控制流,由一系列目錄入口(directory entry)組成。每個目錄入口都指向複合文檔的一個倉庫或流。目錄入口以其在目錄流中出現的順序被列舉,一個以0開始的目錄入口索引稱爲目錄入口標識(DID: directory entry identifier)。
以下圖所示:
DIRECTORY ENTRY 0
DIRECTORY ENTRY 1
DIRECTORY ENTRY 2
DIRECTORY ENTRY 3
⋮
目錄入口的位置不因其指向的倉庫或流的存在與否而改變。若是一個倉庫或流被刪除了,其相應的目錄入口就標記爲空。在目錄的開始有一個特殊的目錄入口,叫作根倉庫入口(root storage entry),其指向根倉庫。
目錄將每一個倉庫的直接成員(倉庫或流)放在一個獨立的紅黑樹(red-black tree)中。紅黑樹是一種樹狀的數據結構,本文僅簡單介紹一下,詳細狀況請參考有關資料。
建構一個Red-Black tree的規則:
1. 每一個節點(node)的顏色屬性不是紅就是黑。
2. 根節點必定是黑的。
3. 若是某個節點是紅的,那它的子節點必定是黑的。
4. 從根節點到每一個葉節點的路徑(path)必須有相同數目的黑節點。
ex: B (用圖形來解說第4點,從根節點
/ / 到最底層的node,你會發現每一個
B B path都剛好有3個black node)
/ / / /
B B R B
/ / / / /
R B B R R
/ /
R R
注意並不老是執行上述規則。安全的方法是忽略節點的顏色。
例:以第一章中的圖爲例
1.根倉庫入口描述根倉庫,它不是任何倉庫入口的成員,所以無需構建紅黑樹。
2.根倉庫的全部直接成員(「Storage1」, 「Storage2」, 「Stream1」, 「Stream2」, 「Stream3」, 和 「Stream4」)將組成一棵紅黑樹,其根節點的DID存放於根倉庫入口中。
3.倉庫Storage1只有一個成員Stream1,Stream1構成一棵紅黑樹,此樹只有一個節點。Storage1的目錄入口包含Stream1的DID。
4. 倉庫Storage2包含3個成員「Stream21」, 「Stream22」, 和「Stream23」。這3個成員將構建一棵紅黑樹,其根節點的DID存放於Storage2的目錄入口中。
這種存放規則將致使每一個目錄入口都包含3個DID:
1.在包含此目錄入口的紅黑樹中,此目錄入口的左節點的DID。
2.在包含此目錄入口的紅黑樹中,此目錄入口的右節點的DID。
3.若此目錄入口表示一個倉庫,則還包含此倉庫的直接成員所組成的另外一顆紅黑樹的根節點的DID。
在構建紅黑樹的過程當中,一個節點究竟做爲左仍是右,是經過比較其名字來判斷的。一個節點比另外一個小是指其名字的長度更短,如長度同樣,則逐字符比較。
規定:左節點<根節點<右節點。
6.2 目錄入口
一個目錄入口的大小嚴格地爲128字節,計算其相對目錄流的偏移量的公式爲:dir_entry_pos(DID) = DID ∙ 128。目錄入口的內容:
Offset Size Contents
0 64 此入口的名字(字符數組), 通常爲16位的Unicode字符,以0結束。(所以最大長度爲31個字符)
64 2 用於存放名字的區域的大小,包括結尾的0。(如:一個名字右5個字符則此值爲(5+1)∙2 = 12)
66 1 入口類型: 00H = Empty 03H = LockBytes (unknown) 01H = User storage
04H = Property (unknown) 02H = User stream 05H = Root storage
67 1 此入口的節點顏色: 00H = Red 01H = Black
68 4 其左節點的DID (若此入口爲一個user storage or stream) 若沒有左節點就爲-1。
72 4 其右節點的DID (若此入口爲一個user storage or stream), 若沒有右節點就爲-1。
76 4 其成員紅黑樹的根節點的DID (若此入口爲storage), 其餘爲-1。
80 16 惟一標識符(若爲storage)(不重要, 可能全爲0)
96 4 用戶標記(不重要, 可能全爲0)
100 8 建立此入口的時間標記。大多數狀況都不寫。
108 8 最後修改此入口的時間標記。大多數狀況都不寫。
116 4 若此爲流的入口,指定流的第一個sector或short-sector的SID,若此爲根倉庫入口,指定短流存放流的第一個sector的SID,其餘狀況,爲0。
120 4 若此爲流的入口,指定流的大小(字節)若此爲根倉庫入口,指定短流存放流的大小(字節)其餘狀況,爲0。
124 4 Not used
時間標記(time stamp) 是一個符號的64位的整數,表示從1601-01-01 00:00:00開始的時間值。此值的單位爲10-7秒。
當計算時間標記是要注意閏年。
例:時間標記值爲01AE408B10149C00H
計算步驟 公式 結果
轉爲十進制 t0 = 121,105,206,000,000,000
化成秒的餘數 rfrac = t0 mod 107 rfrac = 0
化成秒的整數 t1 = t0 / 107 t1 = 12,110,520,600
化成分的餘數 rsec = t1 mod 60 rsec = 0
化成秒的整數 t2 = t1 / 60 t2 = 201,842,010
化成小時的餘數 rmin = t2 mod 60 rmin = 30
化成小時的整數 t3 = t2 / 60 t3 = 3,364,033
化整天的餘數 rhour = t3 mod 24 rhour = 1
化整天的整數 t4 = t3 / 24 t4 = 140,168
距1601-01-01的全年 ryear = 1601 + t4含的年 ryear = 1601 + 383 = 1984
到1984年還剩的天數 t5 = t4 – (1601-01-01 t5 = 140,168 – 139,887 = 281
到1984-01-01的天數)
距1984-01-01的月數 rmonth = 1 + t5含的月數 rmonth = 1 + 9 = 10
到10月還剩的天數 t6 = t5 – (1984-01-01 t6 = 281 – 274 = 7
到1984-10-01的天數)
10月最終天數 rday = 1 + t6 rday = 1 + 7 = 8
第七章 Excel文件實例剖析
這章咱們以一個Excel文件做爲實例來分析其二進制結構。看實例永遠是最好的學習方法,呵呵。
1.複合文檔頭
首先,讀取此文件頭,假定此Excel文件的頭(512字節)內容以下:
00000000H D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00
00000010H 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00
00000020H 06 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
00000030H 0A 00 00 00 00 00 00 00 00 10 00 00 02 00 00 00
00000040H 01 00 00 00 FE FF FF FF 00 00 00 00 00 00 00 00
00000050H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000060H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000070H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000080H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000090H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000000A0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000000B0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000000C0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000000D0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000000E0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000000F0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000100H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000110H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000120H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000130H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000140H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000150H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000160H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000170H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000180H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000190H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000001A0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000001B0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000001C0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000001D0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000001E0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000001F0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
1)前8個字節是固定的標識,表示這是一個複合文檔文件。
00000000H D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00
2)接着16個字節是惟一標識,其後的4個字節表示修訂號和版本號,可不用管它。
00000000H D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00
00000010H 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00
3)接下來的2個字節是字節順序(Byte Order)標識符,老是FEH FFH。
00000010H 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00
4)接着的2個字節表示sector的大小,再2個字節表示short-sector的大小。分別是2^9=512字節和2^6=64字節。
00000010H 00 00 00 00 00 00 00 00 3B 00 03 00 FE FF 09 00
00000020H 06 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
5)接着的10個字節無有效數據,可忽略。
00000020H 06 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
6)接着的4個字節表示用於存放扇區配置表(SAT)的sector總數,此例爲1個。
00000020H 06 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
7)接着的4個字節表示用於存放目錄流的第一個sector的SID,這裏爲sector 10。
00000030H 0A 00 00 00 00 00 00 00 00 10 00 00 02 00 00 00
8)接着的4個字節無有效數據,可忽略。
00000030H 0A 00 00 00 00 00 00 00 00 10 00 00 02 00 00 00
9)接着的4個字節表示標準流的最小大小,這裏爲00001000H = 4096字節。
00000030H 0A 00 00 00 00 00 00 00 00 10 00 00 02 00 00 00
10)接着的4個字節表示用於存放短扇區配置表(SSAT)的第一個sector的SID,其後4個字節表示用於存放短扇區配置表(SSAT)的sector總數,這裏SSAT從sector 2開始,並只佔用1個sector。
00000030H 0A 00 00 00 00 00 00 00 00 10 00 00 02 00 00 00
00000040H 01 00 00 00 FE FF FF FF 00 00 00 00 00 00 00 00
11)接着的4個字節表示用於存放主扇區配置表(MSAT)的第一個sector的SID,其後的4個字節表示用於存放主扇區配置表(MSAT)的sector總數,這裏SID爲-2,說明沒有附加的sector用於存放MSAT。
00000040H 01 00 00 00 FE FF FF FF 00 00 00 00 00 00 00 00
12)最後的436個字節包含MSAT的前109個SID。只有第一個SID有效,由於上面已經說了SAT只佔用1個sector。從這裏可看出爲sector 0。其餘的SID標記爲Free SID值爲-1。
00000040H 01 00 00 00 FE FF FF FF 00 00 00 00 00 00 00 00
00000050H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000060H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
…… ……
2. 主扇區配置表
頭中已經包含整個MSAT,其SID鏈爲[0,-2]。
3. 扇區配置表
在此例中扇區配置表僅佔用sector 0,它開始於文件偏移量爲00000200H = 512字節處。假定內容以下:
00000200H FD FF FF FF FF FF FF FF FE FF FF FF 04 00 00 00
00000210H 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00
00000220H 09 00 00 00 FE FF FF FF 0B 00 00 00 FE FF FF FF
00000230H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000240H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000250H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000260H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000270H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000280H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000290H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000002A0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000002B0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000002C0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000002D0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000002E0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000002F0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000300H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000310H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000320H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000330H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000340H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000350H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000360H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000370H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000380H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000390H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000003A0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000003B0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000003C0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000003D0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000003E0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000003F0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
所以可構建SAT的SID數組爲:
Array index 0 1 2 3 4 5 6 7 8 9 10 11 12 …
SID array | –3 | –1 | –2 | 4 | 5 | 6 | 7 | 8 | 9 | –2 | 11 | –2| –1| … |
可看出sector 0被標記爲SAT SID(-3),sector 1和sector 12及其後的全部sector都未被使用。
4. 短扇區配置表
在頭中咱們知道SSAT從sector 2開始只佔用一個sector。從SAT中可看出位置2的值爲-2,表示結束,故SSAT的SID鏈爲[2, –2]。
開始於文件偏移量爲00000600H = 1536字節處。
假定內容以下:
00000600H 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
00000610H 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00
00000620H 09 00 00 00 0A 00 00 00 0B 00 00 00 0C 00 00 00
00000630H 0D 00 00 00 0E 00 00 00 0F 00 00 00 10 00 00 00
00000640H 11 00 00 00 12 00 00 00 13 00 00 00 14 00 00 00
00000650H 15 00 00 00 16 00 00 00 17 00 00 00 18 00 00 00
00000660H 19 00 00 00 1A 00 00 00 1B 00 00 00 1C 00 00 00
00000670H 1D 00 00 00 1E 00 00 00 1F 00 00 00 20 00 00 00
00000680H 21 00 00 00 22 00 00 00 23 00 00 00 24 00 00 00
00000690H 25 00 00 00 26 00 00 00 27 00 00 00 28 00 00 00
000006A0H 29 00 00 00 2A 00 00 00 2B 00 00 00 2C 00 00 00
000006B0H 2D 00 00 00 FE FF FF FF 2F 00 00 00 FE FF FF FF
000006C0H FE FF FF FF 32 00 00 00 33 00 00 00 34 00 00 00
000006D0H 35 00 00 00 FE FF FF FF FF FF FF FF FF FF FF FF
000006E0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000006F0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000700H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000710H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000720H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000730H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000740H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000750H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000760H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000770H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000780H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
00000790H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000007A0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000007B0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000007C0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000007D0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000007E0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
000007F0H FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
所以可構建SSAT的SID數組爲:
從sector 54開始全部的short-sectors都未被使用。
5.目錄
頭中指明用於存放目錄流的第一個sector爲sector 10。目錄老是存放於sector中,而不是short-sector中。故從SAT構建SID鏈爲[10, 11, –2]。sector 10的偏移量爲00001600H = 5632, sector 11的偏移量爲00001800H = 6144。此例中sector的大小爲512字節,所以每一個sector包含4個目錄入口(一個目錄入口128字節),故此目錄共包含8個入口。
<1>根倉庫入口(Root Storage Entry)
第一個目錄入口老是根目錄入口,假定其內容以下:
00001600H 52 00 6F 00 6F 00 74 00 20 00 45 00 6E 00 74 00
00001610H 72 00 79 00 00 00 00 00 00 00 00 00 00 00 00 00
00001620H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00001630H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00001640H 16 00 05 00 FF FF FF FF FF FF FF FF 01 00 00 00
00001650H 10 08 02 00 00 00 00 00 C0 00 00 00 00 00 00 46
00001660H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00001670H 00 00 00 00 03 00 00 00 80 0D 00 00 00 00 00 00
1)前64個字節是此入口名字的字符數組(爲16位的字符, 以第一個00結束),
可看出此入口的名字是「Root Entry」。
00001600H 52 00 6F 00 6F 00 74 00 20 00 45 00 6E 00 74 00
00001610H 72 00 79 00 00 00 00 00 00 00 00 00 00 00 00 00
00001620H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00001630H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2)接着2個字節表示用於存放上述字符數組的有效區域的大小,這裏是22字節,只有10個有效字符。
00001640H 16 00 05 00 FF FF FF FF FF FF FF FF 01 00 00 00
3)接着1個字節表示入口類型,根倉庫入口故爲05H。
00001640H 16 00 05 00 FF FF FF FF FF FF FF FF 01 00 00 00
4)接着1個字節表示入口節點顏色,這裏爲紅色,打破了根倉庫入口爲黑的規定。
00001640H 16 00 05 00 FF FF FF FF FF FF FF FF 01 00 00 00
5)接着4個字節表示其左節點的DID,再接着4個字節表示右節點的DID。對根倉庫入口來講都是-1。
00001640H 16 00 05 00 FF FF FF FF FF FF FF FF 01 00 00 00
6)接着4個字節表示根倉庫入口的成員構建的紅黑樹的根節點的DID,此例爲1。
00001640H 16 00 05 00 FF FF FF FF FF FF FF FF 01 00 00 00
7)接着16個字節表示惟一標識符,說明這是一個倉庫,其後4字節表示用戶標識,再接着是兩個時間標記,各8字節,代表此倉庫的建立時間和最後修改時間。這些數據均可忽略。
00001650H 10 08 02 00 00 00 00 00 C0 00 00 00 00 00 00 46
00001660H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00001670H 00 00 00 00 03 00 00 00 80 0D 00 00 00 00 00 00
8)接着4個字節表示短流存放流的第一個sector的SID,其後4字節表示短流存放流的大小。此例中第一個sector爲sector 3,其大小爲00000D80H = 3456字節。
00001670H 00 00 00 00 03 00 00 00 80 0D 00 00 00 00 00 00
9)最後4個字節無有效數據,可忽略。
00001670H 00 00 00 00 03 00 00 00 80 0D 00 00 00 00 00 00
<1>第二個目錄入口
第二個目錄入口(DID 1)假定其內容以下:
00001680H 57 00 6F 00 72 00 6B 00 62 00 6F 00 6F 00 6B 00
00001690H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000016A0H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000016B0H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000016C0H 12 00 02 00 02 00 00 00 04 00 00 00 FF FF FF FF
000016D0H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000016E0H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000016F0H 00 00 00 00 00 00 00 00 51 0B 00 00 00 00 00 00
用顏色標記的爲重要數據。入口的名字是「Workbook」,它表示一個流。其左節點的DID是2,右節點的DID是4。其第一個sector的SID是0,其大小是00000B51H = 2897字節,小於4096字節,故其存放於短流存放流中。
<3>剩下的目錄入口
剩下的目錄入口可按上面的方法讀取,下面給出目錄:
從根倉庫入口的第一個成員的DID(此爲1)能夠找到根倉庫的全部成員。入口1有2個子節點,DID 2 和 DID 4,DID 2又有一個子節點DID 3,DID 3和DID 4都沒有子節點了。所以根倉庫包含DID 一、二、三、4這4個成員。
<4>流的SID鏈
短流存放流是存儲在sector中的,小於4096字節的用戶流(爲短流)存儲在短流存放流中,並使用SSAT構建SID鏈。
下表給出全部流的狀況:
DID 流名 配置表 SID 鏈
0 Root Entry (短流存放流) SAT [3, 4, 5, 6, 7, 8, 9, –2]
1 Workbook SSAT [0, 1, 2, 3, 4, 5, …, 43, 44, 45, –2]
2 <01H>CompObj SSAT [46, 47, –2]
3 <01H>Ole SSAT [48, –2]
4 <05H>SummaryInformation SSAT [49, 50, 51, 52, 53, –2]
<5>流的讀取
短流存放流讀取其SID鏈中的全部sector,此例中將按順序讀取sector 3,4,5,6,7,8,9,故此流的大小爲512×7=3584字節。可是隻有前3456字節有效(根倉庫入口中指定的)。這3456字節被分紅大小爲64字節的short-sector,一共54個。
如今咱們來讀取流「<01H>CompObj」,其SID鏈爲[46, 47, –2],此流是一個短流。其數據存放在short-sector 46和short-sector 47中。short-sector 46在短流存放流中的偏移量爲2944字節,short-sector 47的偏移量爲3008字節。
[轉]http://blog.csdn.net/liangjingbo/article/details/2874959