动态连接的诀窍：使用 LD_PRELOAD 去欺骗、注入特性和研究程序
分析 安全 注入
![](/data/attachment/album/201712/24/214230mevm8eqvmmqmvvhv.jpg)


**本文假设你具备基本的 C 技能**


Linux 完全在你的控制之中。虽然从每个人的角度来看似乎并不总是这样，但是高级用户喜欢去控制它。我将向你展示一个基本的诀窍，在很大程度上你可以去影响大多数程序的行为，它并不仅是好玩，在有时候也很有用。


### 一个让我们产生兴趣的示例


让我们以一个简单的示例开始。先乐趣，后科学。


*random\_num.c：*



```
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(){
  srand(time(NULL));
  int i = 10;
  while(i--) printf("%d\n",rand()%100);
  return 0;
}

```

我相信，它足够简单吧。我不使用任何参数来编译它，如下所示：



```
gcc random_num.c -o random_num

```

我希望它输出的结果是明确的：从 0-99 中选择的十个随机数字，希望每次你运行这个程序时它的输出都不相同。


现在，让我们假装真的不知道这个可执行程序的出处。甚至将它的源文件删除，或者把它移动到别的地方 —— 我们已不再需要它了。我们将对这个程序的行为进行重大的修改，而你并不需要接触到它的源代码，也不需要重新编译它。


因此，让我们来创建另外一个简单的 C 文件：


*unrandom.c：*



```
int rand(){
    return 42; //the most random number in the universe
}

```

我们将编译它进入一个共享库中。



```
gcc -shared -fPIC unrandom.c -o unrandom.so

```

因此，现在我们已经有了一个可以输出一些随机数的应用程序，和一个定制的库，它使用一个常数值 `42` 实现了一个 `rand()` 函数。现在 …… 就像运行 `random_num` 一样，然后再观察结果：



```
LD_PRELOAD=$PWD/unrandom.so ./random_nums

```

如果你想偷懒或者不想自动亲自动手（或者不知什么原因猜不出发生了什么），我来告诉你 —— 它输出了十次常数 42。


如果先这样执行



```
export LD_PRELOAD=$PWD/unrandom.so

```

然后再以正常方式运行这个程序，这个结果也许会更让你吃惊：一个未被改变过的应用程序在一个正常的运行方式中，看上去受到了我们做的一个极小的库的影响 ……


**等等，什么？刚刚发生了什么？**


是的，你说对了，我们的程序生成随机数失败了，因为它并没有使用 “真正的” `rand()`，而是使用了我们提供的的那个 —— 它每次都返回 `42`。


**但是，我们*告诉过*它去使用真实的那个。我们编程让它去使用真实的那个。另外，在创建那个程序的时候，假冒的 `rand()` 甚至并不存在！**


这句话并不完全正确。我们只能告诉它去使用 `rand()`，但是我们不能去选择哪个 `rand()` 是我们希望我们的程序去使用的。


当我们的程序启动后，（为程序提供所需要的函数的）某些库被加载。我们可以使用 `ldd` 去学习它是怎么工作的：



```
$ ldd random_nums
linux-vdso.so.1 => (0x00007fff4bdfe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f48c03ec000)
/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(const char *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>

typedef int (*orig_open_f_type)(const char *pathname, int flags);

int open(const char *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);
}

```

`dlfcn.h` 是我们后面用到的 `dlsym` 函数所需要的。那个奇怪的 `#define` 是命令编译器去允许一些非标准的东西，我们需要它来启用 `dlfcn.h` 中的 `RTLD_NEXT`。那个 `typedef` 只是创建了一个函数指针类型的别名，它的参数等同于原始的 `open` —— 它现在的别名是 `orig_open_f_type`，我们将在后面用到它。


我们定制的 `open(...)` 的主体是由一些代码构成。它的最后部分创建了一个新的函数指针 `orig_open`，它指向原始的 `open(...)` 函数。为了得到那个函数的地址，我们请求 `dlsym` 在动态库堆栈上为我们查找下一个 `open()` 函数。最后，我们调用了那个函数（传递了与我们的假冒 `open()` 一样的参数），并且返回它的返回值。


我使用下面的内容作为我的 “邪恶的注入代码”：


