ABOUT UNDETECTABLE VIRUSES -------------------------- Lets consider time of detecting a virus in some executable file. First, time depends on a set of possible variants of virus body. The more possible variants there are in the virus body, the more time needed to iterate them while checking files. This way is the simplest one, and it was choosen by viruses, when they morphed into crypt-, polymorphic-, permutating- and metamorphic- ones. For sure, while checking file, all these variants are iterated very seldom (when their amount is small enough), and in most cases there performed special processing of code and then comparing with some mask, or more complex algorithmic detection -- in this case we consider complexity of such checking, instead number of possible body variants. In case of poly-encrypted virus, this complexity should be increased by a complexity of polymorphic decryptor emulation. In case of simple poly decryptor, it can be the object of detection, instead of virus itself. Well, at second, the time of detecting a virus depends on a set of possible virus locations (or entrypoints) in file. For example, if virus hooks entrypoint, it will be enough to perform a checking for this virus only at that address. But, if virus inserts its body into random location in file, then time of detection for this virus is multiplied by size of this file. (this is true for poly decryptors in which next instructions works depending on result of previous ones) Example of this way is EPO (UEP) technology. Let C - complexity of checking file for some virus Ci - complexity of checking virus in file at some specified address, including poly-decryptor emulation and checking for all possible variants of virus body. I - number of possible addresses in file, where execution of virus body (or part of this body) can be started at. Then C = Ci * I This trivial formula shows as an algorithm of checking file for being infected by some complex virus: for each possible address I, where virus can be located, perform checking for presence of this virus. (Ci). Algorithm looks as follows: for(i = 0; i < I; i++) I { * init_emul(); \ emul(codeblock[i]); Ci check_virus(); / } This tells us that the bigger files are better targets to be infected, that virus should be smaller but more complex, and that polymorphic decryptor should work longer, and that number of possible virus locations in file should be maximal. Well, lets consider now such case, when polymorphic decryptors cosists of N parts, which are located in N different places of file, and that this decryptor works only when all these N blocks are executed consecutively. In this case, if antivirus doesnt emulate all the program, but only combinations of its blocks, possibly containing virus decryptor, summary complexity of such checking will be equal to I! C = Ci * ------ (I-N)! For example, if there are 4 suspicious blocks in program, but there should be only 2 consecutively executed virus blocks, there will be checked 12 variants: {0,1} {0,2} {0,3} {1,0} {1,2} {1,3} {2,0} {2,1} {2,3} {3,0} {3,1} {3,2} As a result, virus checking will look as following: for(i1 = 0; i1 < I; i1++) \ for(i2 = 0; i2 < I; i2++) \ if (i2 != i1) \ for(i3 = 0; i3 < I; i3++) \ if (i3 != i1) \ I! if (i3 != i2) ------ ... (I-N)! for(iN = 0; iN < I; iN++) / if (iN != i1) / if (iN != i2) / if (iN != i3) / ... / { * init_emul(); \ emul(codeblock[i1]); \ ... Ci emul(codeblock[iN]); / check_virus(); / } And now there appears a question: how could we know sequence of program instruction execution to insert virus blocks into such places, that they will be executed in some specified order? This can be achieved by means of tracing the program. And tracing can be performed using win32 debug api. Selected program should be traced for some small (but random) period of time (seconds). While tracing process, the list of executed instructions should be builded for each (or only main) thread. Here is interesting thing: we're tracing not program, but process, i.e. EXE file and some DLL files. As such, there appears possibility to insert these N decryptor blocks into different files, but with known execution order. Fore sure, on small local parts of code all that stuff can be done without tracing, but by means of statical analysis of code instructions. But, for big multithreaded programs, consisting of lots of differents files, and also in case of big distances between viral blocks, statical analysis (i.e. disassembling) is not enough, and tracing (or emulation) is required. So, we should 1. Select some program. 2. Perform static analysis. (parsing on instructions, see Mistfall and ZMist) 3. Perform dynamic analysis (tracing, see Tracer32), but taking into account results of previous disassembling. For example, breakpoint (INT3) should be inserted into beginning of each subroutine, to avoid losing control on callback functions, called from non-tracing DLLs. 4. Perform static analysis once again, with more quality, because taking into account instruction list, obtained while previous tracing. 5. Retry from step 3 if needed. 6. Combine all obtained data. 7. Select some random places of the program, which are executed in some known order, to insert decryptor parts into. 8. Integrate decryptor parts and encrypted body into code section. 9. Build new program. As a result, by means of synthesing two known technologies, there appears possibility to make virus detection much more harder, and, as such, to hammer another one nail into kaspersky's ass. Now some details... Viral blocks should be of minimal length (some instructions), but N should be big enough. The sequence of executing these N blocks should be complex, including big amount of iterations. This can be achieved by finding cycles in program while tracing it, and then integrating viral blocks into these cycles. Well, this all is real, you can believe or not. * * *