Hook d'api, injection de dll, table d'import

Soyez le premier à donner votre avis sur cette source.

Vue 12 753 fois - Téléchargée 1 496 fois

Description

#### Prérequis :

Lorsque l'on lance un exécutable, le loader de Windows mappe le fichier .exe (Ou plus précisément les sections qu'il contient) en RAM. Les lieurs sont généralement configurés pour produire des .exe qui ne peuvent fonctionner correctement qu'à une adresse fixe (Adresse de base de l'image dans les options de Delphi).

Comme le .exe est le premier à être chargé, le loader peut le mettre sans problème à cette adresse.

Puis le loader regarde la table d'importation du fichier .exe pour déterminer les dlls qu'il doit charger.

Cette fois, les dlls risquent de ne pas être chargées à leurs adresses de base préférées : elles peuvent être déjà prises par le .exe ou par un une autre dll. Heureusement, les dlls contiennent une table de relocation. Cette table est traitée par le loader de Windows qui va ainsi corriger des adresses de manière à ce que la dll fonctionne à l'adresse où elle est effectivement chargée.

La table d'importation du .exe contient aussi la liste des fonctions utilisées par le programme, pour chaque dll. Le loader de Windows va déterminer ces adresses et les mettre en place à des emplacements prévus à cet effet dans le mappage du .exe. Les adresses de ces futurs adresses sont donc connues au moment du linkage.

Les appels des fonctions importées ne sont donc pas implémentées sous forme de "exécute la fonction à cette adresse", adresse qui peut théoriquement être aléatoire, mais sous la forme "exécute la fonction dont l'adresse à été mise en place par le loader à cette adresse".

En assembleur on ne fait pas :
call XXXXXXX
car on ne connaît pas XXXXXXX au linkage, mais :
call dword ptr [YYYYYYYY]
où YYYYYYYY est connues au linkage, et le loader mettra "XXXXXXXX" à cette adresse car le .exe l'aura demandé via sa table d'importation.

#### Objectif :

Le hook d'API consiste à remplacer l'adresse mise en place par le loader par une autre. Ainsi, tout appel à la fonction hookée est redirigé vers la fonction de notre choix.

Cela permet deux choses :
1 Connaître les arguments passés à la fonction, pour par exemple connaître tous les fichiers ouverts par l'exécutable.
2 Modifier le comportement d'une application, pour par exemple forcer l'application à demander l'autorisation de l'utilisateur avant que l'application ne lance un exécutable avec CreateProcess ou ShellExecute.

#### Limitations :

Seules les fonctions importées par l'exe peuvent être hookées (Par cette technique...). Donc :

1 Tout emploi de LoadLibrary/GetProcAddress n'est pas filtré. Evidemment, rien n'empêche de hooker ces fonctions.
2 Les fonctions importées par les dlls utilisées par l'exe ne sont pas filtrées.

Le deuxième problème est sans doute peu courant dans les applications en Delphi qui s'appuient généralement uniquement sur des tables d'importations bien fournies avec des fonctions très classiques de l'API Win32.

Par contre, dans le cas d'une application C utilisant une runtime sous forme de dll, CreateFile peut tout à fait être absent de la table d'import par exemple, car le développeur peut n'avoir utilisé que fopen... Il suffit donc de hooker fopen aussi, mais il faut y penser.

Exemple encore plus flagrant : le VB6, où aucune fonction classique n'est importée. Les .exe de VB6 compilés en natif n'importent que depuis msvbvm60.dll, et encore, pas grand chose.

#### Préparation :

Il faut déduire le nom des fonctions hookées de l'objectif final que l'on souhaite atteindre. La doc du SDK Win32 peut nous aider à faire notre choix.

Une fois que l'on a les noms, il faut s'assurer que ces fonctions sont bien importées par le .exe cible.

