版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
PE文件结构详解1摘要WindowsNT3.1引入了一种名为PE文件格式的新可执行文件格式。PE文件格式的规范包含在了MSDN的CD中(SpecsandStrategy,Specifications,WindowsNTFileFormatSpecifications),但是它非常之晦涩。然而这一的文档并未提供足够的信息,所以开发者们无法很好地弄懂PE格式。本文旨在解决这一问题,它会对整个的PE文件格式作一个十分彻底的解释,另外,本文中还带有对所有必需结构的描述以及示范如何使用这些信息的源码示例。为了获得PE文件中所包含的重要信息,我编写了一个名为PEFILE.DLL的动态链接库,本文中所有出现的源码示例亦均摘自于此。这个DLL和它的源代码都作为PEFile示例程序的一部分包含在了CD中(译注:示例程序请在MSDN中寻找,本站恕不提供),你可以在你自己的应用程序中使用这个DLL;同样,你亦可以依你所愿地使用并构建它的源码。在本文末尾,你会找到PEFILE.DLL的函数导出列表和一个如何使用它们的说明。我觉得你会发现这些函数会让你从容应付PE文件格式的。2介绍Windows操作系统家族最近增加的WindowsNT为开发环境和应用程序本身带来了很大的改变,这之中一个最为重大的当属PE文件格式了。新的PE文件格式主要来自于UNIX操作系统所通用的COFF规范,同时为了保证与旧版本MS-DOS及Windows操作系统的兼容,PE文件格式也保留了MS-DOS中那熟悉的MZ头部。在本文之中,PE文件格式是以自顶而下的顺序解释的。在你从头开始研究文件内容的过程之中,本文会详细讨论PE文件的每一个组成部分。很多解决PE文件格式的工作和直接观看数据有关。例如,要弄懂导入地址名称表是如何构成的,我就得同时查看.idata段头部、导入映像数据目录、可选头部以及当前的.idata段实体,而EXEVIEW.EXE就是查看这些信息的最佳示例。在针对PE文件的有关编程中,你可能用到以下一些数据结构:IMAGE_DOS_HEADERIMAGE_IMPORT_DESCRIPTORIMAGE_NT_HEADERS IMAGE_SECTION_HEADERIMAGE_OPTIONAL_HEADERIMAGE_DATA_DIRECTORYIMAGE_FILE_HEADER3PE文件结构图RVA记录在可选头部中(3)RVA记录在可选头部中(3)IMAGE_NT_HEADERS(1)(2)(4)IMAGE_SECTION_HEADERlfnew-DOS头部长度IMAGE_DOS_HEADERDWORDSignature;IMAGE_FILE_HEADERIMAGE_OPTIONAL_HEADER长度:dosHeader->elfnewdosHeader长度图1PE文件结构从MS-DOS文件头结构开始,我将按照PE文件格式各成分的出现顺序依次对其进行讨论,并且讨论的大部分是以示例代码为基础来示范如何获得文件的信息的。3.1MS-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个字节,描述它内容的结构,即图1中的(1)部分的定义如下:typedefstruct_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_res[4];//保留字USHORTe_oemid;//OEM标识符(相对e_oeminfo)USHORTe_oeminfo;//OEM信息USHORTe_res2[10];//保留字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文件头部就是由它定位的。且MS_DOS头部(dos_head)的地址即为文件映像后的基地址,对于WindowsNT的PE文件来说,PE文件头部是紧跟在MS-DOS头部和实模式程序残余之后的。.操作打开文件:hFile=CreateFile(lpFileName,…);MS-DOS头部地址:dos_head=(IMAGE_DOS_HEADER*)basepointer;打印MS-DOS头部信息:3.2实模式残余程序实模式残余程序是一个在装载时能够被MS-DOS运行的实际程序。对于一个MS-DOS的可执行映像文件,应用程序就是从这里执行的。对于Windows、OS/2、WindowsNT这些操作系统来说,MS-DOS残余程序就代替了主程序的位置被放在这里。这种残余程序通常什么也不做,而只是输出一行文本,例如:“ThisprogramrequiresMicrosoftWindowsv3.1orgreater.”当然,用户可以在此放入任何的残余程序,这就意味着你可能经常看到像这样的东西:“Youcan''trunaWindowsNTapplicationonOS/2,it''ssimplynotpossible.”当为Windows3.1构建一个应用程序的时候,链接器将向你的可执行文件中链接一个名为WINSTUB.EXE的默认残余程序。你可以用一个基于MS-DOS的有效程序取代WINSTUB,并且用STUB模块定义语句指示链接器,这样就能够取代链接器的默认行为。为WindowsNT开发的应用程序可以通过使用-STUB:链接器选项来实现。不同的文件,其大小不一样,即图1中(2)部分的大小由MS-DOS头的域e_lfnew来确定。3.3PE文件头部与标志PE文件头部的地址(peheader)是由MS-DOS头部的e_lfanew域定位的,这个域只是给出了文件的偏移量,所以要确定PE头部的实际内存映射地址,就需要添加文件的内存映射基地址。Peheader=dos_head+dos_head->e_lfanew。PE文件头部的定义,即图1中(3)部分的定义如下:TheIMAGE_NT_HEADERSstructurerepresentsthePEheaderformat.typedefstruct_IMAGE_NT_HEADERS{
DWORDSignature;
IMAGE_FILE_HEADERFileHeader;
IMAGE_OPTIONAL_HEADEROptionalHeader;}IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS;其中,文件头FileHeader的结构体定义如下:TheIMAGE_FILE_HEADERstructurerepresentstheCOFFheaderformat.typedefstruct_IMAGE_FILE_HEADER{
WORDMachine;
WORDNumberOfSections;
DWORDTimeDateStamp;
DWORDPointerToSymbolTable;
DWORDNumberOfSymbols;
WORDSizeOfOptionalHeader;
WORDCharacteristics;}IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;这个文件头结构中一个有用的入口是NumberOfSections域,它表示如果你要方便地提取文件信息的话,就需要了解多少个段,更明确一点来说,有多少个段头部和多少个段实体。每一个段头部和段实体都在文件中连续地排列着,所以要决定段头部和段实体在哪里结束的话,段的数目是必需的。以下的语句从PE文件头中提取了段的数目:numberofsection=peHeader->FileHeader.NumberOfSections; 3.4PE可选头部PE可执行文件中接下来的224个字节组成了PE可选头部。虽然它的名字是“可选头部”,但是请确信:这个头部并非“可选”,而是“必需”的。可选头部的偏移量即为:offset=dos_head->e_lfanew+SIZE_OF_NT_SIGNATURE(即:4)+sizeof(IMAGE_FILE_HEADER)。可选头部包含了很多关于可执行映像的重要信息,例如初始的堆栈大小、程序入口点的位置、首选基地址、操作系统版本、段对齐的信息等等。IMAGE_OPTIONAL_HEADER结构如下:TheIMAGE_OPTIONAL_HEADERstructurerepresentstheoptionalheaderformat.typedefstruct_IMAGE_OPTIONAL_HEADER{
WORDMagic;
BYTEMajorLinkerVersion;
BYTEMinorLinkerVersion;
DWORDSizeOfCode;//Sizeofthecodesection,inbytes,orthesumofallsuchsections//iftherearemultiplecodesections.DWORDSizeOfInitializedData;//Sizeoftheinitializeddatasection,inbytes,or//thesumofallsuchsectionsiftherearemultipleinitializeddatasections.DWORDSizeOfUninitializedData;//Sizeoftheuninitializeddatasection,inbytes,orthesumofallsuchsectionsiftherearemultipleuninitializeddatasections.DWORDAddressOfEntryPoint;//Pointertotheentrypointfunction,relativetothe//imagebaseaddress.TheentrypointfunctionisoptionalforDLLs.Whennoentry//pointispresent,thismemberiszero.DWORDBaseOfCode;//Pointertothebeginningofthecodesection,relativetothe//imagebase.DWORDBaseOfData;//Pointertothebeginningofthedatasection,relativetothe//imagebase.DWORDImageBase;//Preferredaddressofthefirstbyteoftheimagewhenitisloaded//inmemory.Thisvalueisamultipleof64Kbytes.ThedefaultvalueforDLLsis//0x10000000.Thedefaultvalueforapplicationsis0x00400000.DWORDSectionAlignment;//Alignmentofsectionsloadedinmemory,inbytes.Thisvalue//mustbegreaterthanorequaltotheFileAlignmentmember.Thedefaultvalueisthe//pagesizeforthesystem.DWORDFileAlignment;
WORDMajorOperatingSystemVersion;
WORDMinorOperatingSystemVersion;
WORDMajorImageVersion;
WORDMinorImageVersion;
WORDMajorSubsystemVersion;
WORDMinorSubsystemVersion;
DWORDWin32VersionValue;
DWORDSizeOfImage;//Sizeoftheimage,inbytes,includingallheaders.Mustbea//multipleofSectionAlignment.DWORDSizeOfHeaders;//CombinedsizeoftheMS-DOSstub,thePEheader,andthesection//headers,roundedtoamultipleofthevaluespecifiedintheFileAlignmentmember.DWORDCheckSum;
WORDSubsystem;
WORDDllCharacteristics;
DWORDSizeOfStackReserve;
DWORDSizeOfStackCommit;
DWORDSizeOfHeapReserve;
DWORDSizeOfHeapCommit;
DWORDLoaderFlags;
DWORDNumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORYDataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];}IMAGE_OPTIONAL_HEADER,*PIMAGE_OPTIONAL_HEADER;其中NumberOfRvaAndSizes这个域标识了接下来的DataDirectory数组。请注意它被用来标识这个数组,而不是数组中的各个入口数字,这一点非常重要。DataDirectory。数据目录表示文件中其它可执行信息重要组成部分的位置。它事实上就是一个IMAGE_DATA_DIRECTORY结构的数组,位于可选头部结构的末尾。TheIMAGE_DATA_DIRECTORYstructurerepresentsthedatadirectory.typedefstruct_IMAGE_DATA_DIRECTORY{
DWORDVirtualAddress;
DWORDSize;}IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;当前的PE文件格式定义了16种可能的数据目录,这之中的11种现在在使用中。数据目录的各个元素依次如下所示:Thefollowingisalistofthedatadirectories:Offset都是用于库函数的引入Description都是用于库函数的引入96Exporttableaddressandsize104Importtableaddressandsize//输入表(引入表)112Resourcetableaddressandsize120Exceptiontableaddressandsize128Certificatetableaddressandsize136Baserelocationtableaddressandsize//重定位信息144Debugginginformationstartingaddressandsize152Architecture-specificdataaddressandsize160Globalpointerregisterrelativevirtualaddress168Threadlocalstorage(TLS)tableaddressandsize176Loadconfigurationtableaddressandsize184Boundimporttableaddressandsize//绑定输入表(引入表)192Importaddresstableaddressandsize200Delayimportdescriptoraddressandsize208Reserved由上表可知,有如下定义#defineIMAGE_NUMBEROF_DIRECTORY_ENTRIES16。4PE文件节表(段头部)PE文件规范由目前为止定义的那些头部以及一个名为“段”的一般对象组成。段包含了文件的内容,包括代码、数据、资源以及其它可执行信息,每个段都有一个头部和一个实体(原始数据)。我将在下面描述段头部的有关信息,但是段实体则缺少一个严格的文件结构。因此,它们几乎可以被链接器按任何的方法组织,只要它的头部填充了足够能够解释数据的信息。段头部定义如下:TheIMAGE_SECTION_HEADERstructurerepresentstheimagesectionheaderformat.typedefstruct_IMAGE_SECTION_HEADER{
BYTEName[IMAGE_SIZEOF_SHORT_NAME];
union{
DWORDPhysicalAddress;//Fileaddress.DWORDVirtualSize;//Totalsizeofthesectionwhenloadedintomemory,inbytes.//ifthisvalueisgreaterthantheSizeOfRawDatamember,thesectionisfilledwith//zeroes.}Misc;
DWORDVirtualAddress;//Addressofthefirstbyteofthesectionwhenloadedintomemory,//relativetotheimagebase.DWORDSizeOfRawData;//Sizeoftheinitializeddataondisk,inbytes.Thisvalue//mustbeamultipleoftheFileAlignmentmemberoftheIMAGE_OPTIONAL_HEADERstructure.//IfthisvalueislessthantheVirtualSizemember,theremainderofthesectionis//filledwithzeroes.Ifthesectioncontainsonlyuninitializeddata,thememberis//zero.DWORDPointerToRawData;//FilepointertothefirstpagewithintheCOFFfile.Thisvaluemust//beamultipleoftheFileAlignmentmemberoftheIMAGE_OPTIONAL_HEADERstructure.If//asectioncontainsonlyuninitializeddata,thismemberiszero.//本节在文件中的偏移量DWORDPointerToRelocations;//Filepointertothebeginningoftherelocationentries//forthesection.Iftherearenorelocations,thisvalueiszero.DWORDPointerToLinenumbers;//Filepointertothebeginningoftheline-number//entriesforthesection.IftherearenoCOFFlinenumbers,thisvalueiszero.WORDNumberOfRelocations;//Numberofrelocationentriesforthesection.Thisvalue//iszeroforexecutableimages.WORDNumberOfLinenumbers;//Numberofline-numberentriesforthesection.DWORDCharacteristics;}IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;段头部信息在文件中的位置已在图1的(4)部分模拟出,要得到所有段头部的总大小:SecAllSize=numberofsection*sizeof(IMAGE_SECTION_HEADER);由于PE文件标志占4个字节,通常有这样的宏定义:#defineSIZE_OF_NT_SIGNATURE4,即可按如下方法得到:则第一个段头部的偏移量可按如下方法得到:SecOffset=dos_head->e_lfanew+SIZE_OF_NT_SIGNATURE+sizeof(IMAGE_FILE_HEADER)+peHeader->FileHeader.SizeOfOptionalHeader;PE的所有段头部是线性排列,且各段头部大小相同,所以可将指向第一个段的指针向前移动sizeof(IMAGE_SECTION_HEADER)多个单元,以指向下一个段头部。*(section_header+1)与section_header[1]指向同一个单元。5PE文件的输入表输入表的结构:输入表是以一个IMAGE_IMPORT_DESCRIPTOR(IID)数组开始,一个程序要调用几个dll就会有几个IID项,即每个IID对应于一个dll。IID结构:typedefstruct_IMAGE_IMPORT_DESCRIPTOR{Union{DWORDCharacteristics;//00hDWORDOriginalFirstThunk;//注释1};DWORDTimeDateStamp;//04h时间标志,可以忽略;DWORDForwarderChain;//08h正向链接索引,一般为0,当程序引用一个dll//中的api,而这个api又引用其它dll中的api时用DWORDName;//0ChDLL名字的指针,以00结尾的ASCII字符的RVA地址;DWORDFirstThunk;//10h注释2}IMAGE_IMPORT_DESCRIPTOR;由该结构体可知,每一个描述输入表信息的数据结构在内存中占20个字节。注释1:该值为一个IMAGE_THUNK_DATA数组的RVA,其中的每个指针都指向IMAGE_IMPORT_BY_NAME结构。(IMAGE_THUNK_DATA包含了一个指向IMAGE_IMPORT_BY_NAME结构的指针,而非该结构本身,即:有几个IMAGE_IMPORT_BY_NAME结构,收集这些结构的RVA(即:IMAGE_THUNK_DATA)组成一个数组,以0结尾,然后将数组的RVA(指针)放入OriginalFirst-Thunk)。IMAGE_THUNK_DATA的结构:typedefstruct_IMAGE_THUNK_DATA{Union{PBYTEForwarderString;//当该结构双字最高位为1时,表示函数以序号方式输//入,此时双字低位为函数序号PDWORDFunction;//当最高位为0时,表示函数以字符串类型的函数名方式输//入,此时整个双字的值是DWORDOrdinal;//一个RVA,指向一个MAGE_IMPORT_BY_NAME结构//(如下定义)PIMAGE_IMPORT_BY_NAMEAddressOfData;}u1;}IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA;该结构只占4个字节,在文件中准确定位到该块数据时,只需要读取4个字节。从而就可判断所读取的数据的最高为是为0还是为1,进而确定函数输入方式。注意该值也是一个相对虚拟地址RVA,指示了IMAGE_IMPORT_BY_NAME结构的数据地址。IMAGE_IMPORT_BY_NAME结构:typedefstruct_IMAGE_IMPORT_BY_NAME{WORDHint;//指示本函数在所驻留dll中的输出表中的序号(不是必须的)BYTEName[1];//含有输入函数的函数名,一个ASCII码字符串,以NULL结尾}IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;在应用过程中,通常将IMAGE_IMPORT_BY_NAME自定义为如下的数据类型:typedefstruct_IMAGE_IMPORT_BY_NAME1{WORDHint;//指示本函数在所驻留dll中的输出表中的序号(不是必须的)BYTEName[256];//含有输入函数的函数名,一个ASCII码字符串,以NULL结尾}IMAGE_IMPORT_BY_NAME1,*PIMAGE_IMPORT_BY_NAME1;注释2:FirstThunk也是一个指向IMAGE_THUNK_DATA数组的RVA地址,如果不是一个指针,则就是该功能在DLL中的序号;注释3:OriginalFirstThunk与FirstThunk在本质上一致,不同在于:在pe文件被pe装载器装入之前,两者一样,指向同一个数组。当pe文件被装载器装入之后,OriginalFirstThunk还指向原来的数组(INT输入名字表:ImportNameTable),而FirstThunk则用调用的输入函数在内存的虚拟地址来代替表中的内容,即指向的是一张指向输入函数地址的表,称为:IAT(输入地址表:ImportAddressTable)。 综上所述,可知输入表有如下的文件存储模式:5PE文件的输出表输出表的结构定义:typedefstruct_IMAGE_EXPORT_DIRECTORY{DWORDCharacteristics;DWORDTimeDateStamp;WORDMajorVersion;WORDMinorVersion;DWORDName;DWORDBase;DWORDNumberOfFunctions;DWORDNumberOfNames;DWORDAddressOfFunctions;//RVAfrombaseofimageDWORDAddressOfNames;//RVAfrombaseofimageDWORDAddressOfNameOrdinals;//RVAfrombaseofimage}IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;输出表一般存在与dll文件中,很少出现在exe文件中(也有)。主要为可执行文件修正其IAT提供信息和依据;输出表的位置在:pe头的可选映像头中的数据目录表的第1个字段中,OffSet=PEHeader->OptionalHeader.DataDirectory[0].VirtualAddress处,这是一个相对虚拟地址。要定位到文件的真实物理地址,或者定位到文件映射后在内存中的地址,只需要将OffSet转化为真实物理地址的偏移地址,即:RVA-->RAW,若是后者,加上文件映射后的基址就可以准确定位到相应的内存单元,以操作IED结构数据。准确定位后,只要读取sizeof(IMAGE_EXPORT_DIRECTORY)字节的数据,即可获取输出表的描述信息。说明:pBuffer为文件映射后的基址。输出表指向一个IMAGE_EXPORT_DIRECTORY(IED),其结构定义如下:typedefstructIMAGE_EXPORT_DIRECTORY{ULONGCharateristics;未使用,总为0ULONGTimeDateStamp;文件生成时间USHORTMajorVersion;主版本号,一般为0USHORTMinorVersion;次版本号,一般为0ULONGName;模块中的真实名称ULONGBase;基数,加上序数就是函数地址数组的索引值//通常为1ULONGNumberOfFunctions;AddressOfFunction序列中的元素个数ULONGNumberOfNames;AddressOfNames序列中的元素个数PULONG*AddressOfFunctions;指向函数地址数组PULONG*AddressOfNames;函数名字的指针地址PUSHORT*AddressOfNameOndinals;指向输出序号数组}IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY说明:01、NumberOfFunctions和NumberOfNames一般相等;
02、NumberOfNames此值一般表示以名称输出的函数个数,通常与输出函数总数相等。若为0,表示模块仅仅通过序号引出。
通过上面的定义,输出表在文件中有如下的存储结构:注意:01.以上描述的地址均是RVA 02.第i个函数名称与第i个输出序号对应,但与第i个函数地址不对应。他们之间存在如下的关系:第i个函数名称对应的地址=(RVA1—>RAW)+第i个输出序号。6PE文件的重定位表为方便理解,作如下定义:PIMAGE_DOS_HEADERpDosHeader;//指向DOS头部PIMAGE_NT_HEADERSpNTHeader;//指向NT头部 PDWORDRelocTableRVA;//指向重定位表的相对虚拟地址的指针PIMAGE_BASE_RELOCATIONpReloc;//指向重定位表的真实地址首先,先判断重定位表是否存在:RelocTableRVA=&(pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress),判断RelocTableRVA是否为0即可。若不为0,则重定位表存在。重定位表的数据组织方式是由许多重定位块串接而成。每个块是必须以4字节对齐,用0填补空缺。重定位块的结构如下:typedefstruct_IMAGE_BASE_RELOCATION{DWORDVirtualAddress;DWORDSizeOfBlock;//WORDTypeOffset[1];}IMAGE_BASE_RELOCATION;(1)VirtualAddress:这组重定位数据的RVA地址。(2)SizeOfBlack:四个字节,当前重定位结构的大小。(3)TypeOffset:是一个数组。数组每项大小是两个字节,其中高4位是重定位的类型,低12位是重定位地址,它与VirtualAddress相加就是PE映像需要修改的地址数据的指针。 每个重定位块的每2个字节的低12位表示一个重定位数据的RVA,该RVA加上VirtualAddress就是该重定位数据的真实偏移地址RAW,RAW加上基地址就得到重定位数据的真实物理地址。把该地址中的数据(需要修正的数据)减去IMAGE_OPTINAL_HEADER中的ImageBase,再加上当前加载的实际基址就可以了。6PE文件的资源段通常情况,资源节的名称一般都为:.rsrc。目前我们只考虑这种情况。Section的结构说明如下:typedefstruct_IMAGE_SECTION_HEADER{ BYTEName[IMAGE_SIZEOF_SHORT_NAME]; Union{ DWORDPhysicalAddress; DWORDVirtualSize; }Misc; DWORDVirtualAddress; DWORDSizeOfRawData; DWORDPointerToRawData; DWORDPointerToRelocations; DWORDPointerToLinenumbers; WORDNumberOfRelocations; WORDNumberOfLinenumbers; DWORDCharacteristics;}IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;获取资源节表的首地址:IMAGE_DOS_HEADER*dosHeadA=(IMAGE_DOS_HEADER*)pFileSource;//DOS头IMAGE_NT_HEADERS*ntHeadA=(IMAGE_NT_HEADERS*)(pFileSource+dosHeadA->e_lfanew);//NT头IMAGE_SECTION_HEADER*secHeadA=(IMAGE_SECTION_HEADER*)((char*)ntHeadA+sizeof(IMAGE_NT_HEADERS));//第一个节的首地址//循环找出.rsrc节for(inti=0;i<ntHeadA->FileHeader.NumberOfSections;i++,secHeadA++){if(strcmp((char*)secHeadA->Name,".rsrc")==0){//找到.rsrc节 ……break;}}下面是几个需要用到的结构与相关的解释:typedefstruct_IMAGE_RESOURCE_DIRECTORY{//资源树结构 DWORDCharacteristics;//标识此资源的类型 DWORDTimeDateStamp; WORDMajorVersion; WORDMinorVersion; WORDNumberOfNamedEntries; WORDNumberOfIdEntries;//此结构下还包含有的资源结构树,即:还有几个子树。//IMAGE_RESOURCE_DIRECTORY_ENTRYDirectoryEntries[];//请注意这里,下面还会讲到。}IMAGE_RESOURCE_DIRECTORY,*PIMAGE_RESOURCE_DIRECTORY;根据节表,我们就可以找到资源的入口地址。IMAGE_RESOURCE_DIRECTORY*dirResourceA=(IMAGE_RESOURCE_DIRECTORY*)((char*)pFileSource+secHeadA->PointerToRawData);//得到资源入口地址此结构的其他解释请见VC的头文件winnt.h.整个资源的结构就好像一棵树型,不同资源如:menu,icon,dialog,cursor等。都如同每根树枝,树枝的Characteristics会标识不同的资源类型,而每根树枝又会有子树枝。这样一直循环,直到IMAGE_RESOURCE_DIRECTORY的NumberOfIdEntries为0时才结束。通常情况,子树都分为三层。每一个子树的类型由IMAGE_RESOURCE_DIRECTORY中的Characteristics来标识。如:当第一层的Characteristics==3时,则说明此结构为ICON资源。Characteristics类型定义如下(可在winuser.h中找到):/**PredefinedResourceTypes*/#defineRT_CURSORMAKEINTRESOURCE(1)#defineRT_BITMAPMAKEINTRESOURCE(2)#defineRT_ICONMAKEINTRESOURCE(3)#defineRT_MENUMAKEINTRESOURCE(4)#defineRT_DIALOGMAKEINTRESOURCE(5)#defineRT_STRINGMAKEINTRESOURCE(6)#defineRT_FONTDIRMAKEINTRESOURCE(7)#defineRT_FONTMAKEINTRESOURCE(8)#defineRT_ACCELERATORMAKEINTRESOURCE(9)#defineRT_RCDATAMAKEINTRESOURCE(10)#defineRT_MESSAGETABLEMAKEINTRESOURCE(11)要得到每个子资源的入口地址。这里要用到的一个结构是:typedefstruct_IMAGE_RESOURCE_DIRECTORY_ENTRY{ Union { struct { DWORDNameOffset:31; DWORDNameIsString:1; }; DWORDName; WORDId; }; Union { DWORDOffsetToData;//指向资源的入口址 struct { DWORDOffsetToDirectory:31; DWORDDataIsDirectory:1;//指向下一级目录的相对地址 }; };}IMAGE_RESOURCE_DIRECTORY_ENTRY,*PIMAGE_RESOURCE_DIRECTORY_ENTRY;上面对IMAGE_RESOURCE_DIRECTORY_ENTRY的解释也已经是非常清楚了。结构中有两个成员:OffsetToData,DataIsDirectroy,当DataIsDirectroy大于0时,则说明此结构还有下一级目录,否则,OffsetToData肯定不为0。那OffsetToData的值就是我们所得到的资源入口的RVA了。那么,IMAGE_RESOURCE_DIRECTORY_ENTRY结构应该怎么得到呢?让我们再看一下,IMAGE_RESOURCE_DIRECTORY的结构说明吧。typedefstruct_IMAGE_RESOURCE_DIRECTORY{//资源树结构 DWORDCharacteristics;//标识此资源的类型 DWORDTimeDateStamp; WORDMajorVersion; WORDMinorVersion; WORDNumberOfNamedEntries; WORDNumberOfIdEntries;// //IMAGE_RESOURCE_DIRECTORY_ENTRYDirectoryEntries[];紧跟在后 //面的就是MAGE_RESOURCE_DIRECTORY_ENTRY结构数组, //DirectoryEntries数组的个数实际上也就是NumberOfIdEntries.你也可以 //理解为IMAGE_RESOURCE_DIRECTORY_ENTRY //DirectoryEntries[NumberOfIdEntri
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 【正版授权】 IEC 62083:2025 FR Medical device software - Requirements for the safety of radiotherapy treatment planning systems
- 2025年高职计算机网络技术(网络安全防护)试题及答案
- 2025年大学运动生理学(肌肉力量训练)试题及答案
- 励志实拍大学生自我介绍开学个人简历
- 工程机械培训课件
- 工程技术类培训课件
- 工程土建类培训课件
- 2026年安全生产隐患排查治理安全培训管理考试题库及答案
- 2026年工程建设领关于开展工程建设领域突出问题专项治理工作
- 成本效益分析与优化策略
- GB/T 8642-2025热喷涂抗拉结合强度的测定
- 贵州省贵阳市2024-2025学年高一上学期期末监测物理试卷(含解析)
- 平昌县2025年下半年公开考调公务员(参照管理工作人员)备考题库附答案
- 2025年华中科技大学职工队伍公开招聘备考题库附答案详解
- 2025年全国自考管理学原理真题及答案
- 期末冲刺备考总动员校长在教师会议上讲话:五字诀精实盯严稳
- 2025年度急诊科护士长述职报告
- 2026年郑州电力高等专科学校单招职业技能考试模拟测试卷附答案解析
- 湖北省武汉市洪山区2024-2025学年五年级上学期期末数学试卷
- 装修工程施工方案简单版
- 七年级历史下册期末模拟试卷题库试题附答案完整版
评论
0/150
提交评论