我想拦截dlopen()内部发生的所有文件系统访问。起初,似乎LD_PRELOAD
或-Wl,-wrap,
可能是可行的解决方案,但由于某些技术原因,我无法使它们正常工作:
ld.so已经在处理LD_PRELOAD时映射了自己的符号。截取初始加载对我来说并不重要,但此时_dl_*
工作函数已解决,因此将来的调用会通过它们。我认为LD_PRELOAD
为时已晚。
不知怎的malloc
绕过了上面的问题,因为ld.so中的malloc()
没有功能free()
,它只是调用memset()
。
文件系统工作者的功能,例如__libc_read()
中包含的ld.so
是静态的,因此我无法使用-Wl,-wrap,__libc_read
拦截它们。
这可能意味着我需要直接从源代码构建我自己的ld.so
,而不是将其链接到包装器中。挑战是libc
和rtld-libc
都来自同一个来源。我知道在构建IS_IN_rtld
时定义了宏rtld-libc
,但是如何保证在导出公共接口函数时只有一个静态数据结构副本? (这是一个glibc构建系统问题,但我没有找到这些细节的文档。)
有没有更好的方法进入dlopen()
?
注意:我不能使用像FUSE
这样的特定于Linux的解决方案,因为这是针对不支持此类事情的最小“计算节点”内核。
答案 0 :(得分:3)
看起来像LD_PRELOAD或-Wl,-wrap,是可行的解决方案
--wrap
解决方案可能不可行:它仅在(静态)链接时有效,而您的ld.so
和libc.so.6
以及libdl.so.2
都已链接,现在使用--wrap
为时已晚。
LD_PRELOAD
可能有效,除了...... ld.so认为dlopen()
调用open()
内部实现细节。因此,它只是调用内部__open
函数,绕过PLT
,并且可以使用open
来插入libc
。
不知何故,malloc规避了问题
这是因为malloc
支持实现自己calloc
的用户(例如用于调试目的)。所以打电话给例如来自dlopen
的{{1}}确实通过PLT
,并通过LD_PRELOAD
进行插入。
这可能意味着我需要直接从源代码构建自己的ld.so,而不是将其链接到包装器中。
重建ld.so
会做什么?我希望您能够拨打__llibc_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
插入。
如果你走这条路线,我建议你不要用你的新拷贝覆盖系统.text
(犯错的可能性和使系统无法启动的可能性太大了)。而是将其安装到例如ld.so
,然后将您的二进制文件与CALL __open
。
另一种选择:运行时修补。这有点像黑客,但你可以(一旦你获得主要控制权)只需扫描ld.so
的{{1}},并查找__open
指令。如果open_verify
未被删除,则您可以找到内部dl-load.c
和要修补的功能(例如CALL
中的mprotect
)。一旦找到有趣的__libc_open
,mprotect
包含它的页面是可写的,并修补您自己的插入器的地址(如果需要,可以调用dlopen()
) ,然后{{1}}回来。任何未来{{1}}现在都将通过您的插入器。