本文主要分析了在延迟绑定中,调用某函数之后如何找到正确的地址。文章中深入的分析了这个过程,并且分析完之后针对该链接介绍了一些攻击手法和程序所作的一些保护。
在动态链接方式实现以前,普遍采用静态链接的方式来生成可执行文件。 如果一个程序使用了外部的库函数,那么整个库都会被直接编译到可执行文件中。ELF 支持动态链接,这在处理共享库的时候就会非常高效。 当一个程序被加载进内存时,动态链接器会把需要的共享库加载并绑定到该进程的地址空间中。随后在调用某个函数时,对该函数地址进行解析,以达到对该函数调用的目的。
(1)简介: 过程连接表,在程序中以 .plt 节表示,该表处于代码段,每一个表项表示了一个与要重定位的函数相关的若干条指令,每个表项长度为 16 个字节,存储的是用于做延迟绑定的代码。
(2)结构简介:
(1)简介: 全局偏移表,在程序中以 .got.plt 表示,该表处于数据段,每一个表项存储的都是一个地址,每个表项长度是当前程序的对应需要寻址长度(32位程序:4字节,64位程序:8字节)。d_tag = DT_PLTGOT
(2)结构简介:
在下面只对一些接下来要用到的结构体成员做一些中文解释。
(1)介绍:
因为在加载过程中,.dynmic 节整个以一个段的形式加载进内存,所以说在程序中的 .dynmic 节也就是运行后的 .dynmic 段。该段主要与动态链接的整个过程有关,所以保存的是与动态链接相关信息,此处主要用于寻找与动态链接相关的其他节( .dynsym .dynstr .rela.plt 等节)。该段保存了许多 Elf64_Dyn 结构,该数据结构保存了一些其他节的信息。下面展示该段所保存的数据结构。p_type = PT_DYNAMIC(值为 0x2)的段。
(2)结构:
(1)介绍:
动态符号表,存储着在动态链接中所需要的每个函数所对应的符号信息,每个结构体分别对应一个符号 (函数) 。结构体数组。 d_tag = DT_SYMTAB(值为 0x6) 的节。
(2)结构:
(1)介绍:
动态字符串表,表中存放了一系列字符串,这些字符串代表了符号的名称,在此处可以看成函数名,以空字符作为终止符。 该结构是一个字符串数组。d_tag = DT_STRTAB(值为 0x5) 的节。
(2)结构:
一个字符串数组
(1)介绍:
重定位节,保存了重定位相关的信息,这些信息描述了如何在链接或者运行时,对 ELF 目标文件的某部分内容或者进程镜像进行补充或修改。每个结构体也与某一个重定位的函数相关。结构体数组。d_tag = DT_REL(值为 0x11) / d_tag = DT_RELA(值为 0x7) 的节。
(2)结构:
本节讨论的是在 Full RELRO 攻击中用到的结构,所以如果不打算研究该攻击手法可以跳过该节。d_tag = DT_DEBUG。
保存着 Binary 里面所有信息的一个结构体,该结构体很大,内容丰富。
完成延迟绑定的函数主要是 dl_runtime__resolve(link_map_obj, reloc_arg) ,该函数的第一个参数是一个 link_map 结构,第二个参数是一个重定位参数,即运行 PLT 中的代码时 PUSH 进栈中的参数。该函数主要是调用一个 dl_fixup(link_map_obj, reloc_arg) 完成了主要功能。参数一的主要作用是:获得重定位函数所在了 libary 的基地址,以及获取在 libary 中寻找需要定位函数时所需要的 Section (.dynstr .dynsym 等)。第二个函数主要是确定需要解析的函数名,以及解析完之后写回的地址。
该过程可以先大概理解为,dl_fixup 函数通过 reloc_arg 参数确定当前正在解析的函数名。之后,拿着这个函数名,再利用 link_map 结构找到 libary 中的 .dynsym .dynstr 。利用 .dynsym .dynstr 进行匹配。若匹配成功,则从 .dynsym 中获取该函数的函数地址。
【问题 1】通过这个 reloc_arg 可以干什么?
【答案 1】拿到这个 reloc_arg 后,链接器会通过该值找到对应函数的 Elf_Rel 结构,通过该结构的 r_info 变量中的偏移量找到对应函数的 Elf_Sym 结构,然后再通过 Elf_Sym 结构的 st_name 结合之前已经确定的 .dynstr 地址,通过 st_name + .dynstr 获得对应函数的函数名。这就是拿到 reloc_arg 参数后链接器获得的信息,即知道了本次链接中的函数的函数名。(注:此处用到的 binary 中的 Elf_Rel Elf_Sym .dynstr 等地址都是通过 link_map->l_info[x] 的方式寻找的。)
【问题 2】通过 link_map 我们能获得什么?
【答案 2】拿到这个变量后链接器会获得所要解析的函数的函数库(通过 link_map 的 l_next 字段),然后拿到这个外部库之后 link_map 的 l_addr 字段会记录该库的基地址,然后链接器通过 new_hash 函数求出要链接函数的 hash(new_hash(st_name + .dynstr)),然后通过该 hash 和之前的保存值进行匹配,如果匹配上就获得了该函数在外部库的 Elf64_Sym 结构,然后通过该结构的 st_value 获取该函数在外部库里面的偏移,最后通过 st_value + l_addr 获取该函数的真实地址,最后通过 Elf64_Rel 的 r_offset 定位该函数在 GOT 中对应的地址,然后将最后结果写入该地址中。(其中有通过这两个参数共同获得的东西,不过为了便于理解就不再分开讨论。)
RELRO:重定位只读手段
在这种模式下关于重定位并不进行任何保护。
在这种模式下,一些段 (包括.dynamic) 在初始化后将会被标识为只读。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2020-4-22 22:42
被1Oin0编辑
,原因: 两个表对应的中文称呼写反