即使在 unsetenv(“LD_PRELOAD”) 之后,LD_PRELOAD 也会影响新的子级

发布于 2024-09-10 19:22:02 字数 918 浏览 3 评论 0原文

我的代码如下: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)。

更多信息

  1. 这个问题在某种程度上与 bash 有关;作为用户运行的命令,或者作为 popen 执行的 bash。
  2. 其他关键因素:1) 从预加载的库执行 popen,2) 可能需要在库的初始化部分执行 popen。
  3. 如果您使用:

    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

  1. This problem relate to bash in some way; either as the command that the user run, or as the bash the popen execute.
  2. 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.
  3. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

爱的十字路口 2024-09-17 19:22:02

编辑:所以问题实际上是:你怎么不能使用 中预加载的 main_init() 可靠地取消设置 LD_PRELOAD bash

原因是 execve(在您 popen 之后调用)从(可能)获取环境,

extern char **environ;

该环境是指向您的环境的某个全局状态变量。 unsetenv() 通常会修改您的环境,因此会对 **environ 的内容产生影响。

如果 bash 尝试对环境做一些特殊的事情(嗯……会吗?作为一个 shell?)那么你可能会遇到麻烦。

显然,bash 甚至在 main_init() 之前就重载了 unsetenv()。将示例代码更改为:

extern char**environ;

int  __attribute__((constructor))  main_init(void)
{
int i;
printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD"));
printf("LD_PRELOAD: \"%s\"\n",getenv("LD_PRELOAD"));
printf("Environ: %lx\n",environ);
printf("unsetenv: %lx\n",unsetenv);
for (i=0;environ[i];i++ ) printf("env: %s\n",environ[i]);
fflush(stdout);
FILE *fp = popen("ls", "r");
pclose(fp);
}

显示了问题。在正常运行中(运行 catls 等),我得到此版本的 unsetenv:

unsetenv: 7f4c78fd5290
unsetenv: 7f1127317290
unsetenv: 7f1ab63a2290

但是,运行 bashsh:

unsetenv: 46d170

所以,你已经明白了。 bash 已经让你上当了;-)

所以只需使用你自己的 unsetenv 修改环境,作用于 **environ

for (i=0;environ[i];i++ )
{
    if ( strstr(environ[i],"LD_PRELOAD=") )
    {
         printf("hacking out LD_PRELOAD from environ[%d]\n",i);
         environ[i][0] = 'D';
    }
}

这可以是可以在 strace 中工作:

execve("/bin/sh", ["sh", "-c", "ls"], [... "DD_PRELOAD=mylib.so" ...]) = 0

QED

edit: so the problem/question actually was: howcome can't you unset LD_PRELOAD reliably using a preloaded main_init() from within bash.

The reason is that execve, which is called after you popen, takes the environment from (probably)

extern char **environ;

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 overloads unsetenv() even before main_init(). Changing the example code to:

extern char**environ;

int  __attribute__((constructor))  main_init(void)
{
int i;
printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD"));
printf("LD_PRELOAD: \"%s\"\n",getenv("LD_PRELOAD"));
printf("Environ: %lx\n",environ);
printf("unsetenv: %lx\n",unsetenv);
for (i=0;environ[i];i++ ) printf("env: %s\n",environ[i]);
fflush(stdout);
FILE *fp = popen("ls", "r");
pclose(fp);
}

shows the problem. In normal runs (running cat, ls, etc) I get this version of unsetenv:

unsetenv: 7f4c78fd5290
unsetenv: 7f1127317290
unsetenv: 7f1ab63a2290

however, running bash or sh:

unsetenv: 46d170

So, there you have it. bash has got you fooled ;-)

So just modify the environment in place using your own unsetenv, acting on **environ:

for (i=0;environ[i];i++ )
{
    if ( strstr(environ[i],"LD_PRELOAD=") )
    {
         printf("hacking out LD_PRELOAD from environ[%d]\n",i);
         environ[i][0] = 'D';
    }
}

which can be seen to work in the strace:

execve("/bin/sh", ["sh", "-c", "ls"], [... "DD_PRELOAD=mylib.so" ...]) = 0

Q.E.D.

彼岸花似海 2024-09-17 19:22:02

(答案纯粹是猜测,可能是不正确的)。

也许,当您分叉进程时,加载的库的上下文仍然存在。因此,当您通过LD_PRELOAD调用主程序时,已加载mylib.so。当您取消设置变量并分叉时,它不会再次加载;然而它已经被父进程加载。也许,您应该在分叉后显式卸载它。

您也可以尝试“降级”mylib.so中的符号。为此,请通过 dlopen 带有将其置于符号解析队列末尾的标志:

dlopen("mylib.so", RTLD_NOLOAD | RTLD_LOCAL);

(The answer is a pure speculation, and may be is 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 via LD_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 via dlopen with flags that place it to the end of the symbol resolution queue:

dlopen("mylib.so", RTLD_NOLOAD | RTLD_LOCAL);

山川志 2024-09-17 19:22:02

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文