samedi 17 novembre 2007

API Hooking - IAT patching

Bonjour à tous ,
Aujourd'hui je vais essayer de vous présenter l'api hooking via l'iat patching.
En effet notre but lors de ce petit billet sera de détourner l'appel d'apis.
J'illustrerais avec un petit exemple de hooking sur le notepad.exe , cependant vous pourriez , après avoir compris le principe , laisser cours à votre imagination afin de creer de multiples attaques.
Cette démonstration de hook sera réalisé dans l'userland (ring3).

Tout d'abord je vais vous exposer ma façon d'opérer :
- Nous allons coder un programme chargé d'injecter notre dll dans le processus cible ( voir billet sur l'injection ).
- Ensuite nous allons coder une dll qui ira modifier directement l'iat du processus cible ( voir billet sur le dump de l'iat ).

Voilà donc les grandes étapes :
- On retrouve le pid de notre processus.
- Nous ouvrons notre processus afin d'avoir un handle sur celui-ci , avec l'api OpenProcess.
- On alloue de la mémoire dans le processus cible , on y écrira le full path de notre dll.
- Nous créons un thread dans le processus sur l'adresse de LoadLibraryA ( exporté par kernel32.dll ) avec comme arguement le path de la dll donc .
- A présent notre dll est loadé par l'application cible.
- Notre dll dès son chargement va aller modifier l'adresse de la fonction à hooker par l'adresse de notre fonction dans l'iat.

L'application à chaque appel de l'api hooké appelera enfaite notre fonction , c'est pour cela qu'elle doit posséder le même prototype que la fonction hooké.
Je tiens aussi à préciser , que la modification de l'iat ne peut se faire qu'après un appel à l'api VirtualProtect.Et ce pour changer l'acces à la zone mémoire , après la modification
nous rétablissons l'accès originel de la zone mémoire.

Voici donc les illustrations :
Tout d'abord l'injecteur : InjecteurDll.
La dll pour hooker le ShellAbout de notepad.exe : HookShellAboutW.dll.
Un petit screenshot pour les plus fainéants :




Puis pour terminé j'ai voulu faire un exemple un peu plus concret mais qui reste très simple.
Imaginons le code d'une petite application permettant de lister les fichiers et dossiers dans le current directory.
Notre but serait de cacher un fichier .
Voici les codes : - HookFindNextFirstFile.dll.
- cible.exe.

Et un petit screnshot :



J'essairais plus tard de coder un petit rootkit userland prenant le contrôle de l'userland par le biais de hook justement.
Sur ce bonne fin de journée , cya.

3 commentaires:

nectrash a dit…

Nice job :)
Bonne continuation à toi.

mikiront a dit…

La recompilation du code source d'InjectDll à partir de Microsoft VC++ 2008 Express Edition + SDK Windows SDK v6.1 nécessite quelques adaptations.

Du fait de l'isolation Session 0, la fonction OpenProcess échoue avec l'erreur ERROR_ACCESS_DENIED.

Dans ce cas, il faut modifier les privilèges du processus en mode DEBUG. Cf l'article : http://www.spiritofhack.net/repo/[TRAD]Injection_de_code.pdf

Ci-dessous l'extrait de code modifié :

int DebugPrivileges()
{
HANDLE TokenHandle;
LUID lpLuid;
TOKEN_PRIVILEGES NewState;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &TokenHandle))
return 0;

if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &lpLuid))
{
CloseHandle(TokenHandle);
return 0;
}

NewState.PrivilegeCount = 1;
NewState.Privileges[0].Luid = lpLuid;
NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!AdjustTokenPrivileges(TokenHandle, FALSE, &NewState, sizeof(NewState), NULL, NULL))
{
CloseHandle(TokenHandle);
return 0;
}

CloseHandle(TokenHandle);
return 1;
}

int InjectDllDansProcessus(long pidProcAInjecter , char* fullPathDll)
{
long tailleStringDll = strlen(fullPathDll) + 1;

printf("[+] Ouverture du process.\n");

HANDLE handleProcess = OpenProcess(PROCESS_ALL_ACCESS , FALSE , pidProcAInjecter);
if(handleProcess == NULL)
{
DWORD error = GetLastError();
if( error == ERROR_ACCESS_DENIED)
{
if(DebugPrivileges() == 0)
{
return 0;
}
}
else
{
return 0;
}

handleProcess = OpenProcess(PROCESS_ALL_ACCESS , FALSE , pidProcAInjecter);
if(handleProcess == NULL)
{
return 0;
}
}

printf("[+] Reservation et écriture dans la mémoire du processus.\n");
LPVOID addrEspaceReserve = VirtualAllocEx( handleProcess , NULL , tailleStringDll , MEM_COMMIT , PAGE_EXECUTE_READWRITE);
if(addrEspaceReserve == NULL)
return 0;

int retourFonctionWrite = WriteProcessMemory( handleProcess , addrEspaceReserve , fullPathDll , tailleStringDll , 0);
if(retourFonctionWrite == 0)
return 0;

printf("[+] Creation du thread dans le processus.\n");
DWORD identificateurThread ;
LPTHREAD_START_ROUTINE addrLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(LoadLibrary("kernel32"),"LoadLibraryA");
HANDLE retourFonctionCreate = CreateRemoteThread( handleProcess , NULL , 0 , addrLoadLibrary , addrEspaceReserve , 0 , &identificateurThread );
if(retourFonctionCreate == NULL)
return 0;

WaitForSingleObject(retourFonctionCreate,INFINITE);
VirtualFreeEx( handleProcess , addrEspaceReserve , 0 , MEM_DECOMMIT);

CloseHandle(handleProcess);
CloseHandle(retourFonctionCreate);


return 1;
}

Adrien a dit…

Iop, énorme ce tuto, il m'a énormément servi pour un projet (qui m'a permis d'avoir mon année), un immense merci à toi!

Par contre, vc++ me tej quand je compare les noms de fonctions... Du coup j'ai dû passer par une comparaison d'@ mémoires:

Ca donne ça, à peu de choses près:


//fonction permettant de retourner l'adresse mémoire de la fonction à hooker
LPDWORD RetourneAddressFunctionAHook(char* FonctionAHook)
{
//@ de la fonc a modif
PROC OldFunc= GetProcAddress(GetModuleHandle("wsock32.dll"), FonctionAHook);



[...]


//tant qu'on est pas au dernier
while(*(PDWORD)imgThunk != 0)
{

// L'endoit ou est stocké l'adresse de la fonction courante
PROC* ppfn = (PROC*) &structAddrFu->u1.Function;

// Si cet endroit contient l'adresse de la fonction que l'on recherche
if (*ppfn == OldFunc)
{

// Alors on memorise cet endroit
return (structAddrFu->u1.Function);

}

//neeeeeext!
imgThunk ++;
structAddrFu++;
}


[...]


Inspiré de http://files.codes-sources.com/fichier.aspx?id=32374&f=Source_DLL\wsock32Hook.cpp

Voila, en espérant que ça pourra aider ceux qui ont le même problème que moi (l'insuffisance cérébrale :) ).

Encore un gros merci!