LD_PRELOAD是Linux/Unix系统的一个环境变量,它可以影响程序的运行时的链接,它允许在程序运行前定义优先加载的动态链接库。通过这个环境变量,可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖系统的函数库。
LD_PRELOAD 超脱于动态链接库的搜索路径先后顺序之外,它可以指定在程序运行前优先加载的动态链接库。
LD_PRELOAD是Linux系统中的一个环境变量,用于在程序运行时动态加载指定的共享库。LD_PRELOAD的作用是在程序运行前,将指定的共享库加载到程序的内存中。这样,程序在运行时会优先使用该共享库中的符号,而不是系统默认的符号。LD_PRELOAD可以用于替换程序本身的函数,增加程序的功能或者调试程序。
LD_PRELOAD的工作原理是,当程序需要调用某个符号时,系统会先在程序自身的符号表中查找,如果找不到,则会在LD_PRELOAD指定的共享库中查找。如果在LD_PRELOAD指定的共享库中找到了该符号,则使用该符号中的代码。
使用LD_PRELOAD可以实现一些特殊的功能,例如:
动态库劫持:可以用LD_PRELOAD来劫持程序中的函数,替换为自己编写的函数,实现一些特殊的功能。
程序调试:可以用LD_PRELOAD来替换程序中的函数,增加一些调试信息,例如,在程序中调用printf函数时,可以用LD_PRELOAD来替换为自己编写的函数,输出调试信息。
库版本控制:可以用LD_PRELOAD来强制程序使用指定版本的共享库,以避免程序在不同版本的环境中产生兼容性问题。
需要注意的是,使用LD_PRELOAD需要注意一些安全和兼容性问题。为了避免程序崩溃或产生意外的行为,替换的函数必须与被替换的函数具有相同的函数原型和行为。在使用LD_PRELOAD时需要注意共享库与程序之间的交互,避免产生意外的结果。
LD_PRELOAD的使用场景非常广泛,主要包括以下几个方面:
动态库劫持
使用LD_PRELOAD可以劫持程序中的函数,替换为自己编写的函数,实现一些特殊的功能。例如,可以用LD_PRELOAD来Hook系统调用,实现一些系统级别的监控或控制。
程序调试
使用LD_PRELOAD可以替换程序中的函数,增加一些调试信息,例如,在程序中调用printf函数时,可以用LD_PRELOAD来替换为自己编写的函数,输出调试信息。
库版本控制
使用LD_PRELOAD可以强制程序使用指定版本的共享库,以避免程序在不同版本的环境中产生兼容性问题。
代码注入
使用LD_PRELOAD可以向程序中注入一些代码,实现一些与程序无关的功能,例如统计程序使用情况、监控程序运行状态等。
LD_PRELOAD的优势主要包括以下几个方面:
灵活性
使用LD_PRELOAD可以在运行时动态加载指定的共享库,灵活性非常高,可以根据需要替换程序中的任何函数。
易用性
使用LD_PRELOAD非常简单,只需要设置LD_PRELOAD环境变量即可,不需要修改程序本身的代码。
可移植性
使用LD_PRELOAD不依赖于具体的编程语言和操作系统,可以在不同的平台上使用。
高效性
使用LD_PRELOAD可以减少代码修改和重编译的时间,提高程序的开发效率。
动态库劫持是一种利用共享库的特性,在程序运行时动态替换程序中的函数的技术。动态库劫持可以用于Hook系统调用、修改程序行为、监控程序运行状态等方面。下面介绍动态库劫持的概念和实现原理。
概念:
动态库劫持是指通过替换程序中的动态链接库(共享库)中的函数,改变程序的行为的一种技术。在程序启动时,操作系统会在预定义的路径中查找所需要的共享库,加载到内存中,并建立程序和共享库之间的链接关系。在程序运行时,如果程序需要用到共享库中的某个函数,就会通过链接关系在共享库中查找该函数,并调用它。动态库劫持的技术就是通过替换共享库中的函数,来改变程序的行为。
实现原理:
动态库劫持的实现原理是利用了Linux系统中的一个环境变量LD_PRELOAD。LD_PRELOAD是一个环境变量,可以用于在程序运行时动态加载指定的共享库。当程序需要调用共享库中的某个函数时,系统会先在程序自身的符号表中查找,如果找不到,则会在LD_PRELOAD指定的共享库中查找。如果在LD_PRELOAD指定的共享库中找到了该函数,则使用该函数中的代码。
动态库劫持的实现步骤如下:
当程序需要调用被替换的函数时,会先在程序自身的符号表中查找,如果找不到,则会在LD_PRELOAD指定的共享库中查找。如果在LD_PRELOAD指定的共享库中找到了该函数,则使用该函数中的代码,从而实现了动态库劫持。
编写共享库。
首先,需要编写一个共享库,该共享库中包含要劫持的函数的替代函数。例如,如果要劫持系统中的open函数,可以编写一个名为myopen的函数,并将其编译成共享库。
设置LD_PRELOAD环境变量。
在命令行中设置LD_PRELOAD环境变量,指定要加载的共享库。例如,要劫持某个程序,可以在命令行中输入以下命令:
$ LD_PRELOAD=./libmy.so ./myprogram
其中,./libmy.so是编译好的共享库的路径,./myprogram是要运行的程序的路径。
注意:替代函数必须与被替换的函数具有相同的函数原型和行为,否则程序可能会崩溃或产生意外的行为。在使用LD_PRELOAD时需要注意共享库与程序之间的交互,避免产生意外的结果。
使用LD_PRELOAD动态库劫持可以增加程序的功能或者调试程序。需要编写一个共享库,并在命令行中设置LD_PRELOAD环境变量,指定要加载的共享库。在运行要劫持的程序时,程序会自动加载共享库,并使用共享库中的替代函数,从而实现动态库劫持的功能。
LD_PRELOAD和hook很像。
编写如下代码:
#include
#include
#include
#include
#include
int main(int argc,char **argv)
{
int fd;
fd=open("/home/fly/workspace/proj/ld_test.c",O_RDONLY);
if(fd!=-1)
fprintf(stdout,"openfile successed!\n");
else
fprintf(stdout,"openfile failed!\n");
close(fd);
return 0;
}
代码很简单,调用open(…)函数打开文件,输出打开成功或打开失败。直接执行就是调用的系统调用:
$ gcc -o ld_test ld_test.c
$ ./ld_test
openfile successed!
open函数不是我们实现的,是系统的api函数,这是系统为应用程序提供的库函数,即posix api。
现在,我们实现一个open函数,内部调用系统调用__open(char *,int,int)。
#include
extern int __open(char *,int,int);
int open(char *path,int flags,int mode)
{
printf("open : %s\n",path);
return __open(path,flags,mode);
}
将其编译成动态库:
gcc -shared -fPIC -o my_open.so my_open.c
使用LD_PRELOAD执行:
LD_PRELOAD=./my_open.so ./ld_test
执行结果:
open : /home/fly/workspace/proj/ld_test.c
openfile successed!
可以发现,相对前面的直接执行,输出端多了这么一句 ”open : /home/fly/workspace/proj/ld_test.c“ ;这说明调用了我们实现的open()函数。
我们可以自己实现一个函数,底层调用系统调用,理由LD_PRELOAD优先加载我们的函数。这可以有一个应用,就是可以检测内存泄漏,我们实现一个malloc函数,使用的还是系统调用,但是可以在多做一些内存泄漏的检测业务逻辑;和hook一样。
DPDK高性能处理框架VPP的vcl就是利用LD_PRELOAD来加载应用,vcl内部实现了相关的posix api函数。要注意的是,vcl和应用不是运行在同一个进程;VPP的协议栈处理完数据后,是将数据放入一段共享内存中,vcl库从共享内存取出数据给到应用。
LD_PRELOAD是Linux系统中的一个环境变量,用于在程序运行时动态加载指定的共享库,可以用于替换程序本身的函数,增加程序的功能或者调试程序。常见的用途包括:
动态库劫持
可以用LD_PRELOAD来劫持程序中的函数,替换为自己编写的函数,实现一些特殊的功能。例如,可以用LD_PRELOAD来Hook系统调用,实现一些系统级别的监控或控制。
程序调试
可以用LD_PRELOAD来替换程序中的函数,增加一些调试信息,例如,在程序中调用printf函数时,可以用LD_PRELOAD来替换为自己编写的函数,输出调试信息。
库版本控制
可以用LD_PRELOAD来强制程序使用指定版本的共享库,以避免程序在不同版本的环境中产生兼容性问题。
使用LD_PRELOAD需要注意以下几点:
共享库必须是可执行文件,即必须具有x权限。
共享库必须是使用-fPIC编译的,以避免地址重定位问题。
为了避免程序崩溃或产生意外的行为,替换的函数必须与被替换的函数具有相同的函数原型和行为。
在使用LD_PRELOAD时需要注意共享库与程序之间的交互,避免产生意外的结果。
使用LD_PRELOAD可以用以下命令实现:
export LD_PRELOAD=/path/to/mylibrary.so
其中,/path/to/mylibrary.so是要加载的共享库路径。可以在程序运行前使用该命令设置环境变量,或者将该命令添加到程序启动脚本中,以动态加载指定的共享库。