home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Hacker 2
/
HACKER2.mdf
/
virus
/
ps_vir4.txt
< prev
next >
Wrap
Text File
|
1995-01-03
|
23KB
|
380 lines
//==// // // /|| // //==== //==// //| //
// // // // //|| // // // // //|| //
//==// //==// //=|| // // // // // || //
// // // // || // // // // // ||//
// // // // || //==== //==== //==// // ||/
/==== // // // /==== /| /|
// // // // // //| //|
===\ // // // ===\ //|| //||
// // \\ // // // ||// ||
====/ // \\ // ====/ // ||/ ||
───────────────────────────────────────────
DISCLAIMER: This file is 100% guaranteed to
exist. The author makes no claims to the
existence or nonexistence of the reader.
───────────────────────────────────────────
This space intentionally left blank.
───────────────────────────────────────────
GREETS: Welcome home, Hellraiser! Hello to
the rest of the PHALCON/SKISM crew: Count
Zero, Demogorgon, Garbageheap, as well as
everyone else I failed to mention.
───────────────────────────────────────────
Dark Angel's Clumpy Virus Writing Guide
──── ─────── ────── ───── ─────── ─────
"It's the cheesiest" - Kraft
──────────────────────────────────────
INSTALLMENT IV: RESIDENT VIRII, PART I
──────────────────────────────────────
Now that the topic of nonresident virii has been addressed, this series now
turns to memory resident virii. This installment covers the theory behind
this type of virus, although no code will be presented. With this
knowledge in hand, you can boldly write memory resident virii confident
that you are not fucking up too badly.
──────────
INTERRUPTS
──────────
DOS kindly provides us with a powerful method of enhancing itself, namely
memory resident programs. Memory resident programs allow for the extention
and alteration of the normal functioning of DOS. To understand how memory
resident programs work, it is necessary to delve into the intricacies of
the interrupt table. The interrupt table is located from memory location
0000:0000 to 0000:0400h (or 0040:0000), just below the BIOS information
area. It consists of 256 double words, each representing a segment:offset
pair. When an interrupt call is issued via an INT instruction, two things
occur, in this order:
1) The flags are pushed onto the stack.
2) A far call is issued to the segment:offset located in the interrupt
table.
To return from an interrupt, an iret instruction is used. The iret
instruction reverses the order of the int call. It performs a retf
followed by a popf. This call/return procedure has an interesting
sideeffect when considering interrupt handlers which return values in the
flags register. Such handlers must directly manipulate the flags register
saved in the stack rather than simply directly manipulating the register.
The processor searches the interrupt table for the location to call. For
example, when an interrupt 21h is called, the processor searches the
interrupt table to find the address of the interrupt 21h handler. The
segment of this pointer is 0000h and the offset is 21h*4, or 84h. In other
words, the interrupt table is simply a consecutive chain of 256 pointers to
interrupts, ranging from interrupt 0 to interrupt 255. To find a specific
interrupt handler, load in a double word segment:offset pair from segment
0, offset (interrupt number)*4. The interrupt table is stored in standard
Intel reverse double word format, i.e. the offset is stored first, followed
by the segment.
For a program to "capture" an interrupt, that is, redirect the interrupt,
it must change the data in the interrupt table. This can be accomplished
either by direct manipulation of the table or by a call to the appropriate
DOS function. If the program manipulates the table directly, it should put
this code between a CLI/STI pair, as issuing an interrupt by the processor
while the table is half-altered could have dire consequences. Generally,
direct manipulation is the preferable alternative, since some primitive
programs such as FluShot+ trap the interrupt 21h call used to set the
interrupt and will warn the user if any "unauthorised" programs try to
change the handler.
An interrupt handler is a piece of code which is executed when an interrupt
is requested. The interrupt may either be requested by a program or may be
requested by the processor. Interrupt 21h is an example of the former,
while interrupt 8h is an example of the latter. The system BIOS supplies a
portion of the interrupt handlers, with DOS and other programs supplying
the rest. Generally, BIOS interrupts range from 0h to 1Fh, DOS interrupts
range from 20h to 2Fh, and the rest is available for use by programs.
When a program wishes to install its own code, it must consider several
factors. First of all, is it supplanting or overlaying existing code, that
is to say, is there already an interrupt handler present? Secondly, does
the program wish to preserve the functioning of the old interrupt handler?
For example, a program which "hooks" into the BIOS clock tick interrupt
would definitely wish to preserve the old interrupt handler. Ignoring the
presence of the old interrupt handler could lead to disastrous results,
especially if previously-loaded resident programs captured the interrupt.
A technique used in many interrupt handlers is called "chaining." With
chaining, both the new and the old interrupt handlers are executed. There
are two primary methods for chaining: preexecution and postexecution. With
preexecution chaining, the old interrupt handler is called before the new
one. This is accomplished via a pseudo-INT call consisting of a pushf
followed by a call far ptr. The new interrupt handler is passed control
when the old one terminates. Preexecution chaining is used when the new
interrupt handler wishes to use the results of the old interrupt handler in
deciding the appropriate action to take. Postexecution chaining is more
straightforward, simply consisting of a jmp far ptr instruction. This
method doesn't even require an iret instruction to be located in the new
interrupt handler! When the jmp is executed, the new interrupt handler has
completed its actions and control is passed to the old interrupt handler.
This method is used primarily when a program wishes to intercept the
interrupt call before DOS or BIOS gets a chance to process it.
────────────────────────────────────────
AN INTRODUCTION TO DOS MEMORY ALLOCATION
────────────────────────────────────────
Memory allocation is perhaps one of the most difficult concepts, certainly
the hardest to implement, in DOS. The problem lies in the lack of official
documentation by both Microsoft and IBM. Unfortunately, knowledge of the
DOS memory manager is crucial in writing memory-resident virii.
When a program asks DOS for more memory, the operating system carves out a
chunk of memory from the pool of unallocated memory. Although this concept
is simple enough to understand, it is necessary to delve deeper in order to
have sufficient knowledge to write effective memory-resident virii. DOS
creates memory control blocks (MCBs) to help itself keep track of these
chunks of memory. MCBs are paragraph-sized areas of memory which are each
devoted to keeping track of one particular area of allocated memory. When
a program requests memory, one paragraph for the MCB is allocated in
addition to the memory requested by the program. The MCB lies just in
front of the memory it controls. Visually, a MCB and its memory looks
like:
┌───────┬─────────────────────────────────────┐
│ MCB 1 │ Chunk o' memory controlled by MCB 1 │
└───────┴─────────────────────────────────────┘
When a second section of memory is requested, another MCB is created just
above the memory last allocated. Visually:
┌───────┬─────────┬───────┬─────────┐
│ MCB 1 │ Chunk 1 │ MCB 2 │ Chunk 2 │
└───────┴─────────┴───────┴─────────┘
In other words, the MCBs are "stacked" one on top of the other. It is
wasteful to deallocate MCB 1 before MCB 2, as holes in memory develop. The
structure for the MCB is as follows:
Offset Size Meaning
────── ─────── ───────
0 BYTE 'M' or 'Z'
1 WORD Process ID (PSP of block's owner)
3 WORD Size in paragraphs
5 3 BYTES Reserved (Unused)
8 8 BYTES DOS 4+ uses this. Yay.
If the byte at offset 0 is 'M', then the MCB is not the end of the chain.
The 'Z' denotes the end of the MCB chain. There can be more than one MCB
chain present in memory at once and this "feature" is used by virii to go
resident in high memory. The word at offset 1 is normally equal to the PSP
of the MCB's owner. If it is 0, it means that the block is free and is
available for use by programs. A value of 0008h in this field denotes DOS
as the owner of the block. The value at offset 3 does NOT include the
paragraph allocated for the MCB. It reflects the value passed to the DOS
allocation functions. All fields located after the block size are pretty
useless so you might as well ignore them.
When a COM file is loaded, all available memory is allocated to it by DOS.
When an EXE file is loaded, the amount of memory specified in the EXE
header is allocated. There is both a minimum and maximum value in the
header. Usually, the linker will set the maximum value to FFFFh
paragraphs. If the program wishes to allocate memory, it must first shrink
the main chunk of memory owned by the program to the minimum required.
Otherwise, the pathetic attempt at memory allocation will fail miserably.
Since programs normally are not supposed to manipulate MCBs directly, the
DOS memory manager calls (48h - 4Ah) all return and accept values of the
first program-usable memory paragraph, that is, the paragraph of memory
immediately after the MCB. It is important to keep this in mind when
writing MCB-manipulating code.
─────────────────────────
METHODS OF GOING RESIDENT
─────────────────────────
There are a variety of memory resident strategies. The first is the use of
the traditional DOS interrupt TSR routines, either INT 27h or INT
21h/Function 31h. These routines are undesirable when writing virii,
because they do not return control back to the program after execution.
Additionally, they show up on "memory walkers" such as PMAP and MAPMEM.
Even a doorknob can spot such a blatant viral presence.
The traditional viral alternative to using the standard DOS interrupt is,
of course, writing a new residency routine. Almost every modern virus uses
a routine to "load high," that is, to load itself into the highest possible
memory location. For example, in a 640K system, the virus would load
itself just under the 640K but above the area reserved by DOS for program
use. Although this is technically not the high memory area, it shall be
referred to as such in the remainder of this file in order to add confusion
and general chaos into this otherwise well-behaved file. Loading high can
be easily accomplished through a series of interrupt calls for reallocation
and allocation. The general method is:
1. Find the memory size
2. Shrink the program's memory to the total memory size - virus size
3. Allocate memory for the virus (this will be in the high memory area)
4. Change the program's MCB to the end of the chain (Mark it with 'Z')
5. Copy the virus to high memory
6. Save the old interrupt vectors if the virus wishes to chain vectors
7. Set the interrupt vectors to the appropriate locations in high memory
When calculating memory sizes, remember that all sizes are in paragraphs.
The MCB must also be considered, as it takes up one paragraph of memory.
The advantage of this method is that it does not, as a rule, show up on
memory walkers. However, the total system memory as shown by such programs
as CHKDSK will decrease.
A third alternative is no allocation at all. Some virii copy themselves to
the memory just under 640K, but fail to allocate the memory. This can have
disastrous consequences, as any program loaded by DOS can possibly use this
memory. If it is corrupted, unpredictable results can occur. Although no
memory loss is shown by CHKDSK, the possible chaos resulting from this
method is clearly unacceptable. Some virii use memory known to be free.
For example, the top of the interrupt table or parts of video memory all
may be used with some assurance that the memory will not be corrupted.
Once again, this technique is undesirable as it is extremely unstable.
These techniques are by no means the only methods of residency. I have
seen such bizarre methods as going resident in the DOS internal disk
buffers. Where there's memory, there's a way.
It is often desirable to know if the virus is already resident. The
simplest method of doing this is to write a checking function in the
interrupt handler code. For example, a call to interrupt 21h with the ax
register set to 7823h might return a 4323h value in ax, signifying
residency. When using this check, it is important to ensure that no
possible conflicts with either other programs or DOS itself will occur.
Another method, albeit a costly process in terms of both time and code
length, is to check each segment in memory for the code indicating the
presence of the virus. This method is, of course, undesirable, since it is
far, far simpler to code a simple check via the interrupt handler. By
using any type of check, the virus need not fear going resident twice,
which would simply be a waste of memory.
─────────────
WHY RESIDENT?
─────────────
Memory resident virii have several distinct advantages over runtime virii.
o Size
Memory resident virii are often smaller than their runtime brethern as
they do not need to include code to search for files to infect.
o Effectiveness
They are often more virulent, since even the DIR command can be
"infected." Generally, the standard technique is to infect each file
that is executed while the virus is resident.
o Speed
Runtime virii infect before a file is executed. A poorly written or
large runtime virus will cause a noticible delay before execution
easily spotted by users. Additionally, it causes inordinate disk
activity which is detrimental to the lifespan of the virus.
o Stealth
The manipulation of interrupts allows for the implementation of
stealth techniques, such as the hiding of changes in file lengths in
directory listings and on-the-fly disinfection. Thus it is harder for
the average user to detect the virus. Additionally, the crafty virus
may even hide from CRC checks, thereby obliterating yet another anti-
virus detection technique.
───────────────────────────────
STRUCTURE OF THE RESIDENT VIRUS
───────────────────────────────
With the preliminary information out of the way, the discussion can now
shift to more virus-related, certainly more interesting topics. The
structure of the memory resident virus is radically different from that of
the runtime virus. It simply consists of a short stub used to determine if
the virus is already resident. If it is not already in memory, the stuf
loads it into memory through whichever method. Finally, the stub restores
control to the host program. The rest of the code of the resident virus
consists of interrupt handlers where the bulk of the work is done.
The stub is the only portion of the virus which needs to have delta offset
calculations. The interrupt handler ideally will exist at a location which
will not require such mundane fixups. Once loaded, there should be no
further use of the delta offset, as the location of the variables is
preset. Since the resident virus code should originate at offset 0 of the
memory block, originate the source code at offset 0. Do not include a jmp
to the virus code in the original carrier file. When moving the virus to
memory, simply move starting from [bp+startvirus] and the offsets should
work out as they are in the source file. This simplifies (and shortens)
the coding of the interrupt handlers.
Several things must be considered in writing the interrupt handlers for a
virus. First, the virus must preserve the registers. If the virus uses
preexecution chaining, it must save the registers after the call to the
original handler. If the virus uses postexecution chaining, it must
restore the original registers of the interrupt call before the call to the
original handler. Second, it is more difficult, though not impossible, to
implement encryption with memory resident virii. The problem is that if
the interrupt handler is encrypted, that interrupt handler cannot be called
before the decryption function. This can be a major pain in the ass. The
cheesy way out is to simply not include encryption. I prefer the cheesy
way. The noncheesy readers out there might wish to have the memory
simultaneously hold two copies of the virus, encrypt the unused copy, and
use the encrypted copy as the write buffer. Of course, the virus would
then take twice the amount of memory it would normally require. The use of
encryption is a matter of personal choice and cheesiness. A sidebar to
preservation of interrupt handlers: As noted earlier, the flags register is
restored from the stack. It is important in preexecution chaining to save
the new flags register onto the stack where the old flags register was
stored.
Another important factor to consider when writing interrupt handlers,
especially those of BIOS interrupts, is DOS's lack of reentrance. This
means that DOS functions cannot be executed while DOS is in the midst of
processing an interrupt request. This is because DOS sets up the same
stack pointer each time it is called, and calling the second DOS interrupt
will cause the processing of one to overwrite the stack of the other,
causing unpredictable, but often terminal, results. This applies
regardless of which DOS interrupts are called, but it is especially true
for interrupt 21h, since it is often tempting to use it from within an
interrupt handler. Unless it is certain that DOS is not processing a
previous request, do NOT use a DOS function in the interrupt handler. It
is possible to use the "lower" interrupt 21h functions without fear of
corrupting the stack, but they are basically the useless ones, performing
functions easily handled by BIOS calls or direct hardware access. This
entire discussion only applies to hooking non-DOS interrupts. With hooking
DOS interrupts comes the assurance that DOS is not executing elsewhere,
since it would then be corrupting its own stack, which would be a most
unfortunate occurence indeed.
The most common interrupt to hook is, naturally, interrupt 21h. Interrupt
21h is called by just about every DOS program. The usual strategy is for a
virus to find potential files to infect by intercepting certain DOS calls.
The primary functions to hook include the find first, find next, open, and
execute commands. By cleverly using pre and postexecution chaining, a
virus can easily find the file which was found, opened, or executed and
infect it. The trick is simply finding the appropriate method to isolate
the filename. Once that is done, the rest is essentially identical to the
runtime virus.
When calling interrupts hooked by the virus from the virus interrupt code,
make sure that the virus does not trap this particular call, lest an
infinite loop result. For example, if the execute function is trapped and
the virus wishes, for some reason, to execute a particular file using this
function, it should NOT use a simple "int 21h" to do the job. In cases
such as this where the problem is unavoidable, simply simulate the
interrupt call with a pushf/call combination.
The basic structure of the interrupt handler is quite simple. The handler
first screens the registers for either an identification call or for a
trapped function such as execute. If it is not one of the above, the
handler throws control back to the original interrupt handler. If it is an
identification request, the handler simply sets the appropriate registers
and returns to the calling program. Otherwise, the virus must decide if
the request calls for pre or postexecution chaining. Regardless of which
it uses, the virus must find the filename and use that information to
infect. The filename may be found either through the use of registers as
pointers or by searching thorugh certain data structures, such as FCBs.
The infection routine is the same as that of nonresident virii, with the
exception of the guidelines outlined in the previous few paragraphs.
──────────────
WHAT'S TO COME
──────────────
I apologise for the somewhat cryptic sentences used in the guide, but I'm a
programmer, not a writer. My only suggestion is to read everything over
until it makes sense. I decided to pack this issue of the guide with
theory rather than code. In the next installment, I will present all the
code necessary to write a memory-resident virus, along with some techniques
which may be used. However, all the information needed to write a resident
virii has been included in this installment; it is merely a matter of
implementation. Have buckets o' fun!
Downloaded From P-80 International Information Systems 304-744-2253