VA: 虚拟内存地址(Virtual Address)PE 文件被操作系统加载进内存后的地址。
RVA: PE文件的相对虚拟地址(Relative Virual Address)是PE文件中的数据、模块等运行在内存中的实际地址相对PE文件装载到内存的基址之间的距离。举例说明,如果PE文件装入虚拟地址(VA)空间的400000h处,且进程从虚址401000h开始执行,我们可以说进程执行起始地址在RVA 1000h。
FOA: 文件偏移地址(File Offset Address),和内存无关,它是指某个位置距离文件头的偏移。
VA和RVA的转换
VA=ImageBase+RVA
RVA和FOA的转换
PE文件中的节等模块加载到内存时,节的数据布局和文件中的内存布局基本保持不变。所以可以根据这个数据位置相对不变的特点来由RVA正确换算出到数据相对文件的偏移。即,每个节(section)中的数据的起始位置相对节的起始位置是不变的,不管节是在文件中还是被加载到内存中。
1.判断指定的RVA在那个节中
2.求得该节的起始地址RVA
3.求出偏移量Offset=RVA-节起始RVA
4.FOA = Offset+该节在磁盘中的起始地址
数据的文件偏移=(数据RVA - 节RVA) + 节的文件偏移
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //8个字节的节区名称
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;
FOA=RVA-VirtualAddress+PointerToRawData
代码实现:
////////////////////////////////////////
// RVA转FOA函数 //
/////////////////////////////////////////
DWORD RvaToOffset(BYTE* pFileBaseAddress, DWORD Rva)
{
// 获取DOS头
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBaseAddress;
// 获取NT头
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pFileBaseAddress + pDosHeader->e_lfanew);
// 得到区段个数
DWORD SectionNumber = pNtHeader->FileHeader.NumberOfSections;
// 得到区段
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
// 遍历区段表,找到RVA所在的区段
/*
* 每个偏移,不管是在文件中,还是在内存中,它们距离区段开始位置的距离总是相等的。
* 而且,区段表中,保存着两个开始偏移:
* 1. 文件中的开始偏移
* 2. 内存中的开始偏移
* 具体过程:
* 找到RVA所在区段, 然后计算出这个RVA到区段在内存中的开始位置的距离。
* 用这个距离加上区段在文件中的开始位置就得到文件偏移了
*/
for (int i = 0; i < SectionNumber; ++i)
{
// 区段的起始相对虚拟地址RVA
DWORD SectionBeginRva = pSectionHeader[i].VirtualAddress;
// 区块的结束相对虚拟地址RVA = 区段的RVA地址 + 文件中的区段对齐大小
DWORD SectionEndRva = pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData;
// 判断RVA是否在当前的区段中
if (Rva >= SectionBeginRva&& Rva <= SectionEndRva)
{
// 计算出RVA对应的文件偏移
// 公式:文件偏移 = RVA - 区段的起始相对虚拟地址RVA + 区段的起始文件偏移FOA
// 1. 要转换的RVA - 区段的起始相对虚拟地址RVA
DWORD Temp = Rva - pSectionHeader[i].VirtualAddress;
// 2. 加上区段的起始文件偏移FOA,dwOffset为FOA
DWORD FileOffset = Temp + pSectionHeader[i].PointerToRawData;
// 3. 得到文件偏移FOA
return FileOffset;
}
}
return -1;
}