数据目录(Data Directory)

数据目录位于 IMAGE_OPTIONAL_HEADER 的末尾,是一个由 IMAGE_DATA_DIRECTORY 结构体数组构成的表。每个数据目录项描述了 PE 文件中某一特定数据结构的偏移和大小。常见的数据目录包括:

  • IMAGE_DIRECTORY_ENTRY_EXPORT(导出表)
    • 作用:记录模块导出的函数和数据。对于 DLL 来说,这部分内容非常关键,系统及其他模块通过它来调用 DLL 中的接口。
  • IMAGE_DIRECTORY_ENTRY_IMPORT(导入表)
    • 作用:记录模块依赖的外部函数和数据。通过导入表,操作系统能够在加载时将所需的外部函数地址填入模块的导入地址表。
  • IMAGE_DIRECTORY_ENTRY_IAT(导入地址表)
    • 作用:保存导入函数的实际地址,加载器在加载时会根据导入表将相应地址写入 IAT,供程序调用时直接使用。
  • IMAGE_DIRECTORY_ENTRY_RESOURCE(资源表)
    • 作用:存放程序所用的各种资源,如图标、对话框、字符串等。
  • IMAGE_DIRECTORY_ENTRY_BASERELOC(重定位表)
    • 作用:当模块加载到非预设基地址时,重定位表中存储的条目会用于修正代码中的绝对地址。
  • IMAGE_DIRECTORY_ENTRY_TLS(线程局部存储)
    • 作用:提供与线程相关的初始化数据,为每个线程分配独立的存储空间。
  • IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT(绑定导入表)
    • 作用:通过提前解析导入模块的地址,可以提高加载速度。它记录了导入库在上一次构建时的地址信息,加载器可以快速验证是否需要重新绑定。
  • IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT(延时加载)
    • 作用:使部分导入的 DLL 在程序运行期间按需加载,从而加快程序启动速度,降低初始加载开销。
  • IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR(.NET 表)
    • 作用:对于 .NET 应用程序,该目录记录了 CLR(公共语言运行时)相关的数据,如元数据、IL 代码等。
  • IMAGE_DIRECTORY_ENTRY_EXCEPTION(异常表)
    • 作用:存放有关异常处理的相关数据,供异常处理机制使用(主要用于 64 位平台)。

通过数据目录,加载器能够定位各个模块的重要数据区域,完成地址重定位、资源加载、导入解析等工作。

节表(Section Header)

PE 文件的节表由一组 IMAGE_SECTION_HEADER 结构体构成,每个结构体对应文件中的一个节。节的作用类似于模块中的一个逻辑区域(例如代码、数据、资源等),它既描述了文件在磁盘中的存储位置,也描述了在内存中加载时的布局。

节表中常见字段

  • Name[8]

    • 描述:节的名称(例如 .text.data.rsrc)。
  • VirtualSize

    • 描述:节在内存中所占的大小,不一定与文件中的大小一致。如果内存大小比文件大小大,多余部分加载后一般会置零。
  • VirtualAddress

    • 描述:节加载到内存时的起始地址,表示相对于 ImageBase 的偏移(即 RVA)。
  • SizeOfRawData

    • 描述:节在文件中实际存储的数据大小。
  • PointerToRawData

    • 描述:节在文件中的起始偏移(文件地址 FA)。
  • PointerToRelocations

    • 描述:指向重定位信息的偏移(一般对可重定位文件有效)。
  • PointerToLinenumbers

    • 描述:指向行号信息的偏移,主要用于调试。
  • NumberOfRelocations

    • 描述:节中包含的重定位条目数量。
  • NumberOfLinenumbers

    • 描述:节中包含的行号信息数量。
  • Characteristics

    • 描述

      :描述该节的属性,如可读、可写、可执行等。常用标志包括:

      • IMAGE_SCN_MEM_SHARED (0x10000000)
        • 表示节可以被多个进程共享。
      • IMAGE_SCN_MEM_EXECUTE (0x20000000)
        • 表示节包含可执行代码。
      • IMAGE_SCN_MEM_READ (0x40000000)
        • 表示节是可读的。
      • IMAGE_SCN_MEM_WRITE (0x80000000)
        • 表示节是可写的。

地址概念

  • FA(File Address)
    • 表示在磁盘文件中的偏移位置。例如,PointerToRawData 就是以 FA 表示的。
  • RVA(Relative Virtual Address)
    • 表示相对于 ImageBase 的偏移量。当模块加载到内存中后,每个节的实际内存地址 = ImageBase + RVA。
  • VA(Virtual Address)
    • 通常指节加载后的内存地址,即基地址加上 RVA,反映程序运行时的地址空间布局。

通过节表,操作系统能够将文件中的各个部分正确地映射到内存,并根据节的属性设置适当的访问权限,保证程序能够正常运行。

总结

  • 数据目录
    • 作为 PE 文件的“目录”,它记录了文件中各类重要数据区域的地址和大小。常见的目录包括导出表、导入表、IAT、资源表、重定位表、TLS、绑定导入、延时加载、.NET 相关表以及异常表。
    • 加载器通过数据目录快速定位这些区域,完成 DLL 导入、重定位和资源加载等工作。
  • 节表
    • 描述了文件中各个逻辑区域(节)的属性和存储布局。
    • 每个节包括名称、在内存和文件中的大小与位置、以及访问属性等信息。
    • 通过节表,可以区分代码、数据、资源等部分,并确保加载时正确映射到内存,同时设置合适的访问权限。
  • 地址概念(FA、RVA、VA)
    • FA 用于描述文件中的位置;
    • RVA 表示相对于 ImageBase 的偏移;
    • VA 则是实际的内存地址(ImageBase + RVA)。