PDF文件解析与PDF恶代分析中的一些坑

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介:

一、简介

最近在做文档类的恶代检测,写个总结。

本篇文章负责介绍pdf文档的格式以及恶代分析中需要注意的问题以及相应工具推荐。希望能给各位做恶代分析时提供一些帮助。

后序会更新一些其他文档格式解析与恶代分析内容等,欢迎各位关注。

PDF文件解析与PDF恶代分析中的一些坑

二、PDF文件格式介绍

PDF(便携式文件格式,Portable Document Format)是由Adobe Systems于1993年基于文件交换所发展出的一种文件格式。Adobe公司素有“漏洞之王”的美誉,所以学习PDF文件格式对研究分析漏洞具有极大帮助。PDF格式较为复杂,本文以研究漏洞的目的分析PDF格式,探寻如何找出并分析PDF中存在的恶意代码,而并非做一个详细的PDF parser解析器,因此会省略对不相关关键字的介绍,请各位留意。

PDF的结构可以从文件结构和逻辑结构两个方面来理解。PDF的文件结构指的是其文件物理组织方式,逻辑结构则指的是其内容的逻辑组织方式。

1. PDF的文件结构

PDF文件格式包含以下4个部分:

  • 文件头——指明了该文件所遵从的PDF规范的版本号,它出现在PDF文件的第一行。
  • 文件体——又称对象集合,PDF文件的主要部分,由一系列对象组成。
  • 交叉引用表——对对象进行随机存取而设立的一个间接对象的地址索引表。(实际以偏移+索引的方式储存对象地址,下文会提及)
  • 文件尾——声明了交叉引用表的地址,即指明了文件体的根对象(Catalog),从而能够找到PDF文件中各个对象体的位置,达到随机访问。另外还保存了PDF文件的加密等安全信息。

2. PDF文件格式图示:

PDF文件的逻辑结构

3. PDF文件的逻辑结构

本段主要介绍PDF文件体的读取方式。

作为一种结构化的文件格式,一个PDF文档是由一些称为“对象”的模块组成的。每个对象都有数字标号,这样的话可以这些对象就可以被其他的对象所引用。这些对象不需要按照顺序出现在PDF文档里面,出现的顺序可以是任意的,比如一个PDF文件有3页,第3页可以出现在第1页以前,对象按照顺序出现唯一的好处就是能够增加文件的可读性,对象的信息以偏移+索引的形式保存在交叉引用表内。

文件尾说明了根对象的对象号,并且说明交叉引用表的位置,通过对交叉引用表的查询可以找到目录对象(Catalog)。这个目录对象是该PDF文档的根对象,包含PDF文档的大纲(outline)和页面组对象(pages)引用。大纲对象是指PDF文件的书签树;页面组对象(pages)包含该文件的页面数,各个页面对象(page)的对象号。

4. PDF的层级结构图示:

 PDF的层级结构图示

页面(page)对象为PDF中最重要的对象,包含如何显示该页面的信息,例如使用的字体,包含的内容(文字,图片等),页面的大小。里面的信息可以直接给出,当然里面的子项更多的是对其他对象的引用,真正的信息存放在其他对象里面。页面中包含的信息是包含在一个称为流(stream)的对象里,这个流的长度(字节数)必须直接给出或指向另外一个对象(包含一个整数值,表明这个流的长度)。

可见stream流对象我们恶代分析需要获取的重点。

5. 页面信息图示:

页面信息图示

理解了上面的内容之后,我们可以得出针对恶代分析的PDF文件的大致解析思路:

针对恶代分析的PDF文件的大致解析思路

当然,也可以采取针对PDF层级结构的文档解析方式,见仁见智,因人而异。

三、以二进制文本解析Pdf文档结构

