PE32格式学习之手动创建一个简单的Windows程序
字数 3480 2025-08-05 11:39:43

PE32格式学习之手动创建一个简单的Windows程序

一、项目目标

手动创建一个简单的PE.exe程序,功能是弹出一个对话框。

二、基础知识

1. 进程虚拟地址空间

Windows运行程序时会创建一个"进程"容器,程序被加载到"虚拟内存地址空间"而非直接物理内存。32位进程有4GB虚拟内存空间。

2. 内存页和物理内存映射

  • 虚拟内存和物理内存被"裁减"成4KB大小的内存片
  • 只有需要使用的虚拟内存片才会映射到物理内存片
  • 系统DLL的物理内存片可映射到所有进程的虚拟内存空间

3. PE32可执行文件基本结构

典型exe程序包含:

  • 文件头:PE32文件头是多个结构的组合
  • .code节:包含用户代码编译后的二进制指令流
  • .data节:包含预初始化数据
  • .idata节:包含导入表

4. 从磁盘File到内存Image

  • 程序加载时有"拉伸"过程
  • 磁盘对齐:512字节(传统磁盘扇区大小)
  • 内存对齐:4096字节(内存页大小)
  • FOA(File Offset Address):字段在文件中相对文件起始位置的偏移量
  • RVA(Relative Virtual Address):字段在内存Image中相对Image起始位置的偏移量

三、创建PE32可执行文件框架

创建2048字节(512*4)的全0二进制文件,保存为PE.exe,分为:

  1. 文件头(512字节)
  2. .code节(512字节)
  3. .data节(512字节)
  4. .idata节(512字节)

四、编辑PE.exe文件头

1. DOS Header

FOA RVA Size Field Value Description
0 0 2 e_magic 0x5A4D DOS头标志 'MZ'
0x3C 0x3C 4 e_lfanew 0x80 指向PE Header头部的偏移量

2. PE File Header

FOA RVA Size Field Value Description
0x80 0x80 4 PE Signature 0x4550 PE头标志 'PE\x00\x00'
0x84 0x84 2 Machine 0x014C Intel 386或兼容处理器
0x86 0x86 2 NumberOfSections 3 3个节:.code、.data、.idata
0x94 0x94 2 SizeOfOptionalHeader 0xE0 PE32的OptionalHeader大小
0x96 0x96 2 Characteristics 0x0102 32位可执行文件

3. PE Optional Header

FOA RVA Size Field Value Description
0x98 0x98 2 Magic 0x010B PE32文件标识
0x9C 0x9C 4 SizeOfCode 0x1000 .code节内存大小
0xA8 0xA8 4 AddressOfEntryPoint 0x1000 程序入口点RVA
0xAC 0xAC 4 BaseOfCode 0x1000 .code节起始RVA
0xB0 0xB0 4 BaseOfData 0x2000 .data节起始RVA
0xB4 0xB4 4 ImageBase 0x400000 加载起始位置
0xB8 0xB8 4 SectionAlignment 0x1000 内存对齐边界
0xBC 0xBC 4 FileAlignment 0x200 文件对齐边界
0xD0 0xD0 4 SizeOfImage 0x4000 内存Image大小
0xDC 0xDC 2 Subsystem 2 Windows图形界面子系统
0x100 0x100 4 Import Table RVA 0x3000 导入表结构RVA
0x104 0x104 4 Import Table Size 20 导入表结构大小

4. Section Table (Section Headers)

FOA RVA Size Field Value Description
0x178 0x178 8 Name ".code" 节名称
0x180 0x180 4 VirtualSize 0x1000 内存大小
0x184 0x184 4 VirtualAddress 0x1000 内存起始RVA
0x188 0x188 4 SizeOfRawData 0x200 文件大小
0x18C 0x18C 4 PointerToRawData 0x200 文件起始FOA
0x19C 0x19C 4 Characteristics 0x60000020 可执行代码节

五、编辑.idata节

1. 导入表结构

导入表包含三个关键数据结构:

  1. Directory Entry (Import Descriptor) - 描述需要导入的DLL
  2. Import Lookup Entry - 描述DLL中需要导入的函数
  3. Hint/Name Entry - 包含函数名信息

2. 手动构造导入表

FOA RVA Size Field Value Description
0x600 0x3000 4 Import Lookup Table RVA 0x3028 导入查询表位置
0x60C 0x300C 4 DLL Name RVA 0x3030 DLL名字符串位置
0x610 0x3010 4 Import Address Table RVA 0x3028 导入地址表位置
0x628 0x3028 4 Import Lookup Table 0x303B 指向函数名Entry
0x630 0x3030 11 DLL Name "user32.dll\0" DLL名称
0x63D 0x303D 12 Function Name "MessageBoxA\0" 函数名称

六、编辑.data节

FOA RVA Value Description
0x400 0x2000 "Hello, snake!\0" 对话框标题
0x40E 0x200E "This is an example..." 消息内容

七、编辑.code节

MessageBoxA函数调用汇编代码:

push 0x40
push 0x402000
push 0x40200E
push 0
call DWORD ptr ds:0x403028
ret

对应二进制字节流:

{ 0x6A, 0x40, 0x68, 0x00, 0x20, 0x40, 0x00, 0x68, 0x0E, 0x20, 0x40, 0x00, 0x6A, 0x00, 0xFF, 0x15, 0x28, 0x30, 0x40, 0x00, 0xC3 }

写入.code节起始位置0x200处。

八、验证

完成编辑后执行PE.exe,应能弹出指定对话框。

