当前位置:   article > 正文

宏病毒的研究与实例分析02——复合文档格式分析_复合文档分析

复合文档分析


目前主流杀软在处理宏病毒时,都是直接删除含有宏病毒的文档,这样处理显得有些粗暴,将导致用户无法查看文档里的数据,如果是一些重要的业务数据,将造成业务数据的丢失,产生无法估计的后果。本文将介绍一种宏病毒处理思路,在不删除文档文件的情况下清除宏病毒。

复合文档二进制解析

在正式处理宏病毒前,我们必须对Office文档的数据结构以及宏的数据结构有着初步的了解。

复合文档数据结构解析

Office文档(如:.doc、.ppt、.xls等)很多是复合文档(OLE文件),所有文件数据都是存储在一个或多个流中。每个流都有一个相似的数据结构,用于存储元数据的数据结构。这些元数据有用户和系统的信息、文件属性、格式信息、文本内容、媒体内容。宏代码信息也是以这种方式存储在复合文档中的。为了在Office文档文件中提取出宏代码,必须能够解析复合文档的二进制格式,下面以word为例,分析复合文档的二进制结构。

准备工作

(1)准备工具:

Office Visualization Tool:微软提供的office二进制格式查看工具,用于学习doc,xls,ppt等文档二进制格式;

010Editor:一款流行的二进制编辑器。

(2)doc、docx和docm

​ 许多用户在新建word文档时会发现有两个选项,新建Microsoft Word 97 - 2003 文档和新建新建 Microsoft Word文档。当用户点击新建Microsoft Word 97 - 2003 文档时,就会创建出一个.doc文件;而用户点击新建Microsoft Word 文档时,就会创建出一个.docx文件,实际上Microsoft Word 2007及之后的Word版本还支持.docm文件,那么这三种文件有什么区别呢?.doc文件是一种普通的OLE文件(复合文件),能够包含宏。而.docx和.docm文件,实际上都是是压缩文件,下图是.doc文件.docx和.docm文件头二进制数据的对比图:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
将docx.docx文件的文件名修改为docx.zip就可以以压缩包的形式打开.docx文件,如图所示:

在这里插入图片描述

实际上.docx主要的内容基本都存在于word目录下,比较重要的有以下的内容:

  • document.xml:记录Word文档的正文内容

  • footer*.xml:记录Word文档的页脚

  • header*.xml:记录Word文档的页眉

  • comments.xml:记录Word文档的批注

  • footnotes.xml:记录Word文档的脚注

  • endnotes.xml:记录Word文档的尾注

利用同样的方式打开.docm文件,可以发现和.docx文件的内容基本相同,但是比.docx文件多了一个文件:

vbaProject.bin:这是一个复合文件,记录vba工程信息

基础知识

OLE文件数据的存储结构和磁盘数据的存储结构有很大的相似性,都是以扇区为单位进行存储的,但二者的扇区是截然不同的。

对于一个doc文件,其实质是一复合二进制文件(OLE文件),用Office Visualization Tool载入.doc文件,可以看到清晰的文件结构,如图所示:

在这里插入图片描述

首先是文件头Header,是固定的512字节。Header中记录着文件解析必须的所有参数。Header之后的区域是不同的Sector。

Sector(扇区),一般是512字节,是实际数据存储的地方,复合文档中数据都是以扇区为单位进行存储的。扇区有两种Sector和Mini-Sector(Sector:扇区,一般是512字节;Mini-Sector:短扇区,64字节)。扇区内存储的数据有种类有Stroage、Stream、Directory、FAT、Mini-FAT(属于Mini-Sector)、DIF等,但每个扇区中只能存储一种数据类型。每个Sector都有一个SectorID,但是Header所在的扇区ID是-1,并不是0。Header后的Sector才是“第一个”Sector,其SectorID为0[22]。

Storage与Stream的功能相当于文件系统中的文件夹与文件的功能。Storage中是没有任何“实质性”的内容的,只会记录其包含的Stream和Storage,是一个Stream和Storage的目录。“实质性”的内容全在Stream里面。

Directory,中文“目录”,和Storage的“目录”功能有着很大区别,Directory是一个Storage和Stream的索引。这一部分存储空间是用来记录Storage和Stream的存储结构以及名称、大小、起始地址等信息。

FAT是索引表。数据在硬盘上的存储是离散的,需要有一个索引表能找到这些数据,索引表中存放着数据的起始地址(即扇区的),其实每一条索引也是离散的,索引中一般还有一条数据指向下一条索引,类似于链表。FAT实际记录了该扇区指向的下一个扇区的地址。

DIFAT是分区表,是FAT的索引表。FAT也是存储在Sector里面,但是FAT本身也比较大,所以利用DIFAT作为FAT的索引表,记录了FAT所在的Sector的起始地址以及逻辑关系

