可移植可执行
- 是一种用于可执行文件、目标文件和动态链接库的文件格式,主要使用在32位和64位的Windows操作系统上。
可移植的
是指该文件格式的通用性,可用于许多种不同的操作系统和体系结构中。PE文件格式封装了Windows操作系统加载可执行程序代码时所必需的一些信息。这些信息包括动态链接库、API导入和导出表、资源管理数据和线程局部存储数据。在Windows NT操作系统中,PE文件格式主要用于EXE文件、DLL文件、.sys(驱动程序)和其他文件类型。可扩展固件接口(EFI)技术规范书中说明PE格式是EFI环境中的标准可执行文件格式。开头为DOS头部。 - PE格式是由Unix中的COFF格式修改而来的。在Windows开发环境中,PE格式也称为PE/COFF格式。
DOS头 winnt.h
结构体信息
1 | typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header |
重要字段说明
- e_magic: 4D5A
该字段标识是否是一个PE文件以 - e_lfanew: –
指向 NT头(IMAGE_NT_HEADER) 起始地址
NT头
NT在64和32结构没有发生变法, 只是字段类型长度有变
结构体信息
x86
1 | typedef struct _IMAGE_NT_HEADERS { |
x64
1 | typedef struct _IMAGE_NT_HEADERS64 { |
重要字段说明
- Signature: 5045
4字节PE标识
文件头
结构体信息
1 | typedef struct _IMAGE_FILE_HEADER { |
重要字段说明
NumberOfSections
节数量SizeOfOptionalHeader
可选PE头的大小Characteristics
属性信息
可选PE头
结构体信息
x86
1 | typedef struct _IMAGE_OPTIONAL_HEADER { |
x64
1 | typedef struct _IMAGE_OPTIONAL_HEADER64 { |
重要字段说明
Magic:
- IMAGE_NT_OPTIONAL_HDR32_MAGIC: 0x10b
32位可执行文件 - IMAGE_NT_OPTIONAL_HDR64_MAGIC: 0x20b
64位可执行文件 - IMAGE_ROM_OPTIONAL_HDR_MAGIC: 0x107
ROM文件
- IMAGE_NT_OPTIONAL_HDR32_MAGIC: 0x10b
AddressOfEntryPoint:
存储入口函数偏移, 该值只是一个偏移, 真实地址需要加上 ImageBaseImageBase:
- 在内存中加载的首地址, 该值是
64K
字节的倍数 - DLL 的默认值为 0x10000000。应用程序的默认值为0x00400000,但0x00010000的 Windows CE 除外
- 在内存中加载的首地址, 该值是
SectionAlignment:
- 在内存中加载后节表应该按多少字节对齐
FileAlignment:
- 在文件中应该按多少字节对齐, 默认按
512K
对齐
- 在文件中应该按多少字节对齐, 默认按
SizeOfImage:
- 加载到内存中的总大小
SizeOfHeaders:
- PE头总大小, 包括节表
节表
节表中定义了相关数据开始与结束信息 (如: 代码段, 数据段, …), 它很重要,但节表中的值都是RAV 也就是拉伸后的偏移地址
结构体信息
1 | typedef struct _IMAGE_SECTION_HEADER { |
重要字段说明
- Name:
节表名称, 注意它不会以0结尾, 所以在获取节表名称时需要注意 - Misc:
它在内存中加载后的大小 - VirtualAddress:
内存中的偏移(RVA)
- SizeOfRawData:
节区数据大小,Misc
中的数据可能比它大 - PointerToRawData:
文件偏移地址(FOV)
目录表
目录表有16个, 分别为:
- IMAGE_DIRECTORY_ENTRY_ARCHITECTURE: 7
Architecture-specific data - IMAGE_DIRECTORY_ENTRY_BASERELOC: 5
Base relocation table - IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: 11
Bound import directory - IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR: 14
COM descriptor table - IMAGE_DIRECTORY_ENTRY_DEBUG: 6
Debug directory - IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: 13
Delay import table - IMAGE_DIRECTORY_ENTRY_EXCEPTION: 3
Exception directory - IMAGE_DIRECTORY_ENTRY_EXPORT: 0
Export directory - IMAGE_DIRECTORY_ENTRY_GLOBALPTR: 8
The relative virtual address of global pointer - IMAGE_DIRECTORY_ENTRY_IAT: 12
Import address table - IMAGE_DIRECTORY_ENTRY_IMPORT: 1
Import directory - IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: 10
Load configuration directory - IMAGE_DIRECTORY_ENTRY_RESOURCE: 2
Resource directory - IMAGE_DIRECTORY_ENTRY_SECURITY: 4
Security directory - IMAGE_DIRECTORY_ENTRY_TLS: 9
Thread local storage directory
结构体信息
1 | typedef struct _IMAGE_DATA_DIRECTORY { |
重要字段信息说明
- VirtualAddress:
指向各表偏移(RVA)
目标表相关结构体
这里只列出 导出表, 导入表, 重定位表
导出表
导出表比较复杂, 需要理解导出方式与查找函数首地址
结构体信息
1 | typedef struct _IMAGE_EXPORT_DIRECTORY { |
重要字段说明
- Name:
导出文件名称偏移地址(RVA)
- Base:
需要开始 - NumberOfFunctions:
导出函数数量 - NumberOfNames:
以名称导出的函数数量 - AddressOfFunctions:
导出的函数存放首地址偏移(RVA)
- AddressOfNames:
以名称导出的函数存放首地址偏移(RVA)
- AddressOfNameOrdinals:
以名称导出的函数指向的序号地址偏移(RVA)
打印32位
导出表信息列子
1 | #include <Windows.h> |
导入表
导入表也是比较复杂的, 需要理解导入方式
相关结构体信息
导入表
1 | typedef struct _IMAGE_IMPORT_DESCRIPTOR { |
名称表
1 | typedef struct _IMAGE_IMPORT_BY_NAME { |
导入名称表与导入地址表
1 | typedef struct _IMAGE_THUNK_DATA32 { |
重要字段说明
导入表
- DUMMYUNIONNAME:
指向导入名称表的偏移(RVA)
- Name:
导入动态库的名称 - FirstThunk:
指向导入地址表的偏移(RVA)
, 导入地址表在文件中是和导入名称表一样的, 但加载到内存后是不一样的
- DUMMYUNIONNAME:
导入名称表与导入地址表
- u1:
指向的是函数名称的偏移或者序号,
如果是函数明显那么最高位为0
否则为序号
序号 = ul.Ordinal & ~(0b1 << 31)
- u1:
名称表
- Name
函数名称
- Name
打印32位
导入表列子
1 | #include <Windows.h> |
重定位表
结构体信息
1 | typedef struct _IMAGE_BASE_RELOCATION { |
重要字段说明
- VirtualAddress:
偏移基址 - SizeOfBlock:
当前表大小
打印32位
重定位表列子
1 | #include <Windows.h> |