2010-07-18 12 views
5

mi código es el siguiente: preload.c, con el siguiente contenido:LD_PRELOAD afecta nuevo niño, incluso después de unsetenv ("LD_PRELOAD")

#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); 
} 

a continuación en la cáscara (hacer el segundo comando con cuidado !!):

gcc preload.c -shared -Wl,-soname,mylib -o mylib.so -fPIC 
    LD_PRELOAD=./mylib.so bash 

!!! tenga cuidado con el último comando que resultará con un bucle infinito de bifurcación "sh -c ls". Deténgalo después de 2 segundos con^C, (o mejor^Z y luego vea ps).

Más información

  1. Este problema se refieren a golpear de alguna manera; ya sea como el comando que el usuario ejecuta, o como el bash que ejecuta el popen.
  2. adicionales Factores clave: 1) realizar el popen desde la biblioteca precargada, 2) probablemente necesite hacer el popen en la sección de inicialización de la biblioteca.
  3. si utiliza:

    LD_DEBUG=all LD_DEBUG_OUTPUT=/tmp/ld-debug LD_PRELOAD=./mylib.so bash 
    

    en lugar del último comando, recibirá muchos archivos ld-depuración, llamado /tmp/ld-debug.*. Uno para cada proceso bifurcado. EN TODOS ESTOS ARCHIVOS verá que los símbolos se buscan por primera vez en mylib.so aunque LD_PRELOAD se eliminó del entorno.

+0

¿De qué idioma estamos hablando? – mvds

+0

estamos hablando del lenguaje C – avner

+0

@ user395074, entonces, tal vez, debería haber ajustado sus etiquetas para reflejar el idioma (haga clic en el enlace "editar"). Además, la etiqueta [preloader] no parece reflejar el componente del SO que estamos discutiendo. –

Respuesta

8

edición: por lo que el problema/pregunta en realidad era: howcome no puede desarmaste LD_PRELOAD de manera confiable usando un main_init() precargado desde bash.

La razón es que execve, que se llama después de que popen, lleva el medio ambiente de (probablemente)

extern char **environ; 

que es un poco variable de estado global que apunta a su medio ambiente. unsetenv() normalmente modifica su entorno y, por lo tanto, tendrá un efecto sobre los contenidos de **environ.

Si bash trata de hacer algo especial con el medio ambiente (bueno ... ¿sería un caparazón?) Entonces puede que tenga problemas.

Aparentemente, bash sobrecarga unsetenv() incluso antes de main_init(). Cambiar el código de ejemplo a:

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

muestra el problema. En carreras normales (que se ejecuta cat, ls, etc) me sale esta versión de unsetenv:

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

, correr o bashsh:

unsetenv: 46d170 

Así que, ahí lo tienen. bash te tiene engañado ;-)

Así que basta con modificar el medio ambiente en su lugar con su propio unsetenv, que actúa sobre **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'; 
    } 
} 

que puede verse a trabajar en el strace:

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

QED

+0

LD_PRELOAD no está en el entorno del proceso secundario (ni en el elemento primario después de la falta de conexión). La respuesta de Pavel indica el motivo: LD_PRELOAD es utilizado por el cargador y no por mi programa. mylib.so ya está cargado y el cargador probablemente mantiene su comportamiento sin volver a leer LD_PRELOAD. fork & execle es una opción; sin embargo, popen es muy preferible ya que me permite leer el resultado del proceso hijo de una manera simple. strace gace mucha información que no me ayudó. Utilicé: exportar LD_DEBUG = symnols y vi claramente que todos los símbolos se buscan en mylib.so antes de cualquier otra lib. – avner

+0

esto es cada vez más interesante, acaba de actualizar mi respuesta con algunas sugerencias más – mvds

+0

@avner: incluso más sugerencias en la respuesta, construí mi propio '.so' y todavía no puedo ver lo que está viendo ... – mvds

2

(La respuesta es una pura especulación, y puede haber es incorrecto).

Tal vez, cuando realiza el proceso, el contexto de las bibliotecas cargadas persiste. Por lo tanto, mylib.sose cargó cuando invocó el programa principal a través de LD_PRELOAD. Cuando desarmaba la variable y bifurcaba, no se cargaba de nuevo; sin embargo, ya se ha cargado en el proceso principal. Tal vez, debes descargarlo explícitamente después de bifurcar.

También puede intentar "degradar" símbolos en mylib.so. Para ello, vuelva a abrirlo a través dlopen con banderas que lo colocan al final de la cola de símbolo resolución:

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

+0

Gracias por el comentario de Pavel. Esto suena razonable; sin embargo, en mi caso esta solución es imposible. No puedo cambiar nada después del tenedor, ya que estoy usando popen, que hace el tenedor/ejecutivo por mí. (Aún así, fork & exec son la opción alternativa si no puedo hacerlo con popen). El proceso principal debe permanecer con el entorno LD_PRELOAD (es un proceso de cliente con varios subprocesos). – avner

+0

¿Estás diciendo que después de 'exec' las bibliotecas cargadas persisten? Veo en una barra que incluso libc se vuelve a abrir de nuevo después de 'exec', no puedo imaginar que alguna otra biblioteca prevalecerá. – mvds

+0

@mvds, volver a abrir y descargarse/cargarse son cosas diferentes. Puede volver a abrir una biblioteca ya cargada. –

0

¡la respuesta de mvds es incorrecta!

popen() generará un proceso hijo que hereda el archivo .so cargado previamente en el proceso principal. a este proceso hijo no le importa el entorno LD_PRELOAD.

Cuestiones relacionadas