Welcome to Coding : Sécurité Programmation Réseaux

Search   in  

 Create an Account Home | Submit News Your Account Content | Topics | Top 10  


Accueil
· Home
· Listing des Articles
· Top 10
· Repository des Exploits

Les sujets / parties
· C / C ++
· Visual Basic
· Asm
· Reseaux
· Java
· Securite
· Divers

Utile
· Listing des Articles

· Telecharger
· Le Forum
· Liens
· Proposer un article

Top20 des Downloads
· 1: Etude des reseaux generalites et protocoles
· 2: Cheval de troie en VB avec sources
· 3: Netcat 1.1
· 4: Keylogger
· 5: Etudes des reseaux hauts debits architectures et protocoles
· 6: Ecoute de port
· 7: Etude du Smart Spoofing
· 8: Win Packet Capture Utils
· 9: Tutorial on Traffic Interception on Switched Lan using ARP spoofing
· 10: Cours de C

User Info
Welcome, Anonymous
Nickname
Password
(Register)
Membership:
Latest: trapcodien
New Today: 1
New Yesterday: 0
Overall: 2207

People Online:
Visitors: 40
Members: 1
Total: 41

Online Now:
01: trapcodien

  
Comment faire des plugins en utilisant la librairie lidl
Posted on Friday, May 27 @ 16:25:12 CEST
Topic: C
C

	Comment faire des plugins en utilisant la librairie lidl . Ces fonctions sont à la base de la notion des "plug-in" en C et C++, leur utilisation me semble encore trop restreinte. Voyons à travers quelques exemples ce que nous pouvons en faire. 

Voir la suite ...

Notice Le présent document est publié sous la licence « Verbatim Copying » telle que définie par la Free Software Fondation dans le cadre du projet GNU.
Toute copie ou diffusion de ce document dans son intégralité est permise (et même encouragée), quel qu'en soit le support à la seule condition que cette notice, incluant les copyrights, soit préservée.
Copyright (C) 2003, 2004 Yann LANGLAIS, ilay, Intrasys.
La plus récente version en date de ce document est sise à l'url http://ilay.org/yann/articles/dlfcn/.
Toute remarque, suggestion ou correction est bienvenue et peut être adressée à Yann LANGLAIS, ilay.org. Introduction En 1992, alors que je découvrais les joies de l'édition de texte avec emacs, une question m'est venue à l'esprit. Pourquoi diable les extensions d'emacs ont-elles été écrites en elisp (emacs-lisp) et non en C? La réponse était simple. Il n'existait pas alors de possibilité standard en C de charger et décharger des bibliothèques en cours d'exécution. Quelques années plus tard, et bien qu'emacs ait été remplacé par vi dans mes habitudes dactylographiques, je découvris avec plaisir un outil qui rendait caduque mon explication. Je venais de tomber par hasard sur dlfcn.h. Ce fichier d'entète défini les prototypes de points d'entrée fort appréciable et permettant de charger et décharcher des bibliothèques et de récupérer des points d'entrée ou des symboles (variables) et de les utiliser a volonté. Aujourd'hui, et bien que ces fonctions soient à la base de la notion des "plug-in" en C et C++, leur utilisation me semble encore trop restreinte. Voyons à travers quelques exemples ce que nous pouvons en faire. Présentation de la libdl.so Présentation générale Description des points d'entrée dlopen extern void * dlopen(const char *, int); Cette fonction charge en mémoire la bibliothèque désignée par la chaîne de caractère passée en premier paramètre. Le second paramètre est un drapeau qui fixe le mode de fonctionnement de l'éditeur de lien à la volée. Ce second peut prendre les valeurs (documentées) suivantes :
  • RTLD_LAZY, pour ne résoudre les symboles de la bibliothèque à ouvrir que lorsque ceux-ci sont explicitement demandés,
  • RTLD_NOW, pour resoudre tous les symboles lors du chargement de la bibliothèque.

