Linux延迟绑定机制过程


pwn的时候了解到linux存在延迟绑定机制,延迟绑定之后plt->got->函数真实地址,这两天比赛遇到问题了才去关注了一下延迟绑定的具体过程

概念

延迟绑定是当函数第一次被调用的时候才进行绑定(包括符号查找、重定位等),如果函数不被调用就不进行绑定。延迟绑定机制可以大大加快程序的启动速度,特别有利于一些引用了大量函数的程序。

过程分析

以调用system函数为例,汇编中call system时会调用system_plt,而system_plt中是三条汇编指令(不是system_got地址),其中第一条是jmp system_gotsystem_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_403640qword_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_403640qword_403648)分别是linkmapdl_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]

  目录