Beware of int 2c instruction

Rating: 2 votes, 1.50 average.
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:

Submit "Beware of int 2c instruction" to Digg Submit "Beware of int 2c instruction" to Submit "Beware of int 2c instruction" to StumbleUpon Submit "Beware of int 2c instruction" to Google