转自http://blog.csdn.net/guiguzi5512407/article/details/53012878 仅供个人学习
0x01. dl-resolve解析原理
return to dl-resolve 主要是利用Linux glibc的延迟绑定技术(Lazy binding)。Linux下glibc库函数在第一次被调用的时候,才会去寻找函数的真正地址然后进行绑定。在这一过程中,主要由过程链接表(PLT)提供跳转到解析函数的胶水代码,然后将函数的真正地址回填到函数的全局偏移表中,再将控制权交给需要解析的函数。
重定位表(.rel.plt和.rel.dyn)
重定位表中.rel.plt用于函数重定位,.rel.dyn用于变量重定位。
r_offset
即为该函数在.got.plt
中的地址(就是函数的.got.plt地址)。
函数重定位表表项为地址解析函数提供一个参数(r_info的前24位),定位需要解析的函数。
typedef struct
{
Elf32_Addr r_offset; /* Address */
Elf32_Word r_info; /* Relocation type and symbol index */
} Elf32_Rel;
typedef struct
{
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
Elf64_Sxword r_addend; /* Addend */
} Elf64_Rela;
全局偏移表(.got和.got.plt)
全局偏移表中.got 保存全局变量偏移表,.got.plt 保存全局函数偏移表。
刚开始全局函数偏移表(.got.plt)中存放的是:过程链接表PLT中该函数胶水代码中的第二条指令地址。在函数解析之后,才存放了函数的真正地址。
动态链接符号表(.dyn .sym)/动态链接字符串表(.dyn.str)
动态链接符号表是一个结构体数组,保存了每个函数解析是需要的信息,比如函数名在动态链接字符串表中的偏移等等。动态链接字符串表中保存了函数的名称,并且以\x00作为开始和结尾。
解析过程
解析的过程是比较绕的,我们可以根据一条线索来讨论,这样更加清晰
1. 跳转到.plt
2. 跳转到.got.plt(即上一节提到的GOT表真实的名字,.got是变量的地址,.got.plt是函数的地址)
3. 第一次运行,push index,进入PLT 第一个表项(0号表项),进入解析过程
4. 通过index+ (.rel.plt的地址) 找到Elf32_rel结构,这个结构包括两个内容{r_offset, r_info}
5. 通过Elf32_rel结构的r_info找到 dynsym section中的sym项的索引
6. 通过sym项索引在dynsym section中找到Elf32_sym结构,这个结构体包括多个内容{st_name, st_value, st_size, st_info, st_other},其中st_name为字符串表中该字符串的索引
7. 通过st_name,在dynstr中找到字符串
8. 根据字符串找到目标地址