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;
}
发布了19 篇原创文章 · 获赞 21 · 访问量 992

猜你喜欢

转载自blog.csdn.net/weixin_43742894/article/details/105235629