PDF文件是一种文本和二进制混排的格式,但是Adobe更愿意让人把它当成二进制的文件,所以,PDF文件可以直接拖入16进制编辑器中打开。前面我们介绍了PDF的文件结构以及逻辑结构,现在我们在16进制编辑器中打开PDF文件,更直白的展示PDF的关键字段以及文件结构。


 
 
  1. %PDF-1.6 #文件头+版本号,16进制读取文件0x25 0x50 0x44 0x46开头即证明是pdf文件 
  2. %çóÏÓ    #下面就是很多的Object对象 
  3. 2 0 obj  #Object对象,其中2是Obj顺序号,0是Obj的版本号,obj也是对象开始的标志 
  4. <<       #<<>>之间为Object对象的字典内容,包含关键字 
  5. [/ICCBased 3 0 R] 
  6. >> 
  7. Endobj   #Object结束关键字 
  8. 7 0 obj 
  9. << 
  10. /Filter 
  11. /FlateDecode   #流对象的压缩方式为/FlateDecode   
  12. /Length 148    #流对象的长度 
  13. >> 
  14. Stream         #流对象 
  15. #文件内容信息,注:此处为直观从而手动填写的 
  16. Endstream      #流对象结束标志 
  17. Endobj 
  18. 8 0 obj 
  19. << 
  20. /Contents 7 0 R              #页面内容对象的对象号为7 
  21. /MediaBox [0 0 595.2 841.68] #页面显示大小,以像素为单位 
  22. /PageIndex 1 
  23. /Parent 1 0 R               #其父对象号为1以及Pages对象 
  24. /Resources                  #该页包含的资源 
  25. <</Font <</F4 4 0 R >>      #字体的类型 
  26. /Shading <<>> 
  27. /XObject <<>>               #外部对象 
  28. /ColorSpace <</CS1 2 0 R>>  
  29. >> 
  30. /Type /Page 
  31. >> 
  32. Endobj 
  33. 1 0 obj 
  34. << 
  35. /Count 1        #页码数量为1 
  36. /Kids [8 0 R ]  #kids对象说明它的子页对象为8 
  37. /Type /Pages 
  38. >> 
  39. Endobj 
  40. 13 0 obj 
  41. << 
  42. /Author (? Cryin') 
  43. /CreationDate (D:20100926145832+08'00') 
  44. /Title (? PDF文件格式分析) 
  45. >> 
  46. endobj 
  47. Xref           #表示交叉引用表开始 
  48. 0 14           #0表明引用表描述的对象编号从0开始,8说明共有8个对象#此行在交叉引用表中可出现多个 
  49. 0000000000 65536 f #一般pdf都是以这行开始交叉引用表的,起始地址0和产生号 
  50. 0000003195 00000 n #表示对象1,就是catalog,3195为偏移地址n表示对象在使用 
  51. 0000000018 00000 n 
  52. 0000000051 00000 n 
  53. 0000003464 00000 n 
  54. 0000000000 00000 f 
  55. 0000004282 00000 n 
  56. 0000002728 00000 n 
  57. 0000002992 00000 n 
  58. 0000003256 00000 n 
  59. 0000003892 00000 n 
  60. 0000003620 00000 n 
  61. 0000008660 00000 n 
  62. 0000008712 00000 n 
  63. Trailer      #说明文件尾对象开始 
  64. <</Size 14   #14说明PDF文件对象数目 
  65. /Root 12 0 R #说明跟对象号为12 
  66. /Info 13 0 R>> 
  67. startxref 
  68. 8980     #8980为交叉引用表的偏移地址,此处为十进制表示 
  69. %%EOF    #文件结束标志 

对于对象的额外解释:如果一个样本文件的交叉引用表格式如下


 
 
  1. xref 
  2. 0 5 
  3. 0000000000 00000 n #第1行 
  4. 0000004996 00000 n #第2行 
  5. 0000000022 00000 n #第3行 
  6. 0000005101 00000 n #第4行 
  7. 0000004976 00000 n #第5行 
  8. 0000004996 00000 n #第n行 
  9. 4 0 obj 
  10. Xxxxx 
  11. endobj 

即交叉引用表中第五行顺序数为4的对象,其偏移为4976

四、Pdf文件混淆

如图,下面的样本进行了混淆


 
 
  1. %PDF-1.5 
  2. 1 0 obj 
  3. <</#54#79P#65 R 0 5 
  4. O#70e#6e#41c#74i#6fn 3 Pages C#61ta#6c#6f#67>> 
  5. endobj 

解释:<<>>代表obj对象之间的字典内容,保存了流的关键字和特征信息,因此去除混淆是必要的第一步操作,pdf文件的混淆只出现在这里#54代表0x54,上面的内容去除混淆之后即为


 
 
  1. 1 0 obj 
  2. <</TyPe R 0 5 OpenAction 3 Pages 
  3. Catalog>> 
  4. endobj 

五、关键字

下面介绍了PDF文件解析时所需要的关键字


 
 
  1. obj            #obj对象开始 
  2. endobj         #obj对象结束 
  3. stream         #stream流对象开始 
  4. endstream      #stream流对象结束 
  5. xref           #交叉引用表开始 
  6. trailer        #文件尾对象开始 
  7. startxref      #交叉引用表结束 
  8. /Page        #文件页数 
  9. /Encrypt       #是否加密 
  10. /ObjStm        #objectstreams的数量,objectstreams可包含其他Object对象,即嵌套 
  11. /JS            #代表javascript嵌有JavaScript代码,可直接提取恶意代码 
  12. /JavaScript    #代表javascript嵌有JavaScript代码,可直接提取恶意代码 
  13. /AA            #以下三个为特定特征,打开对象自动执行 
  14. /OpenAction 
  15. /AcroForm 
  16. /URI           #内嵌url链接 
  17. /Filter        #/Filter字段出现,表示了下面的stream流进行了加密 
  18. /RichMedia     #富文本 
  19. /Launch        #执行Action的次数与OpenAction字段关联 
  20. #/xxxx 带斜杠的关键字包含在<<>>字典内部 

六、流的提取

/Filter关键字之后保存了stream流的编码信息一共包括以下几种:


 
 
  1. /FlateDecode 
  2. /ASCIIHexDecode 
  3. /ASCII85Decode 
  4. /LZWDecode 
  5. /DCTDecode 
  6. /RunLengthDecode 
  7. /CCITTFaxDecode 
  8. /JBIG2Decode 
  9. /JPXDecode 
  10. /Crypt 

一共包括上面几种编码方式,按常见顺序进行了排序,可以级联编码。例如:


 
 
  1. 0 0 obj 
  2. <</Filter 
  3. [/FlateDecode /ASCIIHexDecode] 
  4. /Length 14278>> 

表示流先经过了ASCIIHexDecode再经过了FlateDecode编码解密是即先对流进行FlateDecode解码再对流进行ASCIIHexDecode解码目前遇到2种级联编码样本(如上),可能会有更多级联编码方式(3级或以上)解码后能够触发攻击的流对象为javascript脚本或者图片对象,常见的恶意攻击代码储存在javascript脚本中。

下面的图片是提取自样本中的PDF steam流文件中的js脚本,已经很明显是攻击代码了:

七、一些坑

PDF的恶意攻击样本毫无疑问会使用一些特殊手段对抗杀软的扫描检查,下面统计了一下恶意样本常见的规避行为:

1. 交叉引用表

(1) 坑1 引用表偏移不正确


 
 
  1. Xref    #表示交叉引用表开始 
  2. 0 2     #0表明引用表描述的对象编号从0开始,8说明共有8个对象 
  3. 0000000000 65536 f #一般pdf都是以这行开始交叉引用表的,起始地址0和产生号 
  4. 0000003195 00000 n #表示对象1,就是catalog,3195为偏移地址n表示对象在使用 
  5. startxref 
  6. 8980  #8980为交叉引用表的偏移地址,此处为十进制表示 
  7. %%EOF #文件结束标志 

上面有提到过交叉引用表的偏移地址为固定数值,推测adobe的parser是从文件尾开始解析,获得交叉引用表的偏移地址(Xref中X在文档中所在的位置即为偏移地址),找到交叉引用表再定位到各个对象,实际测试发现偏移地址可以不正确8980偏移地址实际可能为任意地址。

(2) 坑2 引用表可以有多个


 
 
  1. xref 
  2. 0 4 
  3. 0000000000 65535 f 
  4. 0000000000 65536 n 
  5. 0000039095 00000 n 
  6. 0000000015 00000 n 
  7. trailer 
  8. <</ID 
  9. [<386e381fac5d8245e24ee620741d0d06><b15c4bebcdae6f2210f09460b841e7a3>]/Root 
  10. 26 0 R/Size 28/Info 27 0 R>> 
  11. startxref 
  12. 39630 
  13. %%EOF 
  14. <</Filter/FlateDecode/Length 
  15. 6960/Subtype/Type1C>> 
  16. Stream 
  17. Ddd 
  18. endstream 
  19. xref 
  20. 20 1 
  21. 0000040341 00000 n 
  22. 26 4 
  23. 0000040380 00000 n 
  24. 0000040484 00000 n 
  25. 0000040677 00000 n 
  26. 0000040734 00000 n 
  27. 55 2 
  28. 0000172790 00000 n 
  29. 0000172925 00000 n 
  30. trailer 
  31. <</Root 26 0 R/Info 27 0 
  32. R/ID[<386E381FAC5D8245E24EE620741D0D06><39FE58436C8CC909F538F88909F1EE55>]/Size 
  33. 63/Prev 39630>> 
  34. startxref 
  35. 173446 
  36. %%EOF 

样本如上,正常来讲,一个文档只存在一个%EOF结束符,但是这个样本里出现了两个

2. 字符串长度

(1) 坑1 流对象长度可以直接跟对象

正常一个字典语句中/Length之后的数值代表stream~endstream两个关键字之间流的长度,如下


 
 
  1. 7 0 obj 
  2. <</Filter/FlateDecode/Length 6960/Subtype/Type1C>> 
  3. stream 

但测试发现流的长度可以是obj对象


 
 
  1. 2 0 obj 
  2. << /Length 4 0 R /Filter 
  3. /FlateDecode >> 
  4. #对应的obj对象中包含的长度如下 
  5. 4 0 obj 
  6. 4880 
  7. endobj 

所以stream流对象压缩前的实际长度为4880虽然是PDF格式的正规使用方法,但同时也是规避杀软的一种手法。

(2) 坑2 流对象长度可以为任意值


 
 
  1. 7 0 obj 
  2. <</Filter/FlateDecode/Length 
  3. 6960/Subtype/Type1C>> 
  4. stream 

同理,正常流对象长度为上图,实际测试发现样本


 
 
  1. 16 0 obj 
  2. << 
  3. /Length ANIWAY_____LEN 
  4. >> 
  5. stream 

WTF is ANIWAY_LEN??? 长度可以为填ascii字符???所以,/Length后面可以不跟数值stream流的实际长度实际==关键字endstream偏移-关键字stream偏移-包含的0x0D或0x0A

3. 解码问题

(1) 坑1 javascript可以支持文本和八进制


 
 
  1. 7 0 obj 
  2. << /Type /Action 
  3. /S /JavaScript 
  4. /JS (\145\166\141\154\050\146\165\156) 
  5. endobj 
  6. /JS 16 0 R 
  7. /S /JavaScript 
  8. >> 
  9. endobj 
  10. 16 0 obj 
  11. << 
  12. /Length ANIWAY_____LEN 
  13. >> 
  14. stream 
  15. function urpl(k,sc){ 
  16. var c = "\x75"
  17. var kkc=k+c; 
  18. var re = /MM/g; 
  19. scsc = sc.replace(re,kc); 
  20. return sc; 
  21. padding_0c = "MM0c0cMM0c0c"
  22. padding00="MM0000"
  23. padding_41 = "MM4141"
  24. var x1=0
  25. var x2=0
  26. var x3=0
  27. endstream 
  28. endobj 

有JS编程基础的肯定注意到了,因此在这里需要判断javascript内容在对象中还是在()内亦或是否需要转码

(2) 坑2 编码方式缩写形式


 
 
  1. 1 0 obj 
  2. <</Filter [/Fl /Fl] /Length 8331 
  3. >> 
  4. Stream 
  5. xœíÜYXù 
  6. ÷qÆÌ0K3ÆØRÆVdoUƒQ"[M!S(íRÓ¾0–h¥B*d•hß~5´© 
  7. endstream 

正常文件默认/Filter关键字之后会出现xxdecode关键字表示stream流编码方式,但测试发现样本可以没有xxdecode关键字,但同样进行了编码处理,如上/Fl字段即为/FlateDecode的简写,对应表如下:


 
 
  1. /FlateDecode     /Fl 
  2. /ASCIIHexDecode  /AHx 
  3. /ASCII85Decode   /A85 
  4. /LZWDecode       /LZW 
  5. /RunLengthDecode /RL 
  6. /CCITTFaxDecode  /CCF 
  7. /JBIG2Decode #/JBIG2Decode其实和/DCTDecode解码方式是一样的 
  8. /DCTDecode       /DCT 

(3) 坑3 编码形式可以级联


 
 
  1. 10 0 obj 
  2. <</Filter 
  3. [/FlateDecode /ASCIIHexDecode] 
  4. /Length 14278>> 

如上表示流先经过了ASCIIHex加密再进行了FlateDecode加密解码时需要先进行FlateDecode解密之后再进行ASCIIHexDecode解密

八、常用分析工具推荐

介绍完恶代格式后,推荐一些恶代分析的基本工具

1. PdfStreamDumper

  • stream流解析工具
  • vb开源项目。工具存在一些bug,无法解析级联编码后的stream流,例如/Fl/Fl编码就无法解析

2. PDFParser

  • c++开源项目,pdf格式解析,逻辑比较清晰,可以参考

3. ParanoiDF

  • python开源项目,恶代分析
  • 此外还有一些很赞的开源项目如pyew、peepdf等等,不一一贴地址了。

4. References

  • PDF, let me count the way...
  • Cryin/PDFTear
  • Adobe PDF 官方文档
  • Malicious Document PDF analysis in 5 steps
  • C#实现的PDF解析器

原文发布时间为:2017-11-06

本文作者: red0range

本文来自云栖社区合作伙伴51CTO,了解相关信息可以关注51CTO。

目录
相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
68 2
|
27天前
|
数据采集 自然语言处理 搜索推荐
基于qwen2.5的长文本解析、数据预测与趋势分析、代码生成能力赋能esg报告分析
Qwen2.5是一款强大的生成式预训练语言模型,擅长自然语言理解和生成,支持长文本解析、数据预测、代码生成等复杂任务。Qwen-Long作为其变体,专为长上下文场景优化,适用于大型文档处理、知识图谱构建等。Qwen2.5在ESG报告解析、多Agent协作、数学模型生成等方面表现出色,提供灵活且高效的解决方案。
131 49
|
3天前
|
JavaScript
jquery图片和pdf文件预览插件
EZView.js是一款jquery图片和pdf文件预览插件。EZView.js可以为图片和pdf格式文件生成在线预览效果。支持的文件格式有pdf、jpg、 png、jpeg、gif。
32 16
|
18天前
|
测试技术 开发者 Python
使用Python解析和分析源代码
本文介绍了如何使用Python的`ast`模块解析和分析Python源代码,包括安装准备、解析源代码、分析抽象语法树(AST)等步骤,展示了通过自定义`NodeVisitor`类遍历AST并提取信息的方法,为代码质量提升和自动化工具开发提供基础。
32 8
|
16天前
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
34 4
|
26天前
|
消息中间件 存储 Java
RocketMQ文件刷盘机制深度解析与Java模拟实现
【11月更文挑战第22天】在现代分布式系统中,消息队列(Message Queue, MQ)作为一种重要的中间件,扮演着连接不同服务、实现异步通信和消息解耦的关键角色。Apache RocketMQ作为一款高性能的分布式消息中间件,广泛应用于实时数据流处理、日志流处理等场景。为了保证消息的可靠性,RocketMQ引入了一种称为“刷盘”的机制,将消息从内存写入到磁盘中,确保消息持久化。本文将从底层原理、业务场景、概念、功能点等方面深入解析RocketMQ的文件刷盘机制,并使用Java模拟实现类似的功能。
40 3
|
1月前
|
存储
文件太大不能拷贝到U盘怎么办?实用解决方案全解析
当我们试图将一个大文件拷贝到U盘时,却突然跳出提示“对于目标文件系统目标文件过大”。这种情况让人感到迷茫,尤其是在急需备份或传输数据的时候。那么,文件太大为什么会无法拷贝到U盘?又该如何解决?本文将详细分析这背后的原因,并提供几个实用的方法,帮助你顺利将文件传输到U盘。
|
27天前
|
数据采集 存储 自然语言处理
基于Qwen2.5的大规模ESG数据解析与趋势分析多Agent系统设计
2022年中国上市企业ESG报告数据集,涵盖制造、能源、金融、科技等行业,通过Qwen2.5大模型实现报告自动收集、解析、清洗及可视化生成,支持单/多Agent场景,大幅提升ESG数据分析效率与自动化水平。
|
2月前
|
数据安全/隐私保护 流计算 开发者
python知识点100篇系列(18)-解析m3u8文件的下载视频
【10月更文挑战第6天】m3u8是苹果公司推出的一种视频播放标准,采用UTF-8编码,主要用于记录视频的网络地址。HLS(Http Live Streaming)是苹果公司提出的一种基于HTTP的流媒体传输协议,通过m3u8索引文件按序访问ts文件,实现音视频播放。本文介绍了如何通过浏览器找到m3u8文件,解析m3u8文件获取ts文件地址,下载ts文件并解密(如有必要),最后使用ffmpeg合并ts文件为mp4文件。
|
2月前
|
Java Apache Maven
将word文档转换成pdf文件方法
在Java中,将Word文档转换为PDF文件可采用多种方法:1) 使用Apache POI和iText库,适合处理基本转换需求;2) Aspose.Words for Java,提供更高级的功能和性能;3) 利用LibreOffice命令行工具,适用于需要开源解决方案的场景。每种方法都有其适用范围,可根据具体需求选择。

推荐镜像

更多