2020.07.06-2020.07.12
MS-DOS头部
每一个pe文件都以一个dos程序开始。程序在dos下执行,dos就能识别出这是一个有效的执行体,然后运行MZ header的DOS stub(dos块)。DOS stub是一个有效的exe,大多数情况下由编译器或汇编器自动生成。通常把DOS MZ头和DOS stub合称为DOS文件头。
PE文件的第一个字节位于MS-DOS头部,称作IMAGE_DOS_HEADER,其中有两个字段:e_magic和e_lfanew。
-
e_magic的值被设为5A4Dh,ASCII值为MZ
-
e_lfanew指出真正的PE文件头的相对偏移位置,占4字节,在文件开始偏移3Ch字节处。
PE文件头部
紧接着DOS stub的是PE文件头。PE文件头是PE相关结构NT映像头的简称,其中包含许多PE装载器能用到的重要字段。当执行体在支持PE文件结构的操作系统中执行是,PE装载器将从IMAGE_DOS_HADER结构的e_lfanew字段中找到PE文件头的起始偏移量,加上基址就得到PE文件头的指针。
IMAGE_DOS_HEADER有两个版本,一个是为32位(PE32)可执行文件准备的,另一个是64位(PE32+)的,两个几乎没有区别。
IMAGE_DOS_HEADER中有3个字段:
Signature字段
在一个有效的PE文件里,Signature字段被设置为0x00004550,对应ASCII字符为PE00
MS-DOS头部的e_lfanew正是指向这个字段
FileHeader字段
IMAGE_FILE_HEADER(映像头文件)结构包含PE文件的一些基本信息和这个结构的大小。
结构的各个字段包括:
-
Machine:可执行文件的目标cpu类型
-
NumberOfSection:区块的数目
-
TimeDateStamp:文件创建时间
-
PointerToSymbolTable:COFF符号表的文件偏移位置
-
NumberOfSymbols:如果有COFF符号表,它代表其中的符号数目,可以用来找到COFF符号表的结束处
-
SizeOfOptionalHeader:表示数据的大小,依赖于文件是32位还是64位
-
Characteristics:文件属性,定义域winnt.h内的IMAGE_FILE_xxx值
OptionalHeader字段
定义PE文件的其他属性
区块
区块表
区块表是一个IMAGE_SECTION_HEADER结构数组,每个区块表结构包含了它所关联的区块的信息,例如位置、长度等,数组的数目由IMAGE_NT_HEADERS.FileHeader.NumberOfSection指出。
常见区块与区块合并
链接器能够合并区块,优点是节省空间。每个区块至少占用一个内存页,将两个区块合并就有可能少用一个内存页。
部分在载入内存时由Windows加载器写入的输入数据可能会被放入只读区块,因为在加载时,系统会临时修改那些包含输入数据的页属性为可读可写,初始化完成后恢复为原来的属性
区块对齐值
区块有两种对齐值,一种用于磁盘文件内,一种用于内存,两者可以不同。
PE文件头里的FileAlignment定义了磁盘区块的对齐值。在不足的地方(区块间隙)用00h填充
PE文件头里的SectionAlignment定义了内存区块的对齐值。当PE文件被映射到内存中时,区块总是至少从一个页边界处开始,即每个区块的第一个字节对应于某个内存页。
建立一个区块在文件中的偏移和内存中的偏移相同的PE文件,可以提高载入速度并使文件变大
文件偏移与虚拟地址的转换
对于磁盘对齐值和内存页不同的区块,同一数据在磁盘文件中的偏移和在内存中的偏移不同,需要进行转换。
文件被映射到内存中时,MS-DOS头部、PE文件头和块表的偏移位置和大小没有变化,而各区块被映射到内存后偏移位置发生变化
输入表
可执行文件使用来自其他DLL的代码或数据的动作称为输入。当PE文件被载入时,加载器的工作之一就是定位所有被输入的函数和数据,并让文件可以使用那些地址。这个过程通过PE文件的输入表Import Table完成。输入表中保存的是函数名和其驻留的DLL名等动态链接所需的信息。
输入函数的调用
输入函数被程序调用,但执行代码不在程序中。这些函数的代码位于相关的DLL文件中,在程序中只保留函数信息如函数名、DLL文件名等。磁盘上的PE文件无法得知这些输入函数在内存中的地址,只有当载入内存后,加载器才将相关DLL载入,并将函数地址和调用的指令联系起来