每个扇区占据512字节,Header占据一个扇区。除去文件头外,扇区ID从0开始。若要读取某个扇区的信息,可以采用以下的公式:

扇区ID x 扇区大小 + 512

但是要长扇区和短扇区,默认扇区是长扇区,长扇区大小为512字节,短扇区大小是64字节。

Header

复合文档的文件的头Header是固定的512字节,其中每一个字节都有十分重要的含义。图所示的是一个doc文件的Header部分:

在这里插入图片描述

下表是对图中这512字节的解析:

字节偏移说明
0x00H~0x07H8个字节:固定值:0xD0 0xCF 0x11 0xE0 0xA1 0xB1 0x1A0xE1,表示此文件是复合文件
0x08H~0x17H16个字节:ClassID,不过很多文件都置0
0x18H~0x19H2个字节:一般为0x3E,表示文件格式的修订号
0x1AH~0x1BH2个字节:一般为0x3,表示文件格式的版本号
0x1CH~0x1DH2个字节:一般0FE 0xFF,表示文档使用的存储格式是小端模式(理论上0xFF 0xFE则表示大端存储方式)
0x1EH~0x1FH2个字节:默认值:0x09 0x00,表示Sector的大小,当值为9时,即每个Sector为512(29)字节
0x20H~0x21H2个字节:默认值:0x06 0x00,表示Mini-Sector的大小,当值为6时,即每个Mini-Sector为64(26)字节
0x22H~0x23H2个字节,UInt16,预留值,置0
0x24H~0x27H4个字节,UInt32,预留值,置0
0x28H~0x2BH4个字节:表示目录扇区Directory Sectors的数量的数量;若版本号为3,则该值为0
0x2CH~0x2FH4个字节:表示FAT的数量
0x30H~0x33H4个字节:表示Directory开始的SectorID文件目录流的起始扇区编号
0x34H~0x37H4个字节:一般置0,用于事务
0x38H~0x3BH4个字节:表示ulMiniSectorCutoff,是最小串(Stream)的最大大小,默认为4096(0x00 0x10 0x00 0x00)
0x3CH~0x3FH4个字节:_sectMiniFatStart ,是MiniFAT表开始的SectorID
0x40H~0x43H4个字节:表示MiniFAT表的数量
0x44H~0x47H4个字节:表示DIFAT开始的SectorID,DIFAT的起始扇区编号
0x48H~0x4BH4个字节:表示DIFAT的数量
0x4CH~0x1FFH436个字节:UInt32,包含了109个DIFAT,每个DIFAT占用4个字节,是前109块FAT表的SectorID。Header中存储的了109个DIFAT数据。但是有时候109个还不够,这时候可以使用其它扇区专门存储DIFAT数据,0x44H~0x47H中记录的就是除这109项外,其他的DIFAT数据的起始扇区编号。在DIFAT扇区的最后4个字节,存储了下个DIFAT扇区的ID,以表示下个DIFAT数据的存储位置
FAT

FAT实际记录了该扇区指向的下一个扇区的地址。

在这里插入图片描述

前文已述一个扇区能且只能存放一种类型的数据,但是复合文档怎么知道扇区中存储的是哪一种类型的数据?因而在复合文档的FAT数据中,除了表示扇区ID的数字,还有些特殊的数字ID表示一些特定的扇区,详细介绍见表:

类型ID值描述
REGSECT0x00000000-0xFFFFFFF9正常的数据扇区ID,表示下一个数据扇区
MAXREGSECT0xFFFFFFFA扇区ID最大值
Not applicable0xFFFFFFFB保留扇区ID
DIFSECT0xFFFFFFFCDIFAT扇区ID,表示该扇区存储了DIFAT的数据
FATSECT0xFFFFFFFDFAT扇区ID,表示该扇区存储了FAT的数据
ENDOFCHAIN0xFFFFFFFE结束字符
FREESECT0xFFFFFFFF空扇区ID

一个扇区大小为512字节,一组FAT信息占据4个字节。因而一个FAT扇区中,最多能够存放128组FAT信息,可以利用DIFAT列表把不同的FAT扇区串联起来。FAT信息在一个扇区内部的存储都是连续的。比如某个FAT扇区,第1组FAT信息代表的扇区的ID是3的话,则这个扇区可以表示的扇区ID范围是3到131。

MINIFAT的解析,其方法和FAT类似,其起始扇区ID存放在Header信息中,MINIFAT表的顺序,当记录在FAT中,这里就不赘述了

Directory

复合文档从字面上理解就是很多内容放在一起复合形成文档,这么多内容当然需要有个目录,而Directory就是这个目录。Directory起始的SectorID需要从Header中读取,定位的方法在上文中提到(512+ 扇区大小 * 扇区ID)。Directory中每个DirectoryEntry固定为128字节,其主要结构如表所示:

