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