Linux Tips 之 狸猫换太子:LD_PRELOAD

来源:互联网 发布:哪里可以下载外汇数据 编辑:程序博客网 时间:2024/05/17 04:24

$ cat 1.c
void *malloc ( unsigned sz ) { return 0; }
$ gcc -shared 1.c -o libmyc.so
$ cat 2.c
#include <stdlib.h>
int main () {
  void *p = malloc ( 3 );
  return p == NULL;
}
$ gcc 2.c
$ ./a.out
$ echo $?
0
$ LD_PRELOAD="libmyc.so" ./a.out
./a.out: error while loading shared libraries: libmyc.so: cannot open shared object file: No such file or directory
$ LD_LIBRARY_PATH="." LD_PRELOAD="libmyc.so" ./a.out
$ echo $?
1

看出什么了么?虽然我没有把 2.c 在编译时链接到我的 libmyc.so 上,但是在运行时通过指定 LD_PRELOAD(必要时需要搭配 LD_LIBRARY_PATH 使 ld.so 可以找到必须的库文件),用 libmyc.so 中的函数覆盖 glibc 中的同名函数达到替换的作用。因为 man pages section 2 所说的 syscall 都是 glibc 中实现的包装函数,因此也可以使用这个方法进行替换(经试验,open(2) 替换成功)。这个方法非常灵活方便,可以用来制作调试库用于检测内存泄漏,以及其它各种用途,不一而足。

开始考虑实现一个自己的 filesystem sandbox 。。。

参考:ld.so(8) manual

-----------------------------------------------------------------------------------------------

[13 Hours later...]

# cat sandbox.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

int open (const char *path, int oflag, ...)
{
        int rtfd;
        void *dlhandler = dlopen ( "libc.so.6", RTLD_LAZY );
        if ( dlhandler == NULL ) {
                fprintf ( stderr, dlerror() );
                exit ( 1 );
        }

        int (*glibc_open) ( const char *, int, ... );
        glibc_open = dlsym ( dlhandler, "open" );
        if ( glibc_open == NULL ) {
                fprintf ( stderr, dlerror() );
                exit ( 1 );
        }

        printf ( "Opening file: %s/n", path );
        va_list ap;
        va_start ( ap, oflag );
        mode_t mode = va_arg ( ap, mode_t );
        rtfd = (*glibc_open) ( path, oflag, mode );
        va_end ( ap );

        return rtfd;
}

int open64 (const char *path, int oflag, ...)
{
        int rtfd;
        void *dlhandler = dlopen ( "libc.so.6", RTLD_LAZY );
        if ( dlhandler == NULL ) {
                fprintf ( stderr, dlerror() );
                exit ( 1 );
        }

        int (*glibc_open) ( const char *, int, ... );
        glibc_open = dlsym ( dlhandler, "open64" );
        if ( glibc_open == NULL ) {
                fprintf ( stderr, dlerror() );
                exit ( 1 );
        }

        printf ( "Opening file: %s/n", path );
        va_list ap;
        va_start ( ap, oflag );
        mode_t mode = va_arg ( ap, mode_t );
        rtfd = (*glibc_open) ( path, oflag, mode );
        va_end ( ap );

}
#

编译成 sandbox.so,然后用 LD_PRELOAD 加载,基本可以“监视”各种程序对文件系统的访问了。
发现这里的一个问题,就是说程序必须是动态链接到 libc 的时候,open 函数才会调用我给出的版本。如果程序和我的 sandbox.so 一样,通过 dlopen / dlsym 查找 open 的入口直接调用,则 ld-linux.so 不起作用,我的“钩子”函数也就失去了效果。

原创粉丝点击