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

Soyez le premier à donner votre avis sur cette source.

Vue 12 144 fois - Téléchargée 1 416 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
Messages postés
11
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
14 avril 2009

Merci, c'est une très bonne source !
Messages postés
3
Date d'inscription
vendredi 12 septembre 2003
Statut
Membre
Dernière intervention
15 juillet 2008

Certains exe, exporte des fonctions. Je suppose pour inter-opérabilité avec d'autres softs. C'est justement ce que je cherche a faire... ;)

Je teste cette piste, merci :)
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
Cela marcherait si le code de l'exe passait par sa propre table d'export pour appeler cette fonction. Mais il serait stupide de passer par celle-ci pour récupérer une adresse que l'on connait déjà. Donc il y a peu de chance que des compilos le fasse.

Par contre...

Ton exe exporte la fonction, donc tu peux facilement récupérer son adresse dans le processus distant (Pour cela, on peut exploiter la technique d'injection de ce source). Une fois que l'on a l'adresse de cette fonction, rien n'empèche de modifier le début du code de cette fonction pour appeler une fonction à nous, injectée elle aussi.

1. Injection d'une dll dans le processus distant, via la technique de ce source ou autre.

2. Dans le code de la dll, récupérer un handle du .exe avec GetModuleHandle.

3. Récupérer l'adresse de la fonction exportée avec GetProcAdresse, en lui passant le handle du .exe.

4. Modifier le début de la fonction exporté (Ecriture à l'adresse ci-dessus). La fonction commence très certainement par un :
push ebp
mov ebp, esp

(Les compilos Microsoft mettent généralement un mov edi,edi en début de fonction, idéal pour un hook)

Remplacer ce début par un jmp vers une fonction de la dll que l'on a injecté.

5. La fonction de la dll qui va récupérer les appel doit faire le début qui a été remplacé, et doit faire un saut vers la fonction originale à la fin.

Bien qu'un peu technique, c'est très faisable.
Par contre, je ne vois pas dans quel cas ce serait utile...
Messages postés
3
Date d'inscription
vendredi 12 septembre 2003
Statut
Membre
Dernière intervention
15 juillet 2008

Superbe source... Et merci pour la blague sur le "string" ;) Ca détend....

juste une question:
Comment faire pour "Hooker" une fonction exporté dans l'exe et non importé à partir de la DLL ?

ansi dans mon code j'ai :
-----------------
implementation

{$R *.dfm}
function SommeINCLUDE(A, B: Integer): Integer;
begin
result:=A+B;
end;

exports SommeINCLUDE;
end.
-----------------
SommeINCLUDE est donc Exporté... Et non importé certes. Mais les mécanismes sont les mêmes. Ne peut on pas ce basé sur ce code pour "Hooké" sommeINCLUDE ?
Messages postés
17
Date d'inscription
vendredi 25 avril 2003
Statut
Membre
Dernière intervention
12 juin 2008

Belle source, super bien documentée et expliquée, merci :-)
Afficher les 11 commentaires

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.