利用LD_PRELOAD实现函数劫持以及用法总结

利用LD_PRELOAD实现函数劫持以及用法总结

端午节的第二天还是在寝室度过的,趁着空闲的时间写写博客还是蛮有意思的。恰好昨天看到一些关于linux下利用LD_PRELOAD环境变量实现函数劫持的文章很感兴趣,所以就打算按照别人的思路试验一番。因为我也是初次了解,所以就引用一段网上的介绍。

LD_PRELOAD,是个环境变量,用于动态库的加载,动态库加载的优先级最高,一般情况下,其加载顺序为LD_PRELOAD > LD_LIBRARY_PATH > /etc/ld.so.cache > /lib>/usr/lib。而程序中我们经常要调用一些外部库的函数。

为什么要当心LD_PRELOAD环境变量?需要说明一下程序的链接。所谓链接,也就是说编译器找到程序中所引用的函数或全局变量所存在的位置。

一般来说,程序的链接分为静态链接和动态链接,静态链接就是把所有所引用到的函数或变量全部地编译到可执行文件中。动态链接则不会把函数编译到可执行文件中,而是在程序运行时动态地载入函数库,也就是运行链接。所以,对于动态链接来说,必然需要一个动态链接库。

动态链接库的好处在于,一旦动态库中的函数发生变化,对于可执行程序来说是透明的,可执行程序无需重新编译。这对于程序的发布、维护、更新起到了积极的作用。对于静态链接的程序来说,函数库中一个小小的改动需要整个程序的重新编译、发布,对于程序的维护产生了比较大的工作量。

我们知道,Linux的用的都是glibc,有一个叫libc.so.6的文件,这是几乎所有Linux下命令的动态链接中,其中有标准C的各种函数。对于GCC而言,默认情况下,所编译的程序中对标准C函数的链接,都是通过动态链接方式来链接libc.so.6这个函数库的。

以rand为例,如果我们有个自定义的rand函数,把它编译成动态库后,通过LD_PRELOAD加载,当程序中调用rand函数时,调用的其实是我们自定义的函数,下面以一个例子说明。

首先我们用C写一段生成10个随机数的代码。

接下来编译执行。

现在我们将自己的rand函数使用如下命令将其编成动态库。

此时已经生成了unrandom.so文件。将其添加到LD_PRELOAD环境变量中并重新执行程序,用LD_PRELOADhack之前的random程序。那么接下来就是见证奇迹的时刻了。

上面的例子说明,我们已经成功将rand函数替换为我们自己所编写的版本。

使用ldd命令可以查看在两种运行方式下所加载的动态库,当直接运行时由于没有加载unrandom.so,因此会使用原本的rand函数,如果我们指定了LD_PRELOAD=unrandom.so,使用ldd查看所加载的so中有我们自己实现的unrandom.so。由于LD_PRELOAD加载顺序最高,因此会优先使用unrandom.so中的rand函数。

充分说明了在指定了LD_PRELOAD,程序执行时是优先使用了我们的动态链接库,从而实现了函数的劫持。

虽然看网上的文章,还有一些其他的示例,但是由于目前能力有限,就对这一个示例进行了试验。下面就对LD_PRELOAD用法进行简单的总结。

  1. 定义与目标函数完全一样的函数,包括名称、变量及类型、返回值及类型等
  2. 将包含替换函数的源码编译为动态链接库
  3. 通过命令 export LD_PRELOAD=”库文件路径”,设置要优先替换动态链接库
  4. 如果找不替换库,可以通过 export LD_LIBRARY_PATH=库文件所在目录路径,设置系统查找库的目录
  5. 替换结束,要还原函数调用关系,用命令unset LD_PRELOAD 解除
  6. 想查询依赖关系,可以用ldd 程序名称

我把上面的代码和程序进行了打包,感兴趣的小伙伴可以自行下载试验一下。

LD_PRELOAD.tar.gz

zgao

如果有什么技术上的问题,可以加我的qq 1761321396 一起交流。