• Aucun résultat trouvé

Il Faites-vous respecter par votre machine !

Par ce premier exemple, nous allons explorer ce que permet LD_PRELOAD.

2.1

Le problème

Parfois, je trouve que les messages d'erreur renvoyés par ma machine sont un peu agressifs.

S i s xxx . 1 çq

ls: impossible d'acceder a xxx.jpg: Aucun fichier ou dossier de ce

type

15

_ _ _

56 Open S i l i c i u m Magazine N°4 www.opensilici u m.co m

Pourquoi tant de haine ?? ]e suis déjà désolé de ne pas retrouver mon fichier,j'apprécierais donc que cette machine fasse preuve d'un minimum de compassion à mon égard, plutôt que de me renvoyer un message si abrupt ! ]e crois même y discerner une vague insinuation que je ne tape pas les bonnes commandes ! Non mais c'est qui le chef ??

L D_P R E LOAD va nous permettre de lui apprendre la 'politesse,

2.2

Premier essai

La première étape est de trouver quelle fonction af­

fiche ces messages insultants. Pour cela, on peut utiliser lt race, qui affiche les appels de fonction effectués par le processus :

S ltrace ·0 S i tty ' ls xxx,jpg 2>&1 1 sec -e s . */# ICI LE COUPABLE 1 =/ g '

1 l " � , ]

, �IIIIU I Uldliull ( ) = 0xb7797694

1

ff ICI LE COUPABLE ! ;;

errorl� , l , �xh7;Rfba4 , ex826C,jeJ , extlS8fbdt, =

eXlt i2 "Jnfln i shed "

1

Normalement, c'est-à-dire sans l 'option - 0 <fichier>, ltrate affiche la trace des fonctions détectées sur la sortie d'erreur, Or, ls va envoyer son message d'erreur sur cette même sortie d'erreur, ce qui peut rendre les choses difficiles à lire, j'avoue que dans ce cas simple, la difficulté aurait été toute relative, mais il m'a été difficile de résister à chercher

la petite commande qui va bien. ;)

C'est donc apparemment la fonction e r ro r ( ) qui est responsable de ma mauvaise humeur.

Après avoir regardé un peu sa page de manuel, on peut redéfinir cette fonction pour imiter son comportement en l'adaptant à notre besoin,

Dans cette première version, pour simplifier, on ne va pas

gérer les deux premiers paramètres de cette fonction (status et errnum) et on sortira dans tous les cas à la fin (en théorie cela dépend de status).

#defl ne _GNU_SOURCE

#incl ude <std i o . h>

#i ncl ude <stda r g , h>

#incl ude <stdl i b . h;>

#define PREFIXUOLI "Monsieur, vous m ' en voyez desol ee, " \ 'ma i s je croi s que l a commande a renvoye une erreur . . . " \ 'je vous l ' i ndi que ci -apres . \n " \

"Je sui s a votre d i spos i t i on pour toute autre requete ; " \ 'je sui s enti erement devouee a vous satisfai re ."

void error ( i nt statu s , i nt errnum, const char *format , . . . }

Le monde merveilleux de LD]RELOAD

vaJ i st api

char *chai ne_formatee ;

Il formatons l a cha i ne de l ' erreur va_start(ap, format } ;

vasprintf(&chai ne_formatee , format , ap} ; va_end(ap} ;

Il affi chons cette chai ne avec un

LD PRELOAD

Il prefixe qui rend l es choses pl us agreabl es

fprintf( stde r r , "%s\n%s\n" , PREFIXUOLI , chai ne_forma tee } ; Il 1 i berons 1 a memoi re

free( chai ne_forma tee } ; li on qui tte exi t ( EXIUAI LURE} ;

La seule chose un peu moins évidente dans cette fonction, si vous n'y êtes pas habitué, est la gestion des arguments variables (avec les macros va_start ( ), va_end ( ) et la fonc­

tion vasprintf( ), pour lesquelles vous pourrez au besoin consulter les pages de manuel correspondantes),

Compilons ce code (my _e rror. c) sous -la forme d'une librairie partagée libmy _e rror. so :

1 S gcc -Wa l l ·'PIC - c - c �Le'rnr n �y errnr c

1

i gcc - sha red ··PI: -11· .-SU'd WI . b�y " 1 I J I . c" I,llbmy_error.so

�y e'rJ ' , Ü ,

- - - ----

--Ce petit makeiile nous permettra d'automatiser la chose :

CC=gcc LD=gcc

CFLAGS_OYNLIB=$ (CFLAGS} ·Wa l l ·fPIC LOFLAGS_OYNLIB=$( LOFLAGS } ·fPIC ·shared OBJECTS=mLerror . 0

a l l : l i bmLerror . so l i bmLerro r . so : $ ( OBJECTS )

$ ( lO) $ ( LOFLAGS_OYNLIB) ·Wl , · soname ·Wl , $@ .0 $@ $A

%.0: % . C

$(CC) $ ( CFLAGS_OYNLIB) ·c ·0 $ * . 0 $* . c

O n v a ensuite charger cette librairie partagée'au démar­

rage de notre processus t5 xxx.j pg, en plus des librairies normales dont a besoin l'exécutable ls. Il suffit pour cela de définir la variable d'environnement LD]RELOAD avec le

chemin vers notre librairie :

Au lancement de cette commande t5 xxx.j pg, la fonction error ( ) sera donc définie avec exactement le même prototype

