PE文件解析器详细文档

文章目录

  • PE文件解析器详细文档
    • [1. 项目概述与目标](#1. 项目概述与目标)
    • [2. 技术栈与环境依赖](#2. 技术栈与环境依赖)
      • [2.1 技术栈](#2.1 技术栈)
      • [2.2 环境依赖](#2.2 环境依赖)
    • [3. PE文件结构详细分析](#3. PE文件结构详细分析)
      • [3.1 DOS头(IMAGE_DOS_HEADER)](#3.1 DOS头(IMAGE_DOS_HEADER))
      • [3.2 NT头(IMAGE_NT_HEADERS)](#3.2 NT头(IMAGE_NT_HEADERS))
      • [3.3 文件头(IMAGE_FILE_HEADER)](#3.3 文件头(IMAGE_FILE_HEADER))
      • [3.4 可选头(IMAGE_OPTIONAL_HEADER32)](#3.4 可选头(IMAGE_OPTIONAL_HEADER32))
      • [3.5 可选头(IMAGE_OPTIONAL_HEADER64)](#3.5 可选头(IMAGE_OPTIONAL_HEADER64))
      • [3.6 数据目录(IMAGE_DATA_DIRECTORY)](#3.6 数据目录(IMAGE_DATA_DIRECTORY))
      • [3.7 节区头(IMAGE_SECTION_HEADER)](#3.7 节区头(IMAGE_SECTION_HEADER))
      • [3.8 导入描述符(IMAGE_IMPORT_DESCRIPTOR)](#3.8 导入描述符(IMAGE_IMPORT_DESCRIPTOR))
      • [3.9 导出目录(IMAGE_EXPORT_DIRECTORY)](#3.9 导出目录(IMAGE_EXPORT_DIRECTORY))
      • [3.10 重定位块(IMAGE_BASE_RELOCATION)](#3.10 重定位块(IMAGE_BASE_RELOCATION))
      • [3.11 重定位条目](#3.11 重定位条目)
    • [4. 项目结构](#4. 项目结构)
    • [5. 核心类/函数说明](#5. 核心类/函数说明)
      • [5.1 PeParser 类](#5.1 PeParser 类)
        • [5.1.1 公共方法](#5.1.1 公共方法)
        • [5.1.2 私有方法](#5.1.2 私有方法)
      • [5.2 异常处理类](#5.2 异常处理类)
    • [6. 解析流程详解](#6. 解析流程详解)
      • [6.1 加载文件](#6.1 加载文件)
      • [6.2 解析DOS头](#6.2 解析DOS头)
      • [6.3 解析NT头](#6.3 解析NT头)
      • [6.4 解析节区表](#6.4 解析节区表)
      • [6.5 解析导入表](#6.5 解析导入表)
      • [6.6 解析导出表](#6.6 解析导出表)
      • [6.7 解析重定位表](#6.7 解析重定位表)
    • [7. 关键算法解析](#7. 关键算法解析)
      • [7.1 RVA到文件偏移量的转换](#7.1 RVA到文件偏移量的转换)
      • [7.2 导入表解析](#7.2 导入表解析)
      • [7.3 导出表解析](#7.3 导出表解析)
    • [8. 示例代码与使用说明](#8. 示例代码与使用说明)
      • [8.1 基本使用示例](#8.1 基本使用示例)
      • [8.2 命令行工具使用](#8.2 命令行工具使用)
    • [9. 异常处理机制](#9. 异常处理机制)
    • [10. 性能优化策略](#10. 性能优化策略)
    • [11. 代码风格与规范](#11. 代码风格与规范)
    • [12. 未来改进方向](#12. 未来改进方向)
    • [13. 结论](#13. 结论)
    • error_handling.h
    • pe_parser.h
    • pe_structures.h
    • pe_parser.cpp
    • main.cpp

PE文件解析器详细文档

1. 项目概述与目标

本项目旨在开发一个全面、高效的PE(Portable Executable)文件解析器,使用现代C++20标准实现。PE文件是Windows操作系统上的可执行文件格式,包括.exe、.dll、.sys等文件类型。

项目目标:

  • 全面解析PE文件的各个组成部分,包括DOS头、NT头、节表、导入表、导出表、重定位表等

  • 提供清晰、结构化的代码架构,便于维护和扩展

  • 实现详细的错误处理机制,确保解析过程的稳定性

  • 提供友好的API接口,方便其他项目集成使用

  • 支持PE32(32位)和PE32+(64位)文件格式

2. 技术栈与环境依赖

2.1 技术栈

  • 编程语言: C++20

  • 构建系统: CMake

  • 标准库: STL(Standard Template Library)

  • 编译器: 支持C++20的编译器(如MSVC、GCC、Clang)

2.2 环境依赖

  • 操作系统: Windows(主要测试平台)

  • CMake版本: 3.16或更高

  • C++编译器: 支持C++20标准

3. PE文件结构详细分析

3.1 DOS头(IMAGE_DOS_HEADER)

字段名称 偏移量 大小(字节) 功能描述
e_magic 0x00 2 DOS魔术字,值为0x5A4D("MZ")
e_cblp 0x02 2 最后一页的字节数
e_cp 0x04 2 文件的页数
e_crlc 0x06 2 重定位项的数量
e_cparhdr 0x08 2 DOS头的段落数
e_minalloc 0x0A 2 程序需要的最小额外段落数
e_maxalloc 0x0C 2 程序需要的最大额外段落数
e_ss 0x0E 2 初始堆栈段值
e_sp 0x10 2 初始堆栈指针值
e_csum 0x12 2 校验和
e_ip 0x14 2 初始指令指针值
e_cs 0x16 2 初始代码段值
e_lfarlc 0x18 2 重定位表的文件偏移量
e_ovno 0x1A 2 覆盖号
e_res[4] 0x1C 8 保留字
e_oemid 0x24 2 OEM标识符
e_oeminfo 0x26 2 OEM信息
e_res2[10] 0x28 20 保留字
e_lfanew 0x3C 4 NT头的文件偏移量

3.2 NT头(IMAGE_NT_HEADERS)

字段名称 偏移量 大小(字节) 功能描述
Signature 0x00 4 NT签名,值为0x00004550("PE")

3.3 文件头(IMAGE_FILE_HEADER)

字段名称 偏移量 大小(字节) 功能描述
Machine 0x04 2 目标机器类型
NumberOfSections 0x06 2 节区数量
TimeDateStamp 0x08 4 时间戳
PointerToSymbolTable 0x0C 4 符号表的文件偏移量
NumberOfSymbols 0x10 4 符号数量
SizeOfOptionalHeader 0x14 2 可选头的大小
Characteristics 0x16 2 文件特性

3.4 可选头(IMAGE_OPTIONAL_HEADER32)

字段名称 偏移量 大小(字节) 功能描述
Magic 0x18 2 魔术字,PE32为0x10B
MajorLinkerVersion 0x1A 1 链接器主版本号
MinorLinkerVersion 0x1B 1 链接器次版本号
SizeOfCode 0x1C 4 代码节的大小
SizeOfInitializedData 0x20 4 初始化数据节的大小
SizeOfUninitializedData 0x24 4 未初始化数据节的大小
AddressOfEntryPoint 0x28 4 入口点地址
BaseOfCode 0x2C 4 代码节的基址
BaseOfData 0x30 4 数据节的基址
ImageBase 0x34 4 镜像加载基址
SectionAlignment 0x38 4 节区对齐值
FileAlignment 0x3C 4 文件对齐值
MajorOperatingSystemVersion 0x40 2 操作系统主版本号
MinorOperatingSystemVersion 0x42 2 操作系统次版本号
MajorImageVersion 0x44 2 镜像主版本号
MinorImageVersion 0x46 2 镜像次版本号
MajorSubsystemVersion 0x48 2 子系统主版本号
MinorSubsystemVersion 0x4A 2 子系统次版本号
Win32VersionValue 0x4C 4 Win32版本值
SizeOfImage 0x50 4 镜像大小
SizeOfHeaders 0x54 4 头部大小
CheckSum 0x58 4 校验和
Subsystem 0x5C 2 子系统类型
DllCharacteristics 0x5E 2 DLL特性
SizeOfStackReserve 0x60 4 堆栈保留大小
SizeOfStackCommit 0x64 4 堆栈提交大小
SizeOfHeapReserve 0x68 4 堆保留大小
SizeOfHeapCommit 0x6C 4 堆提交大小
LoaderFlags 0x70 4 加载器标志
NumberOfRvaAndSizes 0x74 4 数据目录数量
DataDirectory[16] 0x78 128 数据目录表

3.5 可选头(IMAGE_OPTIONAL_HEADER64)

字段名称 偏移量 大小(字节) 功能描述
Magic 0x18 2 魔术字,PE32+为0x20B
MajorLinkerVersion 0x1A 1 链接器主版本号
MinorLinkerVersion 0x1B 1 链接器次版本号
SizeOfCode 0x1C 4 代码节的大小
SizeOfInitializedData 0x20 4 初始化数据节的大小
SizeOfUninitializedData 0x24 4 未初始化数据节的大小
AddressOfEntryPoint 0x28 4 入口点地址
BaseOfCode 0x2C 4 代码节的基址
ImageBase 0x30 8 镜像加载基址(64位)
SectionAlignment 0x38 4 节区对齐值
FileAlignment 0x3C 4 文件对齐值
MajorOperatingSystemVersion 0x40 2 操作系统主版本号
MinorOperatingSystemVersion 0x42 2 操作系统次版本号
MajorImageVersion 0x44 2 镜像主版本号
MinorImageVersion 0x46 2 镜像次版本号
MajorSubsystemVersion 0x48 2 子系统主版本号
MinorSubsystemVersion 0x4A 2 子系统次版本号
Win32VersionValue 0x4C 4 Win32版本值
SizeOfImage 0x50 4 镜像大小
SizeOfHeaders 0x54 4 头部大小
CheckSum 0x58 4 校验和
Subsystem 0x5C 2 子系统类型
DllCharacteristics 0x5E 2 DLL特性
SizeOfStackReserve 0x60 8 堆栈保留大小(64位)
SizeOfStackCommit 0x68 8 堆栈提交大小(64位)
SizeOfHeapReserve 0x70 8 堆保留大小(64位)
SizeOfHeapCommit 0x78 8 堆提交大小(64位)
LoaderFlags 0x80 4 加载器标志
NumberOfRvaAndSizes 0x84 4 数据目录数量
DataDirectory[16] 0x88 128 数据目录表

3.6 数据目录(IMAGE_DATA_DIRECTORY)

字段名称 偏移量 大小(字节) 功能描述
VirtualAddress 0x00 4 虚拟地址
Size 0x04 4 大小

3.7 节区头(IMAGE_SECTION_HEADER)

字段名称 偏移量 大小(字节) 功能描述
Name 0x00 8 节区名称
Misc.VirtualSize 0x08 4 节区的虚拟大小
VirtualAddress 0x0C 4 节区的虚拟地址
SizeOfRawData 0x10 4 节区在文件中的大小
PointerToRawData 0x14 4 节区在文件中的偏移量
PointerToRelocations 0x18 4 重定位表的偏移量
PointerToLinenumbers 0x1C 4 行号表的偏移量
NumberOfRelocations 0x20 2 重定位项的数量
NumberOfLinenumbers 0x22 2 行号的数量
Characteristics 0x24 4 节区特性

3.8 导入描述符(IMAGE_IMPORT_DESCRIPTOR)

字段名称 偏移量 大小(字节) 功能描述
OriginalFirstThunk 0x00 4 原始导入名称表的RVA
TimeDateStamp 0x04 4 时间戳
ForwarderChain 0x08 4 前向链
Name 0x0C 4 DLL名称的RVA
FirstThunk 0x10 4 导入地址表的RVA

3.9 导出目录(IMAGE_EXPORT_DIRECTORY)

字段名称 偏移量 大小(字节) 功能描述
Characteristics 0x00 4 特性
TimeDateStamp 0x04 4 时间戳
MajorVersion 0x08 2 主版本号
MinorVersion 0x0A 2 次版本号
Name 0x0C 4 模块名称的RVA
Base 0x10 4 导出函数的基地址
NumberOfFunctions 0x14 4 导出函数的数量
NumberOfNames 0x18 4 导出名称的数量
AddressOfFunctions 0x1C 4 导出函数地址表的RVA
AddressOfNames 0x20 4 导出名称表的RVA
AddressOfNameOrdinals 0x24 4 导出序号表的RVA

3.10 重定位块(IMAGE_BASE_RELOCATION)

字段名称 偏移量 大小(字节) 功能描述
VirtualAddress 0x00 4 块的虚拟地址
SizeOfBlock 0x04 4 块的大小

3.11 重定位条目

字段名称 偏移量 大小(字节) 功能描述
Offset 0x00 2 偏移量(低12位)
Type 0x00 2 类型(高4位)

4. 项目结构

复制代码
parser_pe_file/
├── CMakeLists.txt          # 项目构建配置
├── src/
│   ├── pe_structures.h     # PE文件结构定义
│   ├── error_handling.h    # 异常处理类
│   ├── pe_parser.h         # PE解析器类定义
│   ├── pe_parser.cpp       # PE解析器实现
│   └── main.cpp            # 主程序
└── build/                  # 构建输出目录

5. 核心类/函数说明

5.1 PeParser 类

5.1.1 公共方法
方法名称 参数 返回值 功能描述
LoadFile std::string_view file_path void 加载PE文件到内存,验证文件大小
Parse void 解析PE文件的各个部分
IsLoaded bool 检查文件是否已加载
Is64Bit bool 检查是否为64位PE文件
GetResult const PeParseResult& 获取解析结果
ToString std::string 将解析结果转换为字符串
5.1.2 私有方法
方法名称 参数 返回值 功能描述
ParseDosHeader void 解析DOS头,填充DosHeaderInfo结构
ParseNtHeaders void 解析NT头、文件头和可选头
ParseSections void 解析节区表,填充SectionInfo结构
ParseImports void 解析导入表,填充ImportInfo结构
ParseExports void 解析导出表,填充ExportInfo结构
ParseRelocations void 解析重定位表,填充RelocationBlock结构
RvaToFileOffset std::uint32_t rva std::uint32_t 将RVA转换为文件偏移量
ReadString std::uint32_t offset std::string 从指定偏移量读取字符串
ValidateDosHeader void 验证DOS魔术字
ValidateNtSignature void 验证NT签名
GetPtr std::size_t offset const T* 获取指定偏移量的指针
ValidateOffset std::size_t offset, std::size_t size, std::string_view field_name void 验证偏移量是否有效

5.2 异常处理类

类名称 父类 功能描述
PeException std::runtime_error 基础异常类
FileException PeException 文件操作异常
InvalidPeException PeException 无效PE文件异常
ParseException PeException 解析异常
MemoryAccessException PeException 内存访问异常

6. 解析流程详解

6.1 加载文件

  1. 打开指定路径的文件

  2. 获取文件大小并验证是否足够大(至少包含DOS头)

  3. 将文件数据读取到内存中的std::vector<std::uint8_t>

6.2 解析DOS头

  1. 验证DOS头偏移量

  2. 读取DOS头结构

  3. 填充DosHeaderInfo结构

  4. 验证DOS魔术字

6.3 解析NT头

  1. 根据DOS头中的e_lfanew字段获取NT头偏移量

  2. 验证NT头偏移量

  3. 读取NT签名并验证

  4. 读取文件头并填充FileHeaderInfo结构

  5. 根据文件头中的SizeOfOptionalHeader字段确定可选头大小

  6. 读取可选头魔术字,确定是PE32还是PE32+

  7. 读取并填充相应的可选头信息

  8. 填充数据目录信息

6.4 解析节区表

  1. 计算节区表偏移量(NT头偏移量 + NT头大小 + 可选头大小)

  2. 验证节区表偏移量

  3. 循环解析每个节区头

  4. 填充SectionInfo结构

6.5 解析导入表

  1. 从数据目录中获取导入表的RVA和大小

  2. 将RVA转换为文件偏移量

  3. 循环解析导入描述符,直到遇到全0的描述符

  4. 对每个导入描述符,解析DLL名称和导入函数

  5. 填充ImportInfo结构

6.6 解析导出表

  1. 从数据目录中获取导出表的RVA和大小

  2. 将RVA转换为文件偏移量

  3. 解析导出目录结构

  4. 解析模块名称、导出函数名称等信息

  5. 填充ExportInfo结构

6.7 解析重定位表

  1. 从数据目录中获取重定位表的RVA和大小

  2. 将RVA转换为文件偏移量

  3. 循环解析重定位块,直到遇到大小为0的块

  4. 对每个重定位块,解析重定位条目

  5. 填充RelocationBlockRelocationEntry结构

7. 关键算法解析

7.1 RVA到文件偏移量的转换

算法步骤:

  1. 遍历所有节区

  2. 对于每个节区,检查RVA是否在该节区的虚拟地址范围内

  3. 如果找到对应的节区,计算文件偏移量:文件偏移量 = RVA - 节区虚拟地址 + 节区文件偏移量

  4. 验证计算得到的文件偏移量是否有效

  5. 返回计算得到的文件偏移量或0(如果未找到对应的节区)

实现代码:

复制代码
std::uint32_t PeParser::RvaToFileOffset(std::uint32_t rva) const {
  for (const auto& section : result_.sections) {
    if (rva >= section.virtual_address && 
        rva < section.virtual_address + section.virtual_size) {
      const auto file_offset = rva - section.virtual_address + section.pointer_to_raw_data;
      if (file_offset < file_data_.size()) {
        return static_cast<std::uint32_t>(file_offset);
      }
      break;
    }
  }
  return 0;
}

7.2 导入表解析

算法步骤:

  1. 从数据目录中获取导入表的RVA和大小

  2. 将RVA转换为文件偏移量

  3. 循环解析导入描述符,直到遇到全0的描述符

  4. 对每个导入描述符: a. 解析DLL名称 b. 解析导入函数(通过OriginalFirstThunk) c. 区分按名称导入和按序号导入

  5. 填充ImportInfo结构

7.3 导出表解析

算法步骤:

  1. 从数据目录中获取导出表的RVA和大小

  2. 将RVA转换为文件偏移量

  3. 解析导出目录结构

  4. 解析模块名称

  5. 解析导出函数名称表和序号表

  6. 填充ExportInfo结构

8. 示例代码与使用说明

8.1 基本使用示例

复制代码
#include "pe_parser.h"
#include <iostream>

int main() {
  try {
    // 创建解析器实例
    pe_parser::PeParser parser;
    
    // 加载PE文件
    parser.LoadFile("C:\\Windows\\System32\\notepad.exe");
    
    // 解析PE文件
    parser.Parse();
    
    // 获取解析结果
    const auto& result = parser.GetResult();
    
    // 输出基本信息
    std::cout << "PE File Type: " << (result.is_64bit ? "PE32+ (64-bit)" : "PE32 (32-bit)") << std::endl;
    std::cout << "Number of Sections: " << result.file_header.number_of_sections << std::endl;
    std::cout << "Entry Point: 0x" << std::hex << (result.is_64bit ? result.opt64.address_of_entry_point : result.opt32.address_of_entry_point) << std::endl;
    std::cout << "Image Base: 0x" << std::hex << (result.is_64bit ? result.opt64.image_base : result.opt32.image_base) << std::endl;
    
    // 输出所有导入的DLL
    std::cout << "\nImports:" << std::endl;
    for (const auto& import : result.imports) {
      std::cout << "  - " << import.dll_name << std::endl;
    }
    
  } catch (const std::exception& e) {
    // 捕获并处理异常
    std::cerr << "Error: " << e.what() << std::endl;
    return 1;
  }
  
  return 0;
}

8.2 命令行工具使用

编译后,可以通过命令行运行PE解析器:

复制代码
PEParser.exe <pe_file_path>

例如:

复制代码
PEParser.exe C:\Windows\System32\notepad.exe

9. 异常处理机制

本项目实现了完整的异常处理机制,主要包括以下异常类:

  1. PeException: 基础异常类,所有PE解析相关的异常都继承自此类

  2. FileException: 文件操作异常,如文件打开失败、读取失败等

  3. InvalidPeException: 无效PE文件异常,如DOS魔术字错误、NT签名错误等

  4. ParseException: 解析异常,如偏移量无效、数据格式错误等

  5. MemoryAccessException: 内存访问异常,如内存越界等

在解析过程中,当遇到错误时,会抛出相应的异常,调用者可以捕获这些异常并进行处理。

异常处理示例:

复制代码
try {
  // 解析操作
  parser.Parse();
} catch (const pe_parser::FileException& e) {
  std::cerr << "File error: " << e.what() << std::endl;
} catch (const pe_parser::InvalidPeException& e) {
  std::cerr << "Invalid PE file: " << e.what() << std::endl;
} catch (const pe_parser::ParseException& e) {
  std::cerr << "Parse error: " << e.what() << std::endl;
} catch (const pe_parser::MemoryAccessException& e) {
  std::cerr << "Memory access error: " << e.what() << std::endl;
} catch (const std::exception& e) {
  std::cerr << "Other error: " << e.what() << std::endl;
}

10. 性能优化策略

  1. 内存管理: 使用std::vector<std::uint8_t>存储文件数据,避免频繁的内存分配和释放

  2. 偏移量验证: 在访问内存前进行偏移量验证,避免内存越界

  3. 字符串处理: 限制字符串长度,避免读取过长的字符串导致性能问题

  4. 缓存机制: 解析结果存储在PeParseResult结构中,避免重复解析

  5. 边界检查: 对所有内存访问进行边界检查,确保解析过程的稳定性

  6. 算法优化: 优化RVA到文件偏移量的转换算法,减少遍历次数

  7. 异常处理: 合理使用异常处理,避免过度使用影响性能

11. 代码风格与规范

本项目遵循以下代码风格和规范:

  1. 命名规范: 使用驼峰命名法,类名首字母大写,函数和变量名首字母小写

  2. 注释规范: 每个类、函数、字段都有详细的注释,说明其功能和用法

  3. 异常处理: 使用异常处理机制处理错误,避免使用错误码

  4. 内存管理: 使用RAII原则管理内存,避免内存泄漏

  5. 代码组织: 代码结构清晰,逻辑分明,便于维护和扩展

  6. 常量定义: 使用constexpr定义常量,提高代码可读性和性能

  7. 类型安全: 使用强类型,避免使用void*等不安全的类型

12. 未来改进方向

  1. 支持更多PE文件特性: 如资源表、TLS表、异常表等

  2. 添加PE文件修改功能: 支持修改PE文件的各个部分

  3. 增加更多的分析工具: 如导入导出函数分析、节区分析、重定位分析等

  4. 优化解析性能: 进一步提高解析速度和内存使用效率

  5. 跨平台支持: 支持在Linux和macOS上解析PE文件

  6. 添加图形界面: 提供可视化的PE文件分析工具

  7. 支持更多文件格式: 如ELF、Mach-O等其他可执行文件格式

13. 结论

本PE文件解析器项目实现了全面、高效的PE文件解析功能,支持PE32和PE32+格式,提供了清晰的API接口和详细的错误处理机制。通过本项目,开发者可以深入了解PE文件的结构和解析原理,为Windows平台的应用开发和安全分析提供有力的工具。

本文档详细介绍了PE文件的各个结构和字段,以及解析器的实现细节,希望能帮助开发者更好地理解和使用本项目。

error_handling.h

cpp 复制代码
#pragma once

// 包含标准异常类型
#include <stdexcept>
#include <string>
#include <string_view>

// PE解析器命名空间
namespace pe_parser {

    // 基础异常类,继承自std::runtime_error
    // 表示PE文件解析过程中的所有异常
    class PeException : public std::runtime_error {
    public:
        // 构造函数,接受字符串参数
        // 参数: message - 异常消息
        explicit PeException(const std::string& message)
            : std::runtime_error(message) {}

        // 构造函数,接受C风格字符串参数
        // 参数: message - 异常消息
        explicit PeException(const char* message)
            : std::runtime_error(message) {}
    };

    // 文件异常类,继承自PeException
    // 表示文件操作相关的异常
    class FileException : public PeException {
    public:
        // 构造函数,接受文件路径、操作名称和异常消息
        // 参数: file_path - 文件路径
        //       operation - 操作名称
        //       message - 异常消息
        FileException(std::string_view file_path, std::string_view operation, std::string_view message)
            : PeException("文件错误: " + std::string(file_path) + ", 操作: " + std::string(operation) + ", 消息: " + std::string(message)) {
        }
    };

    // 无效PE文件异常类,继承自PeException
    // 表示无效的PE文件格式异常
    class InvalidPeException : public PeException {
    public:
        // 构造函数,接受异常消息和偏移量
        // 参数: message - 异常消息
        //       offset - 无效位置偏移
        InvalidPeException(std::string_view message, std::size_t offset)
            : PeException("无效的PE文件: " + std::string(message) + ", 偏移: 0x" + std::to_string(offset)) {
        }
    };

    // 解析异常类,继承自PeException
    // 表示解析过程中的异常
    class ParseException : public PeException {
    public:
        // 构造函数,接受解析器名称、字段名称和异常消息
        // 参数: parser_name - 解析器名称
        //       field_name - 字段名称
        //       message - 异常消息
        ParseException(std::string_view parser_name, std::string_view field_name, std::string_view message)
            : PeException(std::string(parser_name) + "错误: 字段 " + std::string(field_name) + ", 消息: " + std::string(message)) {
        }
    };

    // 内存访问异常类,继承自PeException
    // 表示内存访问相关的异常
    class MemoryAccessException : public PeException {
    public:
        // 构造函数,接受异常消息和偏移量
        // 参数: message - 异常消息
        //       offset - 内存访问偏移
        MemoryAccessException(std::string_view message, std::size_t offset)
            : PeException("内存访问错误: " + std::string(message) + ", 偏移: 0x" + std::to_string(offset)) {
        }
    };

}  // namespace pe_parser

pe_parser.h

cpp 复制代码
#pragma once

// 包含必要的头文件
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>

// 包含自定义头文件
#include "error_handling.h"
#include "pe_structures.h"

// PE解析器命名空间
namespace pe_parser {

    // 导入信息结构
    // 存储导入表信息
    struct ImportInfo {
        std::string dll_name;               // 导入的DLL名称
        std::vector<std::string> functions; // 导入的函数名称列表
    };

    // 导出信息结构
    // 存储导出表信息
    struct ExportInfo {
        std::string module_name;                 // 模块名称
        std::uint32_t base = 0;                  // 导出函数基址
        std::uint32_t number_of_functions = 0;   // 导出函数数量
        std::uint32_t number_of_names = 0;       // 导出函数名称数量
        std::vector<std::string> function_names; // 导出函数名称列表
    };

    // 重定位条目结构
    // 存储单个重定位条目
    struct RelocationEntry {
        std::uint16_t offset;  // 重定位偏移
        std::uint8_t type = 0; // 重定位类型
    };

    // 重定位块结构
    // 存储重定位块
    struct RelocationBlock {
        std::uint32_t virtual_address = 0;     // 块虚拟地址
        std::uint32_t size_of_block = 0;       // 块大小
        std::vector<RelocationEntry> entries;  // 重定位条目列表
    };

    // 节信息结构
    // 存储节信息
    struct SectionInfo {
        std::string name;                       // 节名称
        std::uint32_t virtual_size = 0;         // 节虚拟大小
        std::uint32_t virtual_address = 0;      // 节虚拟地址
        std::uint32_t size_of_raw_data = 0;     // 节在文件中的大小
        std::uint32_t pointer_to_raw_data = 0;  // 节在文件中的偏移
        std::uint32_t characteristics = 0;      // 节特征
    };

    // DOS头信息结构
    // 存储DOS头信息
    struct DosHeaderInfo {
        std::uint16_t magic = 0;                 // 魔数,通常为0x5A4D("MZ")
        std::uint16_t last_page_bytes = 0;       // 最后一页的字节数
        std::uint16_t pages = 0;                 // 文件页数
        std::uint16_t relocations = 0;           // 重定位项数
        std::uint16_t header_paragraphs = 0;     // 头部的段落数
        std::uint16_t min_extra_paragraphs = 0;  // 最小额外段落数
        std::uint16_t max_extra_paragraphs = 0;  // 最大额外段落数
        std::uint16_t stack_segment = 0;         // 堆栈段
        std::uint16_t stack_pointer = 0;         // 堆栈指针
        std::uint16_t checksum = 0;              // 校验和
        std::uint16_t instruction_pointer = 0;   // 指令指针
        std::uint16_t code_segment = 0;          // 代码段
        std::uint16_t relocation_offset = 0;     // 重定位表偏移
        std::uint16_t overlay_number = 0;        // 覆盖号
        std::uint16_t oem_id = 0;                // OEM标识符
        std::uint16_t oem_info = 0;              // OEM信息
        std::int32_t nt_header_offset = 0;       // NT头偏移量
    };

    // 文件头信息结构
    // 存储文件头信息
    struct FileHeaderInfo {
        std::uint16_t machine = 0;               // 目标计算机类型
        std::uint16_t number_of_sections = 0;    // 节的数量
        std::uint32_t time_date_stamp = 0;       // 时间戳
        std::uint32_t pointer_to_symbol_table = 0; // 符号表偏移
        std::uint32_t number_of_symbols = 0;     // 符号数量
        std::uint16_t size_of_optional_header = 0; // 可选头大小
        std::uint16_t characteristics = 0;       // 文件特征
    };

    // 数据目录信息结构
    // 存储数据目录信息
    struct DataDirectoryInfo {
        std::string name;                  // 数据目录名称
        std::uint32_t virtual_address = 0; // 数据目录虚拟地址
        std::uint32_t size = 0;            // 数据目录大小
    };

    // 32位可选头信息结构
    // 存储PE32可选头信息
    struct OptionalHeader32Info {
        std::uint16_t magic = 0;                           // 魔数,PE32为0x10B
        std::uint8_t major_linker_version = 0;             // 主链接器版本号
        std::uint8_t minor_linker_version = 0;             // 次链接器版本号
        std::uint32_t size_of_code = 0;                    // 代码大小
        std::uint32_t size_of_initialized_data = 0;        // 已初始化数据大小
        std::uint32_t size_of_uninitialized_data = 0;      // 未初始化数据大小
        std::uint32_t address_of_entry_point = 0;          // 入口点地址
        std::uint32_t base_of_code = 0;                    // 代码基址
        std::uint32_t base_of_data = 0;                    // 数据基址
        std::uint32_t image_base = 0;                      // 映像基址
        std::uint32_t section_alignment = 0;               // 节对齐
        std::uint32_t file_alignment = 0;                  // 文件对齐
        std::uint16_t major_operating_system_version = 0;  // 主操作系统版本号
        std::uint16_t minor_operating_system_version = 0;  // 次操作系统版本号
        std::uint16_t major_image_version = 0;             // 主映像版本号
        std::uint16_t minor_image_version = 0;             // 次映像版本号
        std::uint16_t major_subsystem_version = 0;         // 主子系统版本号
        std::uint16_t minor_subsystem_version = 0;         // 次子系统版本号
        std::uint32_t win32_version_value = 0;             // Win32版本值
        std::uint32_t size_of_image = 0;                   // 映像大小
        std::uint32_t size_of_headers = 0;                 // 头大小
        std::uint32_t check_sum = 0;                       // 校验和
        std::uint16_t subsystem = 0;                       // 子系统类型
        std::uint16_t dll_characteristics = 0;             // DLL特征
        std::uint32_t size_of_stack_reserve = 0;           // 堆栈保留大小
        std::uint32_t size_of_stack_commit = 0;            // 堆栈提交大小
        std::uint32_t size_of_heap_reserve = 0;            // 堆保留大小
        std::uint32_t size_of_heap_commit = 0;             // 堆提交大小
        std::uint32_t loader_flags = 0;                    // 加载器标志
        std::uint32_t number_of_rva_and_sizes = 0;         // 数据目录数量
        std::vector<DataDirectoryInfo> data_directories;   // 数据目录列表
    };

    // 64位可选头信息结构
    // 存储PE32+可选头信息
    struct OptionalHeader64Info {
        std::uint16_t magic = 0;                           // 魔数,PE32+为0x20B
        std::uint8_t major_linker_version = 0;             // 主链接器版本号
        std::uint8_t minor_linker_version = 0;             // 次链接器版本号
        std::uint32_t size_of_code = 0;                    // 代码大小
        std::uint32_t size_of_initialized_data = 0;        // 已初始化数据大小
        std::uint32_t size_of_uninitialized_data = 0;      // 未初始化数据大小
        std::uint32_t address_of_entry_point = 0;          // 入口点地址
        std::uint32_t base_of_code = 0;                    // 代码基址
        std::uint64_t image_base = 0;                      // 映像基址(64位)
        std::uint32_t section_alignment = 0;               // 节对齐
        std::uint32_t file_alignment = 0;                  // 文件对齐
        std::uint16_t major_operating_system_version = 0;  // 主操作系统版本号
        std::uint16_t minor_operating_system_version = 0;  // 次操作系统版本号
        std::uint16_t major_image_version = 0;             // 主映像版本号
        std::uint16_t minor_image_version = 0;             // 次映像版本号
        std::uint16_t major_subsystem_version = 0;         // 主子系统版本号
        std::uint16_t minor_subsystem_version = 0;         // 次子系统版本号
        std::uint32_t win32_version_value = 0;             // Win32版本值
        std::uint32_t size_of_image = 0;                   // 映像大小
        std::uint32_t size_of_headers = 0;                 // 头大小
        std::uint32_t check_sum = 0;                       // 校验和
        std::uint16_t subsystem = 0;                       // 子系统类型
        std::uint16_t dll_characteristics = 0;             // DLL特征
        std::uint64_t size_of_stack_reserve = 0;           // 堆栈保留大小(64位)
        std::uint64_t size_of_stack_commit = 0;            // 堆栈提交大小(64位)
        std::uint64_t size_of_heap_reserve = 0;            // 堆保留大小(64位)
        std::uint64_t size_of_heap_commit = 0;             // 堆提交大小(64位)
        std::uint32_t loader_flags = 0;                    // 加载器标志
        std::uint32_t number_of_rva_and_sizes = 0;         // 数据目录数量
        std::vector<DataDirectoryInfo> data_directories;   // 数据目录列表
    };

    // PE解析结果结构
    // 存储所有解析出的PE文件信息
    struct PeParseResult {
        DosHeaderInfo dos_header;            // DOS头信息
        std::uint32_t nt_signature = 0;      // NT签名
        FileHeaderInfo file_header;          // 文件头信息
        bool is_64bit = false;               // 是否为64位PE文件
        OptionalHeader32Info opt32;          // 32位可选头信息
        OptionalHeader64Info opt64;          // 64位可选头信息
        std::vector<SectionInfo> sections;   // 节信息列表
        std::vector<ImportInfo> imports;     // 导入信息列表
        ExportInfo exports;                  // 导出信息
        std::vector<RelocationBlock> relocations; // 重定位信息列表
    };

    // PE解析器类
    // 解析PE文件
    class PeParser {
    public:
        // 默认构造函数
        PeParser() = default;
        // 默认析构函数
        ~PeParser() = default;
        // 移动构造函数
        PeParser(PeParser&&) noexcept = default;
        // 移动赋值运算符
        auto operator=(PeParser&&) noexcept -> PeParser & = default;
        // 禁用拷贝构造函数
        PeParser(const PeParser&) = delete;
        // 禁用拷贝赋值运算符
        auto operator=(const PeParser&) -> PeParser & = delete;

        // 将PE文件加载到内存中
        // 参数: file_path - PE文件路径
        void LoadFile(std::string_view file_path);

        // 解析PE文件
        void Parse();

        // 检查文件是否已加载
        // 返回值: 已加载返回true,否则返回false
        [[nodiscard]] auto IsLoaded() const noexcept -> bool {
            return !file_data_.empty();
        }

        // 检查是否为64位PE文件
        // 返回值: 64位返回true,否则返回false
        [[nodiscard]] auto Is64Bit() const noexcept -> bool {
            return result_.is_64bit;
        }

        // 获取解析结果
        // 返回值: 解析结果的常量引用
        [[nodiscard]] auto GetResult() const -> const PeParseResult& {
            return result_;
        }

        // 将解析结果转换为字符串
        // 返回值: 解析结果的字符串表示
        [[nodiscard]] auto ToString() const -> std::string;

    private:
        std::vector<std::uint8_t> file_data_; // 文件数据存储
        PeParseResult result_;               // 解析结果存储

        // 解析DOS头
        void ParseDosHeader();
        // 解析NT头
        void ParseNtHeaders();
        // 解析节表
        void ParseSections();
        // 解析导入表
        void ParseImports();
        // 解析导出表
        void ParseExports();
        // 解析重定位表
        void ParseRelocations();

        // 模板函数:获取指定偏移处的指针
        // 参数: offset - 偏移量
        // 返回值: 指定类型的指针,无效时返回nullptr
        template <typename T>
        [[nodiscard]] auto GetPtr(std::size_t offset) const -> const T*;

        // 将RVA转换为文件偏移
        // 参数: rva - 相对虚拟地址
        // 返回值: 对应的文件偏移,无效时返回0
        [[nodiscard]] auto RvaToFileOffset(std::uint32_t rva) const -> std::uint32_t;

        // 从指定偏移处读取字符串
        // 参数: offset - 偏移量
        // 返回值: 读取的字符串
        [[nodiscard]] auto ReadString(std::uint32_t offset) const -> std::string;

        // 模板函数:验证偏移量
        // 参数: offset - 偏移量
        //       size - 大小
        //       field_name - 字段名称
        // 抛出: 偏移量无效时抛出ParseException异常
        template <typename T>
        void ValidateOffset(std::size_t offset, std::size_t size, std::string_view field_name) const;
        template <typename T>
        void ValidateOffset(std::size_t offset, std::string_view field_name) const ;
        // 验证DOS头
        void ValidateDosHeader() const;
        // 验证NT签名
        void ValidateNtSignature() const;
    };

    // 便捷函数:解析PE文件
    // 参数: file_path - PE文件路径
    // 返回值: 解析结果
    [[nodiscard]] inline auto ParsePeFile(std::string_view file_path) -> PeParseResult {
        PeParser parser;
        parser.LoadFile(file_path);
        parser.Parse();
        return parser.GetResult();
    }

}  // namespace pe_parser

pe_structures.h

cpp 复制代码
#pragma once

// 包含标准整数类型
#include <cstdint>

// PE解析器命名空间
namespace pe_parser {

    // PE文件机器类型枚举
    // 标识目标计算机架构
    enum class PeMachineType : uint16_t {
        kI386 = 0x014cu,      // Intel 386 及兼容处理器
        kIa64 = 0x0200u,      // Intel Itanium 处理器
        kAmd64 = 0x8664u,     // AMD64 处理器
        kArm = 0x01cu,        // ARM 处理器
        kArmNt = 0x0AA4u,     // ARM NT 处理器
        kEbc = 0x0EBCu,       // EFI 字节码
        kMips16 = 0x0266u,     // MIPS 16位处理器
        kMipsFpu = 0x0366u,    // 带FPU的MIPS处理器
        kMipsFpu16 = 0x0466u,  // 带FPU的MIPS 16位处理器
        kAlpha = 0x0184u,      // Alpha 处理器
        kSH3 = 0x01a2u,        // SH3 处理器
        kSH3E = 0x01a4u,       // SH3E 处理器
        kSH4 = 0x01a6u,        // SH4 处理器
        kMipsWce = 0x0169u     // MIPS WCE 处理器
    };

    // 文件特征枚举
    // 标识PE文件的各种属性
    enum class PeCharacteristics : uint16_t {
        kRelocsStripped = 0x0001u,         // 重定位信息已剥离
        kExecutableImage = 0x0002u,         // 可执行映像
        kLineNumsStripped = 0x0004u,        // 行号信息已剥离
        kLocalSymsStripped = 0x0008u,       // 局部符号已剥离
        kAggressiveWsTrim = 0x0100u,        // 积极的工作集修剪
        kLargeAddressAware = 0x0200u,       // 支持大地址
        kBytesReversedLo = 0x0080u,         // 低字节序反转
        k32BitMachine = 0x1000u,            // 32位计算机
        kDebugStripped = 0x0400u,           // 调试信息已剥离
        kRemovableRunFromSwap = 0x0800u,    // 可移动介质从交换区运行
        kNetRunFromSwap = 0x1000u,          // 网络从交换区运行
        kSystem = 0x2000u,                  // 系统文件
        kDll = 0x4000u,                     // DLL文件
        kUpSystemOnly = 0x8000u,            // 仅在单处理器上运行
        kBytesReversedHi = 0x8000u          // 高字节序反转
    };

    // 子系统枚举
    // 标识执行环境
    enum class PeSubsystem : uint16_t {
        kUnknown = 0u,                    // 未知子系统
        kNative = 1u,                     // 本地子系统
        kWindowsGui = 2u,                 // Windows GUI子系统
        kWindowsCui = 3u,                 // Windows CUI子系统
        kOs2Cui = 5u,                     // OS/2 CUI子系统
        kPosixCui = 7u,                   // POSIX CUI子系统
        kNativeWindows = 8u,               // 本地Windows子系统
        kWindowsCeGui = 9u,                // Windows CE GUI子系统
        kEfiApplication = 10u,             // EFI应用程序
        kEfiBootServiceDriver = 11u,       // EFI引导服务驱动程序
        kEfiRuntimeDriver = 12u,           // EFI运行时驱动程序
        kEfiRom = 13u,                     // EFI ROM
        kXbox = 14u,                       // Xbox
        kWindowsBootApplication = 16u      // Windows引导应用程序
    };

    // 数据目录索引枚举
    // 标识PE文件中的各种数据目录
    enum class DataDirectoryIndex : uint32_t {
        kExport = 0,          // 导出表
        kImport = 1,          // 导入表
        kResource = 2,        // 资源表
        kException = 3,       // 异常表
        kSecurity = 4,        // 安全表
        kBaseReloc = 5,       // 基址重定位表
        kDebug = 6,           // 调试信息
        kArchitecture = 7,     // 架构特定数据
        kGlobalPtr = 8,       // 全局指针
        kTls = 9,             // TLS表
        kLoadConfig = 10,      // 加载配置表
        kBoundImport = 11,     // 绑定导入表
        kIat = 12,            // 导入地址表
        kDelayImport = 13,     // 延迟导入表
        kComDescriptor = 14,   // COM描述符
        kReserved = 15         // 保留
    };

    // 节特征枚举
    // 标识节的各种属性
    enum class SectionCharacteristics : uint32_t {
        kTypeMask = 0x00000000u,        // 类型掩码
        kNoPad = 0x00000008u,           // 无填充
        kCode = 0x00000020u,             // 代码节
        kInitializedData = 0x00000040u,  // 已初始化数据节
        kUninitializedData = 0x00000080u, // 未初始化数据节
        kShare = 0x10000000u,            // 可共享
        kExecute = 0x20000000u,          // 可执行
        kRead = 0x40000000u,             // 可读
        kWrite = 0x80000000u              // 可写
    };

    // 重定位类型枚举
    // 标识重定位条目类型
    enum class RelocationType : uint8_t {
        kAbsolute = 0,          // 绝对重定位
        kHigh = 1,              // 高16位重定位
        kLow = 2,               // 低16位重定位
        kHighLow = 3,           // 高16位和低16位重定位
        kHighAdj = 4,           // 高位调整重定位
        kMipsJmpaddr = 5,       // MIPS跳转地址重定位
        kArmMov32 = 5,          // ARM MOV32重定位
        kRisc386HighLow = 3,    // RISC 386高16位和低16位重定位
        kImageRelative = 10      // 映像相对重定位
    };

    // DOS头结构
    // 存储DOS头信息
    struct ImageDosHeader {
        uint16_t e_magic;       // 魔数,通常为0x5A4D("MZ")
        uint16_t e_cblp;        // 最后一页的字节数
        uint16_t e_cp;          // 文件页数
        uint16_t e_crlc;        // 重定位项数
        uint16_t e_cparhdr;      // 头部的段落数
        uint16_t e_minalloc;     // 最小额外段落数
        uint16_t e_maxalloc;     // 最大额外段落数
        uint16_t e_ss;           // 堆栈段
        uint16_t e_sp;           // 堆栈指针
        uint16_t e_csum;         // 校验和
        uint16_t e_ip;           // 指令指针
        uint16_t e_cs;           // 代码段
        uint16_t e_lfarlc;       // 重定位表偏移
        uint16_t e_ovno;         // 覆盖号
        uint16_t e_res[4];       // 保留字段
        uint16_t e_oemid;        // OEM标识符
        uint16_t e_oeminfo;      // OEM信息
        uint16_t e_res2[10];     // 保留字段
        int32_t e_lfanew;        // NT头偏移量
    };

    // 文件头结构
    // 存储文件头信息
    struct ImageFileHeader {
        uint16_t machine;                // 目标计算机类型
        uint16_t number_of_sections;     // 节的数量
        uint32_t time_date_stamp;        // 时间戳
        uint32_t pointer_to_symbol_table; // 符号表偏移
        uint32_t number_of_symbols;      // 符号数量
        uint16_t size_of_optional_header; // 可选头大小
        uint16_t characteristics;        // 文件特征
    };

    // 数据目录结构
    // 存储数据目录信息
    struct ImageDataDirectory {
        uint32_t virtual_address;  // 虚拟地址
        uint32_t size;            // 大小
    };

    // 32位可选头结构
    // 存储PE32可选头信息
    struct ImageOptionalHeader32 {
        uint16_t magic;                               // 魔数,PE32为0x10B
        uint8_t major_linker_version;                 // 主链接器版本号
        uint8_t minor_linker_version;                 // 次链接器版本号
        uint32_t size_of_code;                        // 代码大小
        uint32_t size_of_initialized_data;            // 已初始化数据大小
        uint32_t size_of_uninitialized_data;          // 未初始化数据大小
        uint32_t address_of_entry_point;              // 入口点地址
        uint32_t base_of_code;                        // 代码基址
        uint32_t base_of_data;                        // 数据基址
        uint32_t image_base;                          // 映像基址
        uint32_t section_alignment;                   // 节对齐
        uint32_t file_alignment;                      // 文件对齐
        uint16_t major_operating_system_version;      // 主操作系统版本号
        uint16_t minor_operating_system_version;      // 次操作系统版本号
        uint16_t major_image_version;                 // 主映像版本号
        uint16_t minor_image_version;                 // 次映像版本号
        uint16_t major_subsystem_version;             // 主子系统版本号
        uint16_t minor_subsystem_version;             // 次子系统版本号
        uint32_t win32_version_value;                 // Win32版本值
        uint32_t size_of_image;                       // 映像大小
        uint32_t size_of_headers;                     // 头大小
        uint32_t check_sum;                           // 校验和
        uint16_t subsystem;                           // 子系统类型
        uint16_t dll_characteristics;                 // DLL特征
        uint32_t size_of_stack_reserve;               // 堆栈保留大小
        uint32_t size_of_stack_commit;                // 堆栈提交大小
        uint32_t size_of_heap_reserve;                // 堆保留大小
        uint32_t size_of_heap_commit;                 // 堆提交大小
        uint32_t loader_flags;                        // 加载器标志
        uint32_t number_of_rva_and_sizes;             // 数据目录数量
        ImageDataDirectory data_directory[16];        // 数据目录数组
    };

    // 64位可选头结构
    // 存储PE32+可选头信息
    struct ImageOptionalHeader64 {
        uint16_t magic;                               // 魔数,PE32+为0x20B
        uint8_t major_linker_version;                 // 主链接器版本号
        uint8_t minor_linker_version;                 // 次链接器版本号
        uint32_t size_of_code;                        // 代码大小
        uint32_t size_of_initialized_data;            // 已初始化数据大小
        uint32_t size_of_uninitialized_data;          // 未初始化数据大小
        uint32_t address_of_entry_point;              // 入口点地址
        uint32_t base_of_code;                        // 代码基址
        uint64_t image_base;                          // 映像基址(64位)
        uint32_t section_alignment;                   // 节对齐
        uint32_t file_alignment;                      // 文件对齐
        uint16_t major_operating_system_version;      // 主操作系统版本号
        uint16_t minor_operating_system_version;      // 次操作系统版本号
        uint16_t major_image_version;                 // 主映像版本号
        uint16_t minor_image_version;                 // 次映像版本号
        uint16_t major_subsystem_version;             // 主子系统版本号
        uint16_t minor_subsystem_version;             // 次子系统版本号
        uint32_t win32_version_value;                 // Win32版本值
        uint32_t size_of_image;                       // 映像大小
        uint32_t size_of_headers;                     // 头大小
        uint32_t check_sum;                           // 校验和
        uint16_t subsystem;                           // 子系统类型
        uint16_t dll_characteristics;                 // DLL特征
        uint64_t size_of_stack_reserve;               // 堆栈保留大小(64位)
        uint64_t size_of_stack_commit;                // 堆栈提交大小(64位)
        uint64_t size_of_heap_reserve;                // 堆保留大小(64位)
        uint64_t size_of_heap_commit;                 // 堆提交大小(64位)
        uint32_t loader_flags;                        // 加载器标志
        uint32_t number_of_rva_and_sizes;             // 数据目录数量
        ImageDataDirectory data_directory[16];        // 数据目录数组
    };

    // NT头结构
    // 存储NT头信息
    struct ImageNtHeaders {
        uint32_t signature;        // NT签名,通常为0x00004550("PE")
        ImageFileHeader file_header; // 文件头
    };

    // 节头结构
    // 存储节头信息
    struct ImageSectionHeader {
        uint8_t name[8];               // 节名称(最多8个字符)
        union {
            uint32_t physical_address;   // 物理地址
            uint32_t virtual_size;       // 虚拟大小
        } misc;                        // 联合体
        uint32_t virtual_address;       // 虚拟地址
        uint32_t size_of_raw_data;      // 原始数据大小
        uint32_t pointer_to_raw_data;   // 原始数据指针
        uint32_t pointer_to_relocations; // 重定位指针
        uint32_t pointer_to_line_numbers; // 行号指针
        uint16_t number_of_relocations;  // 重定位项数
        uint16_t number_of_line_numbers; // 行号项数
        uint32_t characteristics;       // 节特征
    };

    // 导入描述符结构
    // 存储导入表信息
    struct ImageImportDescriptor {
        union {
            uint32_t characteristics;     // 特征(未使用)
            uint32_t original_first_thunk; // 原始第一个Thunk的RVA
        };
        uint32_t time_date_stamp;       // 时间戳
        uint32_t forwarder_chain;       // 转发器链
        uint32_t name;                  // DLL名称RVA
        uint32_t first_thunk;           // 第一个Thunk的RVA
    };

    // 导出目录结构
    // 存储导出表信息
    struct ImageExportDirectory {
        uint32_t characteristics;         // 特征(未使用)
        uint32_t time_date_stamp;         // 时间戳
        uint16_t major_version;           // 主版本号
        uint16_t minor_version;           // 次版本号
        uint32_t name;                    // 模块名称RVA
        uint32_t base;                    // 导出函数基址
        uint32_t number_of_functions;     // 函数数量
        uint32_t number_of_names;         // 名称数量
        uint32_t address_of_functions;    // 函数地址的RVA
        uint32_t address_of_names;        // 名称地址的RVA
        uint32_t address_of_name_ordinals; // 名称序号的RVA
    };

    // 基址重定位块结构
    // 存储重定位表信息
    struct ImageBaseRelocation {
        uint32_t virtual_address;   // 块虚拟地址
        uint32_t size_of_block;     // 块大小
    };

    // 导入提示结构
    // 存储导入函数提示信息
    struct ImageImportHint {
        uint16_t hint;    // 导入提示(序号)
        char name[1];     // 导入名称(可变长度)
    };

}  // namespace pe_parser

pe_parser.cpp

cpp 复制代码
// 包含必要的头文件
#include <fstream>
#include <iomanip>
#include <sstream>

// 包含自定义头文件
#include "pe_parser.h"

// PE解析器命名空间
namespace pe_parser {

    // 常量定义
    constexpr std::uint16_t kDosMagic = 0x5A4Du;       // DOS魔数 "MZ"
    constexpr std::uint32_t kNtSignature = 0x00004550u; // NT签名 "PE"
    constexpr std::uint16_t kPe32Magic = 0x10Bu;       // PE32魔数
    constexpr std::uint16_t kPe32PlusMagic = 0x20Bu;   // PE32+魔数

    // 将PE文件加载到内存中
    // 参数: file_path - PE文件路径
    void PeParser::LoadFile(std::string_view file_path) {
        // 打开文件
        std::ifstream file(file_path.data(), std::ios::binary);
        if (!file.is_open()) {
            throw FileException(file_path, "打开", "无法打开文件");
        }

        // 获取文件大小
        file.seekg(0, std::ios::end);
        const auto file_size = file.tellg();
        file.seekg(0, std::ios::beg);

        // 检查文件大小是否有效
        if (file_size < static_cast<std::streampos>(sizeof(ImageDosHeader))) {
            throw InvalidPeException("文件太小,不是有效的PE文件", 0);
        }

        // 读取文件数据到内存
        file_data_.resize(static_cast<std::size_t>(file_size));
        if (!file.read(reinterpret_cast<char*>(file_data_.data()), file_size)) {
            throw FileException(file_path, "读取", "读取文件失败");
        }
    }

    // 解析PE文件
    void PeParser::Parse() {
        // 检查文件是否已加载
        if (!IsLoaded()) {
            throw PeException("未加载文件");
        }

        // 解析DOS头
        ParseDosHeader();
        // 验证DOS头
        ValidateDosHeader();
        // 解析NT头
        ParseNtHeaders();
        // 验证NT签名
        ValidateNtSignature();
        // 解析节表
        ParseSections();
        // 解析导入表
        ParseImports();
        // 解析导出表
        ParseExports();
        // 解析重定位表
        ParseRelocations();
    }

    // 解析DOS头
    void PeParser::ParseDosHeader() {
        // 验证偏移量
        ValidateOffset<ImageDosHeader>(0, sizeof(ImageDosHeader), "DOS头");

        // 获取DOS头指针
        const auto* dos_header = GetPtr<ImageDosHeader>(0);
        if (!dos_header) {
            throw ParseException("PeParser", "DOS头", "获取DOS头指针失败");
        }

        // 填充DOS头信息
        result_.dos_header.magic = dos_header->e_magic;
        result_.dos_header.last_page_bytes = dos_header->e_cblp;
        result_.dos_header.pages = dos_header->e_cp;
        result_.dos_header.relocations = dos_header->e_crlc;
        result_.dos_header.header_paragraphs = dos_header->e_cparhdr;
        result_.dos_header.min_extra_paragraphs = dos_header->e_minalloc;
        result_.dos_header.max_extra_paragraphs = dos_header->e_maxalloc;
        result_.dos_header.stack_segment = dos_header->e_ss;
        result_.dos_header.stack_pointer = dos_header->e_sp;
        result_.dos_header.checksum = dos_header->e_csum;
        result_.dos_header.instruction_pointer = dos_header->e_ip;
        result_.dos_header.code_segment = dos_header->e_cs;
        result_.dos_header.relocation_offset = dos_header->e_lfarlc;
        result_.dos_header.overlay_number = dos_header->e_ovno;
        result_.dos_header.oem_id = dos_header->e_oemid;
        result_.dos_header.oem_info = dos_header->e_oeminfo;
        result_.dos_header.nt_header_offset = dos_header->e_lfanew;
    }

    // 解析NT头
    void PeParser::ParseNtHeaders() {
        // 获取NT头偏移量
        const auto nt_header_offset = result_.dos_header.nt_header_offset;

        // 验证偏移量
        ValidateOffset<ImageNtHeaders>(nt_header_offset, sizeof(ImageNtHeaders), "NT头");

        // 获取NT头指针
        const auto* nt_header = GetPtr<ImageNtHeaders>(nt_header_offset);
        if (!nt_header) {
            throw ParseException("PeParser", "NT头", "获取NT头指针失败");
        }

        // 存储NT签名
        result_.nt_signature = nt_header->signature;

        // 填充文件头信息
        result_.file_header.machine = nt_header->file_header.machine;
        result_.file_header.number_of_sections = nt_header->file_header.number_of_sections;
        result_.file_header.time_date_stamp = nt_header->file_header.time_date_stamp;
        result_.file_header.pointer_to_symbol_table = nt_header->file_header.pointer_to_symbol_table;
        result_.file_header.number_of_symbols = nt_header->file_header.number_of_symbols;
        result_.file_header.size_of_optional_header = nt_header->file_header.size_of_optional_header;
        result_.file_header.characteristics = nt_header->file_header.characteristics;

        // 计算可选头偏移量
        const auto optional_header_offset = nt_header_offset + sizeof(ImageNtHeaders);

        // 验证可选头偏移量
        ValidateOffset<std::uint16_t>(optional_header_offset, sizeof(std::uint16_t), "可选头魔数");

        // 获取魔数指针
        const auto* magic_ptr = GetPtr<std::uint16_t>(optional_header_offset);
        if (!magic_ptr) {
            throw ParseException("PeParser", "可选头魔数", "获取可选头魔数指针失败");
        }

        // 检查魔数以确定是PE32还是PE32+
        const auto magic = *magic_ptr;
        if (magic == kPe32Magic) {
            // PE32
            result_.is_64bit = false;

            // 验证32位可选头偏移量
            ValidateOffset<ImageOptionalHeader32>(optional_header_offset, sizeof(ImageOptionalHeader32), "可选头32");

            // 获取32位可选头指针
            const auto* opt_header = GetPtr<ImageOptionalHeader32>(optional_header_offset);
            if (!opt_header) {
                throw ParseException("PeParser", "可选头32", "获取32位可选头指针失败");
            }

            // 填充32位可选头信息
            result_.opt32.magic = opt_header->magic;
            result_.opt32.major_linker_version = opt_header->major_linker_version;
            result_.opt32.minor_linker_version = opt_header->minor_linker_version;
            result_.opt32.size_of_code = opt_header->size_of_code;
            result_.opt32.size_of_initialized_data = opt_header->size_of_initialized_data;
            result_.opt32.size_of_uninitialized_data = opt_header->size_of_uninitialized_data;
            result_.opt32.address_of_entry_point = opt_header->address_of_entry_point;
            result_.opt32.base_of_code = opt_header->base_of_code;
            result_.opt32.base_of_data = opt_header->base_of_data;
            result_.opt32.image_base = opt_header->image_base;
            result_.opt32.section_alignment = opt_header->section_alignment;
            result_.opt32.file_alignment = opt_header->file_alignment;
            result_.opt32.major_operating_system_version = opt_header->major_operating_system_version;
            result_.opt32.minor_operating_system_version = opt_header->minor_operating_system_version;
            result_.opt32.major_image_version = opt_header->major_image_version;
            result_.opt32.minor_image_version = opt_header->minor_image_version;
            result_.opt32.major_subsystem_version = opt_header->major_subsystem_version;
            result_.opt32.minor_subsystem_version = opt_header->minor_subsystem_version;
            result_.opt32.win32_version_value = opt_header->win32_version_value;
            result_.opt32.size_of_image = opt_header->size_of_image;
            result_.opt32.size_of_headers = opt_header->size_of_headers;
            result_.opt32.check_sum = opt_header->check_sum;
            result_.opt32.subsystem = opt_header->subsystem;
            result_.opt32.dll_characteristics = opt_header->dll_characteristics;
            result_.opt32.size_of_stack_reserve = opt_header->size_of_stack_reserve;
            result_.opt32.size_of_stack_commit = opt_header->size_of_stack_commit;
            result_.opt32.size_of_heap_reserve = opt_header->size_of_heap_reserve;
            result_.opt32.size_of_heap_commit = opt_header->size_of_heap_commit;
            result_.opt32.loader_flags = opt_header->loader_flags;
            result_.opt32.number_of_rva_and_sizes = opt_header->number_of_rva_and_sizes;

            // 填充数据目录信息
            const char* data_dir_names[] = {
              "导出表", "导入表", "资源表", "异常表", "安全表", "基址重定位表",
              "调试信息", "架构数据", "全局指针", "TLS表", "加载配置表", "绑定导入表",
              "导入地址表", "延迟导入表", "COM描述符", "保留"
            };

            for (std::size_t i = 0; i < 16; ++i) {
                DataDirectoryInfo dd_info;
                dd_info.name = data_dir_names[i];
                dd_info.virtual_address = opt_header->data_directory[i].virtual_address;
                dd_info.size = opt_header->data_directory[i].size;
                result_.opt32.data_directories.push_back(dd_info);
            }
        }
        else if (magic == kPe32PlusMagic) {
            // PE32+
            result_.is_64bit = true;

            // 验证64位可选头偏移量
            ValidateOffset<ImageOptionalHeader64>(optional_header_offset, sizeof(ImageOptionalHeader64), "可选头64");

            // 获取64位可选头指针
            const auto* opt_header = GetPtr<ImageOptionalHeader64>(optional_header_offset);
            if (!opt_header) {
                throw ParseException("PeParser", "可选头64", "获取64位可选头指针失败");
            }

            // 填充64位可选头信息
            result_.opt64.magic = opt_header->magic;
            result_.opt64.major_linker_version = opt_header->major_linker_version;
            result_.opt64.minor_linker_version = opt_header->minor_linker_version;
            result_.opt64.size_of_code = opt_header->size_of_code;
            result_.opt64.size_of_initialized_data = opt_header->size_of_initialized_data;
            result_.opt64.size_of_uninitialized_data = opt_header->size_of_uninitialized_data;
            result_.opt64.address_of_entry_point = opt_header->address_of_entry_point;
            result_.opt64.base_of_code = opt_header->base_of_code;
            result_.opt64.image_base = opt_header->image_base;
            result_.opt64.section_alignment = opt_header->section_alignment;
            result_.opt64.file_alignment = opt_header->file_alignment;
            result_.opt64.major_operating_system_version = opt_header->major_operating_system_version;
            result_.opt64.minor_operating_system_version = opt_header->minor_operating_system_version;
            result_.opt64.major_image_version = opt_header->major_image_version;
            result_.opt64.minor_image_version = opt_header->minor_image_version;
            result_.opt64.major_subsystem_version = opt_header->major_subsystem_version;
            result_.opt64.minor_subsystem_version = opt_header->minor_subsystem_version;
            result_.opt64.win32_version_value = opt_header->win32_version_value;
            result_.opt64.size_of_image = opt_header->size_of_image;
            result_.opt64.size_of_headers = opt_header->size_of_headers;
            result_.opt64.check_sum = opt_header->check_sum;
            result_.opt64.subsystem = opt_header->subsystem;
            result_.opt64.dll_characteristics = opt_header->dll_characteristics;
            result_.opt64.size_of_stack_reserve = opt_header->size_of_stack_reserve;
            result_.opt64.size_of_stack_commit = opt_header->size_of_stack_commit;
            result_.opt64.size_of_heap_reserve = opt_header->size_of_heap_reserve;
            result_.opt64.size_of_heap_commit = opt_header->size_of_heap_commit;
            result_.opt64.loader_flags = opt_header->loader_flags;
            result_.opt64.number_of_rva_and_sizes = opt_header->number_of_rva_and_sizes;

            // 填充数据目录信息
            const char* data_dir_names[] = {
              "导出表", "导入表", "资源表", "异常表", "安全表", "基址重定位表",
              "调试信息", "架构数据", "全局指针", "TLS表", "加载配置表", "绑定导入表",
              "导入地址表", "延迟导入表", "COM描述符", "保留"
            };

            for (std::size_t i = 0; i < 16; ++i) {
                DataDirectoryInfo dd_info;
                dd_info.name = data_dir_names[i];
                dd_info.virtual_address = opt_header->data_directory[i].virtual_address;
                dd_info.size = opt_header->data_directory[i].size;
                result_.opt64.data_directories.push_back(dd_info);
            }
        }
        else {
            // 无效魔数
            throw InvalidPeException(
                "无效的可选头魔数,期望0x10B(PE32)或0x20B(PE32+)",
                optional_header_offset);
        }
    }

    // 解析节表
    void PeParser::ParseSections() {
        // 计算节表偏移量
        const auto nt_header_offset = result_.dos_header.nt_header_offset;
        const auto section_table_offset = nt_header_offset + sizeof(ImageNtHeaders) + result_.file_header.size_of_optional_header;

        // 验证节表偏移量
        ValidateOffset<ImageSectionHeader>(section_table_offset, sizeof(ImageSectionHeader) * result_.file_header.number_of_sections, "节表");

        // 解析每个节
        for (std::uint16_t i = 0; i < result_.file_header.number_of_sections; ++i) {
            // 计算当前节头偏移量
            const auto section_header_offset = section_table_offset + i * sizeof(ImageSectionHeader);

            // 获取节头指针
            const auto* section_header = GetPtr<ImageSectionHeader>(section_header_offset);
            if (!section_header) {
                throw ParseException("PeParser", "节头", "获取节头指针失败");
            }

            // 填充节信息
            SectionInfo section_info;

            // 提取节名称
            char name[9] = { 0 };
            std::memcpy(name, section_header->name, 8);
            section_info.name = name;

            section_info.virtual_size = section_header->misc.virtual_size;
            section_info.virtual_address = section_header->virtual_address;
            section_info.size_of_raw_data = section_header->size_of_raw_data;
            section_info.pointer_to_raw_data = section_header->pointer_to_raw_data;
            section_info.characteristics = section_header->characteristics;

            result_.sections.push_back(section_info);
        }
    }

    // 解析导入表
    void PeParser::ParseImports() {
        // 获取导入表RVA和大小
        std::uint32_t import_table_rva = 0;
        std::uint32_t import_table_size = 0;

        if (result_.is_64bit) {
            import_table_rva = result_.opt64.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kImport)].virtual_address;
            import_table_size = result_.opt64.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kImport)].size;
        }
        else {
            import_table_rva = result_.opt32.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kImport)].virtual_address;
            import_table_size = result_.opt32.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kImport)].size;
        }

        // 检查导入表是否存在
        if (import_table_rva == 0 || import_table_size == 0) {
            return;  // 无导入表
        }

        // 将RVA转换为文件偏移
        const auto import_table_offset = RvaToFileOffset(import_table_rva);
        if (import_table_offset == 0) {
            return;  // 无效的RVA
        }

        // 解析导入描述符
        std::size_t current_offset = import_table_offset;
        while (true) {
            // 验证偏移量
            ValidateOffset<ImageImportDescriptor>(current_offset, sizeof(ImageImportDescriptor), "导入描述符");

            // 获取导入描述符指针
            const auto* import_descriptor = GetPtr<ImageImportDescriptor>(current_offset);
            if (!import_descriptor) {
                throw ParseException("PeParser", "导入描述符", "获取导入描述符指针失败");
            }

            // 检查是否到达导入表末尾(所有字段都为0)
            if (import_descriptor->original_first_thunk == 0 &&
                import_descriptor->time_date_stamp == 0 &&
                import_descriptor->forwarder_chain == 0 &&
                import_descriptor->name == 0 &&
                import_descriptor->first_thunk == 0) {
                break;
            }

            // 解析DLL名称
            const auto name_offset = RvaToFileOffset(import_descriptor->name);
            if (name_offset == 0) {
                current_offset += sizeof(ImageImportDescriptor);
                continue;
            }

            const auto dll_name = ReadString(name_offset);
            if (dll_name.empty()) {
                current_offset += sizeof(ImageImportDescriptor);
                continue;
            }

            // 创建导入信息
            ImportInfo import_info;
            import_info.dll_name = dll_name;

            // 解析导入函数
            const auto original_first_thunk = import_descriptor->original_first_thunk;
            if (original_first_thunk != 0) {
                const auto thunk_offset = RvaToFileOffset(original_first_thunk);
                if (thunk_offset != 0) {
                    std::size_t thunk_current = thunk_offset;
                    while (true) {
                        // 验证偏移量
                        ValidateOffset<std::uint32_t>(thunk_current, sizeof(std::uint32_t), "导入Thunk");

                        // 获取导入提示/名称表条目
                        const auto* thunk_data = GetPtr<std::uint32_t>(thunk_current);
                        if (!thunk_data) {
                            break;
                        }

                        // 检查是否到达末尾
                        if (*thunk_data == 0) {
                            break;
                        }

                        // 检查是否为按名称导入(高位为0)
                        if ((*thunk_data & 0x80000000u) == 0) {
                            // 按名称导入
                            const auto hint_name_offset = RvaToFileOffset(*thunk_data);
                            if (hint_name_offset != 0) {
                                // 验证偏移量
                                ValidateOffset<ImageImportHint>(hint_name_offset, sizeof(ImageImportHint), "导入提示");

                                // 获取导入提示指针
                                const auto* hint = GetPtr<ImageImportHint>(hint_name_offset);
                                if (hint) {
                                    // 提取函数名称(跳过提示字段)
                                    const auto function_name = ReadString(hint_name_offset + sizeof(hint->hint));
                                    if (!function_name.empty()) {
                                        import_info.functions.push_back(function_name);
                                    }
                                }
                            }
                        }
                        else {
                            // 按序号导入
                            // 序号 = 低16位
                            const auto ordinal = static_cast<std::uint16_t>(*thunk_data & 0xFFFFu);
                            import_info.functions.push_back("#" + std::to_string(ordinal));
                        }

                        // 移动到下一个导入条目
                        thunk_current += sizeof(std::uint32_t);
                    }
                }
            }

            // 添加到导入信息列表
            result_.imports.push_back(import_info);

            // 移动到下一个导入描述符
            current_offset += sizeof(ImageImportDescriptor);
        }
    }

    // 解析导出表
    void PeParser::ParseExports() {
        // 获取导出表RVA和大小
        std::uint32_t export_table_rva = 0;
        std::uint32_t export_table_size = 0;

        if (result_.is_64bit) {
            export_table_rva = result_.opt64.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kExport)].virtual_address;
            export_table_size = result_.opt64.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kExport)].size;
        }
        else {
            export_table_rva = result_.opt32.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kExport)].virtual_address;
            export_table_size = result_.opt32.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kExport)].size;
        }

        // 检查导出表是否存在
        if (export_table_rva == 0 || export_table_size == 0) {
            return;  // 无导出表
        }

        // 将RVA转换为文件偏移
        const auto export_table_offset = RvaToFileOffset(export_table_rva);
        if (export_table_offset == 0) {
            return;  // 无效的RVA
        }

        // 验证偏移量
        ValidateOffset<ImageExportDirectory>(export_table_offset, sizeof(ImageExportDirectory), "导出目录");

        // 获取导出目录指针
        const auto* export_dir = GetPtr<ImageExportDirectory>(export_table_offset);
        if (!export_dir) {
            throw ParseException("PeParser", "导出目录", "获取导出目录指针失败");
        }

        // 填充导出信息
        result_.exports.base = export_dir->base;
        result_.exports.number_of_functions = export_dir->number_of_functions;
        result_.exports.number_of_names = export_dir->number_of_names;

        // 解析模块名称
        const auto name_offset = RvaToFileOffset(export_dir->name);
        if (name_offset != 0) {
            result_.exports.module_name = ReadString(name_offset);
        }

        // 解析导出函数名称
        if (export_dir->number_of_names > 0) {
            const auto names_rva = export_dir->address_of_names;
            const auto ordinals_rva = export_dir->address_of_name_ordinals;

            const auto names_offset = RvaToFileOffset(names_rva);
            const auto ordinals_offset = RvaToFileOffset(ordinals_rva);

            if (names_offset != 0 && ordinals_offset != 0) {
                for (std::uint32_t i = 0; i < export_dir->number_of_names; ++i) {
                    // 验证偏移量
                    ValidateOffset<std::uint32_t>(names_offset + i * sizeof(std::uint32_t), sizeof(std::uint32_t), "导出名称RVA");
                    ValidateOffset<std::uint16_t>(ordinals_offset + i * sizeof(std::uint16_t), sizeof(std::uint16_t), "导出序号");

                    // 获取名称RVA
                    const auto* name_rva_ptr = GetPtr<std::uint32_t>(names_offset + i * sizeof(std::uint32_t));
                    if (!name_rva_ptr) {
                        continue;
                    }

                    // 获取序号
                    const auto* ordinal_ptr = GetPtr<std::uint16_t>(ordinals_offset + i * sizeof(std::uint16_t));
                    if (!ordinal_ptr) {
                        continue;
                    }

                    // 解析函数名称
                    const auto function_name_offset = RvaToFileOffset(*name_rva_ptr);
                    if (function_name_offset != 0) {
                        const auto function_name = ReadString(function_name_offset);
                        if (!function_name.empty()) {
                            result_.exports.function_names.push_back(function_name);
                        }
                    }
                }
            }
        }
    }

    // 解析重定位表
    void PeParser::ParseRelocations() {
        // 获取重定位表RVA和大小
        std::uint32_t reloc_table_rva = 0;
        std::uint32_t reloc_table_size = 0;

        if (result_.is_64bit) {
            reloc_table_rva = result_.opt64.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kBaseReloc)].virtual_address;
            reloc_table_size = result_.opt64.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kBaseReloc)].size;
        }
        else {
            reloc_table_rva = result_.opt32.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kBaseReloc)].virtual_address;
            reloc_table_size = result_.opt32.data_directories[static_cast<std::size_t>(DataDirectoryIndex::kBaseReloc)].size;
        }

        // 检查重定位表是否存在
        if (reloc_table_rva == 0 || reloc_table_size == 0) {
            return;  // 无重定位表
        }

        // 将RVA转换为文件偏移
        const auto reloc_table_offset = RvaToFileOffset(reloc_table_rva);
        if (reloc_table_offset == 0) {
            return;  // 无效的RVA
        }

        // 解析重定位块
        std::size_t current_offset = reloc_table_offset;
        const std::size_t end_offset = reloc_table_offset + reloc_table_size;

        while (current_offset < end_offset) {
            // 验证偏移量
            ValidateOffset<ImageBaseRelocation>(current_offset, sizeof(ImageBaseRelocation), "基址重定位块");

            // 获取重定位块指针
            const auto* reloc_block = GetPtr<ImageBaseRelocation>(current_offset);
            if (!reloc_block) {
                throw ParseException("PeParser", "基址重定位块", "获取基址重定位块指针失败");
            }

            // 检查是否到达重定位表末尾(大小为0)
            if (reloc_block->size_of_block == 0) {
                break;
            }

            // 创建重定位块信息
            RelocationBlock block;
            block.virtual_address = reloc_block->virtual_address;
            block.size_of_block = reloc_block->size_of_block;

            // 计算重定位条目数量
            const std::size_t entry_count = (reloc_block->size_of_block - sizeof(ImageBaseRelocation)) / sizeof(std::uint16_t);

            // 解析重定位条目
            const auto entries_offset = current_offset + sizeof(ImageBaseRelocation);
            for (std::size_t i = 0; i < entry_count; ++i) {
                // 验证偏移量
                ValidateOffset<std::uint16_t>(entries_offset + i * sizeof(std::uint16_t), sizeof(std::uint16_t), "重定位条目");

                // 获取重定位条目
                const auto* entry_ptr = GetPtr<std::uint16_t>(entries_offset + i * sizeof(std::uint16_t));
                if (!entry_ptr) {
                    continue;
                }

                // 提取重定位类型和偏移
                const auto entry = *entry_ptr;
                const auto type = static_cast<std::uint8_t>((entry >> 12) & 0x0Fu);
                const auto offset = static_cast<std::uint16_t>(entry & 0x0FFFu);

                // 添加到重定位条目列表
                RelocationEntry reloc_entry;
                reloc_entry.offset = offset;
                reloc_entry.type = type;
                block.entries.push_back(reloc_entry);
            }

            // 添加到重定位块列表
            result_.relocations.push_back(block);

            // 移动到下一个重定位块
            current_offset += reloc_block->size_of_block;
        }
    }

    // 模板函数:获取指定偏移处的指针
    // 参数: offset - 偏移量
    // 返回值: 指定类型的指针,无效时返回nullptr
    template <typename T>
    auto PeParser::GetPtr(std::size_t offset) const -> const T* {
        // 检查偏移量是否有效
        if (offset + sizeof(T) > file_data_.size()) {
            return nullptr;
        }

        // 返回指定类型的指针
        return reinterpret_cast<const T*>(file_data_.data() + offset);
    }

    // 将RVA转换为文件偏移
    // 参数: rva - 相对虚拟地址
    // 返回值: 对应的文件偏移,无效时返回0
    auto PeParser::RvaToFileOffset(std::uint32_t rva) const -> std::uint32_t {
        // 遍历所有节,查找包含此RVA的节
        for (const auto& section : result_.sections) {
            // 检查RVA是否在当前节中
            if (rva >= section.virtual_address &&
                rva < section.virtual_address + section.virtual_size) {
                // 计算文件偏移
                const auto file_offset = rva - section.virtual_address + section.pointer_to_raw_data;
                // 检查文件偏移是否有效
                if (file_offset < file_data_.size()) {
                    return static_cast<std::uint32_t>(file_offset);
                }
                break;
            }
        }

        // 未找到节或文件偏移无效
        return 0;
    }

    // 从指定偏移处读取字符串
    // 参数: offset - 偏移量
    // 返回值: 读取的字符串
    auto PeParser::ReadString(std::uint32_t offset) const -> std::string {
        // 检查偏移量是否超出文件大小
        if (offset >= file_data_.size()) {
            return {};
        }

        // 获取字符串起始指针
        const char* ptr = reinterpret_cast<const char*>(file_data_.data() + offset);
        // 计算最大字符串长度
        std::size_t max_len = file_data_.size() - offset;
        std::size_t len = 0;

        // 读取字符串直到遇到空字符或达到最大长度
        while (len < max_len && len < 256 && ptr[len] != '\0') {
            ++len;
        }

        // 返回读取的字符串
        return std::string(ptr, len);
    }

    // 模板函数:验证偏移量
    // 参数: offset - 偏移量
    //       size - 大小
    //       field_name - 字段名称
    // 抛出: 偏移量无效时抛出ParseException异常
    template <typename T>
    void PeParser::ValidateOffset(std::size_t offset, std::size_t size, std::string_view field_name) const {
        // 检查偏移量+大小是否超出文件大小
        if (offset + size > file_data_.size()) {
            throw ParseException("PeParser", std::string(field_name), "偏移量超出范围");
        }
    }

    // 模板辅助函数(可选)
    template <typename T>
    void PeParser::ValidateOffset(std::size_t offset, std::string_view field_name) const {
        ValidateOffset(offset, sizeof(T), field_name);
    }


    // 验证DOS头
    // 抛出: DOS魔数无效时抛出InvalidPeException异常
    void PeParser::ValidateDosHeader() const {
        // 检查DOS魔数是否为"MZ"
        if (result_.dos_header.magic != kDosMagic) {
            throw InvalidPeException(
                "无效的DOS魔数,期望0x5A4D,实际0x" +
                std::to_string(result_.dos_header.magic),
                0);
        }
    }

    // 验证NT签名
    // 抛出: NT签名无效时抛出InvalidPeException异常
    void PeParser::ValidateNtSignature() const {
        // 检查NT签名是否为"PE"
        if (result_.nt_signature != kNtSignature) {
            throw InvalidPeException(
                "无效的NT签名,期望0x00004550,实际0x" +
                std::to_string(result_.nt_signature),
                result_.dos_header.nt_header_offset);
        }
    }

    // 将解析结果转换为字符串
    // 返回值: 解析结果的字符串表示
    auto PeParser::ToString() const -> std::string {
        std::ostringstream oss;  // 用于构建输出的字符串流
        oss << std::hex << std::setfill('0');  // 设置输出为十六进制,填充字符为'0'

        // 输出DOS头信息
        oss << "[DOS头]\n";
        oss << "  e_magic: 0x" << std::setw(4) << result_.dos_header.magic << "\n";
        oss << "  e_cblp: 0x" << std::setw(4) << result_.dos_header.last_page_bytes << "\n";
        oss << "  e_cp: 0x" << std::setw(4) << result_.dos_header.pages << "\n";
        oss << "  e_crlc: 0x" << std::setw(4) << result_.dos_header.relocations << "\n";
        oss << "  e_cparhdr: 0x" << std::setw(4) << result_.dos_header.header_paragraphs << "\n";
        oss << "  e_minalloc: 0x" << std::setw(4) << result_.dos_header.min_extra_paragraphs << "\n";
        oss << "  e_maxalloc: 0x" << std::setw(4) << result_.dos_header.max_extra_paragraphs << "\n";
        oss << "  e_ss: 0x" << std::setw(4) << result_.dos_header.stack_segment << "\n";
        oss << "  e_sp: 0x" << std::setw(4) << result_.dos_header.stack_pointer << "\n";
        oss << "  e_csum: 0x" << std::setw(4) << result_.dos_header.checksum << "\n";
        oss << "  e_ip: 0x" << std::setw(4) << result_.dos_header.instruction_pointer << "\n";
        oss << "  e_cs: 0x" << std::setw(4) << result_.dos_header.code_segment << "\n";
        oss << "  e_lfarlc: 0x" << std::setw(4) << result_.dos_header.relocation_offset << "\n";
        oss << "  e_ovno: 0x" << std::setw(4) << result_.dos_header.overlay_number << "\n";
        oss << "  e_oemid: 0x" << std::setw(4) << result_.dos_header.oem_id << "\n";
        oss << "  e_oeminfo: 0x" << std::setw(4) << result_.dos_header.oem_info << "\n";
        oss << "  e_lfanew: 0x" << result_.dos_header.nt_header_offset << "\n";

        // 输出NT头信息
        oss << "\n[NT头]\n";
        oss << "  签名: 0x" << result_.nt_signature << "\n";

        // 输出文件头信息
        oss << "\n[文件头]\n";
        oss << "  计算机类型: 0x" << std::setw(4) << result_.file_header.machine << "\n";
        oss << "  节数量: " << std::dec << result_.file_header.number_of_sections << "\n";
        oss << "  时间戳: 0x" << std::hex << result_.file_header.time_date_stamp << "\n";
        oss << "  符号表指针: 0x" << std::hex << result_.file_header.pointer_to_symbol_table << "\n";
        oss << "  符号数量: " << std::dec << result_.file_header.number_of_symbols << "\n";
        oss << "  可选头大小: 0x" << std::hex << result_.file_header.size_of_optional_header << "\n";
        oss << "  特征: 0x" << std::setw(4) << result_.file_header.characteristics << "\n";

        // 输出可选头信息
        oss << "\n[可选头]\n";
        if (result_.is_64bit) {
            const auto& opt = result_.opt64;
            oss << "  魔数: 0x" << std::setw(4) << opt.magic << " (PE32+, 64位)\n";
            oss << "  主链接器版本: " << std::dec << static_cast<int>(opt.major_linker_version) << "\n";
            oss << "  次链接器版本: " << std::dec << static_cast<int>(opt.minor_linker_version) << "\n";
            oss << "  代码大小: 0x" << std::hex << opt.size_of_code << "\n";
            oss << "  已初始化数据大小: 0x" << std::hex << opt.size_of_initialized_data << "\n";
            oss << "  未初始化数据大小: 0x" << std::hex << opt.size_of_uninitialized_data << "\n";
            oss << "  入口点地址: 0x" << std::hex << opt.address_of_entry_point << "\n";
            oss << "  代码基址: 0x" << std::hex << opt.base_of_code << "\n";
            oss << "  映像基址: 0x" << std::hex << opt.image_base << "\n";
            oss << "  节对齐: 0x" << std::hex << opt.section_alignment << "\n";
            oss << "  文件对齐: 0x" << std::hex << opt.file_alignment << "\n";
            oss << "  主操作系统版本: " << std::dec << opt.major_operating_system_version << "\n";
            oss << "  次操作系统版本: " << std::dec << opt.minor_operating_system_version << "\n";
            oss << "  主映像版本: " << std::dec << opt.major_image_version << "\n";
            oss << "  次映像版本: " << std::dec << opt.minor_image_version << "\n";
            oss << "  主子系统版本: " << std::dec << opt.major_subsystem_version << "\n";
            oss << "  次子系统版本: " << std::dec << opt.minor_subsystem_version << "\n";
            oss << "  Win32版本值: 0x" << std::hex << opt.win32_version_value << "\n";
            oss << "  映像大小: 0x" << std::hex << opt.size_of_image << "\n";
            oss << "  头大小: 0x" << std::hex << opt.size_of_headers << "\n";
            oss << "  校验和: 0x" << std::hex << opt.check_sum << "\n";
            oss << "  子系统: 0x" << std::setw(2) << opt.subsystem << "\n";
            oss << "  DLL特征: 0x" << std::setw(4) << opt.dll_characteristics << "\n";
            oss << "  堆栈保留大小: 0x" << std::hex << opt.size_of_stack_reserve << "\n";
            oss << "  堆栈提交大小: 0x" << std::hex << opt.size_of_stack_commit << "\n";
            oss << "  堆保留大小: 0x" << std::hex << opt.size_of_heap_reserve << "\n";
            oss << "  堆提交大小: 0x" << std::hex << opt.size_of_heap_commit << "\n";
            oss << "  加载器标志: 0x" << std::hex << opt.loader_flags << "\n";
            oss << "  数据目录数量: " << std::dec << opt.number_of_rva_and_sizes << "\n";

            // 输出数据目录信息
            oss << "\n[数据目录]\n";
            for (const auto& dd : opt.data_directories) {
                if (dd.virtual_address != 0 || dd.size != 0) {
                    oss << "  " << dd.name << ": VA=0x" << std::hex << dd.virtual_address
                        << ", 大小=0x" << dd.size << "\n";
                }
            }
        }
        else {
            const auto& opt = result_.opt32;
            oss << "  魔数: 0x" << std::setw(4) << opt.magic << " (PE32, 32位)\n";
            oss << "  主链接器版本: " << std::dec << static_cast<int>(opt.major_linker_version) << "\n";
            oss << "  次链接器版本: " << std::dec << static_cast<int>(opt.minor_linker_version) << "\n";
            oss << "  代码大小: 0x" << std::hex << opt.size_of_code << "\n";
            oss << "  已初始化数据大小: 0x" << std::hex << opt.size_of_initialized_data << "\n";
            oss << "  未初始化数据大小: 0x" << std::hex << opt.size_of_uninitialized_data << "\n";
            oss << "  入口点地址: 0x" << std::hex << opt.address_of_entry_point << "\n";
            oss << "  代码基址: 0x" << std::hex << opt.base_of_code << "\n";
            oss << "  数据基址: 0x" << std::hex << opt.base_of_data << "\n";
            oss << "  映像基址: 0x" << std::hex << opt.image_base << "\n";
            oss << "  节对齐: 0x" << std::hex << opt.section_alignment << "\n";
            oss << "  文件对齐: 0x" << std::hex << opt.file_alignment << "\n";
            oss << "  主操作系统版本: " << std::dec << opt.major_operating_system_version << "\n";
            oss << "  次操作系统版本: " << std::dec << opt.minor_operating_system_version << "\n";
            oss << "  主映像版本: " << std::dec << opt.major_image_version << "\n";
            oss << "  次映像版本: " << std::dec << opt.minor_image_version << "\n";
            oss << "  主子系统版本: " << std::dec << opt.major_subsystem_version << "\n";
            oss << "  次子系统版本: " << std::dec << opt.minor_subsystem_version << "\n";
            oss << "  Win32版本值: 0x" << std::hex << opt.win32_version_value << "\n";
            oss << "  映像大小: 0x" << std::hex << opt.size_of_image << "\n";
            oss << "  头大小: 0x" << std::hex << opt.size_of_headers << "\n";
            oss << "  校验和: 0x" << std::hex << opt.check_sum << "\n";
            oss << "  子系统: 0x" << std::setw(2) << opt.subsystem << "\n";
            oss << "  DLL特征: 0x" << std::setw(4) << opt.dll_characteristics << "\n";
            oss << "  堆栈保留大小: 0x" << std::hex << opt.size_of_stack_reserve << "\n";
            oss << "  堆栈提交大小: 0x" << std::hex << opt.size_of_stack_commit << "\n";
            oss << "  堆保留大小: 0x" << std::hex << opt.size_of_heap_reserve << "\n";
            oss << "  堆提交大小: 0x" << std::hex << opt.size_of_heap_commit << "\n";
            oss << "  加载器标志: 0x" << std::hex << opt.loader_flags << "\n";
            oss << "  数据目录数量: " << std::dec << opt.number_of_rva_and_sizes << "\n";

            // 输出数据目录信息
            oss << "\n[数据目录]\n";
            for (const auto& dd : opt.data_directories) {
                if (dd.virtual_address != 0 || dd.size != 0) {
                    oss << "  " << dd.name << ": VA=0x" << std::hex << dd.virtual_address
                        << ", 大小=0x" << dd.size << "\n";
                }
            }
        }

        // 输出节信息
        oss << "\n[节]\n";
        for (std::size_t i = 0; i < result_.sections.size(); ++i) {
            const auto& sec = result_.sections[i];
            oss << "  节 " << i << ": " << sec.name << "\n";
            oss << "    虚拟大小: 0x" << std::hex << sec.virtual_size << "\n";
            oss << "    虚拟地址: 0x" << std::hex << sec.virtual_address << "\n";
            oss << "    原始数据大小: 0x" << std::hex << sec.size_of_raw_data << "\n";
            oss << "    原始数据指针: 0x" << std::hex << sec.pointer_to_raw_data << "\n";
            oss << "    特征: 0x" << std::hex << sec.characteristics << "\n";
        }

        // 输出导入信息
        oss << "\n[导入表]\n";
        if (result_.imports.empty()) {
            oss << "  (无导入)\n";
        }
        else {
            for (std::size_t i = 0; i < result_.imports.size(); ++i) {
                const auto& imp = result_.imports[i];
                oss << "  导入 " << i << ": " << imp.dll_name << "\n";
            }
        }

        // 输出导出信息
        oss << "\n[导出表]\n";
        if (result_.exports.module_name.empty()) {
            oss << "  (无导出)\n";
        }
        else {
            oss << "  模块名称: " << result_.exports.module_name << "\n";
            oss << "  基址: 0x" << std::hex << result_.exports.base << "\n";
            oss << "  函数数量: " << std::dec << result_.exports.number_of_functions << "\n";
            oss << "  名称数量: " << std::dec << result_.exports.number_of_names << "\n";
            for (std::size_t i = 0; i < result_.exports.function_names.size() && i < 10; ++i) {
                oss << "    - " << result_.exports.function_names[i] << "\n";
            }
            if (result_.exports.function_names.size() > 10) {
                oss << "    ... 还有 " << (result_.exports.function_names.size() - 10) << " 个函数\n";
            }
        }

        // 输出重定位信息
        oss << "\n[重定位表]\n";
        if (result_.relocations.empty()) {
            oss << "  (无重定位)\n";
        }
        else {
            oss << "  块数量: " << std::dec << result_.relocations.size() << "\n";
            for (std::size_t i = 0; i < result_.relocations.size(); ++i) {
                const auto& block = result_.relocations[i];
                oss << "  块 " << i << ": VA=0x" << std::hex << block.virtual_address
                    << ", 大小=0x" << block.size_of_block << "\n";
                oss << "    条目数: " << std::dec << block.entries.size() << "\n";
                for (std::size_t j = 0; j < block.entries.size() && j < 5; ++j) {
                    oss << "      条目 " << j << ": 类型=0x" << std::hex
                        << static_cast<int>(block.entries[j].type)
                        << ", 偏移=0x" << std::setw(3) << std::setfill('0')
                        << block.entries[j].offset << "\n";
                }
                if (block.entries.size() > 5) {
                    oss << "      ... 还有 " << (block.entries.size() - 5) << " 个条目\n";
                }
            }
        }

        // 返回构建的字符串
        return oss.str();
    }

}  // namespace pe_parser

main.cpp

cpp 复制代码
// 包含PE解析器头文件
#include "pe_parser.h"

// 包含必要的标准头文件
#include <iostream>  // 提供输入/输出流操作
#include <string>    // 提供字符串类

// 主函数
// 参数: argc - 命令行参数个数
//       argv - 命令行参数数组
// 返回值: 成功返回0,失败返回1
int main(int argc, char* argv[]) {
    // 检查命令行参数是否正确
    if (argc != 2) {
        // 输出使用说明
        std::cerr << "使用方法: PEParser <pe文件路径>" << std::endl;
        return 1;  // 返回错误码1
    }

    // 获取PE文件路径
    const std::string file_path = argv[1];

    try {
        // 创建PE解析器实例
        pe_parser::PeParser parser;
        // 加载PE文件
        parser.LoadFile(file_path);
        // 解析PE文件
        parser.Parse();
        // 输出解析结果
        std::cout << parser.ToString() << std::endl;
    }
    catch (const std::exception& e) {
        // 捕获并输出异常信息
        std::cerr << "错误: " << e.what() << std::endl;
        return 1;  // 返回错误码1
    }

    return 0;  // 返回成功码0
}
相关推荐
Z文的博客2 小时前
SLCAN工程搭建与实现教程(下)
stm32·单片机·嵌入式·can
老师用之于民3 小时前
【DAY39】Linux 驱动开发关键技术研究:设备树、Input 子系统与 I2C 通信
单片机·嵌入式硬件
发发就是发3 小时前
触摸屏驱动调试手记:从I2C鬼点到坐标漂移的实战录
linux·服务器·驱动开发·单片机·嵌入式硬件
芯岭技术郦3 小时前
XL32F001 单片机产品简介
单片机·嵌入式硬件
发发就是发3 小时前
I2C适配器与算法:从一次诡异的时序问题说起
服务器·驱动开发·单片机·嵌入式硬件·算法·fpga开发
阿凉07023 小时前
STM32 Flash 扇区分布学习
stm32·嵌入式硬件·学习
qq_429499574 小时前
STM32C011
stm32·单片机·嵌入式硬件
不怕犯错,就怕不做4 小时前
(Hisilicon)笔试题:嵌入式Linux C语言GPIO中断与按键消抖(转载)
linux·驱动开发·嵌入式硬件
Jason_zhao_MR4 小时前
STM32MP135F安全芯引入!米尔MYD-YF13X系统、安全、功能三重升级
stm32·嵌入式硬件·安全·嵌入式