什么是Linux上的LD_PRELOAD招数
简介
LD_PRELOAD 通常用于调试和测试目的,但也可用于恶意目的,如向进程注入恶意软件。
LD_PRELOAD实际上是如何工作的?
LD_PRELOAD环境变量指定了一个共享对象文件的列表,动态链接器应该在任何其他共享对象文件之前加载这些文件。这些共享对象文件被称为 “预加载库”。当一个进程被执行时,动态链接器会搜索LD_PRELOAD中指定的共享对象文件,并将它们加载到进程的地址空间。进程所做的任何函数调用将被引导到预加载库中的实现,而不是系统库或其他共享对象文件中的实现。
示例
例如,考虑这个简单的 “C “程序,它从stdio .h库中调用printf函数—。
#include <stdio.h>
int main() {
printf("Hello, Earth!
");
return 0;
}
输出
Hello, Earth
如果我们编译这个程序并正常执行,printf函数将从libc.so库中调用,这是大多数Linux系统上的标准 “C “库。然而,如果我们设置LD_PRELOAD环境变量指向一个包含printf自定义实现的共享对象文件,动态链接器将加载该共享对象文件而不是libc.so,并且在程序执行时调用printf的自定义实现。
为了设置LD_PRELOAD环境变量,我们可以在终端使用export命令—-。
$ export LD_PRELOAD=/path/to/custom_printf.so
然后,当我们执行程序时,”printf “的自定义实现将被调用,而不是libc.so中的那个。
$ ./a.out
Hello, Earth!
LD_PRELOAD的用例
LD_PRELOAD有几种常见的使用情况 —
调试和测试
LD_PRELOAD最常见的用途之一是覆盖程序中的函数,用于调试和测试。例如,我们可能想写一个printf的自定义实现,记录对该函数的所有调用,或者写一个malloc的自定义实现,检查内存泄漏。通过使用LD_PRELOAD,我们可以很容易地在程序中插入这些自定义的实现,而不需要修改源代码。
动态链接
LD_PRELOAD 也可以用来动态链接一个程序到一个在编译时没有链接的共享对象文件。如果我们想使用一个没有安装在系统上的库,或者想使用一个安装在系统上的库的较新版本,这可能很有用。
恶意软件注入
不幸的是,LD_PRELOAD也可以被用于恶意的目的,例如将恶意软件注入进程中。出于这个原因,在使用LD_PRELOAD时必须谨慎,只使用受信任的共享对象文件。
LD_PRELOAD的限制
在使用LD_PRELOAD时,有一些限制需要考虑 —
仅限功能重写
LD_PRELOAD 只能覆盖通过动态链接器调用的函数。这意味着它不能覆盖直接通过程序代码调用的函数,或静态链接库中实现的函数。
共享对象的依赖性
预装库必须是独立的,不能依赖其他共享对象文件。如果一个预加载库依赖于另一个共享对象文件,动态链接器将无法加载它。
预加载库的顺序
在LD_PRELOAD中指定预加载库的顺序是很重要的。如果两个预加载库都提供了相同函数的实现,将使用在LD_PRELOAD中首先指定的库中的实现。
安全方面的影响
如前所述,LD_PRELOAD可以被用于恶意目的,例如将恶意软件注入进程。在使用LD_PRELOAD时要谨慎,只使用受信任的共享对象文件是很重要的。
例子。重写printf
为了演示LD_PRELOAD是如何工作的,让我们创建一个简单的共享对象文件,提供printf函数的自定义实现。我们首先创建一个名为custom_printf.c的文件,内容如下
#include <studio.h>
int printf(const char *format, ...) {
va_list args;
va_start(args, format);
vprintf("Custom printf: ", args);
va_end(args);
return 0;
}
这个printf的实现只是在输出的开头加上字符串 “Custom printf:”到输出的开头。接下来,我们将使用gcc编译器将custom_printf.c编译成一个共享对象文件—-。
$ gcc -fPIC -shared -o custom_printf.so custom_printf.c
现在,我们可以使用LD_PRELOAD来覆盖程序中的printf函数。首先,我们将设置LD_PRELOAD环境变量,使其指向custom_printf.so —
$ export LD_PRELOAD=/path/to/custom_printf.so
示例
然后,我们将运行一个简单的 “C “程序,调用printf –
#include <stdio.h>
int main() {
printf("Hello, Earth!
");
return 0;
}
输出
Hello, Earth!
当我们运行该程序时,输出将是 “Custom printf。你好,世界!” –
$ ./a.out
Custom printf: Hello, Earth!
我们可以看到,custom_printf.so中提供的printf的自定义实现被调用,而不是libc.so中的那个。
结论
总的来说,LD_PRELOAD是Linux动态链接器中一个强大的功能,它允许用户在进程开始执行之前将共享对象文件预装到进程的地址空间中。这可以用于各种目的,如调试和测试、动态链接和恶意软件注入。然而,在使用LD_PRELOAD时,重要的是要谨慎,只使用受信任的共享对象文件。LD_PRELOAD有一些限制,例如不能覆盖静态链接的函数,以及要求预加载库是自成一体的。了解LD_PRELOAD是如何工作的以及它的局限性可以帮助用户在工作中有效地利用这一功能。