En complément de ces valeurs, il existe une co-valeur RTLD_GLOBAL augmentant la portée des symboles en les rendant disponibles externes (i.e. not préfixes en C par static) pour les bibliothèques chargées par la suite.
Elle s'empoie en l'accolant par un ou binaire « | » à l'une des valeur possible du drapeau :
RTLD_LAZY | RTLD_GLOBAL ou RTLD_NOW | RTLD_GLOBAL dlopen retourne un pointeur vers une poignée (handle) caractéristique de la bibliothèque sous forme d'un pointeur générique (void *). Celui-ci sera nul en cas d'échec. Notons que si dlopen renvoie NULL, la variable globale errno ne devrait pas avoir été modifiée et que strerr() ne pourra donc fournir d'explication correcte. Il faudra pour cela appeler dlerror(). dlsym extern void * dlsym(void *, const char *); dlsym se charge de retrouver (résoudre) l'adresse d'un symbole dont le nom est passé en paramètre. Le premier paramètre passé est un pointeur vers la « poignée » retournée par dlopen. Il existe deux poignées paticulières définies lorsque _GNU_SOURCE est définie. Ces poignées sont définie par les symboles RTLD_DEFAULT et RTLD_NEXT. RTLD_DEFAULT précise à dlsym qu'il faut rechercher la première occurence du symbole dans les bibliothèques déjà chargées en mémoire, dans l'ordre de leur chargement. RTLD_NEXT stipule à dlsym qu'il lui faut chercher l'occurence suivante du symbole dans les bibliothèques suivant la bibliothèque en cours. Ce pseudo-pointeur ne fonctionne que pour les bibliothèques partagées. Le second paramètre est le symbole à rechercher. dlsym retourne le pointeur sur le symbole demandé, ou, si celui-ci n'est pas retrouvé, un poineur NULL. dlclose extern int dlclose(void *); dlerror extern char * dlerror(void); dladdr extern int dladdr(void *, Dl_info *); Principe des "plug-in" ou greffons Le principe des greffons se base sur 2 points. Le premier est constitué par les fonctions dl* (ou des fonctions équivalentes) et le second par la définition d'un « protocole » représenté par un nombre prédédini de points d'entrée aux prototypes fixés, voire par la définitions d'une API particulière.
Les implémentations de ce principe sont nombreuses. Les modules du noyau linux souscrit à ce principe. Vers l'auto-programmation Le mythe de l'auto-programmation n'est pas récent. Le concept lisp » le programme est une liste et la liste est [potentiellement] un programme » en témoigne.
En règle générale, les langages de script sont des instruments de choix pour l'autoprogrammation ou la génération de code spécifique conditionnel et son evaluation à la volée : #!/bin/zsh cmd="ls -alrt $1" eval $cmd Le BASIC des années 80 avait sa commande « merge » qui permettait de créer du code dans un fichier et de le reinjecter par la suite.
De nombreux programmeur, dont je fais partie, ont un peu été déroutés par l'absence d'équivalent en C.

