一、了解LD_PRELOAD
LD_PRELOAD
是Linux/Unix
系统的一个环境变量,它影响程序的运行时的链接(Runtime linker),它允许在程序运行前定义优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。
(一)、程序的链接
程序的链接可以分为以下三种
- 静态链接:在程序运行之前先将各个目标模块以及所需要的库函数链接成一个完整的可执行程序,之后不再拆开。
- 装入时动态链接:源程序编译后所得到的一组目标模块,在装入内存时,边装入边链接。
- 运行时动态链接:原程序编译后得到的目标模块,在程序执行过程中需要用到时才对它进行链接。
静态链接库,在Linux下文件名后缀为.a
,如libstdc++.a
。在编译链接时直接将目标代码加入可执行程序。
动态链接库,在Linux下是.so
文件,在编译链接时只需要记录需要链接的号,运行程序时才会进行真正的“链接”,所以称为“动态链接”。如果同一台机器上有多个服务使用同一个动态链接库,则只需要加载一份到内存中共享。因此, 动态链接库也称共享库 或者共享对象。
(二)、Linux规定动态链接库的文件名规则比如如下:
libname.so.x.y.z
lib
:统一前缀。so
:统一后缀。name
:库名,如libstdc++.so.6.0.21的name就是stdc++。x
: 主版本号 。表示库有重大升级,不同主版本号的库之间是不兼容的。如libstdc++.so.6.0.21的主版本号是6。y
: 次版本号 。表示库的增量升级,如增加一些新的接口。在主版本号相同的情况下, 高的次版本号向后兼容低的次版本号 。如libstdc++.so.6.0.21的次版本号是0。z
: 发布版本号 。表示库的优化、bugfix等。相同的主次版本号,不同的发布版本号的库之间 完全兼容 。如libstdc++.so.6.0.21的发布版本号是21。
(三)、动态链接库的搜索路径搜索的先后顺序
- 编译目标代码时指定的动态库搜索路径(可指定多个搜索路径,按照先后顺序依次搜索);
- 环境变量
LD_LIBRARY_PATH
指定的动态库搜索路径(可指定多个搜索路径,按照先后顺序依次搜索); - 配置文件
/etc/ld.so.conf
中指定的动态库搜索路径(可指定多个搜索路径,按照先后顺序依次搜索); - 默认的动态库搜索路径
/lib
; - 默认的动态库搜索路径
/usr/lib
;
二、利用
我们重写程序运行过程中所调用的函数并将其编译为动态链接库文件,然后通过我们对环境变量的控制来让程序优先加载这里的恶意的动态链接库,进而实现我们在动态链接库中所写的恶意函数。
(一)、具体的操作步骤如下:
- 定义一个函数,函数的名称、变量及变量类型、返回值及返回值类型都要与要替换的函数完全一致。这就要求我们在写动态链接库之前要先去翻看一下对应手册等。
- 将所写的 c 文件编译为动态链接库。
- 对 LD_PRELOAD 及逆行设置,值为库文件路径,接下来就可以实现对目标函数原功能的劫持了
- 结束攻击,使用命令 unset LD_PRELOAD 即可
(二)、用一个 C 语言的 demo 来进行一下测试。
1.创建一个LD_PRELOAD.dir目录,在下面创建一个c语言文件并写入代码
[root@zxw-c LD_PRELOAD.dir]# ll
total 0
[root@zxw-c LD_PRELOAD.dir]# vim whoami.c
[root@zxw-c LD_PRELOAD.dir]# cat whoami.c
#include <stdio.h>
#include <string.h>int main(int argc, char **argv) {
char name[] = "mon";
if (argc < 2) {
printf("usage: %s <given-name>\n", argv[0]);
return 0;
}
if (!strcmp(name, argv[1])) {
printf("\033[0;32;32mYour name Correct!\n\033[m");
return 1;
} else {
printf("\033[0;32;31mYour name Wrong!\n\033[m");
return 0;
}
}
2.编译whoami.c
[root@zxw-c LD_PRELOAD.dir]# vim whoami.c
[root@zxw-c LD_PRELOAD.dir]# gcc -o whoami whoami.c
[root@zxw-c LD_PRELOAD.dir]# ll
total 28
-rwxr-xr-x. 1 root root 18200 Aug 15 06:13 whoami
-rwxrwxrwx. 1 root root 403 Aug 15 06:13 whoami.c
-rw-r--r--. 1 root root 193 Aug 15 06:11 whoami.s
[root@zxw-c LD_PRELOAD.dir]#
查看,此时还没有劫持住,编译后的文件正常判断
3.写一个动态链接库,目标函数为这里进行判断的 strcmp 函数
[root@zxw-c LD_PRELOAD.dir]# vim whoami.s
[root@zxw-c LD_PRELOAD.dir]# cat whoami.s
#include <stdlib.h>
#include <string.h>
int strcmp(const char *s1, const char *s2) {
if (getenv("LD_PRELOAD") == NULL) {
return 0;
}
unsetenv("LD_PRELOAD");
return 0;
}
通过whoami.s生成动态链接文件whoami.so
[root@zxw-c LD_PRELOAD.dir]# gcc -shared -fPIC whoami.s.c -o whoami.so
加载动态链接文件
[root@zxw-c LD_PRELOAD.dir]# export LD_PRELOAD=$PWD/whoami.so
4.测试
如2中查看