1. 程式人生 > 實用技巧 >C/C++ 實現VA與FOA之間的轉換

C/C++ 實現VA與FOA之間的轉換

PE結構中的地址互轉,這次再來系統的複習一下關於PE結構中各種地址的轉換方式,最終通過程式設計來實現自動解析計算。

本章中使用的工具是上次講解PE結構文章中製作的CMD迷你結構解析器,後期會通過解析器配合完成計算任務,基本命令如下所示,請熟練使用。

下載地址:https://www.blib.cn/soft/pe.zip


將VA地址轉換為FOA檔案偏移: VA就是虛擬地址,轉換為FOA檔案偏移,其手工計算過程如下所示。

首先需要得到 ImageBase(映象基址) 其次得到入口點地址,將兩個地址相加即可得到VA,也就是實際裝入地址。

通過上方的已知條件我們就可以計算出程式實際裝入記憶體後的入口地址了.

VA(實際裝入地址) = ImageBase(基址) + RVA(偏移) => 00400000 + 0000158b = 0040158b

如果不放心,可以將源程式拖入X64DBG中觀察是否一致,如下OEP為 0040158b 與我們的計算結果完全一致。

接著我們需要得到 .text節 基地址以及 .text節 RVA,如下命令即可獲取到。

虛擬地址開始位置:節區基地址 + 節區RVA => 00400000 + 00001000 = 00401000

虛擬地址結束位置:text節地址 + 節區尺寸 => 00401000 + 0x00000B44 = 00401B44

計算得出,虛擬地址text節開始位置 00401000 結束位置是 00401B44 開啟X64dbg驗證沒錯 確實落在了 text節上。

判斷是否落在了.text節的依據是 開始位置 >= 401000 結束位置 <= 402000 很明顯:00401B44小於402000。

接著我們就來計算一下,當前的VA地址0040158B其對應到檔案中的偏移FOA位置是多少,計算公式如下。

RVA(相對偏移) = VA - (.text節首地址) => 0040158B - 00401000 = 58B
FOA(檔案偏移) = RVA + .text節對應到檔案中的偏移 => 58B + 400 = 98B

計算出結果了,我們使用WinHEX定位到98B處看看是否符合要求,機器碼完全一致,符合要求。

接著就是使用C語言來實現這一計算過程了,有了計算流程之後使用C語言實現就變得簡單許多。

DWORD VA_To_FOA(HANDLE ImageBase,DWORD dwVA)
{
	PIMAGE_NT_HEADERS pNtHead = NULL;
	PIMAGE_FILE_HEADER pFileHead = NULL;
	PIMAGE_SECTION_HEADER pSection = NULL;
	DWORD NumberOfSectinsCount = 0;
	DWORD dwImageBase = 0;

	pNtHead = GetNtHeader(ImageBase);
	pSection = IMAGE_FIRST_SECTION(pNtHead);

	dwImageBase = pNtHead->OptionalHeader.ImageBase;
	NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
	for (int each = 0; each < NumberOfSectinsCount; each++)
	{
		DWORD Section_Start = dwImageBase + pSection[each].VirtualAddress;                                  // 獲取節的開始地址
		DWORD Section_Ends = dwImageBase + pSection[each].VirtualAddress + pSection[each].Misc.VirtualSize; // 獲取節的結束地址

		if (dwVA >= Section_Start && dwVA <= Section_Ends)
		{
			DWORD RVA = dwVA - pNtHead->OptionalHeader.ImageBase;                                    // 計算RVA
			DWORD FOA = pSection[each].PointerToRawData + (RVA - pSection[each].VirtualAddress);     // 計算FOA
			return FOA;
		}
	}
	return -1;
}

將FOA檔案偏移轉換為VA地址: 將十六進位制的檔案偏移地址,反轉為VA地址。

如下,通過公式計算一下檔案偏移為0xF43的位置,其對應到VA虛擬地址是多少。

VPK(實際大小) = (text節首地址 - ImageBase) - 實際偏移 => 401000-400000-400 = C00

VA(虛擬地址) = FOA(.text節) + ImageBase + VPK => F43 + 400000 + C00 = 401B43

計算後的結果F43對應到VA地址是401B43 驗證一下,沒錯。

通過C語言實現也很簡單,只需要把這個計算過程流程化即可。

DWORD RVA_To_FOA(HANDLE ImageBase, DWORD dwRVA)
{
	PIMAGE_NT_HEADERS pNtHead = NULL;
	PIMAGE_FILE_HEADER pFileHead = NULL;
	PIMAGE_SECTION_HEADER pSection = NULL;
	DWORD NumberOfSectinsCount = 0;
	DWORD dwImageBase = 0;

	pNtHead = GetNtHeader(ImageBase);
	pSection = IMAGE_FIRST_SECTION(pNtHead);

	dwImageBase = pNtHead->OptionalHeader.ImageBase;
	NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
	for (int each = 0; each < NumberOfSectinsCount; each++)
	{
		DWORD Section_Start = pSection[each].VirtualAddress;                                  // 計算RVA開始位置
		DWORD Section_Ends = pSection[each].VirtualAddress + pSection[each].Misc.VirtualSize; // 計算RVA結束位置

		if (dwRVA >= Section_Start && dwRVA <= Section_Ends)
		{
			DWORD VA = pNtHead->OptionalHeader.ImageBase + dwRVA;                                  // 得到VA地址
			DWORD FOA = pSection[each].PointerToRawData + (dwRVA - pSection[each].VirtualAddress); // 得到FOA
			return FOA;
		}
	}
	return -1;
}

將RVA相對地址轉換為FOA檔案偏移: RVA就是相對地址,將相對地址轉換為FOA檔案內偏移,例如將158b轉換為FOA。

FOA = 實際偏移 + (RVA - 虛擬偏移) => 0x00000400 + (158b - 0x00001000) = 400 + 58b = 98B

計算出結果158b虛擬地址對應到檔案偏移為98B,驗證一下看看。

使用C語言實現此過程。

DWORD FOA_To_VA(HANDLE ImageBase, DWORD dwFOA)
{
	PIMAGE_NT_HEADERS pNtHead = NULL;
	PIMAGE_FILE_HEADER pFileHead = NULL;
	PIMAGE_SECTION_HEADER pSection = NULL;
	DWORD NumberOfSectinsCount = 0;
	DWORD dwImageBase = 0;

	pNtHead = GetNtHeader(ImageBase);
	pSection = IMAGE_FIRST_SECTION(pNtHead);

	dwImageBase = pNtHead->OptionalHeader.ImageBase;
	NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
	for (int each = 0; each < NumberOfSectinsCount; each++)
	{
		DWORD PointerRawStart = pSection[each].PointerToRawData;                                // 檔案偏移開始位置
		DWORD PointerRawEnds = pSection[each].PointerToRawData + pSection[each].SizeOfRawData;  // 檔案偏移結束位置

		if (dwFOA >= PointerRawStart && dwFOA <= PointerRawEnds)
		{
			DWORD RVA = pSection[each].VirtualAddress + (dwFOA - pSection[each].PointerToRawData);  // 計算出RVA
			DWORD VA = RVA + pNtHead->OptionalHeader.ImageBase;                                     // 計算出VA
			return VA;
		}
	}
	return -1;
}