office文档恢复基础之office文件格式1.docx
- 文档编号:6637410
- 上传时间:2023-01-08
- 格式:DOCX
- 页数:31
- 大小:39.43KB
office文档恢复基础之office文件格式1.docx
《office文档恢复基础之office文件格式1.docx》由会员分享,可在线阅读,更多相关《office文档恢复基础之office文件格式1.docx(31页珍藏版)》请在冰豆网上搜索。
office文档恢复基础之office文件格式1
不要钱的,看了好的就下载,支持一下^_^!
Office文件格式
1.Ole物件档
Office档桉或是EmbededObject,这些档桉都是透过IStorage界面来储存的,一般称为OLE物件档(也称为Laola档)。
什麽是IStorage界面呢?
它是Windows所提供的一个OLE界面,主要是提供给OLE物件做为储存资料之用。
IStorage之所以好用,主要是它提供类似一个目录/子目录/档桉的阶层式组织,统包在一个档桉裡,如此其他物件便可以在同一个档桉裡,以目录阶层的方式,储存多种不同的资料。
因此要解Office档,首先必须要弄清IStorage所储存的OLE物件档格式。
为了快速存取类似目录档桉的结构,IStorage模彷了类似实际的目录档桉结构。
它将档桉中每512byte视为一个单位,称为大区块资料(BBD,BigBlockData)。
不过说实在的,这些名称真的很容易令人溷淆不清(看过MS的文件就会知道,因为还有很多定义的用字都很接近)。
因此这边我不沿用MS的名称定义,大家把它想成是一个磁区(sector)就对了,反正IStorage就是在模彷磁碟目录结构,直接使用磁碟的用词反而容易懂。
而为了管理这些磁区,当然就要有FAT(档桉磁区配置表,MS称它为大区块库,真难懂)。
不过档桉一词在这边反而会溷淆,因此储存在IStorage的“档桉”,我便沿用MS的名称,称为资料串(stream)。
以下便开始从档头说起:
档头当然就是在档桉的开头处,刚好是一个磁区(512byte)。
由于这个档头是固定有的,不能被使用,因此实际的磁区位置,必须从512byte开始计算起。
也就是说Sec#0的位置在512,Sec#1的位置在1024,以此类推。
以下便是档头的重要资料:
00h(8byte):
档头标记,一开始的前8个byte固定为D0CF11E0A1B11AE1,否则便不是OLE物件档
2Ch(long):
FAT使用的磁区数
30h(long):
档桉目录结构属性开始的磁区
3Ch(long):
小资料储存区FAT开始的磁区
44h:
额外记录FAT使用磁区的开始磁区
48h:
额外记录FAT使用磁区的磁区数
4Ch开始:
FAT使用的磁区(long),数量由2Ch中的磁区数决定
这边注意到有一个小资料储存区。
由于每个磁区都是512byte,拿来放小资料的话,会非常浪费空间,因此IStorage便将较小的资料,统一另行储存。
各位可以将小资料储存区想成是另一个档桉,这个档桉又是模拟目录档桉结构,只是每个磁区缩小到64byte而已。
这个部份我待会再谈,先将基本512byte磁区的模拟方式弄懂,小资料储存区的格式便更容易懂了。
由于在IStorage中,所有资料在磁区的储存次序,都和FAT有关,因此必须先弄清FAT的配置方式。
从档头中,我们可以知道FAT使用了那些磁区,将这些磁区的资料组合在一起,便是真正的FAT资料(磁区可能跳来跳去的)。
而在FAT资料裡,其实便是记录着每个磁区的下一个磁区是什麽(每笔资料均为long)。
这边的磁区值可能为:
0xfffffffd(-3):
特殊区块(FAT使用的磁区便是)
0xfffffffe(-2):
结束标记(表示已无下个资料磁区)
0xffffffff(-1):
尚未被使用的磁区
其他:
下个资料所在的磁区
因此如果知道一个资料串从那个磁区开始,便可以直接参照FAT,看看资料所在的下个磁区是在那裡。
例如Sec#10,便查看FAT中第10个(由0编起)的long值磁区编号,便是下一个。
如此一路查下去,便可以得到整个资料串所使用的磁区数和次序。
应该很简单吧?
这裡有一个情况必须特别处理的,那就是超大型的OLE档。
由于FAT表使用的磁区是放在Ole档头4Ch开始的地方,但因为档头只有512byte而已,因此只能记录109个磁区(约为7MB左右)。
如果Ole档更大,使得FAT使用的磁区表记录空间不够使用时,便必须读取44h所指的磁区,视为下一个记录FAT使用磁区的磁区。
只是如果还是不够储存时,再下一个磁区在那裡呢?
其实它是记录在该磁区内容的最后一个值,以串列形式组成(-1表示结束)。
因此在读取整个FAT表时,必须考虑到此一情形。
接下来我们来看看档桉目录结构属性的资料(起始磁区记录在档头)。
这个部份也是一个资料串,因此算出来的位置,都是相对于资料串,你必须换算成是第几个磁区,然后再从FAT裡得到实际所在的磁区。
例如所要的资料是在第540byte处,那麽由资料串开头算起是Sec#1,如果在FAT表裡查到这个资料串的磁区编号为{9,13,22,41},实际所在的磁区便是Sec#13(offset也要重算)。
不懂的话请再想想弄清楚。
档桉目录结构属性的资料,每个佔128byte,并以指标加以串连。
以下便是每个结构属性的值(第一个结构属性就是根目录):
00h:
共64byte,记录资料串名称(unicode),根目录一律为"RootEntry"(把它想成是档名或目录名就对了)
40h(short):
资料串名称的byte长度,0表这个属性没有用到(deleted)
42h(char):
本结构属性的形态,1=目录,2=档桉,5=根目录
44h(long):
上一个结构属性指标,-1表没有
48h(long):
下一个结构属性指标,-1表没有
4Ch(long):
若本结构属性的形态为目录或根目录,则指向本目录裡各子目录/档桉的第一个结构属性(指标)
74h(long):
资料所在的起始磁区
78h(long):
资料的byte数
大家可以看到,这个结构其实和磁碟的档桉目录结构没什麽两样,同样也是树状阶层式的。
其中结构属性指标指的是第几个结构属性值(由0编起),相对于资料串起点的位置,便是指标值*128。
有一点必须特别注意的是,上一个/下一个结构属性指标并非双向链结,而是随意键结,例如:
attr#3:
上一个是#2,下一个是#4
attr#2:
上一个是#5,没有下一个
attr#5:
没有上一个,没有下一个
attr#4:
没有上一个,没有下一个
因此共计有2,3,4,5等4个结构属性。
也就是说,你必须将所有键结的结构属性都展开到,才能得知整层的档桉目录资料。
另外,资料所在的起始磁区,可能指向标准的512byte磁区,也可能指向小资料储存区裡的64byte磁区,其分别在于资料的byte数。
若byte数>=4096,便是储存在512byte磁区,否则便是储存在64byte磁区。
唯一例外的是根目录,一律储存在512byte磁区中。
至于资料串的内容,除了根目录RootEntry外,其他都是使用者自己订的。
因此每个资料串裡的资料如何安排,表示什麽意思,都必须另行处理,这点无关IStorage的事。
IStorage只是尽责地,将使用者要储存的资料,依照上面的格式储存下来而已。
接下来开始说明小资料储存区的部份。
在档头3Ch的地方记录了小资料储存区FAT开始的磁区。
小资料储存区FAT也是一个资料串,必须将整个资料串读入后才能处理。
这个小资料储存区FAT裡的资料格式,和之前提到FAT资料格式都是完全一模一样,以串列的方式来记录各资料串使用的磁区。
但小资料磁区裡的资料实际上在那裡呢?
其实就是根目录裡的资料串。
这也就是为什麽根目录的资料,一律都是放在标准512byte磁区裡。
而在根目录资料串裡,便是以64byte为一单位,切割成小磁区,供小资料存放使用。
于是,要取得一个资料串的过程,便成为:
(1)在档桉目录结构裡,找出该资料串相同名称的属性。
属性裡的资料磁区指标和大小,便是资料所在的位置。
(2)如果资料串的资料,是在标准512byte磁区中,便到FAT表裡找出该资料串使用的磁区,一个一个依次载入。
注意OLE档可能不是刚好512byte,如果是最后一个sector,必须只读取档桉长度剩馀的部份。
(3)如果资料串的资料,是在小资料储存区中,便必须载入根目录的整个资料串,然后到小资料储存区FAT表裡,找出该资料串使用的小资料磁区,再一个一个从根目录资料串的小磁区裡载入。
其实整个OLE物件档的结构应该算是很简单。
各位有空可以去看一下类似的文件(http:
//user.cs.tu-berlin.de/~schwartz/pmh/guide.html),即使你都已经弄懂了OLE物件档,还是可能看不懂这些文件。
2.Office档的摘要内容
Office档的摘要主要有两个部份,分别放在"\005DocumentSummaryInformation"和"\005SummaryInformation"这两个资料串中。
以下便分别加以说明(请配合MSWord的摘要设定操作来看比较容易懂):
(1)"\005DocumentSummaryInformation"资料串
18h(long):
GUID数目
1Ch开始的20byte:
依次存放{GUID+属性组资料位置},前者为16byte,后者为long
在这个资料串裡,可以记录两种属性组,一个是DocumentSummaryInformation,一个是UserDefinedProperties。
以下便是这两个属性组的GUID:
DocumentSummaryInformation:
0x02,0xD5,0xCD,0xD5,0x9C,0x2E,0x1B,0x10,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE
UserDefinedProperties:
0x05,0xD5,0xCD,0xD5,0x9C,0x2E,0x1B,0x10,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE
使用者自订的摘要部份待会再说,底下便先针对标准的摘要部份,也就是DocumentSummaryInformation。
从上述的GUID比对到后,后面便是指向这个属性组(PropertySet)的资料区块。
属性组资料区块的格式如下(这边的位置都是相对于资料区块,而非整个资料串):
00h(long):
资料区块大小
04h(long):
属性数目
08h开始每8byte:
属性编号(long)+属性资料位置
标准的摘要属性编号(PropertyID)是固定的,以下便是各编号的意义(大部份配合MSWord摘要设定就能懂了):
01:
CodePage,long-属性组文字资料使用的编码方式(固定的ProperSetID),这个部份我最后再来说明
02:
Category,字串-类别
03:
PresentationTarget,字串-展示方式(印表机/萤幕),PowerPoint在用的
04:
Bytes,long-文件byte数
05:
Lines,long-文件行数
06:
Paragraphs,long-文件段落数
07:
Slides,long-文件Slides数,PowerPoint在用的
08:
Notes,long-有注记的页数,PowerPoint在用的
09:
HiddenSlides,long-隐藏的Slides数,PowerPoint在用的
0A:
MMClips,long-声音/影片数,PowerPoint在用的
0B:
ScaleCrop,bool-是否需要缩放Thumbnail,FindFile在用的
0C:
HeadingPairs,variant/vector-Office内部在用的,不管它
0D:
TitlesofParts,字串/vector-所有的文件名称(如Excel的Sheet名称,PowerPoint的Slide标题等等)
0E:
Manager,字串-主管
0F:
Company,字串-公司
10:
LinksUpToDate,bool-Office内部在用的,不管它
至于属性的资料,其格式为:
属性资料形态(long)+实际属性资料
由于我们的目的是要建索引,因此只需取出文字的属性资料即可。
文字属性的资料形态为0x1E,实际的属性资料则为:
字串资料byte数+字串资料
如果要解使用者自订的属性组,只需找出所有的属性资料,只要是文字属性的,都加以读出即可。
(2)"\005SummaryInformation"资料串
其格式和"\005DocumentSummaryInformation"完全一模一样,差别只有属性编号的意义。
以下我只挑出重要的列一下:
GUID:
0xE0,0x85,0x9F,0xF2,0xF9,0x4F,0x68,0x10,0xAB,0x91,0x08,0x00,0x2B,0x27,0xB3,0xD9
02:
Title,字串-标题
03:
Subject,字串-主旨
04:
Author,字串-作者
05:
Keyword,字串-关键字
06:
Commenct,字串-注解
其中若标题没有在本资料串时,应该到前述DocumentSummaryInformation资讯裡的TitlesofParts中取得。
关于编码方式(CodePage)的重要值域:
932:
日文
936:
简体中文
949:
韩文
950:
繁体中文
1200:
Unicode
1252:
英文
如果取得的属性值料是字串的话,便必须依照指定的编码方式进行转码的动作。
3.Word97的格式
Word档桉的资料,主要是记录在"WordDocument"资料串与"0Table"/"1Table"资料串中,由于文字资料主要是记录在"WordDocument"资料串中,因此我们先从此一部份着手。
至于EmbededObject是另行记录的,这我们最后再来说明。
"WordDocument"资料串一开头的地方,称为FIB(FileInformationBlock),裡面记录了各种重要的资讯与指标。
因此要解出文字资料,首先必须弄懂FIB。
以下便列出FIB比较重要的部份:
0002h(short):
版本
000Ah(short):
状态旗标(以bit0为最低位元)
bit2(mask=0x0004):
是否为複杂格式
bit8(mask=0x0100):
档桉是否加密
bit9(mask=x00200):
0表使用"0Table"资料串,1表"1Table"资料串
bit14(mask=0x4000):
是否为远东版
000Eh(short):
加密键值
0018h(long):
文字资料起始位置(非複杂格式时)
001Ch(long):
文字资料结束位置+1(非複杂格式时)
0020h(short):
后面的短整数参数数目
0022h开始:
短整数参数,比较重要的是第13个(由0编起,003Ch),若为远东版,则这个参数记录了语系ID(后述)
?
?
?
?
h(short):
后面的长整数参数数目
?
?
?
?
h开始:
长整数参数
#0:
资料串长度
#1:
建立日期
#2:
修改日期
#3:
文件(document)文字长度
#4:
注脚(footnote)文字长度
#5:
页眉(header)文字长度
#6:
巨集(macro)文字长度
#7:
annotation文字长度
#8:
endnote文字长度
#9:
文字块文字长度
#10:
页眉文字块文字长度
?
?
?
?
h(short):
FC/LCB数目
?
?
?
?
h(long/long):
FC/LCB资料
#33:
piecetable位置/大小
其中处理起来比较麻烦的是複杂格式,这是当使用者使用快速存档(QuickSave)时,才会形成的格式,这部份后面再说明。
至于加密的文件,目前不予以处理(要自行对Word档解密,会花太多时间,同时加密文件无法建索引,应该是很正常的)。
关于语系ID的值域,bit0-9=主语系,bit10-15=次语系,相关资料可以参考MSDN,以下是一些辨识方法:
语系ID=0x0404表繁体中文,0x0804表简体中文
主语系=0x09表英文,0x11表日文,0x12表韩文
这些语系资料可以提供,当Word档裡面记录的不是Unicode时,应该如何转码。
这样即使外界设错内码格式,我们仍能正确转成Unicode处理。
以下便开始说明非複杂非加密格式的Word文件如何抽出文字资料。
Word的文字资料,分成document,footnote,header...等好几个部份(参见FIB裡的长整数参数),这些文字资料都是相连接在一起储存的。
储存的起终位置便记录在FIB的18h,1Ch裡。
然而我们却无法直接到裡面取出文字资料,因为这些文字资料是以512byte为一单位放在一起,而且语系并不一定相同(可能是ASCII,也可能是Unicode,这样做的目的当然是要档桉小一些)。
因此我们必须藉助piecetable的资讯来取得真正的文字资料。
至于如何取得,待会再来说明。
因为较早期的word档并没有piecetable,这种情况下表示文字资料是以同一种语系储存的,如此取得文字资料的方式就很简单,只需到文字资料起始位置开始,依照FIB长整数参数裡记录的各部份文字长度,一个一个加以读出处理便可以了。
不过在处理前必须先辨别储存的文字是ASCII形式,还是Unicode形式。
方法就是:
文字byte数=文字资料结束位置-文字资料开始位置
文字总字数=文件文字长度+注脚文字长度+页眉文字长度+....
如果文字总字数*2=文字byte数(unicode每个字是2byte),便是unicode形式。
不过由于Word有时在文字资料最后面会多加一个段落标记(总字数少了),因此判断时要以"文字总字数*2<=文字byte数"为准。
另外,取出的Word文字资料裡也有一些控制字元必须特别加以处理,这些字元包括(以ASCII字码10进位列出):
07:
cellmark
09:
tab
11:
breakline
12:
pagebreak/sectionmark
13:
paragraphend
14:
clumnbreak
19:
fieldbegin
20:
fieldseperator
21:
fieldend
30:
non-breakinghyphen
31:
non-requiredhyphen
160:
non-breakingspace
如果字串是ASCII形式,则还会有以下的一些特殊字元:
85h:
...
92h:
'
93h:
"
96h:
--
97h:
---
其中比较需要注意的是fieldstart(19)/fieldseperator(20)/fieldend(21)等三个字元。
这些字元是用来夹住word的一些特殊标记文字,例如hyperlink的相关资讯,以及"目录"等由word自行做出的结构,其中fieldstart到fieldseperator之间的字串是控制用的(不显示),fieldseperator到fieldend之间的字串则是显示用的,故前半部的资料应泸除,后半部的资料要取出。
如果没有特别处理的话,便会出现一堆如"HYPERLINK\l"TOC1899651""等无意义字串。
如果有piecetable时,文字资料便不能像前面所说的,直接判断并加以读取,必须经由piecetable的资讯加以判断。
piecetable资讯是记录在Table资料串,至于是使用"0Table"资料串,还是"1Table"资料串,可由FIB裡的资讯得知。
取得Table资料串后,piecetable的资讯为:
(byte)1
(short)grpprl大小
grpprl
(byte)1
(short)grpprl大小
grpprl
...
(byte)2
(long)plcfpcd大小
plcfpcd
我们要的是plcfpcd的资讯,因此必须将grpprl全部略过。
plcfpcd主要由PLC和PCD两个阵列所组成,因此必须先算出元素数目:
(plcfpcd大小-4)/12。
其中PLC元素数目要再加一。
PLC阵列的主要目的,是用来记录累积的文字数,因此第i个文字piece的文字字数,便是PLC[i+1]-PLC[i],这也就是为何PLC元素要多一个的原因。
PCD阵列,主要用来取得文字piece的位置,其元素格式如下:
(short)状态值
(long)文字piece的位置,最高的第二个位元(0x40000000)若为1,表示文字是ASCII,否则为Unicode
(short)记录PRM或grpprl的索引值(这部份无关重要,可以不管)
因此要解出所有文字资讯,只须依次取得各文字piece的位置/字数,并决定为何种语系,再加以读出处理即可。
不过要注意的是,文字piece的位置,当形式为ASCII时,其位置会x2,因此换算成实际位置时,必须再除以2。
至于複杂格式是什麽呢?
其实就是文字资料并没有集中放在一起,而是随着编辑过程而分散在各处。
要取得这些文字资料,其实只须依照piecetable裡面的资讯来取便可以了。
4.Word95的格式
Word95的档桉格式,基本上和Word97差不多,然而由于该时期的版本并未支援Unicode,因此档桉中文字的编码并非Unicode,而是以一种很怪、类似于Unicode的方式储存。
也就是说,中/英文都是2byte,但中文记录的不是Unicode,而是它的两个ASCII字码。
例如"一"的BIG5码是A440h,它便将A440h视为一个2byte字码储存起来,因此先存40h,再存A4h。
于是文字资料读取以后,还是必须进行转码的动作,才能得到实际的Unicode。
5.Word更早期版本的格式
Word更早期版本的档桉格式,本身并非OLE物件档。
事实上,该档桉的内容便是OLE裡的WordDocument资料串。
也就是说,当IStorage界面製订出来以后,word便将整个档桉视为资料串,存到OLE物件档裡。
因此要解这种早期版本,只需直接将它视为WordDocument取出的资料串,然后一样到18h的地方读取文字位置和长度,即可解出文字资料。
不过这种早期版本的word档,还没有Unicode的观念,因此存的全部都是ASCII码。
6.RTF档格式
.doc的档桉,不只是Word档格式而已,还可能是RTF档或是纯文字档,因此在处理前必须先行判断。
以下便针对RTF档的格式进行说明。
在说明RTF的格式之前,我们先看一下RTF的一个简单范例:
{\rtf1\ansi\ansicpg950{\fonttbl...}...}
RTF档的内容,主要由三个部份所组成,一是命令,也就是以\开头的字;一是群组,也就是{}括起来的部份;最后一个当然就是资料。
RTF命令的格式如下:
\
keyword必须都是英文字母(RTF档是大小写有关的),或是单一特殊字元。
number可有可无,当有的时候,便做为命令的参数。
这个数字可能是负的(以‘-’做开头)‘而且RTF裡的数字一律为2byte的短整数。
delimitor可以是空白,或者任何一个非英文字母的字元,若是空白便必须将它吃掉,不视为资料处理。
RTF的命令,主要可分为下列三种:
(1)资料属性定义命令:
用来定义资料的相关属性,例如语系、字型、版面等等
(2)资料意义命令:
用来说明资料实际的意义,例如内文、注脚、页眉、字型表等等
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- office 文档 恢复 基础 文件格式