dans 2 librairies partagées différentes (la nôtre et la 'libc).

1 EN C O U V E RT U R E 1 R E P E R E S 1 E X P É R I M E NTATION 1 D O M OT I Q U f 1 RESI':AU 1

Mais, comme on parle de PREloading, notre librairie sera chargée la PREmière, et c'est donc elle qui recevra les appels à cette fonction.

Testons :

S i S xxx . j pg

Monsieur. vous m ' en voyez desol ee. mai s je croi s que l a commanae renvcye une erreur . . . je vous l i ndi QJe ci · aJres.

Je s u ' s a voUe c' spos ' , ; or Jour toute adtre reqlJete je sui s ert'ere�er, cevouee a VOJS sa:i sfa i re .

' mpos s ' b l e a ' a::éder à xxx . jJg 1

Aaaah c'est quand même plus agréable. :)

2.3

dlsym(RTLD_NEXT, ... )

,

le chaînon manquant

La fonction error( ) de notre exemple était simple, donc facile à imiter. Mais si on veut implémenter toutes les carac­

téristiques de cette fonction (gestion de status et errnum, écriture vers la sortie d'erreur et non pas la sortie standard, ajout du message correspondant à strerror(errno), etc.), on va finalement compliquer notre code de manière assez importante. Ce qu'on voudrait faire, c'est juste afficher notre message sympathique, puis passer la main à la fonction error( ) originelle, celle de la libc.

Le souci, c'est qu'en écrivant une fonction avec le même prototype, on a, en quelque sorte, caché cette fonction ori­

ginelle : si on tente de l'appeler directement, c'est vers notre fonction que l 'on sera redirigé.

On peut néanmoins obtenir un pointeur vers cette fonction error( ) originelle de la manière suivante :

typeof(error) *Ubc_error;

[ . . . )

Ubc_error = dlsym(RTLD_NEXT, "error" ) ;

Quelques explications s'imposent. L a fonction dlsym O fait partie de la l ibrairie l i b dl. Cette librairie permet d'implémenter un système de plugins dans une application, le plus souvent en utilisant uniquement les 3 fonctions suivantes :

-dtopen O pour charger une librairie ;

-dlsym ( ) pour obtenir un pointeur vers un des symboles de cette librairie (en général, on lui passe un nom de fonction et on obtient un pointeur vers cette fonction) ; -dlclose ( ) une fois qu'on n'a plus besoin de cette librairie.

Notre technique basée sur LD_PRELOAD est un cas un peu particulier. Premièrement, les librairies en question, et plus particulièrement la libc, ont déjà été chargées au lan­

cement du processus. Donc, on ne va pas utiliser dlopen ( )

n i dlcloseO, et dlsym O va devoir chercher le symbole dans ces librairies déjà chargées. Deuxièmement, on veut pointer sur la fonction e rrorO de la libc et non celle de libmy _error. 50. On doit donc indiquer à dlsym O qu'il faut commencer la recherche après la librairie courante, à savoir libmy _e rror. 50. En indiquant RTLD_NEXT comme premier argument. dlsym O va satisfaire ces deux exigences.

On sait maintenant tout ce qu'il faut pour écrire notre deuxième version :

#defi ne _GNUOURCE

#include <dlfcn,h> Il pour dl sym( )

#incl ude <stdio , h>

#incl ude <stda rg , h>

#incl ude <stdl i b . h>

#define PREFIXE_POLI "Monsieur, vous m ' en voyez desolee, " \ 'ma i s je croi s que l a commande a renvoye une erreur . . . " \

"je vous l ' i ndique c i · apres . \n " \

"Je sui s a votre di spos i t i on pour toute autre requete ; " \ 'je s u i s entierement devouee a vous sati sfai re . "

voi d erro r ( i n t status , i n t errnum, const char *Format , . . ,) ( typeof(error) *libc_error;

vaJ i st api

char *chai ne_formatee;

Il affi chons 1 e message sympathi que fpri ntH stder r , "%s\n " , PREFIXUOLI ) ; Il formatons l a chaine d e l ' erreur va_start(ap, forma t ) ;

vaspri ntf(&chai ne_formatee, format , ap) ; va_end(ap) ;

Il recuperons un poi nteur vers l a "vraie"

Il Fonct i on error ( ) de l a l i bc libc_error = dlsym(RTLD_NEXT, "error") ;

1 1 appelons cette fonct i on

(*libc_error) (status, errnum, "\5", chaine_Formatee) i

Il 1 i berons 1 a memoi re free( chai ne_forma tee ) ;

O n peut remarquer que c e code n'est guère plus complexe

que le précédent, alors que mine de rien on a mis en place toutes les fonctionnalités de la fonction error( i originelle ...

Testons :

!

! ma ke lee '1Ia" ·fP:C · e ·0 �y erro r . o �y errJ r . c ._---, lCC .tPI C · sha red ·',·il . · loname ·�· . i i bmy error . so -0 ' l bny err:r . SO r'Lerror . o

: i LD _PRELOAD=! PIŒlibmL error. 50 I l xxx . j rg

'Ionsieur. vous m ' en vey'z desol ee. cd i , ,le uoi s que I d WII;lldlld" d renvoye Uêe erreur . . . j" VOliS l ' i nd i que ci · Jpr" ,; .

Il Quelques infos