PE文件结构详解 超详细 C代码

上传人:ji****72 文档编号:37526078 上传时间:2018-04-17 格式:DOC 页数:25 大小:269.50KB
返回 下载 相关 举报
PE文件结构详解 超详细 C代码_第1页
第1页 / 共25页
PE文件结构详解 超详细 C代码_第2页
第2页 / 共25页
PE文件结构详解 超详细 C代码_第3页
第3页 / 共25页
PE文件结构详解 超详细 C代码_第4页
第4页 / 共25页
PE文件结构详解 超详细 C代码_第5页
第5页 / 共25页
点击查看更多>>
资源描述

《PE文件结构详解 超详细 C代码》由会员分享,可在线阅读,更多相关《PE文件结构详解 超详细 C代码(25页珍藏版)》请在金锄头文库上搜索。

1、翻译:Jason Sun(木水鱼). 2004 年 5 月 10 日译注:仅供大家学习使用,您在复制或使用此文档时请保留这个文件头Peering Inside the PE: A Tour of the Win32 Portable Executable File FormatMatt Pietrek 1994 年 3 月 Matt Pietrek 是 Windows Internals (Addison-Wesley, 1993)的作者。他就职于 Nu-Mega 技术有限公司,可通过 CompuServe: 71774,362 联系到他。 这篇文章出自 1994 年 3 月发行的 Micro

2、soft 系统期刊。版权所有1994 Miller Freeman, Inc.保留所有权利。未经 Miller Freeman 同意,这篇文章的任何部分不得以任何形式被复 制(除了在评论文章里以摘要引用)。一个操作系统的可执行文件的格式在很多方面是这个操作系统的一面镜子。虽然学习一个 可执行文件格式不是大多数程序员的首要任务,但是从中你可学到大量的知识。这篇文章 中, 我将给出 Microsoft 为他们的基于 Win32 的系统所设计的 PE 文件格式的详细说明。可以预 知在未来,PE 文件格式在 Microsoft 的所有操作系统包括 Windows 2000 中都将扮演着很 重要的角色。

3、如果你在使用 Win32s 或 WinNT,那么你已经在使用 PE 文件了。甚至你只是 在 Windows3.1 下用 Visual C+编程,你也已在使用 PE 文件了(Visual C+的 32 位 DOS 扩展组件使用此格式)。简而言之,PE 格式已得到普遍应用并且在不短的将来也不会取消。 现在是时间找出这种新的可执行文件格式为操作系统所带来的影响了。我不会让你盯住无穷无尽的 16 进制 Dumps 和详细讨论页面中每个单独位的重要性。代替的, 我将介绍 PE 文件格式中内含的概念并且把它们和你每天都会遇到的东西联系起来。例如, 线程局部变量的概念,比如declspec(thread)

4、int i; 它使我快要发疯了,直到我明白它是怎样在可执行文件里优雅而简单的实现的。既然你们 大多数都有使用 16 位 Windows 的背景,我将把 Win32 PE 文件格式的结构和与其等价的 16 位的 NE 文件格式联系起来。 除了一个不同的可执行文件格式之外, Microsoft 还引入了一个由它的编译器和汇编器生 成的新的目标模块格式。这个新的 OBJ 文件格式和 PE 格式有许多相同的东西。为了找到这 个新的 OBJ 文件格式的文档我做了许多徒劳的搜索。所以我以自己的理解来解释它,并且 除了 PE 格式之外我会在这里描述它其中的一部分。 大家都知道 Windows NT 继承了

5、VAX VMS 和 UNIX。许多 Windows NT 的创建者在进入 Microsoft 之前都在那些平台上进行设计和编码。当开始设计 Windows NT 时, 很自然的他 们设法使用以前编写的并经过测试的工具以最小化项目启动时间。这些工具生成的并且与 之一起工作的可执行文件和目标模块格式被叫做 COFF(Common Object File Format 的首 字母缩写)。COFF 格式自身是一个好的起点,但需要被扩展以满足一个现代操作系统如 Windows NT 或者 Windows 95 的所有需要。这个扩展的结果就是 PE 格式。它被称为“可移植”是因为 Windows NT 在

