为什么要转

一个PE文件,在磁盘中以0x200(512)字节对齐,其余以0填充,而在内存中以0x1000(4KB)对齐。当它被从硬盘中加载到内存中时,其大小就会发生改变。为了确定导入导出表的物理地址,需要把其在内存中的偏移地址(RVA)换算成其在文件中的偏移(FOA)

怎么转

分情况:

  1. RVA在文件头(DOS 头 + PE 头 + 节表)时或者文件对齐和内存对齐相同时,由于DOS头和PE头和节表在文件中和在内存中展开都是一样的,所以此时RVA = FOA
  2. RVA不在文件头,就判断它在哪个节里面:
    • 判断方法:section的起始RVA <= RVA < section的起始RVA + section的大小
    • 转换FOA = RVA - section的起始RVA + 节表在文件中的偏移。不难看出,对于同一section内同一个RVA,在内存中的偏移(相对于section的,同下文)和在文件中的偏移是一样的,算出内存中的偏移就能知道文件中的偏移,加上一个section在文件中的地址就可以得出FOA。

以下是代码实现:

DWORD PEAnalyzer::RvaToFoa(DWORD rva)
{
    if (pNtHeader == nullptr || m_mapBase == nullptr)
    {
        QMessageBox::warning(this, "错误", "请先解析NT header或打开文件");
        return 0;
    }

    WORD magic = pNtHeader->OptionalHeader.Magic;
    pSection = nullptr;

    if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
    {
        pSection = IMAGE_FIRST_SECTION((PIMAGE_NT_HEADERS32)pNtHeader);
    }
    else if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
    {
        pSection = IMAGE_FIRST_SECTION((PIMAGE_NT_HEADERS64)pNtHeader);
    }
    else
    {
        return 0;
    }

    int sectionCount = pNtHeader->FileHeader.NumberOfSections;

    for (int i = 0; i < sectionCount; i++)
    {
        DWORD sectionRVA = pSection->VirtualAddress;
        DWORD sectionSize = pSection->Misc.VirtualSize;
        if (sectionSize == 0)
        {
            sectionSize = pSection->SizeOfRawData;
        }

        if (rva >= sectionRVA && rva < (sectionRVA + sectionSize))
        {
            DWORD Foa = rva - sectionRVA + pSection->PointerToRawData;
            return Foa;
        }

        pSection++;
    }
    return rva;
}
posted on 2026-05-07 13:43  %HuTao%  阅读(2)  评论(0)    收藏  举报