2.4 PE结构:节表详细解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 节表(Section Table)是Windows PE/COFF格式的可执行文件中一个非常重要的数据结构,它记录了各个代码段、数据段、资源段、重定向表等在文件中的位置和大小信息,是操作系统加载文件时根据节表来进行各个段的映射和初始化的重要依据。节表中的每个记录则被称为`IMAGE_SECTION_HEADER`,它记录了一个段的各种属性信息和在文件中的位置和大小等信息,一个文件可以由多个`IMAGE_SECTION_HEADER`构成。

节表(Section Table)是Windows PE/COFF格式的可执行文件中一个非常重要的数据结构,它记录了各个代码段、数据段、资源段、重定向表等在文件中的位置和大小信息,是操作系统加载文件时根据节表来进行各个段的映射和初始化的重要依据。节表中的每个记录则被称为IMAGE_SECTION_HEADER,它记录了一个段的各种属性信息和在文件中的位置和大小等信息,一个文件可以由多个IMAGE_SECTION_HEADER构成。

在执行PE文件的时候,Windows 并不在一开始就将整个文件读入内存,PE装载器在装载的时候仅仅建立好虚拟地址和PE文件之间的映射关系,只有真正执行到某个内存页中的指令或者访问页中的数据时,这个页面才会被从磁盘提交到内存中,这种机制极大的节约了内存资源,使文件的装入速度和文件的大小没有太多的关系。

Windows 装载器在装载DOS部分PE文件头部分和节表部分时不进行任何处理,而在装载节区的时候会根据节的不同属性做不同的处理,一般需要处理以下几个方面的内容:

节区的属性: 节是相同属性的数据的组合,当节被装入内存的时候,同一个节对应的内存页面将被赋予相同的页属性,Windows系统对内存属性的设置是以页为单位进行的,所以节在内存中的对其单位必须至少是一个页的大小,对于X86来说这个值是4KB(1000h),而对于X64来说这个值是8KB(2000h),磁盘中存储的程序并不会对齐4KB,而只有被PE加载器载入内存的时候,PE装载器才会自动的补齐4KB对其的零头数据。

节区的偏移: 节的起始地址在磁盘文件中是按照IMAGE_OPTIONAL_HEADER结构的FileAhgnment字段的值对齐的,而被加载到内存中时是按照同一结构中的SectionAlignment字段的值对齐的,两者的值可能不同,所以一个节被装入内存后相对于文件头的偏移和在磁盘文件中的偏移可能是不同的。

节区的尺寸: 由于磁盘映像和内存映像的对齐单位不同,磁盘中的映像在装入内存后会自动的进行长度扩展,而对于未初始化的数据段(.data?)来说,则没有必要为它在磁盘文件中预留空间,只要可执行文件装入内存后动态的为其分配空间即可,所以包含未初始化数据的节在磁盘中长度被定义为0,只有在运行后PE加载器才会动态的为他们开辟空间。

不进行映射的节: 有些节中包含的数据仅仅是在装入的时候用到,当文件装载完毕时,他们不会被递交到物理内存中,例如重定位节,该节的数据对于文件的执行代码来说是透明的,他只供Windows装载器使用,可执行代码根本不会访问他们,所以这些节存在于磁盘文件中,不会被映射到内存中。

一般来说,当一个PE文件被编译生成时则默认会存在.text,.data等基本节表,而每一个节表都是由一个IMAGE_SECTION_HEADER结构排列而成,每个结构都用来描述一个节,节表总被存放在紧接在PE文件头的地方,也即是从PE文件头开始偏移为00f8h的位置,针对每一个节中的定义可查看节表结构体的定义;

typedef struct _IMAGE_SECTION_HEADER
{
   
   
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
   
   
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;           // 节区尺寸
    } Misc;
    DWORD   VirtualAddress;                // 节区RVA
    DWORD   SizeOfRawData;                 // 在文件中对齐后的尺寸
    DWORD   PointerToRawData;              // 在文件中的偏移
    DWORD   PointerToRelocations;          // 在OBJ文件中使用
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;               // 节区属性字段
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

针对IMAGE_SECTION_HEADER中各个字段的详细解析:

  • Name:段名,是一个8字节的ASCII字符串,不足8字节用0补齐。
  • VirtualSize:虚拟大小,标识在内存中占用的大小,请勿与PhysicalSize(物理大小)混淆。
  • VirtualAddress:虚拟地址,标识在内存中对应段头的地址,与实际加载的位置有关。
  • SizeOfRawData:物理大小,标识在PE文件中该段的占用大小,不足以文件对齐单位则会进行填充。
  • PointerToRawData:物理地址,标识该段在文件中的偏移位置。
  • PointerToRelocations:重定向表的偏移位置。
  • PointerToLinenumbers:行号表的偏移位置。
  • NumberOfRelocations:重定向表数量。
  • NumberOfLinenumbers:行号表数量。

  • Characteristics:标识该段的各种属性信息,包括下列常用属性:

    • IMAGE_SCN_MEM_READ:可读;
    • IMAGE_SCN_MEM_WRITE:可写;
    • IMAGE_SCN_MEM_EXECUTE:可执行;
    • IMAGE_SCN_CNT_CODE:代码段;
    • IMAGE_SCN_CNT_INITIALIZED_DATA:已初始化数据段;
    • IMAGE_SCN_CNT_UNINITIALIZED_DATA:未初始化数据段;
    • IMAGE_SCN_LNK_INFO:包含附加信息。

