学pwn
的时候了解到linux
存在延迟绑定机制,延迟绑定之后plt->got->函数真实地址
,这两天比赛遇到问题了才去关注了一下延迟绑定的具体过程
概念
延迟绑定是当函数第一次被调用的时候才进行绑定(包括符号查找、重定位等),如果函数不被调用就不进行绑定。延迟绑定机制可以大大加快程序的启动速度,特别有利于一些引用了大量函数的程序。
过程分析
以调用system
函数为例,汇编中call system
时会调用system_plt
,而system_plt
中是三条汇编指令(不是system_got
地址),其中第一条是jmp system_got
,system_plt
内容如下:
.plt:0000000000401050 ; int system(const char *command)
.plt:0000000000401050 _system proc near
.plt:0000000000401050 jmp cs:off_403660 ;system_got
.plt:0000000000401050 _system endp
.plt:0000000000401050
.plt:0000000000401056 ; ---------------------------------------------------------------------------
.plt:0000000000401056 push 2
.plt:000000000040105B jmp sub_401020
第一次调用system
时,system_got
中并没有system
的真实地址,而是system_plt
中第二条指令的地址,即push 2
之后跳转到sub_401020
pwndbg> x/20gx 0x403660
0x403660 <system@got.plt>: 0x0000000000401056 0x00007ffff7e21c90
其中函数sub_401020
内容如下
.plt:0000000000401020 sub_401020 proc near ; CODE XREF: .plt:000000000040103B↓j
.plt:0000000000401020 ; .plt:000000000040104B↓j ...
.plt:0000000000401020 ; __unwind {
.plt:0000000000401020 push cs:qword_403640
.plt:0000000000401026 jmp cs:qword_403648
.plt:0000000000401026 sub_401020 endp
qword_403640
和qword_403648
存在于got
表中,got
表内容如下
.got.plt:0000000000403638 _got_plt segment qword public 'DATA' use64
.got.plt:0000000000403638 assume cs:_got_plt
.got.plt:0000000000403638 ;org 403638h
.got.plt:0000000000403638 dq offset stru_403458
.got.plt:0000000000403640 qword_403640 dq 0 ;linkmap_ptr
.got.plt:0000000000403648 qword_403648 dq 0 ;dl_runtime_resolve
.got.plt:0000000000403650 off_403650 dq offset puts
.got.plt:0000000000403658 off_403658 dq offset setbuf
.got.plt:0000000000403660 off_403660 dq offset system
.got.plt:0000000000403668 off_403668 dq offset printf
.got.plt:0000000000403670 off_403670 dq offset memset
.got.plt:0000000000403678 off_403678 dq offset read
.got.plt:0000000000403680 off_403680 dq offset __isoc99_scanf
.got.plt:0000000000403688 off_403688 dq offset exit
.got.plt:0000000000403688 _got_plt ends
got[0]
和got[1]
(即qword_403640
和qword_403648
)分别是linkmap
和dl_runtime_resolve
pwndbg> x/2gx 0x403640
0x403640: 0x00007ffff7ffe190 0x00007ffff7fe7bc0
pwndbg> x/gx 0x00007ffff7fe7bc0
0x7ffff7fe7bc0 <_dl_runtime_resolve_xsavec>: 0xe3894853fa1e0ff3
所以函数sub_401020
就是将linkmap
入栈并跳转到dl_runtime_resolve
寻找函数真实地址,找到真实地址之后会更新system_got
内容为system
函数的真实地址
pwndbg> x/gx 0x0000000000403660
0x403660 <system@got.plt>: 0x00007fad32b74290
后续再次调用system
函数时会通过system_plt
跳转到system_got
直接得到system
函数的真实地址
总结
plt
中是汇编指令,通过jmp got
跳转到got
表- 第一次调用之前
got
表存放了plt
第二条汇编指令的地址,如果没开pie
那么这个地址是已知的 - 第一次调用时通过
jmp got[1]
调用dl_runtime_resolve
并以got[0]
为基址找到函数真实地址,所以在延迟绑定之前不能覆盖got[0]
和got[1]