深入学习PE文件结构系列三Data-Directory-Section-Headers-Section

admin 2026-02-06 01:41:11 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细解析PE文件结构中的数据目录、节及节头。阐述了数据目录作为特定功能数据索引的16种类型,区分了节在内存与磁盘中的大小差异,并深度剖析节头结构体各字段含义与节属性,为理解PE文件映射机制提供了技术基础。 综合评分: 80 文章分类: 二进制安全,逆向分析


cover_image

深入学习PE文件结构系列三Data-Directory-Section-Headers-Section

原创

ybdt ybdt

卡卡罗特取西经

2026年2月5日 12:27 吉林

0x1 前言

本篇文章会详细讲述Data Directory(数据目录)、Section Headers(节头)、Section(节),从概念、作用到具体的结构体定义,话不多说开始正文

0x2 Data Directory

Data Directory是PE Optional Header中的最后一个成员,本质是IMAGE_DATA_DIRECTORY结构体数组,定义如下

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

其中IMAGE_NUMBEROF_DIRECTORY_ENTRIES是一个常量,值是16,结构体定义如下

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

这是一个简单的结构体,只有2个成员,第一个成员存储地址,第二个成员存储大小

那Data Directory是什么?简单说,它存储的是一段特定功能数据的索引和大小,注意这段特定功能数据不等于节,不要和节搞混,它是节中的一段数据,一共有16个索引,比如说导出表这段特定功能的数据,用于对外暴露可被外部调用的函数,它位于.rdata节(只读初始化数据节),全部索引及特定功能如下

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // 导出表
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // 导入表
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // 资源表
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // 异常表
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // 安全目录
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // 重定位表
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // 调试目录
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // 架构相关
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // 全局指针
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS目录
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // 加载配置
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // 绑定导入表
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // 导入地址表
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // 延迟导入表
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM描述符
保留                                          15   // 保留

上述16段数据,经常用到的有导出表、导入表、重定位表、其中导入表位于.rdata节中,里面装的是程序需要用到的外部函数的名字,重定位表位于.reloc节中,里面装的是需要修复的地址,我们看一个实际PE文件的Data Directory部分

image

如果Address和Size都为0的话,表示没有这部分数据

0x3 Section

简单说,节就是PE文件中存储数据的容器,它们占据了除DOS Header、NT Headers、Section Headers外,PE文件中剩余的全部内容,常见的节如下

  • • .text:包含程序的可执行代码
  • • .data:包含初始化数据
  • • .bss:包含未初始化数据
  • • .rdata:包含只读初始化数据
  • • .edata:包含导出表(有时没有这个节,导出表位于.rdata中)
  • • .idata:包含导入表(有时没有这个节,导入表位于.rdata中)
  • • .reloc:包含重定位信息
  • • .rsrc:包含程序使用的资源信息
  • • .tls:为程序的每个线程提供存储

我们看一个实际PE文件的各个节

image

0x4 Section Headers

PE Optional Header之后,Section之前,中间这部分,就是Section Headers,它包含着PE中各个节的信息

Section Headers本质是一个IMAGE_SECTION_HEADER结构体数组,定义位于winnt.h中,其中结构体IMAGE_SECTION_HEADER定义如下

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;
    DWORD   PointerToRawData;
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

Name:这个字段装的是节的名字,其中IMAGE_SIZEOF_SHORT_NAME是一个常量,值为8,表示名字最长为8个字符 PhysicalAddress、VirtualSize:这个字段是一个联合体,定义2个名字,其实指向同一个东西,表示PE文件载入到内存后,节在内存中的大小 VirtualAddress:PE文件被载入内存后,节的首地址相对于PE文件内存基址的偏移,对于Object文件,这个字段表示重定位被应用前,相对于基址的偏移 SizeOfRawData:这个字段表示节在磁盘上的大小,它必须是IMAGE_OPTIONAL_HEADER.FileAlignment的整数倍 PointerToRawData:磁盘上PE文件中的节,相对于文件起始地址的偏移 PointerToRelocations:节中重定位项开始地址的指针,对于PE文件来说通常不涉及,字段值为0 PointerToLineNumbers:节中COFF行号项开始地址的指针,由于不鼓励COFF调试信息,字段值为0 NumberOfRelocations:节中重定位项的数量,对于PE文件来说,字段值为0 NumberOfLinenumbers:节中COFF行号项的数量,由于不鼓励COFF调试信息,字段值为0 Characteristics:用于描述节属性,比如节是否包含可执行代码、是否初始化数据、映射到内存后是否可被共享,完整的属性列表可以查看:https://learn.microsoft.com/en-us/windows/win32/debug/pe-format

需要注意VirtualSize和SizeOfRawData是有区别的

  • • VirtualSize是PE文件在内存中的大小,SizeOfRawData是PE文件在磁盘中大小,在磁盘中需要遵循FileAlignment对齐,如果文件大小不足FileAlignment的整数倍,需要填充0,但是PE文件映射到内存后,VirtualSize的大小是PE文件FileAlignment对齐前的大小,这个时候VirtualSize是小于SizeOfRawData
  • • 还有相反的情况,如果PE文件被载入到内存的同时,未初始化数据被初始化了数据,这个时候VirtualSize是大于SizeOfRawData

如下是实际PE文件中的Section Headers

image

如图所示,原始地址就是PointerToRawData,原始大小就是SizeOfRawData,虚拟地址就是VirtualAddress,虚拟大小就是VirtualSize,如果你尝试加一下原始地址和原始大小,虚拟地址和虚拟大小,会发现正好等于下一个节的原始地址和虚拟地址

特征字段表示节是只读的,还是读写的,还是读写执行的


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:卡卡罗特取西经 ybdt ybdt《深入学习PE文件结构系列三Data-Directory-Section-Headers-Section》

评论:0   参与:  0