之前的博文中介绍了IMAGE_FILE_HEADER结构,现在来讨论比较复杂的“可选文件头”结构体。(转载请指明来自breaksoftware的csdn博客)先看下其声明

typedef struct _IMAGE_OPTIONAL_HEADER {//// Standard fields.//WORD    Magic;...DWORD   BaseOfData;    // not exist in PE32+//// NT additional fields.//DWORD   ImageBase;...IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

看一下64位版本该结构体

typedef struct _IMAGE_OPTIONAL_HEADER64 {WORD        Magic;...DWORD       BaseOfCode;ULONGLONG   ImageBase;...IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

我们观察这个32位版本结构体,可以看到该结构体包含两块数据:Standard fields和NT additional fields。我们可以猜想到,该结构体应该在第一个NT操作系统之前就存在了,只是当时其内容只有Standard fields(以后称为标准域)下的内容,后来NT系统增加了NT additional fields(以后称为扩展域)下元素。

此处需要特别注意一点,我们看两个在WinNT.h中定义的结构体

typedef struct _IMAGE_NT_HEADERS64 {DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;typedef struct _IMAGE_NT_HEADERS {DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

该结构给出了PE文件头的结构体布局,但是切记,这仅仅是布局。我们千万不要想当然的认为直接从PE头部开始将IMAGE_NT_HEADERS32(64)结构体大小的数据拷贝到该结构体对象中。

memcpy( &ImageNTHeader32,lpPEStart,sizeof(IMAGE_NT_HEADERS32 );// 这是错误的!!

为什么?因为一个文件中不一定有完整的IMAGE_OPTIONAL_HEADER32(64)结构体对象信息。原因在《可选文件头1》做了介绍,IMAGE_FILE_HEADER中字段SizeOfOptionalHeader指定了该文件中保存的“可选文件头”真实长度,我们应该根据该元素来给IMAGE_OPTIONAL_HEADER32(64)对象赋值。

我们的文件是使用IMAGE_OPTIONAL_HEADER32还是IMAGE_OPTIONAL_HEADER64结构体呢?可能有人会记起,我们在《可选文件头1》中介绍了判断文件是32位还是64位的方法,我们是否可以通过该判断的结果来判断是哪种结构体呢?最开始我也是这么想的,后来我发现我电脑上Microsoft Visual Studio 10.0\VC\lib\amd64\Microsoft.VisualC.STLCLR.dll文件是个64位文件但是使用了IMAGE_OPTIONAL_HEADER32结构体!!!是不是很惊讶!我不知道微软这么设计的原因,但是我知道了通过之前判断是否为64位文件来决定可选文件头结构体类型是错误的。那如何判断呢?

其实是有标记的。紧跟着IMAGE_FILE_HEADER结构体的肯定是IMAGE_OPTIONAL_HEADER32(64)的Magic字段。如果该字段是0x010B,则是使用了IMAGE_OPTIONAL_HEADER32(称为PE32);如果是0x020B,则使用了IMAGE_OPTIONAL_HEADER64(称为PE32+)。切记PE32和PE32+和这个文件是32位文件还是64位文件是没有关系的!它们是两种不同的概念!切记要分清。

BOOL CGetPEInfo::GetOptionalHeader(){CHECKOPHEADER();GETFILETYPE();size_t unDwordSize = sizeof(DWORD);size_t unImgFileHeaderSize = sizeof(IMAGE_FILE_HEADER);size_t unImgOpHeaderSize = 0;LPBYTE lpImgOpHeaderAddr = m_lpPEStart + unDwordSize + unImgFileHeaderSize;LPVOID lpOpHeaderStart= NULL;WORD dwOptionHeader = 0;if ( FALSE == SafeCopy( &dwOptionHeader, lpImgOpHeaderAddr, sizeof(WORD) ) ) {return FALSE;}if ( E64Bit == m_eFileType && PE32MAGICNUM == dwOptionHeader ) {// D:\Microsoft Visual Studio 10.0\VC\lib\amd64\Microsoft.VisualC.STLCLR.dll//_ASSERT(FALSE);}if ( IMAGE_NT_OPTIONAL_HDR32_MAGIC == dwOptionHeader ) {// 64位系统文件也存在该格式可选头m_eFileOpType  = EOp32;unImgOpHeaderSize = sizeof(IMAGE_OPTIONAL_HEADER32);lpOpHeaderStart = &m_OptionalHeader32;}else if ( IMAGE_NT_OPTIONAL_HDR64_MAGIC== dwOptionHeader ) {m_eFileOpType = EOp32Plus;unImgOpHeaderSize = sizeof(IMAGE_OPTIONAL_HEADER64);lpOpHeaderStart = &m_OptionalHeader64;}else {_ASSERT(FALSE);return FALSE;}memset( lpOpHeaderStart, 0 , unImgOpHeaderSize);// 根据镜像文件头中可选文件头大小拷贝数据BOOL bSuc = SafeCopy( lpOpHeaderStart, lpImgOpHeaderAddr, m_FileHeader.SizeOfOptionalHeader );if ( bSuc ) {m_dwInfoMask |= OPHEADER;}else {_ASSERT(FALSE);}if ( EOp32 == m_eFileOpType ) {m_dwFileAlignment = m_OptionalHeader32.FileAlignment;}else {m_dwFileAlignment = m_OptionalHeader64.FileAlignment;}return bSuc;
}

现在我们将重心放到IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];这个数组元素,我在《可选文件头1》中对此有了点描述,而且我还说可选文件头大小要看这个数组元素的“位置”(而不是个数)来决定的。现在我来细说下。先看下微软的声明

typedef struct _IMAGE_DATA_DIRECTORY {DWORD   VirtualAddress;DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

DataDirectory保存了指向“块信息”的目录信息,其中包括偏移(除了IMAGE_DIRECTORY_ENTRY_SECURITY元素是相对文件偏移RA,其他都是相对虚拟首地址偏移RVA)和大小。如果某文件只包含IMAGE_DIRECTORY_ENTRY_EXPORT(0) 、IMAGE_DIRECTORY_ENTRY_IMPORT(1) 和IMAGE_DIRECTORY_ENTRY_BASERELOC(5)等三个目录,则IMAGE_DIRECTORY_ENTRY_EXCEPTION(2)、IMAGE_DIRECTORY_ENTRY_SECURITY(3)和IMAGE_DIRECTORY_ENTRY_SECURITY(4)的信息都要被填充0。于是IMAGE_FILE_HEADER::SizeOfOptionalHeader所指定的可选文件头大小为DataDirectory之前的元素总大小加上6(最后一个目录IMAGE_DIRECTORY_ENTRY_BASERELOC所在的位置5+1)*sizeof(IMAGE_DATA_DIRECTORY)。这就说明了为什么可选文件头大小是根据目录的位置而不是数量来决定的。

下篇博文我们将详细说一下IMAGE_OPTIONAL_HEADER32和IMAGE_OPTIONAL_HEADER64中其他元素的意义。

PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头2相关推荐

  1. PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头1

    本文将讨论PE文件中非常重要的一部分信息.(转载请指明来源于breakSoftware的CSDN博客) 首先说一下VC中对应的数据结构."签名.COFF文件头和可选文件头"这三部分 ...

  2. PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头3

    <PE2>中介绍了一些可选文件头中重要的属性,为了全面起见,本文将会讲解那些不是那么重要的属性.虽然不重要,但是还是可以发现很多好玩的情况.首先看一下32位的可选文件头详细定义.(转载请指 ...

  3. PE文件和COFF文件格式分析——RVA和RA相互计算

    之前几节一直是理论性质的东西非常多.本文将会讲到利用之前的知识得出一个一个非常有用的一个应用.(转载请指明来源于breaksoftware的csdn博客) 首先我们说下磁盘上A.exe文件和正在内存中 ...

  4. PE文件和COFF文件格式分析——节信息

    在<PE文件和COFF文件格式分析--签名.COFF文件头和可选文件头3>中,我们看到一些区块的信息都有偏移指向.而我们本文讨论的节信息是没有任何偏移指向的,所以它是紧跟在可选文件头后面的 ...

  5. PE文件和COFF文件格式分析——导出表的应用——一种摘掉Inline钩子(Unhook)的方法

    在日常应用中,某些程序往往会被第三方程序下钩子(hook).如果被下钩子的进程是我们的进程,并且第三方钩子严重影响了我们的逻辑和流程,我们就需要把这些钩子摘掉(Unhook).本件讲述一种在32位系统 ...

  6. PE文件和COFF文件格式分析——导出表的应用——通过导出表隐性加载DLL

    通过导出表隐性加载DLL?导出表?加载DLL?还隐性?是的.如果觉得不可思议,可以先看<PE文件和COFF文件格式分析--导出表>中关于"导出地址表"的详细介绍.(转载 ...

  7. PE文件和COFF文件格式分析——导出表的应用——一种插件模型

    可能在很多人想想中,只有DLL才有导出表,而Exe不应该有导出表.而在<PE文件和COFF文件格式分析--导出表>中,我却避开了这个话题.我就是想在本文中讨论下载Exe中存在导出表的场景. ...

  8. PE文件和COFF文件格式分析——导出表

    在之前的<PE可选文件头>相关博文中我们介绍了可选文件头中很多重要的属性,而其中一个非常重要的属性是(转载请指明来源于breaksoftware的CSDN博客) IMAGE_DATA_DI ...

  9. PE文件和COFF文件格式分析--概述

    刚工作的时候,我听说某某大牛在做病毒分析时,只是用notepad打开病毒文件,就能大致猜到病毒的工作原理.当时我是佩服的很啊,同时我也在心中埋下了一个种子:我也得有这天.随着后来的工作进行,一些任务的 ...

最新文章

  1. 瑟瑟发抖!2020年博士学位论文将抽查50%、硕士20%!
  2. 通过http协议访问FTP服务器的搭建,ftp+nginx 图片服务器搭建之后使用http访问进行配置文件的修改
  3. Shell之系统函数和自定义函数
  4. 将 Sidecar 容器带入新的阶段
  5. 常用的xshell用vi命令编辑文本
  6. HALCON示例程序color_pieces.hdev通过MLP训练器对彩色棋子进行分类识别
  7. MyEclipse2014配置Git
  8. 试题12 交叉排序(小-大,大-小)
  9. shall 基本语法
  10. 转: DH密钥交换和ECDH原理
  11. 九江学院计算机考研,2017年江西财经大学与九江学院联合培养考研招生事宜的通知...
  12. Android将网页转为pDf,UrlToPDF 输入网址直接将网页转存为 PDF 档(Android)
  13. 【MPEG】DVB / ATSC / ISDB区别
  14. 十年育林,百度NLP已枝繁叶茂
  15. 用计算机判断函数单调性吗,高中数学函数单调性的判断方法(全)
  16. 【第3版emWin教程】第45章 emWin6.x窗口管理器之定时器使用
  17. Flutter高仿微信-第57篇-添加好友
  18. firefly-rk3288j开发板--linux I2C实验之eeprom驱动
  19. 五年磨一剑,灵雀云的蜕变
  20. docker-compose 安装 Kafka 3.X 附带可视化界面

热门文章

  1. 使用Python,OpenCV,面部标志进行面部对齐
  2. (一)神经网络训练不起来怎么办:局部最小值(local minia)与鞍点(saddle point)
  3. 嵌入式系统降低功耗的设计技术
  4. GitHub上共享的简单易用 TensorFlow 代码集
  5. 基于深度学习的目标检测综述
  6. POJ - 2513 Colored Sticks 欧拉通路+并查集+静态树
  7. 哪种营销方法效果最差_今日头条广告投放形式分几种?头条品牌营销曝光效果哪种广告更好?...
  8. srand((unsigned)time(NULL))详解
  9. requests.exceptions.ConnectionError: ('Connection aborted.', BadStatusLine('',))
  10. 在CentOS 6.6 64bit上安装vim智能补全插件YouCompleteMe