Pour cela, on peut utiliser l'utilitaire Import.exe inclus en source et en binaire (Renommer le .exec en .exe) dans le zip. C'est une appli console qui prend en argument un fichier image (.exe, .dll... Tant que le fichier est au format PE, c'est ok), et qui affiche toutes les fonctions de la table d'import.

Hooker une fonction ne nous servira pas à grand chose si on ne connaît pas son prototype, c'est à dire les arguments qu'elle attend et sa convention d'appel. On ne peut pas facilement connaître ces informations sans avoir accès à la documentation de la dll.

#### Injection de la procédure dans le processus :

On souhaite remplacer une fonction importée par notre propre fonction. On sait que l'on va remplacer une adresse par une autre dans la table d'import. Il faut donc que l'on commence par avoir l'adresse de notre fonction, et ce, dans le processus distant.

Certains seraient peut être tentés de mettre leur procédure dans le processus qui installe le hook, puis de mettre en place l'adresse de cette procédure dans le processus hooker. C'est absolument impossible : deux processus ne partagent pas de mémoire, les adresses valables dans un processus pointent sur n'importe quoi dans l'autre. L'adressage est tout à fait virtuel.

Il faut donc charger notre procédure dans le processus à hooker. Le plus logique est de faire une injection de dll.

On va donc commencer par réaliser une dll qui exporte une procédure avec le même prototype que la fonction que l'on souhaite hooker.

Un exemple est dans le répertoire InjectedDll.

Cette dll exporte ShellAboutW, la même procédure que celle exportée par la dll shell32.dll.
Cette fonction est utilisée par de nombreux programmes sous windows (calc, mspaint, notepad...) lorsque l'on demande leur "A propos de ..."

On va afficher une boîte de dialogue juste avant d'afficher la boîte standard.

La méthode d'injection de la dll utilisée est détaillée au chapitre 22 de "Programming Applications for Microsoft Windows", de Richter.

#### Remplacement de la fonction :

Le remplacement de la fonction peut par exemple se faire dans la fonction d'initialisation de la dll injectée. La table d'import est parcourue à la recherche de la fonction à remplacer et est modifiée quand elle est trouvée.

La structure de la table d'import est détaillée la spécification pe/coff de M$.

#### Application finale :

L'application finale lance une instance de la calculatrice de Windows et injecte la dll dedans.

La principale difficulté lors de la réalisation de cette appli a été de lancer l'injection suffisement tard. Par exemple, avec CREATE_SUSPENDED, le loader de Windows ne charge même pas kernel32, ce qui conduit à l'échec de l'injection.

L'emploi d'un Sleep, bien que possible, est vraiment pas propre (Tiens y a un jeu de mot là. Vivi je sors.).

La meilleur solution que j'ai trouvé a d'utiliser DEBUG_PROCESS. Bien pratique : on est informé des chargement de modules.

J'ai cependant dû rajouter un thread à cause d'un deadlock : le processus débogué attendais que le débogueur acquitte la création du thread, tandis que le débogueur attendais la fin du thread d'injection...

Conclusion :


pecoff :
http://www.google.fr/...

Richter :
http://brunews.free.fr/brunews/download/JR4.zip

Codes Sources

A voir également

Ajouter un commentaire Commentaires
Caribensila Messages postés 2527 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 16 octobre 2019 18
17 déc. 2007 à 11:18
Salut,

Eh bein!..
Ca nous change du SIMOBROWSER.
Tu pourrais peut-être pousser jusqu'au niveau «expert», non? ;)
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 37
17 déc. 2007 à 17:21
Houla! clair que c'est pas jo l'rigolo l'brunews :)

en tout cas ça m'a l'air bien costaud, bien programmé, même si j'ai pas tout tester.

le seul truc qui m'ennerve dans ce code c'est ça :

procedure ConsolePause();

sans arguments .... pas de parenthese! on est pas en C/C++ et t'es plus un debutant maintenant :)

par contre pour le niveau expert ... mmm ... je me demande si c'est vraiment utile. il est vrai que faut deja avoir un bon niveau pour bien comprendre la source.
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
17 déc. 2007 à 17:45
f0xi -> Ca m'est venu très récemment le coup des parenthèses. Je me suis dit : pourquoi pas ?
Bah maintenant j'ai ma réponse...

Je mettrai mes sources dans "expert" quand j'emploierai des fonctions de M$ non documentées par M$. Niark niark.
cs_jeanr Messages postés 17 Date d'inscription vendredi 25 avril 2003 Statut Membre Dernière intervention 12 juin 2008
19 déc. 2007 à 18:36
Belle source, super bien documentée et expliquée, merci :-)
strobinateur Messages postés 11 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 14 avril 2009
19 juil. 2011 à 17:01
Merci, c'est une très bonne source !

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.