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);

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

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
实现PE文件解析器的步骤如下: 1. 读取PE文件头,获取文件头信息。 2. 读取节头,获取节信息。 3. 读取导入、导出重定位等其他,获取相关信息。 4. 解析每个节的数据,获取代码、数据等信息。 5. 根据节中的信息,将PE文件中的代码、数据等部分映射到内存中。 6. 解析导入,获取导入的函数信息,并进行符号解析。 7. 解析导出,获取导出函数信息,并生成符号。 8. 处理重定位,修正代码中的地址。 9. 实现其他功能,如获取PE文件中的资源信息、版本信息等。 下面是一个简单的golang实现PE文件解析器的代码示例: ```go package main import ( "debug/pe" "fmt" "os" ) func main() { if len(os.Args) != 2 { fmt.Println("Usage: pe-parser <filename>") return } file, err := os.Open(os.Args[1]) if err != nil { fmt.Println("Failed to open file:", err) return } defer file.Close() peFile, err := pe.NewFile(file) if err != nil { fmt.Println("Failed to parse PE file:", err) return } fmt.Println("PE Header Info:") fmt.Printf("\tMachine: %s\n", peFile.FileHeader.Machine) fmt.Printf("\tNumber of Sections: %d\n", peFile.FileHeader.NumberOfSections) fmt.Printf("\tSize of Optional Header: %d\n", peFile.FileHeader.SizeOfOptionalHeader) fmt.Println("\nSection Table Info:") for _, section := range peFile.Sections { fmt.Printf("\tName: %s\n", section.Name) fmt.Printf("\tVirtual Address: %d\n", section.VirtualAddress) fmt.Printf("\tVirtual Size: %d\n", section.VirtualSize) fmt.Printf("\tRaw Size: %d\n", section.Size) fmt.Printf("\tOffset: %d\n", section.Offset) fmt.Println() } } ``` 该代码使用了golang标准库中的`debug/pe`包来实现PE文件解析器,可以获取PE文件头信息和节信息。你可以根据需要扩展代码以支持更多功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yuleo_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值