PE文件

PE文件的全称是Portable Executable,意为可移植的可执行的文件,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件

PE文件是指32位可执行文件,也称为PE32。64位的可执行文件称为PE+或PE32+,是PE(PE32)的一种扩展形式

而不是PE64

结构

网上找了个图

image

PE文件结构分为五个部分:

  • DOS文件头
  • DOS加载模块
  • PE文件头
  • 区段表
  • 区段

Dos头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
定义

typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

我们只需要关注两个域:

e_magic:一个WORD类型,值是一个常数0x4D5A,用文本编辑器查看该值位‘MZ’,可执行文件必须都是’MZ’开头。

e_lfanew:为32位可执行文件扩展的域,用来表示DOS头之后的NT头相对文件起始地址的偏移。

Dos头的特征是4D5A也就是MZ开头。这里面比较有用的信息是最后两个字节(Dos头只有64字节)。

img

PE头

1
2
3
4
5
6
7
定义
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

PE文件头分为三个结构,第一个为签名字段,第二个为文件头字段,第三个为可选头字段。

Signature:在一个PE文件中Signature字段被设置为4550h,ASCII码为”PE00“。

IMAGE_FILE_HEADER是PE文件头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
typedefstruct_IMAGE_OPTIONAL_HEADER
{
WORD Magic;// 标志字, ROM 映像(0107h),普通可执行文件(010Bh)
BYTE MajorLinkerVersion;// 链接程序的主版本号
BYTE MinorLinkerVersion;// 链接程序的次版本号
DWORD SizeOfCode;// 所有含代码的节的总大小
DWORD SizeOfInitializedData;// 所有含已初始化数据的节的总大小
DWORD SizeOfUninitializedData;// 所有含未初始化数据的节的大小
DWORD AddressOfEntryPoint;// 程序执行入口RVA
DWORD BaseOfCode;// 代码的区块的起始RVA
DWORD BaseOfData;// 数据的区块的起始RVA
//
// NT additional fields. 以下是属于NT结构增加的领域。
//
DWORD ImageBase;// 程序的首选装载地址
DWORD SectionAlignment;// 内存中的区块的对齐大小
DWORD FileAlignment;// 文件中的区块的对齐大小
WORD MajorOperatingSystemVersion;// 要求操作系统最低版本号的主版本号
WORD MinorOperatingSystemVersion;// 要求操作系统最低版本号的副版本号
WORD MajorImageVersion;// 可运行于操作系统的主版本号
WORD MinorImageVersion;// 可运行于操作系统的次版本号
WORD MajorSubsystemVersion;// 要求最低子系统版本的主版本号
WORD MinorSubsystemVersion;// 要求最低子系统版本的次版本号
DWORD Win32VersionValue;// 莫须有字段,不被病毒利用的话一般为0
DWORD SizeOfImage;// 映像装入内存后的总尺寸
DWORD SizeOfHeaders;// 所有头 + 区块表的尺寸大小
DWORD CheckSum;// 映像的校检和
WORD Subsystem;// 可执行文件期望的子系统
WORD DllCharacteristics;// DllMain()函数何时被调用,默认为 0
DWORD SizeOfStackReserve;// 初始化时的栈大小
DWORD SizeOfStackCommit;// 初始化时实际提交的栈大小
DWORD SizeOfHeapReserve;// 初始化时保留的堆大小
DWORD SizeOfHeapCommit;// 初始化时实际提交的堆大小
DWORD LoaderFlags;// 与调试有关,默认为 0
DWORD NumberOfRvaAndSizes;// 下边数据目录的项数,这个字段自Windows NT 发布以来一直是16
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
// 数据目录表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

区段表

区段表分为.text表、.data表、.rsrc表,区段表相当于区段的目录,里面包含着每个区段的信息,如区段名称、区段大小、区段基地址、区段偏移地址等。

img

.text:代码段,是在编译或汇编结束时产生的一种块,它的内容全部是指令代码。也有的编译器将该段命名为.code
.data:初始化的数据块,是初始化的数据块,包含那些编译时被初始化的变量、字符串
.idata:输入表,包含其他外来dll的函数和数据信息,也就是输入表,也有人称之为导入表。
.rsrc:资源数据块,包含模块的全部资源数据,如图标、菜单、位图等。
.reloc:重定位表,用于保存基址的重定位表。即当装在程序不能按照连接器所指定的地址装载文件是,需要对指令或已经初始化的变量进行调整,该块中也包含了调整过程中所需要的一些数据,如果装载能够正常装在则忽略此段中的数据。
.edata:导出表,是pe文件的输出表,以供其他模块使用,并不是每个pe文件都有此数据段,因为有的文件并不需要输出一些函数,该数据段常见于动态连接库文件中。
.radata:存放调试目录、说明字符串,该数据块并不常见主要是用于存放一些调试信息。

区段