逆向笔记——PE文件【内存中的虚拟相对地址转文件中的相对地址】RVA->FOA

手工

编译一个带有全局变量的exe
这是代码


#include <stdio.h>

int globalVar = 0x12345678;

void main()
{
	printf("globalVar = %x, RVA=%x\n",globalVar,&globalVar);	//RVA=424a30 | FOA=424a30-400000=24a30

}

打印出来的是全局变量globalVar 在内存中的地址 VA=424a30
逆向笔记——PE文件【内存中的虚拟相对地址转文件中的相对地址】RVA->FOA

PETool看基址=00400000,RVA=VA-ImageBase=424a30-00400000 = 24a30
逆向笔记——PE文件【内存中的虚拟相对地址转文件中的相对地址】RVA->FOA

再看看在内存中、文件中的对齐方式
1、如果在内存中和文件中的对齐方式是一致的,那么 RVA=FOA=24a30
2、如果它们的对齐方式不一样,那么 FOA = 节.PointToRawData+差值偏差
差值偏差=RVA - 节.VirtuallAddress,如果想不通,看下图:
逆向笔记——PE文件【内存中的虚拟相对地址转文件中的相对地址】RVA->FOA

我这里的对齐方式是一样的,所以FOA=24a30
逆向笔记——PE文件【内存中的虚拟相对地址转文件中的相对地址】RVA->FOA

PE编辑工具打开exe文件,找24a30地址的值,改之为 66666666
逆向笔记——PE文件【内存中的虚拟相对地址转文件中的相对地址】RVA->FOA
逆向笔记——PE文件【内存中的虚拟相对地址转文件中的相对地址】RVA->FOA

保存,运行exe看这时候的值
逆向笔记——PE文件【内存中的虚拟相对地址转文件中的相对地址】RVA->FOA

代码实现 RVA->FVA

步骤:
逆向笔记——PE文件【内存中的虚拟相对地址转文件中的相对地址】RVA->FOA

代码:


#include <stdio.h>
#include <windows.h>
/************************
功能:内存中的相对虚拟偏移转换为文件中的偏移
步骤:假设x是需要计算FOA的内存地址
	1、先计算 RVA = x-ImageBase
	2、计算差值偏差 y = RVA-节.VirtualAddress
	3、计算FOA=节.PointToRawData+y
*************************/
unsigned char* FileBuffer(const char* FileName);
unsigned char* RVAToFVA(unsigned char* x) //x的位置
{
	unsigned char* FVA=NULL;
	unsigned char* RVA=NULL;
	PIMAGE_DOS_HEADER pDosHeader;
	PIMAGE_NT_HEADERS pNtHeaders;
	PIMAGE_SECTION_HEADER pSectionHeader;

	//打开文件映射到filebuffer
	unsigned char* fileBuffer = FileBuffer("test.exe");

	//得到 PIMAGE_OPTIONAL_HEADER=>ImageBase  PIMAGE_SECTION_HEADER=>VirtualAddress  PointToRawData
     pDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
     pNtHeaders = (PIMAGE_NT_HEADERS)(fileBuffer + pDosHeader->e_lfanew);
     pSectionHeader = (PIMAGE_SECTION_HEADER)(pNtHeaders+1);
	 printf("%x\n",pNtHeaders->OptionalHeader.ImageBase);
	 RVA = x-pNtHeaders->OptionalHeader.ImageBase;
	 //判断是否在DOS+NT头部内,是则RVA=FOA
	 if(x<=(unsigned char*)pNtHeaders->OptionalHeader.SizeOfHeaders)
	 {
		FVA = RVA;
		printf("在头部FVA=RVA=%x\n",FVA);
		return FVA;
	 }
	
	 //判断在哪个节
	 unsigned int i=0;
	 for(i=0;i<pNtHeaders->FileHeader.NumberOfSections;i++)
	 {
		printf("%x\n",pSectionHeader[i].PointerToRawData);

		printf("%x\n",pSectionHeader[i].VirtualAddress);

		if(RVA>=(unsigned char*)pSectionHeader[i].VirtualAddress && RVA<=(unsigned char*)(pSectionHeader[i].VirtualAddress+pSectionHeader[i].SizeOfRawData) )
		{
			//在这个节:计算偏差->计算FOA

			FVA = pSectionHeader[i].PointerToRawData+(RVA-pSectionHeader[i].VirtualAddress);
			printf("在%s节中,FVA=%x\n", pSectionHeader[i].Name,FVA);
			return FVA;
		}
	 }
	return NULL;
}

void main()
{
	unsigned char* x = (unsigned char*)0x424a30;
	RVAToFVA(x); //内存中的虚拟地址
}

//将PE文件读到FileBuffer
unsigned char* FileBuffer(const char* FileName)
{
     unsigned char* Heap = NULL;
     FILE* Stream;

     //打开文件
     Stream = fopen(FileName,"rb");
     //计算文件大小
     fseek(Stream,0,SEEK_END);
     long FileSize = ftell(Stream);
     fseek(Stream,0,SEEK_SET);

     //分配堆空间
     Heap = (unsigned char*)malloc(sizeof(char)*FileSize);
     //将文件拷到堆
     fread(Heap,sizeof(char),FileSize,Stream);
     fclose(Stream);

	 return Heap;
}

FVA->RVA 原理同上

      设x 为节数据的任意一位置

      1.计算差值偏移:
差值 = x - 节.PointerToRawData(节数据在文件中开始的位置)

      2.计算RVA
RVA = 差值 + 节.VirtuallAddress(节数据在内存中展开的位置)

      3.计算虚拟地址:
VA = RVA + ImageBase

判断x在哪一个节中的条件: x <= 节.PointerToRawData + 节.SizeofRawData

上一篇:C/C++ 使用 CRC32 检测内存映像完整性


下一篇:手写PE结构解析工具