Reverse engineering

The Sysenter Instruction and 0x2e Interrupt

Dejan Lukan
March 29, 2013 by
Dejan Lukan

In this article, we'll present a couple of examples where we'll be using the 0x2e int instruction to interrupt the kernel and call some interrupt service routine. We'll also be using the sysenter instruction to do the same. The basic idea is presenting both methods of transferring the control from user mode to kernel mode by showing an easy to use example.

Let's first present the address where the IDT table is located in memory. We can do that by printing the value stored in the IDTR register. On the picture below we used the "r idtr" to get the address of the IDTR table, which is 0x8003f400. Then we dumped the memory at that address, which printed the interrupt descriptors.

Become a certified reverse engineer!

Become a certified reverse engineer!

Get live, hands-on malware analysis training from anywhere, and become a Certified Reverse Engineering Analyst.

Rather than dumping memory with the dd command, we can use the !idt command to display the whole IDT table in a more transparent way:

When dumping the memory with ddcommand, two columns actually correspond to the single IDT entry on the image above (this is because each IDT entry is 8 bytes long). If we look at both of the pictures carefully, we can see that the same IDT entries have been presented. But we'll be using the 0x2e, so it's best to present it.

This means that whenever the 0x2e software exception is triggered, the KiSystemService function in the kernel will be called. So whenever a software interrupt occurs, the KiSystemService function is called, which verifies that the right service number has been passed to it. The KiSystemService is the gateway between the user and kernel space.

The 0x2e Interrupt

At first, I had a thought that I should call some ntdll library function, which would automatically interrupt the kernel in some way. But while experimenting with this feature, the first thing that was bothering me was working my way through the system call function layers in order to get to the actual int instruction. This required several steps and jumps to get from the place where I called the function to the actual point of interest, the int instruction. And then again, the sysenter instruction was called, not the int instruction, which was an additional problem. It was at that time that I decided that I would just code my own assembly instructions that would initiate certain system calls.

Right after deciding that, I had to find the system call numbers to put in the eax register when performing a system call. You can find all the system call numbers for Windows NT/2000/XP/2003/Vista on the link here: http://www.pediy.com/document/Windows_System_Call_Table/Windows_System_Call_Table.htm.

Basically, the table is giving us the information of what system call number we have to use on particular system to call the needed system call; the names of the system calls that will actually be called are given in the first column. If we click on system call, the line will expand and the function prototype will be shown, which is very useful when we need the prototype as soon as possible and have no time to loose. On the picture below, we can see that we're looking at the NtClose system call that takes one parameter; we didn't present the system call numbers for every available operating system because the picture would be too large for presentation, but let's just look at the last three columns which present the system call number of the Windows XP SP0, SP1 and SP2 (different service packs). In all the three cases, the system call number is 0x0019, which means that the system call number didn't change between service packs (this usually doesn't happen, but just keep in mind that it could).

Okay: what does all of this mean? It means that if we store the number 0x19 in the register eax before calling the int 0x2e instruction, we'll essentially be calling the NtClose function in kernel.

Let's take a look at a simple example that we'll be using to present the system call being called via the int 0x2e interrupt. The source code of a simple C++ program can be seen below:

[cpp]

#include "stdafx.h"

#include<stdio.h>

#include <windows.h>

#include <Winternl.h>

int _tmain(intargc, _TCHAR* argv[]) {

__asm {

int 3

moveax, 19h

int 2eh

};

getchar();

return 0;

}

[/cpp]

Note that the program doesn't actually do anything and it doesn't work; we've only used the presented code to make it simple to view what the program will do once the "int 0x2e" is being called. We're not particularly interested in what the program does.

The first thing that we need to do is to compile and run the program, after which the Windbg will catch the exception as follows:

The execution of the program is stopped at the "int 3" instruction. If we disassemble the instructions at that address, we can see the following:

If we step-into the instructions now, the execution won't go into the "int 2e" instruction, because we're not dealing with a call instruction. The execution will go right to the "movesi, esp" instruction without us being able to see the instructions in between. To be able to see those instructions as well, we need to set the right breakpoints, but for this we need to have a deep understanding of Windows internals. We need to be aware of the fact that whenever the "int 2e" instruction is called, the KiSystemService function will be invoked. We can see that if we execute the "idt -a" command in Windbg:

[plain]

kd>idt -a

bb88390f0000002e: 8053d541 nt!KiSystemService

[/plain]

The KiSystemService is located at the address 0x8053d541, so we can set the breakpoint there. On the picture below, we've set the breakpoint with the bp instruction and listed all currently set breakpoints with the bl instruction. Notice that only one breakpoint is set and that is exactly on the KiSystemService function?

Then, we can use the twindbg command to step through the instructions. At first, we'll be executing the "moveax, 19h" instruction, which requests the KiSystemService function to call NtClose function as we've already determined before. Then the "int 2e" instruction is called, but this time we're not simply executing it with one go, because we've set the appropriate breakpoints. Take a look at the picture below, where we've landed at the first instruction in the KiSystemService function.

Also notice that we're in kernel mode, since we're executing the instruction at address higher than 0x80000000? Let's present the instructions that will be executed in the KiSystemService function:

If you look carefully, you can see that this is just an initialization code and the value in the eax register isn't used anywhere. At the end of the code, there's a jump to the KiFastCallEntry function (at offset 0x8d). If we also disassemble the instructions at that address we can see the following:

On the picture above, we can see the value in register eax being used, which means that the system call number is being inspected in some way.

We've seen that when we invoke the 0x2e interrupt, the KiSystemService function is being called. It uses the value in the register eax, which is passed from user mode to kernel mode to determine which function to call in the kernel mode. By using the 0x2e interrupt, we can successfully invoke the system calls in kernel mode, which is executed with kernel privileges.

The sysenter Instruction

Previously, we had to put the system call number into the eax register and invoke the "int 0x2e" interrupt to call specific function in kernel. But with sysenter instruction, we can also invoke the same function in kernel, just faster. Let's take a look at how it works.

Let's present the example that we'll be using to present the sysenter instruction internals. The program is written in C++ and can be seen below:

[cpp]

#include "stdafx.h"

#include <stdio.h>

#include <windows.h>

#include <Winternl.h>

int _tmain(intargc, _TCHAR* argv[]) {

HANDLE file;

LPCWSTR filename = L"C:/temp.txt";

/* create a file */

file = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);

if(file == INVALID_HANDLE_VALUE) {

printf("File creation failed.n");

}

else {

printf("File created successfully.n");

}

/* write some data to a file */

charDataBuffer[] = "Some text being written to a file.";

DWORD dwBytesToWrite = (DWORD)strlen(DataBuffer);

DWORD dwBytesWritten = 0;

BOOL bErrorFlag = FALSE;

bErrorFlag = WriteFile(file, DataBuffer, dwBytesToWrite, &amp;dwBytesWritten, NULL);

if(bErrorFlag == FALSE) {

printf("Writing to file failed.n");

}

else {

printf("Writing to file succeeded.n");

}

/* close the file */

CloseHandle(file);

/* wait */

getchar();

return 0;

}

