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中的下标

  1. 通过第一个参数访问到.dynamic,从.dynamic中取出.dynstr,.dynsym,.rel.plt的指针
  2. .rel.plt+第二个参数 求出当前函数在.rel.plt中的地址
  3. 在.rel.plt当前的结构体取出第二个变量>>8求出该函数在.dynsym段下的地址
  4. .dynstr+该函数在.dynsym段下的地址的第一个变量求出该函数在.dynstr下的地址
  5. 在动态链接库查找这个函数的地址,并且把地址赋值给GOT表
  6. 调用这个函数

添加新评论