L'API Win32 est riche de centaines de fonctions, que j'appelle fonctions systèmes (par oppositions aux fonctions ne faisant pas partie de cette API).
Les programmes en font un usage intensif. Mais on peut se demander comment ils font pour appeler celles-ci !
Comment un programme obtient-il l'adresse des fonctions systèmes qu'il va utiliser ?
Vous le savez certainement, les fonctions système sont contenues dans diverses dll "système" (installées avec Windows). Parmi les plus courantes : kernel32.dll (gestion de processus, mémoire, système de fichiers, ...), user32.dll (gestion des fenêtres, boîtes de dialogue ...), gdi32.dll (fonctions de dessin, ...).
Mais comment l'exécutable fait-il pour appeler les fonctions que ces dll contiennent ?
Si vous avez déjà travaillé avec les dll, vous connaissez l'existence de la fonction GetProcAddress qui permet justement d'obtenir l'adresse d'une fonction dont on spécifie le nom. Mais un programme qui vient d'être chargé en mémoire ne peut pas utiliser cette fonction, puisqu'il n'a pas son adresse !
De plus il a aussi besoin de la fonction LoadLibrary qui permet de charger une dll en mémoire afin de l'utiliser.
Il existe donc un autre procédé.
En fait, c'est Windows qui, quand il charge l'exécutable en mémoire, va aussi charger les dll que le programme veut utiliser et va initialiser un tableau avec l'adresse de toutes les fonctions que le programme souhaite appeler. C'est donc lui qui va procéder aux appels de GetProcAddress et non pas le programme.
Mais comment Windows connaît-il les dll et fonctions que le programme veut utiliser ? Pour cela, il consulte les informations contenues dans l'exécutable. Pour plus d'informations, consultez une documentation sur le format PE. Mais voici tout de même un rapide aperçu :
Un fichier au format PE contient (entre autre) diverses sections. Une section est en quelque sorte un type de donnée. Il y a en a de plusieurs types. Chacune a des attributs spécifiques (lecture/écriture pour une section de données, lecture/exécution pour du code, ...).
Un fichier exécutable typique contient une section de code, une section de donnée initialisées (constantes, chaînes de caractères), une section de données non initialisées (variables globales), une section pour les ressources (icônes, menus, dialogues, ...) et ce qui nous intéresse ici : une section d'importation.
C'est dans celle-ci que se trouve le fameux tableau contenant les adresses des fonctions. Ce tableau s'appelle en fait une table des adresses importées (Import Address Table). Et il n'y en pas qu'une seule, mais une par dll utilisé.
Il y a, en plus de cette table, deux autres types de table :
L'import lookup table, qui est tout simplement l'équivalent de notre tableau, mais en version texte, c'est à dire qui contient le nom des fonctions importées et non pas leur adresse. C'est cette table que Windows va regarder afin de remplir la table des adresses.
Pour chaque ligne de cette table, il va compléter la ligne correspondante dans la première table avec l'adresse de la fonction dont le nom est donné.
Vous l'aurez compris, ces deux tables fonctionnent par paires. Et pour chaque dll demandée, on trouvera un couple "Table des adresses - Table des noms".
La troisième table est justement celle qui permet d'associer ces deux tables. C'est pour cela qu'elle est baptisée répertoire des tables d'importation (Import Directory Table).