RVA-FOA之间转换
1.首先PE头加载到内存之后是和文件头内容一样的,就算是偏移不同,一个是磁盘扇区大小(400H)另一个是内存页大小(1000H),但是因为两个都是开头位置,所以相同。
2.看下IMAGE_SECTION_HEADER定义
也就是这样:
然后就可以算了(RVA->FOA)
(1) RVA20 < RVA < RVA30 可以知道在第二节
(2) 然后 off = RVA - RVA20 ,得出距离第二节的偏移off
(3) FOA = FOA 20 + off 求出FOA
计算FOA->RVA也是同理...
/************************************************************************/
/*
功能:虚拟内存相对地址和文件偏移的转换
参数: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;
}
/************************************************************************/
/*
功能:文件偏移地址和虚拟地址的转换
参数: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;
return pSection[i].VirtualAddress + dwDiffer;
}
else if (stOffset < dwBeginVA) //如果文件偏移地址不在区段中
{
return dwImageBase + stOffset;
}
}
return 0;
}
测试了上面两个函数,没发现什么问题,测试结果:
随手写了个简单的工具,直接控制台写的,功能简单,懒得写界面了。源码在:
http://download.csdn.net/detail/u013761036/9643230
运行效果: