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;
}
我们转换的思路是:模拟内存对齐方式,看需要转换的虚拟地址是否在该区段之间。
转化的步骤:
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;
}
相关文章推荐
- 【PE结构】由浅入深PE基础学习-菜鸟手动查询导出表、相对虚拟地址(RVA)与文件偏移地址转换(FOA)
- [PE文件结构学习]1.相对虚拟地址(RVA)与物理地址的转换
- [PE结构分析] 7.相对虚拟地址(RVA)和文件偏移间的转换
- Windows Pe 第三章 PE头文件-EX-相关编程-2(RVA_FOA转换)
- 理解PE文件相对虚拟地址(RVA)到文件偏移的转换
- PE文件中RVA和Offset的转换
- 我认识的PE文件之相对虚拟地址 + 文件偏移地址
- PE文件RV转FOA及FOA转RVA
- PE文件中, RVA和文件偏移的转换
- 流水账笔记:PE文件格式(RVA & FOA)
- :PE文件中RVA和RAW是如何互相转换的?"(看雪)
- PE文件结构(二) 区块,文件偏移与RVA转换
- PE文件格式的RVA概念
- PE文件详解(三)--区块描述、对齐值以及RVA
- PE文件关于虚拟相对地址RVA与基于文件的偏移地址关系
- PE文件和COFF文件格式分析——RVA和RA相互计算
- 转: PE rva to raw 虚拟偏移地址和文件物理偏移地址
- PE文件详解四:PE详解之区块描述、对齐值以及RVA详解
- 我自己的PE文件RVA-VA-Offset心得
- PE文件格式的RVA概念