动态库*.so的延时绑定分析

1、程序运行过程中需要调用动态库中的函数,因此必须先指定动态库的路径,否则会报无法找到动态库的错误。需要动态添加动态库路径

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/dyh/project/test_space/malloc/output/libs

2、程序运行过程中会调用动态库中的很多函数,这些函数的地址不是在运行的时候全部完成绑定,而是需要的时候才做绑定动作,俗称延时绑定(lazy binding)。效率最大化的表现。

3、如何完成延时绑定

3.1、准备工作

查看代码反汇编

objdump -d malloc_main > malloc_main.txt

查看代码各个section的完整内容

objdump -s malloc_main > malloc_main_s.txt

3.2、从func函数的汇编代码中看出,调用malloc_so后面有plt(procedure linkage table)字样,这个函数就是延时绑定。

00000000000009da <func>:
 9da:	55                   	push   %rbp
 9db:	48 89 e5             	mov    %rsp,%rbp
 9de:	48 83 ec 10          	sub    $0x10,%rsp
 9e2:	48 c7 45 f8 00 00 00 	movq   $0x0,-0x8(%rbp)
 9e9:	00 
 9ea:	bf 1e 00 00 00       	mov    $0x1e,%edi
 9ef:	e8 7c fe ff ff       	callq  870 <malloc_so@plt>	;@plt延时绑定
 9f4:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
 9f8:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
 9fc:	48 89 c7             	mov    %rax,%rdi
 9ff:	e8 7c fe ff ff       	callq  880 <free_so@plt>
 a04:	bf 1e 00 00 00       	mov    $0x1e,%edi
 a09:	e8 92 fe ff ff       	callq  8a0 <malloc_free_so@plt>
 a0e:	90                   	nop
 a0f:	c9                   	leaveq 
 a10:	c3                   	retq  

3.3、查看870地址的汇编代码

Disassembly of section .plt:

0000000000000830 <.plt>:
 830:	ff 35 52 07 20 00    	pushq  0x200752(%rip)        # 200f88 <_GLOBAL_OFFSET_TABLE_+0x8>		;将link_map结构的地址压入栈中。对dynsym区域的解析
 836:	ff 25 54 07 20 00    	jmpq   *0x200754(%rip)        # 200f90 <_GLOBAL_OFFSET_TABLE_+0x10>		;跳转到_dl_runtime_resolve此函数在动态库*.so的地址。查找到后把函数地址填充到GOT表中
 83c:	0f 1f 40 00          	nopl   0x0(%rax)

0000000000000840 <strlen@plt>:
 840:	ff 25 52 07 20 00    	jmpq   *0x200752(%rip)        # 200f98 <strlen@GLIBC_2.2.5>
 846:	68 00 00 00 00       	pushq  $0x0
 84b:	e9 e0 ff ff ff       	jmpq   830 <.plt>

0000000000000850 <__stack_chk_fail@plt>:
 850:	ff 25 4a 07 20 00    	jmpq   *0x20074a(%rip)        # 200fa0 <__stack_chk_fail@GLIBC_2.4>
 856:	68 01 00 00 00       	pushq  $0x1
 85b:	e9 d0 ff ff ff       	jmpq   830 <.plt>

0000000000000860 <printf@plt>:
 860:	ff 25 42 07 20 00    	jmpq   *0x200742(%rip)        # 200fa8 <printf@GLIBC_2.2.5>
 866:	68 02 00 00 00       	pushq  $0x2
 86b:	e9 c0 ff ff ff       	jmpq   830 <.plt>

0000000000000870 <malloc_so@plt>:
 870:	ff 25 3a 07 20 00    	jmpq   *0x20073a(%rip)        # 200fb0 <malloc_so>			;GOT中保存malloc_so函数信息。但是,第一次不是,需要查找。
 876:	68 03 00 00 00       	pushq  $0x3							;GOT先保存这条指令的地址。push后面的数值是.rel.dyn重定位段表的下标
 87b:	e9 b0 ff ff ff       	jmpq   830 <.plt>

3.4、870行会跳转到200fb0地址,200fb0地址的内容查看malloc_main_s.txt文件的内容。发现200fb0存在于.got section中,存放的内容是876(蓝框)。相当于870行的下一行地址,重新返回876行,把0x03压入栈区(0x03值后续会用到)。再跳转到830行,830行把数据压入栈区,836行跳转到200f90。转换成C语言就是:

dl_runtime_resolve(link_map_obj, reloc_arg)
第一个参数830:    ff 35 52 07 20 00        pushq  0x200752(%rip)确定
第二个参数876:    68 03 00 00 00           pushq  $0x3    确定

在.got section中,前三项的内容固定
①、dynamic段的地址
②、动态库*.so的ID
③、_dl_runtime_resolve()的地址

红框中对应着.dynamic段的地址

 .dynamic 动态链接器所需要的基本信息。如:依赖哪些共享对象,动态链接符号表的位置,动态链接重定位表的位置,共享对象初始化代码的地址等。类似于ELF文件头(readelf -d xxx)

3.5、dl_runtime_resolve函数

dl_runtime_resolve(link_map_obj, reloc_arg)

功能就是调用link_map_obj函数解析.dynamic section,通过reloc_arg参数查找到函数名,再根据函数名dl_runtime_resolve到动态库中查找到函数的地址(假如多个动态库中存在多个函数,以找到的第一个动态库为准),最后把查找到的函数地址填充到.got表中(.got可以被修改,假如.got表中数据被修改,数据就会被跑飞),回看3.4,下次再到.got表中查找的时候就是真正的函数入口地址了。

link_map_obj函数先找到.dynamic段,根据偏移量找到.dynstr  .dynsym   .rel.plt段
.dynstr    = 0x04d0
.dynsym    = 0x2d8
.rel.plt    = 0x758
查看具体段的二进制数据(objdump -s -j .dynsym ./malloc_main)
①、先从.rel.plt段寻找,ELF32_Rel = .rel.plt + reloc_arg = 0x758 + 0x01 * size
r_offset = 0x200fb0
r_info = 0x0500000007
.rel.dyn 数据段的重定位表(readelf -r xxx)
对数据引用的修正,修正后的数据位于.got中
.rel.plt 代码段的重定位表
对函数引用的修正,修正后的位置位于.got.plt中
R_X86_64_GLOB_DAT    数据类型
R_X86_64_JUMP_SLO    函数类型

②、从.dynsym段寻找,r_info右移,得到下标5,.dynsym[5]
.dynsym[5].name_offset = 0x54
③、从.dynstr段查找,st_name = .dynstr + name_offset = 0x04d0 + 0x54
查找到字符串malloc_so,根据字符串到动态库中查找函数地址,so文件优先找到为准。
地址存放到*rel->offset中,即GOT表中
④、查看GOT表内容,GOT的中已经填充函数的真实地址

3.6、一言以蔽之

①、plt延时绑定,从.got中查找函数地址,第一次没有。

②、保存函数下表,跳转到dl_runtime_resolve函数,查找到函数在动态库的地址,将地址回写到.got中

③、以后访问,.got中能查找到函数地址,直接调用函数。

3.7、测试demo源码

GitHub - dyh-git/so_plt_exp: 动态库延时绑定跟踪验证

4、题外话

从全局变量打印的地址发现,GOT的数据刚好在.data段的上面。

当访问全局变量的时候,往地址减少的方向写数据,GOT中的地址数据就会出现异常,出现代码飞踩的情况。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值