RVA和FOA的相互轉換


全局變量有無初始值的區別:

沒有初始值:PE文件在文件中的狀態的時候不會對該變量的地址進行存儲,只有在內存中運行的時候的狀態才會進行分配

有初始值:PE文件在文件中的狀態的時候會對該變量的地址進行存儲


什么是VA、RVA、FOA

VA:英文全稱是Virual Address,簡稱VA,中文意思是虛擬地址,指的是文件被載入虛擬內存后的地址。

ImageBase:中文意思是基址,指的是程序在虛擬內存中被裝載的位置。

RVA:相對虛擬地址,可以理解為文件被裝載到虛擬內存(拉伸)后先對於基址的偏移地址。

它的對齊方式一般是以1000h為單位,以虛擬內存對齊的方式對齊的,具體對齊需要參照IMAGE_OPTIONAL_HEADER32中的SectionAlignment成員。

FOA:文件偏移地址。可以理解為文件在磁盤上存放時相對於文件開頭的偏移地址。

它的對齊方式一般是以200h為單位,以文件對齊的方式對齊的,具體對齊需要參照IMAGE_OPTIONAL_HEADER32中的FileAlignment成員。

計算方式:RVA = VA(虛擬地址) - ImageBase(基址)


RVA轉換為FOA

RVA(相對虛擬地址)轉換FOA(文件偏移地址)過程:

int a = 0x12345678;
int main() {
    printf("地址:%.8X\n",&a);
    printf("數值:%d\n",a);
    return 0; 
} 

運行結果:

D:\VC6EN\COMMON\MSDEV98\BIN\Debug>5.exe
地址:00424A30
數值:305419896

我們要求RVA,RVA = VA - ImageBase,VA虛擬地址為0x00424A30

那么 RVA = 424A30 - ImageBase

查看Imagebase:0x00400000

RVA = 424A30 - 400000 = 24A30

接下來尋址FOA,就要考慮文件對齊跟內存對齊不是一樣的問題,大家可以想象下如果一樣的話 ,那么就是 文件對齊和內存對齊是一樣的 唯一變化的就是內存中會加上基址的地址,現在基址已經減去了,那么這兩種也都一樣了!


第一種情況:文件對齊跟內存對齊一樣的情況,那么這樣就可以直接去找 0x24A30的地址了 這個地址也就是FOA

第二種情況:文件對齊和內存對齊不一樣的情況

我們需要判斷RVA屬於哪個節/頭,這里也要分為兩種情況!

1):如果RVA屬於文件頭部(DOS頭 + PE頭 + 節表),頭部大小是文件對齊大小的整數倍!

那么不需要進行計算了,因為DOS頭和PE頭和節表在文件中和在內存中展開都是一樣的,直接從開始位置尋找到RVA個字節即可,就是找0x24A30,也就是FOA(文件偏移地址)

2):如果RVA不在頭,就要判斷在哪個節里面

判斷節開始位置到節結束位置 我們的RVA是否在這個范圍里面,總共分為三步驟:

第一步:指定節.VirtualAddress <= RVA <= 指定節.VirtualAddress + VirtualSize(當前節內存實際大小)

第二步:差值 = RVA - 指定節.VirtualAddress

第三步:FOA = 指定節.PointerToRawData + 差值

代碼實現:

#include<windows.h>
#include<stdio.h>

#define FILE_PATH "C:\\Documents and Settings\\Administrator\\桌面\\PE練習素材\\練習素材\\NOTEPAD.EXE"

int GetFileLength(FILE *pf, DWORD *Length)
{
	int ret = 0;
	
	fseek(pf, 0, SEEK_END);
	*Length = ftell(pf);
	fseek(pf, 0, SEEK_SET);
	
	return ret;
}

int MyReadFile(void **pFileAddress)
{
	int ret = 0;
	DWORD Length = 0;
	//打開文件
	FILE* pf = fopen(FILE_PATH, "rb");
	if (pf == NULL)
	{
		ret = -1;
		printf("func ReadFile() Error!\n");
		return ret;
	}
	
	//獲取文件長度
	ret = GetFileLength(pf, &Length);
	if (ret != 0 && Length == -1)
	{
		ret = -2;
		printf("func GetFileLength() Error!\n");
		return ret;
	}
	
	//分配空間
	*pFileAddress = (PVOID)malloc(Length);
	if (*pFileAddress == NULL)
	{
		ret = -3;
		printf("func malloc() Error!\n");
		return ret;
	}
	memset(*pFileAddress, 0, Length);
	
	//讀取文件進入內存
	fread(*pFileAddress, Length, 1, pf);
	
	fclose(pf);
	return ret;
}