6、不同的平台(x86, MIPS, Alpha, 等等)上的所有实现都使用 这个相同的可执行格式。当然,也有不同的地方比如 CPU 指令的二进制编码。重要的是操 作系统加载器和程序设计工具不必为每种 CPU 完全重写。 Microsoft 抛弃了现存的 32 位工具和文件格式的事实证明了他们想让 Windows NT 升级并 且运行的更快的决心。为 16 位 Windows 编写的虚拟设备驱动使用一个不同的 32 位文件布 局LE 格式它在 Windows NT 出现很早以前就存在了。比那更重要的是 OBJ 格式的改变。 在 Windows NT 的 C 编译器以前,所有的 Microsoft

7、编译器使用 Intel OMF(Object Module Format)规范。以前提到,Microsoft 的 Win32 编译器生成 COFF 格式的 OBJ 文件。 一些 Microsoft 的竞争者例如 Borland 和 Symantec 选择放弃 COFF 格式的 OBJs 而坚持 使用 Intel OMF 格式。结果导致生成 OBJs 或 LIBs 的公司为了使用不同的编译器就必须回 去为不同的编译器发布他们产品的不同版本 (如果他们还没有那么做)。 PE 格式在 WINNT.H 头文件中被文档化了。大约在 WINNT.H 文件的中间一个标题为“Image Format”的区域。

8、这块区域的开头是我们熟悉的老的 MS-DOS MZ 格式和 NE 格式文件头接下 来才是更新的 PE 格式的信息。WINNT.H 提供 PE 文件用到的原始数据结构的定义,但只包 含了很少有用的以助于理解这些结构和标志的意思的注释。当使用 WINNT.H 编码时, 类似 这样的表达式很常见:pNTHeader- OptionalHeader.DataDirectoryIMAGE_DIRECTORY_ENTRY_DEBUG.VirtualAddress; 要帮助逻辑地理解 WINNT.H 中的信息,可以阅读 PE 和 COFF 规范。 即刻转到 COFF 格式的 OBJs 文件的主题上来,WIN

9、NT.H 头文件包括 COFF 格式的 OBJ 和 LIB 文件的 structure 定义和 typedefs 定义。不幸的是,我还没有找到上面提到的可执行文件 格式的类似文档。既然 PE 文件和 COFF OBJ 文件是如此的相似,我决定是时候把这些文件 拿出来,并且文档化。 阅读了 PE 文件由什么组成后,你自己也想 Dump 一些 PE 文件来看看这些概念。如果你使用 Microsoft 的工具进行基于 Win32 的开发,DUMPBIN 程序可以分析并把 PE 文件和 COFF OBJ/LIB 文件输出为可读的形式。在所有的 Dump 工具中,DUMPBIN 是容易的和最全面的。 它

10、甚至有一个极好的选项来反汇编它正在解析的文件的代码节(code sections)。Borland 用户可使用 TDUMP 查看 PE 可执行文件,但 TDUMP 不能解析 COFF OBJ 文件。这不是一个大 的问题因为 Borland 编译器首先就不生成 COFF 格式的 OBJs 文件。 我写了一个 PE 和 COFF OBJ 文件的 Dump 程序,PEDUMP(见表 1),我是想提供比 DUMPBIN 更可理解的输出。虽然它没有反汇编器也不能和 LIB 文件一起工作,但它在其它方面和 DUMPBIN 的功能是一样的,并且添加了一些新的特性以使它值得被认同。PEDUMP 的源代码 在任

11、何 MSJ 电子公告板都可找到,因此我不把它在这儿全部列出。代替的,我将会列出一 些 PEDUMP 输出的例子以举例说明我描述到的概念。 表表 1.1. PEDUMP.CPEDUMP.C 略 Win32Win32 和和 PEPE 基本概念基本概念让我们复习一下几个基本概念,这些基本概念渗透于整个 PE 文件的设计(见图 1)。我将 用术语“模块(module)”来表示一个可执行文件或 DLL 加载到内存中的代码,数据和资 源。除了你的程序直接使用的代码和数据之外,一个模块还包括 Windows 用来确定代码和 数据在内存中被载入的位置的支撑数据结构。在 16 位 Windows 中,这些支撑数

