LD_PRELOAD是linux下的一个环境变量,动态链接器在载入一个程序所需的所有动态库之前,首先会载入LD_PRELOAD环境变量所指定的动态库。运用这个机制,我们可以修改/替换已有动态库中的方法,加入我们自己的逻辑,从而改变程序的执行行为。不过该方法只对动态链接的程序有效,对静态链接的程序无效。
看一个简单的例子:
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
srand(time(NULL));
int i = 10;
while(i--) printf("%d\n",rand()%100);
return 0;
}
gcc main.c -o main
该程序产生0-99之间的一个随机数,用到了C运行时库中的rand函数,假如我们实现了自己的rand函数,如下:
myrand.c
int rand(){
return 42; //the most random number in the universe
}
我们想让main程序使用我们自己实现的rand函数,同时我们只有main程序的可执行文件,无法通过修改源码实现,那怎么办尼?
首先,我们将myrand.c编译成动态库
gcc -shared -fPIC myrand.c -o myrand.so
然后我们指定LD_PRELOAD并运行main,如下:
LD_PRELOAD=$PWD/myrand.so ./main
结果是程序的每次运行都会返回42
由于动态链接器是按照先后顺序进行符号解析的,当myrand.so首先载入之后,动态链接器已经找到了rand函数,所以会忽略C运行库中的rand实现.
再进一步,我们通过ldd查看两种情况下的链接库顺序,
$ldd ./main
linux-vdso.so.1 => (0x00007ffe3511d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5a693ff000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5a697d4000)
$LD_PRELOAD=./myrand.so ldd ./main
linux-vdso.so.1 => (0x00007ffdc5594000)
./myrand.so (0x00007f623ff20000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f623fb4d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f6240124000)
可以看到,myrand.so在C运行时库libc.so.6之前