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;
}