能否强制ld解析共享库局部符号?如何在C中读取共享库局部符号值?
能否强制ld忽略局部符号标识完成解析?
答案是不能。原因是你在objdump中看到的带l标记的局部符号(ELF规范中的STB_LOCAL绑定)仅存在于ELF文件的静态符号表(.symtab节)中,静态链接/动态链接阶段ld只会读取动态符号表(.dynsym节)中的全局符号完成解析,局部符号根本不会进入动态符号表,ld完全感知不到该符号的存在,因此没有任何参数可以让ld完成这类符号的解析。
可实现读取局部符号值的替代方案
下面给出4种适合调试/逆向探索场景的可行方案:
方案1:运行时手动解析ELF静态符号表
你可以在C代码中自行解析已加载到进程地址空间的libc.so的ELF结构,从静态符号表中匹配到main_arena的偏移,再结合libc的加载基址计算出实际内存地址,步骤如下:- 读取
/proc/self/maps或者调用dlopen("libc.so.6", RTLD_LAZY)获取libc.so在当前进程中的加载基地址 - 解析基地址指向的ELF头,遍历节表找到静态符号表(
.symtab)和对应字符串表(.strtab)的位置 - 遍历符号表条目,匹配字符串为
main_arena的条目,读取其st_value字段即为该符号相对于libc基址的偏移 - 基地址+偏移得到
main_arena的实际内存指针,按照glibc中struct malloc_state的结构定义即可读取其字段
该方法适用于未被strip的glibc库,不需要预先知道偏移,兼容性较好。
- 读取
方案2:固定环境下硬编码偏移
如果你的运行环境固定(比如仅使用你自己编译的glibc_2.30_no-tcache版本),可以直接硬编码符号偏移:
你用objdump查到的00000000003b4b60就是main_arena相对于libc基址的固定偏移,直接在代码中用libc基址加该偏移即可得到目标地址,示例代码片段如下:#include <dlfcn.h> #include <stddef.h> // 自己参考glibc源码定义malloc_state结构 struct malloc_state { // 按需填充字段... }; #define MAIN_ARENA_OFFSET 0x3b4b60 int main() { void *libc_base = dlopen("libc.so.6", RTLD_LAZY); struct malloc_state *main_arena = (struct malloc_state *)((unsigned long)libc_base + MAIN_ARENA_OFFSET); // 后续读取main_arena字段即可 return 0; }编译时需要加
-ldl参数链接dl库。该方案实现最简单,适合固定调试场景使用。方案3:修改glibc源码重新编译
如果你是自行编译glibc使用,可以直接修改glibc的malloc/arena.c源码,去掉main_arena定义前的static修饰符,重新编译后main_arena就会变成全局符号,直接用extern声明即可正常链接访问,是调试场景下最省心的方案。方案4:借助第三方ELF解析库
可以使用libelf、libbfd这类成熟的ELF解析库帮你完成符号查找逻辑,不需要自己手写ELF解析代码,仅需调用库API即可查询到目标符号的偏移,缺点是需要额外依赖第三方库。
注意事项
以上所有方案仅适用于调试、逆向工程、技术探索场景,绝对不可用于生产环境:不同glibc版本、不同编译参数下main_arena的偏移、结构定义都会发生变化,无任何向前/向后兼容性。
内容的提问来源于stack exchange,提问作者SRobertJames

