AFFICHER CET ARTICLE EN MODE PAGE ENTIERE

SOMMAIRE

 

1) Introduction

2) Qu'est-ce qu'une DLL ?

3) Ecriture d'une DLL

4) Ecriture d'un programme utilisant notre DLL

5) Conclusion


 

1) Introduction

Cet article vise à vous montrer les possibilités offertes par les DLL WIN32, utilisées par beaucoup d'applications, y compris WINDOWS lui-même.
Nous allons tout d'abord expliquer l'utilité de tels fichiers, puis nous allons voir comment écrire nos propres DLL en C.
Pour comprendre cet article, il est préférable de posséder des bases en C.

 

 

2) Qu'est-ce qu'une DLL ?

Vous vous êtes peut-être déjà demandé à quoi correspondaient tout ces fichiers *.dll présents dans la plupart des programmes, et si abondants dans le dossier ‘c:\windows\system32’.
Ces fichiers ne contiennent en réalité aucun code exécutable, mais seulement une liste de fonctions ‘exportées’ ; DLL signifiant par ailleurs Dynamic Linked Library (Bibliothèque Liée Dynamiquement), en opposition aux bibliothèques classiques de fonctions qui sont qualifiées de ‘statiques’.
Cela signifie que n’importe quel exécutable peut accéder à ces fonctions et les utiliser, sans que la fonction ne soit chargée en mémoire indépendamment pour chaque programme. Ainsi, la mémoire est économisée, et si la même fonction est utilisée par différents exécutables, il est avantageux de l’écrire dans une DLL. Toutes les fonctions que vous utilisez habituellement dans vos programmes, telles que strncpy() ou printf() sont stockées dans les DLL de WINDOWS, pour que la mémoire ne soit pas surchargée par l’utilisation constante de ces fonctions.
Si vous voulez avoir la liste des fonctions exportées par une DLL, vous pouvez par exemple les ouvrir avec W32Dasm et aller dans le menu Functions | Exports , vous pourrez ainsi voir à quoi sert chaque DLL. Il existe aussi des outils spécialisés permettant de faire la même chose, comme ‘Depends’, fourni avec VISUAL C++ 6.0 (et peut-être les autres versions).

 

 

 

3) Écriture d’une DLL

Bien, maintenant que vous savez en quoi consiste une DLL, il est temps de passer à la pratique, et d’en coder une nous-mêmes. Comme exemple, j’ai choisi une DLL simple, exportant une seule fonction permettant une vérification de mot de passe. Il s’agit d’un exemple simple, pas très utile en soi, mais qui me permettra d’illustrer mes explications.

Tout d’abord il faut savoir qu’une DLL ne contient pas de fonction main() comme les exécutables classiques, mais une fonction équivalent qui lui est propre : DllMain().

Sa structure est la suivante, comme indiquée dans la documentation de microsoft :

BOOL WINAPI DllMain(
_ HINSTANCE hinstDLL, // handle to the DLL module
_ DWORD fdwReason, ___// reason for calling function
_ LPVOID lpvReserved _// reserved
);

Cette fonction constitue donc la fonction principale d’une DLL, et est par conséquent indispensable à sa compilation. A l’image des arguments de la fonction main(), ce n’est pas à vous de donner une valeur aux trois arguments de DllMain() : ils servent à apporter des précisions sur les conditions d’appel de la DLL.

hinstDLL est un handle vers la DLL en cours, c’est-à-dire qu’il nous servira à désigner la DLL dans certaines fonctions.
lpvReserved est très peu utile, et vaut NULL la plupart du temps.
L’argument le plus important est fdwReason, qui nous indique pour quelle raison la fonction DllMain() a été appelée.

Il peut prendre quatre valeurs différentes :

- DLL_PROCESS_ATTACH : La DLL est chargée en mémoire suite à l’appel de la fonction LoadLibrary().
- DLL_THREAD_ATTACH : Le processus est en train de créer un nouveau thread et a déjà fait appel à LoadLibrary().
- DLL_THREAD_DETACH : Le processus ferme un thread.
- DLL_PROCESS_DETACH : La DLL est déchargée de la mémoire.


Voici donc un exemple de mise en oeuvre classique de la fonction DllMain() :

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
_____switch(dwReason)
_____{
_____ case DLL_PROCESS_ATTACH:
______case DLL_THREAD_ATTACH:
______case DLL_THREAD_DETACH:
______case DLL_PROCESS_DETACH:
_____ break;
_____}

________return TRUE;
}


Dans notre DLL, nous n’aurons pas besoin d’aller plus loin dans l’exploitation de cette fonction.

 

Il nous faut aussi penser à inclure le header nécessaire aux DLL :

#include "Windows.h"



 

Ensuite, nous devons écrire la fonction exportée par la DLL, que nous appelons VerifPwd(). Elle doit prendre comme argument une chaîne de caractères, et retourner ‘true’ si elle correspond au mot de passe, ‘false’ le cas contraire. Comme on aime bien avoir un code propre, on définit le mot de passe avec #define, afin qu’il soit plus facilement modifiable. Ce qui donne :

