PE文件(三)

本文介绍了PE文件中的节表,它包含PE文件的属性信息,如代码段、数据段等。节表用于相对虚拟地址(RVA)到文件偏移(Offset)的转换,是解析数据目录表的基础。通过节表可以实现RVA到Offset的计算,进而理解PE文件的加载和数据解析过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写图片描述
节表,又叫区段表,区段表是我们直接探索的最后位置了,区段是不需要直接解析的地方。
区段表存储着PE文件的一些属性,区段表是一个由若干个结构体依次排列组成的,每一个结构体代表着PE文件主体中的一段数据的属性,也就是每一个区段头都对应着PE文件主体的一段数据,这段数据叫做节或者叫区段,区段表规定了区段(节)的属性。
下面是节的定义

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;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;//这一区段的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

区段表是由多个这样的结构体组成的,以一个全是0的结构体结尾
关于区段(节)名字的规矩如下 :
.text :一般是代码段
.data :一般是数据段
.bss : 表示未初始化的数据
.rdata : 表示只读的数据
.textbss :和代码相关
.idata 和edata :储存导入表和导出表的信息
.rsrc :储存资源的区段(节)
.relcoc : 储存重定位信息的区段(节)
那么这个区段(节)表到底有什么用呢?
其实这个区段表就是解析数据目录表的基础,用于 相对虚拟地址 (RVA)与文件偏移(Offectset)的转换。
在此之前的结构体中出现了很多的RVA,其实这些地址都落在了PE文件主体的某一个区段中,当我们想在文件中找到这个位置的时候,却不能直接用这个相对虚拟地址,而是需要一定的转换。
要转换的相对虚拟地址一定会落在一个区段中,我们想要知道落在哪一个区段中,就需要分别与各个区段起始的相对虚拟地址做比较(也就是与结构体中的VirtualAddress做比较)。
如果落在一个区段中,就用要转换的相对虚拟地址减去区段起始的相对虚拟基址,得到的是这个地址相对于这个区段的偏移,再用 这个偏移加上区段在文件中的起始位置,就是加上PointerToRawData,就得到了文件偏移。
Offect(转)=RVA(转)-RVA(区段)+Offect(区段)
我们可以用代码来实现,那么怎么找到区段头呢,系统提供了一个宏来方便的找到它的位置

#define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER)        \
    ((ULONG_PTR)(ntheader) +                                            \
     FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) +                 \
     ((ntheader))->FileHeader.SizeOfOptionalHeader   \
    ))

我们只需要调用 IMAGE_FIRST_SECTION(pNT)就可以了,参数是NT头的指针。

DWORD RvaToOffect(DWORD Rva, PBYTE buf)
{
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buf;//获取DOS头

    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buf);//获取NT头
    PIMAGE_SECTION_HEADER pHerder = IMAGE_FIRST_SECTION(pNt);//获取区段头
    DWORD dwNumber = pNt->FileHeader.NumberOfSections;
    if (Rva < pHerder[0].PointerToRawData)
    {
        return Rva;
    }
    for (int i = 0; i < dwNumber; i++)
    {
        if (
            (Rva >= pHerder[i].VirtualAddress) &&
            (Rva <= pHerder[i].VirtualAddress + pHerder[i].Misc.VirtualSize)
            )
        {
            return Rva - pHerder[i].VirtualAddress + pHerder[i].PointerToRawData;
        }
    }//循环比较在哪一个区段
}

上面的代码就是RVA和Offect的转换函数,这样我们就能去 解析前面没有 详细解析的 数据目录表。
到这里PE文件的头部信息就结束了,之后 就是PE文件 的主体部分,主体 部分在加载的时候就是根据区段表进行加载的一块一块的数据,一般不用去直接理会区段,因为所有PE的信息都已经包含在头中,我们可以通过头中的引导去解析主体中的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值