我想拦截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
,而不是将其链接到包装器中。挑战在于libc
和rtld-libc
都是从同一个源构建的。我知道宏IS_IN_rtld
是在构建rtld-libc
时定义的,但我如何保证在导出公共接口函数的同时只有一个静态数据结构的副本?(这是一个油嘴滑舌的构建系统问题,但我还没有找到这些细节的文档。(
有没有更好的方法进入dlopen()
?
注意:我不能使用像FUSE
这样的Linux特定解决方案,因为这是针对不支持此类功能的最小"计算节点"内核的。
看起来LD_PRELOAD或-Wl,-wrap将是可行的解决方案
--wrap
解决方案不可能是可行的:它只在(静态(链接时工作,并且您的ld.so
、libc.so.6
和libdl.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.so
,open
的libc.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
(。一旦您找到有趣的CALL
,mprotect
包含它的页面是可写的,并插入您自己的插入器的地址(如果需要,它可以反过来调用__libc_open
(,然后mprotect
将其返回。任何未来的dlopen()
现在都将通过您的插入器。