我正在玩LD_PRELOAD拦截libc调用,似乎写入调用不被拦截与wc,虽然它似乎与猫一起工作。 下面显示了一个精简版的问题。
RedHat Linux 2.6.9-42.ELsmp
Makefile文件
writelib: gcc -Wall -rdynamic -fPIC -c write.c gcc -shared -Wl,-soname,libwrite.so -Wl,-export-dynamic -o libwrite.so write.o -ldl
为write.c:
#include <stdio.h> #include <string.h> #ifndef __USE_GNU #define __USE_GNU #define __USE_GNU_DEFINED #endif #include <dlfcn.h> #ifdef __USE_GNU_DEFINED #undef __USE_GNU #undef __USE_GNU_DEFINED #endif #include <unistd.h> #include <stdlib.h> static ssize_t (*libc_write)(int fd, const void *buf, size_t len); ssize_t write(int fd, const void *buf, size_t len) { static int already; ssize_t ret; if (!already) { if ((libc_write = dlsym(RTLD_NEXT, "write")) == NULL) { exit(1); } already = 1; } ret = (*libc_write)(fd,"LD_PRELOAD\n",11); return len; // not ret so cat doesn't take forever }
输出:
prompt: make gcc -Wall -rdynamic -fPIC -c write.c gcc -shared -Wl,-soname,libwrite.so -Wl,-export-dynamic -o libwrite.so write.o -ldl prompt: LD_PRELOAD=./libwrite.so /bin/cat write.c LD_PRELOAD prompt: LD_PRELOAD=./libwrite.so /usr/bin/wc write.c 32 70 572 write.c
任何解释?
这是因为,当cat
使用write
, wc
使用printf
,这可能是使用内联版本的write
,或者其write
引用绑定到libc
,因此不能插入。
这可以很容易地看到使用ltrace
:
$ echo foo | ltrace wc 2>&1 | grep 'write\|print' printf("%*s", 7, "1") = 7 printf(" %*s", 7, "1") = 8 printf(" %*s", 7, "4") = 8 $ echo foo | ltrace cat 2>&1 | grep 'write\|print' write(1, "foo\n", 4foo
LD_PRELOAD
是一个非常糟糕的方法来拦截和重定向调用。 它只适用于共享库,根据库链接的方式以及使用的优化和内联的级别,您要拦截的调用可能无法可靠地被拦截。
避免所有这些问题的一个很好的选择是使用ptrace
跟踪/调试接口,特别是当你想拦截和重写的系统调用时。 不幸的是,目前似乎还没有任何工具可以使这种方法自动化。