mon code est le suivant: preload.c, avec le contenu suivant:
#include #include int __atsortingbute__((constructor)) main_init(void) { printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD")); FILE *fp = popen("ls", "r"); pclose(fp); }
puis dans la shell (faites la 2ème commande avec soin !!):
gcc preload.c -shared -Wl,-soname,mylib -o mylib.so -fPIC LD_PRELOAD=./mylib.so bash
!!! attention avec la dernière commande il en résultera une boucle sans fin de “sh -c ls”. Arrêtez-le après 2 secondes avec ^ C, (ou mieux ^ Z et voyez ensuite ps).
si tu utilises:
LD_DEBUG=all LD_DEBUG_OUTPUT=/tmp/ld-debug LD_PRELOAD=./mylib.so bash
Au lieu de la dernière commande, vous obtiendrez de nombreux fichiers ld-debug, nommés /tmp/ld-debug.*. Un pour chaque processus fourchu. Dans tous ces fichiers, vous verrez que les symboles sont d’abord recherchés dans mylib.so même si LD_PRELOAD a été supprimé de l’environnement.
edit: le problème / la question était donc le suivant: comment pouvez-vous désinstaller LD_PRELOAD
manière fiable en utilisant main_init()
préchargé depuis bash
?
La raison est que execve
, qui est appelé après que vous ayez popen
, prenne l’environnement (probablement)
extern char **environ;
qui est une variable d’état globale qui pointe vers votre environnement. unsetenv()
modifie normalement votre environnement et aura donc un effet sur le contenu de **environ
.
Si bash
essaie de faire quelque chose de spécial avec l’environnement (eh bien … serait-ce un shell?) Alors vous pourriez avoir des problèmes.
unsetenv()
, bash
surcharge unsetenv()
avant même main_init()
. Changer l’exemple de code pour:
extern char**environ; int __atsortingbute__((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); }
montre le problème. Dans les courses normales ( cat
courant, ls
, etc.) je reçois cette version de unsetenv:
unsetenv: 7f4c78fd5290 unsetenv: 7f1127317290 unsetenv: 7f1ab63a2290
Cependant, en exécutant bash
ou sh
:
unsetenv: 46d170
Alors, voilà. bash
vous a dupé 😉
Il suffit donc de modifier l’environnement en place en utilisant votre propre unsetenv
, en agissant sur **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'; } }
qui peut être vu pour travailler dans le strace
:
execve("/bin/sh", ["sh", "-c", "ls"], [... "DD_PRELOAD=mylib.so" ...]) = 0
QED
(La réponse est une pure spéculation et peut être incorrecte).
Peut-être que lorsque vous décomposez votre processus, le contexte des bibliothèques chargées persiste. Ainsi, mylib.so
été chargé lorsque vous avez mylib.so
le programme principal via LD_PRELOAD
.Lorsque vous avez désélectionné la variable et que vous avez créé une fourchette, celle-ci n’a plus été chargée. Cependant, il a déjà été chargé par le processus parent. Peut-être devriez-vous le décharger explicitement après avoir bifurqué.
Vous pouvez également essayer de “rétrograder” les symboles dans mylib.so
. Pour ce faire, rouvrez-le via dlopen
avec les indicateurs qui le placent à la fin de la queue de résolution des symboles:
dlopen("mylib.so", RTLD_NOLOAD | RTLD_LOCAL);
la réponse de mvds est incorrecte!
popen () va générer un processus enfant qui hérite du fichier .so préchargé dans le processus parent. ce processus enfant ne se soucie pas de l’environnement LD_PRELOAD.