手工
编译一个带有全局变量的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
PETool看基址=00400000,RVA=VA-ImageBase=424a30-00400000 = 24a30
再看看在内存中、文件中的对齐方式
1、如果在内存中和文件中的对齐方式是一致的,那么 RVA=FOA=24a30
2、如果它们的对齐方式不一样,那么 FOA = 节.PointToRawData+差值偏差
差值偏差=RVA - 节.VirtuallAddress,如果想不通,看下图:
我这里的对齐方式是一样的,所以FOA=24a30
PE编辑工具打开exe文件,找24a30地址的值,改之为 66666666
保存,运行exe看这时候的值
代码实现 RVA->FVA
步骤:
代码:
#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