找回密码
 注册
搜索
热搜: 回贴

可移植的可执行文件格式全接触(图),移植,加密算法

2010-1-22 18:39| 发布者: admin| 查看: 69| 评论: 0|原作者: 江海


可移植的可执行文件格式全接触(图),移植,加密算法
2008年06月23日 星期一 下午 12:29
一、摘要



Windows NT 3.1版操作系统引进了一种叫做可移植的可执行(PE)文件格式的新文件格式。尽管《可移植的可执行文件格式规范》的内容相当含糊,但公众已可得到了;并且它也已被包括在我们的微软开发者网络CD(其中的:Windows NT文件格式规范->规范->规范和战略)当中。



不过,对开发者来说,仅此规范一文并不足以提供足够的信息来让他们对PE文件格式的理解变得容易,哪怕是更合理一点。本文档的目的就是用来解决这个问题的。从本文档之中,你可找到整个PE文件格式的完整的解释,还有所有必须的结构体的描述以及演示怎样使用这个信息的源代码例子。



出现在本文中的所有源代码例子都是从一个叫PEFILE.DLL的动态链接库中例举出来的。我写出这个DLL的目的就是为了发现包含在一个PE文件中的重要信息。这个DLL和它的源代码也被包含在这个CD当中作为PE文件例子程序的一部分;你可以自由地将这个DLL使用在你自己的应用程序之中。同样,你也可以自由地取得这些源代码并在它的基础之上为你的任何目的去构建(程序)。在本文的末尾,你会找到一个从PEFILE.DLL中导出的简短的函数列表,以及怎样使用它们的解释。我想,你会发现利用这些函数会让你对PE文件格式的理解要容易一些。





二、介绍



最近Windows操作系统的家族得到了微软®Windows NT操作系统的加入给开发环境带来了很多的变化,并且也给应用程序本身带来了不小的变化。比较重要的变化之一就是对可移植的可执行(PE)文件格式的引入。新的PE文件格式主要源自于UNIX操作系统常用的COFF(Common Object File Format,通用目标文件格式)规范。不过,为了保持对以前各版的MS-DOS®和 Windows操作系统的兼容性,PE文件格式也保留了大家过去比较熟悉的MS-DOS的MZ头。



在本文中,PE文件格式将使用从头到尾的方式来解释。文中将依照你通读文件的内容时文件的每个组成部分出现的顺序来一一讨论它们,开始时是头部并沿着你曾走过的路线全程直到结束。



许多单个的文件组成部分的定义来自于WINNT.H文件中,这是一个包含在Windows NT的微软Win32?软件开发工具箱(SDK)中的文件。在这个文件中,你能找到被用来表示文件中各个组成部分、每个文件头和数据目录等的结构类型定义。而其他方面,在文件中WINNT.H缺少对文件结构的足够的定义。在这些方面,我决定定义我自己的、能被用来访问文件数据的新结构。你将会发现这些结构被定义在PEFILE.H文件中,而这个文件就是用来建立PEFILE.DLL的。全套的PEFILE.DLL开发文件包含在PEFILE实例应用这一节中。



作为PEFILE.DLL实例代码的补充,伴随此文的还有一个单独的、名为EXEVIEW.EXE的基于Win32的实例程序。此实例的建立基于以下两个目的:第一,我需要一种能检验PEFILE.DLL功能的方法,在一些情况下这种检验要求同时能查看多个文件----也就是说多重查看的支持。第二,领会PE文件格式的许多工作都和能交互地看见数据有关。例如,要搞清输入地址名字表是怎样的结构,我得同时查看.idata节的节头、输入映象文件的数据目录、可选头、以及实际的.idata节的节身等等。EXEVIEW.EXE就是查看上述信息的最佳人选。



不再罗嗦,我们马上开始。





三、PE文件的结构



PE文件格式被组织为一个线性的数据流。开始的是MS-DOS头,然后是实模式的程序根,再就是PE文件签名,紧随其后的便是PE文件头和可选头。在这之后,出现的是所有的节头,再跟着的就是所有节的节身。文件常以一些其它方面的杂项信息,包括重定位信息、符号表信息、行数信息以及字串表数据等作为结尾。所有这些都可以通过查看图1中的图象信息更轻松地被消化吸收。




screen.width*0.6) {this.width=screen.width*0.6;this.alt='此图已经缩小,点击察看原图。';}" border=0>


图1. 一个可移植的可执行文件映像的结构



我们将从MS-DOS文件头结构开始讲解。以后,PE文件结构的每个组成部分的讨论都将按照它在文件中出现的顺序来进行。这其中的很多讨论是基于那个演示如何到达文件中特定信息的实例代码来的。所有的实例代码都是从PEFILE.C文件,也就是PEFILE.DLL的源模块①中提取的。这些实例中的每一个都利用了Windows NT中最酷的特性之一----内存镜像文件②。内存镜像文件允许使用取消指向的简单指针来访问包含在文件中的数据。实例中的每个程序都使用内存镜像文件来访问PE文件中的数据。



注意:请参看本文的最后一节以讨论如何使用PEFILE.DLL文件。





四、MS-DOS(实模式)头



