无法通过dlsym获取stat系列函数原指针的技术求助
dlsym无法获取stat/lstat/fstat原始函数指针的问题 我来帮你排查下这个棘手的问题——你尝试Hook stat/lstat/fstat这几个函数,但用dlsym获取原始函数指针时,三个变量都返回NULL,连dlerror()都没给出任何错误信息,这确实挺让人困惑的。结合我处理这类Hook问题的经验,下面是几个常见的原因和对应的解决办法:
1. 必须使用RTLD_NEXT作为dlsym的第一个参数
dlsym默认的查找逻辑是从当前库开始找符号,而我们需要的是系统libc中的原始函数,所以必须传入RTLD_NEXT作为第一个参数,它会告诉dlsym去查找下一个定义该符号的共享库(也就是系统的libc)。
错误的写法可能是用了RTLD_DEFAULT或者直接传NULL,这会导致dlsym在当前Hook库中找不到符号,返回NULL。正确的调用示例:
#include <dlfcn.h> // ... original_stat = (stat_func)dlsym(RTLD_NEXT, "stat");
2. 编译共享库的选项必须正确
编译Hook共享库时,有几个关键选项不能少:
-fPIC:生成位置无关代码,这是共享库的必备条件-shared:告诉编译器生成共享库而非可执行文件-ldl:链接libdl库,因为dlsym/dlerror这些函数都来自这个库
完整的编译命令应该是:
gcc -fPIC -shared -o fakestat.so fakestat.c -ldl
如果漏了-ldl,可能会导致隐式链接失败,dlsym无法正常工作,甚至不会给出明确错误。
3. 注意系统对stat系列函数的宏定义替换
很多Linux系统的stat/lstat/fstat其实是宏定义,比如在glibc中,它们可能被替换为stat64/lstat64/fstat64(为了支持大文件)。这时候你直接用"stat"作为符号名调用dlsym,会找不到对应的函数。
解决方法是在代码开头先取消这些宏定义,让代码中的stat指向真实的函数:
#undef stat #undef lstat #undef fstat
这样再用dlsym(RTLD_NEXT, "stat")就能正确找到原始函数了。
4. 确保LD_PRELOAD的使用方式正确
加载Hook库时,要注意:
- 如果共享库在当前目录,必须加上
./,否则系统会去系统库路径查找,导致找不到:LD_PRELOAD=./fakestat.so ./test - 测试程序必须是动态链接的,如果是静态链接的程序,
LD_PRELOAD不会生效(静态链接程序不会加载外部共享库)。可以用file ./test查看是否是动态链接。
5. 调试技巧:验证系统libc中的符号存在性
如果还是有问题,可以用nm命令查看系统libc中是否存在这些符号:
nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep -E 'stat|lstat|fstat'
输出应该包含类似00000000000f2340 W stat的行,确认符号名和你在dlsym中使用的一致。
完整的Hook示例代码
下面是一个可运行的示例,包含了上述所有注意点:
#define _GNU_SOURCE #include <dlfcn.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> // 取消宏定义,避免函数名被替换 #undef stat #undef lstat #undef fstat // 定义函数指针类型 typedef int (*stat_func)(const char *, struct stat *); typedef int (*lstat_func)(const char *, struct stat *); typedef int (*fstat_func)(int, struct stat *); // 保存原始函数指针(静态变量仅初始化一次) static stat_func original_stat = NULL; static lstat_func original_lstat = NULL; static fstat_func original_fstat = NULL; // Hook stat函数 int stat(const char *pathname, struct stat *buf) { if (!original_stat) { original_stat = (stat_func)dlsym(RTLD_NEXT, "stat"); if (!original_stat) { fprintf(stderr, "Failed to get original stat: %s\n", dlerror()); exit(EXIT_FAILURE); } } // 插入你的Hook逻辑 printf("[Hooked] stat called for path: %s\n", pathname); // 调用原始函数 return original_stat(pathname, buf); } // Hook lstat函数 int lstat(const char *pathname, struct stat *buf) { if (!original_lstat) { original_lstat = (lstat_func)dlsym(RTLD_NEXT, "lstat"); if (!original_lstat) { fprintf(stderr, "Failed to get original lstat: %s\n", dlerror()); exit(EXIT_FAILURE); } } printf("[Hooked] lstat called for path: %s\n", pathname); return original_lstat(pathname, buf); } // Hook fstat函数 int fstat(int fd, struct stat *buf) { if (!original_fstat) { original_fstat = (fstat_func)dlsym(RTLD_NEXT, "fstat"); if (!original_fstat) { fprintf(stderr, "Failed to get original fstat: %s\n", dlerror()); exit(EXIT_FAILURE); } } printf("[Hooked] fstat called for fd: %d\n", fd); return original_fstat(fd, buf); }
编译后用LD_PRELOAD=./fakestat.so ls测试,你应该能看到Hook的输出。
内容的提问来源于stack exchange,提问作者Aneesh Durg

