摘要:intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,
实验十六:C语言实现将RVA地址转换为VA地址。
RVA地址转换为VA地址的方法:
sectionStartVa =sectionHeader->VirtualAddress + ntHeaders->OptionalHeader.ImageBase;
va = sectionStartVa + (rva - sectionStartRva)。
■源代码
/*
FileName:RvaToVa.c
实验16:Rva地址转换为VA地址
(c) bcdaren, 2024
*/
#include
#include
#defineMAXSIZE 1024
#pragmacomment (lib,"dbghelp")
/*
函数使用64位PE的NT头结构体
*/
PVOIDRvaToVa64(PIMAGE_NT_HEADERS64ntHeaders, DWORDrva) {
PIMAGE_SECTION_HEADERsectionHeader = IMAGE_FIRST_SECTION(ntHeaders);
WORDnumberOfSections = ntHeaders->FileHeader.NumberOfSections;
for (WORDi = 0; i
DWORDsectionStartRva = sectionHeader->VirtualAddress;
DWORDsectionEndRva = sectionStartRva +sectionHeader->Misc.VirtualSize;
if (rva >= sectionStartRva && rva
//节区起始地址=节区虚拟内存起始地址+PE模块基址
DWORDsectionStartVa = (DWORD)sectionHeader->VirtualAddress +
(DWORD)ntHeaders->OptionalHeader.ImageBase;
unsignedlongva = sectionStartVa + (rva - sectionStartRva);
return (PVOID)va;
}
sectionHeader++;
}
returnNULL; // RVA not found
}
intWINAPIWinMain(HINSTANCEhInstance, HINSTANCEhPrevInstance,
PSTRszCmdLine, intiCmdShow)
{
constTCHARszCaption[MAXSIZE] = TEXT("RvaToVa");
staticTCHARszText[MAXSIZE] = { 0 };
// 获取当前模块的句柄
HMODULEhModule = GetModuleHandle(NULL);
// 获取模块的 IMAGE_NT_HEADERS 结构
PIMAGE_NT_HEADERSntHeaders = ImageNtHeader(hModule);
if (ntHeaders) {
// 假设要转换的 RVA 是 0x1000
ULONGrva = 0x1000;
//32位系统
//PVOID va = ImageRvaToVa(ntHeaders, hModule, rva, NULL);
//64位系统
PVOIDva = RvaToVa64((PIMAGE_NT_HEADERS64)ntHeaders, rva);
if (va) {
wsprintf(szText,TEXT("RVA 0x%08X converted to virtual address: %p\n"),rva, va);
}
else {
wsprintf(szText,TEXT("Failed to convert RVA to virtual address.\n"));
}
}
else {
wsprintf(szText, TEXT("Failed to retrieve IMAGE_NT_HEADERS.\n"));
}
MessageBox(NULL,szText,szCaption, MB_OK);
return 0;
图3-15 RVA 地址转VA地址
注意
1.注意观察多次在Windows 64位和32位系统下运行时,转换后的VA地址的变化。
2.XP系统使用VC6.0编译时,需要在编译目录C:\Program Files\Microsoft Visual Studio\VC98\Lib和Include中添加dbghelp.lib和dbghelp.h(注意选用i386处理器版本)。如果dbghelp.h头文件出现错误提示,请将其错误行去掉就可以了。
3.ImageRvaToVa函数在64位系统中会得到一个错误的VA地址,ImageRvaToVa函数只能在32位Windows系统中使用。为了兼容64位Windows系统,我们自定义了一个RvaToVa64函数,通过遍历节表的方法,确定RVA地址属于哪个节区,然后通过“节区起始地址=节区虚拟内存起始地址+PE模块基址”的方法获取VA地址。
4.PE文件内获取的基址是静态的基址,64位Windows系统将映像文件加载内存后动态获取的基址为重定位后的基址。
练习
使用汇编代码实现上述功能。可以考虑两种不同的方法实现:
方法1:调用ImageRvaToVa函数(64位系统中错误);
方法2:自己实现ImageRvaToVa函数的功能,兼容32位和64位操作系统;
实验十七:C语言实现将RVA地址转换为FOA地址。
RVA地址转换为FOA地址的方法:
sectionStartVa =sectionHeader->VirtualAddress + ntHeaders->OptionalHeader.ImageBase;
foa = sectionHeader->PointerToRawData(文件中的起始地址) + (rva - sectionStartRva)。
■源代码
/*
FileName:RvaToFoa.c
实验17:Rva地址转换为FOA地址
(c) bcdaren, 2024
*/
DWORDRvaToFoa(PIMAGE_NT_HEADERSntHeaders, DWORDrva) {
// ntHeaders+4+sizeof(IMAGE_FILE_HEADER)+FileHeader.SizeOfOptionalHeader(32或64位PE)
DWORDsectionEndRva = sectionStartRva + sectionHeader->SizeOfRawData;
DWORDfoa = sectionHeader->PointerToRawData +(rva - sectionStartRva);
returnfoa;
}
}
return 0; // RVA not found
}
{
HMODULEhModule = GetModuleHandle(NULL); // 获取当前模块的句柄
if (ntHeaders) {
DWORDfoa = RvaToFoa(ntHeaders, rva);
if (foa) {
wsprintf(szText,TEXT("RVA 0x%08X converted to FOA: 0x%08X\n"), rva, foa);
}
else {
wsprintf(szText, TEXT("Failed to convert RVA to FOA.\n"));
}
}
else {
}
return 0;
}
运行:
图3-16 RVA地址转FOA地址
练习
使用汇编代码实现上述功能。
来源:墨先生文化艺术