为什么LD_PRELOAD在Python中不起作用?

9 投票
3 回答
6746 浏览
提问于 2025-04-16 19:58

在Python中使用函数拦截来处理open()这个函数,似乎在前几次调用后就不再有效了。我怀疑Python在做某种初始化,或者有东西暂时绕过了我的函数。

在这里,open的调用明显被拦截了:

$ cat a
hi
$ LD_PRELOAD=./libinterpose_python.so cat a
sandbox_init()
open()
hi

在Python初始化期间,这种情况发生了一次:

$ LD_PRELOAD=./libinterpose_python.so python
sandbox_init()
Python 2.7.2 (default, Jun 12 2011, 20:20:34) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
open()
>>> 
sandbox_fini()

但在这里根本没有发生,而且没有错误提示说明文件句柄的写权限被移除了:

$ LD_PRELOAD=./libinterpose_python.so python3 -c 'b = open("a", "w"); b.write("hi\n"); b.flush()'
sandbox_init()
sandbox_fini()

相关的代码可以在这里找到。使用make -f Makefile.interpose_python进行构建。

完整的解决方案可以在这里找到。

3 个回答

1

原来有一个叫做 open64() 的函数:

$ objdump -T /lib32/libc.so.6  | grep '\bopen'
00064f10 g    DF .text  000000fc  GLIBC_2.4   open_wmemstream
000cc010 g    DF .text  0000007b  GLIBC_2.0   openlog
000bf6d0  w   DF .text  000000b6  GLIBC_2.1   open64
00094460  w   DF .text  00000055  GLIBC_2.0   opendir
0005f9b0 g    DF .text  000000d9  GLIBC_2.0   open_memstream
000bf650  w   DF .text  0000007a  GLIBC_2.0   open
000bf980  w   DF .text  00000081  GLIBC_2.4   openat
000bfb90  w   DF .text  00000081  GLIBC_2.4   openat64

这个 open64() 函数是大文件扩展的一部分,和用 O_LARGEFILE 标志调用 open() 是一样的。

运行示例代码时,如果取消注释 open64 的部分,会得到:

$ LD_PRELOAD=./libinterpose_python.so python3 -c 'b = open("a", "w"); b.write("hi\n"); b.flush()'
sandbox_init()
open64()
open64()
open64()
Traceback (most recent call last):
  File "<string>", line 1, in <module>
open64()
open64()
open64()
open64()
open64()
open64()
open64()
IOError: [Errno 9] Bad file descriptor
sandbox_fini()

这清楚地显示了所有 Python 的 open 调用,以及由于调用中去掉了写入标志而产生的几个错误。

2

你可以通过在 strace 工具下运行你的 Python 程序,来查看它到底在做什么(可能不需要你之前加载的东西)。

我的 Python 3.1(在 AMD64 系统上)确实会使用 open 这个功能:

axa@ares:~$ strace python3.1 -c 'open("a","r+")'
...
open("a", O_RDWR)                       = -1 ENOENT (No such file or directory)
8

有两个函数,分别叫做 open() 和 open64(),你可能需要同时重新定义这两个函数。

撰写回答