Results 1 to 4 of 4

Thread: Beware of int 2c instruction

  1. #1
    Red wine, not vodka! ZaiRoN's Avatar
    Join Date
    Oct 2001
    Blog Entries

    Beware of int 2c instruction

    In the last days I played a little with Win32.Virtob virus, thanks to Kayaker for passing it out to me. It’s a nice virus with some interesting tricks. You can read a detailed analysis made by Kayaker from his recent blog entries.
    In this post I’m not going to talk about the malware itself, I simply focus my attention on an single instruction: int 2c. The instruction is located in the beginning of the virus and I spent some time on it trying to answer Kayaker’s question: is “int 2c” used as an antidebug trick? I don’t have an answer for the question, but after some tries I came up with something that can be interesting.

    execution changes the content of some registers, but one of them (in particular edx) is always changed in the same way. edx contains the address of the instruction that follows the int 2c instruction.
    100: mov edx, 80   <– edx = 0×80
    105: int 2c   <– it changes edx value
    107: xor eax, eax   <– here: edx = 0×107
    Pretty weird. To know why you have to look at ntoskrnl code (starting from KiSetLowWaitHighThread), it’s pretty easy to find out why. Here’s the last part of the int2c code:
    804d4f95 pop     edx   <-- edx is changed here
    804d4f96 add     esp,8
    804d4f99 pop     ecx
    804d4f9a sti
    804d4f9b sysexit
    Sysexit is the key of this snippet. The function is used when there’s a transition from ring0 to ring3 code. ring3.eip is updated with ring0.edx value. That’s why edx contains the magic value, if you want to go back into ring3 world you have to put the return address into edx.
    Is this fact important? Not really, per se. There’s a interesting use of this behaviour btw, infact it could be used inside an obfuscation engine or as an anti Ollydbg trick.

    Look at this simple and stupid sample I wrote (attached at the end of the post):
    00401000 LEA EDX,DWORD PTR DS:[403000]      ; 403000 -> "easy sample"
    00401006 PUSH EDX
    00401007 INT 2C
    00401009 PUSH 0                             ; Style = MB_OK|MB_APPLMODAL
    0040100B INT 2C
    0040100D SUB BYTE PTR DS:[EDX+6],9          ; 403006 = ‘X’
    00401011 LEA EAX,DWORD PTR DS:[403015]      ; 403015 -> “I’m the caption”
    00401017 PUSH EAX                           ; Title_1: “I’m the caption”
    00401018 INT 2C
    0040101A XOR BYTE PTR DS:[EDX+6],1D         ; 403006 = ‘E’
    0040101E LEA EAX,DWORD PTR DS:[403038]      ; 403038 -> “I’m the text”
    00401024 PUSH EAX                           ; Text_1: “I’m the text”
    00401025 INT 2C
    00401027 PUSH 0                             ; hOwner = NULL
    00401029 INT 2C
    0040102B CALL <JMP.&user32.MessageBoxA>     ; MessageBoxA, display the 1° message box
    00401030 POP EDX                            ; edx -> “easy sEmple”
    00401031 PUSH 0                             ; Style = MB_OK|MB_APPLMODAL
    00401033 PUSH msgbox.0040300C               ; Title_2: “Int2c…”
    00401038 PUSH EDX                           ; Text_2: “easy sEmple” (”sEmple” and not “sample”)
    00401039 PUSH 0                             ; hOwner = NULL
    0040103B CALL <JMP.&user32.MessageBoxA>     ; MessageBoxA, display the 2° message box
    00401040 PUSH 0                             ; ExitCode = 0
    00401042 CALL <JMP.&kernel32.ExitProcess>   ; ExitProcess
    This is the static analysis of the snippet above, it’s not right! Try running the attached exe (I can assure you it’s not dangerous) and you’ll see the real behaviour of this piece of code. It displays two message boxes with the real messages, the first one with:
    caption: “Int2c…”
    text: “Obfusction sample”
    and the second with:
    caption: “Int 2c…”
    text: “easy sample” (it’s not “sEmple”)

    Why? Well, it’s pretty simple:
    0040100B  CD 2C          INT 2C                          ; int2c changes edx value
    0040100D  806A 06 09     SUB BYTE PTR DS:[EDX+6],9       ; edx = 40100D, it changes 403015 into 40300C
    00401011  8D05 15304000  LEA EAX,DWORD PTR DS:[403015]   ; eax = 40300C, 40300C -> “Int2c…”
    Execution of int 2c changes edx value, and if you don’t know anything about it you’ll have a wrong static analysis. It’s a simple code obfuscation, but you have to take care of it for sure.

    Now, try loading the attached exe file into Ollydbg. These are the results I got from my tests based on an XP sp1 and sp2 machines.

    Ollydbg and XP sp1
    The file runs fine when you launch it with F9 key, it shows the real messages.
    The problems arise when you step through the code with “step into”(F7) or “step over”(F8) modes. No matter if you are stepping with F7 or F8, executing an int 2c instruction the debugger won’t break unless you have inserted a bpx onto one of the next instructions. A perfect trick for those who don’t know how to deal with it, you step the instruction and the malware performs all his nasty operations…

    Ollydbg and XP sp2
    The file runs fine when you launch it with F9 key, it shows the real messages.
    F7 and F8 modes have the same behaviour, Ollydbg stops at the second instruction after “int 2c“; the problem is that edx value is not updated with the correct address value. Here’s what happen:
    00401006 PUSH EDX                    ; edx = 403000
    00401007 INT 2C                      ; F7 or F8 here and Ollydbg
    00401009 PUSH 0                      ; will break at 40100B
    0040100B INT 2C                      ; F7 or F8 here and Ollydbg will
    0040100D SUB BYTE PTR DS:[EDX+6],9   ; break here due to the exception:
    ; “Access violation when writing to [00000005]”

    The exception occours because (at 40100D) edx value is 0xFFFFFFFF. edx value was changed by “int 2c” at 40100B.
    The only way to obtain a correct edx value is to use F9 setting a bpx at 40100D.

    I tried the same exe with Ida debugger on a XP sp2 machine. You can step using F8 without problem, but not with F7 because you’ll get the same access violation.

    I think it’s definitely something to take care of, what do you think?

    (File available here:

  2. #2
    Interesting, as always Zai. Thanks for your contribution.


  3. #3
    Take a look at wrk-v1.2\base\ntos\ke\i386\trap.asm from Windows Research Kernel for full src of exception dispatcher/handler

    IDTEntry        _KiGetTickCount,  D_INT332          ;2A: KiGetTickCount service
    IDTEntry        _KiCallbackReturn,  D_INT332        ;2B: KiCallbackReturn
    IDTEntry        _KiRaiseAssertion,  D_INT332        ;2C: KiRaiseAssertion service
    IDTEntry        _KiDebugService,  D_INT332          ;2D: debugger calls
    IDTEntry        _KiSystemService, D_INT332          ;2E: system service calls
    IDTEntry        _KiTrap0F, D_INT032                 ;2F: Reserved for APIC
            page ,132
            subttl "Raise Assertion"
    ; Routine Description:
    ;    This routine is entered as the result of the execution of an int 2c
    ;    instruction. Its function is to raise an assertion.
    ; Arguments:
    ;     None.
            ENTER_DR_ASSIST kira_a, kira_t, NoAbiosAssist
            align dword
            public  _KiRaiseAssertion
    _KiRaiseAssertion proc
            push    0                           ; push dummy error code
            ENTER_TRAP kira_a, kira_t           ;
            sub     dword ptr [ebp]+TsEip, 2    ; convert trap to a fault
            mov     ebx, [ebp]+TsEip            ; set exception address
            mov     eax, STATUS_ASSERTION_FAILURE ; set exception code
            jmp     CommonDispatchException0Args ; finish in common code
    _KiRaiseAssertion endp
    ...and more.

    int 2b is interesting.
    ; NtCallbackReturn (
    ;    IN PVOID OutputBuffer OPTIONAL,
    ;    IN ULONG OutputLength,
    ;    IN NTSTATUS Status
    ;    )
    ; Routine Description:
    ;    This function returns from a user mode callout to the kernel
    ;    mode caller of the user mode callback function.
    "Backdoor" for r3->r0 jump. Return address is on the kernel stack, but maybe it's possible to do something fun with this proc.
    Last edited by omega_red; December 24th, 2007 at 08:45.
    Vulnerant omnes, ultima necat.

  4. #4
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Blog Entries
    That Int2C definition is only valid for Server2003 and Vista.
    For XP the interrupt is defined as ntoskrnl!KiSetLowWaitHighThread. See the end of my post here:

    Checking with Livekd on XP, the Int2B KiCallbackReturn is the same though.

Similar Threads

  1. [.NET] Nopping out an instruction
    By theblackbird in forum The Newbie Forum
    Replies: 4
    Last Post: September 30th, 2010, 04:16
  2. Replies: 0
    Last Post: April 23rd, 2008, 10:01
  3. How to edit instruction in IDA pro
    By viewer in forum Tools of Our Trade (TOT) Messageboard
    Replies: 3
    Last Post: December 31st, 2004, 18:51
  4. <b>Question about ASM instruction?</b>
    By xOptiMus in forum Malware Analysis and Unpacking Forum
    Replies: 1
    Last Post: November 17th, 2000, 13:10
  5. How to know what a ASM instruction is in HEX
    By -[Z]- in forum Advanced Reversing and Programming
    Replies: 3
    Last Post: November 16th, 2000, 16:40


Posting Permissions

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