int RVA_TO_FOA(PVOID FileAddress, DWORD RVA, PDWORD pFOA)
{
	int ret = 0;
	
	PIMAGE_DOS_HEADER pDosHeader				= (PIMAGE_DOS_HEADER)(FileAddress);
	PIMAGE_FILE_HEADER pFileHeader				= (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader	= (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER pSectionGroup			= (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	
	//RVA在文件頭中 或 SectionAlignment 等於 FileAlignment 時RVA等於FOA
	if (RVA < pOptionalHeader->SizeOfHeaders || pOptionalHeader->SectionAlignment == pOptionalHeader->FileAlignment)
	{
		*pFOA = RVA;
		return ret;
	}

	//循環判斷RVA在節區中
	for (int i = 0; i < pFileHeader->NumberOfSections; i++)
	{
		if (RVA >= pSectionGroup[i].VirtualAddress && RVA < pSectionGroup[i].VirtualAddress + pSectionGroup[i].Misc.VirtualSize)
		{
			*pFOA = pSectionGroup[i].PointerToRawData + RVA - pSectionGroup[i].VirtualAddress;

			return ret;
		}
	}
	
	//沒有找到地址
	ret = -4;
	printf("func RAV_TO_FOA() Error: %d 地址轉換失敗!\n", ret);
	return ret;
}


int main(){
	PVOID pFileAddress;
	DWORD FOA;

	MyReadFile(&pFileAddress);
	RVA_TO_FOA(pFileAddress,(DWORD)0x1080,&FOA);
	printf("%x",FOA);

	

	return 0;
}

FOA轉換為RVA

FOA(文件偏移地址)轉換RVA(相對虛擬地址)過程:

設FOA為節數據的任意一位置

1.計算差值偏移:

FOA - 指定節.PointerToRawData = 差值

2.計算RVA:

差值 + 指定節.VirtuallAddress(節數據在內存中展開的位置) = RVA

3.計算虛擬地址:

VA = RVA + ImageBase

需要注意的就是我們的 FOA 在哪一個節中:

指定節.PointerToRawData <= FOA <= 指定節..PointerToRawData + 指定節..SizeofRawData

代碼實現:

#include<windows.h>
#include<stdio.h>

#define FILE_PATH "C:\\Documents and Settings\\Administrator\\桌面\\PE練習素材\\練習素材\\NOTEPAD.EXE"

int GetFileLength(FILE *pf, DWORD *Length)
{
	int ret = 0;
	
	fseek(pf, 0, SEEK_END);
	*Length = ftell(pf);
	fseek(pf, 0, SEEK_SET);
	
	return ret;
}

int MyReadFile(void **pFileAddress)
{
	int ret = 0;
	DWORD Length = 0;
	//打開文件
	FILE* pf = fopen(FILE_PATH, "rb");
	if (pf == NULL)
	{
		ret = -1;
		printf("func ReadFile() Error!\n");
		return ret;
	}
	
	//獲取文件長度
	ret = GetFileLength(pf, &Length);
	if (ret != 0 && Length == -1)
	{
		ret = -2;
		printf("func GetFileLength() Error!\n");
		return ret;
	}
	
	//分配空間
	*pFileAddress = (PVOID)malloc(Length);
	if (*pFileAddress == NULL)
	{
		ret = -3;
		printf("func malloc() Error!\n");
		return ret;
	}
	memset(*pFileAddress, 0, Length);
	
	//讀取文件進入內存
	fread(*pFileAddress, Length, 1, pf);
	
	fclose(pf);
	return ret;
}

int FOA_TO_RVA(PVOID FileAddress, PDWORD pRVA, DWORD FOA)
{
	int ret = 0;
	
	PIMAGE_DOS_HEADER pDosHeader				= (PIMAGE_DOS_HEADER)(FileAddress);
	PIMAGE_FILE_HEADER pFileHeader				= (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader	= (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER pSectionGroup			= (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	
	//RVA在文件頭中 或 SectionAlignment 等於 FileAlignment 時RVA等於FOA
	if (FOA < pOptionalHeader->SizeOfHeaders || pOptionalHeader->SectionAlignment == pOptionalHeader->FileAlignment)
	{
		*pRVA = FOA;
		return ret;
	}

	//循環判斷FOA在節區中
	for (int i = 0; i < pFileHeader->NumberOfSections; i++)
	{
		if (FOA >= pSectionGroup[i].PointerToRawData && FOA < pSectionGroup[i].PointerToRawData + pSectionGroup[i].Misc.VirtualSize)
		{
			*pRVA = FOA - pSectionGroup[i].PointerToRawData + pSectionGroup[i].VirtualAddress;

			return ret;
		}
	}
	
	//沒有找到地址
	ret = -4;
	printf("func FOA_TO_RVA() Error: %d 地址轉換失敗!\n", ret);
	return ret;
}


int main(){
	PVOID pFileAddress;
	DWORD RVA;

	MyReadFile(&pFileAddress);
	FOA_TO_RVA(pFileAddress,&RVA,(DWORD)0x480);
	printf("%x",RVA);

	

	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM