Ret2dl_resolve初探
June 8, 2019 PWN 访问: 25 次
延迟绑定
基本思想当函数第一次用到时才进行绑定(符号查找、重定位),如果没有用到的话就不进行绑定。
当我们在调用某个外部的模块时,如果按照通常的做法应该是通过GOT表中相应的项进行间接跳转。PLT为了实现延迟绑定,在这个过程中间又增加了一层间接跳转。调用函数并不直接通过GOT跳转,而是通过一个叫做PLT项的结构在进行跳转。每个外部函数在PLT中都有一个相应的项
通过readelf可以查看ELF格式的文件信息
首先看一下节头信息
找到.plt表,跟进看.plt中具体的汇编代码
可以看出来,前两句汇编
0x8048380: push DWORD PTR ds:0x804a004
0x8048386: jmp DWORD PTR ds:0x804a008
接着规定每一项的长度是0x10个字节,刚好来存放3条指令,如
=> 0x8048390 <read@plt>: jmp DWORD PTR ds:0x804a00c
0x8048396 <read@plt+6>: push 0x0
0x804839b <read@plt+11>: jmp 0x8048380
这里以第一次调用read函数为例,在.plt段的read函数处下断点
首先执行了一句jmp跳转指令,这一句汇编的意思就跳到.got.plt相应的地址处,但是这是第一次调用read函数,got表中并没有read函数的真实地址,此时,got表中存放的是0x8048396,也就是.plt每一项的第二条汇编指令的地址,然后压入栈中一个num,这个数字是read这个符号在.rel.plt中的下标
.rel.plt是什么呢?.rel.plt中保存了重定位表的信息,可以使用lazy的连接方式,内部结构如下:
typedef struct {
Elf32_Addr r_offset; // 对于可执行文件,此值为虚拟地址
Elf32_Word r_info; // 符号表索引
} Elf32_Rel;
该结构是一个结构体类型,结构体的第一个变量表示的是这个函数在GOT表中的地址,第二个数字代表这个函数在.dynsym段中的索引,但是这个索引需要计算(一般后两位是07)
r.info >> 8
那.dynsym是什么呢?.dynsym只保存了与动态链接相关的符号,为了表示动态链接这些模块之间的符号导入导出关系。我们可以通过readelf来查看它的地址,结构如下
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility under glibc>=2.2 */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;
我们可以来验证一下,这是在.rel.plt段中各个函数结构体中的数据
下面这是.dynsym段:
下面这是.got.plt段:
经过对比之后发现确实是一一照应的
那么现在接着刚刚执行的流程继续走,刚刚执行到把一个数字压入栈中,然后跳转到plt段中的前两句指令,前两句指令的第一句是“push DWORD PTR ds:0x804a004”,这一句的意思就是把当前模块ID压入栈中,这个模块ID的地址在.got.plt表的第二位数据
.got.plt的结构
1. .dynamic段的地址
2. Module ID "libc.so"
3. _dl_runtime_resolve()的地址
4. 每个函数的got表的位置
……
接着跳转到_dl_runtime_resolve()函数,这个函数进行一系列的工作以后讲read函数的真实地址填入到read@GOT中
_dl_runtime_resolve具体都做了什么呢?
首先是第一个参数[0x804a004],这个指针指向一个结构,该结构包含了.dynamic的指针,_dl_runtime_resolve通过第一个参数可以访问到.dynamic这个段,第二个参数就是要定位的函数在.rel.plt中的下标
- 通过第一个参数访问到.dynamic,从.dynamic中取出.dynstr,.dynsym,.rel.plt的指针
- .rel.plt+第二个参数 求出当前函数在.rel.plt中的地址
- 在.rel.plt当前的结构体取出第二个变量>>8求出该函数在.dynsym段下的地址
- .dynstr+该函数在.dynsym段下的地址的第一个变量求出该函数在.dynstr下的地址
- 在动态链接库查找这个函数的地址,并且把地址赋值给GOT表
- 调用这个函数