该方法最大的作用在无法泄露libc时可以调用libc中的函数,其本质是向一个地址,写入一个值,而这两个部分都可以在一定程度上被控制的。
分析源码前,先看一些相关的结构体与宏。
该节存放了许多Elf64_Dyn结构体,在IDA中位于got表的上面,保存了动态链接器所需要的基本信息,比如存放了ELF文件其他节的标识和起始地址。结构体定义如下所示。
其中关键字d_tag定义如下:
该节存放了许多Elf64_Rela结构体,是对函数引用的修正,修正的位置在got.plt表,每个libc库函数都有自己的Elf64_Rela结构体。在程序入口附近,位于LOAD段。
r_info是一个复合值,其高32位表示该重定位项在动态链接符号表.dynsym中对应项的下标,低32位表示该重定位项的重定向类型。
重定位类型(Relocation Types)
32位ELF一般用来函数重定位的重定位类型就是R_386_JMP_SLOT类型,64位ELF函数重定位的重定位类型就是R_X86_64_JUMP_SLOT类型,源码对其的注释是Create PLT entry。这种类型的函数重定位都会在ELF中创建一个PLT入口。
该节,用于普通动态重定位,程序加载时处理,常见定位项:
其中结构体也为Elf64_Rela。
该节存放了许多Elf64_Sym结构体,同样地,每个libc函数都有自己的Elf64_Sym。在程序入口附近,位于LOAD段。
st_info大小为 1 Btyes,高4位表示符号的绑定特征,低4位表示符号类型。
绑定特征(高四位),
绑定特征0,1,2均可取。
符号类型(低四位)
变量、函数分别取1, 2 即可。
那么稍微总结一下得到st_info取值的一般规律如下:
节头表:
即字符串表(STRTAB),该节存放的是libc函数的符号名,就是一个一个的字符串,诸如此类'exit'、'read'。在程序入口附近,位于LOAD段。
link_map 是 glibc 动态链接器 ld-linux 用来描述“一个已加载 ELF 对象”的核心结构体。
一个进程里每加载一个 ELF 对象,就会有一个对应的 struct link_map:
它们通过链表串起来:
该结构体在 ld 中。
无pie示例:
开pie实例:
ELFW( 是 glibc / ELF 代码里常见的宏,用来根据当前平台自动选择 32 位 ELF 类型/宏 或 64 位 ELF 类型/宏。
找 .dynamic 中的 tab 的地址
计算符号 ref 的运行时地址。
elf中的link_map与_dl_runtime_resolve_xsavec()指针在.bss段中.got段开头
下图省略所有被调用函数,调试模块,部分结构体,绿色为常规执行(判断返回为 ture),红色为可控执行流,蓝色为相加

该方法最大的作用在无法泄露libc时可以调用libc中的函数,其本质是向一个地址,写入一个值,而这两个部分都可以在一定程度上被控制:
该利用并不只限制于覆盖got表段,可以覆盖任意位置可被覆盖(可写)的能被调用的函数指针,然后调用 libc 任意位置的代码片段(内核未开启ibt保护时)。
汇编层面,延迟绑定第一次执行函数执行流如下:
所以,有如下两种触发方式:
触发前栈布局:
出发后栈布局:
该模式下,上如所有表段中的结构体可写
有任意写可以根据以上执行流,覆盖任意偏移进行利用;
只有溢出,需泄露pie或无pie,泄露 ld 地址,伪造link_map。
这种情况下思路比较广泛,只需知道调用过程、各结构体位置,就可以根据具体情况利用。
该模式下,所有表段不可被写,但是.bss段的link_map、_dl_runtime_resolve_xsavec()的指针可以被写
有任意写,可以覆盖.bss段指针,伪造link_map;
只有溢出,需泄露pie或无pie,需泄露 ld 地址,伪造link_map。
伪造link_map:
根据上面的执行流,可以有如下的利用思路:

通过将sym结构体伪造在got表附近,使st_value与st_other命中合适位置
使st_other非 0 ,将执行流导向非常规流程(红色)
使vt_value为已经被写过的 libc 函数入口地址,此时link_map为通过参数传入的link_map,通过l_addr调整偏移,到目标代码段地址
由于调整了传入的link_map的l_addr,所以在写rela结构体时,需将r_offset设置为target - l_addr
以上思路,只是需要一个共享器内的地址,以及其版本,就能通过偏移计算调用共享器中的函数,调用 ld 的思路相同。
当开启FULL_RELERO时,整个GOT表将标记为read-only,并且所有的外部引用变量/函数都将在程序装载时由动态链接器解析完成。
此时.got.plt表中的第二项 表项GOT[1]装载的link_map地址 以及第二项 表项GOT[2]装载的dl_runtime_resolve函数地址将是0。
所以此时,利用方式与上面完全一样,仍然可以在.got表进行伪造,只是很难泄露 ld 的地址。
下面介绍一个结构体:
[培训]《冰与火的战歌:Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。