调试器中通过ELF、RELA获取共享库函数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 = §ion_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 = §ion_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

