You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

给定_start反汇编,如何编程确定ELF中.plt调用的函数名?

如何通过编程确定PLT段call指令指向的函数名称?

嘿,我来帮你拆解这个问题——你拿到了_start函数的反汇编代码,其中0x4009d4处的call指令目标落在.plt段,现在想通过编程方式找出这个call对应的函数名,对吧?这本质是ELF文件动态符号解析的典型场景,我给你梳理一套清晰的分步流程,还有核心的实现思路:

分步实现流程

1. 解析ELF文件,定位关键段/节

首先你的程序得先读取目标ELF文件,解析它的核心结构:

  • 先读ELF头部(Elf64_Ehdr或Elf32_Ehdr,根据目标是64/32位选择),通过头部找到**节表(Section Header Table)**的位置。
  • 遍历节表,定位三个关键节:
    • .plt:记录PLT跳转条目的节,要记下它的虚拟地址范围和文件偏移。
    • .dynsym:动态符号表,存储所有动态链接函数的符号信息。
    • .dynstr:动态符号字符串表,存储符号对应的名称字符串。

2. 计算call指令的绝对目标地址

x86/x86-64的call指令是相对寻址,所以得把反汇编里的相对偏移转换成绝对地址:

  • 找到0x4009d4处的call指令:x86-64的call机器码以E8开头,后面跟着4字节的有符号相对偏移。
  • 计算绝对地址:目标地址 = 0x4009d4 + 5 + 相对偏移(因为call指令本身占5字节,偏移是相对于下一条指令的地址)。
  • 验证这个地址确实落在.plt段的地址范围内,符合你的前提条件。

3. 从PLT条目提取动态符号索引

PLT的每个条目对应一个动态函数的跳转逻辑,x86-64的PLT条目结构大概是这样:

0x400XXXXX: jmp QWORD PTR [GOT条目地址]  ; 6字节机器码: FF 25 XX XX XX XX
0x400XXXXX+6: push 0xN                   ; 5字节机器码: 68 N N N N
0x400XXXXX+0xB: jmp 0x400ZZZZZ           ; 5字节机器码: E9 XX XX XX XX

其中push 0xN里的N就是这个PLT条目对应的动态符号表索引。

所以你需要:

  • 跳到PLT目标地址,跳过前面6字节的jmp指令。
  • 读取push指令后的立即数N,这就是我们要的符号索引。

4. 从动态符号表+字符串表获取函数名

拿到符号索引N后:

  • 从.dynsym表中取出第N个符号条目(Elf64_Sym结构),其中st_name字段是该符号名称在.dynstr节中的偏移量。
  • 把.dynstr节的起始地址加上这个偏移,就能读取到以null结尾的字符串——这就是call指令指向的函数名称了。

核心代码示例(伪代码)

// 假设已通过mmap或文件读取加载了ELF文件,解析好头部和节表
Elf64_Ehdr *ehdr = (Elf64_Ehdr*)file_base;
Elf64_Shdr *dynsym_shdr = find_section_by_name(ehdr, ".dynsym");
Elf64_Shdr *dynstr_shdr = find_section_by_name(ehdr, ".dynstr");
Elf64_Sym *dynsym_table = (Elf64_Sym*)(file_base + dynsym_shdr->sh_offset);
char *dynstr_table = (char*)(file_base + dynstr_shdr->sh_offset);

// 计算call指令的目标PLT地址
uint8_t *call_insn = (uint8_t*)(file_base + (0x4009d4 - ehdr->e_entry + load_base)); // 处理加载基址或直接用虚拟地址
int32_t rel_offset = *(int32_t*)(call_insn + 1);
uint64_t plt_target = 0x4009d4 + 5 + rel_offset;

// 提取PLT条目中的符号索引
uint8_t *plt_entry = (uint8_t*)plt_target;
plt_entry += 6; // 跳过开头的jmp指令
if (*plt_entry == 0x68) { // 确认是push imm32指令
    uint32_t sym_idx = *(uint32_t*)(plt_entry + 1);
    Elf64_Sym *target_sym = &dynsym_table[sym_idx];
    char *func_name = dynstr_table + target_sym->st_name;
    printf("Call指令指向的函数名: %s\n", func_name);
}

注意事项

  • 要区分32位和64位ELF的结构差异:比如符号表结构大小、PLT条目的长度都不一样,代码里要做分支处理。
  • 建议用mmap映射整个ELF文件到内存,这样虚拟地址的计算更直接,避免手动转换文件偏移和虚拟地址。
  • 这个流程只适用于动态链接的ELF文件(因为静态链接没有PLT段),刚好符合你的场景。

内容的提问来源于stack exchange,提问作者user7183233

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:44:28