LD_PRELOAD affects new child even after unsetenv(“LD_PRELOAD”)

前端 未结 3 850
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-05 00:39

my code is as follows: preload.c, with the following content:

#include 
#include 

int  __attribute__((constructor))  main_ini         


        
相关标签:
3条回答
  • 2021-01-05 01:07

    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.

    0 讨论(0)
  • 2021-01-05 01:08

    (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);
    

    0 讨论(0)
  • 2021-01-05 01:19

    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.

    0 讨论(0)
提交回复
热议问题