LD_PRELOAD技巧是什么?

458

最近在proggit上看到了它的参考,但目前还没有解释。

我猜想this可能是它,但我不确定。


3
这不是一个确切的答案,所以我不会将其发布为答案,但是…… Stephen Kell在这个视频中使用LD_PRELOAD来加载他的liballocs库,如果您观看之前的部分,您可能会更好地理解如何以及为什么使用liballocs,似乎liballocs被用于使其他动态语言能够相互通信。这次演讲涵盖了一些深层次的内部原理。 - Elijah Lynn
9个回答

545
如果你将LD_PRELOAD设置为共享对象的路径,那么该文件将在任何其他库(包括C运行时库libc.so)之前被加载。所以要使用您特殊的malloc()实现运行ls,请执行以下操作:
$ LD_PRELOAD=/path/to/my/malloc.so /bin/ls

27
我不知道这个存在...看起来它可能是安全攻击的主要途径。你知道它如何被保护吗? - rmeador
178
如果 ruid != euid,加载程序将忽略 LD_PRELOAD,这一事实保证了安全性。 - Joshua
35
ruid和euid是Unix/Linux操作系统中的两个用户身份标识符。ruid代表实际用户ID,euid代表有效用户ID。 - heinrich5991
30
@heinrich5991 真实有效的用户ID:http://www.lst.de/~okir/blackhats/node23.html - gsgx
86
需要记住的一件重要事情是:通常您希望指定一个绝对路径LD_PRELOAD。原因是它是一个环境变量,会被子进程继承 - 这些子进程可能具有与父进程不同的工作目录。因此,任何相对路径都无法定位要预加载的库。 - Frerich Raabe
显示剩余7条评论

68

您可以通过创建一个包含相同符号的库,并在LD_PRELOAD中指定该库,来覆盖库中的符号。

有些人使用它来指定非标准位置的库,但是对于这个目的,LD_LIBRARY_PATH更好。


23
有些人使用它来指定非标准位置的库......真的吗?听起来像是“有些人用错了”! - Tom
7
LD_PRELOAD可以通过加载顺序拦截应用程序指定的硬编码路径。 - Joshua
1
如果它们兼容,预加载不同版本的库是否会被误用? - z0r
2
我曾经看到它被用来加载调试或仪器变体,或者加载一个完全不同于基本库的库,仿佛要模拟其他系统。 - Joshua
1
在库没有正确编译的情况下(我经常遇到mysql这样的情况,它与通用的libmysql_client松散耦合,会覆盖旧版本的符号链接 - 根据您使用的perl版本,您必须使用LD_PRELOAD指定/强制它.. 这是一个有用的技巧。如果我没记错的话,valgrind使用这种技术为二进制文件提供调试能力,而无需重新编译.. 它非常有用。 - synthesizerpatel
@z0r:如果你受制于一个糟糕的系统管理员,无法自行安装软件,那么使用这种方法并不算滥用。 :p - synthesizerpatel

60

正如很多人提到的那样,可以使用LD_PRELOAD来预加载库。另外,您可以使用ldd命令检查是否可用该设置。

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


例子:假设您需要预装自己的 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)
    ...

50
使用LD_PRELOAD可以给库设置优先级。
例如,您可以编写一个实现mallocfree的库,并通过使用LD_PRELOAD来加载这些库,使其优先于标准库执行mallocfree

3
如果程序使用 calloc 那会怎样呢?这不会搞乱一切吗? - Janus Troelsen
10
如果你编写的库没有实现某个部分,那么这个部分将从原始库中加载。 - Woodrow Barlow
@JanusTroelsen,换句话说,LD_PRELOAD允许您指定使用特定符号的哪个实现。如果预加载库未导出符号,则会在其他地方找到它。 - sherrellbc
3
原文:@JanusTroelsen: 原来在glibc中,mallocfree是专门设计用来实现这一点的,标准的calloc函数也能调用你导入的malloc函数。不要尝试使用其他函数来实现这个目的,效果可能不好。翻译:原来在glibc库中,mallocfree函数被特别设计成可以实现这个功能,而标准的calloc函数会调用你所导入的malloc函数。但不要尝试使用其他函数来实现这个目的,效果可能不佳。 - Joshua

12

LD_PRELOAD 是一种列出具有覆盖标准库函数的共享库的方法,与 /etc/ld.so.preload 类似。这些功能由加载器 /lib/ld-linux.so 实现。如果您只想覆盖一些选择的功能,可以创建一个覆盖对象文件并设置 LD_PRELOAD;该对象文件中的函数将仅覆盖那些函数,而保留其他函数不变。

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


8
mylib.so 导出到环境变量中:
$ export LD_PRELOAD=/path/mylib.so
$ ./mybin

要禁用它:

$ unset LD_PRELOAD

12
取消设置LD_PRELOAD。 - Morten
1
unsetexport VAR= 并不完全相同,使用 unset 更为合适。 - Michał Leon

4

18
感谢您发布您的答案!请注意,您应该在这个网站上发布答案的关键部分,否则您的帖子可能会被删除参见 FAQ 中提到的“几乎只是一个链接”的答案。 如果您愿意,仍然可以包括链接,但只作为“参考”。答案应该独立存在,不需要链接就能解决问题。 - Taryn

3
当使用LD_PRELOAD时,该文件将在任何其他文件之前加载。使用$export LD_PRELOAD=/path/lib来预加载lib。这甚至可以在程序中使用。"Original Answer"翻译成"最初的回答"。

2
使用LD_PRELOAD路径,您可以强制应用程序加载器加载提供的共享对象,而不是默认提供的对象。
开发人员使用这种方法通过提供不同版本的共享对象来调试他们的应用程序。
我们利用它来通过准备好的共享对象覆盖现有函数来黑掉某些应用程序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接