与ret2dl有关的前置知识-动态链接

前言

之前写这篇博客的时候没有学习静态链接就去补了补知识,索性把这篇博客也改为基础知识,后面再写具体的攻击方法。看这篇前可以先了解一下静态链接。

一些重要的section

“.interp”

当我们的系统将可执行文件装载之后,其中好多地址还处于无效状态,此时需要动态链接器通过映射的方法加载到进程的地址空间中。操作系统加载完动态链接器后,将控制权交给动态链接器的入口地址。动态链接器获取控制器,执行自身初始化工作,然后根据环境参数对可执行文件进行动态链接工作。当动态链接工作完成后将控制权交由可执行文件入口,程序开始正式执行。

而动态链接器的位置由谁来决定那?实际上是由ELF文件自己决定的,而决定这一切的section叫做.interp

’.interp‘

上图就是某个文件的’.interp’段,大多数情况下动态链接器都位于/lib里,但是这里我用patchelf更改过了,也说明这里是可以更改的,由ELF文件自己决定的。

这个段的内容很简单,里面就保存了上面这样的字符串,而这个字符串就是动态链接器的路径。

“.dynamic”

动态链接最为重要的结构就是.dynamic,这个段保存了动态链接库需要的最基本的信息,比如依赖于哪些共享对象、动态连接符号表的位置、动态链接重定位表位置、共享对象初始化代码的地址等。书上写这个section有点类似于ELF的文件头,确实,其形式更像是ELF Header,而我觉得这个部分功能上更接近SHT,有着和SHT相似的功能,都是帮助我们寻找到section的,或许可以说是二者的结合。

'.dynamic'

其结构体为

typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Word d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
类型 说明
NEEDED 依赖的共享对象文件
INIT 初始化代码地址,也就是.init的位置
FINT 是结束代码地址,也就是.fini的位置
GUN_HASH 动态链接哈希表地址
STRTAB 动态链接字符串表的位置,表示.dynstr的位置
SYMTAB 动态链接符号表的位置,表示.dynsym的位置;
PLTGOT 指向.got的位置
JMPREL 表示重定位表,也就是.rel.plt
REL\RELA 表示动态链接重定位表的位置;RELENT/RELAENT表示动态重读位表入口数量

在静态链接中我们是通过

“.dynsym”,”.dynstr”&”.hash”

在静态链接中有一个专门的段叫做.symtab,里面保存了所有关于该目标的符号的定义和引用.而为了表示动态链接模块间的符号导入导出关系,ELF专门有一个叫做动态链接符号表的段用来保存这些信息,也就是.dynsym.

重要的是”.dynsym”只保存了动态链接相关的符号,而模块内私有的变量则不会保存,这一点和静态链接中符号表是相似的.通常动态链接的模块同时包含:”.symtab”和”。”.dynsm“这两个段。”.symtab”包含所有的符号,也包括”.dynsym”中的符号.

“.strtab”,一个用于保存符号名的字符串表.而”.cybsym”对应的就是动态符号字符串表

.dynstr.用于动态链接中我们需要在程序运行的时候查找符号,为了加快符号的查找过程,往往通过符号哈希表.hash来加快符号查找速度,这个是动态链接独有的.

'.dynsym'

'.hash'

“.rel.dyn”&”.rel.plt”

静态链接中有.rel.text(代码段重定位表)和.rel.data(数据段重定位表)作为静态链接中用于表示重定位信息的重定位表.

在动态链接中自然也有与之对应的重定位表.rel.dyn,.rel,plt.”rel.dtn”实际上是对数据引用的修正,它所修正的位置位于”.got”以及数据段;而”.rel.plt”是对函数引用的修正,它所修正的位置位于”.got.plt”.

'.rel.fyn'&'.rel.plt'

SHT

动态链接和静态链接不同以及解决方法

动态链接和静态链接相同需要解决符号解析和重定位的问题,这部分和静态链接是差不多的,只有其指令修正方式会有所不同。最大的问题在于:由于其共享库数量不能确定,我们不可能进行Segement 合并,当加载多个共享库时,所有的共享库都需要驻留在Mmap中,独自占有一块空间。因此共享库需要支持任意地址被加载,共享库的起始位置只有当运行的时候才会被确定。

位置无关代码(PIC)

GOT

为了可以解决动态链接中出现的这些问题,这里采取PIC技术。PIC的实现依赖于一个事实:EOF文件的Segment是按照它们在磁盘文件中的结构被复制到运行内存的。这一点可能对于我们来说司空见惯了,EOF文件中任何一个付号对于文件的偏移在加载到内存后都是固定的。然而这是实现PIC的关键所在,因为这样的话,任何两个符号之间的偏移就会变成一个固定的值

PIC通过全局偏移表也就是我们经常劫持的GOT表来实现,GOT被储存在.data段中,正如我们上面所说的其关于.text的偏移是固定的。当GOT表在磁盘上的时候其值是为空的,只有当EOF文件被加载到内存后,才有可能让动态链接器根据重定位表和符号表找到符号的绝对地址,将其写入got表。

PLT

GOT解决了共享库的全局变量引用,但是我们还要处理函数调用。这里就需要函数链接表:PLT表。

因为我们不可以修改.text段,所以我们需要采用GOT加PLT的方法,PLT表跳转的位置指向GOT表储存的地址。

延迟绑定

ELF下的延迟绑定过程——PLT,在ELF文件中当我们调用函数都是通过一个叫做PLT项的结构来进行跳转的,假设我们有一个bar()函数,其在PLT中的地址我们称为bar@PLT。那么其实现为

bar@plt:
jmp *(bar@GOT)
push n
push moduleID
jump _dl_runtime_resolve

bar@plt的第一条通过GOT间接跳转指令,假设我们已经进行过延迟绑定,此时bar@GOT里存放这我们真正的bar函数对应的项,那么这个跳转指令自然会跳转到我们想要的地方。但是,如果还没有进行过延迟绑定那,那么此时这里存放的必定不是我们想要的地址,此时bar@GOT存放的其实是bar@plt的第二条指令,第二条指令是将一个数字n压入栈里,而这个数字是bar这个符号引用在重定位表”.rel.plt”的下标。接下来又一个push将指令模块的ID压入栈中,最后再跳转到**_dl_runtime_resolve**。

当bar()这个函数被解析过一次,那么下次调用就不用再次重定位,只需要直接跳转到bar()函数中。ELF将GOT拆为两个表”.got”和 “.got.plt”,其中”.got”来报存全局变量引用地址,”.got.plt”保存函数引用地址。

结语

以上基本阐述了动态链接的过程,接下来就是**_dl_runtime_resolve**这个函数的问题了。这个留在ret2dl再讨论吧

Author: Kr0emer
Link: http://kr0emer.com/2021/08/11/与ret2dl有关的前置知识-动态链接/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.