[/cpp]

The program is very simple: all it does is creates a file C:temp.txt if it doesn't already currently exist and writes some data to it. Then it closes the file and quits. If we compile and run the program, the following will be displayed in the window console:

There is also a new file C:temp.txt created with the following contents:

We can see that the program above does exactly what we want it to do. Now let's add the "__asm{ int 3 };" code block at the beginning of the program, which will cause the interrupt to be generated and the program execution will pause. Actually, it's better if we add this instruction right before and after the CloseHandle function call, so we can start inspecting that function immediately.

When we run the program, the Windbg will catch it as presented on the picture below. We've also disassembled the code at the breakpoint address 0x004114d6. Notice the two "int 3" instructions, which embed the CloseHandle function call?

Let's also present the same instructions loaded into Ida:

When we enter the CloseHandle function, we'll be executing the following code:

At the end of the instructions above, the following instructions immediately follow::

Eventually the execution will lead to the NtClose, where the file also needs to be closed when we're done working with it. This code is presented on the picture below:

And at the call instruction on the picture above, we'll jump to the KiFastSystemCall function, as presented below:

Since the sysenter instruction is located at the address 0x7C90E512, we can set a breakpoint at that address as follows:

After that, we can use the g command to run the program until the breakpoint is hit. At that point, we can use the rdmsr command to display the values of the machine specific registers (MSR). At this point, we must be aware of the fact that 174, 175 and 176 MSR registers are used to transfer control to kernel mode. The 174 is the IA32_SYSENTER_CS MSR register, the 175 is the IA32_SYSENTER_ESP MSR register and the 176 is the IA32_SYSENTER_EIP MSR register. Let's dump the value of those registers, which can be seen on the picture below:

The 176 MSR register specifies the linear address where we'll jump to when executing the sysenter instruction. We can see that we'll jump to the address 8053d600, which is located in kernel mode. If we disassemble that address, we can determine that we're actually talking about the KiFastCallEntry function, as can be seen below:

If we set a breakpoint on that location and execute the t command, we can see that the breakpoint is immediately hit. Here's the first difference between the "int 0x2e" and the sysenter instruction: when using the "int 0x2e" interrupt, we jumped to the offset 0x8d of the KiFastSystemCall function, while with sysenter we're jumping to the beginning of that function.

Conclusion

We can see that both 0x2e interrupt as well as the sysenter instruction led to the same point in the kernel mode, which means that they are really used for the same thing, but the actual procedure of doing it is a little different. In the next article we'll take a look at what happens in greater details.

 

Become a certified reverse engineer!

Become a certified reverse engineer!

Get live, hands-on malware analysis training from anywhere, and become a Certified Reverse Engineering Analyst.

References:

[1] SYSENTER, http://wiki.osdev.org/SYSENTER.

[2] x86 Instruction Set Reference, http://x86.renejeschke.de/html/file_module_x86_id_313.html.

[3] System Call Optimization with the SYSENTER Instruction, http://www.codeguru.com/cpp/misc/misc/system/article.php/c8223/System-Call-Optimization-with-the-SYSENTER-Instruction.htm.

Dejan Lukan
Dejan Lukan

Dejan Lukan is a security researcher for InfoSec Institute and penetration tester from Slovenia. He is very interested in finding new bugs in real world software products with source code analysis, fuzzing and reverse engineering. He also has a great passion for developing his own simple scripts for security related problems and learning about new hacking techniques. He knows a great deal about programming languages, as he can write in couple of dozen of them. His passion is also Antivirus bypassing techniques, malware research and operating systems, mainly Linux, Windows and BSD. He also has his own blog available here: http://www.proteansec.com/.