前言:
PE("portableexecutable")文件格式是针对MSwindowsNT,windows95and
win32s可执行 2进制代码(DLLsandprograms)在windowsNT内,驱动也是这个格式也可以用于对象文件和库
这个格式是Microsoft设计并在1993经过TIS(toolerfacestandard)委员会(Microsoft,Intel,Borland,Watcom,IBM等)标准化了它基于在UNIX和VMS上运行对象文件和可执行文件COFF"commonobjectfileformat"格式
win32SDK包括个头文件<winnt.h>包括对PE格式定义我将提及成员名和定义你也可能发现DLL文件"imagehelp.dll"非常有用它是NT部分但文档很少它些在"DeveloperNetwork"被描述
总览:
在PE文件开始我们可以发现MSDOS执行部分("stub");这使得任何个PE文件是有效DOS执行文件在DOS-stub的后是32位魔数0x00004550(IMAGE_NT_SIGNATURE).然后是个COFF格式文件头指明在何种机器上运行多少个节在里面连接时间是否是可执行文件或者DLL等DLL和可执行文件区别:DLL不能够启动只可以被其他可执行文件使用个可执行文件不能够连接到另个可执行文件
接着我们看到个可选文件头optionalheader(虽然叫“可选”它实际上直存在)
COFF把可选文件头用于库不用于目标文件这里告诉我们文件如何被调入:起始地址预留堆栈数数据段尺寸
个有趣部分是尾巴上数据目录datadirectories这些目录包含指向节内数据指针例如如果文件有输出目录可以在成员IMAGE_DIRECTORY_ENTRY_EXPORT内发现个指针指向那个目录(目录描述结构->THUNKDATA结构->BYNAME结构)他将指向个节
在头后面是节头实际上节内容就是真正需要运行个所需要东西所有头和目录成员就是帮你找到它每个节有几个标志:对齐包含数据类型(化数据等)是否可以共享等及数据自身多数节含有个或多个通过“可选头”内数据目录项引用目录没有目录类型内容是化数据或者可执行代码(节是物理意义上内容组织目录是逻辑意义上内容组织两者互相配合才能找到需要东西节是存储内容地方区域安排目录是如何对里面东西进行查找目是寻找里面内容)
+-------------------+
|DOS-stub |
+-------------------+
|file-header |
+----------+
|optionalheader |
|----------|
| |
|datadirectories |
| |
+-------------------+
| |
|sectionheaders |
| |
+-------------------+
| |
|section1 |
| |
+-------------------+
| |
|section2 |
| |
+-------------------+
| |
|... |
| |
+-------------------+
| |
|sectionn |
| |
+-------------------+
DOS-stubandSignature
----------------------DOSSTUB概念在16位WINDOWS可执行文件内就已经被熟知了STUB是用于OS/2可执行文件自解压文档和其他对于PE文件它是DOS2兼容可执行文件总是包含100字节内容输出个信息:比如"thisprogramneedswindowsNT".
你认识到个DOSSTUB通过验证DOS-header就是个IMAGE_DOS_HEADER结构前两个字节必须使"MZ"(有个定义针对这个WORDIMAGE_DOS_SIGNATURE)你通过尾部'e_lfa'给出偏移量所确定签名区别个PE文件对于PE文件它是个32位按照8字节对齐边界其值0x00004550由IMAGE_NT_SIGNATURE定义.
IMAGE_NT_HEADERSSTRUCT
Signature DWORD ?
FileHeader IMAGE_FILE_HEADER <>
OptionalHeader IMAGE_OPTIONAL_HEADER32<>
IMAGE_NT_HEADERSENDS
文件头FileHeader-----------
IMAGE_FILE_HEADERSTRUCT
Machine WORD ?
NumberOfSections WORD ?
TimeDateStamp DWORD ?
PoerToSymbolTable DWORD ?
NumberOfSymbols DWORD ?
SizeOfOptionalHeader WORD ?
Characteristics WORD ?
IMAGE_FILE_HEADERENDS
要得到IMAGE_FILE_HEADER,确认DOS头前2个字节"MZ"然后找到'e_lfa'成员然后从文件开始跳过许多字节验证你找到签名文件头作为个IMAGE_FILE_HEADER结构,就从它后面开始从上到下描述其成员第1:Machine,16位值指明可执行文件所需要系统已知合法值如下:
IMAGE_FILE_MACHINE_I386
0x014c Intel80386处理器
0x014d Intel80486处理器
0x014e Pentium处理器
0x0160 R3000(MIPS)处理器
IMAGE_FILE_MACHINE_R3000(0x162)R3000(MIPS)处理器
IMAGE_FILE_MACHINE_R4000(0x166)R4000(MIPS)处理器
IMAGE_FILE_MACHINE_R10000(0x168)R10000(MIPS)处理器
IMAGE_FILE_MACHINE_ALPHA(0x184)DECAlphaAXP处理器
IMAGE_FILE_MACHINE_POWERPC(0x1F0)IBMPowerPC处理器
第2:NumberOfSections,16位值它是跟随于头后面节数我们在后面讨论
第3:TimeDateStamp32位值,文件创建时间可以通过该值区分区别文件版本时间戳用于绑定输入目录后面讲到有些连接器设置该值为荒唐值
第3:PoerToSymbolTable和NumberOfSymbols都是32位用于调试信息般都是0
第4:SizeOfOptionalHeader16位是IMAGE_OPTIONAL_HEADER尺寸.可以用它确认PE文件结构正确性
第5:Characteristics16位值包括个标志集合多数只对目标文件和库有效
Bit0(IMAGE_FILE_RELOCS_STRIPPED)如果文件内没有重定位信息该位置1这里指是每个节内重定位信息不用于可执行文件可执行文件重定位信息在后面提到baserelocation目录
Bit1(IMAGE_FILE_EXECUTABLE_IMAGE)如果文件是可执行则置1例如不是个目标文件或者库文件如果连接器试图创建可执行文件但由于某种原因失败了也置1
Bit2(IMAGE_FILE_LINE_NUMS_STRIPPED)如果行数信息剥离置1对可执行文件无效
Bit3(IMAGE_FILE_LOCAL_SYMS_STRIPPED)如果没有本地符号信息该位置1对可执行文件无效
Bit4(IMAGE_FILE_AGGRESIVE_WS_TRIM)如果操作系统被假定通过页换出抢占式修剪进程工作集(进程使用内存数)该位置1
Bits7(IMAGE_FILE_BYTES_REVERSED_LO)和15(IMAGE_FILE_BYTES_REVERSED_HI)如果文件endianess不是机器期望则置1于是读的前必须交换字节对可执行文件不可靠
Bit8(IMAGE_FILE_32BIT_MACHINE)如果机器被期望是32位机器置1
Bit9(IMAGE_FILE_DEBUG_STRIPPED)如果没有调试信息在文件内置1对可执行文件无效
Bit10(IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP)如果不能够从可移动媒体比如软盘或光驱置1操作系统建议拷贝文件到交换文件然后执行
Bit11(IMAGE_FILE_NET_RUN_FROM_SWAP)如果不能够才网络运行置1操作系统建议拷贝文件到交换文件然后执行
Bit12(IMAGE_FILE_SYSTEM)如果文件是类似驱动系统文件置1对执行文件无效
Bit13(IMAGE_FILE_DLL)如果文件是DLL置1.
Bit14(IMAGE_FILE_UP_SYSTEM_ONLY)如果文件不是为多处理器设计置1
相对虚拟地址RelativeVirtualAddresses
--------------------------
PE格式使用所谓RVA用于描述内存地址如果你不知道基地址话需要你加上基地址得到线性地址基地址是PE映像加载地址例如:假如可执行文件加载到0x400000可执行文件RVA是0x1560.有效执行起始地址为0x401560.如果被加载到0x100000,则执行起始位置在0x101560.
情况变复杂起来由于节不必按照加载映像那样对齐例如节般按照512字节对齐加载映像可能按照4096字节对齐参看'SectionAlignment'and'FileAlignment'对齐意思就是地址值=对齐长度倍数
于是为了找到个特殊RVA指向信息你必须计算偏移量好象文件被加载样假如知道执行起点在RVA0x1560,想从这反汇编代码要找到文件内地址你必须找出在RAM内按照4096对齐节".code"节自内存RVA0x1000开始16384字节长你知道RVA0x1560偏移量在那个节内是0x560.找出节在文件内按照512字节对齐且".code"从0x800开始那么在文件内代码执行起点是0x800+0x560=0xd60
然后反汇编并发现个存取地址0x1051d0处变量.线性地址在加载执行文件时重新分配并给出优先加载地址你发现优先加载地址是0x100000,于是我们处理RVA0x51d0.这是个开始于RVA0x5000数据区2048字节长它开始于文件偏移量0x4800.变量可以在文件偏移量0x4800+0x51d0-0x5000=0x49d0处发现
可选头OptionalHeader
---------------
紧跟在文件头后面是IMAGE_OPTIONAL_HEADER尽管名字是可选实际直存在包含有关如何精确处理PE文件信息从上到下介绍成员
IMAGE_OPTIONAL_HEADER32STRUCT
Magic WORD ?
MajorLinkerVersion BYTE ?
MinorLinkerVersion BYTE ?
SizeOfCode DWORD ?
SizeOfInitializedData DWORD ?
SizeOfUninitializedData DWORD ?
AddressOfEntryPo DWORD ?
BaseOfCode DWORD ?
BaseOfData DWORD ?
ImageBase DWORD ?
SectionAlignment DWORD ?
FileAlignment DWORD ?
MajorOperatingVersion WORD ?
MinorOperatingVersion WORD ?
MajorImageVersion WORD ?
MinorImageVersion WORD ?
MajorSubsystemVersion WORD ?
MinorSubsystemVersion WORD ?
Win32VersionValue DWORD ?
SizeOfImage DWORD ?
SizeOfHeaders DWORD ?
CheckSum DWORD ?
Subsystem WORD ?
DllCharacteristics WORD ?
SizeOfStackReserve DWORD ?
SizeOfStackCommit DWORD ?
SizeOfHeapReserve DWORD ?
SizeOfHeapCommit DWORD ?
LoaderFlags DWORD ?
NumberOfRvaAndSizes DWORD ?
DataDirectory IMAGE_DATA_DIRECTORYIMAGE_NUMBEROF_DIRECTORY_ENTRIESdup(<> )
IMAGE_OPTIONAL_HEADER32ENDS
IMAGE_OPTIONAL_HEADER equ <IMAGE_OPTIONAL_HEADER32>
第1个16位字是'Magic'总是0x010b.下面2个字节是连接器版本号'MajorLinkerVersion'和'MinorLinkerVersion'这些值都不可靠不能总是妥当反映连接器版本有些连接器不设置该域
下面3个longwords(32位)指定执行代码尺寸('SizeOfCode'),化数据尺寸
'SizeOfInitializedData',所谓数据段"datasegment",未化数据尺寸
'SizeOfUninitializedData',所谓"bsssegment".这些数值也不可靠
往下个32位RVA.是入口点偏移量('AddressOfEntryPo').执行从此开始
下面2个32位是可执行代码('BaseOfCode')和化数据('BaseOfData')RVAs我们对它没有兴趣可以通过节来查看更可靠信息非化数据没有RVA
下面是个32位值ImageBase'作为整个文件优先加载地址包括所有头在内该值总是
64KB倍数文件已经被连接器重定位如果文件能够真正加载到这个地址加载器不必重定位文件如果另个映像已经被加载到那个地址则优先地址不可使用这种情况下映像被加载到其他地址需要重定位如果映像是DLL还有更多结果"boundimports"不再有效需要对使用DLL执行文件进行修正参见'importdirectory'
下面2个32位是当映像文件加载后PE文件节在内存内对齐'SectionAlignment',以及在文件内对齐'FileAlignment'.般文件对齐是512节对齐是4096.
下面2个16位字是期望操作系统版本'MajorOperatingVersion'和'MinorOperatingVersion'
下面2个16位字是期望可执行文件版本'MajorImageVersion'和
'MinorImageVersion'.许多连接器不正确设置这些信息
下面2个16位字是期望子系统版本'MajorSubsystemVersion和MinorSubsystemVersion.这个必须是Win32版本或者POSIX版本该版本需要正确提供它被检查并使用如果是Win32-GUI并运行在NT4,子系统版本不是4.0,对话框不是3D效果
然后是Win32VersionValue32位大部分情况下是0
下面是32位映像需要内存数量'SizeOfImage'.是所有头和节总和如果节已经对齐它是给加载器线索需要多少页加载映像
下面个是32位所有头总和包括数据目录和节头'SizeOfHeaders'.它也是才文件开始到第节偏移量
然后是32位校验码'CheckSum'.对当前版本NT只校验映像是否是NT驱动对于其他可执行文件类型不必提供这个码可能为0
然后是16子系统Subsystem'表明在什么系统上运行:
IMAGE_SUBSYSTEM_NATIVE(1)执行文件不需要子系统用于驱动
IMAGE_SUBSYSTEM_WINDOWS_GUI(2)映像是Win32图形可以打开控制台
IMAGE_SUBSYSTEM_WINDOWS_CUI(3)映像是Win32控制台可以得到缺省控制台
IMAGE_SUBSYSTEM_OS2_CUI(5)映像是OS/2控制台是OS/2格式
IMAGE_SUBSYSTEM_POSIX_CUI(7)映像使用POSIX控制台子系统
Windows95可执行文件总是使用Win32subsystem,于是合法值是2和3
下面是16位DllCharacteristics,表明是否是DLL,如果0位置1DLL被通知进程结合位1置1DLL被通知线程脱离位2置1DLL被通知线程结合位3置1DLL被通知进程脱离
下面4个32位预留堆栈大小'SizeOfStackReserve',提交堆栈大小'SizeOfStackCommit',预留堆大小'SizeOfHeapReserve'和提交堆大小'SizeOfHeapCommit'.
预留数量是地址空间不是真实RAM启动时提交数量是真正分配内存这个值也是堆和栈根据需要增长个数量
例如:个预留1MB堆并提交堆时64KB,该堆就从64KB开始并保证可以加大到1MB.堆将以64KB块增长该堆在这里是主要堆默认堆个进程可以创建多个堆如果需要话栈是第个线程栈进程可以创建许多线程每个都有自己堆栈DLLs没有栈或者堆于是在其映像内该值被忽略
下面是32位LoaderFlags,没有用
然后是32位NumberOfRvaAndSizes,在随后目录内有效项目数最好使用
IMAGE_NUMBEROF_DIRECTORY_ENTRIES即16
下面是具有IMAGE_NUMBEROF_DIRECTORY_ENTRIES(16)个成员IMAGE_DATA_DIRECTORYs结构.
IMAGE_DATA_DIRECTORYSTRUCT
VirtualAddress DWORD ?
isize DWORD ?
IMAGE_DATA_DIRECTORYENDS
每个目录描述了节内特定信息位置32bitsRVAVirtualAddress和尺寸32bit,各个成员索引如下(括号内为索引值):IMAGE_DIRECTORY_ENTRY_EXPORT(0)输出符号目录用于DLL
IMAGE_DIRECTORY_ENTRY_IMPORT(1)输入符号目录
IMAGE_DIRECTORY_ENTRY_RESOURCE(2)资源目录
IMAGE_DIRECTORY_ENTRY_EXCEPTION(3)异常目录
IMAGE_DIRECTORY_ENTRY_SECURITY(4)安全目录
IMAGE_DIRECTORY_ENTRY_BASERELOC(5)重定位表
IMAGE_DIRECTORY_ENTRY_DEBUG(6)调试目录
IMAGE_DIRECTORY_ENTRY_COPYRIGHT(7)描述版权串
IMAGE_DIRECTORY_ENTRY_GLOBALPTR(8)机器值
IMAGE_DIRECTORY_ENTRY_TLS(9)Threadlocalstorage目录
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG(10)Loadconfiguration目录
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT(11)Boundimportdirectory目录
IMAGE_DIRECTORY_ENTRY_IAT(12)ImportAddressTable输入地址表目录
例如如果我们找到索引72个值0x12000和33,加载地址是0x10000,我们知道版权数据是在0x10000+0x12000版权字数为33如果个特定类型目录没有被使用地址和尺寸都为0
节目录Sectiondirectories
-------------------
节包含2个部分:节头IMAGE_SECTION_HEADER节数据在数据目录的后我们看到个具有NumberOfSections个节头成员按RVA排序
节头包括:
IMAGE_SECTION_HEADERSTRUCT
Name1dbIMAGE_SIZEOF_SHORT_NAMEdup(?)
unionMisc
PhysicalAddressdd ?
VirtualSizedd ?
ends
VirtualAddressdd ?
SizeOfRawDatadd ?
PoerToRawDatadd ?
PoerToRelocationsdd?
PoerToLinenumbersdd?
NumberOfRelocationsdw ?
NumberOfLinenumbersdw ?
Characteristicsdd ?
IMAGE_SECTION_HEADERENDS
IMAGE_SIZEOF_SHORT_NAME(8)个字节组成节名字如果所有8个字节被用掉没有0做为结尾典型名字如".data"或者".text"或者".bss".没有必要前导'.',可以是是"CODE"或"IAT".注意名字不全部跟节内容有关个".code"节可能或没有可能包括可执行代码可能只包括输入地址表可能包含代码和地址表和化数据要找到在节内信息必须通过“可选头”内数据目录查找他们不要依赖名字不要假定节原始数据起始于节开始
最新评论