该方法的主要原理是利用dl_runtime_resolve
函数来对动态链接的函数进行重定位。
在linux下,ELF想要调用动态函数库中的函数,为了避免没必要的消耗,而采用了延迟绑定
的方法,其核心思想就是函数用到时才对该函数进行绑定(符号查找及重定位),如果没有用到便不会绑定,以便减少资源的消耗。
由于延迟绑定机制,所以在第一次调用puts函数时,0x80496f8
内并没有存放着真实的write地址,而是跳转到了下一条指令上。
然后push 0
再跳转到PLT表的头部,再push 0x80496f0
,最后跳转到dl_runtime_resolve函数上
而push的0
,和0x80496f0
正是dl_runtime_resolve的两个参数reloc_arg
和link_map
reloc_arg=0
是要调用的函数也就是puts在重定向链接表(.rel.plt)中的偏移link_map=0x80496f0
则是指向了.dynamic节(GOT+4),使之可以访问到.dynamic而dl_runtime_resolve函数内实际上是由_dl_fixup实现的,上面的两个参数也都是传到了这个函数里面
_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);
}
总结一下_dl_fixup 函数的过程
通过link_map参数得出.rel.plt
`.dynsym\
.dynstr`三个表的地址
reloc
info
参数索引(忽略掉最后两位)来找到它在.dynsym(符号表)中的位置,记作sym
st_name
,来确定它在.dynstr(字符串表)中的名字,记作result
,而result也就是libc的基地址value
readelf
文章中所接触到的三个表都存在于
PT_DYNAMIC
段的.dynamic
节中,下面以readelf
来说一下这三个表的主要作用
例如:wirte
函数
r_offset
=0x401c
r_info
=0x807
那么就可以通过r_info
去掉最后两位07
来确定write在符号表中的位置
包含了动态链接符号表
.dynsym[8]
指向的就是write函数
,而value
的值则是该函数的偏移量
字符串表节区包含了以\x00
结尾的字符序列,通常称为字符串
Ret2dlresolve学习(2)(https://cloud.tencent.com/developer/article/1740197)