如何拦截GNU C标准库中的__fork()?

时间:2016-03-23 11:06:52

标签: ld-preload

我看一下GNU C标准库的来源,我看到system函数的实现调用了__fork()。我需要使用我自己的__forkLD_PRELOAD技术的包装来拦截该调用。

我想我知道如何使用LD_PRELOAD因为:

  1. 如果我在我的应用程序中自己调用__fork(),则会被正确拦截。这意味着,原则上可以拦截__fork()
  2. 如果我在__fork()的标准库实现中将fork()更改为system()并重新编译标准库并使用它,则会拦截fork()
  3. 但是,标准库中的__fork()未被截获 - 我的包装器未被调用。

    为什么?

1 个答案:

答案 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数组添加了函数。不幸的是,该数组被标记为受保护,并且无法直接访问该符号。