可执行文件格式.docx
《可执行文件格式.docx》由会员分享,可在线阅读,更多相关《可执行文件格式.docx(26页珍藏版)》请在冰豆网上搜索。
可执行文件格式可执行文件格式可执行文件格式一、裸机中的可执行文件没有文件头部,是纯代码文件。
因为没有操作系统,不能依托加载器构建可执行程序的映像。
故一样裸机中的执行文件都没有文件格式。
二、标准的Dos可执行文件包括批处置文件、Dos下的可加载驱动程序(.SYS)文件和一般的.COM、.EXE可执行文件。
批处置文件,是以.BAT结尾的文件,在.BAT文件中能够包括一些DOS命令,和在批处置文件中挪用其它的可执行文件。
批处置文件还有一些简单的流程操纵功能,能够实现循环、条件判定等简单的编程工作。
设备驱动文件,是以.sYS结尾的文件,如config.SYs和IO.sys等,是Dos操作系统利用的设备驱动程序。
COM文件,是以.COM结尾的纯代码文件。
没有文件头部份,默许情形下老是从0xl00H处开始执行,没有重定位项,这也限制了它所有代码和数据必需操纵在64KB之内。
EXE文件,是以.EXE结尾的文件,这种文件以英文字母MZ开头,通常称之为MZ文件。
MZ文件有一个文件头,用来指出每一个段的概念,和重定位表。
.EXE文件摆脱了代码大小最多不能超过64KB的限制,是DOS下,最主要的文件格式。
MZ文件的格式:
MZ文件由三部份组成:
文件头、重定位表和二进制代码。
Mz文件头包括Ms-Dos用于加载程序的信息,例如程序的大小和寄放器的初始值。
文件头还指向一个重定位表,该表包括指向程序映像中可重定位段的指针链表。
文件头的格式用EXEHHEADER结构概念如下(注释符号后面的十六进制表示相应字段的偏移)EXEHEADERSTRUCexsi,aturedw?
;00H,文件类型标记:
ox4DSA(ASCll字符Mz)exExraBytesdw?
;oZH,文件最后一页的字节数(51ZB一页)exPagesdw?
;04H,文件的总页数exRefocltemsdw?
;06H,重定位项的个数exHeadersizedw?
;0SH,以字节为单位的文件头大小exMIILAlfoodw?
;0AH,依照代码大小分派的最小内存exMaxAllocdw?
;OCH,依照代码大小分派的最大段(Para歹aph)数exlnitSSdw?
;0EH,初始55值,相关于入口点exlnitSPdw?
;1OH,初始SP值exChechsumdw?
;12H,校验和或零exlnitIPdw?
;14H,初始IP值exlnitCSdw?
;16H,初始CS值,相对入口点exRelocTabledw?
;lSH,重定位表的偏移地址exoverlaydw?
;IAH,连接程序产生的覆盖号EXEHEADERENDS下面简单介绍DOS下.EXE文件的加载进程。
MZ文件包括一个文件头和一个可从头定位程序映像(载入模块)。
文件头包括MS一DOS用于加载程序的信息,例如程序的大小和寄放器的初始值。
程序映像包括处置器代码和程序的初始数据,它紧接在文件头以后,它的大小等于MZ文件大小减去文件头的大小。
MS一DOS通过把该映像直接从文件复制到内存加载EXE程序,然后调整定位表中说明的可重定位段的地址。
重定位表示一个重定位指针数组,每一个指针指向程序阻碍中的可重定位段地址。
文件头中的偏移06H处说明了数组中指针的个数。
偏移18H处指向重定位表的指针链表。
每一个重定位指针由两个16位值(即偏移量和段地址)组成。
3、诚ndows下的可执行文件格式NE格式:
Windows3.0/3.1版本中,微软推出了一种新的可执行文件格式,在MZ文件头以后又有一个以NE开始的文件头,称之为NE(NewExecutable)文件。
由于Windows的可执行文件同DOS相较增加了很多内容,如资源、动态库等,NE格式表现极为复杂,NE格式文件在装载程序读取磁盘上的文件后,需要在内存中组装成一个完全不同的数据结构才开始运行。
PE格式:
在Windows32位平台微软又推出了一种新的可执行文件格式-可移植的可执行文件(PortableExecutableFile)格式。
即PE格式。
它同NE格式不同的是在MZ文件头以后是一个以PE开始的文件头。
PE文件格式是从COFF(UNIX中普遍利用的通用二进制文件格式)的对象格式进展而来,它比NE格式前进了一大步,其文件在磁盘中的格式同内存中的格式区别不大,装载程序实现起来相当简单。
PE文件格式被组织为一个线性的数据流,它由一个MS一DOS头部开始,接着是一个是模式的程序残余和一个PE文件标志,这以后紧接着PE文件头和可选头部。
这些以后是所有的段头部,段头部以后跟从着所有的段实体。
文件的终止处是一些其它的区域,其中是一些混杂的信息,包括重分派信息、符号表信息、行号信息和字串表数据。
PE文件格式详解:
WindowsNT3.1引入了一种名为PE文件格式的新可执行文件格式。
PE文件格式的标准包括在了MSDN的CD中(SpecsandStrategy,Specifications,WindowsNTFileFormatSpecifications),可是它超级之晦涩。
但是这一的文档并未提供足够的信息,因此开发者们无法专门好地弄懂PE格式。
本文旨在解决这一问题,它会对整个的PE文件格式作一个十分完全的说明,另外,本文中还带有对所有必需结构的描述和示范如何利用这些信息的源码例如。
为了取得PE文件中所包括的重要信息,我编写了一个名为PEFILE.DLL的动态链接库,本文中所有显现的源码例如亦均摘自于此。
那个DLL和它的源代码都作为PEFile例如程序的一部份包括在了CD中(译注:
例如程序请在MSDN中寻觅,本站恕不提供),你能够在你自己的应用程序中利用那个DLL;一样,你亦能够依你所愿地利用并构建它的源码。
在本文末尾,你会找到PEFILE.DLL的函数导出列表和一个如何利用它们的说明。
我感觉你会发觉这些函数会让你从容应付PE文件格式的。
介绍Windows操作系统家族最近增加的WindowsNT为开发环境和应用程序本身带来了专门大的改变,这当中一个最为重大的当属PE文件格式了。
新的PE文件格式要紧来自于UNIX操作系统所通用的COFF标准,同时为了保证与旧版本MS-DOS及Windows操作系统的兼容,PE文件格式也保留了MS-DOS中那熟悉的MZ头部。
在本文当中,PE文件格式是以自顶而下的顺序说明的。
在你从头开始研究文件内容的进程当中,本文会详细讨论PE文件的每一个组成部份。
许多单独的文件成份概念都来自于MicrosoftWin32SDK开发包中的WINNT.H文件,在那个文件中你会发觉用来描述文件头部和数据目录等各类成份的结构类型概念。
可是,在WINNT.H中缺少对PE文件结构足够的概念,在这种情形下,我概念了自己的结构来存取文件数据。
你会在PEFILE.DLL工程的PEFILE.H中找到这些结构的概念,整套的PEFILE.H开发文件包括在PEFile例如程序当中。
本文配套的例如程序除PEFILE.DLL例如代码之外,还有一个单独的Win32例如应用程序,名为EXEVIEW.EXE。
创建这一例如目的有二:
第一,我需要测试PEFILE.DLL的函数,而且某些情形要求我同时查看多个文件;第二,很多解决PE文件格式的工作和直接观看数据有关。
例如,要弄懂导入地址名称表是如何组成的,我就得同时查看.idata段头部、导入映像数据目录、可选头部和当前的.idata段实体,而EXEVIEW.EXE确实是查看这些信息的最正确例如。
闲话少叙,让咱们开始吧。
PE文件结构PE文件格式被组织为一个线性的数据流,它由一个MS-DOS头部开始,接着是一个是模式的程序残余和一个PE文件标志,这以后紧接着PE文件头和可选头部。
这些以后是所有的段头部,段头部以后跟从着所有的段实体。
文件的终止处是一些其它的区域,其中是一些混杂的信息,包括重分派信息、符号表信息、行号信息和字串表数据。
我将所有这些成排列于图1。
图1.PE文件映像结构从MS-DOS文件头结构开始,我将依照PE文件格式各成份的显现顺序依次对其进行讨论,而且讨论的大部份是以例如代码为基础来示范如何取得文件的信息的。
所有的源码均摘自PEFILE.DLL模块的PEFILE.C文件。
这些例如都利用了WindowsNT最酷的特色之一-内存映射文件,这一特色许诺用户利用一个简单的指针来存取文件中所包括的数据,因此所有的例如都利用了内存映射文件来存取PE文件中的数据。
注意:
请查阅本文末尾关于如何利用PEFILE.DLL的那一段。
MS-DOS头部/实模式头部如上所述,PE文件格式的第一个组成部份是MS-DOS头部。
在PE文件格式中,它并非一个新概念,因为它与MS-DOS2.0以来就已有的MS-DOS头部是完全一样的。
保留那个相同结构的最要紧缘故是,当你尝试在Windows3.1以下或MS-DOS2.0以上的系统下装载一个文件的时候,操作系统能够读取那个文件并明白它是和当前系统不相兼容的。
换句话说,当你在MS-DOS6.0下运行一个WindowsNT可执行文件时,你会取得如此一条消息:
ThisprogramcannotberuninDOSmode.若是MS-DOS头部不是作为PE文件格式的第一部份的话,操作系统装载文件的时候就会失败,并提供一些完全没用的信息,例如:
Thenamespecifiedisnotrecognizedasaninternalorexternalcommand,operableprogramorbatchfile.MS-DOS头部占据了PE文件的头64个字节,描述它内容的结构如下:
/WINNT.Htypedefstruct_IMAGE_DOS_HEADER/DOS的.EXE头部USHORTe_magic;/魔术数字USHORTe_cblp;/文件最后页的字节数USHORTe_cp;/文件页数USHORTe_crlc;/重概念元素个数USHORTe_cparhdr;/头部尺寸,以段落为单位USHORTe_minalloc;/所需的最小附加段USHORTe_maxalloc;/所需的最大附加段USHORTe_ss;/初始的SS值(相对偏移量)USHORTe_sp;/初始的SP值USHORTe_csum;/校验和USHORTe_ip;/初始的IP值USHORTe_cs;/初始的CS值(相对偏移量)USHORTe_lfarlc;/重分派表文件地址USHORTe_ovno;/覆盖号USHORTe_res4;/保留字USHORTe_oemid;/OEM标识符(相对e_oeminfo)USHORTe_oeminfo;/OEM信息USHORTe_res210;/保留字LONGe_lfanew;/新exe头部的文件地址IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER;第一个域e_magic,被称为魔术数字,它被用于表示一个MS-DOS兼容的文件类型。
所有MS-DOS兼容的可执行文件都将那个值设为0x5A4D,表示ASCII字符MZ。
MS-DOS头部之因此有的时候被称为MZ头部,确实是那个缘故。
还有许多其它的域关于MS-DOS操作系统来讲都有效,可是关于WindowsNT来讲,那个结构中只有一个有效的域-最后一个域e_lfnew,一个4字节的文件偏移量,PE文件头部确实是由它定位的。
关于WindowsNT的PE文件来讲,PE文件头部是紧跟在MS-DOS头部和实模式程序残余以后的。
实模式残余程序实模式残余程序是一个在装载时能够被MS-DOS运行的实际程序。
关于一个MS-DOS的可执行映像文件,应用程序确实是从那个地址执行的。
关于Windows、OS/二、WindowsNT这些操作系统来讲,MS-DOS残余程序就代替了主程序的位置被放在那个地址。
这种残余程序通常什么也不做,而只是输出一行文本,例如:
ThisprogramrequiresMicrosoftWindowsv3.1orgreater.固然,用户能够在此放入任何的残余程序,这就意味着你可能常常看到像如此的东西:
YoucantrunaWindowsNTapplicationonOS/2,itssimplynotpossible.当为Windows3.1构建一个应用程序的时候,链接器将向你的可执行文件中链接一个名为WINSTUB.EXE的默许残余程序。
你能够用一个基于MS-DOS的有效程序取代WINSTUB,而且用STUB模块概念语句指示链接器,如此就能够够取代链接器的默许行为。
为WindowsNT开发的应用程序能够通过利用-STUB:
链接器选项来实现。
PE文件头部与标志PE文件头部是由MS-DOS头部的e_lfanew域定位的,那个域只是给出了文件的偏移量,因此要确信PE头部的实际内存映射地址,就需要添加文件的内存映射基地址。
例如,以下的宏是包括在PEFILE.H源文件当中的:
/PEFILE.H#defineNTSIGNATURE(a)(LPVOID)(BYTE*)a+(PIMAGE_DOS_HEADER)a)-e_lfanew)在处置PE文件信息的时候,我发觉文件当中有些位置需要常常查阅。
既然这些位置仅仅是对文件的偏移量,那么用宏来实现这些定位就比较容易,因为它们较之函数有更好的表现。
请注意那个宏所取得的是PE文件标志,而并非PE文件头部的偏移量。
那是由于自Windows与OS/2的可执行文件开始,.EXE文件都被给予了目标操作系统的标志。
关于WindowsNT的PE文件格式而言,这一标志在PE文件头部结构之前。
在Windows和OS/2的某些版本中,这一标志是文件头的第一个字。
一样,关于PE文件格式,WindowsNT利用了一个DWORD值。
以上的宏返回了文件标志的偏移量,而不管它是哪一种类型的可执行文件。
因此,文件头部是在DWORD标志以后,仍是在WORD标志处,是由那个标志是不是WindowsNT文件标志所决定的。
要解决那个问题,我编写了ImageFileType函数(如下),它返回了映像文件的类型:
/PEFILE.CDWORDWINAPIImageFileType(LPVOIDlpFile)/*第一显现的是DOS文件标志*/if(*(USHORT*)lpFile=IMAGE_DOS_SIGNATURE)/*由DOS头部决定PE文件头部的位置*/if(LOWORD(*(DWORD*)NTSIGNATURE(lpFile)=IMAGE_OS2_SIGNATURE|LOWORD(*(DWORD*)NTSIGNATURE(lpFile)=IMAGE_OS2_SIGNATURE_LE)return(DWORD)LOWORD(*(DWORD*)NTSIGNATURE(lpFile);elseif(*(DWORD*)NTSIGNATURE(lpFile)=IMAGE_NT_SIGNATURE)returnIMAGE_NT_SIGNATURE;elsereturnIMAGE_DOS_SIGNATURE;else/*不明文件种类*/return0;以上列出的代码当即告知了你NTSIGNATURE宏有何等有效。
关于比较不同文件类型而且返回一个适当的文件种类来讲,那个宏就会使这两件事变得超级简单。
WINNT.H当中概念的四种不同文件类型有:
/WINNT.H#defineIMAGE_DOS_SIGNATURE0x5A4D/MZ#defineIMAGE_OS2_SIGNATURE0x454E/NE#defineIMAGE_OS2_SIGNATURE_LE0x454C/LE#defineIMAGE_NT_SIGNATURE0x00004550/PE00第一,Windows的可执行文件类型没有出此刻这一列表中,这一点看起来很奇怪。
可是,在略微研究一下以后,就能够取得缘故了:
除操作系统版本标准的不同之外,Windows的可执行文件和OS/2的可执行文件实在没有什么区别。
这两个操作系统拥有相同的可执行文件结构。
此刻把咱们的注意力转向WindowsNTPE文件格式,咱们会发觉只要咱们取得了文件标志的位置,PE文件以后就会有4个字节相跟从。
下一个宏标识了PE文件的头部:
/PEFILE.C#definePEFHDROFFSET(a)(LPVOID)(BYTE*)a+(PIMAGE_DOS_HEADER)a)-e_lfanew+SIZE_OF_NT_SIGNATURE)那个宏与上一个宏的唯一不同是那个宏加入了一个常量SIZE_OF_NT_SIGNATURE。
不幸的是,那个常量并未概念在WINNT.H当中,于是我将它概念在了PEFILE.H中,它是一个DWORD的大小。
既然咱们明白了PE文件头的位置,那么就能够够检查头部的数据了。
咱们只需要把那个位置赋值给一个结构,如下:
PIMAGE_FILE_HEADERpfh;pfh=(PIMAGE_FILE_HEADER)PEFHDROFFSET(lpFile);在那个例子中,lpFile表示一个指向可执行文件内存映像基地址的指针,这就显出了内存映射文件的益处:
不需要执行文件的I/O,只需利用指针pfh就能够存取文件中的信息。
PE文件头结构被概念为:
/WINNT.Htypedefstruct_IMAGE_FILE_HEADERUSHORTMachine;USHORTNumberOfSections;ULONGTimeDateStamp;ULONGPointerToSymbolTable;ULONGNumberOfSymbols;USHORTSizeOfOptionalHeader;USHORTCharacteristics;IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;#defineIMAGE_SIZEOF_FILE_HEADER20请注意那个文件头部的大小已经概念在那个包括文件当中了,如此一来,想要取得那个结构的大小就很方便了。
可是我感觉对结构本身利用sizeof运算符(译注:
原文为function)更简单一些,因为如此的话我就没必要记住那个常量的名字IMAGE_SIZEOF_FILE_HEADER,而只需要记住结构IMAGE_FILE_HEADER的名字就能够够了。
另一方面,记居处有结构的名字已经够有挑战性的了,尤其在是这些结构只有WINNT.H中才有的情形下。
PE文件中的信息大体上是一些高级信息,这些信息是被操作系统或应用程序用来决定如何处置那个文件的。
第一个域是用来表示那个可执行文件被构建的目标机械种类,例如DEC(R)Alpha、MIPSR4000、Intel(R)x86或一些其它处置器。
系统利用这一信息来在读取那个文件的其它数据之前决定如何处置它。
Characteristics域表示了文件的一些特点。
比如关于一个可执行文件而言,分离调试文件是如何操作的。
调试器通常利用的方式是将调试信息从PE文件中分离,并保留到一个调试文件(.DBG)中。
要这么做的话,调试器需要了解是不是要在一个单独的文件中寻觅调试信息,和那个文件是不是已经将调试信息分离了。
咱们能够通过深切可执行文件并寻觅调试信息的方式来完成这一工作。
要使调试器不在文件中查找的话,就需要用到IMAGE_FILE_DEBUG_STRIPPED那个特点,它表示文件的调试信息是不是已经被分离了。
如此一来,调试器能够通过快速查看PE文件的头部的方式来决定文件中是不是存在着调试信息。
WINNT.H概念了假设干其它表示文件头信息的标记,就和以上的例子差不多。
我把研究这些标记的情形留给读者作为练习,由你们来看看它们是不是很有趣,这些标记位于WINNT.H中的IMAGE_FILE_HEADER结构以后。
PE文件头结构中另一个有效的入口是NumberOfSections域,它表示若是你要方便地提取文件信息的话,就需要了解多少个段-更明确一点来讲,有多少个段头部和多少个段实体。
每一个段头部和段实体都在文件中持续地排列着,因此要决定段头部和段实体在哪里终止的话,段的数量是必需的。
以下的函数从PE文件头中提取了段的数量:
PEFILE.CintWINAPINumOfSections(LPVOIDlpFile)/*文件头部中所表示出的段数量*/return(int)(PIMAGE_FILE_HEADER)PEFHDROFFSET(lpFile)-NumberOfSections);如你所见,PEFHDROFFSET和其它宏用起来超级方便。
PE可选头部PE可执行文件中接下来的224个字节组成了PE可选头部。
尽管它的名字是可选头部,可是请确信:
那个头部并非可选,而是必需的。
OPTHDROFFSET宏能够取得指向可选头部的指针:
/PEFILE.H#defineOPTHDROFFSET(a)(LPVOID)(BYTE*)a+(PIMAGE_DOS_HEADER)a)-e_lfanew+SIZE_OF_NT_SIGNATURE+sizeof(IMAGE_FILE_HEADER)可选头部包括了很多关于可执行映像的重要信息,例如初始的堆栈大小、程序入口点的位置、首选基地址、操作系统版本、段对齐的信息等等。
IMAGE_OPTIONAL_HEADER结构如下:
/WINNT.Htypedefstruct_IMAGE_OPTIONAL_HEADER/标准域/USHORTMagic;UCHARMajorLinkerVersion;UCHARMinorLinkerVersion;ULONGSizeOfCode;ULONGSizeOfInitializedData;ULONGSizeOfUninitializedData;ULONGAddressOfEntryPoint;ULONGBaseOfCode;ULONGBaseOfData;/NT附加域/ULONGImageBase;ULONGSectionAlignment;ULONGFileAlignment;USHORTMajorOperatingSystemVersion;USHORTMinorOperatingSystemVersion;USHORTMajorImageVersion;USHORTMinorImageVersion;USHORTMajorSubsystemVersion;USHORTMinorSubsystemVersion;ULONGReserved1;ULONGSizeOfImage;ULONGSizeOfHeaders;ULONGCheckSum;USHORTSubsystem;USHORTDllCharacteristics;ULONGSizeOfStackReserve;ULONGSizeOfStackCommit;ULONGSizeOfHeapReserve;ULONGSizeOfHeapCommit;ULONGLoaderFlags;ULONGNumberOfRvaAndSizes;IMAGE_DATA_DIRECTORYDataDirectoryIMAG