随着恶意PDF文件日益增多,人们对这种文档的恶意代码分析技术也越来越感兴趣。本文将教您如何分析一种特殊类型的恶意PDF文件:它们可以利用内嵌JavaScript解释器的安全漏洞。通过阅读本文,还有助于分析其他类型的恶意PDF文件,如利用PDF解析器内的安全漏洞的情形。虽然几乎所有的恶意PDF文档的攻击目标都是Windows操作系统,但是这里介绍的PDF语言是独立于操作系统的,它同时适用于在Windows、Linux和OSX上的PDF文档。
一、PDF中的Hello World
现在,我们从手工制作一个最简单的PDF文档开始入手,该文档只是在一个页面中显示文字Hello World而已。您很可能从未见过如此简陋的文档,但是它很适合于本文的需要,因为我们只对一个PDF文档的内部构造感兴趣。我们的文档仅仅包含显示一个页面所必需的最基本元素,如果您为该文件添加更多的格式的话,可读性会更好一些。该文档的特性之一是,只包含有一些ASCII字符,因此即使使用记事本这样最简单的编辑器,同样也能阅读它的内容。另一个特性是,其中含有大量(多余的)空格和缩排,这使得这个PDF的结构更加突出。最后一个特性是,其中的内容没有进行压缩处理。
二、头部
每个PDF文档必须以标明其为PDF文档的一行代码(即幻数)开头;它还规定了用于描述这个文档的PDF语言规范版本号:%PDF-1.1。
在一个PDF文档中,以符号%开头的行都是注释行,注释行的内容将被忽略,但是有两个例外:
文档的开头:%PDF-X.Y
文档的结尾:%%EOF
三、对象
在第一行之后,我们开始为我们的PDF文档添加对象,对象是PDF语言的基本元素。这些对象在文件中的出现顺序对页面显示时的布局没有任何影响。不过为简单起见,我们将按照逻辑顺序来介绍这些对象。需要注意的是,PDF语言是大小写敏感的。
我们首先介绍的是catalog(即目录)对象,它告诉PDF阅读程序(例如Adobe的Acrobat Reader),为了装配这个文档,需要从哪里开始查找对象:
1 0 obj
/Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R
|
这实际上是一个间接对象,因为它具有一个编号,并且可以通过该编号来引用该对象。其语法很简单:一个编号、一个版本号、单词obj、对象本身,最后是单词endobj,如下所示:
通过联合使用对象编号和版本号,我们就能够唯一地引用一个对象。
我们第一个对象catalog的类型是字典类型,字典类型在pdf文档中非常常见。该类型以符号<<开头,并以符合>>作为结束,如下所示:
字典的元素由键和值两部分组成,也就是说一个元素就是一个名/值对,即数据有一个名称,还有一个与之相对应的值;字典不仅可以存放元素,而且还能存放对象甚至其他字典。 大部分字典都是利用第一个元素来声明自身的类型,该元素以/type为键,其后跟一个类型本身的名称(对本例而言就是/Catalog)为值:
对象catalog必须给出在这个PDF中能找到的页面(对应于pages对象)和大纲(对应于outline对象),如下:
/Outlines 2 0 R
/Pages 3 0 R
|
2 0 R和3 0 R 分别表示引用间接对象2和间接对象3。间接对象2描述大纲,间接对象3描述页面。
下面开始为我们的PDF文档添加第二个间接对象:
2 0 obj
/Type /Outlines
/Count 0
endobj
|
通过前面对间接对象1的说明,您现在应该对这个对象的语法并不陌生了。这个对象是一个/Outlines类型的字典。它具有一个键为/Count、值为0的元素,这意味着这个PDF文档没有大纲。我们可以通过编号2和版本0来引用这个对象。
让我们总结一下我们的PDF文档已有的内容:
PDF标识行
间接对象1:catalog
间接对象2:outline
在添加文字页面之前,让我们演示PDF语言的另一个特性。我们的1号对象catalog引用了我们的2号对象outline,如图1所示。
|
图1 引用间接对象 |
PDF语言还允许我们把2号对象直接嵌入到1号对象中,如图2所示。
|
图2 被嵌入到对象中的间接对象 |
事实上,outline对象的长度只有一行,并且对语义也没有什么影响,现在只是为了可读性才加上。先不管它,我们继续组装我们的PDF文档。我们前面定义了catalog(目录)和outlines(大纲)对象,接下来还得定义我们的页面。
除/Kids元素之外,下面的代码应该很容易理解。Kids 元素是一个页面列表;一个列表必须用方括弧括住。因此依据这个Pages对象来看,我们的文档中只有一个页面;这个页面的具体规定,见间接对象4(注意引用4 0 R ):
3 0 obj
/Type /Pages
/Kids [4 0 R]
/Count 1
endobj
|
要描述页面,我们必须规定页面的内容、用于显示这个页面的资源以及页面的大小。这些任务可由下面的代码来完成:
4 0 obj
/Type /Page
/Parent 3 0 R
/MediaBox [0 0 612 792]
/Contents 5 0 R
/Resources <<
/ProcSet [/PDF /Text]
/Font << /F1 6 0 R >>
endobj
|
页面内容是由间接对象5来规定的。/MediaBox 是页面的尺寸。这个页面所用的资源是字体和PDF文字绘制例程。我们在间接对象6中将字体规定为[F1]。
间接对象5中存放的是页面内容,它是一种特殊的对象,即流对象。流对象可以用来保存由单词stream和endstream包围的对象内容。流对象的好处是允许使用多种类型的编码技术(在PDF语言中称为过滤器),例如压缩(例如zlib FlateDecode编码)。考虑到易读性,我们没有在这个流中实施压缩处理:
5 0 obj
/Length 43 >>
stream
BT /F1 24 Tf 100 700 Td (Hello World) Tj ET
endstream
endobj
|
这个流的内容是一组PDF文字绘制指令。这些指令是由BT和ET括起来的,实际上就是命令绘制例程做下面的事情:
使用大小为24的F1字体
转到100 700位置处
绘制文字:Hello World
在PDF语言中,字符串必须用圆括号括起来。
我们的PDF文档已经基本上组装好了。我们需要的最后一个对象是font(字体)对象:
6 0 obj
/Type /Font
Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Encoding /MacRomanEncoding
endobj
|
现在,阅读这个结构您应该没有问题了。
|