*inspect\_open.c (片段)：*



```
printf("The victim used open(...) to access '%s'!!!\n",pathname); //remember to include stdio.h!

```

要编译它，我需要稍微调整一下编译参数：



```
gcc -shared -fPIC  inspect_open.c -o inspect_open.so -ldl

```

我增加了 `-ldl`，因此，它将这个共享库链接到 `libdl` —— 它提供了 `dlsym` 函数。（不，我还没有创建一个假冒版的 `dlsym` ，虽然这样更有趣）


因此，结果是什么呢？一个实现了 `open(...)` 函数的共享库，除了它有 *输出* 文件路径的意外作用以外，其它的表现和真正的 `open(...)` 函数 **一模一样**。:-)


如果这个强大的诀窍还没有说服你，是时候去尝试下面的这个示例了：



```
LD_PRELOAD=$PWD/inspect_open.so gnome-calculator

```

我鼓励你去看看自己实验的结果，但是简单来说，它实时列出了这个应用程序可以访问到的每个文件。


我相信它并不难想像为什么这可以用于去调试或者研究未知的应用程序。请注意，这个特定诀窍并不完整，因为 `open()` 并不是唯一一个打开文件的函数 …… 例如，在标准库中也有一个 `open64()`，并且为了完整地研究，你也需要为它去创建一个假冒的。


### 可能的用法


如果你一直跟着我享受上面的过程，让我推荐一个使用这个诀窍能做什么的一大堆创意。记住，你可以在不损害原始应用程序的同时做任何你想做的事情！


