RVA和FOA的相互转换
全局变量有无初始值的区别:
没有初始值:PE文件在文件中的状态的时候不会对该变量的地址进行存储,只有在内存中运行的时候的状态才会进行分配
有初始值:PE文件在文件中的状态的时候会对该变量的地址进行存储
什么是VA、RVA、FOA
VA:英文全称是Virual Address,简称VA,中文意思是虚拟地址,指的是文件被载入虚拟内存后的地址。
ImageBase:中文意思是基址,指的是程序在虚拟内存中被装载的位置。
RVA:相对虚拟地址,可以理解为文件被装载到虚拟内存(拉伸)后先对于基址的偏移地址。
它的对齐方式一般是以1000h为单位,以虚拟内存对齐的方式对齐的,具体对齐需要参照IMAGE_OPTIONAL_HEADER32
中的SectionAlignment
成员。
FOA:文件偏移地址。可以理解为文件在磁盘上存放时相对于文件开头的偏移地址。
它的对齐方式一般是以200h为单位,以文件对齐的方式对齐的,具体对齐需要参照IMAGE_OPTIONAL_HEADER32
中的FileAlignment
成员。
计算方式:RVA = VA(虚拟地址) - ImageBase(基址)
RVA转换为FOA
RVA(相对虚拟地址)转换FOA(文件偏移地址)过程:
int a = 0x12345678; int main() { printf("地址:%.8X\n",&a); printf("数值:%d\n",a); return 0; }
运行结果:
D:\VC6EN\COMMON\MSDEV98\BIN\Debug>5.exe 地址:00424A30 数值:305419896
我们要求RVA,RVA = VA - ImageBase,VA虚拟地址为0x00424A30
,
那么 RVA = 424A30 - ImageBase
查看Imagebase:0x00400000
RVA = 424A30 - 400000 = 24A30
接下来寻址FOA,就要考虑文件对齐跟内存对齐不是一样的问题,大家可以想象下如果一样的话 ,那么就是 文件对齐和内存对齐是一样的 唯一变化的就是内存中会加上基址的地址,现在基址已经减去了,那么这两种也都一样了!
第一种情况:文件对齐跟内存对齐一样的情况,那么这样就可以直接去找 0x24A30的地址了 这个地址也就是FOA
第二种情况:文件对齐和内存对齐不一样的情况
我们需要判断RVA属于哪个节/头,这里也要分为两种情况!
1):如果RVA属于文件头部(DOS头 + PE头 + 节表),头部大小是文件对齐大小的整数倍!
那么不需要进行计算了,因为DOS头和PE头和节表在文件中和在内存中展开都是一样的,直接从开始位置寻找到RVA个字节即可,就是找0x24A30
,也就是FOA(文件偏移地址)
2):如果RVA不在头,就要判断在哪个节里面
判断节开始位置到节结束位置 我们的RVA是否在这个范围里面,总共分为三步骤:
第一步:指定节.VirtualAddress <= RVA <= 指定节.VirtualAddress + VirtualSize(当前节内存实际大小)
第二步:差值 = RVA - 指定节.VirtualAddress
第三步:FOA = 指定节.PointerToRawData + 差值
代码实现:
#include<windows.h> #include<stdio.h> #define FILE_PATH "C:\\Documents and Settings\\Administrator\\桌面\\PE练习素材\\练习素材\\NOTEPAD.EXE" int GetFileLength(FILE *pf, DWORD *Length) { int ret = 0; fseek(pf, 0, SEEK_END); *Length = ftell(pf); fseek(pf, 0, SEEK_SET); return ret; } int MyReadFile(void **pFileAddress) { int ret = 0; DWORD Length = 0; //打开文件 FILE* pf = fopen(FILE_PATH, "rb"); if (pf == NULL) { ret = -1; printf("func ReadFile() Error!\n"); return ret; } //获取文件长度 ret = GetFileLength(pf, &Length); if (ret != 0 && Length == -1) { ret = -2; printf("func GetFileLength() Error!\n"); return ret; } //分配空间 *pFileAddress = (PVOID)malloc(Length); if (*pFileAddress == NULL) { ret = -3; printf("func malloc() Error!\n"); return ret; } memset(*pFileAddress, 0, Length); //读取文件进入内存 fread(*pFileAddress, Length, 1, pf); fclose(pf); return ret; } int RVA_TO_FOA(PVOID FileAddress, DWORD RVA, PDWORD pFOA) { int ret = 0; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(FileAddress); PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4); PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER)); PIMAGE_SECTION_HEADER pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader); //RVA在文件头中 或 SectionAlignment 等于 FileAlignment 时RVA等于FOA if (RVA < pOptionalHeader->SizeOfHeaders || pOptionalHeader->SectionAlignment == pOptionalHeader->FileAlignment) { *pFOA = RVA; return ret; } //循环判断RVA在节区中 for (int i = 0; i < pFileHeader->NumberOfSections; i++) { if (RVA >= pSectionGroup[i].VirtualAddress && RVA < pSectionGroup[i].VirtualAddress + pSectionGroup[i].Misc.VirtualSize) { *pFOA = pSectionGroup[i].PointerToRawData + RVA - pSectionGroup[i].VirtualAddress; return ret; } } //没有找到地址 ret = -4; printf("func RAV_TO_FOA() Error: %d 地址转换失败!\n", ret); return ret; } int main(){ PVOID pFileAddress; DWORD FOA; MyReadFile(&pFileAddress); RVA_TO_FOA(pFileAddress,(DWORD)0x1080,&FOA); printf("%x",FOA); return 0; }
FOA转换为RVA
FOA(文件偏移地址)转换RVA(相对虚拟地址)过程:
设FOA为节数据的任意一位置
1.计算差值偏移:
FOA - 指定节.PointerToRawData = 差值
2.计算RVA:
差值 + 指定节.VirtuallAddress(节数据在内存中展开的位置) = RVA
3.计算虚拟地址:
VA = RVA + ImageBase
需要注意的就是我们的 FOA 在哪一个节中:
指定节.PointerToRawData <= FOA <= 指定节..PointerToRawData + 指定节..SizeofRawData
代码实现:
#include<windows.h> #include<stdio.h> #define FILE_PATH "C:\\Documents and Settings\\Administrator\\桌面\\PE练习素材\\练习素材\\NOTEPAD.EXE" int GetFileLength(FILE *pf, DWORD *Length) { int ret = 0; fseek(pf, 0, SEEK_END); *Length = ftell(pf); fseek(pf, 0, SEEK_SET); return ret; } int MyReadFile(void **pFileAddress) { int ret = 0; DWORD Length = 0; //打开文件 FILE* pf = fopen(FILE_PATH, "rb"); if (pf == NULL) { ret = -1; printf("func ReadFile() Error!\n"); return ret; } //获取文件长度 ret = GetFileLength(pf, &Length); if (ret != 0 && Length == -1) { ret = -2; printf("func GetFileLength() Error!\n"); return ret; } //分配空间 *pFileAddress = (PVOID)malloc(Length); if (*pFileAddress == NULL) { ret = -3; printf("func malloc() Error!\n"); return ret; } memset(*pFileAddress, 0, Length); //读取文件进入内存 fread(*pFileAddress, Length, 1, pf); fclose(pf); return ret; } int FOA_TO_RVA(PVOID FileAddress, PDWORD pRVA, DWORD FOA) { int ret = 0; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(FileAddress); PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4); PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER)); PIMAGE_SECTION_HEADER pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader); //RVA在文件头中 或 SectionAlignment 等于 FileAlignment 时RVA等于FOA if (FOA < pOptionalHeader->SizeOfHeaders || pOptionalHeader->SectionAlignment == pOptionalHeader->FileAlignment) { *pRVA = FOA; return ret; } //循环判断FOA在节区中 for (int i = 0; i < pFileHeader->NumberOfSections; i++) { if (FOA >= pSectionGroup[i].PointerToRawData && FOA < pSectionGroup[i].PointerToRawData + pSectionGroup[i].Misc.VirtualSize) { *pRVA = FOA - pSectionGroup[i].PointerToRawData + pSectionGroup[i].VirtualAddress; return ret; } } //没有找到地址 ret = -4; printf("func FOA_TO_RVA() Error: %d 地址转换失败!\n", ret); return ret; } int main(){ PVOID pFileAddress; DWORD RVA; MyReadFile(&pFileAddress); FOA_TO_RVA(pFileAddress,&RVA,(DWORD)0x480); printf("%x",RVA); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY