如何通过LD_PRELOAD定位调用openat系统调用的待拦截函数?
首先得明确一个关键差异:strace追踪的是内核态的系统调用,而LD_PRELOAD只能拦截用户态动态链接的库函数。你之前试的openat、openat64等函数没生效,大概率是因为程序实际调用的不是这些公开的libc封装函数,或者甚至直接绕过libc用syscall指令调用了openat。
下面是不用逐个排查的高效定位方法:
1. 用ltrace追踪用户态函数调用(最直接)
ltrace专门追踪用户态的库函数调用,能直接告诉你程序实际调用了哪个函数来发起openat操作,完全不用猜。执行:
ltrace -f /sbin/depmod 3.10.0-693.17.1.el7.x86_64
输出里会清晰显示类似openat(AT_FDCWD, "/some/path", O_RDONLY|O_CLOEXEC) = 3的行,这里的openat就是你需要拦截的函数名——如果显示的是其他名字(比如__openat、__openat64_2),直接拦截那个名字就行。
2. 分析程序的PLT(过程链接表)
PLT里记录了程序所有需要动态链接的外部函数,是LD_PRELOAD能拦截的唯一目标集合。用objdump或readelf查看:
# 用objdump看PLT里的open相关函数 objdump -R /sbin/depmod | grep -i open # 或者用readelf看动态符号表 readelf -s /sbin/depmod | grep -i open
输出里的符号就是程序实际会调用的动态函数,比如如果看到__openat64,那就是你要拦截的目标——之前用readelf -p .dynstr可能漏看了,因为.dynstr是字符串表,而动态符号表(-s参数)更准确。
3. 检查是否直接调用syscall(绕过libc)
有些程序为了性能,会直接用汇编指令调用syscall,不经过libc的封装函数。这种情况下LD_PRELOAD的普通函数拦截无效,你需要:
- 先确认是否存在直接syscall:
# x86_64下openat的syscall号是257,搜索对应的指令 objdump -d /sbin/depmod | grep -A2 -B2 0xf7 # 0xf7是syscall号257的十六进制
- 如果确实是直接syscall,你可以尝试拦截
syscall函数本身(如果程序用了libc的syscall()封装),或者改用ptrace工具、内核模块来拦截系统调用。
4. 确认程序是否为动态链接
如果/sbin/depmod是静态链接的,LD_PRELOAD完全不起作用——先执行file /sbin/depmod确认:
file /sbin/depmod
如果输出显示statically linked,那你得换其他拦截方式了(比如ptrace、eBPF)。
内容的提问来源于stack exchange,提问作者Mark Galeck