偏移描述
0x00H~0x3FH64个字节:存储DirectoryEntry名称的,并且是以Unicode存储的,即每个字符占2个字节,
0x40H~0x41H2个字节:DirectoryEntry名称的长度(包括最后的“\0”)
0x42H~0x42H1个字节:是DirectoryEntry的类型。0为非法,1为目录(storage),2为节点(Stream),5为根节点(Root Entry即第一个目录内容)
0x43H~0x43H1个字节:节点的颜色
0x44H~0x47H4个字节:该DirectoryEntry左兄弟的EntryID(第一个DirectoryEntry的EntryID为0,下同)
0x48H~0x4BH4个字节:该DirectoryEntry右兄弟的EntryID
0x4CH~0x4FH4个字节:该DirectoryEntry一个孩子的EntryID
0x50H~0x5FH16个字节:表示CLSID
0x60H~0x63H4个字节:表示UserFlags,一般是0
0x64H~0x6BH8个字节:表示文件创建时间
0x6CH~0x73H8个字节:表示文件修改时间
0x74H~0x77H4个字节:表示该DirectoryEntry开始的SectorID
0x78H~0x7BH4个字节:是该DirectoryEntry存储的所有字节长度
0x7CH~0x7FH4个字节:保留置0

Directory为Storage的有clsid、time、和sidChild,stream可能没有。Stream有个有效的sectdtart和ulsize,但是storage的是0。

当usize小于ulMiniSectorCutoff时,表示stream使用的是MiniFat。常见的Directory有很多,比较重要的有root entry、WordDocument、1Table、SummaryInformation、DocumentSummaryInformation、Macros和Vba。root entry是Directory里的第一个内容,只是作为根节点,有些文档中直接命名为R,接下来的介绍WordDocument、1Table、SummaryInformation、DocumentSummaryInformation、Macro都是位于root entry节点下的,其中Vba位于Orphaned节点下。DocumentSummaryInformation和SummaryInformation是摘要信息。对于DocumentSummaryInformation,其结构如表所示:

在这里插入图片描述

对于每个属性组,其结构如下:

1.0x00H~0x03H:是属性组大小。

2.0x04H~0x07H:是属性组中属性的个数。

3.从008H开始的每8字节,是属性的信息:

​ 前4字节:属性的种类。

​ 后4字节:属性内容相对于属性组的偏移。

常见的属性编号如表所示:

属性属性
Unknown0x00Scale0x0B
CodePage0x01HeadingPairs0x0C
Category0x02DocumentParts0x0D
PresentationTarget0x03Manager0x0E
Bytes0x04Company0x0F
LineCount0x05LinksDirty0x10
ParagraphCount0x06CountCharsWithSpaces0x11
Slides0x07SharedDoc0x13
Notes0x08HyperLinksChanged0x16
HiddenSlides0x09Version0x17
MMClips0x0AContentStatus0x1B

对于每个属性,其结构如下:

1.0x00H~0x03H:属性内容的类型,值有:

0x02表示UInt16

0x03表示UInt32

0x0B表示Boolean

0x1E表示String

2.属性组中剩下的字节是属性的内容,当属性组中内容类型是String时,剩下字节不定长,剩下三种类型都是4个字节(多余字节置0)。

“WordDocument”和“1Table”是专门存储文档内容的DirectoryEntry。需要说明的是,Word中的存储文档内容的DirectoryEntry与PowerPoint和Excel中都不同,Word中是“WordDocument”和“1Table”,PowerPoint是“PowerPoint Document”,Excel是“Workbook”。

对于WordDocument,其最重要的应该是其中包含的FIB(File Information Block)了,FIB位于WordDocument的开头,其包含着Word文件非常重要的参数,诸如文件的加密方式、文字的编码等等。

FIB是可变长的,开头是固定32字节长的FibBase:

0x00H~0x01H:0xA5EC(0xEC 0xA5),表示Word二进制文件。

0x02H~0x03H:一般是0xC1,表示Word97版本,表示最低版本

在这里插入图片描述

0x0AH~0x0BH:16bit,被分成了13部分。除了第5部分占了4bit外,其余12部分各站1bit,总计16bit。说明下13部分是如何分配的,最左为最低位。

A:为文档是否是.Dot文件(Word模板文件)

B:目前该位没有解析出含义。

C:文档快速保存时生成的格式。

D,文档中是否存在图片。

E,快速保存的次数。

F,文档有没有加密。

G,文字存储的位置,为1表示1Table,为0表示0Table。

H,是否只读方式代开

I,是否含有写保护密码。

J,固定值1。

K,应用程序默认的语言和字体。

L,文档语言是东亚语言。

M,文档加密方式,1表示异或混淆,0则是其他加密方式;如果文档未加密则忽略该值。

0x0CH~0x0DH:一般为0x00BF,少数是0x00C1。

