QQ登录

只需一步,快速开始

PE文件结构之节表的组成及编程读取

[ 复制链接 ]
前面几个帖子介绍了PE文件头几个部分的组成及代码上的读取。
这里介绍PE头最后一个部分的读取,节表的读取及成员含义。
下图可以帮助回顾下PE文件的大概组成。
2019-12-23_135842.jpg
这帖子介PE文件头最后组成部分:节表。
节表信息读取代码在前面帖子基础上添加实现,例程界面如下:
2019-12-23_135338.jpg
打开一PE文件后,点击界面的节表按钮,可以显示PE文件的节表信息。
节表信息由报表形式显示,双击报表单元格可以弹出窗口进行内容的修改。

节表紧跟在PE文件头后,也就是结构体IMAGE_NT_HEADER后就为节表数据,在编程上还是很容易定位。
节表是由多个节表项组成的,每个节表项对应一个结构体IMAGE_SECTION_HEADER.
具体由几个节表项组成?
它由标准PE头中的成员NumberOfSections指定。
那么节表的定位与节表项个数如何通过代码得到呢?
可以参考例程代码:
  1. void CSectionHeaderDlg::UpdateData(PIMAGE_DOS_HEADER pDosHeader,bool bSetGet)
  2. {
  3.         if(pDosHeader == NULL)
  4.                 return;
  5.         PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS )((char *)pDosHeader+pDosHeader->e_lfanew);       
  6.         int nSectionNum = pNtHeader->FileHeader.NumberOfSections;
  7.         PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
复制代码

例如程序中通过PE文件的dos_mz头获取pe头,pe头中的标准pe头成员NumberOfSections就为节表项个数。通过宏IMAGE_FIRST_SECTION可以快速获取第一个节表项地址。
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
此宏可以选择后按F12定位到定义:
  1. #define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER)        \
  2.     ((ULONG_PTR)(ntheader) + FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + ((ntheader))->FileHeader.SizeOfOptionalHeader ))
复制代码
宏表示3部分相加和。
1、IMAGE_NT_HEADERS的起始地址
2、IMAGE_OPTIONAL_HEADER32 (PE扩展头)在IMAGE_NT_HEADERS中的偏移
3、IMAGE_OPTIONAL_HEADER32的大小
这里FIELD_OFFSET宏输出可选PE头OptionalHeader在IMAGE_NT_HEADERS结构体中的偏移。
后两个加起来的大小恰好就是IMAGE_NT_HEADERS的大小,再跟第一个相加就得到Session Table的地址。
这里没有直接使用sizeof(IMAGE_NT_HEADERS)来偏移指向节表地址,
是因为IMAGE_OPTIONAL_HEADER32(OptinalHeader)的大小不固定,
默认情况,
32位PE文件,这个值等于00e0h,
64位PE文件,这个值大小为00f0h。
还可以由我们通过标准PE头的SizeOfOptionalHeader 字段指定。


可以定位第一个节表项IMAGE_SECTION_HEADER,以及知道节表项IMAGE_SECTION_HEADER的个数,
就可以循环访问每个节表项的成员了。
例程中通过一个函数将PE文件中全部节表项成员都显示在了界面上:
  1. <blockquote>void CSectionHeaderDlg::UpdateData(PIMAGE_DOS_HEADER pDosHeader,bool bSetGet)
复制代码

代码是实现了每个节表项IMAGE_SECTION_HEADER成员的访问与显示或修改。但节表项每个成员的含义是什么呢?
IMAGE_SECTION_HEADER的定义如下:
  1. typedef struct _IMAGE_SECTION_HEADER {
  2.     BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
  3.     union {
  4.             DWORD   PhysicalAddress;
  5.             DWORD   VirtualSize;
  6.     } Misc;
  7.     DWORD   VirtualAddress;
  8.     DWORD   SizeOfRawData;
  9.     DWORD   PointerToRawData;
  10.     DWORD   PointerToRelocations;
  11.     DWORD   PointerToLinenumbers;
  12.     WORD    NumberOfRelocations;
  13.     WORD    NumberOfLinenumbers;
  14.     DWORD   Characteristics;
  15. } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
复制代码
1.Name:
8字长长度。
表示节表项名称,默认以'.'结尾,如“.text”。名称无意义,可随意,但要保证每个节表项名称不重复。
另外要注意读取时请自行添加字符串结束标识‘/0'.

2.Misc:
双字长度。
共用体结构,其VirtualSize表示节数据在没有进行对齐处理前的实际大小。

3.VirtualAddress:
双字长度。
节的RVA地址。

4.SizeOfRawData:
双字长度。
文件对齐后,节大小。

5.PointerToRawData:
双字长度。
节起始地址在文件中的偏移。

6.PointerToRelocations:
双字长度。
未用到,主要在.obj文件,作为指向重定位表的指针用。

7.NumberOfRelocations:
单字长度。
未用到。主要在.obj文件,作为重定位表的个数用。

8.PointerToLinenumbers:
双字长度。
未用到。在调试用,表示行号表位置。


9.NumberOfLinenumbers:
单字长度。
未用于。表示行号数量。

10.Characteristics:
双字长度。
表示节的标志,很重要。32个二进制位分别表示不同属性,可或操作组合使用。
个别位系统保留,个别位为0,下面是常用的位介绍。
IMAGE_SCN_CNT_CODE                                 0x00000020  包含代码,常与 0x10000000一起设置。IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  包含以初始化的数据。IMAGE_SCN_CNT_UNINITIALIZED_DATA  0x00000080  包含未初始化的数据。IMAGE_SCN_MEM_DISCARDABLE                 0x02000000  进程载入后,可被丢弃。IMAGE_SCN_MEM_SHARED                           0x10000000   为共享区块。IMAGE_SCN_MEM_EXECUTE                         0x20000000   可以执行。IMAGE_SCN_MEM_READ                               0x40000000   可读,可执行文件中的区块总是设置该标志。IMAGE_SCN_MEM_WRITE                            0x80000000   可写。
节表项对应描述一个节表的信息,通过这些信息可以定位一个节表,进而读取节表中数据。节表有很种类,导入表,导出表,栈与重定位表等等。会在后面帖子介绍与通过代码读取。例程下载地址VS2010,mfc 编写:
游客,为过滤非法行为,全站隐藏资源仅对充值会员开放进入升级


回复

使用道具 举报

快速回复 返回列表 客服中心 搜索