什么是 LD_PRELOAD 技巧?

Posted

技术标签:

【中文标题】什么是 LD_PRELOAD 技巧?【英文标题】:What is the LD_PRELOAD trick? 【发布时间】:2010-09-30 09:16:21 【问题描述】:

我最近在 proggit 上看到了对它的引用,并且(截至目前)它没有得到解释。

我怀疑this 可能是它,但我不确定。

【问题讨论】:

不是一个真正的答案,所以我不会把它作为一个答案发布,但是......斯蒂芬凯尔在这个视频中使用 LD_PRELOAD 作为他的 liballocs 库,如果你观看前面的部分,你可能会得到更好的理解如何/为什么。 liballocs 似乎正在被使用,因此其他动态语言可以相互交谈。这个谈话有一些深刻的内部解释。 youtu.be/LwicN2u6Dro?t=24m10s 【参考方案1】:

如果您将LD_PRELOAD 设置为共享对象的路径,则该文件将在 任何其他库(包括C 运行时libc.so)之前加载。因此,要使用您的特殊 malloc() 实现运行 ls,请执行以下操作:

$ LD_PRELOAD=/path/to/my/malloc.so /bin/ls

【讨论】:

我不知道这存在......它似乎是安全攻击的主要载体。知道它是如何保护的吗? 如果 ruid != euid -- Joshua,加载程序将忽略 LD_PRELOAD 这一事实确保了这一点 @Joshua:什么是ruid和euid? @heinrich5991 真实有效的用户ID:lst.de/~okir/blackhats/node23.html 要记住的重要一点:您通常希望指定一个 absolute 路径到LD_PRELOAD。原因是它是一个环境变量,它是由子进程继承的——它可能具有与父进程不同的工作目录。因此,任何相对路径都无法找到要预加载的库。【参考方案2】:

您可以通过创建具有相同符号的库并在 LD_PRELOAD 中指定库来覆盖股票库中的符号。

有些人使用它来指定非标准位置的库,但LD_LIBRARY_PATH 更适合此目的。

【讨论】:

“有些人用它来指定非标准位置的库”...真的吗?听起来像是“有些人用错了”! LD_PRELOAD 可以凭借加载顺序拦截应用程序指定的硬编码路径。 预加载不同的版本库会不会是一种误用——假设它们是兼容的? 我见过它用于加载调试或检测变体,或加载与基础库完全不同的库,就好像模拟其他系统一样。 在库未正确编译的情况下(过去常常与 mysql 一起运行,因为它与通用 libmysql_client 松散耦合,覆盖了旧版本的符号链接 - 取决于哪个版本你使用的 perl 必须用 LD_PRELOAD 指定/强制它。有用的技巧。如果我没记错的话,valgrind 使用这种技术为二进制文件提供调试能力,而无需重新编译。它非常有用。【参考方案3】:

使用LD_PRELOAD,您可以为库提供优先权。

例如,您可以编写一个实现mallocfree 的库。通过使用LD_PRELOAD 加载这些,您的mallocfree 将被执行,而不是标准的。

【讨论】:

但是如果程序使用calloc呢?那不是把一切都搞砸了吗? @JanusTroelsen 如果您编写的库没有实现某个部分,则该部分将从原始库中加载。 @JanusTroelsen,换句话说,LD_PRELOAD 允许您指定使用特定 symbol 的哪个实现。如果预加载的库没有导出符号,则会在 elsehwere 中找到。 @JanusTroelsen:事实证明,malloc 和 free 是在 glibc 中专门设计的,以实现这一点,而股票 calloc 设法调用您导入的 malloc。不要尝试使用任何其他功能。它不会那么好用。【参考方案4】:

正如许多人提到的,使用LD_PRELOAD 预加载库。顺便说一句,您可以CHECK通过ldd命令设置是否可用。

示例:假设您需要预加载自己的libselinux.so.1

> ldd /bin/ls
    ...
    libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f3927b1d000)
    libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007f3927914000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f392754f000)
    libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f3927311000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f392710c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f3927d65000)
    libattr.so.1 => /lib/x86_64-linux-gnu/libattr.so.1 (0x00007f3926f07000)

因此,设置您的预加载环境:

export LD_PRELOAD=/home/patric/libselinux.so.1

再次检查您的图书馆:

>ldd /bin/ls
    ...
    libselinux.so.1 =>
    /home/patric/libselinux.so.1 (0x00007fb9245d8000)
    ...

【讨论】:

【参考方案5】:

LD_PRELOAD 列出了具有覆盖标准集的函数的共享库,就像/etc/ld.so.preload 所做的那样。这些由加载程序/lib/ld-linux.so 实现。如果您只想覆盖几个选定的函数,您可以通过创建一个覆盖目标文件并设置 LD_PRELOAD 来做到这一点;此目标文件中的函数将仅覆盖那些函数,而其他函数则保持原样。

有关共享库的更多信息,请访问 http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html

【讨论】:

【参考方案6】:

mylib.so 导出到环境:

$ export LD_PRELOAD=/path/mylib.so
$ ./mybin

禁用它:

$ unset LD_PRELOAD

【讨论】:

unset LD_PRELOAD unsetexport VAR= 并不完全相同。 unset 是要走的路。【参考方案7】:

这是一篇关于预加载的详细博文:

https://blog.cryptomilk.org/2014/07/21/what-is-preloading/

【讨论】:

感谢您发布您的答案!请注意,您应该在此处、此站点上发布答案的基本部分,否则您的帖子有被删除的风险See the FAQ where it mentions answers that are 'barely more than a link'. 如果您愿意,您仍然可以包含该链接,但仅作为“参考”。答案应该是独立的,不需要链接。【参考方案8】:

使用LD_PRELOAD 路径,您可以强制应用程序加载器加载提供的共享对象,而不是默认提供的。

开发人员通过提供不同版本的共享对象来使用它来调试他们的应用程序。

我们使用它来破解某些应用程序,方法是使用准备好的共享对象覆盖现有函数。

【讨论】:

【参考方案9】:

当使用 LD_PRELOAD 时,该文件将在任何其他文件之前加载。采用 $export LD_PRELOAD=/path/lib 用于预加载 lib。这甚至可以在程序中使用。

【讨论】:

以上是关于什么是 LD_PRELOAD 技巧?的主要内容,如果未能解决你的问题,请参考以下文章

为啥堆栈上的LD_PRELOAD的值

UNIX下的LD_PRELOAD环境变量

使用Linux功能是否会禁用LD_PRELOAD

LD_PRELOAD - 可以拦截像 + 和 - 这样的原始运算符吗?

使用LD_PRELOAD指定多个文件

如何将 gdb 与 LD_PRELOAD 一起使用