与数据目录表的枚举方式基本一致,数据目录表的枚举也不会太难,读者只需要通过NtHeader->FileHeader.NumberOfSections获取到当前有多少个节,并通过循环的方式依次得到这些节中的指针,并将该指针转换为PIMAGE_SECTION_HEADER结构,依次循环输出即可得到;

int main(int argc, char * argv[])
{
   
   
    BOOL PE = IsPeFile(OpenPeFile("c://pe/x86.exe"), 0);

    if (PE == TRUE)
    {
   
   
        printf("编号\t 节区名称\t虚拟偏移\t虚拟大小\t实际偏移\t实际大小\t节区属性\n");

        for (DWORD each = 0; each < NtHeader->FileHeader.NumberOfSections; each++, pSection++)
        {
   
   
            printf("%d\t %-9s\t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X \n",
                each + 1, pSection->Name, pSection->VirtualAddress, pSection->Misc.VirtualSize,
                pSection->PointerToRawData, pSection->SizeOfRawData, pSection->Characteristics);
        }
    }
    else
    {
   
   
        printf("非标准程序 \n");
    }

    system("pause");
    return 0;
}

运行上述程序,即可输出当前程序中存在的节表信息,输出效果如下图所示;

相关文章
|
23天前
|
人工智能
歌词结构的巧妙安排:写歌词的方法与技巧解析,妙笔生词AI智能写歌词软件
歌词创作是一门艺术,关键在于巧妙的结构安排。开头需迅速吸引听众,主体部分要坚实且富有逻辑,结尾则应留下深刻印象。《妙笔生词智能写歌词软件》提供多种 AI 功能,帮助创作者找到灵感,优化歌词结构,写出打动人心的作品。
|
1月前
|
机器学习/深度学习 搜索推荐 大数据
深度解析:如何通过精妙的特征工程与创新模型结构大幅提升推荐系统中的召回率,带你一步步攻克大数据检索难题
【10月更文挑战第2天】在处理大规模数据集的推荐系统项目时,提高检索模型的召回率成为关键挑战。本文分享了通过改进特征工程(如加入用户活跃时段和物品相似度)和优化模型结构(引入注意力机制)来提升召回率的具体策略与实现代码。严格的A/B测试验证了新模型的有效性,为改善用户体验奠定了基础。这次实践加深了对特征工程与模型优化的理解,并为未来的技术探索提供了方向。
92 2
深度解析:如何通过精妙的特征工程与创新模型结构大幅提升推荐系统中的召回率,带你一步步攻克大数据检索难题
|
15天前
|
机器学习/深度学习 自然语言处理 数据管理
GraphRAG核心组件解析:图结构与检索增强生成
【10月更文挑战第28天】在当今数据科学领域,自然语言处理(NLP)和图数据管理技术的发展日新月异。GraphRAG(Graph Retrieval-Augmented Generation)作为一种结合了图结构和检索增强生成的创新方法,已经在多个应用场景中展现出巨大的潜力。作为一名数据科学家,我对GraphRAG的核心组件进行了深入研究,并在此分享我的理解和实践经验。
38 0
|
21天前
光纤电缆(FOC)的结构深度解析
【10月更文挑战第21天】
37 0
|
1月前
|
存储 编译器 C++
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
|
2月前
|
数据采集 存储 JavaScript
如何使用Cheerio与jsdom解析复杂的HTML结构进行数据提取
在现代网页开发中,复杂的HTML结构给爬虫技术带来挑战。传统的解析库难以应对,而Cheerio和jsdom在Node.js环境下提供了强大工具。本文探讨如何在复杂HTML结构中精确提取数据,结合代理IP、cookie、user-agent设置及多线程技术,提升数据采集的效率和准确性。通过具体示例代码,展示如何使用Cheerio和jsdom解析HTML,并进行数据归类和统计。这种方法适用于处理大量分类数据的爬虫任务,帮助开发者轻松实现高效的数据提取。
如何使用Cheerio与jsdom解析复杂的HTML结构进行数据提取
|
3月前
|
存储 缓存 NoSQL
深入解析Memcached:内部机制、存储结构及在大数据中的应用
深入解析Memcached:内部机制、存储结构及在大数据中的应用
|
4天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
16 2
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
67 0
|
1月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
52 0

推荐镜像

更多