我看一下GNU C标准库的来源,我看到system
函数的实现调用了__fork()
。我需要使用我自己的__fork
和LD_PRELOAD
技术的包装来拦截该调用。
我想我知道如何使用LD_PRELOAD
因为:
__fork()
,则会被正确拦截。这意味着,原则上可以拦截__fork()
。 __fork()
的标准库实现中将fork()
更改为system()
并重新编译标准库并使用它,则会拦截fork()
。但是,标准库中的__fork()
未被截获 - 我的包装器未被调用。
为什么?
答案 0 :(得分:1)
首先,__fork
是__libc_fork
的同义词,它直接执行fork系统调用。 fork
是一个弱的符号,指的是同一个东西。如果某个其他共享库正在调用该特定函数,则覆盖任何这些函数都将起作用。
$ readelf -Wa /lib/x86_64-linux-gnu/libc.so.6 | grep b84c0
42: 00000000000b84c0 784 FUNC GLOBAL DEFAULT 13 __libc_fork@@GLIBC_PRIVATE
80: 00000000000b84c0 784 FUNC GLOBAL DEFAULT 13 __fork@@GLIBC_2.2.5
408: 00000000000b84c0 784 FUNC WEAK DEFAULT 13 fork@@GLIBC_2.2.5
但是,在libc本身中,链接器知道__fork
位于同一个库中,并且它选择不通过PLT来访问该函数。它只是发出一个直接调用指令。这是GCC在模块调用静态函数时执行的常见优化,或者当库调用其自己的函数之一时。如下所示(如果通过PLT,则呼叫将__fork@plt
):
$ objdump -d /lib/x86_64-linux-gnu/libc.so.6 | grep __fork | head -n 4
6a074: e8 47 e4 04 00 callq b84c0 <__fork@@GLIBC_2.2.5>
00000000000b84c0 <__fork@@GLIBC_2.2.5>:
b84da: 74 5c je b8538 <__fork@@GLIBC_2.2.5+0x78>
b84e1: 74 ed je b84d0 <__fork@@GLIBC_2.2.5+0x10>
当您在内部更改libc以使用fork()时,它正在调用弱符号,这些符号可能是overridden by the user。因此,链接器别无选择,只能发出通过PLT的调用。这意味着您实际上可以使用LD_PRELOAD
重载它。
一般来说,劫持这样的叉子并不容易。函数总是可以直接调用fork系统调用,并且无法拦截它。如果您的代码使用pthread,您可能会对pthread_atfork
感兴趣。这为glibc中的__fork_handlers
数组添加了函数。不幸的是,该数组被标记为受保护,并且无法直接访问该符号。