没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
探索 PE 文件内幕
—— Win32 可移植可执行文件格式之旅
作者:Matt Pietrek
一种操作系统之上的可执行文件格式从许多方面反映了这种操作系统本身的情况。尽管研究
可执行文件格式并不是大多数程序员的首要任务,但从中你却能够获得许多知识。在本文中,我
要带你游历可移植可执行(Portable Executable,PE)文件格式,它是 Microsoft 设计用于所
有基于 Win32®的系统,包括 Windows NT®、Win32s™以及 Windows 95 之上的可执行文件格式。在
可预见的将来,PE 格式会在 Microsoft 的所有操作系统中扮演重要角色,其中包括 Windows 2000。
如果你使用过 Win32s 或 Windows NT,那么你已经使用过 PE 文件了。即使你只是使用 Visual C++®
为 Windows 3.1 编写程序,你也使用了 PE 文件(Visual C++的 32 位 MS-DOS®扩展组件使用这种
格式)。简而言之,PE 格式已经深入到各种系统以及系统的各个角落,在将来不可避免要碰到
它。现在是找出这种新型的可执行文件格式到底给操作系统带来了什么好处的时候了。
我并不是让你从无穷无尽的十六进制数据的角度去研究 PE 文件格式,也不是让你记住整页
整页的 PE 文件中各个位的含义。相反,我要向你呈现嵌入在 PE 文件格式中的内容以及它们与你
日常工作之间的关系。例如下面的语句中涉及到的线程局部变量的概念:
__declspec(thread) int i;
曾经几乎让我发疯,直到我看到它在可执行文件中是如何简洁优美地被实现的。由于你们中许多
人都来自 16 位的 Windows,所以我会把 Win32 PE 文件格式的结构与 16 位 NE 文件格式中等价的
内容作一比较。
除了不同的可执行文件格式之外,Microsoft 也在他的编译器和汇编程序生成的目标文件中
使用了新的格式。这种新的 OBJ 文件格式与 PE 可执行文件格式有许多相同的地方。我虽然试图
去寻找这种新的 OBJ 文件格式的文档,但最终一无所获。因此我要以自己的方式来解密这种格式,
在这里我除了讲解 PE 格式之外也会讲解它的部分内容。
众所周知,Windows NT 继承自 VAX® VMS®和 UNIX。Windows NT 的许多创建者在到 Microsoft
之前都曾为这些平台设计和编写程序。当他们设计 Windows NT 时,很自然会使用以前写过的和
测试过的工具以尽快开始他们的新项目。这些工具产生的和使用的可执行文件和目标模块的格式
被称为 COFF(Common Object File Format 的首字母,通用目标文件格式)。你从 COFF 的一些
域所用的竟然是八进制形式的数据就可以看出它是多么老。COFF 格式本身是很好的起点,但需
要扩充才能满足现代操作系统,例如 Windows NT 或者 Windows 95 的需要。结果就产生了可移植
可执行格式。它之所以被称为“可移植”是因为 Windows NT 在各种平台(x86、MIPS®、Alpha
等等)上的所有实现都使用同样的可执行文件格式。当然,像 CPU 指令的二进制编码之类的内容
会有所不同。重要的是操作系统加载器和编程工具不需要针对遇到的每种新的 CPU 再完全重写。
从 Microsoft 抛弃现有的 32 位工具和文件格式上可以看出它承诺让 Windows NT 运行得更快
的决心。16 位 Windows 上的虚拟设备驱动程序使用的是不同的 32 位文件格式——LE 格式,它在
Windows NT 出现之前很早就出现了。比这更重要的是更换了 OBJ 文件的格式。在 Windows NT 的
C 编译器之前,所有的 Microsoft 编译器使用的都是 Intel 的 OMF(Object Module Format,目
标模块格式)规范。正如前面提到的那样,Microsoft 的 Win32 编译器产生的都是 COFF 格式的
OBJ 文件。一些 Microsoft 的竞争者,例如 Borland 和 Symantec,放弃 COFF 格式的 OBJ 而继续
使用 Intel OMF 格式。结果导致这些公司为多个编译器产生的 OBJ 或 LIB 文件需要针对不同的编
译器发布不同的版本。
PE 格式被公开在 WINNT.H 头文件中(非常零散)。大概在 WINNT.H 文件的中间有一个“Image
Format”节。这个节以 MS-DOS MZ 格式和 NE 格式开头,后面才是新的 PE 格式。WINNT.H 提供了
PE 文件使用的原始数据结构的定义,但是它包含的关于这些结构和标志的意义的有用注释却很
少。为 PE 格式写头文件的人(Michael J. O'Leary)一定特别喜欢冗长的、描述性的名称,以
及嵌套很深的结构和宏。当使用 WINNT.H 编写代码时,你经常会使用类似下面这样的表达式:
pNTHeader->
OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
为了对 WINNT.H 中的信息有一个较好的认识,最好阅读 Microsoft 可移植可执行文件和通用
目标文件格式文件规范,可以在直到 2001 年十月(含)的 MSDN Library 每季度的光盘中找到。
现在再来看 COFF 格式的 OBJ 文件,WINNT.H 头文件中包含了 COFF 格式的 OBJ 和 LIB 文件使
用的结构定义和类型定义。不幸的是,与上面提到的可执行文件一样,我找不到关于它的任何文
档。由于 PE 文件与 COFF 格式的 OBJ 文件非常相似,我觉得是时候把这些文件呈现给大众,并为
它们写些文档了。
在阅读 PE 文件的结构之余,你可能想转储(DUMP)一些 PE 文件来自己看看这些概念。如果
你使用 Microsoft®的基于 32 位的开发工具,它提供的 DUMPBIN 程序能够剖析 PE 文件以及 OBJ
和 LIB 文件并且能够以易读的形式输出其结果。在所有的 PE 文件转储工具中,DUMPBIN 是最全
面的,它甚至有一个很好的选项用来对它处理的文件的代码节进行反汇编。Borland 的用户可以
使用 TDUMP 来查看 PE 可执行文件,但是 TDUMP 并不能理解 COFF 格式的 OBJ 文件。这并不是个大
问题,因为首先 Borland 的编译器根本就不生成 COFF 格式的 OBJ 文件。
我已经写了一个 PE 和 COFF 格式的 OBJ 文件的转储程序,称为 PEDUMP(见表 1),它的输出
比 DUMPBIN 的输出更容易理解。尽管它不包含反汇编程序,也不能处理 LIB 文件,但其它功能与
DUMPBIN 一样,并且增加了新的功能。PEDUMP 的源代码在 MSJ 的 BBS 上可以找到,因此我在这里
不列出它的全部代码。我会用它的某些输出实例来解释我要讲解的概念。
表 1 PEDUMP.C
//--------------------
// PROGRAM: PEDUMP
// FILE: PEDUMP.C
// AUTHOR: Matt Pietrek - 1993
//--------------------
#include <windows.h>
#include <stdio.h>
#include "objdump.h"
#include "exedump.h"
#include "extrnvar.h"
// 这里是 EXEDUMP.C 和 OBJDUMP.C 中使用的全局变量
BOOL fShowRelocations = FALSE;
BOOL fShowRawSectionData = FALSE;
BOOL fShowSymbolTable = FALSE;
BOOL fShowLineNumbers = FALSE;
char HelpText[] =
"PEDUMP - Win32/COFF .EXE/.OBJ file dumper - 1993 Matt Pietrek\n\n"
"Syntax: PEDUMP [switches] filename\n\n"
" /A include everything in dump\n"
" /H include hex dump of sections\n"
" /L include line number information\n"
" /R show base relocations\n"
" /S show symbol table\n";
// 打开一个文件,然后对它创建内存映射文件,并调用合适的转储例程
void DumpFile(LPSTR filename)
{
HANDLE hFile;
HANDLE hFileMapping;
LPVOID lpFileBase;
PIMAGE_DOS_HEADER dosHeader;
hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if ( hFile = = INVALID_HANDLE_VALUE )
{ printf("Couldn't open file with CreateFile()\n");
return; }
hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if ( hFileMapping = = 0 )
{ CloseHandle(hFile);
printf("Couldn't open file mapping with CreateFileMapping()\n");
return; }
lpFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
if ( lpFileBase = = 0 )
{
CloseHandle(hFileMapping);
CloseHandle(hFile);
printf("Couldn't map view of file with MapViewOfFile()\n");
return;
}
printf("Dump of file %s\n\n", filename);
dosHeader = (PIMAGE_DOS_HEADER)lpFileBase;
if ( dosHeader->e_magic = = IMAGE_DOS_SIGNATURE )
{ DumpExeFile( dosHeader ); }
else if ( (dosHeader->e_magic = = 0x014C) // 它看起来像 i386 上的 COFF
&& (dosHeader->e_sp = = 0) ) // 格式的 OBJ 文件吗?
{
// 以上两个测试实际是在检测 IMAGE_FILE_HEADER.Machine = = i386 (0x14C)
// 以及 IMAGE_FILE_HEADER.SizeOfOptionalHeader = = 0;
DumpObjFile( (PIMAGE_FILE_HEADER)lpFileBase );
}
else
printf("unrecognized file format\n");
UnmapViewOfFile(lpFileBase);
CloseHandle(hFileMapping);
CloseHandle(hFile);
}
// 处理所有的命令行参数并返回指向文件名参数的指针
PSTR ProcessCommandLine(int argc, char *argv[])
{
int i;
for ( i=1; i < argc; i++ )
{
strupr(argv[i]);
// 它是一个选项吗?
if ( (argv[i][0] = = '-') || (argv[i][0] = = '/') )
{
if ( argv[i][1] = = 'A' )
{ fShowRelocations = TRUE;
fShowRawSectionData = TRUE;
fShowSymbolTable = TRUE;
fShowLineNumbers = TRUE; }
else if ( argv[i][1] = = 'H' )
fShowRawSectionData = TRUE;
else if ( argv[i][1] = = 'L' )
fShowLineNumbers = TRUE;
else if ( argv[i][1] = = 'R' )
fShowRelocations = TRUE;
else if ( argv[i][1] = = 'S' )
fShowSymbolTable = TRUE;
}
else // 不是选项,一定是文件名
{ return argv[i]; }
}
}
int main(int argc, char *argv[])
{
PSTR filename;
if ( argc = = 1 )
{ printf( HelpText );
return 1; }
filename = ProcessCommandLine(argc, argv);
if ( filename )
DumpFile( filename );
return 0;
}
Win32 和 PE 的基本概念
让我们先来回顾一下 PE 文件设计中的一些基本概念(如图 1)。我使用“模块”来指代加
载进内存中的可执行文件或 DLL 的数据、代码和资源。除了你的程序中直接使用的代码和数据外,
模块中还包含一些支持性的数据结构供 Windows 用来确定代码和数据在内存中的位置。在 16 位
Windows 中,这些支持性的数据结构位于模块数据库中(通过 HMODULE 引用的段)。在 Win32 中,
这些数据结构位于 PE 文件头中,后面我会解释。
图 1 PE 文件格式
剩余33页未读,继续阅读
资源评论
bobzhu2002
- 粉丝: 10
- 资源: 8
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功