Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I intercept write(2) in bash with LD_PRELOAD?


OK, I get it. You can't override with LD_PRELOAD a libc function when it's called by another libc function.


I was playing with the Dante socksifier and noticed that it doesn't work with bash /dev/udp FDs. Then I wrote a simple .so file with the write function, and it doesn't work with bash too:
libtest.c:

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

test.c:

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

_

$ 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

upd: according to ensc, it has something to do with symbol versions.

What needs to be changed in linking ./test so it would fail just like bash? I mean, with the same .so file the command: $ LD_PRELOAD=./libtest.so ./test would print "abc", because test would bind to a versioned write in glibc.

I'm also trying the opposite - make a .so file with the versioned write. version.script:

GLIBC_2.2.5 {
    write;
};

But my library still can't intercept write in bash

$ gcc -g -O0 -fPIC -shared -Wl,--version-script=./version.script -o libtest.so libtest.c
$ LD_PRELOAD=./libtest.so bash -c 'echo abc'
abc
like image 585
basin Avatar asked Mar 21 '23 21:03

basin


1 Answers

When you look at the used symbols, you will probably see that versioned symbols are used for write:

$ readelf -a test | grep write
48: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND write@@GLIBC_2.2.5

You have to write a linker script with a VERSION section (info ld -> "Version command").

Your case (manipulating bash behavior) is more complex because bash calls printf(3) which in turn uses some internal functions of libc. When you have luck and a weak internal function is called (afais, there is only '__write'), you can overload it in your library.

Else, you have to override the printf() called by bash; you can find it out with 'ltrace'; e.g.:

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

or by setting LD_DEBUG (--> man ld.so)

like image 110
ensc Avatar answered Mar 31 '23 21:03

ensc