为什么LD_PRELOAD似乎不能用wc写入

时间:2011-07-01 20:32:38

标签: c linux linker ld

我正在玩LD_PRELOAD来拦截libc调用,似乎写入调用没有被wc拦截,尽管它似乎与cat一起使用。问题的精简版本如下所示。

RedHat Linux 2.6.9-42.ELsmp

生成文件

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

有任何解释吗?

2 个答案:

答案 0 :(得分:7)

这是因为虽然cat使用write,但wc使用printf,可能使用write的内联版本,或者write的引用{1}}绑定到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

答案 1 :(得分:1)

LD_PRELOAD实际上是一种非常糟糕的拦截和重定向呼叫的方法。它仅适用于共享库,并且根据库的链接方式以及使用的优化和内联级别,您要拦截的调用可能无法可靠地拦截。

避免所有这些问题的一个很好的选择,特别是当你要拦截和重写它的系统调用时,正在使用ptrace跟踪/调试接口。不幸的是,目前似乎没有任何工具可以自动化这种方法。