2020-12-31

PE解析:修改重定位表

我的目的是:改掉DLL的ImageBase,修复重定位表中的地址,这个DLL还能用。
看了网上很多资料,大都是写得如何去解析重定位表中的各条数据,没有说怎么去修改,修改了以后DLL还能用的问题。我也是查阅了很多资料,弄了一天,把经验总结一下。(本人小白,大佬请无视)

首先得熟悉重定位表结构:
1.重定位表位于NT_Header的OptionalHeader(可选头)的DataDirectory结构体的第6个成员(这么说有点绕 )。

2.结构体定义如下:

IMAGE_DIRECTORY_ENTRY_BASERELOC
struct _IMAGE_DATA_DIRECTORY {
0x00 DWORD VirtualAddress;		//重定位表所在的RVA
0x04 DWORD Size;				//大小
};

3.根据上面的RVA,转换成FOA,在文件中找到类似以下的东西:
在这里插入图片描述
我使用的这个DLL只实现了Plus、Mul、Div、Sub四个函数,也就是加减乘除。其中每一个红框部分视为一个整体,根据重定位表结构得知,每一个整体第一个DWORD为一个VirtualAddress,类似于Windows分页机制。紧跟的一个DWORD为这个整体使用了多少个字节。然后以WORD开始分,每一个WORD加上VirtualAddress再加上ImageBase,就是加载时需要修复的地址。

4.系统时怎么修复重定位表的(以上面给出的第一个红框数据举例)

比如:
这个DLL希望加载到0x10000000位置,也就是ImageBase,但是在exe使用过程中还加载了其他的DLL,0x10000000位置没有了,此时假如系统将这个DLL分到了0x30000000的位置,此时系统开始修复重定位表

系统找到VirtualAddress:00 10 00 00,也就是0x1000
然后找到SizeOfBlock:10 01 00 00,也就是0x110
SizeOfBlock减去8字节再除以2(WORD的长度),就是第一个红框里要修复的条数。

以前三条数据举例:

0x304E、0x30B3、0x30DF

32位程序中,高4位为3,表示这条数据需要修复,此时取低12位的值,取完后为:

0x04E、0x0B3、0x0DF

加上VirtualAddress的0x1000,得到:

0x104E、0x10B3、0x10DF

这些都是RVA,转成FOA后,去找到这三个地址的值:
在这里插入图片描述
分别是0x10003004、0x10002448、0x10003358
此时系统会将实际装载的地址:0x30000000,减去希望装载的地址:0x10000000,得到一个差值:0x20000000,再加上VirtualAddress。也就是说,之前能在0x10003004找到的数据,现在需要加上0x20000000的偏移,也就是0x30003004,才能找到。每一条重定位数据都需要如此进行修复。

下面说说我的做法

1.修改ImageBase,原来是0x10000000,改为0x20000000。
2.对重定位表中RVA所指向的地址进行修复。所有的数据都需要加上差值,也就是由原来的0x10003004改为0x20003004。

代码太长(杂七杂八加上DLL有好几百行),贴个主要部分吧:

循环遍历,修改重定位表中的数据:

//重定位表以全0表示结束,这里判断有没有结束,没有就进入循环
while (Relocation!=0)
	{
		//给定一个双字节指针,指向 ”0x304E“,此后以这个指针进行遍历
		short* relocaddr = (short*)((char*)Relocation + 8);
		//获得VirtualAddress和SizeOfBlock
		int va = (int)Relocation->VirtualAddress;
		int block = (int)Relocation->SizeOfBlock;
		//((block - 8) / 2)为数据个数,需要遍历多少次
		for (int i = 0; i < ((block - 8) / 2); i++)
		{
			//判断最高位是否为3,左移和右移取低12位,注意使用无符号数
			if ((*relocaddr>>0xc)==0x3)
			{
				unsigned short a = *relocaddr << 0x4;
				a = a >> 0x4;
				//RvaToFoa是我写的Rva转换函数,传入int RVA和FILE*,返回int FOA
				int* b = (int*)(begin + RvaToFoa(a + va, filebuffer));
				printf("%x\n",*b);
				//加上差值
				*b = *b + 0x10000000;

			}
			relocaddr++;
		}
		//判断下一个整体的开始是否为0,重定位表是否结束
		next = next + block;
		if (*((int*)next)==0)
		{
			break;
		}
		//结构体指针重新赋值,指向下一个整体的开始
		Relocation = (_IMAGE_BASE_RELOCATION*)next;
	}
	//存盘,这里偷懒就直接写了文件大小
	char newfile[] = "D:\\mydllnew.dll";
	FILE* nnfile;
	nnfile= fopen(newfile, "wb");
	fwrite(filebuffer, 1, 0x2200, nnfile);

malloc在RVA函数已经释放过了,再释放会报错(这里还把我坑了半个小时,没搞懂怎么回事)。

总结:学习就是一个还债的过程,以前不认真学,到用的时候,所有的问题都浮现出来了。不过结果还是挺令人满意的,程序运行一切正常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值