12、据结构位于 模块数据库中(HMODULE 指向的一个段)。在 Win32 中,这些数据结构位于 PE 头中,我将 简要地介绍一下这些。图图 1.1. PEPE 文件格式文件格式 对于 PE 文件重要的是要知道磁盘上的可执行文件和在被 Windows 调入内存后是很相似的。 Windows 加载器从磁盘文件创建一个进程时不必很费力。加载器使用内存映射文件机制把 文件中适当的部分映射到虚拟地址空间中。这种方式应用到 PE 格式的 DLLs 也同样容易。 一旦模块被载入,Windows 就能有效的把它和其它内存映射文件同等对待。这和 16 位 Windows 明显不同。16 位 NE 文件加载器读取

13、文件的一部分并且创建完全不同的 数据结构来描述内存中的模块。当一个代码或数据段需要被调入时,加载器必须从全局堆 中分配一个新的段,从可执行文件中找到原始数据的存储位置,转到这个位置,读入原始 数据,并且进行适当的修正。另外,每个 16 位模块都有责任记住它用到的所有段选择器, 不管这个段是否已被丢弃,等等。对 Win32 来说,模块中的代码,数据,资源,导入表,导出表,和其它必需的模块数据结 构用到的所有内存都在一个连续的内存块中。这种情况下你所要知道的就是加载器把文件 映射到内存中的位置。通过存储在映像中的指针你可以很容易地找到模块中的所有部分。 你需要熟悉的另一个概念是相对虚拟地址(RVA

14、)。PE 文件中的许多域都用术语 RVA 指定。 一个 RVA 只是一些项目相对于映射到内存后的文件的偏移。例如,让我们假定加载器把一 个 PE 文件映射到了虚拟地址空间中起始地址为 0x10000 的位置。如果映像中某个表的起始 地址是 0x10464,那么这个表的 RVA 是 0x464。(Virtual address 0x10464)-(base address 0x10000) = RVA 0x00464 要把一个 RVA 转换成一个有用的指针,只要把 RVA 和模块的基址相加就行了。基址是一个 EXE 或 DLL 内存映射的起始地址,在 Win32 中是一个重要的概念。为了方便,W

15、indows NT 和 Windows 95 使用模块的基址作为这个模块的实例句柄(HINSTANCE)。在 Win32 中,把 模块的基址称为 HINSTANCE 可能有点混淆,因为术语“实例句柄”来自于 16 位 Windows。16 位 Windows 中一个应用程序的每个拷贝都有它自己的单独的数据段(和一个关 联的全局句柄) 把它和这个应用程序的其它拷贝区别开来,因此就形成了术语实例句柄。 在 Win32 中,应用程序不必和其它程序区分开,因为它们不会共享相同的地址空间。尽管 如此,术语 HINSTANCE 仍被用来保持 16 位 Windows 和 Win32 之间的连续性。Win3

16、2 中重要 的是你可以为任何 DLL 调用 GetModuleHandle 方法得到一个指针用来访问这个模块的组件。你要知道的关于 PE 文件的最后的概念是“节(Section)”。PE 文件中的一个节和 NE 文 件中的一个段或者资源大致等价。节中包含的不是代码就是数据。和段不同,节是内存中 的连续的空间并且没有大小限制。一些节中包含你的程序中直接声明和使用的代码和数据, 另一些被链接器和库为你创建的数据节中包含操作系统要用到的重要的信息。在 PE 格式的 一些描述中,节也被称为“对象(objects)”。术语“对象(object)”有太多的含义, 因此我将把代码和数据区称为“节(Section)”。 PEPE 头头就像所有其它的可执行文件格式一样,PE 文件中在一个大家都知道的 (或者容易找到)位 置有一个包括很多字段的集合,它定义了文件其余部分的样式。这个头中包含了一些信息 例如代码和数据区的位置和大小,是什么操作系统下的文件,初始堆栈大小,和另外一些 我将要讨论到的重要的信息块。和 Microsoft 的其它一些可执行格式不一样,

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号