| 前面几个帖子介绍了PE文件头几个部分的组成及代码上的读取。 这里介绍PE头最后一个部分的读取,节表的读取及成员含义。
 下图可以帮助回顾下PE文件的大概组成。
 
   这帖子介PE文件头最后组成部分:节表。
 节表信息读取代码在前面帖子基础上添加实现,例程界面如下:
 
   打开一PE文件后,点击界面的节表按钮,可以显示PE文件的节表信息。
 节表信息由报表形式显示,双击报表单元格可以弹出窗口进行内容的修改。
 
 节表紧跟在PE文件头后,也就是结构体IMAGE_NT_HEADER后就为节表数据,在编程上还是很容易定位。
 节表是由多个节表项组成的,每个节表项对应一个结构体IMAGE_SECTION_HEADER.
 具体由几个节表项组成?
 它由标准PE头中的成员NumberOfSections指定。
 那么节表的定位与节表项个数如何通过代码得到呢?
 可以参考例程代码:
 
 复制代码void CSectionHeaderDlg::UpdateData(PIMAGE_DOS_HEADER pDosHeader,bool bSetGet)
{
        if(pDosHeader == NULL)
                return;
        PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS )((char *)pDosHeader+pDosHeader->e_lfanew);        
        int nSectionNum = pNtHeader->FileHeader.NumberOfSections;
        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定位到定义:
 
 宏表示3部分相加和。复制代码#define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER)        \
    ((ULONG_PTR)(ntheader) + FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + ((ntheader))->FileHeader.SizeOfOptionalHeader ))
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文件中全部节表项成员都显示在了界面上:
 
 复制代码<blockquote>void CSectionHeaderDlg::UpdateData(PIMAGE_DOS_HEADER pDosHeader,bool bSetGet)
代码是实现了每个节表项IMAGE_SECTION_HEADER成员的访问与显示或修改。但节表项每个成员的含义是什么呢?
 IMAGE_SECTION_HEADER的定义如下:
 
 1.Name:复制代码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;
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 编写:
 
 
 |