如何在dlopen()中拦截文件系统访问



我想拦截dlopen((内部发生的所有文件系统访问。起初,LD_PRELOAD-Wl,-wrap,似乎是可行的解决方案,但由于一些技术原因,我很难使它们发挥作用:

  • 到处理ld_PRELOAD时,ld.so已经映射了它自己的符号。拦截初始加载对我来说并不重要,但_dl_*工作函数此时已解析,因此将来的调用将通过它们。我认为LD_PRELOAD太晚了。

  • malloc以某种方式规避了上述问题,因为ld.so内部的malloc()没有函数free(),它只是调用memset()

  • ld.so中包含的文件系统工作函数(例如__libc_read()(是静态的,因此我无法用-Wl,-wrap,__libc_read截取它们。

这可能意味着我需要直接从源代码构建自己的ld.so,而不是将其链接到包装器中。挑战在于libcrtld-libc都是从同一个源构建的。我知道宏IS_IN_rtld是在构建rtld-libc时定义的,但我如何保证在导出公共接口函数的同时只有一个静态数据结构的副本?(这是一个油嘴滑舌的构建系统问题,但我还没有找到这些细节的文档。(

有没有更好的方法进入dlopen()

注意:我不能使用像FUSE这样的Linux特定解决方案,因为这是针对不支持此类功能的最小"计算节点"内核的。

看起来LD_PRELOAD或-Wl,-wrap将是可行的解决方案

--wrap解决方案不可能是可行的:它只在(静态(链接时工作,并且您的ld.solibc.so.6libdl.so.2都已经链接,所以现在使用--wrap为时已晚。

LD_PRELOAD本来可以工作的,除了。。。ld.so认为dlopen()调用open()是一个内部实现细节。因此,它只是调用内部__open函数,绕过PLT,以及您插入open的能力。

malloc以某种方式规避了问题

这是因为libc支持实现自己的malloc的用户(例如用于调试目的(。因此,从dlopen到例如calloc的调用确实经过PLT,并且可经由LD_PRELOAD插入。

这可能意味着我需要直接从源代码构建自己的ld.so,而不是将其链接到包装器中。

重建后的ld.so会做什么?我认为您希望它调用__libc_open(在libc.so.6中(,但这不可能工作,原因很明显:首先是ld.soopenlibc.so.6(在进程启动时(。

您可以重建ld.so,将对__open的调用替换为对open的调用。这将导致ld.so通过PLT,并将其暴露于LD_PRELOAD的插入。

如果你走这条路,我建议你不要用你的新副本覆盖系统ld.so(犯错误并使系统无法启动的可能性太大了(。相反,将其安装到例如/usr/local/my-ld.so,然后将二进制文件与-Wl,--dynamic-linker=/usr/local/my-ld.so链接。

另一种选择:运行时修补。这有点像黑客,但你可以(一旦你在主控中获得控制权(简单地扫描ld.so.text,然后寻找CALL __open指令。如果未剥离ld.so,则可以找到内部__open和要修补的函数(例如dl-load.c中的open_verify(。一旦您找到有趣的CALLmprotect包含它的页面是可写的,并插入您自己的插入器的地址(如果需要,它可以反过来调用__libc_open(,然后mprotect将其返回。任何未来的dlopen()现在都将通过您的插入器。

最新更新