如上所述,PE文件的第一个组成部分是MS-DOS头。MS-DOS头不是PE文件格式新发明的。它就是那个大约从MS-DOS操作系统第二版就已有的MS-DOS头。在PE文件格式的开头完整地保留这个同样的结构的主要原因就是:以便在你试图将创建的文件载入到Windows 3.1或以前、或者是MS-DOS 2.0或以后的各版本上时,操作系统能够读取文件并明白这是不兼容的。换句话说,在你试图在MS-DOS 6.0版之上运行Windows NT的可执行文件时,你能得到:“此文件不能运行在DOS模式之下。”这样的信息。如果不将MS-DOS头包括在PE文件格式的第一部分,操作系统将只会尝试载入文件失败并给出一些完全无用的东西,比如:“认不出指定的文件名是内部还是外部命令,是可操作的程序还是批处理文件。”等等。



MS-DOS头占据PE文件的头64(0x40)个字节。反映它的内容的一个结构如下所述:



WINNT.H



typedef struct _IMAGE_DOS_HEADER { // DOS下的.EXE文件头

USHORT e_magic; // 魔数

USHORT e_cblp; // 文件最后一页的字节数

USHORT e_cp; // 文件的页数

USHORT e_crlc; // 重定位

USHORT e_cparhdr; // 段中头的大小

USHORT e_minalloc; // 需要的最少额外段

USHORT e_maxalloc; // 需要的最多额外段

USHORT e_ss; // 初始的(相对的)SS寄存器值

USHORT e_sp; // 初始的SP寄存器值

USHORT e_csum; // 校验和

USHORT e_ip; // 初始的IP寄存器值

USHORT e_cs; // 初始的(相对的)CS寄存器值

USHORT e_lfarlc; // 重定位表在文件中的地址

USHORT e_ovno; // 交叠数

USHORT e_res[4]; // 保留字

USHORT e_oemid; // OEM识别符(用于e_oeminfo成员)

USHORT e_oeminfo; // OEM信息; e_oemid中指定的

USHORT e_res2[10]; // 保留字

LONG e_lfanew; // 新exe头在文件中的地址

} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;



第一个域,e_magic,是所谓的魔数,这个域被用来鉴定一个MS-DOS兼容文件的类型。所有MS-DOS兼容的可执行文件都把这个值设为0x54AD,代表ASCII码字符MZ。正因为这个原因MS-DOS头有时也被称为MZ头。其它的很多域对MS-DOS操作系统很重要,但对Windows NT系统,这个结构中实际上只另有一个重要的域,即最后一个域,e_lfanew,它是一个4字节的、PE文件头被定位到的文件中的偏移量。我们必须使用这个偏移量来定位文件中的文件头。对Windows NT系统的PE文件而言,PE文件头在和MS-DOS头之间仅相隔实模式的根程序之后很快就出现了。





五、实模式根程序



实模式根程序指的是当可执行文件被载入后MS-DOS实际运行的程序。对于一个实际的MS-DOS可执行映像文件而言,应用程序就从这里开始执行。对于后来的操作系统,包括Windows,OS/2®,和Windows NT等系统来说,一个MS-DOS根程序放在这里只是为了代替实际的应用程序运行的。典型的情况下,程序只输出一行文字,比如:“此程序需要微软Windows v3.1或更高的版本支持。”当然,那些创建此应用程序的人可以将他们喜欢的任何根放在这儿,也就是说你可能经常看到诸如:“你不能在OS/2系统上运行Windows NT应用程序,很明显的这不可能。”之类的东西。



在我们为Windows 3.1版构建一个应用程序时,链接器会将一个缺省的叫做WINSTUB.EXE的根程序链接到你的可执行文件之中。你可以通过用你自己的有效的基于MS-DOS的程序替换掉原WINSTUB程序并用STUB模块定义声明将它指定给链接器的方法来覆盖缺省的链接器行为。为Windows NT开发的应用程序在链接可执行文件时也可以通过使用-STUB:链接器选项来实现同样的功能。





六、PE文件头和签名



PE文件头可以通过MS-DOS头中的e_lfanew(新exe头在文件中的地址)域来索引定位。e_lfanew域只是提供在文件中的偏移量,因此要加上文件的内存镜像基址才能确定实际的内存镜像地址。例如:下面的宏③包含在PEFILE.H源文件中:



PEFILE.H



#define NTSIGNATURE(a) ((LPVOID)((BYTE *)a \

((PIMAGE_DOS_HEADER)a)->e_lfanew))



当我操纵PE文件信息时,我发现有好几个我需要的文件中的位置经常被用到。因为它们只是针对文件中的偏移量,所以用宏来实现是比较容易的,因为宏的表现要比函数的好很多。



注意:这个宏是检索PE文件签名的位置的,而不是检索PE文件头的偏移量的。从Windows和OS/2的可执行文件开始,.EXE文件将被给出文件签名用来指定预定的目标操作系统。对于Windows NT的PE文件格式,这个签名就在PE文件头结构的前面出现。在各个版本的Windows和OS/2系统中,签名常常在文件头的第一个word单元中。同样,对于PE文件格式,Windows NT系统也使用一个DWORD来定义签名。




最新评论

QQ|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )

GMT+8, 2024-9-30 03:34 , Processed in 0.190771 second(s), 12 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

返回顶部