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才是它真正的地址。


寻找重定位表

  1. 数据目录表中记录重定位表的偏移地址是 0x1F000h
    在这里插入图片描述
  2. 在区段表中寻找: 0x1F000的合适位置,可以得到区段表的索引为8的区段的偏移地址为 0x1F000h,就是重定位表的RVA,所以 利用公式: 数据FOA= 数据RVA - 区段 RVA + 区段FOA,可以得到: 1F000 - 1F000 + 9200h(FOA) 可以得到 重定位表的FOA: 0x9200。
    在这里插入图片描述
  3. 可以找到 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);

得到下一个重定位表,然后再此重复执行此操作,即可得到了所有的便宜数据。
在这里插入图片描述
在这里插入图片描述

posted @ 2022-11-02 23:08  hugeYlh  阅读(160)  评论(0编辑  收藏  举报  来源