我在玩LD_PRELOAD来拦截libc调用,看起来写调用不会被wc拦截,尽管它似乎可以与cat一起使用。下面显示了问题的精简版本。
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_PRELOADn",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, "foon", 4foo
LD_PRELOAD
对于拦截和重定向调用来说确实是一种非常糟糕的方法。它只适用于共享库,根据库的链接方式以及使用的优化和内联级别,您想要拦截的调用可能无法可靠地拦截。
避免所有这些问题的一个很好的替代方案是使用ptrace
跟踪/调试接口,尤其是当它是要拦截和重写的系统调用时。不幸的是,目前似乎没有任何工具可以自动化这种方法。