#define PWD "lol"

bool VerifPwd(char* pass)
{
_______if(!strncmp(PWD, pass, 3))
_________return true;

_________else
_________return false;
}


A ce stade, la compilation devrait fonctionner, et on obtient un fichier *.dll . Par contre, si l’on essaie de le désassembler, on remarque qu’aucune fonction n’est exportée. En effet, il faut indiquer au compilateur que VerifPwd() doit être exportée, et donc que ce n’est pas simplement une fonction utile à la DLL. Pour cela, il faut ajouter ‘extern "C" __declspec(dllexport)’ devant l’en-tête de chaque fonction exportée. Toujours pour plus de clarté dans le code, on utilise l’instruction #define :

#define DLLEXPORT extern "C" __declspec(dllexport)

DLLEXPORT bool VerifPwd(char* pass)
{
__// code de la fonction
}


On relance la compilation et tout fonctionne : on a désormais une DLL avec une fonction VerifPwd() exportée et utilisable par n’importe quel programme.

 

 

 

4) Écriture d’un programme utilisant notre DLL

Pour tester notre DLL, rien de tel qu’un programme appelant la fonction VerifPwd(). Pour que la DLL soit utilisable, il faut la copier dans le répertoire du programme appelant et la renommer par exemple en verif.dll.

Nous allons avoir besoin de nouvelles fonctions pour l’implémentation de notre DLL :

HMODULE LoadLibrary(
__LPCTSTR lpFileName // file name of module
);

 

LoadLibrary() prend comme argument une chaîne contenant le nom de la DLL, la charge en mémoire, et retourne un handle vers cette dernière.

BOOL FreeLibrary(
__HMODULE hModule // handle to DLL module
);

FreeLibrary() prend comme argument un handle vers la DLL et la décharge de la mémoire. Si l’opération n’a pas fonctionné, la fonction retourne 0.

FARPROC GetProcAddress(
__HMODULE hModule, ___// handle to DLL module
__LPCSTR lpProcName __// function name
);

GetProcAddress() prend comme arguments un handle vers la DLL et une chaîne contenant le nom de la fonction à appeler, et retourne l’adresse mémoire de cette dernière.

 

A l’aide de ces fonctions, nous pouvons aisément écrire notre programme, dont voici la source commentée :

#include <iostream> ___________________________// header pour les sorties
#include "Windows.h" __________________________// header pour la DLL

using namespace std; __________________________// toujours pour les sorties

typedef bool (CALLBACK* VerifPwd)(char *); ____// définition d'une structure pour la fonction appelée
VerifPwd pVerifPwd = NULL; ____________________// déclaration d'un pointeur pour stocker l'adresse de la fonction

int main()
{
_______char pass[10]; _________________________// tableau d'entrée du mot de passe
______ HMODULE hDll; __________________________// déclaration d'un handle vers la dll

_______hDll = LoadLibrary("verif.dll"); _______// on charge la DLL en mémoire

_______if(!hDll) ______________________________// si elle n'est pas chargée...
_______________cout << "La DLL n'a pas pu etre trouvee.\n"; ______________________// ... on affiche une erreur

----------- else_______________________________________________________________________// sinon on continue
_______{
_______________pVerifPwd = (VerifPwd)GetProcAddress(hDll,"VerifPwd"); ____________// on initialise le pointeur __________________________________________________________________________________//avec l'adresse de la fonction

_______________if(!pVerifPwd) ____________________________________________________// si ca n'a pas fonctionné...
______________________cout << "La fonction n'a pas pu etre trouvee dans la DLL\n"; // ... on affiche une erreur

____________________else ______________________________________________________________// sinon on continue
_______________{
______________________cin.getline(pass, 10); ______________________// le mot de passe est stocké dans le tableau

______________________if(pVerifPwd(pass)) _________________________// appel de la fonction de la DLL
_____________________________cout << "Vous etes bien enregistre.\n\n";

______________________________else
_____________________________cout << "Le mot de passe est errone.\n\n";
_______________}

____________________FreeLibrary(hDll); _________________________________// on libère la DLL de la mémoire
________}

___________system("pause");
________return 0;
}

On compile, on test, et tout fonctionne.

5) Conclusion

Grâce à cet article, vous avez donc programmé votre première DLL et appris à écrire un programme utilisant des fonctions exportées par une DLL. J’espère ainsi vous avoir fait entrevoir une des multiples facettes de ce fantastique langage qu’est le C.
Mais souvenez-vous, la programmation est principalement de la pratique et du test. Alors désormais, c’est à vous de coder vos propres DLL et de faire vos propres essais afin d’en découvrir les nombreuses possibilités.
Dans mon prochain article nous approfondirons ces connaissances en programmant des DLL permettant de logger les appels aux fonctions exportées des DLL, et vous aurez besoin d’avoir bien compris cet article.

Amusez-vous bien et bon coding de DLL !

 

BY THESHADE

Copyright © 2004 ARENHACK - DHS

HAUT DE PAGE