#de& ne IMAGE_SIZEOF_FILE_HEADER 20
请注意这个文件头部的大小已经定义在这个包含文件之中了,这样一来,想要得到这个结构
的大小就很方便了。但是我觉得对结构本身使用 sizeof 运算符(译注:原文为“function”)
更简单一些,因为这样的话我就不必记住这个常量的名字
IMAGE_SIZEOF_FILE_HEADER,而只需要记住结构 IMAGE_FILE_HEADER 的名字就可
以了。另一方面,记住所有结构的名字已经够有挑战性的了,尤其在是这些结构只有
WINNT.H 中才有的情况下。
PE 文件中的信息基本上是一些高级信息,这些信息是被操作系统或者应用程序用来决
定如何处理这个文件的。第一个域是用来表示这个可执行文件被构建的目标机器种类,例如
DEC(R) Alpha、MIPS R4000、Intel(R) x86 或一些其它处理器。系统使用这一信息来
在读取这个文件的其它数据之前决定如何处理它。
Characteristics 域表示了文件的一些特征。比如对于一个可执行文件而言,分离调试
文件是如何操作的。调试器通常使用的方法是将调试信息从 PE 文件中分离,并保存到一个调
试文件(.DBG)中。要这么做的话,调试器需要了解是否要在一个单独的文件中寻找调试信
息,以及这个文件是否已经将调试信息分离了。我们可以通过深入可执行文件并寻找调试信
息的方法来完成这一工作。要使调试器不在文件中查找的话,就需要用到
IMAGE_FILE_DEBUG_STRIPPED 这个特征,它表示文件的调试信息是否已经被分离了。
这样一来,调试器可以通过快速查看 PE 文件的头部的方法来决定文件中是否存在着调试信息。
WINNT.H 定义了若干其它表示文件头信息的标记,就和以上的例子差不多。我把研究
这些标记的事情留给读者作为练习,由你们来看看它们是不是很有趣,这些标记位于
WINNT.H 中的 IMAGE_FILE_HEADER 结构之后。
PE 文件头结构中另一个有用的入口是 NumberOfSections 域,它表示如果你要方便
地提取文件信息的话,就需要了解多少个段——更明确一点来说,有多少个段头部和多少个
段实体。每一个段头部和段实体都在文件中连续地排列着,所以要决定段头部和段实体在哪
里结束的话,段的数目是必需的。以下的函数从 PE 文件头中提取了段的数目:
PEFILE.C
int WINAPI NumOfSections(LPVOID lpFile)
{
/* 文件头部中所表示出的段数目 */
return (int)((PIMAGE_FILE_HEADER)
PEFHDROFFSET (lpFile))->NumberOfSections);
}
如你所见,PEFHDROFFSET 以及其它宏用起来非常方便。
PE 可选头部
PE 可执行文件中接下来的 224 个字节组成了 PE 可选头部。虽然它的名字是“可选头部”,
但是请确信:这个头部并非“可选”,而是“必需”的。OPTHDROFFSET 宏可以获得指向可选
头部的指针:
//PEFILE.H
#de& ne OPTHDROFFSET(a) ((LPVOID)((BYTE *)a + \