如何在dlopen()中拦截文件系统访问? [英] How to intercept file system access inside dlopen()?
问题描述
我想拦截dlopen()内部发生的所有文件系统访问.最初,LD_PRELOAD
或-Wl,-wrap,
似乎是可行的解决方案,但是由于某些技术原因,我无法使它们正常工作:
I want to intercept all file system access that occurs inside of dlopen(). At first, it would seem like LD_PRELOAD
or -Wl,-wrap,
would be viable solutions, but I have had trouble making them work due to some technical reasons:
-
ld.so在处理LD_PRELOAD时已经映射了自己的符号.对我来说,拦截初始加载并不重要,但是
_dl_*
worker函数此时已解析,因此将来的调用将通过它们.我认为LD_PRELOAD
为时已晚.
ld.so has already mapped its own symbols by the time LD_PRELOAD is processed. It's not critical for me to intercept the initial loading, but the
_dl_*
worker functions are resolved at this time, so future calls go through them. I thinkLD_PRELOAD
is too late.
以某种方式malloc
绕过了上述问题,因为ld.so内部的malloc()
没有功能性的free()
,它仅调用memset()
.
Somehow malloc
circumvents the issue above because the malloc()
inside of ld.so does not have a functional free()
, it just calls memset()
.
文件系统工作程序功能,例如ld.so
中包含的__libc_read()
是静态的,因此我无法使用-Wl,-wrap,__libc_read
拦截它们.
The file system worker functions, e.g. __libc_read()
, contained in ld.so
are static so I can't intercept them with -Wl,-wrap,__libc_read
.
这可能都意味着我需要直接从源代码构建自己的ld.so
,而不是将其链接到包装器中.这里的挑战是libc
和rtld-libc
都是从同一来源构建的.我知道在构建rtld-libc
时定义了宏IS_IN_rtld
,但是如何在仍然导出公共接口函数的同时保证只有静态数据结构的一个副本? (这是一个glibc构建系统问题,但是我还没有找到这些详细信息的文档.)
This might all mean that I need to build my own ld.so
directly from source instead of linking it into a wrapper. The challenge there is that both libc
and rtld-libc
are built from the same source. I know that the macro IS_IN_rtld
is defined when building rtld-libc
, but how can I guarantee that there is only one copy of static data structures while still exporting a public interface function? (This is a glibc build system question, but I haven't found documentation of these details.)
有什么更好的方法进入dlopen()
吗?
Are there any better ways to get inside dlopen()
?
注意:我不能使用像FUSE
这样的特定于Linux的解决方案,因为这是针对不支持此类事物的最小计算节点"内核.
Note: I can't use a Linux-specific solution like FUSE
because this is for minimal "compute-node" kernels that do not support such things.
推荐答案
似乎LD_PRELOAD或-Wl,-wrap是可行的解决方案
it would seem like LD_PRELOAD or -Wl,-wrap, would be viable solutions
--wrap
解决方案可能不可行:它仅在(静态)链接时有效,并且您的ld.so
和libc.so.6
和libdl.so.2
已经全部链接了,所以现在为时已晚使用--wrap
.
The --wrap
solution could not possibly be viable: it works only at (static) link time, and your ld.so
and libc.so.6
and libdl.so.2
have all already been linked, so now it is too late to use --wrap
.
LD_PRELOAD
可能有用,除了... ld.so认为dlopen()
调用open()
是一个内部实现细节.这样,它仅调用内部__open
函数,绕过PLT
,并具有插入open
的能力.
The LD_PRELOAD
could have worked, except ... ld.so considers the fact that dlopen()
calls open()
an internal implementation detail. As such, it just calls the internal __open
function, bypassing PLT
, and your ability to interpose open
with it.
malloc以某种方式规避了这个问题
Somehow malloc circumvents the issue
这是因为libc
支持实现自己的malloc
的用户(例如出于调试目的).因此,例如dlopen
中的calloc
确实通过了PLT
,并且可以通过LD_PRELOAD
插入.
That's because libc
supports users who implement their own malloc
(e.g. for debugging purposes). So the call to e.g. calloc
from dlopen
does go through PLT
, and is interposable via LD_PRELOAD
.
这可能都意味着我需要直接从源代码构建自己的ld.so,而不是将其链接到包装器中.
This might all mean that I need to build my own ld.so directly from source instead of linking it into a wrapper.
重建的ld.so
会做什么?我认为您希望它调用__llibc_open
(在libc.so.6
中),但是由于明显的原因可能无法正常工作:ld.so
首先是open
s libc.so.6
(在进程启动时)
What will the rebuilt ld.so
do? I think you want it to call __llibc_open
(in libc.so.6
), but that can't possibly work for obvious reason: it is ld.so
that open
s libc.so.6
in the first place (at process startup).
您可以用对__open
的调用替换为对open
的调用来重建ld.so
.这将导致ld.so
通过PLT
,并将其暴露于LD_PRELOAD
插入位置.
You could rebuild ld.so
with the call to __open
replaced with a call to open
. That will cause ld.so
to go through PLT
, and expose it to LD_PRELOAD
interposition.
如果您走这条路,我建议您不要用新副本覆盖系统ld.so
(犯错并导致系统无法启动的机会太大了).而是将其安装到/usr/local/my-ld.so
,然后将您的二进制文件与-Wl,--dynamic-linker=/usr/local/my-ld.so
链接.
If you go that route, I suggest that you don't overwrite the system ld.so
with your new copy (the chance of making a mistake and rendering the system unbootable is just too great). Instead, install it to e.g. /usr/local/my-ld.so
, and then link your binaries with -Wl,--dynamic-linker=/usr/local/my-ld.so
.
另一种选择:运行时修补.这有点骇人听闻,但是您可以(一旦在main中获得控制权)就可以简单地扫描ld.so
的.text
,并查找CALL __open
指令.如果未删除ld.so
,则可以找到内部__open
和要修补的功能(例如dl-load.c
中的open_verify
).找到有趣的CALL
之后,在mprotect
所在页面中将其写入即可,并打入您自己的插入器的地址(如果需要,可以依次调用__libc_open
),然后单击mprotect
背部.现在,将来任何dlopen()
都将通过插入器.
Another alternative: runtime patching. This is a bit of a hack, but you can (once you gain control in main) simply scan the .text
of ld.so
, and look for CALL __open
instructions. If ld.so
is not stripped, then you can find both the internal __open
, and the functions you want to patch (e.g. open_verify
in dl-load.c
). Once you find the interesting CALL
, mprotect
the page that contains it to be writable, and patch in the address of your own interposer (which can in turn call __libc_open
if it needs to), then mprotect
it back. Any future dlopen()
will now go through your interposer.
这篇关于如何在dlopen()中拦截文件系统访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!