当前位置: 首页 > news >正文

深入glibc源码:图解_dl_fixup如何解析动态链接函数(附ret2dlresolve利用原理)

深入glibc源码:图解_dl_fixup如何解析动态链接函数

在Linux系统的动态链接机制中,_dl_fixup函数扮演着至关重要的角色。本文将带您深入glibc-2.23源码,通过图解方式剖析这个关键函数的运作原理,并揭示其与ret2dlresolve攻击技术的内在联系。

1. 动态链接基础架构

动态链接是现代操作系统实现代码共享的核心机制。当一个程序调用动态链接库中的函数时,系统不会立即加载该函数的实际代码,而是采用"延迟绑定"(Lazy Binding)技术,在函数第一次被调用时才进行解析和加载。

这种机制依赖于几个关键数据结构:

  • PLT(Procedure Linkage Table):程序链接表,包含跳转到GOT的指令
  • GOT(Global Offset Table):全局偏移表,存储函数地址
  • .rel.plt:重定位表,记录需要重定位的函数信息
  • .dynsym:动态符号表,存储符号信息
  • .dynstr:动态字符串表,存储函数名等字符串

当程序第一次调用动态链接函数时,会经历以下流程:

  1. 跳转到PLT表对应条目
  2. PLT条目跳转到GOT中存储的地址
  3. 第一次调用时,GOT指向PLT中的下一条指令
  4. 该指令将重定位参数压栈并跳转到PLT[0]
  5. PLT[0]将link_map压栈并跳转到_dl_runtime_resolve

2. _dl_fixup函数源码解析

_dl_fixup是动态链接解析的核心函数,位于glibc的dl-runtime.c文件中。让我们逐段分析其实现:

