天天看點

PE之FOA與RVA互相轉換過程與C語言實作說明一、FOA和RVA二、RVA轉為FOA三、FOA轉為RVA

文章目錄

  • 說明
  • 一、FOA和RVA
  • 二、RVA轉為FOA
    • 1.大緻步驟
    • 2.特殊情況
    • 3.C語言實作函數功能
  • 三、FOA轉為RVA
    • 1.大緻步驟
    • 2.特殊情況
    • 3.C語言實作函數功能

說明

看滴水的視訊寫學習筆記總結

語言:c/c++

編譯環境:vc++6.0

C語言函數中定義的結構類型來自于頭檔案

windows.h

準确的說,定義的PE的結構體類型的所有資料都來自與頭檔案

winnt.h

,隻不過windows.h内部聲明了winnt.h

一、FOA和RVA

縮寫 英文全稱 含義
FOA File Offset Address 檔案偏移位址
RVA Relative Virtual Addresses 相對虛拟位址
VA Virtual Address 虛拟位址

RVA

VA = RVA + ImageBase
PE之FOA與RVA互相轉換過程與C語言實作說明一、FOA和RVA二、RVA轉為FOA三、FOA轉為RVA

二、RVA轉為FOA

1.大緻步驟

第一步:判斷RVA是否在PE頭區。在PE頭區RVA與FOA相等,直接傳回RVA。

對比第一個節表頭VirtualAddress.
若RVA < VirtualAddress則說明RVA在PE頭區
           

第二步:若RVA沒有在頭區,就周遊節表頭,尋找這個RVA位于哪個節表區。

循環周遊節表頭,在該 節表頭内 滿足下列條件
VirtualAddress <= RVA <= VirtualAddress + SizeOfRawData
即可确定RVA位于哪個節表頭
           

第三步:找到RVA對應的節表區頭後,計算并傳回FOA的值

在對應的節表頭區内,
FOA = (RVA - VirtualAddress) + PointerToRawData;
           

2.特殊情況

兩種情況的RVA沒有對應的FOA

  • RVA為記憶體拉伸後系統填充的位址
  • RVA超出記憶體範圍
PE之FOA與RVA互相轉換過程與C語言實作說明一、FOA和RVA二、RVA轉為FOA三、FOA轉為RVA

3.C語言實作函數功能

說明 意義
函數名稱 size_t ConvertRvaToFoa( size_t RVA , LPVOID pFileBuffer )
功能 将RVA轉化為FOA
參數 參數1:要轉換的RVA的值(size_t);參數2:指向檔案緩沖區的指針(LPVOID)
傳回值 成功則傳回對應的FOA,失敗則傳回0(這個RVA是沒有對應的FOA,為記憶體拉伸後系統填充的位址,或者RVA超出記憶體範圍)
調用頭檔案 stdio.h

window.h

//功能:将RVA轉化為FOA
//參數:參數1:要轉換的RVA的值(size_t);參數2:指向檔案緩沖區的指針(LPVOID)
//傳回值:成功則傳回對應的FOA,失敗則傳回0(這個RVA是沒有對應的FOA,為記憶體拉伸後系統填充的位址,或者RVA超出記憶體範圍)。
size_t ConvertRvaToFoa( size_t RVA , LPVOID pFileBuffer )
{
	int i ;//用于周遊節表
//	size_t FOA = 0;

	//定義表頭指針
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS32 pNtHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;	

	//給表頭賦初值
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);
	pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader+4 );
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);	
	//第一個節表頭
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);
	
	if(RVA < pSectionHeader->VirtualAddress)//判斷RVA是否在PE頭區
	{
		if(RVA < pSectionHeader->PointerToRawData)
			return RVA;//此時FOA == RVA
		else
			return 0;
	}

	for(i=0 ; i<pFileHeader->NumberOfSections ; i++)//循環周遊節表頭
	{
		if( i )//周遊節表頭,第一次不周遊,
			pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
		
		if(RVA >= pSectionHeader->VirtualAddress )//是否大于這個節表的RVA
		{
			if( RVA <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData )//判斷是否在這個節區
				return ( RVA - pSectionHeader->VirtualAddress ) + pSectionHeader->PointerToRawData;//确定節區後,計算FOA
		}
		else//RVA不可能此時的pSectionHeader->VirtuallAddress小,除非是傳回值為0的情況。
			return 0;		
	}
		
	return 0;
}
           

三、FOA轉為RVA

1.大緻步驟

VA轉為FOA

的步驟架構差不多,但細節不一樣

第一步:判斷FOA是否在PE頭區。在PE頭區FOA與RVA相等,直接傳回這個FOA。

對比第一個節表頭的PointerToRawData
若FOA < PointerToRawData則證明FOA在PE頭區
           

第二步:若FOA沒有在頭區,就周遊節表頭,尋找這個FOA位于哪個節表區。

循環周遊節表頭,在該 節表頭内 滿足下列條件
PointerToRawData <= FOA < PointerToRawData + SizeOfRawData
即可确定FOA位于哪個節表頭
           

第三步:找到FOA對應的節表區頭後,計算并傳回RVA的值

在對應的節表頭區内,
RVA = (FOA - PointerToRawData) + VirtualAddress;
           

2.特殊情況

FOA超出檔案記憶體的範圍

3.C語言實作函數功能

說明 意義
函數名稱 size_t ConvertFoaToRva( size_t FOA , LPVOID pFileBuffer )
功能 将FOA轉換為RVA
參數 參數1:要轉換的FOA值(size_t);參數2:指向檔案記憶體的指針(LPVOID)
傳回值 成功則傳回對應的RVA,失敗傳回0(此時為FOA越界)
調用頭檔案 stdio.h

windows.h

//功能:将FOA轉換為RVA
//參數:參數1:要轉換的FOA值(size_t);參數2:指向檔案記憶體的指針(LPVOID);
//傳回值:成功則傳回對應的RVA,失敗傳回0(此時為FOA越界)。
size_t ConvertFoaToRva( size_t FOA , LPVOID pFileBuffer )
{
	int i = 0;//用于周遊節表。
//	size_t RVA = 0;
	
	//定義表頭指針
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS32 pNtHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;

	//給表頭賦初值
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew  );
	pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4 );
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
	//第一個節表頭
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );

	if( FOA < pSectionHeader->PointerToRawData )//判斷是否位于 頭區
		return FOA ; //這是RVA == FOA ;
	
	for(i=0 ; i<pFileHeader->NumberOfSections ; i++)//循環周遊節表頭
	{
		if( i )//周遊節表頭,第一次不周遊,
			pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);

		if( FOA >= pSectionHeader->PointerToRawData )//是否大于這個節表的FOA
		{
			if( FOA < pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData )//判斷是否在這個節表區域
				return (FOA - pSectionHeader->PointerToRawData ) + pSectionHeader->VirtualAddress ;//計算并傳回RVA
		}			
	}

	return 0;
}
           

歡迎大家留言交流 ^ _ ^

繼續閱讀