pwn return-to-dl-resolve(一)原理分析和认识
字数 1337 2025-08-05 08:16:32

Return-to-dl-resolve 技术原理分析与利用

一、延迟绑定机制基础

ELF文件采用了一种称为"延迟绑定"(lazy binding)的机制,该机制会在第一次调用函数时进行动态链接:

  1. 第一次调用函数时,程序会跳转到PLT表项
  2. PLT表项首先跳转到GOT表中保存的地址
  3. 由于是第一次调用,GOT表中保存的是PLT表项的下一条指令地址
  4. 程序执行push和jmp指令,跳转到PLT[0]
  5. PLT[0]会push link_map结构指针并跳转到_dl_runtime_resolve函数

_dl_runtime_resolve函数原型:

_dl_runtime_resolve(*link_map, rel_offset)

二、ELF文件关键结构

1. 关键节区

  • .dynamic节区:包含动态链接信息,可通过readelf -d查看
  • .rel.plt:用于函数重定位
  • .rel.dyn:用于变量重定位
  • .dynsym:动态符号表
  • .dynstr:动态字符串表
  • .got:全局变量偏移表
  • .got.plt:全局函数偏移表

2. 重要数据结构

Elf32_Rel (重定位表项结构)

typedef struct {
    Elf32_Addr r_offset;  // 重定位位置
    Elf32_Word r_info;    // 符号表索引和重定位类型
} Elf32_Rel;

Elf32_Sym (符号表项结构)

typedef struct {
    Elf32_Word st_name;   // 符号名在.dynstr中的偏移
    Elf32_Addr st_value;
    Elf32_Word st_size;
    unsigned char st_info;
    unsigned char st_other;
    Elf32_Half st_shndx;
} Elf32_Sym;

三、动态解析过程详解

_dl_runtime_resolve函数的解析流程:

  1. 通过reloc_offset找到.rel.plt中的重定位表项

    const PLTREL *const reloc = (const void*)(D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
    
  2. 通过r_info获取符号表索引

    const ElfW(Sym) *sym = &symtab[ELF32_R_SYM(reloc->r_info)];
    
  3. 验证重定位类型为R_386_JMP_SLOT(7)

    assert(ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
    
  4. 通过符号表项获取函数名并查找真实地址

    result = _dl_lookup_symbol_x(strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
    
  5. 将找到的地址写入GOT表并执行

    value = DL_FIXUP_MAKE_VALUE(result, sym ? (LOOKUP_VALUE_ADDRESS(result) + sym->st_value) : 0);
    return elf_machine_fixup_plt(l, result, reloc, rel_addr, value);
    

四、攻击原理与利用方法

1. 攻击思路

  1. 修改.dynstr节中的字符串:将目标函数名改为我们需要的函数名
  2. 伪造重定位表项:修改reloc_offset使其指向我们伪造的重定位表项

2. 关键攻击步骤

  1. 构造伪造的Elf32_Rel结构体,控制r_info指向我们控制的符号表项
  2. 构造伪造的Elf32_Sym结构体,控制st_name指向我们控制的字符串
  3. 确保伪造的字符串是我们想要调用的函数名
  4. 通过栈溢出等手段控制程序执行流,调用_dl_runtime_resolve并传入我们控制的reloc_offset

3. 利用条件

  1. 能够控制程序执行流(如栈溢出)
  2. 能够写入内存特定区域(如.bss段)
  3. 了解目标程序的动态链接信息(各节区地址)

五、防御措施

  1. RELRO保护

    • Partial RELRO:防止.dynamic被修改
    • Full RELRO:在程序启动时解析所有函数,使GOT表只读
  2. ASLR:随机化内存布局,增加预测地址难度

  3. Stack Canary:防止栈溢出攻击

六、调试与分析工具

  1. readelf -d:查看.dynamic节区
  2. readelf -S:查看节区头表
  3. readelf -r:查看重定位表
  4. readelf -s:查看符号表
  5. objdump -d:反汇编程序

七、总结

Return-to-dl-resolve技术利用了ELF动态链接器解析函数地址的过程,通过伪造重定位信息来达到执行任意函数的目的。理解这一技术需要深入掌握ELF文件格式、动态链接机制以及内存布局等知识。防御此类攻击最有效的方法是启用Full RELRO保护。

Return-to-dl-resolve 技术原理分析与利用 一、延迟绑定机制基础 ELF文件采用了一种称为"延迟绑定"(lazy binding)的机制,该机制会在第一次调用函数时进行动态链接: 第一次调用函数时,程序会跳转到PLT表项 PLT表项首先跳转到GOT表中保存的地址 由于是第一次调用,GOT表中保存的是PLT表项的下一条指令地址 程序执行push和jmp指令,跳转到PLT[ 0 ] PLT[ 0]会push link_ map结构指针并跳转到 _dl_runtime_resolve 函数 _dl_runtime_resolve 函数原型: 二、ELF文件关键结构 1. 关键节区 .dynamic节区 :包含动态链接信息,可通过 readelf -d 查看 .rel.plt :用于函数重定位 .rel.dyn :用于变量重定位 .dynsym :动态符号表 .dynstr :动态字符串表 .got :全局变量偏移表 .got.plt :全局函数偏移表 2. 重要数据结构 Elf32_ Rel (重定位表项结构) Elf32_ Sym (符号表项结构) 三、动态解析过程详解 _dl_runtime_resolve 函数的解析流程: 通过reloc_ offset找到.rel.plt中的重定位表项 通过r_ info获取符号表索引 验证重定位类型为R_ 386_ JMP_ SLOT(7) 通过符号表项获取函数名并查找真实地址 将找到的地址写入GOT表并执行 四、攻击原理与利用方法 1. 攻击思路 修改.dynstr节中的字符串 :将目标函数名改为我们需要的函数名 伪造重定位表项 :修改reloc_ offset使其指向我们伪造的重定位表项 2. 关键攻击步骤 构造伪造的Elf32_ Rel结构体,控制r_ info指向我们控制的符号表项 构造伪造的Elf32_ Sym结构体,控制st_ name指向我们控制的字符串 确保伪造的字符串是我们想要调用的函数名 通过栈溢出等手段控制程序执行流,调用 _dl_runtime_resolve 并传入我们控制的reloc_ offset 3. 利用条件 能够控制程序执行流(如栈溢出) 能够写入内存特定区域(如.bss段) 了解目标程序的动态链接信息(各节区地址) 五、防御措施 RELRO保护 : Partial RELRO:防止.dynamic被修改 Full RELRO:在程序启动时解析所有函数,使GOT表只读 ASLR :随机化内存布局,增加预测地址难度 Stack Canary :防止栈溢出攻击 六、调试与分析工具 readelf -d :查看.dynamic节区 readelf -S :查看节区头表 readelf -r :查看重定位表 readelf -s :查看符号表 objdump -d :反汇编程序 七、总结 Return-to-dl-resolve技术利用了ELF动态链接器解析函数地址的过程,通过伪造重定位信息来达到执行任意函数的目的。理解这一技术需要深入掌握ELF文件格式、动态链接机制以及内存布局等知识。防御此类攻击最有效的方法是启用Full RELRO保护。