为什么要转
一个PE文件,在磁盘中以0x200(512)字节对齐,其余以0填充,而在内存中以0x1000(4KB)对齐。当它被从硬盘中加载到内存中时,其大小就会发生改变。为了确定导入导出表的物理地址,需要把其在内存中的偏移地址(RVA)换算成其在文件中的偏移(FOA)
怎么转
分情况:
- RVA在文件头(DOS 头 + PE 头 + 节表)时或者文件对齐和内存对齐相同时,由于DOS头和PE头和节表在文件中和在内存中展开都是一样的,所以此时RVA = FOA
- 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;
}
浙公网安备 33010602011771号