PE解析器的编写(三)——区块表的解析

PE文件中所有节的属性都被定义在节表中,节表由一系列的IMAGE_SECTION_HEADER结构排列而成,每个结构用来描述一个节,结构的排列顺序和它们描述的节在文件中的排列顺序是一致的。
具有相同属性的数据被安排到同一个区块中。
区块表的结构为IMAGE_SECTION_HEADER,在PE文件中存在一个该结构的数组,用来保存各个区块的信息,这个数组的大小在PE头的结构 IMAGE_NT_HEADERS 的成员NumberOfSections描述。
区块表结构IMAGE_SECTION_HEADER结构如下:

typedef struct _IMAGE_SECTION_HEADER 
{
       BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 节表名称,如“.text”
        //IMAGE_SIZEOF_SHORT_NAME=8
        union
         {
                DWORD PhysicalAddress;      // 物理地址
                DWORD VirtualSize;          // 真实长度,这两个值是一个联合结构,可以使用其中的任何一个,一般是取后一个
        } Misc;
        DWORD VirtualAddress;               // 节区的 RVA 地址        
        DWORD SizeOfRawData;                // 在文件中对齐后的尺寸     
        DWORD PointerToRawData;             // 在文件中的偏移量        
        DWORD PointerToRelocations;         // 在OBJ文件中使用,重定位的偏移  
        DWORD PointerToLinenumbers;         // 行号表的偏移(供调试使用地)
        WORD NumberOfRelocations;           // 在OBJ文件中使用,重定位项数目
        WORD NumberOfLinenumbers;           // 行号表中行号的数目
        DWORD Characteristics;              // 节属性如可读,可写,可执行等
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

在程序中我们主要列出了,区块名称、RVA、在进行内存对齐后的尺寸,在磁盘中的大小,在文件中的偏移,节属性。
在界面中,定义了一个listctrl来显示这些信息。
在CPeFileInfo类中定义了一个vector m_SectionTable;专门用来存储区块表的属性信息。获取这个信息。
在这个类中与区块表有关的函数主要有两个:
GetSectionHeader : 用来获取指向表的指针
InitSectionTable:初始化上面定义的结构
下面来一一说明这两个函数

PIMAGE_SECTION_HEADER CPeFileInfo::GetSectionHeader()
{
    PIMAGE_FILE_HEADER pFileHeader = GetFileHeader();
    PIMAGE_SECTION_HEADER pSectionHeader = NULL;
    if (NULL == pFileHeader)
    {
        return NULL;
    }
    PIMAGE_OPTIONAL_HEADER pOptionHeader = GetOptionalHeader();
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)(pOptionHeader) + pFileHeader->SizeOfOptionalHeader);
    return pSectionHeader;

//  PIMAGE_NT_HEADERS pNtHeader = GetNtHeaders();
//  return (PIMAGE_SECTION_HEADER)((DWORD)pNtHeader + sizeof(IMAGE_NT_HEADERS));
}

在PE文件中区块表的属性信息是紧密排列在PE头结构后面的,所以我们只要知道OptionHeader结构的指针,然后加上这个结构的大小就可以获取到区块表的地址,上面的代码也是这样做的,首先获取了FileHeader的指针,这个结构中的SizeOfOptionalHeader定义了OptionHeader这个结构的大小,我们利用FileHeader + SizeOfOptionalHeader这样就偏移到了区块表所在的位置。
或者更简单的方式是利用PE文件头的地址 + 文件头的大小也一样可以获取到区块表的地址

void CPeFileInfo::InitSectionTable()
{
    if (!m_SectionTable.empty())
    {
        return ;
    }
    PIMAGE_SECTION_HEADER pSectionHeader = GetSectionHeader();
    PIMAGE_FILE_HEADER pFileHeader = GetFileHeader();
    if (NULL != pSectionHeader && NULL != pFileHeader)
    {
        DWORD dwCountOfSection = pFileHeader->NumberOfSections;
        int nCount = 0;
        while (nCount < dwCountOfSection)
        {
            IMAGE_SECTION_HEADER ImageSec = pSectionHeader[nCount];
            m_SectionTable.push_back(ImageSec);
            nCount++;
        }
    }
}

后面就是循环遍历将所有信息写入m_SectionTable这个动态数组中。在这份代码中我们首先利用FileHeader的NumberOfSections成员获取区块表的个数,然后在循环中以这个个数作为条件,以此往后寻址,将信息写入到对应的数组中,最后在输出的时候只需要根据需求输出我们感兴趣的内容即可

posted @ 2017-10-24 20:55  masimaro  阅读(273)  评论(0编辑  收藏  举报