PE文件:VA、RVA和FOA

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章