如何在dlopen()中拦截文件系统访问? [英] How to intercept file system access inside dlopen()?

查看:120
本文介绍了如何在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 think LD_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,而不是将其链接到包装器中.这里的挑战是libcrtld-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.solibc.so.6libdl.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 opens 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