Linux下的LD_PRELOAD环境变量与库打桩

Linux下的LD_PRELOAD环境变量与库打桩

LD_PRELOAD是Linux系统的一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库,一方面,我们可以以此功能来使用自己的或是更好的函数(比如,你可以使用Google开发的tcmalloc来提升效率),而另一方面,我们也可以向别人的程序注入程序,从而达到特定的目的。

我们下面以一个 foepn() 函数的例子来展示一下如何实现运行时库打桩。

正常库函数调用

首先,我们建立一个测试目录并在该目录下创建一个文本文件:

mkdir demo && cd demo
touch test.txt

然后,我们在测试目录下准备一个测试程序,在其中用 foepn() 函数打开我们刚刚创建的文本文件,这个过程应该是成功的,因为我们确实有这样一个文本文件:

// perload_test.c

#include <stdio.h>

int main(){
    printf("Calling the fopen() function \n");

    FILE *fd = fopen("test.txt", "r");
    if (!fd) {
        printf("fopen() returned NULL\n");
        return 1;
    }

    printf("fopen() suceeded\n");
    return 0;
}

我们编译运行这个 preload_test.c

gcc preload_test.c -o preload_test
./preload_test
# 输出:
# Calling the fopen() function
# fopen() suceeded

如我们所预期的,打开文件成功。

运行时库打桩

我们首先准备我们自己想要库打桩的函数 foepn(),在这个函数中无论怎么样都会返回错误:

// myfopen.c

#include <stdio.h>

FILE *fopen(const char *path, const char *node) {
    printf("Always failing fopen in myfopen()\n");
    return NULL;
}

将它编译成一个共享库 myfopen.so

gcc -shared -fpic myfopen.c -o myfopen.so

然后将其添加到 LD_PRELOAD 环境变量:

export LD_PRELOAD=/home/song/demo/myfopem.so

现在我们在来运行一下刚才编译的 preload 程序(无需重新编译):

./preload
# 输出:
# Calling the fopen() function
# Always failing fopen in myfopen()
# fopen() returned NULL

虽然目录下确实有这个文本文件可以被打开,但是由于执行的是我们自己的 fopen() 函数,无论如何都会返回错误。这样就成功地使用我们自己的 fopen() 函数完成了运行时库打桩,在运行时,会优先执行环境变量 LD_PRELOAD 中指定的库打桩函数。

警告:测试完之后记得 unset LD_PRELOAD,否则你会发现你什么文件也打不开了,因为系统级的 fopen() 函数已经被我们打桩替换掉了,不信的话可以在unset之前随便vim一个文本试一下。所以,使用库打桩机制时一定要慎重,否则可能造成不堪设想的后果。

库打桩的应用

库打桩机制有很多有意思的应用场景

  • 通过添加某些语句,可以追踪自己的程序对某些库函数的调用情况
  • 我们可以以此功能来使用自己的或是更好的函数(比如,你可以使用Google开发的tcmalloc来提升效率)
  • 可以向别人的程序注入程序,从而达到特定的目的

Ref:

https://fengmuzi2003.gitbook.io/csapp3e/di-07-zhang-lian-jie

https://www.bilibili.com/video/BV1RK4y1R7Kf?p=7


版权声明:本文为weixin_44966641原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。