Mais, à l'étape de compilation près, les fonctions dl* sont maintenant disponibles pour mimer ce fonctionnement : #include #include #include #include #include /* Compilation : gcc -o dltest -g dltest.c -ldl */ int main(void) { int fd; void *lib; void (*funct)(); char code[] = "#include void hello() { printf("hello, worldn"); } "; /* Create hello.c */ if ((fd = open("./hello.c", O_WRONLY | O_CREAT, 0640)) /* compile hello.c */ if (system("gcc -shared -fPIC -G hello.c -o hello.so") /* read hello.so */ if (!((lib = dlopen("./hello.so", RTLD_LAZY)))) return 3; /* retrieve "hello" symbol pointer */ if (!(funct = dlsym(lib, "hello"))) { dlclose(lib); return 4; } /* call funct() */ funct(); dlclose(lib); return 0; } Chaînage de fonctions Détournements admettons que nous voulions afficher un message à chaque malloc() et a chaque free(). Il nous suffit de réécrire nos deux fonctions et de les faire charger en lieux et place des points d'entrée système par un petit tour de passe-passe. Cependant, il nous faut aussi appeler les fonctions du système afin d'effectuer les opérations d'allocation ou de désallocation. Le prémier tour de passe-passe (le chargement de notre code au lieu du code système) peut se faire de deux façons :
  • en recompilant le programme cible et en forcant l'éditeur de lien à utiliser notre bibliothèque,
  • en demandant le préchargement de notre bibliothèque (nettement plus élégant, et presque toujours possible.
Concentrons nous sur la seconde possibilité. Il nous suffit, avant lancement du programme cible, de renseigner la variable LD_PRELOAD : # en sh/ksh/bash/zsh ...: LD_PRELOAD=malib.so programme_cible # en csh/tcsh: (setenv LD_PRELOAD malib.so programme_cible) Supposons que notre programme cible tst_alloc soit le suivant : #include int main() { char *str; if (!(str = (char *) malloc(32))) return 1; free(str); return 0; } que nous compilons de la manière suivante : gcc -o tst_alloc tst_alloc.c Supposons maintenant le code mymalloc.c suivant que nous voulons executer : #include void * malloc(size_t size) { printf("malloc(%d) ", size); return NULL; } void free(void *p) { printf("free(%x) ", p); } que nous compilerons de la façon suivante afin d'en faire une bibliothèque : gcc -shared -o libmymalloc.so mymalloc.c Le lancement de tst_malloc seul s'effectue (normalement) sans erreur ni message et retournera 0 (la variable $? vaudra 0). Inserrons maintenant notre bibliothèque: LD_PRELOAD=libmymalloc.so tst_malloc affichera malloc(32) de plus, la commande echo $? retournera 1, ce qui est tout à fait normal, puisque notre fonction malloc retournera le pointeur 0 et n'effectuera pas l'allocation. Voyons maintenant comment appeler le VRAI malloc au sein de notre malloc. Pour cela, penchons nous sur l'aide de dlsym() (man dlsym). Nous voyons que certains defines peuvent remplacer le pointeur vers la bibliothèque (handle). En particuliers RTLD_NEXT. L'appel à dlsym(RTLD_NEXT, symbol) va rechercher le point d'entrée "symbol" dans la bibliothèque suivante (par rapport à celle en cours). Notre bibliothèque libmymalloc.so va donc se compliquer de la sorte : #include #include void * malloc(size_t size) { static void *(*sys_malloc)(size_t) = NULL; if (!sys_malloc) { if (!(sys_malloc = (void *(*)(size_t)) dlsym(RTLD_NEXT, "malloc"))) { perror("cannot fetch system malloc "); exit(1); } } printf("malloc(%d) ", size); return sys_malloc(size); } void free(void *p) { static void (*sys_free)(void *) = NULL; if (!sys_free) { if (!(sys_free = (void (*)(void *)) dlsym(RTLD_NEXT, "free"))) { perror("cannot fetch system free "); exit(2); } } printf("free(%x) ", p); sys_free(p); } L'étape de compilation de la bibliothèque se voit aussi légèrement modifiée : gcc -shared -o libmymalloc.so mymalloc.c -ldl -D_GNU_SOURCE La commande LD_PRELOAD=libmymalloc.so tst_malloc affichera malloc(32) free(20888) (l'adresse en paramêtre de free est variable en fonction de l'architecture, entre autre) et la variable $? devrait maintenant être à 0 De nombreuses applications de cette technique sont possibles. Elles peuvent aller du débugger mémoire (voir http://ilay.org/yann/articles/memdbg/ pour un débugger plus "complet") à l'écriture de chevaux de Troie ou portes dérobées... Certaines restrictions sont cependant tout naturellement imposées pour des questions de sécurité : LD_PRELOAD est ignoré par les programmes en suid :). Espionnage de greffons Lorsqu'on programme des greffons, il arrive que les greffons ne se chargent pas correctements, que certains symboles ne soient pas définis, ou encore que le freffon chargé ne soit pas celui attendu. Il arrive de plus que le programme greffé ne nous transmette pas les messages d'erreur de la libdl.so. Dans ces cas, il est assez difficile de trouver les causes des dysfonctionements. Pour contourner le problème, on peut détourner les fonctions dlsym, dlopen, dlerror et dlclose. Oui mais voilà, en détournant dlsym, comment récupérer le point d'entrée dlsym du système? Certains systèmes nous facilitent la tache. Un nm de /usr/lib/libdl.so.1 sur Solaris 8 nous montre que les symboles dlsym, dlopen, dlclose et dlerror ont des contreparties en _dlsym, _dlopen, _dlclose et _dlerror : nm /usr/lib/libdl.so.1 | egrep "dlsym|dlopen|dlclose|dlerror" [37] | 2236| 8|FUNC |GLOB |0 |7 |_dlclose [35] | 2244| 8|FUNC |GLOB |0 |7 |_dlerror [27] | 2220| 8|FUNC |GLOB |0 |7 |_dlopen [55] | 2228| 8|FUNC |GLOB |0 |7 |_dlsym [26] | 2236| 8|FUNC |WEAK |0 |7 |dlclose [53] | 2244| 8|FUNC |WEAK |0 |7 |dlerror [47] | 2220| 8|FUNC |WEAK |0 |7 |dlopen [44] | 2228| 8|FUNC |WEAK |0 |7 |dlsym Les points d'entrée standard semblent bien être des alias de plus faible priorité que les originaux préfixés par un souligné. Dans ce cas, il est possible d'appeler directement _dlsym dans à l'intérieur de notre fonction détournée. Sous Linux, par contre, ces alias n'existent pas. un nm de la libdl.so ne nous apprendra en fait rien dans les distributions où les bibliothèques systèmes sont débarassées de leurs symboles (strip). L'utilitaire string nous donnera les points d'entrée. Las , aucun de ces points d'entrée, après consulatation des sources de la glibc et tests ne nous sera d'un grand secours. Il faut alors se tourner vers la fonction dlvsym(void *handle, const char *name, const char *version_str) dont la documentation laisse encore à désirer, quelqu'en soit la source. Cette fonction nécessite en plus du nom du point d'entrée une chaîne de caractères représentant la version. Pour retrouver cette chaîne, il suffit d'écrire un petit bout de code faisant appel à dlsym, de le compiler et le lier avec la libdl.so, puis faire un nm sur le binaire. nm dltest ... 08049840 W data_start U dlclose@@GLIBC_2.0 U dlopen@@GLIBC_2.1 U dlsym@@GLIBC_2.0 ... Cette chaîne est accolée au symbole dlsym sous la forme "dlsym@@version_st" : nm dltest | grep dlsym | cut -d@ -f3 GLIBC_2.0 Une fois cette chaîne trouvée, nous retrouvons le point d'entré, et nous pouvons réécrire nos fonctions : #include #include #ifdef _LINUX_ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #endif #include typedef struct { void *handle; void *(*open)(const char *, int); void *(*sym)(void *, const char *); char *(*error)(void); int (*close)(void *); } dlspy_t; dlspy_t __dlspy__ = { NULL, NULL, NULL, NULL, NULL }; #ifdef LINUX #include "config.h" #ifndef DLSPY_GLIBC_VERSION #define DLSPY_GLIBC_VERSION "GLIBC_2.0" #endif #endif void dlspy_init() { /* 1st step: save standard dl* functions */ #if defined(SUN) __dlspy__.sym = (void *(*)(void *, const char *)) _dlsym; __dlspy__.open = (void *(*)(const char *, int)) _dlopen; __dlspy__.error = (char *(*)(void)) _dlerror; __dlspy__.close = (int (*)(void *)) _dlclose; #elif defined(LINUX) if (!(__dlspy__.sym = dlvsym(RTLD_NEXT, "dlsym", DLSPY_GLIBC_VERSION))) { perror("dlspy fatal: cannot fetch system's dlsym entry point "); exit(1); } __dlspy__.open = (void *(*)(const char *, int)) __dlspy__.sym(RTLD_NEXT, "dlopen"); __dlspy__.error = (char *(*)(void)) __dlspy__.sym(RTLD_NEXT, "dlerror"); __dlspy__.close = (int (*)(void *)) __dlspy__.sym(RTLD_NEXT, "dlclose"); #else #error ERROR: cannot get dl* original entry points.; #endif } void dlspy_echo(char *fmt, ...) { va_list args; char buf[1024], buf2[1024]; va_start(args, fmt); sprintf(buf2, "dlspy: %s", fmt); vsprintf(buf, buf2, args); fprintf(stderr, "%s ", buf); va_end(args); } char * dlerror(void) { static char *errstr = NULL; char *t; if (!__dlspy__.open) dlspy_init(); t = __dlspy__.error(); if (!t && errstr) return errstr; if (t) { errstr = t; return t; } return (char *) strdup(" "); } void * dlopen(const char *name, int mode) { void *t = NULL; if (!__dlspy__.open) dlspy_init(); dlspy_echo("try to open "%s" shared object with mode %d...", name, mode); if (!(t = __dlspy__.open(name, mode))) dlspy_echo("failed (%s) ", dlerror()); else dlspy_echo("ok "); return t; } void * dlsym(void *h, const char *name) { void *t = NULL; if (!__dlspy__.open) dlspy_init(); dlspy_echo("try to bind symbol "%s" as new entry point...", name); if (!(t = __dlspy__.sym(h, name))) dlspy_echo("failed (%s) ", dlerror()); else dlspy_echo("ok at %x ", t); return t; } int dlclose(void *h) { if (!__dlspy__.open) dlspy_init(); dlspy_echo("dlclose(%p)", h); return __dlspy__.close(h); } Peut-on aller plus loin? Un de mes objectifs est de pouvoir tracer chaque appel aux fonctions résolues par dlsym(). Pour ce faire, il semble nécessaire de définir un pool de fonctions qui devront, lors du premier appel, prendre l'addresse du point d'entrée fraichement résolu, et dans, un second temps, appeler le point d'entrée avec les paramètres qui lui ont été passés, sans pour autant connaître leur nombre ni leur taille. Je n'ai pour le moment aucune solution précise en tête, mais il me semble, a priori, qu'il faille pour ce faire jongler en (assembleur?) avec la pile. En tout état de cause, je suis preneur de toute solution. La tour de babel Conclusions Références linux standard base v1.3, chapitre 13 Libraries man dlopen; man dlclose; man dlsym; man dlvsym, man dladdr; man dlerror L'éditeur de lien et ses variables d'environnement, Samuel Dralet, Linux Magazine France numéro 63, Juillet/Août 2004, page 76 Gestion de la mémoire en C, http://ilay.org/yann/articles/mem/ Structures avancées de gestion de la mémoire (listes doublement chaînées, btrees, hash coding, ...), http://ilay.org/yann/articles/toolbox/ D'autres articles techniques : http://ilay.org/yann/articles/


 
Liens connexes
· Plus à propos de C
· Nouvelles transmises par Romain_Le_Guen


L'article le plus lu à propos de C:
Programmer un sniffeur (sniffer ?) de deux facons.


Article Rating
Average Score: 0
Votes: 0

Please take a second and vote for this article:

Excellent
Very Good
Good
Regular
Bad


Options

 Format imprimable Format imprimable


PHP-Nuke Copyright © 2005 by Francisco Burzi. This is free software, and you may redistribute it under the GPL. PHP-Nuke comes with absolutely no warranty, for details, see the license.
Page Generation: 0.92 Seconds