_dl_fixup (struct link_map *l, ElfW(Word) reloc_arg) { // 获取符号表地址 const ElfW(Sym) *const symtab = (const void *) D_PTR (l, l_info[DT_SYMTAB]); // 获取字符串表地址 const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); // 计算重定位项地址 const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset); // 获取符号表项 const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; // 检查重定位类型 assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);

这段代码展示了_dl_fixup如何通过link_map结构访问各种关键数据结构。其中:

  • l_info数组存储了动态段的各种信息
  • DT_JMPREL指向.rel.plt
  • DT_SYMTAB指向.dynsym
  • DT_STRTAB指向.dynstr

函数接着进行符号查找:

if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) { // 复杂查找路径 result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL); value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym->st_value) : 0); } else { // 简化查找路径 value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value); result = l; }

最后,函数将解析得到的地址写入GOT表:

// 写入GOT表 return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);

3. 关键数据结构详解

理解_dl_fixup需要熟悉几个核心数据结构:

3.1 Elf32_Rel结构体

32位系统下的重定位项结构:

typedef struct { Elf32_Addr r_offset; // 需要重定位的地址 Elf32_Word r_info; // 符号索引和重定位类型 } Elf32_Rel;
  • r_offset:指向GOT表中需要修改的位置
  • r_info:低8位表示重定位类型,高24位表示符号表索引

3.2 Elf32_Sym结构体

符号表项结构:

typedef struct { Elf32_Word st_name; // 符号名在字符串表中的偏移 Elf32_Addr st_value; // 符号值 Elf32_Word st_size; // 符号大小 unsigned char st_info;// 符号类型和绑定属性 unsigned char st_other;// 符号可见性 Elf32_Section st_shndx;// 节区索引 } Elf32_Sym;

3.3 link_map结构

link_map是动态链接器的核心数据结构,包含模块的加载信息和各种表的指针:

struct link_map { ElfW(Addr) l_addr; // 模块加载基址 char *l_name; // 模块名称 ElfW(Dyn) *l_ld; // 动态段指针 struct link_map *l_next, *l_prev; // 链表指针 // 更多字段... };

4. ret2dlresolve攻击原理

ret2dlresolve攻击正是基于对_dl_fixup函数解析过程的理解。攻击者通过伪造关键数据结构,"欺骗"动态链接器解析并执行任意函数。

攻击步骤通常包括:

  1. 控制reloc_arg:使重定位项指向可控内存区域
  2. 伪造Elf32_Rel结构:控制r_info指向伪造的符号表项
  3. 伪造Elf32_Sym结构:控制st_name指向伪造的字符串
  4. 伪造函数名字符串:将目标函数名(如"system")写入内存

下表展示了正常解析与攻击伪造的对比:

步骤正常解析ret2dlresolve攻击
重定位项.rel.plt中的合法项伪造的Elf32_Rel结构
符号表项.dynsym中的合法项伪造的Elf32_Sym结构
字符串.dynstr中的函数名伪造的字符串(如"system")

5. 64位系统的特殊考量

64位系统下的ret2dlresolve攻击面临额外挑战:

  1. 数据结构差异

    • 重定位项使用Elf64_Rela结构,包含64位字段和addend
    • 符号表项大小为24字节
  2. 额外验证

    if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) { const ElfW(Half) *vernum = (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff; // ... }

    这段代码会检查符号版本信息,如果伪造的符号表索引过大,可能导致越界访问。

解决方案通常包括:

  • 设置st_other不为0,跳过版本检查
  • 精心构造伪造数据,使索引值合法

6. 防御措施与绕过技术

现代系统采用了多种防护措施对抗ret2dlresolve攻击:

  1. RELRO保护

    • No RELRO:.dynamic等段可写,攻击最简单
    • Partial RELRO:部分段只读,增加攻击难度
    • Full RELRO:所有段在启动时解析并设为只读
  2. 绕过技术

    • 对于Partial RELRO,可以伪造整个link_map结构
    • 利用已解析函数的GOT条目作为伪造的符号表
# 示例:伪造link_map的Python代码片段 def fake_linkmap(fake_addr, known_got, offset): linkmap = p64(offset & (2**64-1)) # l_addr linkmap += p64(0) # 填充 linkmap += p64(fake_addr + 0x18) # DT_JMPREL指针 # 更多伪造数据... return linkmap

7. 实际应用与思考

理解_dl_fixup不仅有助于安全研究,还能加深对动态链接机制的认识。在实际应用中,我们需要注意:

  1. 内存布局:精心安排伪造数据的内存位置
  2. 对齐要求:符号表项需要16字节对齐(32位)或24字节对齐(64位)
  3. 参数控制:确保函数调用时的参数正确传递

通过深入分析glibc源码和动态链接过程,我们不仅能掌握ret2dlresolve攻击技术,更能理解Linux系统底层的工作机制。这种从原理到实践的认知路径,正是安全研究最具价值的部分。

http://www.jsqmd.com/news/954380/

相关文章:

  • 数据分析时代终结?不,是决策增强新范式崛起
  • AWVS新手避坑指南:手把手教你配置DVWA靶场登录序列,避免扫描失败
  • 手机蓝牙发送指令STM32串口接收控制 LED 亮灭
  • 零基础入门:在快马平台上手YOLOv8目标检测第一课
  • vue2 + vue3差异点
  • 【X5】快速调试验证MIPI摄像头
  • 告别卡顿与闪退:Quartus II 13.1在Win10/Win11下的稳定运行与性能优化配置全攻略
  • MATLAB行星齿轮动力学仿真脚本:含ode45求解器与完整系统建模函数
  • 别再只调参了!遗传算法解VRP时,这3个编码细节才是性能关键
  • 你的产品能过EMC认证吗?一文搞懂CS/RS传导辐射抗扰、ESD静电、EFT群脉冲测试要求
  • 企业AI编程解决方案:2026最新权威AI编程工具必看开篇
  • 遗传算法工业实战:四大核心杠杆调优指南
  • 2026 张家界防水补漏三家品牌横向测评:厨卫屋面地下室修缮哪家靠谱?吉修匠 99.8 分五星稳居榜首 - 吉修匠
  • 给Jetson Nano B01换颗‘中国心’:手把手教你配置清华源并安装Python全家桶
  • MinerU2.5 Pro技术解析:1.2B参数SOTA PDF解析模型,完整部署教程(Transformers/vLLM/SGLang/Docker)
  • DenseNet实战:用TensorFlow 2.x在小型数据集上做图像分类,参数少效果也不错
  • 嵌入式新手福音,用快马生成带详解的dma示例代码,轻松攻克直接内存访问
  • 跳出传统 Agent 桎梏,浅析代码即智能体的底层运行逻辑与落地实践
  • 计算机毕业设计之基于Django和Vue的汽车销量数据分析系统的设计与实现
  • 不只是驱动问题:深度解析TI XDS100仿真器EEPROM数据损坏的根源与预防
  • C#上位机开发笔记:封装一个稳定可靠的欧姆龙NX PLC通信类库(附源码)
  • 新手福音:基于快马平台轻松上手吴恩达claude中文手册实践
  • 从‘炼丹’到‘工程’:深度学习中权重初始化和输入归一化的实战避坑指南
  • Anaconda安装后必做的三件事:验证、配环境变量、创建你的第一个Python 3.8虚拟空间
  • 别再死磕D-H参数了!用Matlab Robotic Toolbox 10.4快速复现一个四轴机械臂(附完整代码)
  • MuleSoft企业级AI编排:让大模型真正融入ERP/CRM核心业务流
  • LLM投毒:大模型数据层精准攻击与七道防御体系
  • 2026年高县亲子水上乐园选型指南:龙源溪山泉水乐园深度评测 - 企业名录优选推荐
  • 用NodeMCU和Blinker自制万能红外遥控器,手把手教你让旧家电秒变智能(附完整代码)
  • 不止是游戏!HMS Core 5.2.0的CG Kit体积云特效,还能这样用在你的App里