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

调试器中通过ELF、RELA获取共享库函数GOT地址失败求解

延迟绑定场景下获取GOT中共享库函数地址失败的问题分析

已知可执行文件路径和目标全局函数名(来自共享库),尝试通过以下步骤获取其在GOT中的地址:

  • 定位ELF文件的RELA节,利用sh_link找到dynsym和dynstr节;
  • 匹配RELA条目对应的符号名,得到目标函数的RELA偏移;
  • 在调试器中使用ptrace的PTRACE_PEEKTEXT读取该偏移对应的数据。

但操作后无法获取正确的GOT地址,有时甚至找不到目标符号。当前场景仅使用延迟绑定(Lazy binding),相关代码如下:

unsigned long find_symbol_address(const char* symbol_name, const char* executable_path) {

 int elf_fd = open(executable_path, O_RDONLY);

void *elf = mmap(NULL, lseek(elf_fd, 0, SEEK_END), PROT_READ, MAP_PRIVATE, elf_fd, 0);
Elf64_Ehdr* elf_header = (Elf64_Ehdr*)elf;
Elf64_Shdr* section_h_arr = (Elf64_Shdr*)((char*)elf + elf_header->e_shoff);

Elf64_Shdr sh_str_section = section_h_arr[elf_header->e_shstrndx];

char *sh_str_tbl = (char*)elf + sh_str_section.sh_offset;
Elf64_Half sections_amount = elf_header->e_shnum;

int symbols_amount = 0;

Elf64_Half index_dyn;
Elf64_Half index_str;
Elf64_Rela  *rela;
Elf64_Sym  *dyn;
char *strtab;
char* curr_symbol_name; 
Elf64_Addr offset;



printf("func name %s\n",symbol_name);
for (int j = 0; j < elf_header->e_shnum; j++) {
printf("[%2d] %s\n", j, sh_str_tbl + section_h_arr[j].sh_name);
}

for(int i = 0; i < sections_amount; i++) {

    if(section_h_arr[i].sh_type==SHT_RELA){
    printf("rela is - [%2d] %s\n", i, sh_str_tbl + section_h_arr[i].sh_name);
    rela=(Elf64_Rela*)((char*)elf + section_h_arr[i].sh_offset);
    symbols_amount=section_h_arr[i].sh_size / section_h_arr[i].sh_entsize;
    dyn=(Elf64_Sym*)((char*)elf + section_h_arr[section_h_arr[i].sh_link].sh_offset);
    strtab = ((char*)elf + section_h_arr[section_h_arr[section_h_arr[i].sh_link].sh_link].sh_offset);
    
    for (int j=0;j<symbols_amount;j++)
    {
    
        index_dyn=ELF64_R_SYM(rela[j].r_info);
    
        index_str=ELF64_ST_BIND(dyn[index_dyn].st_info);
    
        curr_symbol_name= strtab + dyn[j].st_name;
    
        printf("current symbol - %s\n",curr_symbol_name);
            
        if (strcmp(curr_symbol_name,symbol_name)==0)
            {
                offset=rela[j].r_offset;
                return offset;
            }

            
        }
    }
    
    
}
return 0;
    
    
    
    
}



//after I get the offset, I used this to get the data from that address.
func_addr=ptrace(PTRACE_PEEKTEXT, child_pid, offset, NULL);
func_addr=func_addr-6;

核心问题点

1. 符号索引匹配完全错误

代码中curr_symbol_name = strtab + dyn[j].st_name是致命错误:RELA条目关联的符号索引是ELF64_R_SYM(rela[j].r_info)得到的index_dyn,而非循环变量j。你用j去索引dynsym表,完全无法对应到当前RELA条目绑定的符号。

2. 错误处理延迟绑定的GOT表项

延迟绑定机制下,未被调用的函数,其GOT表项存储的是PLT跳转代码的地址,而非函数真实地址。你硬编码func_addr=func_addr-6的操作没有任何依据,属于无意义的偏移调整。

要获取真实地址,要么触发函数绑定(让目标进程调用一次该函数),要么直接解析动态链接器维护的运行时符号信息。

3. 未筛选目标RELA节

ELF文件中存在多个RELA节(如.rela.plt和.rela.dyn),其中只有.rela.plt是与PLT/GOT函数绑定相关的节。你遍历所有SHT_RELA类型的节,会处理到无关的RELA条目,导致无法找到目标符号或得到错误地址。

4. 字符串表索引推导错误

strtab = ((char*)elf + section_h_arr[section_h_arr[section_h_arr[i].sh_link].sh_link].sh_offset);这行逻辑错误:dynsym节的sh_link直接指向dynstr节的索引,无需三层嵌套推导。正确的获取方式是:

Elf64_Shdr* dynsym_shdr = &section_h_arr[section_h_arr[i].sh_link];
strtab = (char*)elf + section_h_arr[dynsym_shdr->sh_link].sh_offset;

5. 缺失系统调用错误检查

open、mmap、lseek等系统调用均未做错误检查,一旦调用失败,后续操作会在非法内存上执行,导致隐性错误。

修正后的关键逻辑示例

// 专门定位到.rela.plt节处理函数绑定
for(int i = 0; i < sections_amount; i++) {
    if(section_h_arr[i].sh_type == SHT_RELA && 
       strcmp(sh_str_tbl + section_h_arr[i].sh_name, ".rela.plt") == 0) {
        rela = (Elf64_Rela*)((char*)elf + section_h_arr[i].sh_offset);
        symbols_amount = section_h_arr[i].sh_size / section_h_arr[i].sh_entsize;
        // sh_link指向dynsym节
        Elf64_Shdr* dynsym_shdr = &section_h_arr[section_h_arr[i].sh_link];
        dyn = (Elf64_Sym*)((char*)elf + dynsym_shdr->sh_offset);
        // dynsym的sh_link直接指向dynstr节
        strtab = (char*)elf + section_h_arr[dynsym_shdr->sh_link].sh_offset;
        
        for(int j = 0; j < symbols_amount; j++) {
            index_dyn = ELF64_R_SYM(rela[j].r_info);
            // 跳过空符号
            if(dyn[index_dyn].st_name == 0) continue;
            curr_symbol_name = strtab + dyn[index_dyn].st_name;
            if(strcmp(curr_symbol_name, symbol_name) == 0) {
                // rela[j].r_offset是程序运行时的虚拟地址,直接用于ptrace
                return rela[j].r_offset;
            }
        }
        break;
    }
}

另外需要注意:

  • rela[j].r_offset是程序加载后的虚拟地址,不是文件偏移,可直接作为ptrace的地址参数;
  • 延迟绑定状态下,读取到的值为PLT跳转地址,需触发函数调用完成绑定后,再次读取才能得到真实函数地址。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 09:25:05