什么是ret2dl攻击
- 当程序在第一次加载某个函数的时候,got表中对应的表项还没有写入真实的地址(因为是第一次调用),所以这个时候就需要调用
_dl_runtime_resolve
函数将真实的地址写入got表对应的表项中,然后将控制权交还这个函数,此时就完成了第一次的调用,got表中也有了对应的真实地址。
整个调用过程梳理
_dl_fixup之前
- 当我们第一调用read时,他的跳转过程是
read@plt -> read@got -> read@plt -> plt[0] -> _dl_runtime_resolve_xsave -> _dl_fixup
我们可以再看一下0x4004e0到底是什么。
已知 <read@plt> 是plt中的第一个函数,那么0x4004e0就是plt[0]即plt的首项。这里对应两条汇编,第一条实际上做了一个push操作,他负责将一个参数压栈。这里需要实际解释一下:在_dl_runtime_resolve_xsave
中实际调用了_dl_fixup
而忽略掉宏定义,这个函数是: ```c / This function is called through a special trampoline from the PLT the first time each PLT entry is called. We must perform the relocation specified in the PLT of the given shared object, and return the resolved function address to the trampoline, which will restart the original call to that address. Future calls will bounce directly from the PLT to the function. /DL_FIXUP_VALUE_TYPE attribute_hidden __attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE _dl_fixup (struct link_map *l, ElfW(Word) reloc_arg)
```
可以看到一共有两个参数:struct link_map *l, ElfW(Word) reloc_arg
,然后我们再回过来看刚刚的代码,其实两个push就是做了参数压栈:
_dl_fixup进入以后
- 嗯这、这里是关键,我们一步步分析一下
_dl_fixup
源代码。在这之前,我们先要研究一下几个重要的section和段。
.dynamic
.dynamic是动态链接中非常重要和经典的一个段。首先看一下ELF64_Dyn
的结构如下:
// 该结构都有 64 位程序和 32 位程序的区别,不过大致结构相似,此处只讨论 64 位程序中的
// /usr/include/elf.h
typedef struct
{
Elf64_Sxword d_tag; /* Dynamic entry type */
// d_tag 识别该结构体表示的哪一个节,通过以此字段不同来寻找不同的节
union
{
Elf64_Xword d_val; /* Integer value */
// 对应节的地址,用于存储该结构体表示下的节所在的地址
Elf64_Addr d_ptr; /* Address value */
} d_un;
} Elf64_Dyn;
而在.dynamic之中有几个重要的section如下: | d_tag类型 | d_un含义 | | ------ | ------ | | DT_SYMTAB | 动态链接符号表的地址,d_ptr指向".dynsym"的地址 | | DT_STRTAB | 动态链接字符串表的地址 ,d_ptr指向".dynstr"的地址 | | DT_JMPREL | 动态链接重定位表的信息,d_ptr指向".rel.plt"地址 | | DT_VERSYM | .gnu.version的节的位置 |
.dynsym
其中,ELF64_Sym结构体为:
typedef struct
{
Elf64_Word st_name; /* Symbol name (string tbl index) */
// 保存着该函数函数名在 .dynstr 中的偏移,可以结合 .dynstr 找到准确函数名。
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf64_Section st_shndx; /* Section index */
Elf64_Addr st_value; /* Symbol value */
// 如果这个符号被导出,则存有这个导出函数的虚拟地址,否则为NULL.
Elf64_Xword st_size; /* Symbol size */
} Elf64_Sym;
.dynstr
.rel.plt
其中,ELF64_Rela结构体为:
typedef struct {
Elf64_Addr r_offset; //保存的是应用重定位操作的偏移量。对于一个重定位文件,这个值是从节开始到受重定位影响的存储单元的偏移量。对于一个可执行或共享目标文件,这个值是受重定位影响的存储单元的虚拟地址。
uint64_t r_info; //This member gives both the symbol table index with respect to which the relocation must be made and the type of relocation to apply.
int64_t r_addend; //指定了一个附加的常数用来去计算保存在重定位位置的值。
} Elf64_Rela;
link_map结构
struct link_map
{
ElfW(Addr) l_addr; /* Base address shared object is loaded at. */
char *l_name; /* Absolute file name object was found in. */
ElfW(Dyn) *l_ld; /* Dynamic section of the s