您的位置:首页 > 其它

PE文件的相对虚拟地址(RVA)和文件偏移地址(FOA)的转换

2014-12-27 15:15 399 查看
RVA(相对虚拟地址)和FOA(文件偏移)的具体含义大家可以看看《Windows PE 权威指南》或者是小甲鱼的PE结构详解视频,我相信大家看完之后一定会理解的,我这里就不写这些概念了。之所以会产生两者的转换,是因为同一个文件在硬盘和内存中的对齐方式不一样,我们可以通过IMAGE_OPTIONAL_HEADER结构体的SectionAlignment(内存对齐方式)和FileAlignment(文件对齐方式)这两个字段,知道它们的对齐方式分别是以200h进行对齐和以1000h对齐的,那么当我们知道内存相对虚拟地址时,想转换成文件偏移时,还需要借助IMAGE_SECTION_HEADER这个结构体。该结构体记录了该Section在文件中的起始偏移(PointerToRawData)和内存映像中的起始RVA(VirtualAddress),这是我们可以找到它们唯一联系的地方。

我们转换的思路是:模拟内存对齐方式,看需要转换的虚拟地址是否在该区段之间。

转化的步骤:

1. 找到可执行文件中的Section的数目dwSectionCount,这个可以通过IMAGE_FILE_HEADER结构体中的NumberOfSections字段获取。

2. 找到Section的对齐大小,可以通过IMAGE_OPTIONAL_HEADER的SectionAlignment字段获取这个值。

3. 对dwSectionCount的循环,在这个循环中我们需要判断RVA位于哪个Section中,diff = 需要转换的虚拟地址-VirtualAddr计算出距离该节起始地址的偏移。

4 PointerToRawData + diff就是转换后的结果。

代码如下:

/************************************************************************/
/*
功能:虚拟内存相对地址和文件偏移的转换
参数:stRVA: 虚拟内存相对偏移地址
lpFileBuf: 文件起始地址
返回:转换后的文件偏移地址
*/
/************************************************************************/
size_t RVAToOffset(size_t stRVA,PVOID lpFileBuf)
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpFileBuf;
size_t stPEHeadAddr = (size_t)lpFileBuf + pDos ->e_lfanew;
PIMAGE_NT_HEADERS32 pNT = (PIMAGE_NT_HEADERS32)stPEHeadAddr;
//区段数
DWORD dwSectionCount = pNT->FileHeader.NumberOfSections;
//内存对齐大小
DWORD dwMemoruAil = pNT->OptionalHeader.SectionAlignment;
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNT);
//距离命中节的起始虚拟地址的偏移值。
DWORD dwDiffer = 0;
for (DWORD i = 0; i < dwSectionCount; i++)
{
//模拟内存对齐机制
DWORD dwBlockCount = pSection[i].SizeOfRawData/dwMemoruAil;
dwBlockCount += pSection[i].SizeOfRawData%dwMemoruAil? 1 : 0;

DWORD dwBeginVA = pSection[i].VirtualAddress;
DWORD dwEndVA = pSection[i].VirtualAddress + dwBlockCount * dwMemoruAil;
//如果stRVA在某个区段中
if (stRVA >= dwBeginVA && stRVA < dwEndVA)
{
dwDiffer = stRVA - dwBeginVA;
return pSection[i].PointerToRawData + dwDiffer;
}
else if (stRVA < dwBeginVA)//在文件头中直接返回
{
return stRVA;
}
}
return 0;
}

FOA(文件偏移)和VA虚拟地址的转换。

思路:判断szOffset在那个区段中间,然后计算出距离该区段起始地址的偏移diff,根据PointerToRawData和VirtualAddress的关系,就可以成功的转换为虚拟地址VA(虚拟起始地址 + 虚拟相对地址RVA + 偏移(diff)).

代码如下:

/************************************************************************/
/*
功能:文件偏移地址和虚拟地址的转换
参数:stOffset:文件偏移地址
lpFileBuf:虚拟内存起始地址
返回:转换后的虚拟地址
*/
/************************************************************************/
size_t Offset2VA(size_t stOffset, PVOID lpFileBuf)
{
//获取DOS头
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpFileBuf;
//获取PE头
//e_lfanew:PE头相对于文件的偏移地址
size_t stPEHeadAddr = (size_t)lpFileBuf + pDos ->e_lfanew;
PIMAGE_NT_HEADERS32 pNT = (PIMAGE_NT_HEADERS32)stPEHeadAddr;
//区段数
DWORD dwSectionCount = pNT->FileHeader.NumberOfSections;
//映像地址
DWORD dwImageBase = pNT->OptionalHeader.ImageBase;
//区段头
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNT);

//相对大小
DWORD dwDiffer = 0;
for (DWORD i = 0; i < dwSectionCount; i++)
{
//区段的起始地址和结束地址
DWORD dwBeginVA = pSection[i].PointerToRawData;
DWORD dwEndVA = pSection[i].PointerToRawData + pSection[i].SizeOfRawData;
//如果文件偏移地址在dwBeginVA和dwEndVA之间
if (stOffset >= dwBeginVA && stOffset < dwEndVA)
{
//相对大小
dwDiffer = stOffset - dwBeginVA;
//进程的起始地址 + 区段的相对地址 + 相对区段的大小
return dwImageBase + pSection[i].VirtualAddress + dwDiffer;
}
else if (stOffset < dwBeginVA) //如果文件偏移地址不在区段中
{
return dwImageBase + stOffset;
}
}
return 0;
}




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: