Après avoir recherché partout et fatigué tout le monde via IRC, je n’ai pas trouvé de solution sympa pour résoudre automatiquement les noms de fonction dans IDA quand les appels à celles-ci ne sont pas déclarées dans l’IAT.
Bon, j’ai perdu un lecteur ou deux, Je m’explique;
Par exemple, si je veux faire un programme windows qui pond une messagebox, cet appel de fonctions sera déclarée dans l’IAT et la DLL responsable de la fonction pour ce popup sera elle aussi déclarée et chargée à l’initialisation de l’executable. Cette publicité inattendue est peu souhaité par l’écrivain de malware. Celui-ci préfère éviter de déclarer les fonctions qu’il utilise. Et c’est du travail de se cacher.
J’ai pondu un petit exemple dans le github : https://github.com/Th4nat0s/Chall_Tools/tree/master/asm/hellostealth
Ce n’est pas le but aujourd’hui d’expliquer comment retrouver l’offset d’une fonctions en parsant la table d’EAT d’une dll; Mais en deux mots; Ici je retrouve les adresse des fonctions à partir d’un hash de leur nom grâce à une petite procédure qui parse l’EAT de la dll chargée en mémoire (un grand classique).
Grosso modo dans tous les cas il faut passer par les étapes suivantes :
; Récupère la base addresse de Kernel32 invokel _getdll,HASH_KERNEL32.DLL ; Récupère l'offset de la fonctions LoadLibraryA invokel _getfunction, eax, HASH_LOADLIBRARYA mov [FN_LOADLIBRARY], EAX ; Charge user32.dll invokel [FN_LOADLIBRARY],str_user32 ; Récupère l'addresse de base de user32.dll invokel _getdll,HASH_USER32.DLL ; Récupère l'offset le la fonctions invokel _getfunction, eax, HASH_MESSAGEBOXA mov [FN_MSGBOX], EAX ; Appelle la popup discretos invokel [FN_MSGBOX], 0, txt1, txt1, 0 ; Appel de fonction "Traditionnel" pour quitter invoke _ExitProcess@4, NULL
Une fois compilé, notre appel à MSGBOX est totalement incognito dans la table d’import
$ rabin2 -i nop.exe [Imports] ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=kernel32.dll_ExitProcess 1 imports
Et dans ida, rien n’est résolu et nous on ne sait pas trop ce qu’il se passe.
public start start proc near push 1C9513FBh call sub_40205A push 0BF89D44Eh push eax call sub_4020AE mov dword_40301A, eax push offset aUser32_dll ; "user32.dll" call dword_40301A push 1015679Ah call sub_40205A push 2181CB52h push eax call sub_4020AE mov dword_40301E, eax push 0 push offset aHelloDeclared ; "Hello Declared" push offset aHelloDeclared ; "Hello Declared" push 0 call dword_40301E push 0 ; uExitCode call ExitProcess start endp
Et pour cause… Rien n’est initialisé, les appels sont cachés.
.data:00403000 aHelloDeclared db 'Hello Declared',0 ; DATA XREF: start+41o .data:00403000 ; start+46o .data:0040300F aUser32_dll db 'user32.dll',0 ; DATA XREF: start + 1Ao .data:0040301A dword_40301A dd 0 ; DATA XREF: start+15w .data:0040301A ; start+1Fr .data:0040301E dword_40301E dd 0 ; DATA XREF: start+3Aw .data:0040301E ; start+4Dr
La solution facile dans ce cas là, si l’auteur du malware est sympas et charge tous les offset de fonctions dans une procédure pas loin du démarrage, c’est de dumper le process dans olly après résolution. A partir de là les offsets sont remplis.
.data:00403000 aHelloDeclared db 'Hello Declared',0 ; DATA XREF: start+41o .data:00403000 ; start+46o .data:0040300F aUser32_dll db 'user32.dll',0 ; DATA XREF: start+1Ao .data:0040301A dword_40301A dd 7C801D7Bh ; DATA XREF: start+15w .data:0040301A ; start+1Fr .data:0040301E dword_40301E dd 7E4507EAh ; DATA XREF: start+3Aw .data:0040301E ; start+4Dr
Si on utilise olly on peut connaitre l’offset de la dite fonction et se transformer en Champollion. (View > Executable Modules > Bouton Droit > Show File in all module)
Bon là ca va il y en a que deux fonctions à trouver, mais avec un petit droppeur ou un malware, au bout de la 50eme c’est assez pénible. D’ou le script du jour.
Etape 1, sauver la liste de “All Names” dans c:\temp\export.txt.
Etape 2, Se rendre ensuite dans IDA avec Python d’actif , mettre le curseur dans une fonction et faire Shift + F7 puis sélectionner ce script magique : https://github.com/Th4nat0s/Chall_Tools/blob/master/ollylink.py
Etape 3, hurler de joie, ce qui était non renommé et inconnu en Dword_ et qui a trouvé correspondance dans une table d’export est enfin nommé. IDA fait le reste pour mettre les commentaires adaptés.
[+] Loaded 8071 DLL functions [+] IDA Got 4 Data Ref in 3 Subfunctions in Seg 402000 [+] Find 2 Api call out of 2 unknown ref
public start start proc near push 1C9513FBh call sub_40205A push 0BF89D44Eh push eax call sub_4020AE mov LoadLibraryA, eax push offset aUser32_dll ; "user32.dll" call LoadLibraryA push 1015679Ah call sub_40205A push 2181CB52h push eax call sub_4020AE mov MessageBoxA, eax push 0 push offset aHelloDeclared ; "Hello Declared" push offset aHelloDeclared ; "Hello Declared" push 0 call MessageBoxA push 0 ; uExitCode call ExitProcess start endp
.data:00403000 aHelloDeclared db 'Hello Declared',0 ; DATA XREF: start+41o .data:00403000 ; start+46o .data:0040300F aUser32_dll db 'user32.dll',0 ; DATA XREF: start+1Ao .data:0040301A ; HMODULE __stdcall LoadLibraryA(LPCSTR lpLibFileName) .data:0040301A LoadLibraryA dd 7C801D7Bh ; DATA XREF: start+1w .data:0040301A ; start+1Fr .data:0040301E ; int __stdcall MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) .data:0040301E MessageBoxA dd 7E4507EAh ; DATA XREF: start+3Aw .data:0040301E ; start+4Dr
Voila qui est mieux.
Nice, gain de temps énorme sur certain ctf avec ce mignon petit script.
Dummys après m’avoir tabassé sur mon orthographe vient de m’informer de l’existence de :
http://zairon.wordpress.com/2014/05/15/hardcoded-dll-export-address-python-approach/