代码重定位

代码重定位是将程序逻辑地址转换为内存物理地址的过程,解决因加载地址变化导致的指令和数据访问错误。它涉及到汇编实现、高级语言实现及重定位表的应用。在Windows NT系统下,尽管存在疑惑,但通过重定位表和特定情况下的正确运行,证明了重定位的有效性。问题在于如何在重定位表清零后仍实现程序重定位。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

重定位就是把程序的逻辑地址空间变换成内存中的实际物理地址空间的过程,也就是说在装入时对目标程序中指令和数据的修改过程。

为什么需要重定位?

程序员在编写程序时,在使用某些常量、全局变量时,编译器把这些数据的地址编译成一个绝对地址(比如:call 0x400100),是以ImageBase(0x400000)为基准。当程序加载到ImageBase时,程序正常运行,可是当程序加载地址为0x500000时,访问的绝对地址是一个错误地址,实际应该为call 0x500100,这时就用到了重定位技术。重定位就是修改编译时某些绝对地址的过程。

怎样实现重定位?

1、汇编实现

dwVar    dw     ? ;全局变量需要重定位

          push   ebx
          call    Flg
Flg:
           pop     ebx
           sub      ebx, offset Flg
           mov    dwVar, ebx;dwVar 保存了自身的运行地址 - 编译地址
           pop     ebx


2、高级语言的实现方式 (C++/C)

int __stdcall _GetRunAddrSubAbsAddr(unsigned int RetAddr);

__declspec(naked) int  GetRunAddrSubAbsAddr()
{
	__asm call _GetRunAddrSubAbsAddr
}

int __stdcall _GetRunAddrSubAbsAddr(unsigned int RetAddr)
{
	unsigned int *pRet;
	int  delta;
	pRet = &RetAddr;
	pRet--;
	delta = *pRet - 5 - (unsigned int)GetRunAddrSubAbsAddr;
	*pRet = RetAddr;//程序返回地址
	return delta;

}
int main(int argc, _TCHAR* argv[])
{
	int delat = GetRunAddrSubAbsAddr();//返回的为运行地址和绝对地址的差值
	return 0;
}
__declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码。

如何查看重定位信息?

在数据目录表的重定位表中可以找到重定位数据段的地址。

But:

我有个疑问。。。。这两段代码好像有点瑕疵。不可以把程序二进制码写入内存中任意位置执行。why

网上的很多资料包括书本中使用过类似上面的代码,一般来说这样的结果是正确的。但是我还有些小疑问?

我分析是:WindowsNT 系统下, call   @F 时,应该类似于call   0x7c00, 而这时调用的地址是一个绝对地址(0x7c00),本身就是需要重定位的,怎么可以用来求自身运行地址和编译地址的差值!?更别说把代码移植到内存任意位置执行,但是每次调试时运行的结果又都是正确的,这又是为啥嘞?

我分析可能有两个原因:

1、程序运行时载入的地址就是ImageBase(PE结构里面),这是编译地址恰好等于运行地址,dwVar值为0.

为了验证一下猜想:

	.386
	.model flat,stdcall
	option casemap:none

;***********************************************
;include 文件定义
;***********************************************
include		windows.inc
include		user32.inc
includelib	user32.lib
include		kernel32.inc
includelib	kernel32.lib
;************************************************
;堆栈段
;************************************************
	.stack

;************************************************
;数据段  初始化数据定义
;************************************************
	.data
szCaption	db	'A MessageBox!',0
szText		db	'Hello,World!',0
;************************************************
;数据段  未始化数据定义
;************************************************
	.data?

;***********************************************
;常量定义
;************************************************
	.const

;***********************************************
;代码段
;************************************************
	.code
start:
		invoke MessageBox,NULL,offset szText,offset szCaption,MB_OK or MB_ICONWARNING
		invoke ExitProcess,NULL
;****************************************************
	END	start

当用masm32 编译链接成EXE时,我发现重定位表竟然是零(结果如下图)!难道汇编编译成的EXE文件一定会加载到ImageBase处!这么说的话,在汇编下编译EXE文件时,那么重定位技术就没必要用了。但是在DLL远程注入技术中,必须要用这种重定位方式 计算出执行代码的内存位置,然后拷贝代码内容。


2、当程序运行时载入的地址不是ImageBase时, call @F的地址应该是运行地址。这就是PE结构里重定位表的功劳了,在程序载入内存时把需要数据重定位的结果。

那么问题来了,怎样把重定位表清零后,可以实现程序重定位呢?

其实,也不是没办法。。我把程序测试后再添加代码。

经过测试和windbg的反汇编调试发现,第一种汇编实现的方式是可以实现任意内存位置重定位的 (囧。。,上面的分析都是渣,看来我还是 兔羊 兔森泡),为啥呢?

00c919d0 55              push    ebp
00c919d1 8bec            mov     ebp,esp
00c919d3 81ecd0000000    sub     esp,0D0h
00c919d9 53              push    ebx
00c919da 56              push    esi
00c919db 57              push    edi
00c919dc 8dbd30ffffff    lea     edi,[ebp-0D0h]
00c919e2 b934000000      mov     ecx,34h
00c919e7 b8cccccccc      mov     eax,0CCCCCCCCh
00c919ec f3ab            rep stos dword ptr es:[edi]
00c919ee a12480c900      mov     eax,dword ptr [Test!__security_cookie (00c98024)]
00c919f3 33c5            xor     eax,ebp
00c919f5 8945fc          mov     dword ptr [ebp-4],eax
00c919f8 53              push    ebx
00c919f9 e800000000      call    Test!wmain+0x2e (00c919fe)//这句是关键  --  call Flg ----
00c919fe 5b              pop     ebx


查询机器码可知 E800000000 表示相对call,此机器码的意思是  调用下一条指令地址相对位置为0处的代码。此处拨开云雾见明月 






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值