PE32格式学习之手动创建一个简单的Windows程序 一、项目目标 手动创建一个简单的PE.exe程序,功能是弹出一个对话框。 二、基础知识 1. 进程虚拟地址空间 Windows运行程序时会创建一个"进程"容器,程序被加载到"虚拟内存地址空间"而非直接物理内存。32位进程有4GB虚拟内存空间。 2. 内存页和物理内存映射 虚拟内存和物理内存被"裁减"成4KB大小的内存片 只有需要使用的虚拟内存片才会映射到物理内存片 系统DLL的物理内存片可映射到所有进程的虚拟内存空间 3. PE32可执行文件基本结构 典型exe程序包含: 文件头:PE32文件头是多个结构的组合 .code节:包含用户代码编译后的二进制指令流 .data节:包含预初始化数据 .idata节:包含导入表 4. 从磁盘File到内存Image 程序加载时有"拉伸"过程 磁盘对齐:512字节(传统磁盘扇区大小) 内存对齐:4096字节(内存页大小) FOA(File Offset Address):字段在文件中相对文件起始位置的偏移量 RVA(Relative Virtual Address):字段在内存Image中相对Image起始位置的偏移量 三、创建PE32可执行文件框架 创建2048字节(512* 4)的全0二进制文件,保存为PE.exe,分为: 文件头(512字节) .code节(512字节) .data节(512字节) .idata节(512字节) 四、编辑PE.exe文件头 1. DOS Header | FOA | RVA | Size | Field | Value | Description | |-----|-----|------|-------|-------|-------------| | 0 | 0 | 2 | e_ magic | 0x5A4D | DOS头标志 'MZ' | | 0x3C | 0x3C | 4 | e_ lfanew | 0x80 | 指向PE Header头部的偏移量 | 2. PE File Header | FOA | RVA | Size | Field | Value | Description | |-----|-----|------|-------|-------|-------------| | 0x80 | 0x80 | 4 | PE Signature | 0x4550 | PE头标志 'PE\x00\x00' | | 0x84 | 0x84 | 2 | Machine | 0x014C | Intel 386或兼容处理器 | | 0x86 | 0x86 | 2 | NumberOfSections | 3 | 3个节:.code、.data、.idata | | 0x94 | 0x94 | 2 | SizeOfOptionalHeader | 0xE0 | PE32的OptionalHeader大小 | | 0x96 | 0x96 | 2 | Characteristics | 0x0102 | 32位可执行文件 | 3. PE Optional Header | FOA | RVA | Size | Field | Value | Description | |-----|-----|------|-------|-------|-------------| | 0x98 | 0x98 | 2 | Magic | 0x010B | PE32文件标识 | | 0x9C | 0x9C | 4 | SizeOfCode | 0x1000 | .code节内存大小 | | 0xA8 | 0xA8 | 4 | AddressOfEntryPoint | 0x1000 | 程序入口点RVA | | 0xAC | 0xAC | 4 | BaseOfCode | 0x1000 | .code节起始RVA | | 0xB0 | 0xB0 | 4 | BaseOfData | 0x2000 | .data节起始RVA | | 0xB4 | 0xB4 | 4 | ImageBase | 0x400000 | 加载起始位置 | | 0xB8 | 0xB8 | 4 | SectionAlignment | 0x1000 | 内存对齐边界 | | 0xBC | 0xBC | 4 | FileAlignment | 0x200 | 文件对齐边界 | | 0xD0 | 0xD0 | 4 | SizeOfImage | 0x4000 | 内存Image大小 | | 0xDC | 0xDC | 2 | Subsystem | 2 | Windows图形界面子系统 | | 0x100 | 0x100 | 4 | Import Table RVA | 0x3000 | 导入表结构RVA | | 0x104 | 0x104 | 4 | Import Table Size | 20 | 导入表结构大小 | 4. Section Table (Section Headers) | FOA | RVA | Size | Field | Value | Description | |-----|-----|------|-------|-------|-------------| | 0x178 | 0x178 | 8 | Name | ".code" | 节名称 | | 0x180 | 0x180 | 4 | VirtualSize | 0x1000 | 内存大小 | | 0x184 | 0x184 | 4 | VirtualAddress | 0x1000 | 内存起始RVA | | 0x188 | 0x188 | 4 | SizeOfRawData | 0x200 | 文件大小 | | 0x18C | 0x18C | 4 | PointerToRawData | 0x200 | 文件起始FOA | | 0x19C | 0x19C | 4 | Characteristics | 0x60000020 | 可执行代码节 | 五、编辑.idata节 1. 导入表结构 导入表包含三个关键数据结构: Directory Entry (Import Descriptor) - 描述需要导入的DLL Import Lookup Entry - 描述DLL中需要导入的函数 Hint/Name Entry - 包含函数名信息 2. 手动构造导入表 | FOA | RVA | Size | Field | Value | Description | |-----|-----|------|-------|-------|-------------| | 0x600 | 0x3000 | 4 | Import Lookup Table RVA | 0x3028 | 导入查询表位置 | | 0x60C | 0x300C | 4 | DLL Name RVA | 0x3030 | DLL名字符串位置 | | 0x610 | 0x3010 | 4 | Import Address Table RVA | 0x3028 | 导入地址表位置 | | 0x628 | 0x3028 | 4 | Import Lookup Table | 0x303B | 指向函数名Entry | | 0x630 | 0x3030 | 11 | DLL Name | "user32.dll\0" | DLL名称 | | 0x63D | 0x303D | 12 | Function Name | "MessageBoxA\0" | 函数名称 | 六、编辑.data节 | FOA | RVA | Value | Description | |-----|-----|-------|-------------| | 0x400 | 0x2000 | "Hello, snake !\0" | 对话框标题 | | 0x40E | 0x200E | "This is an example..." | 消息内容 | 七、编辑.code节 MessageBoxA函数调用汇编代码: 对应二进制字节流: 写入.code节起始位置0x200处。 八、验证 完成编辑后执行PE.exe,应能弹出指定对话框。