1. ~~获得 root 权限~~。你想多了！你不会通过这种方法绕过安全机制的。（一个专业的解释是：如果 ruid != euid，库不会通过这种方法预加载的。）
2. 欺骗游戏：**取消随机化**。这是我演示的第一个示例。对于一个完整的工作案例，你将需要去实现一个定制的 `random()` 、`rand_r()`、`random_r()`，也有一些应用程序是从 `/dev/urandom` 之类的读取，你可以通过使用一个修改过的文件路径来运行原始的 `open()` 来把它们重定向到 `/dev/null`。而且，一些应用程序可能有它们自己的随机数生成算法，这种情况下你似乎是没有办法的（除非，按下面的第 10 点去操作）。但是对于一个新手来说，它看起来很容易上手。
3. 欺骗游戏：**让子弹飞一会** 。实现所有的与时间有关的标准函数，让假冒的时间变慢两倍，或者十倍。如果你为时间测量和与时间相关的 `sleep` 或其它函数正确地计算了新的值，那么受影响的应用程序将认为时间变慢了（你想的话，也可以变快），并且，你可以体验可怕的 “子弹时间” 的动作。或者 **甚至更进一步**，你的共享库也可以成为一个 DBus 客户端，因此你可以使用它进行实时的通讯。绑定一些快捷方式到定制的命令，并且在你的假冒的时间函数上使用一些额外的计算，让你可以有能力按你的意愿去启用和禁用慢进或快进任何时间。
4. 研究应用程序：**列出访问的文件**。它是我演示的第二个示例，但是这也可以进一步去深化，通过记录和监视所有应用程序的文件 I/O。
5. 研究应用程序：**监视因特网访问**。你可以使用 Wireshark 或者类似软件达到这一目的，但是，使用这个诀窍你可以真实地控制基于 web 的应用程序发送了什么，不仅是看看，而是也能影响到交换的数据。这里有很多的可能性，从检测间谍软件到欺骗多用户游戏，或者分析和逆向工程使用闭源协议的应用程序。
6. 研究应用程序：**检查 GTK 结构** 。为什么只局限于标准库？让我们在所有的 GTK 调用中注入一些代码，因此我们就可以知道一个应用程序使用了哪些组件，并且，知道它们的构成。然后这可以渲染出一个图像或者甚至是一个 gtkbuilder 文件！如果你想去学习一些应用程序是怎么管理其界面的，这个方法超级有用！
7. **在沙盒中运行不安全的应用程序**。如果你不信任一些应用程序，并且你可能担心它会做一些如 `rm -rf /` 或者一些其它不希望的文件活动，你可以通过修改传递到文件相关的函数（不仅是 `open` ，也包括删除目录等）的参数，来重定向所有的文件 I/O 操作到诸如 `/tmp` 这样地方。还有更难的诀窍，如 chroot，但是它也给你提供更多的控制。它可以更安全地完全 “封装”，但除非你真的知道你在做什么，不要以这种方式真的运行任何恶意软件。
8. **实现特性** 。[zlibc](http://www.zlibc.linux.lu/index.html) 是明确以这种方法运行的一个真实的库；它可以在访问文件时解压文件，因此，任何应用程序都可以在无需实现解压功能的情况下访问压缩数据。
9. **修复 bug**。另一个现实中的示例是：不久前（我不确定现在是否仍然如此）Skype（它是闭源的软件）从某些网络摄像头中捕获视频有问题。因为 Skype 并不是自由软件，源文件不能被修改，这就可以通过使用预加载一个解决了这个问题的库的方式来修复这个 bug。
10. 手工方式 **访问应用程序拥有的内存**。请注意，你可以通过这种方式去访问所有应用程序的数据。如果你有类似的软件，如 CheatEngine/scanmem/GameConqueror 这可能并不会让人惊讶，但是，它们都要求 root 权限才能工作，而 `LD_PRELOAD` 则不需要。事实上，通过一些巧妙的诀窍，你注入的代码可以访问所有的应用程序内存，从本质上看，是因为它是通过应用程序自身得以运行的。你可以修改这个应用程序能修改的任何东西。你可以想像一下，它允许你做许多的底层的侵入…… ，但是，关于这个主题，我将在某个时候写一篇关于它的文章。


这里仅是一些我想到的创意。我希望你能找到更多，如果你做到了 —— 通过下面的评论区共享出来吧！




---


via: <https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/>


作者：[Rafał Cieślak](https://rafalcieslak.wordpress.com/) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
**This post assumes some basic C skills.**

Linux puts you in full control. This is not always seen from everyone’s perspective, but a power user loves to be in control. I’m going to show you a basic trick that lets you heavily influence the behavior of most applications, which is not only fun, but also, at times, useful.

#### A motivational example

Let us begin with a simple example. Fun first, science later.

#include <stdio.h> #include <stdlib.h> #include <time.h> int main(){ srand(time(NULL)); int i = 10; while(i--) printf("%d\n",rand()%100); return 0; }

Simple enough, I believe. I compiled it with no special flags, just

gcc random_num.c -o random_num

I hope the resulting output is obvious – ten randomly selected numbers 0-99, hopefully different each time you run this program.

Now let’s pretend we don’t really have the source of this executable. Either delete the source file, or move it somewhere – we won’t need it. We will significantly modify this programs behavior, yet without touching it’s source code nor recompiling it.

For this, lets create another simple C file:

int rand(){ return 42; //the most random number in the universe }

We’ll compile it into a shared library.

gcc -shared -fPIC unrandom.c -o unrandom.so

So what we have now is an application that outputs some random data, and a custom library, which implements the rand() function as a constant value of 42. Now… just run *random_num *this way, and watch the result:

LD_PRELOAD=$PWD/unrandom.so ./random_nums

If you are lazy and did not do it yourself (and somehow fail to guess what might have happened), I’ll let you know – the output consists of ten 42’s.


This may be even more impressive it you first:

export LD_PRELOAD=$PWD/unrandom.so

and then run the program normally. An unchanged app run in an apparently usual manner seems to be affected by what we did in our tiny library…

**Wait, what? What did just happen?**

Yup, you are right, our program failed to generate random numbers, because it did not use the “real” rand(), but the one we provided – which returns 42 every time.

**But we *told* it to use the real one. We programmed it to use the real one. Besides, at the time we created that program, the fake rand() did not even exist!**

This is not entirely true. We did not choose which rand() we want our program to use. We told it just to use rand().

When our program is started, certain libraries (that provide functionality needed by the program) are loaded. We can learn which are these using *ldd*:


$ldd random_numslinux-vdso.so.1 => (0x00007fff4bdfe000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f48c03ec000) /lib64/ld-linux-x86-64.so.2 (0x00007f48c07e3000)

What you see as the output is the list of libs that are needed by *random_nums*. This list is built into the executable, and is determined compile time. The exact output might slightly differ on your machine, but a **libc.so** must be there – this is the file which provides core C functionality. That includes the “real” rand().

We can have a peek at what functions does libc provide. I used the following to get a full list:

nm -D /lib/libc.so.6

The *nm* command lists symbols found in a binary file. The -D flag tells it to look for dynamic symbols, which makes sense, as libc.so.6 is a dynamic library. The output is very long, but it indeed lists rand() among many other standard functions.

Now what happens when we set up the environmental variable LD_PRELOAD? This variable **forces some libraries to be loaded for a program**. In our case, it loads *unrandom.so* for *random_num*, even though the program itself does not ask for it. The following command may be interesting:


$LD_PRELOAD=$PWD/unrandom.so ldd random_numslinux-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)

Note that it lists our custom library. And indeed this is the reason why it’s code get’s executed: *random_num* calls rand(), but if *unrandom.so* is loaded it is our library that provides implementation for rand(). Neat, isn’t it?

#### Being transparent

This is not enough. I’d like to be able to inject some code into an application in a similar manner, but in such way that it will be able to function normally. It’s clear if we implemented open() with a simple “*return 0;*“, the application we would like to hack should malfunction. The point is to be **transparent**, and to actually call the original open:

int open(const char *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 }

Hm. Not really. This won’t call the “original” open(…). Obviously, this is an endless recursive call.

How do we access the “real” open function? It is needed to use the programming interface to the dynamic linker. It’s simpler than it sounds. Have a look at this complete example, and then I’ll explain what happens there:

#define _GNU_SOURCE #include <dlfcn.h> typedef int (*orig_open_f_type)(const char *pathname, int flags); int open(const char *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); }

The *dlfcn.h* is needed for *dlsym* function we use later. That strange *#define* directive instructs the compiler to enable some non-standard stuff, we need it to enable *RTLD_NEXT* in *dlfcn.h*. That typedef is just creating an alias to a complicated pointer-to-function type, with arguments just as the original open – the alias name is *orig_open_f_type*, which we’ll use later.

The body of our custom open(…) consists of some custom code. The last part of it creates a new function pointer *orig_open* which will point to the original open(…) function. In order to get the address of that function, we ask *dlsym* to find for us the next “open” function on dynamic libraries stack. Finally, we call that function (passing the same arguments as were passed to our fake “open”), and return it’s return value as ours.

As the “evil injected code” I simply used:

printf("The victim used open(...) to access '%s'!!!\n",pathname); //remember to include stdio.h!

To compile it, I needed to slightly adjust compiler flags:

gcc -shared -fPIC inspect_open.c -o inspect_open.so -ldl

I had to append *-ldl*, so that this shared library is linked to *libdl*, which provides the *dlsym* function. (Nah, I am not going to create a fake version of *dlsym*, though this might be fun.)

So what do I have in result? A shared library, which implements the open(…) function so that it behaves **exactly** as the real open(…)… except it has a side effect of *printf*ing the file path :-)

If you are not convinced this is a powerful trick, it’s the time you tried the following:

LD_PRELOAD=$PWD/inspect_open.so gnome-calculator

I encourage you to see the result yourself, but basically it lists every file this application accesses. In real time.

I believe it’s not that hard to imagine why this might be useful for debugging or investigating unknown applications. Please note, however, that this particular trick is not quite complete, because *open()* is not the only function that opens files… For example, there is also *open64()* in the standard library, and for full investigation you would need to create a fake one too.

**Possible uses**

If you are still with me and enjoyed the above, let me suggest a bunch of ideas of what can be achieved using this trick. Keep in mind that you can do all the above without to source of the affected app!

~~Gain root privileges.~~Not really, don’t even bother, you won’t bypass any security this way. (A quick explanation for pros: no libraries will be preloaded this way if ruid != euid)- Cheat games:
**Unrandomize.**This is what I did in the first example. For a fully working case you would need also to implement a custom*random()*,*rand_r()**, random_r()*. Also some apps may be reading from*/dev/urandom*or so, you might redirect them to*/dev/null*by running the original*open()*with a modified file path. Furthermore, some apps may have their own random number generation algorithm, there is little you can do about that (unless: point 10 below). But this looks like an easy exercise for beginners. - Cheat games:
**Bullet time.**Implement all standard time-related functions pretend the time flows two times slower. Or ten times slower. If you correctly calculate new values for time measurement, timed*sleep*functions, and others, the affected application will believe the time runs slower (or faster, if you wish), and you can experience awesome bullet-time action.

Or go**even one step further**and let your shared library also be a DBus client, so that you can communicate with it real time. Bind some shortcuts to custom commands, and with some additional calculations in your fake timing functions you will be able to enable&disable the slow-mo or fast-forward anytime you wish. - Investigate apps:
**List accessed files.**That’s what my second example does, but this could be also pushed further, by recording and monitoring all app’s file I/O. - Investigate apps:
**Monitor internet access.**You might do this with Wireshark or similar software, but with this trick you could actually gain control of what an app sends over the web, and not just look, but also affect the exchanged data. Lots of possibilities here, from detecting spyware, to cheating in multiplayer games, or analyzing & reverse-engineering protocols of closed-source applications. - Investigate apps:
**Inspect GTK structures.**Why just limit ourselves to standard library? Let’s inject code in all GTK calls, so that we can learn what widgets does an app use, and how are they structured. This might be then rendered either to an image or even to a gtkbuilder file! Super useful if you want to learn how does some app manage its interface! **Sandbox unsafe applications.**If you don’t trust some app and are afraid that it may wish to*rm -rf /*or do some other unwanted file activities, you might potentially redirect all it’s file IO to e.g. /tmp by appropriately modifying the arguments it passes to all file-related functions (not just*open*, but also e.g. removing directories etc.). It’s more difficult trick that a chroot, but it gives you more control. It would be only as safe as complete your “wrapper” was, and unless you really know what you’re doing, don’t actually run any malicious software this way.**Implement features.**[zlibc](http://www.zlibc.linux.lu/index.html)is an actual library which is run this precise way; it uncompresses files on the go as they are accessed, so that any application can work on compressed data without even realizing it.**Fix bugs.**Another real-life example: some time ago (I am not sure this is still the case) Skype – which is closed-source – had problems capturing video from some certain webcams. Because the source could not be modified as Skype is not free software, this was fixed by preloading a library that would correct these problems with video.- Manually
**access application’s own memory**. Do note that you can access all app data this way. This may be not impressive if you are familiar with software like CheatEngine/scanmem/GameConqueror, but they all require root privileges to work. LD_PRELOAD does not. In fact, with a number of clever tricks your injected code might access all app memory, because, in fact, it gets executed by that application itself. You might modify everything this application can. You can probably imagine this allows a lot of low-level hacks… but I’ll post an article about it another time.

These are only the ideas I came up with. I bet you can find some too, if you do – share them by commenting!

April 2, 2013 at 5:33 pm

This cannot be used for sandboxing, because applications don’t have to go through dynamically loaded libraries to do anything. They can simply make system calls directly, and you would need to use Linux kernel features to intercept those.

April 2, 2013 at 5:39 pm

True. You are indeed right that this way one can never be guaranteed success, which is precisely why I discourage from sandboxing applications this way. However, I consider this as a useful example of what can be done with a number of hooks.

January 26, 2014 at 4:01 pm

[…] drawbacks: Hooking is very platform depended. On linux we could hook with kernel modules or the LD_PRELOAD trick. On Windows we'd need other hooking techniques like IAT hooking, using the microsoft hooking […]

March 4, 2014 at 12:32 pm

Great article !

I have a programme which require libc.so.6( GLIBC_2.14 ) so I take new libc.so.6 and tried to preload libc.so.6 but I don’t work. Have you a solution ?

Thank for this article.

March 4, 2014 at 11:55 pm

I am not sure what are you trying to do. Do you wish to force an application to use a different libc.so.6 version? Some background information on what are you trying to achieve and how exactly is it not working the way you expect it to work would be great.

March 10, 2014 at 12:39 pm

I would compile a C++11 application on the redhat, I got precompiled binary of g++ but when I launch g++ :

-bash-4.1$ ./g++

./g++: /lib64/libc.so.6: version `GLIBC_2.14′ not found (required by ./g++)

I saw that libc is executable:

-bash-4.1$ /lib64/libc.so.6 | head -1

GNU C Library stable release version 2.12, by Roland McGrath et al.

My libc is too old, I got last precompiled libc.so.6 and when I preload it.

-bash-4.1$ LD_PRELOAD=$PWD/lib64/libc.so.6 ./usr/bin/g++

ERROR: ld.so: object ‘/auto/herison/my_env/dl/hermetic/lib64/libc.so.6′ from LD_PRELOAD cannot be preloaded: ignored.

./usr/bin/g++: /lib64/libc.so.6: version `GLIBC_2.14’ not found (required by ./usr/bin/g++)

And when I execute last libc.so.6 :

-bash-4.1$ $PWD/lib64/libc.so.6

/auto/herison/my_env/dl/hermetic/lib64/libc.so.6: relocation error: /auto/herison/my_env/dl/hermetic/lib64/libc.so.6: symbol _dl_starting_up, version GLIBC_PRIVATE not defined in file ld-linux.so.2 with link time reference

Thanks for your help

March 18, 2016 at 1:19 am

You can use LD_LIBRARY_PATH for this.

Though, it’d probably be easier to just run a docker image of a newer version of linux, like fedora:22 docker image, or even the rhel:7.2 docker image.

Sounds like you’re on RHEL 6 (mentioned below). You could run your compile on RHEL7 (comes with glibc 2.17), or install docker on a fedora or rhel7 box, and run your compile that way (probably the most stable option).

February 17, 2018 at 9:13 am

You probably want to specify a newer ld.so to do such:

env LD_PRELOAD=/path/to/libc.so.6 /path/to/ld/ld-linux.so EXE

July 21, 2014 at 3:15 pm

[…] 这篇 […]

July 24, 2014 at 5:23 pm

[…] is responsible for clean up and for executing the malware. It accomplishes this using the so called ‘LD_PRELOAD’ -technique in which an environment variable, ‘LD_PRELOAD’ is set with the path to the dropped […]

December 24, 2014 at 6:28 pm

I have an application with a class called “A”, for example, and a function in this class called “toast”. I know exactly how is declared the function “toast”. How can I intercept this function using a library? My application is compiled using G++ 4.8 with C++ 11.

I tried to make a library with function “A::toast()”, but my app it’s calling the good one. Why? I created library using G++ aswell.

Can you tell me the flags I need in order to work and also what I have to declare to work? I heard something about “extern “C”“ or something.

December 30, 2014 at 8:12 pm

Hello! The reason why you are unable to get this trick to work is because you are working with C++. The example I used was pure C, and it is indeed easier in such case. The point is that C++ uses name mangling, so you need to take that into account when naming your functions.

Furthermore, and more importantly, you will not see any changes if toast() is defined within your application. The trick described in this article can only substitute functions that come from shared libraries. A function that is defined within the app itself is not processed by the dynamic linker, so it cannot be substituted. It is not possible to simply exchange a function within a program. If you are interested in details, I recommend you to learn about static and dynamic linking ;-)

December 30, 2014 at 10:28 pm

I’ve seen people doing it… :(

May 15, 2015 at 3:48 pm

[…] 実務家向けに必要なことはだいたいここに書かれている https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject… […]

September 1, 2015 at 10:17 pm

[…] refreshing my memory (thanks to this post), I create a little shared library that overrides the rand method. After running […]

November 15, 2015 at 5:37 pm

[…] 其文章內容參考、翻譯自:Dynamic linker tricks: Using LD_PRELOAD to cheat, inject features and investigate programs […]

May 8, 2016 at 10:34 pm

Already using this idea to “fix” Cisco VPN client which messes up my iptables and routing table while running. Found that hack somewhere online. Thank You for explaining how this works!

June 20, 2016 at 4:05 pm

You should have a look at https://github.com/ugtrain/ugtrain. ugtrain is a game trainer with dynamic memory support (hooks malloc(), free(), etc. with LD_PRELOAD), runs the game itself and doesn’t require any root privileges – not even for scanmem. Btw.: I also maintain scanmem/GameConqueror since last year at: https://github.com/scanmem/scanmem/. We have it in the scanmem man page now why scanmem requires root privileges (Yama security module).

August 8, 2016 at 11:45 am

[…] LD_PRELOAD […]

December 29, 2016 at 4:23 am

[…] If youre not familiar with the LD_PRELOAD technique google it quickly or read up on this well written post of it. […]

January 15, 2017 at 11:36 am

How about I bypass you by issuing the open syscall directly ;)

January 15, 2017 at 2:01 pm

How about I ptrace your program ;)

April 19, 2017 at 5:32 pm

[…] Cieślak wrote an excellent intro to LD_PRELOAD; I borrowed the dlsym code from […]

April 28, 2017 at 11:15 pm

[…] googled “LD_PRELOAD hacks”, clicked on Dynamic linker tricks: Using LD_PRELOAD to cheat, inject features and investigate programs, and we were up and […]

July 18, 2017 at 8:49 pm

Thanks!

Note for readers: if you overload `malloc()` to use `printf()` inside it, it gonna cause segfault. Probably `printf()` is calling malloc, causes infinite recursion and crash. What’s really bad: for some reason even gdb doesn’t handle this situation, it just says `During startup program terminated with signal SIGSEGV`, and leaves you in a lone confusion.

The solution is to use `fprintf()` instead.

November 25, 2017 at 8:30 am

[…] Some of the content for this post was inspired by (lifted from) this post. […]

December 4, 2017 at 6:54 am

[…] Linker tricks: Using LD_PRELOAD to inject features and investigate programs 6 by striking | 0 comments on Hacker News. […]

December 4, 2017 at 11:42 am

Notice the third optional argument to open(2), creation mode. It isn’t passed along.

December 4, 2017 at 3:17 pm

Taking this one step further, one could use LD_PRELOAD with Frida and write the instrumentation logic in JavaScript:

https://www.frida.re/docs/gadget/

This also supports monitoring the .js file for changes and reloading the instrumentation logic live, which is great for game tweaks. Just save the file and instantly see the results.

December 10, 2017 at 8:55 am

[…] Dynamic linker tricks: Using LD_PRELOAD to cheat, inject features and investigate programs […]

December 20, 2017 at 9:35 am

[…] might try this tommorow and see what happens P.S. Fakechroot(1): gives fake chroot environment. Dynamic linker tricks: Using LD_PRELOAD to cheat, inject features and investigate programs | Rafał …. This post assumes some basic C skills. Linux puts you in full control. This is not always seen […]

January 3, 2018 at 12:22 pm

[…] https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject… […]

April 25, 2018 at 7:07 pm

[…] Dynamic linker tricks: Using LD_PRELOAD to cheat, inject features and investigate programs […]

March 3, 2019 at 3:50 pm

[…] via: https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject… […]

May 10, 2019 at 1:13 pm

It works with the rand function but not with the sinf (from math.h) function. Any idea why ? It’s like my sinf custom function is ignored although my .so file is loaded.

May 10, 2019 at 1:34 pm

Depending on your standard library implementation, it could be that sinf is a preprocesor macro, and not an actual function.

July 18, 2019 at 6:42 pm

Thank you for the Great article

Can you also summarize what are the preventive mechanism to avoid LD_PRELOAD affecting your application with customer functions that impersonate with malicious users?

July 24, 2019 at 2:34 pm

Any response for above query?

Basically we have faced with our application where LD_PRE_LOAD loading the vulnerable library which resets the uid/gid to presume as root (0) causing application non-admin user to impersonate the root privileges and can restore root files by non-admin user.So, any inputs are appreciated to prevent this from occurring.

July 24, 2019 at 6:40 pm

You can prevent LD_PRELOAD from having any effect at all by not using a dynamic linker in the first place. That is, by compiling your program into a static binary. This will import all external functions into that binary, and thus it will not be possible to modify their behaviour by tricking the dynamic linker.

Static binaries come with their own inconveniences, so you may consider the alternative of writing your own getuid() function that refers directly to getuid syscall, and then use that function instead of getuid provided by the standard library. This may be a bit tricky to implement, but your program will stay dynamically linked, and even when stdlib’s getuid is replaced with any other code, your application’s behavior will stay unaffected.

However, if you’re dealing with a malicious user, neither of these approaches will work. Such user can `ptrace` your program and spoof the getuid syscall with a 0, or even modify the binary code of your program to always consider the value that getuid() returns as 0.

If your application relies on syscall return values or, well, pretty much anything within it’s own memory to prevent users from privilege escalation, then there is nothing you can do to prevent a skilled user from abusing it. You may need to rethink your application’s architecture to make sure that it’s the kernel that prevents from unauthorized the access to files, and not your app. In some scenarios this could be done by ensuring that root is the owner of said restorable files; but obviously the details depend on the context.

July 28, 2019 at 3:43 am

[…] 最佳答案 大多数情况下,你运气不好,除非你发现像 using LD_PRELOAD这样的肮脏技巧来覆盖信号或sigaction行为,当你可以合理地推断你在坏库中时. […]

October 12, 2019 at 8:38 am

The a remporté le Super Bowl 1997 avec lecteur des unités spéciales Desmond Howard obtenir le joueur le plus utile de la award.

March 5, 2020 at 8:30 pm

Thank you for the article! Very easy to read and follow along :)

Please keep up the good work.

July 16, 2020 at 10:53 am

I tried this preload() technique with read() and fopen() library functions and it works perfectly, but I’m not able to do it for a sample function that I created say func test() in main.c and then basically trying to wrap it in inject.c where another func test() is declared. Is it possible to do so?

June 3, 2021 at 7:45 am

[…] 경우에 그래서, 말 그대로 이 달성하고자하는, 당신은 사용할 수 있습니다 LD_PRELOAD 후크를 . 기본 아이디어는 C 라이브러리에서 함수를 다시 작성하여 일반 함수 대신 […]

June 8, 2021 at 12:36 pm

[…] LD_PRELOAD 사용에 대한 일부 정보 : https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject… […]

June 11, 2021 at 11:13 am

[…] 경우에 그래서, 말 그대로 이 달성하고자하는, 당신은 사용할 수 있습니다 LD_PRELOAD 후크를 . 기본 아이디어는 C 라이브러리에서 함수를 다시 작성하여 일반 함수 대신 […]

September 21, 2021 at 4:24 am

[…] https://linux.die.net/man/3/malloc Dynamic linker tricks: Using LD_PRELOAD to cheat, inject features and investigate programs https://linux.die.net/man/3/malloc https://linux.die.net/man/3/malloc […]

November 11, 2021 at 7:20 pm

[…] out this blog post to read further about LD_PRELOAD abuse. However, this is not a technique that we can used since […]

January 2, 2022 at 9:52 pm

[…] is a function that allows any program to use shared libraries. This blog post will give you an idea about the capabilities of LD_PRELOAD. If the “env_keep” […]

January 10, 2022 at 7:52 am

[…] 使用 “代理DLL “的方法 LD_PRELOAD 并不真正涉及到修改一些不属于你的东西，而且注入的东西和普通的动态库加载并没有什么不同。ERESI项目中的 “典型的ELF黑客工具 “与以下内容无关 LD_PRELOAD. 你不应该害怕它。一个好的写作入门 LD_PRELOAD-可 “代理 “是 此处. […]

June 14, 2022 at 1:28 pm

[…] if you literally want to achieve this, you can use a LD_PRELOAD hook. The basic idea is to rewrite a function from the C library and use it instead of the normal […]

October 4, 2022 at 9:41 pm

[…] that my toolchain doesn’t support this feature. In order to confirm that it does, I followed this example to create a simple test library and load it (I replaced rand() with a function that just returns […]

October 16, 2022 at 11:42 pm

[…] 使用 “代理DLL “的方法 LD_PRELOAD 并不真正涉及到修改一些不属于你的东西，而且注入的东西和普通的动态库加载并没有什么不同。ERESI项目中的 “典型的ELF黑客工具 “与以下内容无关 LD_PRELOAD. 你不应该害怕它。一个好的写作入门 LD_PRELOAD-可 “代理 “是 此处. […]

October 25, 2022 at 6:47 pm

[…] Dynamic linker tricks: Using LD_PRELOAD to cheat, inject features and investigate programs […]

September 9, 2023 at 2:01 am

[…] Read More […]