0x0EH~0x11H:文档加密的密钥;否则应置0。

0x12H~0x12H:置0。

0x13H~0x13H:16bit被划分为6部分

第1位:0。

第2位:新建文件的方式。

第3位:页面默认的格式。

第4位:忽略

第5位:忽略。

第6-8位:忽略。

014H~017H:置0,忽略。

018H~01FH:忽略。

FIB中内容仍然有很多,但是了解上述内容就可以读取文档的内容,之后的部分就不再介绍了。

“1Table”和“0Table”也是存储文档内容的目录,有时候也被叫做Table Stream,Table Stream其实是他们的总称,根据FIB中的信息判断文字存于“1Table”还是“0Table”。但是目前仍然不知道文字存储与哪一个扇区中。这些信息都存放在Table Stream中的Piece Table中,Piece Table的位置可以从FIB中获取到。Piece Table,其结构为:

0x00H~0x00H:固定值0x02,Piece Table的标识。

0x01H~0x04H:是Piece Table的大小。

Piece Table中数量的计算公式:

n=(Piece Table的大小 - 4)/(4 + Piece Element的大小)

随后的4*(n + 1)个字节,存储Piece Element中存储的文本的开始位置(结束位置即下一个的开始位置)。

之后的8*n个字节,存储每个Piece Element的信息。

由此可以获取Word中文本的存储位置,文字是按下面的顺序存储的:

正文内容(Main document)

脚注(Footnote subdocument)

页眉和页脚(Header subdocument)

批注(Comment subdocument)

尾注(Endnote subdocument)

文本框(Textbox subdocument)

页眉文本框(Textbox Subdocument of the header)

Macro和Vba的解析与WordDocument的解析类似,需要注意的是其Type标志为1。

补充

在Word中,WordDocument永远是Header后的第一个扇区,但是PowerPoint Document不一定。不过,PowerPoint的数据都存储在PowerPoint Document中。

PowerPoint以Record为基础存储的内容。Record有Container Record和Atom Record两种。类似于Sector中的 Stroge和Stream,Container Record是容器,Atom Record是容器中的内容,那么其实PowerPoint Document中存储结构也类似于WordDocument的其实也就是树形结构。对于每一个Record,其结构如下:

0x00H~0x01H:Record的版本,其中低4位是recVer(如果是0x0F则一定为Container),高12位是recInstance。

0x02H~0x03H:Record的类型recType。

0x04H~0x07H:Record内容的长度recLen。

剩下的字节是Record的具体内容。

由于PowerPoint支持上百种Record,表举一些常用的Record:

值(16进制)值(10进制)描述
0x03E81000DocumentContainer
0x0FF04080MasterListWithTextContainer/SlideListWithTextContainer/NotesListWithTextContainer
0x03F31011MasterPersistAtom/SlidePersistAtom/NotesPersistAtom
0x0F9F3999TextHeaderAtom
0x03EA1002EndDocumentAtom
0x03F81016MainMasterContainer
0x040C1036DrawingContainer
0x03EE1006SlideContainer
0x0FD94057SlideHeadersFootersContainer/NotesHeadersFootersContainer
0x03EF1007SlideAtom
0x03F01008NotesContainer
0x0FA04000TextCharsAtom
0x0FA84008TextBytesAtom
0x0FBA4026CString,储存很多文字的Atom

宏代码数据结构解析

在使用oledump.py提取宏代码数据时,会发现每段宏代码起点处都是一段“Attribute”信息。实际上,我们可以使用“Attribute”字符来定位宏代码在文档中的存储位置。使用010Editor打开demo1.doc,搜索字符串”Attribut”,注意这里不是搜索“Attribute”,不要加e!!!:

在这里插入图片描述

从图中,我们可以看到autoopen()和MsgBox等字符,可以确定这里存储的就是宏代码数据

文档中的宏代码数据是分段存储的,每段最大是0xFFF字节,图中的D3和B0表示此段宏代码数据的长度:0x0*0x100+0xD3=0xD3,0xD3、0xB0后的数据就是宏代码数据(包含宏属性信息)

宏代码里的数据是按组进行划分的,每组包括一个字节的标志,和8个元素:

Flag0x010x020x040x080x100x200x400x80
标志字元素1元素2元素3元素4元素5元素6元素7元素8

一般来说一个元素对应一个字节,标志字Flag的每一位对应一个元素,表示这一个元素的具有两个字节。

在解析宏代码时,就是把这些元素拼接起来,组成完整的宏代码。

说明

  • 本文并非原创,乃是征得作者同意后的转载 原作者为狐狸先生 未经允许,禁止转载
  • 需要相关文件可以到我的Github下载:https://github.com/TonyChen56/Virus-Analysis
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/626595
推荐阅读
相关标签
  

闽ICP备14008679号