从LD_PRELOAD探究子进程的环境变量

Posted yizui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从LD_PRELOAD探究子进程的环境变量相关的知识,希望对你有一定的参考价值。

程序启动时,链接器会优先LD_PRELOAD指定的库中的符号。如果fork()创建子进程后,LD_PRELOAD还能生效么?

1.

main.c

#include <unistd.h>
#include <stdio.h>

extern char** environ;
void foo();
int main()
{
        for(char **current = environ; *current; current++) {
            puts(*current);
        }
        if(0 == fork()){
                foo();
        }else{
                sleep(1);
        }
        return 0;
}

foo.c

#include <stdio.h>
void foo()
{
        printf("real foo
");
}

wfoo.c

#include <stdio.h>

void foo()
{
        printf("wrap foo
");
}

exec.c

将以上代码编译成执行文件和动态库

  • gcc -fPIC -shared foo.c -o libfoo.so
  • gcc -fPIC -shared wfoo.c -o libwfoo.so
  • gcc main.c -L./ -lfoo

执行LD_PRELOAD=./libwfoo.so ./a.out,其输出如下

[root@localhost ~]# LD_LIBRARY_PATH=./ LD_PRELOAD=./libwfoo.so ./a.out
LD_PRELOAD=./libwfoo.so
....
其他环境变量
...
wrap foo

也就是说fork()后的子进程其函数地址是和父进程一样的。

先执行了export LD_LIBRARY_PATH=./

仔细想了一想,其实这和LD_PRELOAD并没有关系。fork()创建了子进程,用的COW,代码段是只读的,父子进程用的应该是同一份。

2.

如果是创建子进程后执行exec簇函数,此时新程序镜像还会收到LD_PRELOAD的影响么?

#include <unistd.h>

int main()
{
        if(0 == fork()){
                execl("./a.out", "./a.out", NULL);
        }
        return 0;
}

gcc exec.c -o exec

[root@localhost ~]# LD_PRELOAD=./libwfoo.so ./exec
LD_PRELOAD=./libwfoo.so
....
其他环境变量
...
wrap foo

如果将excel()替换为excele(),则情况发生变化

[root@localhost ~]# ./exec
./a.out: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory

这意味着LD_LIBRARY_PATH环境变量失效了。

仔细阅读man 3 exec后,恍然大悟。exec函数簇可分为两类,一类将当前进程的char** environ指向的环境变量传递给新程序镜像,如execl(),另一类则只使用函数参数指定的环境变量,如execle()

综上,当使用execle()这类函数时,如果没有手动将当前进程的环境变量通过参数传递下去,那么就会使得LD_PRELOAD失效,无法实现函数拦截的功能了。

以上是关于从LD_PRELOAD探究子进程的环境变量的主要内容,如果未能解决你的问题,请参考以下文章

UNIX下的LD_PRELOAD环境变量

Golang SSH 加载 LD_PRELOAD 和 LD_LIBRARY_PATH 环境变量

越努力越幸运--2-LD_PRELOAD, fork ,僵尸进程

如何从Java设置环境变量?

Linux下利用动态链接劫持库函数并注入代码

LD_PRELOAD加载动态库