为什么我不能用LD_PRELOAD拦截bash中的write(2)?

bas*_*sin 3 c linux shared-libraries


好的我明白了.当libc函数被另一个libc函数调用时,你不能用LD_PRELOAD覆盖它.


我正在玩Dante socksifier,并注意到它不能与bash/dev/udp FD一起使用.然后我用write函数编写了一个简单的.so文件,它也不能用于bash
libtest.c:

#include <unistd.h>
ssize_t write(int fildes, const void *buf, size_t nbyte)
{
  return nbyte;
}
Run Code Online (Sandbox Code Playgroud)

test.c:

#include <unistd.h>
int main(int argc, char *argv[]) {
  write(1,"abc\n",4);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

_

$ gcc -g -O0 -fPIC -shared -o libtest.so libtest.c
$ gcc -g -O0 -o test test.c
$ ./test
abc
$ LD_PRELOAD=./libtest.so ./test
$ LD_PRELOAD=./libtest.so bash -c 'echo abc'
abc
Run Code Online (Sandbox Code Playgroud)

upd:根据ensc,它与符号版本有关.

链接./test需要改变什么才能像bash一样失败?我的意思是,使用相同的.so文件命令:$ LD_PRELOAD=./libtest.so ./test将打印"abc",因为test它将绑定到writeglibc中的版本.

我也正在尝试相反的方法 - 使用版本化的.so文件write.version.script:

GLIBC_2.2.5 {
    write;
};
Run Code Online (Sandbox Code Playgroud)

但我的图书馆仍然无法拦截writebash

$ gcc -g -O0 -fPIC -shared -Wl,--version-script=./version.script -o libtest.so libtest.c
$ LD_PRELOAD=./libtest.so bash -c 'echo abc'
abc
Run Code Online (Sandbox Code Playgroud)

ens*_*nsc 5

当您查看使用过的符号时,您可能会看到版本化符号用于write:

$ readelf -a test | grep write
48: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND write@@GLIBC_2.2.5
Run Code Online (Sandbox Code Playgroud)

您必须编写带有VERSION部分的链接描述文件(info ld - >"Version command").

你的情况(操纵bash行为)更复杂,因为bash调用printf(3),而printf(3)又使用了libc的一些内部函数.当你运气好并且调用弱内部函数时(afais,只有'__write'),你可以在库中重载它.

否则,你必须覆盖bash调用的printf(); 你可以用'ltrace'找到它; 例如:

$ ltrace  bash -c 'echo abc'
__printf_chk(1, "%s", "abc")                                  = 3
_IO_putc('\n', 0x7fc5eeac3420abc
Run Code Online (Sandbox Code Playgroud)

或者通过设置LD_DEBUG( - > man ld.so)