在linux x86平台下,程序链接的plt got里面的值是如何变化的,下面来进行演示:
首先写好一个c文件:
#include <stdio.h>
int main(void)
{
printf("hello world\n");
return 0;
}
非常简单的一个hello world程序,编译程序之后对程序进行反汇编objdump -d main
本文使用gdb的一些操作
ni 跳过当前汇编指令
si 进入汇编指令
x 地址 查看内存地址的值
disassembe 查看汇编代码
好了,在进行调试之前,先看一下文件中plt段需要重定向的符号:readelf -r main
重定位节 '.rel.dyn' 位于偏移量 0x290 含有 1 个条目:
偏移量 信息 类型 符号值 符号名称
08049ffc 00000206 R_386_GLOB_DAT 00000000 __gmon_start__
重定位节 '.rel.plt' 位于偏移量 0x298 含有 2 个条目:
偏移量 信息 类型 符号值 符号名称
0804a00c 00000107 R_386_JUMP_SLOT 00000000 puts@GLIBC_2.0
0804a010 00000307 R_386_JUMP_SLOT 00000000 __libc_start_main@GLIBC_2.0
这里只需要关注plt段的重定向,在这里只有两个符号需要重定向,重点是printf的重定向过程。
printf的地址是放在got.plt这个段中的,这个段有三个预先按顺序定义好的值:
1、dynamic段的地址
2、module ID
3、_dl_runtime_resolve()函数地址
那么第四肯定就是printf的地址了
来进行一下调试,首先跳到main函数,有这么一段:
0x08048419 <+14>: sub $0x4,%esp
0x0804841c <+17>: sub $0xc,%esp
0x0804841f <+20>: push $0x80484c0
=> 0x08048424 <+25>: call 0x80482e0 <puts@plt>
0x08048429 <+30>: add $0x10,%esp
0x0804842c <+33>: mov $0x0,%eax
很明显在箭头指向的位置,就是跳到plt中,跳进去之后:
=> 0x080482e0 <+0>: jmp *0x804a00c
0x080482e6 <+6>: push $0x0
0x080482eb <+11>: jmp 0x80482d0
这时准备跳到printf函数的入口,但是这时肯定不是正确的地址,因为动态链接器使用了延迟绑定的技术,只有在第一次用的时候才会进行真正的重定向,这时候查看一下0x804a00c这个内存的值,发现为0x080482e6 ,也就是下一条汇编指令的位置,继续跟踪跳转到0x80482d0
End of assembler dump.
(gdb) si
0x080482d0 in ?? ()
(gdb) si
0x080482d6 in ?? ()
(gdb) si
_dl_runtime_resolve () at ../sysdeps/i386/dl-trampoline.S:35
35 ../sysdeps/i386/dl-trampoline.S: 没有那个文件或目录.
这里不知道为什么会有“?”,暂且忽略,经过了几次跳转之后来到了_dl_runtime_resolve 函数,这个函数再进行动态绑定,这个函数里面的汇编代码:
=> 0xb7ff0000 <+0>: push %eax
0xb7ff0001 <+1>: push %ecx
0xb7ff0002 <+2>: push %edx
0xb7ff0003 <+3>: mov 0x10(%esp),%edx
0xb7ff0007 <+7>: mov 0xc(%esp),%eax
0xb7ff000b <+11>: call 0xb7fe97e0 <_dl_fixup>
0xb7ff0010 <+16>: pop %edx
0xb7ff0011 <+17>: mov (%esp),%ecx
0xb7ff0014 <+20>: mov %eax,(%esp)
0xb7ff0017 <+23>: mov 0x4(%esp),%eax
0xb7ff001b <+27>: ret $0xc
里面有一条跳转指令,明显fix_up暗示,执行完这条指令之后,printf完成了重定向,执行完之后我们看一下0x804a00c这个内存地址的值,发生了改变:
(gdb) x 0x804a00c
0x804a00c: 0xb7e65ca0
这才是printf函数真正的入口。
这样重定向的过程算是完成了。
既然,printf的地址在.got.plt中是第四个,那么我们来推断一下.got.plt的地址:0x804a00c-3*4=0x804a000,我们知道.got.plt第一项放的是.dynamic段的地址,调试运行查看一下:
(gdb) x 0x804a000
0x804a000: 0x08049f14
同时使用readelf查看.dynamic在main中的位置:
[21] .jcr PROGBITS 08049f10 000f10 000004 00 WA 0 0 4
[22] .dynamic DYNAMIC 08049f14 000f14 0000e8 08 WA 6 0 4
[23] .got PROGBITS 08049ffc 000ffc 000004 04 WA 0 0 4
也是8049f14,这样就证实了我们的推断。