Windows PE
- 可执行文件(Executable file)指的是可以由操作系统进行加载执行的文件
- 可执行文件的格式:
- Windows 平台
PE(Portable Executable)文件结构 - Linux 平台
ELF(Executable and Linking Format)文件结构
- Windows 平台
PE 文件结构
PE 文件的整体结构
PE 文件主要结构
结构体 | 宽度(字节) |
---|---|
IMAGE_DOS_HEADER | 64 |
IMAGE_FILE_HEADER | 20 |
INAGE_OPTIONAL_HEADER32 | 244 |
IMAGE_SECTION_HEADER | 40 |
PE文件的两种状态
PE文件的两种状态
- PE文件在运行前(静态,存储在磁盘上)和运行时(动态,运行在内存中)的格式是有差异的,这种差异对于我们理解PE文件是如何执行的来说很重要。
- 我们在之前的文件分析过程中实际上所看到的是静态的内容,其大小是要根据FileAlignment的值进行文件对齐的,但是在运行时则整体按照扩展PE头的成员SectionAlignment的值进行内存对齐,默认情况下该值为0x1000:
DOS头结构体
1 | typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header |
e_magic
为DOS可执行文件标识符,占用2字节,值为0x5A4D
e_lfanew
保存了PE头的起始位置
标准PE头
1 | typedef struct _IMAGE_NT_HEADERS { |
_IMAGE_NT_HEADERS
Signature
: PE标识, 值为0x00004550
FileHeader
: 文件头OptionalHeader
: 扩展头
文件头(标准PE头)
1 | typedef struct _IMAGE_FILE_HEADER { |
_IMAGE_FILE_HEADER
Machine
: 该字段为WORD类型,占用2字节,该字段标识可执行文件的目标CPU类型NumberOfSections
: 该字段为WORD类型,占用2字节,该字段标识PE文件的节区个数TimeDateStamp
: 该字段表示编译器填写的时间戳与文件属性中(创建时间、修改时间无关),这个值是自1970年1月1日以来用格林威治时间计算的秒数PointerToSymbolTable
: 该字段很少使用,调试相关NumberOfSymbols
: 该字段很少使用,调试相关SizeOfOptionalHeader
: 该字段为WORD类型,占用2字节;该字段指定IMAGE_OPTIONAL_HEADER
结构大小(32位PE文件:0xEO 64位PE文件:0xF0)Characteristics
: 该字段为WORD,占用2字节;该字段指定文件属性
Machine
字段取值范围
宏定义 | 值 | 意义 |
---|---|---|
IMAGE_FILE_MACHINE_I386 | 0x014C | Intel |
IMAGE_FILE_MACHINE_ALPHA | 0x0184 | DEC Alpha |
IMAGE_FILE_MACHINE_IA64 | 0x200 | Intel(64-bit) |
IMAGE_FILE_MACHINE_AXP64 | 0x0284 | DEC Alpha(64-bit) |
IMAGE_FILE_MACHINE_AMD64 | 0x8664 | AMD64 (K8) |
Characteristics
字段取值范围
数据位 | 宏定义 | 值 | 为1时的含义 |
---|---|---|---|
0 | IMAGE_FILE_RELOCS_STRIPPED | 0x0001 | 文件中不存在重定位信息 |
1 | IMAGE_FILE_EXECUTABLE_IMAGE | 0x0002 | 文件是可执行的 |
2 | IMAGE_FILE_LINE_NUMS_STRIPPED | 0x0004 | 不存在行信息 |
3 | IMAGE_FILE_LOCAL_SYMS_STRIPPED | 0x0008 | 不存在符号信息 |
4 | IMAGE_FILE_AGGRESIVE_WS_TRIM | 0x0010 | 调整工作集 |
5 | IMAGE_FILE_LARGE_ADDRESS_AWARE | 0x0020 | 应用程序可处理大于2GB的地址 |
6 | 此标志位保留 | ||
7 | IMAGE_FILE_BYTES_REVERSED_LO | 0x0080 | 小尾方式 |
8 | IMAGE_FILE_32BIT_MACHINE | 0x0100 | 只在32平台运行 |
9 | IMAGE_FILE_DEBUG_STRIPPED | 0x0200 | 不包含调试信息 |
10 | IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP | 0x0400 | 不能从可移动盘运行 |
11 | IMAGE_FILE_NET_RUN_FROM_SWAP | 0x0800 | 不能从网络运行 |
12 | IMAGE_FILE_SYSTEM | 0x1000 | 系统文件(驱动程序),不能直接运行 |
13 | IMAGE_FILE_DLL | 0x2000 | DLL文件 |
14 | IMAGE_FILE_UP_SYSTEM_ONLY | 0x4000 | 文件不能在多处理器计算机上运行 |
15 | IMAGE_FILE_BYTES_REVERSED_HI | 0x8000 | 大尾方式 |
1 | 例如:0102 |
可选头(扩展头)
1 | typedef struct _IMAGE_OPTIONAL_HEADER { |
- 程序真正入口:
ImageBase
内存镜像基址 +AddressOfEntryPoint
程序入口
_IMAGE_SECTION_HEADER(节表)
1 | typedef struct _IMAGE_SECTION_HEADER { |
空白区域添加代码
- 构造跳转地址公式
要跳转的地址 - E8指令当前的地址 - 5
新增节
新增节的步骤
- 判断是否有足够的空间,可以添加一个节表
- 在节表中新增一个成员
- 修改PE头中节的数量
- 修改SizeOfImage的大小
- 在原有数据的最后,新增一个节的数据(内存对齐的整数倍)
- 修正新增节表的属性