PE文件解析(5):重定位表详解
重定位表
1、重定位表的作用
重定位表(Relocation Table)用于在程序加载到内存中时,进行内存地址的修正。
并不是所有的exe程序都有重定位表,但是DLL却是必须需要重定位信息。
每一个重定位表(每一块颜色区域),包括virtualaddress和size和每个表所含的偏移数据值
重定位表的结构体解析
重定位表通过IMAGE_BASE_RELOCATION的结构体。
typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; // WORD TypeOffset[1]; } IMAGE_BASE_RELOCATION; typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
重要字段
- VirtualAddress: 重定位表记录的每个偏移数据的基址。
- SizeOfBlock: 重定位表的大小(VirtualAddress + SizeOfBlock + 各偏移的总大小)
通过重定位表的大小可以定位可以确定这个表中有多少个数据。通过 SizeOfBlock - 0x8 可以得到DWORD型偏移的总大小,偏移数据占据2个字节,因此再除以2可以得到偏移数据的个数。
注意:当我们得到某个偏移数据后,他只是个RVA,还需要加上VirtualAddress才是它真正的地址。
寻找重定位表
- 数据目录表中记录重定位表的偏移地址是 0x1F000h
- 在区段表中寻找: 0x1F000的合适位置,可以得到区段表的索引为8的区段的偏移地址为 0x1F000h,就是重定位表的RVA,所以 利用公式: 数据FOA= 数据RVA - 区段 RVA + 区段FOA,可以得到: 1F000 - 1F000 + 9200h(FOA) 可以得到 重定位表的FOA: 0x9200。
- 可以找到 0x9200的偏移地址,再加上基址可以得到重定位表的地址,在 010editor可以直接看到此地址:前四个字节为DWORD型的virtual
可以看到:
- virtualaddress: 011000h
- size: 68h 重定位表的大小:68h个字节
- 总大小减去 8字节(virtualaddress和size自身的大小),得到60h即是重定位表中偏移数据的大小,偏移数据占据WORD型两个字节,因此除以2可以得到这张重定位表中偏移数据的总个数: 0x30个。
- 如何得到偏移数据的真正地址? 我们可以看到紧跟之后的数据为 0x3698 0x32E8, 但他们都只是偏移,表面上我们还需要加上virtualaddress,才是这个数据真正的地址.
但是要记住
:这个2字节数据占据16位,其中高4位为一个标记,低12位才是数据的真正的值。即:0x3698按位与 0x0FFF 才得到低12位的数据, 然后再加上virtualaddress的基址,即得到:0x11698,所以这个值就是我们重定位表存储的真正的偏移值。
代码解析重定位表
void cPE::GetRelocation() { //获取重定位表的偏移地址 IMAGE_DATA_DIRECTORY RelocationAddr = pOptionHeader->DataDirectory[5]; //获取重定位表 PIMAGE_BASE_RELOCATION RelocationTable = (PIMAGE_BASE_RELOCATION)(RvaToFoa(RelocationAddr.VirtualAddress) + FileBuff); if (RelocationTable == NULL) { printf("重定位表为空!\n"); return; } UINT CountTable = 0; while (1) { if (RelocationTable->VirtualAddress == 0) { break; } CountTable++; //获取偏移数据的个数 DWORD NumOfBlock = (RelocationTable->SizeOfBlock - 8) / 2; //指向相加,加的是sizeof(类型) WORD* PrelocOffset =(WORD*)RelocationTable +4; //跳过8个字节,到达第一个偏移的位置 for (UINT i = 0; i < NumOfBlock; i++) { //偏移地址的高4位为标志 位,低12位为数据位,高四位等于3 证明其是个有效的数据 if (((*PrelocOffset) & 0x3000) == 0x3000) { DWORD Rva = (*PrelocOffset & 0x0FFF) + RelocationTable->VirtualAddress; printf("Rva: %#x\n", Rva); } PrelocOffset++; } //继续遍历下一个重定位表 RelocationTable = (PIMAGE_BASE_RELOCATION)((DWORD)RelocationTable + RelocationTable->SizeOfBlock); } printf("重定位表的总数: %d\n", CountTable); }
运行可得:我们的重定位表不止有一个,在一个重定位表之后就是另一个重定位表,可以通过
RelocationTable = (PIMAGE_BASE_RELOCATION)((DWORD)RelocationTable + RelocationTable->SizeOfBlock);
得到下一个重定位表,然后再此重复执行此操作,即可得到了所有的便宜数据。
本文来自博客园,作者:hugeYlh,转载请注明原文链接:https://www.cnblogs.com/helloylh/p/17209670.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)