利用原理

该方法的主要原理是利用dl_runtime_resolve函数来对动态链接的函数进行重定位。

在linux下,ELF想要调用动态函数库中的函数,为了避免没必要的消耗,而采用了延迟绑定的方法,其核心思想就是函数用到时才对该函数进行绑定(符号查找及重定位),如果没有用到便不会绑定,以便减少资源的消耗。

运作机制

以图为例

过程

  1. 由于延迟绑定机制,所以在第一次调用puts函数时,0x80496f8内并没有存放着真实的write地址,而是跳转到了下一条指令上。

  2. 然后push 0 再跳转到PLT表的头部,再push 0x80496f0,最后跳转到dl_runtime_resolve函数上

  3. push0,和0x80496f0正是dl_runtime_resolve的两个参数reloc_arglink_map

    • reloc_arg=0是要调用的函数也就是puts在重定向链接表(.rel.plt)中的偏移

    • link_map=0x80496f0则是指向了.dynamic节(GOT+4),使之可以访问到.dynamic

  4. 而dl_runtime_resolve函数内实际上是由**_dl_fixup**实现的,上面的两个参数也都是传到了这个函数里面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    _dl_fixup源代码如下:
    _dl_fixup(struct link_map **l*, ElfW(Word) *reloc_arg*)
    {
    *//* *首先通过参数**reloc_arg**计算重定位入口,这里的**JMPREL**即**.rel.plt**,**reloc_offset**即**reloc_arg*
    const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
    *//* *然后通过**reloc->r_info**找到**.dynsym**中对应的条目*
    const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
    *//* *这里还会检查**reloc->r_info**的最低位是不是**R_386_JUMP_SLOT=7*
    assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
    *//* *接着通过**strtab+sym->st_name**找到符号表字符串,**result**为**libc**基地址*
    result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
    *// value**为**libc**基址加上要解析函数的偏移地址,也即实际地址*
    value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym->st_value) : 0);
    *//* *最后把**value**写入相应的**GOT**表条目中*
    return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
    }
  5. 总结一下**_dl_fixup** 函数的过程
    通过link_map参数得出.rel.plt\.dynsym\.dynstr三个表的地址

    1. 然后通过reloc_arg(_dl_fixup参数)+.rel.plt(起始地址) 计算出函数在.rel.plt(重定向链接表)中偏移后的地址,记作reloc
    2. 然后通过函数在reloc(重定向链接表)中的info参数索引(忽略掉最后两位)来找到它在**.dynsym**(符号表)中的位置,记作sym
    3. 之后通过函数在sym(符号表)中的st_name,来确定它在.dynstr(字符串表)中的名字,记作result,而result也就是libc的基地址
    4. 最后通过result(基地址)+sym->st_value(偏移量,在符号表中的st_value的值)得出函数的真实地址,记作value
    5. 最最后将value写入到函数在GOT表地址内

ELF文件结构概括及readelf

文章中所接触到的三个表都存在于PT_DYNAMIC段的.dynamic节中,下面以readelf来说一下这三个表的主要作用

以图为例

.rel.dyn和.rel.plt

.rel.dyn 用于变量重定位

.rel.plt 用于函数重定位,包含了链接C库的函数

例如:wirte函数

  • r_offset=0x401c
  • r_info=0x807

那么就可以通过r_info去掉最后两位07来确定write在符号表中的位置

.dynsym

包含了动态链接符号表

.dynsym[8]指向的就是write函数,而value的值则是该函数的偏移量

.dynstr

字符串表节区包含了以\x00结尾的字符序列,通常称为字符串

[Ret2dlresolve学习(2)]https://oneda1sy.gitee.io/2020/02/25/rop-ret2dlresolve-2/