整个复合文档被切成一块块sector,在文档头部的结构体中标有每个sector的大小。和第一个sector表的偏移。Sector表存放所有sector的块。(除开Compound Document Header后就是第零块sector。)
| SID| Name| Meaning|
| ---------- |
| 0xFFFFFFFF | Free SID | 空闲 sector ,可存在于文件中,但不是任何流的组成部分 |
| 0xFFFFFFFE | End Of Chain SID | SID 链的结束标记 |
| 0xFFFFFFFD | SAT SID | 此 Sector 用于存放扇区配置表 |
| 0xFFFFFFFC | MSAT SID | 此 Sector 用于存放主扇区配置表 |
所有sector存在Sector表中,表头的位置表示SID 0之后就是SID1,SID2......每个位置占4个字节,每个位置存储着下个SID。用于存储流数据的所有 Sectors 的列表叫做扇区链( Sector Chain )。这些 Sectors 可以是无序的。因此用于指定一个流的 Sectors 的顺序的 SID 数组就称为 SID chain 。一个 SID chain 总是以 End Of Chain SID (- 2 )为结束标记。表示直达 End Of Chain SID (0xFFFFFFFE)
例:一个流由 4 个 Sector 组成,其 SID 链为 [1, 6, 3, 5, –2] 。
复合文档头在文件的开始,且其大小必定为 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 |字节顺序规则标识: FEH FFH = Little-Endian FFH FEH = Big-Endian|
|30|2 |复合文档中 sector 的大小 (ssz) ,以 2 的幂形式存储 , sector 实际大小为 s_size|
|32|2 |short-sector 的大小,以 2 的幂形式存储 , short-sector 实际大小物雹|
|34|10 |Not used|
|44|4 |用于存放扇区配置表( SAT )的 sector 总数|
|48|4 |用于存放目录流的第一个 sector 的 SID|
|52|4 |Not used|
|56|4 |标准流的最小大小 ( 一般为 4096 bytes), 小于此值的流即为短流。|
|60|4 |用于存放短扇区配置表( SSAT )的第一个 sector 的 SID 或为 –2 (End Of Chain SID) 如不存在。|
|64|4 |用于存放短扇区配置表( SSAT )的 sector 总数|
|68|4 |用于存放主扇区配置表( MSAT )的第一个 sector 的 SID|
|72|4|用于存放主扇区配置表( MSAT )的 sector 总数|
|76|436 |存放主扇区配置表( MSAT )的第一部分,包含 109 个 SID 。|
主扇区配置表( 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 )。
扇区配置表( SAT : sector allocation table )是一个 SID 数组,包含所有用户流(短流除外)和内部控制流( the short-stream container stream, the short-sector allocation table and the directory,) 的 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 )或头(内部控制流)或目录流本身获得。
例:一个复合文档包含一个用于存放 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] 。
当一个流的大小小于指定的值(在头中指定),就称为短流 (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 )。公式为:
短扇区配置表( 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 。
目录( directory )是一种内部控制流,由一系列目录入口( directory entry )组成。每一个目录入口都指向复合文档的一个仓库或流。目录入口以其在目录流中出现的顺序被列举,一个以 0 开始的目录入口索引称为目录入口标识 (DID: directory entry identifier) 。
如下所示:
目录入口的位置不因其指向的仓库或流的存在与否而改变。如果一个仓库或流被删除了,其相应的目录入口就标记为空。在目录的开始有一个特殊的目录入口,叫做根仓库入口( root storage entry ),其指向根仓库。
目录将每个仓库的直接成员(仓库或流)放在一个独立的红黑树( red-black tree )中。红黑树是一种树状的数据结构,本文仅简单介绍一下,详细情况请参考有关资料。
建构一个 Red-Black tree 的规则:
例:以第一章中的图为例
这种存放规则将导致每个目录入口都包含 3 个 DID:
一个目录入口的大小严格地为 128 字节,计算其相对目录流的偏移量的公式为: dir_entry_pos(DID) = DID ∙ 128 。
目录入口的内容:
| Offset | Size| Contents|
| ---------- |
|0 |64 |此入口的名字(字符数组) , 一般为 16 位的 Unicode 字符 ,以 0 结束。 ( 因此最大长度为 31 个字符 )|
|64 |2 |用于存放名字的区域的大小,包括结尾的 0 。|
|66 |1 |入口类型 : 00H = Empty 03H = LockBytes (unknown) 01H = User storage 04H = Property (unknown) 02H = User stream 05H = Root storage|
|67 |1 |此入口的节点颜色 : 00H = Red01H = 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|
复合文档数据恢复格式方法如卜雀扰下:1、使用winhex工具打开损坏复合文档以及正常复合文档。
2、将页转换成型旦扇区进行分析。
3、复制正常复合文档的文件头覆盖掉损坏的文件头,逐一修改相应参数。
4、岁毁定位目录流扇区起始扇区。
5、短流开始位置及大小。
6、确定扇区分配表(SAT)。
7、确定主扇区分配表(MSAT)的起始扇区号。
8、修复文件内容。
9、复合文档头结构。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)