PRELOAD 去欺骗、注入特性和研究程序(2)

/lib64/ld-linux-x86-64.so.2(0x00007f48c07e3000)

正如你看到的输出那样,它列出了被程序 random_nums 所需要的库的列表。这个列表是构建进可执行程序中的,并且它是在编译时决定的。在你的机器上的具体的输出可能与示例有所不同,但是,一个 libc.so 肯定是有的 —— 这个文件提供了核心的 C 函数。它包含了 “真正的” rand()。

我使用下列的命令可以得到一个全部的函数列表,我们看一看 libc 提供了哪些函数:

nm-D /lib/libc.so.6

这个 nm  命令列出了在一个二进制文件中找到的符号。-D 标志告诉它去查找动态符号,因为 libc.so.6 是一个动态库。这个输出是很长的,但它确实在列出的很多标准函数中包括了 rand()。

现在,在我们设置了环境变量 LD_PRELOAD 后发生了什么?这个变量 为一个程序强制加载一些库。在我们的案例中,它为 random_num 加载了 unrandom.so,尽管程序本身并没有这样去要求它。下列的命令可以看得出来:

$ LD_PRELOAD=$PWD/unrandom.so ldd random_nums

linux-vdso.so.1=>(0x00007fff369dc000)

/some/path/to/unrandom.so (0x00007f262b439000)

libc.so.6=>/lib/x86_64-linux-gnu/libc.so.6(0x00007f262b044000)

/lib64/ld-linux-x86-64.so.2(0x00007f262b63d000)

注意,它列出了我们当前的库。实际上这就是代码为什么得以运行的原因:random_num 调用了 rand(),但是,如果 unrandom.so 被加载,它调用的是我们所提供的实现了 rand() 的库。很清楚吧,不是吗?

更清楚地了解

这还不够。我可以用相似的方式注入一些代码到一个应用程序中,并且用这种方式它能够像个正常的函数一样工作。如果我们使用一个简单的 return 0 去实现 open() 你就明白了。我们看到这个应用程序就像发生了故障一样。这是 显而易见的, 真实地去调用原始的 open():

inspect_open.c:

int open(constchar*pathname,int flags){

/* Some evil injected code goes here. */

return open(pathname,flags);// Here we call the "real" open function, that is provided to us by libc.so

}

嗯,不对。这将不会去调用 “原始的” open(...)。显然,这是一个无休止的递归调用。

怎么去访问这个 “真正的” open() 函数呢?它需要去使用程序接口进行动态链接。它比听起来更简单。我们来看一个完整的示例,然后,我将详细解释到底发生了什么:

inspect_open.c:

#define _GNU_SOURCE

#include<dlfcn.h>

typedefint(*orig_open_f_type)(constchar*pathname,int flags);

int open(constchar*pathname,int flags,...)

{

/* Some evil injected code goes here. */

orig_open_f_type orig_open;

orig_open =(orig_open_f_type)dlsym(RTLD_NEXT,"open");

return orig_open(pathname,flags);

}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/b1d2aed790ac3b2e0320209dc9ef7dbc.html