商城首页欢迎来到中国正版软件门户

您的位置:首页 >C#读取EXE/DLL文件头结构解析

C#读取EXE/DLL文件头结构解析

  发布于2026-02-09 阅读(0)

扫一扫,手机访问

必须先读取DOS头获取e_lfanew值定位PE签名,再根据Magic字段区分32/64位解析可选头,节表数量由NumberOfSections决定,结构体需[StructLayout(Sequential, Pack=1)]且字段类型严格匹配,RVA需转文件偏移。

C#解析Windows PE文件头 C#如何读取EXE或DLL的结构信息

System.IO.FileStream 读取 PE 头前必须跳过 DOS 头

PE 文件开头是 DOS 头(IMAGE_DOS_HEADER),它占 64 字节,但真正关键的 PE 签名("PE\0\0")在偏移 0x3C 处的 e_lfanew 字段指向的位置。直接从文件头开始解析会失败。

  • 先读取前 64 字节,解析出 e_lfanew 字段(4 字节小端整数)
  • 用该值作为偏移,定位到 PE 签名位置;若此处不是 0x00004550(即 "PE\0\0"),说明不是合法 PE 文件
  • DOS stub(DOS 占位程序)可能很长,但 PE 头永远从 e_lfanew 指向的位置开始,不是固定偏移 0x40

解析 IMAGE_FILE_HEADERIMAGE_OPTIONAL_HEADER 要区分 32/64 位

PE 文件可选头有两种:32 位用 IMAGE_OPTIONAL_HEADER32(共 224 字节),64 位用 IMAGE_OPTIONAL_HEADER64(共 240 字节)。仅靠文件扩展名(.exe/.dll)无法判断,必须读 IMAGE_FILE_HEADER.Machine 和可选头中的 Magic 字段。

  • IMAGE_FILE_HEADER.Machine0x014C(I386)或 0x8664(AMD64)等,但不决定可选头大小
  • 真正决定用哪个可选头的是紧接在 IMAGE_FILE_HEADER 后的 Magic 值:0x010B 表示 32 位,0x020B 表示 64 位
  • 忽略这个判断,硬按 32 位读 64 位文件,会导致后续所有字段错位(比如 AddressOfEntryPoint 解析成错误地址)

节表(Section Headers)数量由 NumberOfSections 决定,不能硬写死

节表紧跟在可选头之后,每个节头固定 40 字节(IMAGE_SECTION_HEADER),但节数量由 IMAGE_FILE_HEADER.NumberOfSections 给出——这个值通常为 3~6,但某些加壳或手工构造的 PE 可能只有 1 节或多达 10+ 节。

  • NumberOfSections * 40 计算节表总长度,再用 FileStream.Seek() 跳转到对应位置
  • 常见错误:假设只有 .text/.rdata/.data 三节,漏读 .reloc 或 .rsrc,导致资源或重定位信息丢失
  • 节名是 8 字节 ASCII(如 ".text\0\0\0"),需用 Encoding.ASCII.GetString() 并截断 \0,不能直接 ToString()

Marshal.PtrToStructure 解析结构体时注意字节对齐和字段顺序

C# 默认结构体布局是 Auto,会重排字段顺序并插入填充,而 PE 结构体是 C 风格的 Sequential 布局、1 字节对齐。不显式声明,解析结果全是错的。

  • 每个结构体必须加 [StructLayout(LayoutKind.Sequential, Pack = 1)]
  • 字段类型要严格匹配:比如 WORDUInt16DWORDUInt32PVOID 在 64 位 PE 中是 UInt64,不是 IntPtr
  • 避免用 string 直接映射字符数组;对 IMAGE_DOS_HEADER.e_lfanew 这种 4 字节字段,必须用 UInt32,不能用 Int32(符号扩展会污染高位)

最易被忽略的是:PE 头里大量字段是相对虚拟地址(RVA),不是文件偏移。比如 OptionalHeader.AddressOfEntryPoint 是 RVA,要查节表才能换算成实际文件位置——这个转换逻辑一旦写错,整个反汇编或资源提取就全偏了。

本文转载于:互联网 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注