MZ格式的文件头IMAGE_DOS_HEADER结构定义:
IMAGE_DOS_HEADER STRUCT
e_magic word ? ;DOS可执行文件标记,为“MZ”
e_cblp word ?
e_cp word ?
e_crlc word ?
e_cparhdr word ?
e_minalloc word ?
e_maxalloc word ?
e_ss word ?;DOS代码的初始化堆栈段
e_sp word ?;DOS代码初始化堆栈指针
e_csum word ?
e_ip word ?;DOS代码的入口IP
e_cs word ?;DOS代码的入口CS
e_lfarlc word ?
e_ovno word ?
e_res word ?
e_oemid word ?
e_oeminfo word ?
e_res2 word 10 dup (?)
e_lfanew dword ?;指向PE文件头(总是以8字节为单位对齐的)
IMAGE_DOS_HEADER ENDS
如果是LE,LX等格式文件,那么e_lfanew字段指向的位置回事LE文件头和LX文件头
那么也就是说从3ch位置找到一个双字节,这个双字节指向PE文件头的偏移
PE文件头IMAGE_NT_HEADERS结构:
IMAGE_NT_HEADERS STRUCT
Signature dword ?;PE文件标识,通常为00004550h‘P’,’E’
FileHeader IMAGE_FILE_HEADER <>
OptionalHeader IMAGE_OPTIONAL_HEADER32 <>
IMAGE_NT_HEADERS ENDS
IMAGE_FILE_HEADER结构:
IMAGE_FILE_HEADER STRUCT
Machine word ?;4h-支持平台
NumberOfSections word ?;6h-文件的节数目
TimeDateStamp dword ?;8h-文件创建日期和时间
PointerToSymbolTable dword ?;0ch-指向符号表(用于调试)
NumberOfSysbols dword ?;10h-符号表中符号数量
SizeOfOptionalHeader word ?;14h-IMAGE_OPTIONAL_HEADER32结构的长度
Characteristics word ?16h-文件属性
IMAGE_FILE_HEADER ENDS
IMAGE_OPTIONAL_HEADER32结构:
IMAGE_OPTIONAL_HEADER32 STRUCT
Magic dword ?;18h-107h=ROM Image,10Bh=exe Image
MajorLinkerVersion byte ?;1ah-连接器版本号
MinorLinkerVersion byte ?;1bh
SizeOfCode dword ?;1ch-所有含代码的节总大小
SizeOfInitializedData dword ?;20h-所有含已初始化数据的节总大小
SizeOfUninitializedData dword ?;24h-所有含为初始化数据的节总大小
AddressOfEntryPoint dword ?;28h-代码执行入口RVA
BaseOfCode dword ?;2ch-代码的节的起始RVA
BaseOfData dword ?;30h-数据的节的起始RVA
ImageBase dword ?;34h-程序建议装载地址
SectionAlignment dword ?;38h-内存中的节对齐粒度
FileAlignment dword ?;3ch-文件中的节对齐粒度
MajorOperatingSystemVersion word ?;40h-操作系统主版本号
MinorOperatingSystemVersion word ?;42h-操作系统副版本号
MajorImageVersion word ?;44h-可运行与操作系统的最小版本号
MinorImageVersion word ?;46h
MajorSubsystemVersion word ?;48h-可运行于操作系统的最小子版本号
MinorSubsystemVersion word ?;4ah
Win32VersionValue dword ?;4ch-未用
SizeOfImage dword ?;50h-内存中整个PE映像尺寸
SizeOfHeader dword ?;54h-所有头+节表的大小
CheckSum dword ?;58h
Subsystem word ?;5ch-文件的子系统
DllCharacteristics word ?;5eh
SizeOfStackReserve dword ?;60h-初始化时堆栈大小
SizeOfStackCommit dword ?;64h-初始化时实际提交的堆栈大小
SizeOfHeapReserve dword ?;68h-初始化时保留的堆大小
SizeOfHeapCommit dword ?;6ch-初始化时实际提交的堆大小
LoaderFlags dword ?;70h-未用
NumberOfRvaAndSizes dword ?;74h-下面的数据目录结构的数量
DataDirectory IMAGE_DATA_DIRECTORY 16 dup(<>);78h
IMAGE_OPTIONAL_HEADER32 ENDS
对于每个文件总是使用独立的虚拟地址空间,优先装入地址不可能被其他模块占据,所以exe总是能够按照ImageBase地址装入,但是对于DLL文件来说,由于多个DLL文件全部使用宿主exe文件的地址空间,不能保证优先装入地址没有被其他DLL使用,所以DLL文件中必须包含重定位信息以防万一。
IMAGE_DATA_DIRECTORY结构体:
IMAGE_DATA_DIRECTORY STRUCTS
VirtualAddress dword ?;数据的起始RVA
Isize dword ?;数据块的长度
IMAGE_DATA_DIRECTORY ENDS
Windows装载器在装载DOS部分、PE文件头部分和节表部分时不做任何处理,而装载节的时候根据节的属性做不同的处理:
内存页的属性——对于磁盘映射文件来说,所有的页都是按照磁盘映射文件函数制定的属性设置的,但是装载可执行文件时,与节对应的内存页的属性要按照节的属性来设置,所以在同一个模块的内存页中,从不同节映射过来的内存页的属性是不同的。
节的偏移地址——节的其实地址在磁盘文件中按照IMAGE_OPTIONAL_HEADER32结构的FileAlignment字段对齐的,而被装载到内存中时是按照同一结构中的SectionAlignment对齐的,两者的值可能不同,所以一个节被装入内存后相对于文件头的偏移在磁盘文件中的偏移可能是不同的。节是相同属性数据的组合,当节被装载到内存中的时候,同一个节对应的内存页将被赋予相同的页属性,Windows系统对内存属性的设置是已页尾单位进行的,所以在节内存中的对齐单位必须至少是一个页的大小,对于Win32而言就是4KB,而Win64就是8KB。节在磁盘文件中对齐单位就没有最小4KB的限制,为了减少磁盘文件的大小,文件对齐的单位一般要小于内存的单位,通常为200h,这样,磁盘文件中就不必为每个节最后的零头不足4KB大小了。
节的尺寸——对节的处理有两个方面,首先是由于磁盘映像和内存映像中节对齐单位的不同而造成的长度扩展;其次是对包含为未始化数据的节的处理,对于未初始化数据来说(比如.data?段),没有必要为他们在磁盘文件中预留空间,只要在可执行文件被装载到内存中后卫他们分配空间就可以了,所以包含未初始化数据节的磁盘文件中长度被定义为0,但是被装载到内存中的地址和大小是被明确指定的。对于这种节来说,它所包含的内存页没有磁盘文件与之对应,这些内存页是Windows装载器根据节的定义开辟出来的。
不进行映射的节——有些节中包含的数据仅仅在装载的时候用到,当装载完毕的时候,他们不会被递交物理内存页。最典型的是重定位数据的节,重定位数据对于文件的执行代码是透明的,它只提供Windows装载器使用,执行代码根本不会去访问他们,一旦装载完毕,继续为他们提供内存页是一种浪费,所以这些节存在于磁盘文件中,但不会被映射到内存中。