即使在 unsetenv(“LD_PRELOAD”) 之后,LD_PRELOAD 也会影响新的子级
我的代码如下:preload.c,内容如下:
#include <stdio.h>
#include <stdlib.h>
int __attribute__((constructor)) main_init(void)
{
printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD"));
FILE *fp = popen("ls", "r");
pclose(fp);
}
然后在shell中(小心执行第二个命令!!):
gcc preload.c -shared -Wl,-soname,mylib -o mylib.so -fPIC
LD_PRELOAD=./mylib.so bash
!!!小心最后一个命令,它会导致分叉“sh -c ls”的无限循环。 2 秒后用 ^C 停止它(或者更好的 ^Z 然后看 ps)。
更多信息
- 这个问题在某种程度上与 bash 有关;作为用户运行的命令,或者作为 popen 执行的 bash。
- 其他关键因素:1) 从预加载的库执行 popen,2) 可能需要在库的初始化部分执行 popen。
如果您使用:
LD_DEBUG=全部 LD_DEBUG_OUTPUT=/tmp/ld-debug LD_PRELOAD=./mylib.so bash
您将获得许多 ld-debug 文件,而不是最后一个命令,名为 /tmp/ld-debug.*。每个分叉进程一个。在所有这些文件中,您将看到符号首先在 mylib.so 中搜索,即使 LD_PRELOAD 已从环境中删除。
my code is as follows: preload.c, with the following content:
#include <stdio.h>
#include <stdlib.h>
int __attribute__((constructor)) main_init(void)
{
printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD"));
FILE *fp = popen("ls", "r");
pclose(fp);
}
then in the shell (do the 2nd command with care!!):
gcc preload.c -shared -Wl,-soname,mylib -o mylib.so -fPIC
LD_PRELOAD=./mylib.so bash
!!! be carefull with the last command it will result with endless loop of forking "sh -c ls". Stop it after 2 seconds with ^C, (or better ^Z and then see ps).
More info
- This problem relate to bash in some way; either as the command that the user run, or as the bash the popen execute.
- additional Key factors: 1) perform the popen from the pre-loaded library, 2) probably need to do the popen in the initialization section of the library.
if you use:
LD_DEBUG=all LD_DEBUG_OUTPUT=/tmp/ld-debug LD_PRELOAD=./mylib.so bash
instead of the last command, you will get many ld-debug files, named /tmp/ld-debug.*. One for each forked process. IN ALL THESE FILES you'll see that symbols are first searched in mylib.so even though LD_PRELOAD was removed from the environment.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
编辑:所以问题实际上是:你怎么不能使用
中预加载的
。main_init()
可靠地取消设置LD_PRELOAD
bash原因是
execve
(在您popen
之后调用)从(可能)获取环境,该环境是指向您的环境的某个全局状态变量。
unsetenv()
通常会修改您的环境,因此会对**environ
的内容产生影响。如果 bash 尝试对环境做一些特殊的事情(嗯……会吗?作为一个 shell?)那么你可能会遇到麻烦。
显然,
bash
甚至在main_init()
之前就重载了unsetenv()
。将示例代码更改为:显示了问题。在正常运行中(运行
cat
、ls
等),我得到此版本的 unsetenv:但是,运行
bash
或sh:
所以,你已经明白了。
bash
已经让你上当了;-)所以只需使用你自己的
unsetenv
修改环境,作用于**environ
:这可以是可以在
strace
中工作:QED
edit: so the problem/question actually was: howcome can't you unset
LD_PRELOAD
reliably using a preloadedmain_init()
from withinbash
.The reason is that
execve
, which is called after youpopen
, takes the environment from (probably)which is some global state variable that points to your environment.
unsetenv()
normally modifies your environment and will therefore have an effect on the contents of**environ
.If
bash
tries to do something special with the environment (well... would it? being a shell?) then you may be in trouble.Appearantly,
bash
overloadsunsetenv()
even beforemain_init()
. Changing the example code to:shows the problem. In normal runs (running
cat
,ls
, etc) I get this version of unsetenv:however, running
bash
orsh
:So, there you have it.
bash
has got you fooled ;-)So just modify the environment in place using your own
unsetenv
, acting on**environ
:which can be seen to work in the
strace
:Q.E.D.
(答案纯粹是猜测,
可能是不正确的)。也许,当您分叉进程时,加载的库的上下文仍然存在。因此,当您通过LD_PRELOAD
调用主程序时,已加载mylib.so
。当您取消设置变量并分叉时,它不会再次加载;然而它已经被父进程加载。也许,您应该在分叉后显式卸载它。您也可以尝试“降级”
mylib.so
中的符号。为此,请通过dlopen
带有将其置于符号解析队列末尾的标志:(The answer is a pure speculation, and
may beis incorrect).Perhaps, when you fork your process, the context of the loaded libraries persists. So,mylib.so
was loaded when you invoked the main program viaLD_PRELOAD
. When you unset the variable and forked, it wasn't loaded again; however it already has been loaded by the parent process. Maybe, you should explicitly unload it after forking.You may also try to "demote" symbols in
mylib.so
. To do this, reopen it viadlopen
with flags that place it to the end of the symbol resolution queue:mvds 的答案不正确!
popen() 将生成子进程,该子进程继承预加载的 .so 位于父进程中。该子进程不关心 LD_PRELOAD 环境。
the answer from mvds is incorrect!
popen() will spawn child process which inherit the preloaded .so lied in parent process. this child process don't care LD_PRELOAD environment.