5_区块表与区块

5 区块表与区块

区块表与区块

这一节我们结合实例来谈谈区块表的定义以及各个属性的含义.

首先, 我们先用之前学过的一点知识在二进制文件中手动翻找区块表.

这样做的好处是可以使你很快地对 PE 结构牢记于心.

学来的东西就是能用的东西, 不能用的理论就是空谈, 是瞎扯.

这里我们经过千辛万苦终于找到了我们的区块表, 以后会教大家写一个自己的工具, 让工具去找, 现在让大家自己动手是为了增强感觉.

typedef struct IMAGE_SECTION_HEADER
{
    BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //节表名称, 如 `.text`
    // 这是个数组
    // IMAGE_SIZEOF_SHORT_NAME = 8

    union{
        DWORD PhysicalAddress;  // 物理地址
        DWORD VitualSize;   // 真实长度, 这两个值是一个联合结构, 可以使用其中的任何一个, 一般是取后面一个
    }Misc;

    DWORD VitualAddress;    // 节区的 RVA 地址
    DWORD SizeOfRawData;    // 在文件中对齐后的尺寸
    DWORD PointerToRawData; // 在文件中的偏移量
    DWORD PointerToRelocations; // 在 OBJ 文件中使用, 重定位的偏移
    DWORD PointerToLinenumbers; // 行号表的偏移 (供调试使用地)
    WORD NumberOfRelocations;   // 在 OBJ 文件中使用, 重定位项数目
    WORD NumberOdLinenumbers;   // 行号表中行号的数目
    DWORD Characteristics;  // 节属性, 如可读, 可写, 可执行等
}IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
  • Name : 区块名. 这是一个有 8 位的 ASCII 码名, 用来定义区块的名称. 多数区块名都习惯性以一个 "." 作为开头 (例如 : .text), 但这个点实际上不是必须的.
    值得我们注意的是, 如果区块名超过 8 个字节, 则没有最后的终止标志 "NULL" (也就是 00) 字节.
    并且前边有这个 "$" 符号的区块名字会从链接器那里得到特殊的待遇, 就是前边带有 "$" 的相同名字的区块在载入的时候会被合并, 在合并之后的区块中, 它们是按照 "$" 符号后边的字符的字母顺序进行合并的.

每个区块的名称都是唯一的, 不能有同名的两个区块. 但事实上节的名称不代表任何意义, 它的存在仅仅是为了正规统一编程的时候方便程序员查看而设置的一个标记而已. 所以将包含代码的区块命名为 ".Data" 和把包含数据的区块命名为 ".Code" 都是合法的.

因此, 建议大家 : 当我们要从 PE 文件中读取需要的区块的时候, 不能以区块的名称作为定位的标准和依据, 而是要按照 IMAGE_OPTIONAL_HEADER32 结构中的数据目录字段进行定位.

  • Vitual Size : 该表对应的区块的大小, 这是区块的数据在没有进行对齐处理之前的实际大小.

  • Vitual Address : 该区块装载到内存中的 RVA 地址, 这个地址是按照内存页来对齐的. 因此它的数值总是 SectionAlignment 的值的整数倍. 在 Microsoft 工具中, 第一个块的默认 RVA 总为 1000H. 在 OBJ 中, 该字段没有意义, 被设置为 0.

  • SizeOfRawData : 该区块在磁盘中所占的大小. 在可执行文件中, 该字段是已经被 FileAlignment 潜规则处理过的长度.

  • PointerToRawData : 该区块在磁盘中的偏移. 这个数值是从文件头开始算起的偏移量.

  • PointerToRelocations : 这哥们在 exe 文件中没有意义, 在 obj 文件中, 表示本区块重定位信息的偏移值. (在 obj 文件中如果不是 0, 它会指向一个 IMAGE_RELOCATION 结构的数组)

  • PointerToLinenumbers : 行号表在文件中的偏移值, 文件的调试信息, 于我们没用.

  • NumberOfRelocations : 这哥们在 exe 文件中也没有意义, 在 obj 文件中, 是本区块在重定位表中的重定位数目来着.

  • NumberOfLinenumbers : 该区块在行号表中的行号数目, 鸡肋.

  • Characteristics : 该区块的属性. 这个字段是 按位 来指出区块的属性 (如代码/数据/可读/可写等) 的标志


最重要的其实是 SizeOfRawData 大小, 和 PointerToRawData 偏移量, 这两个加起来就是下一个区块的起始地址 (相对于 PE 文件头的偏移量)

还有个重要的就是 Characteristics 特征属性

在 windows 中每个载入内存中的区块都有各自的属性, 区块就是属性的结合

小甲鱼以下列出最常用的一些区块属性值 :

具体内容可以参考 MSDN 在线文档

都是宏定义, 定义在 winnt.h 里面

属性名称与属性值 解释
IMAGE_SCN_CNT_CODE The section contains executable code.
0*0000 0020 这个区块包含代码, 常与 0*1000 0000一起设置
IMAGE_SCN_CNT_INITIALIZED_DATA The section contains initialized data.
0*0000 0040 该区块包含初始化的数据.
IMAGE_SCN_CNT_UNINITIALIZED_DATA The section contains uninitialized data.
0*0000 0080 该区块包含未初始化的数据.
IMAGE_SCN_MEM_DISCARDABLE The section can be discarded as needed.
0*0200 0000 该区块可以被丢弃, 因为当它一旦被装入内存后, 进程就不再需要它了, 典型例子就是重定位区块.
IMAGE_SCN_MEM_SHARED The section can be shared in memory.
0*1000 0000 该区块为共享区块
IMAGE_SCN_MEM_EXECUTE The section can be executed as code.
0*2000 0000 该区块可以执行. 通常当 0*00000020 被设置的时候, 该标志位也被设置.
IMAGE_SCN_MEM_READ The section can be read.
0*4000 0000 该区块可读, 可执行文件中的区块总是设置该标志
IMAGE_SCN_MEM_WRITE The section can be written to.
0*8000 0000 该区块可写.

例如有一个区块, 属性值为 0*60000020, 工具算出来它具有 CODE, MEM_EXECUTE 和 MEM_READ 三个属性.

那么工具是如何计算的呢 ?

CODE 属性值是 0*00000020, MEM_EXECUTE 属性值是 0*20000000, MEM_READ 属性值是 0*40000000

事实上, 计算过程就是把它们几个数互相依次使用 or 运算即可. 也就是 0*00000020 or 0*20000000 or 0*40000000 = 0*60000020