In the previous article we’ve seen that whether we’re using the int 0x2e interrupt or sysenter instruction, the same method in kernel is being used. We also identified that the KiSystemService is being called in both cases. In this article, we’ll take a look at the details of how this actually happens.

Whenever we use the 0x2e interrupt or a sysenter instruction, the system service number is being used to determine which system call will be invoked. We’ve already seen that the system service number is being passed in an EAX register, which is a 32-bit register on IA-32 machines. However, it isn’t immediately clear how that value is later being used.

The first thing that comes to mind is that it’s just an index into some table, which holds the pointers to system routines that will be invoked. This is pretty close to how the value is actually being used, but we should probably mention that 32-bits are not used as an index, because we would have to have a 4GB-big table of pointers or a multiple level table, which is impractical and isn’t needed.

The system service number is comprised of the following parts:

bits 0-11: the number of the system service to call

bits 12-13: used service descriptor table

bits 14-31: not used

We can see that only the lower 12-bits are used as an index into the table, which is 4096 bytes in size. But there are also 2 bits (from 12-13) that are used to select the appropriate service descriptor table, which means that we can have at most 4 service descriptor tables.

In Windows, the SSDT (System Service Dispatch Table) is a table that points to kernel functions that are handled in ntoskrnl.exe. The ntoskrnl.exe is responsible for various tasks, like hardware virtualization, process and memory management, cache managing, process scheduling, etc. [5] In Windows systems, only two tables are used and they are named KeServiceDescriptorTable and KeServiceDescriptorTableShadow.

On the picture below, we can see the address and the first element of both tables in memory.

Both of these tables contain SST (System Service Tables) structures that have the following elements (summarized from [4]):

ServiceTable: pointer to the SSDT array of addresses that point to kernel functions

CounterTable: not used

ServiceLimit: number of elements in SSDT array

ArgumentTable: pointer to the array of arguments SSPT (System Service Parameter Table)

The picture below shows the structures of SSTs in both tables:

On the picture above, we can see the first SST of the KeServiceDescriptorTable, which is 16 bytes long. This is the SST that points to the SSDT that contains the Windows core functions. The values of the SST are as follows:

ServiceTable: 80501b8c (pointer to KiServiceTable)

CounterTable: not used

ServiceLimit: 0x11c (hex) = 286 (dec)

ArgumentTable: 80502000 (pointer to KiArgumentTable)

The KeServiceDescriptorTableShadow contains two SSTs which occupy the first 32 bytes. We can see that the first SST is the same as the one present in the KeServiceDescriptorTable table, while the second is used to point to the functions in the win32k.sys kernel driver, which takes care of Windows graphical user interface.

Let’s present the fields of this SST:

ServiceTable: bf99e900 (pointer to W32pServiceTable)

CounterTable: not used

ServiceLimit: 0x29b (hex) = 667 (dec)

ArgumentTable: bf99f610 (pointer to W32pArgumentTable)

Let’s summarize what we’ve just done. The KeServiceDescriptorTable table is referenced if the 12-13 bits in the system service number is set to 0x00, while the KeServiceDescriptorTableShadow is referenced if the 12-13 bits are set to 0x01. The other two values 0x10 and 0x11 are currently not being used. This means that the value in the EAX register, which is the system service number, can hold the following values (presenting the 16-bit values):

0000xxxx xxxxxxxx: used by KeServiceDescriptorTable, where the x’s can be 0 or 1, which further implies that the first table is used if the system service numbers are from 0x0 – 0xFFF.

0001yyyy yyyyyyyy: used by KeServiceDescriptorTableShadow, where y’s can be 0 or 1, which further implies that the second table is used if the system service numbers are from 0x1000 – 0x1FFF.

This means that the system service numbers in EAX register can only be in the range of 0x0000 – 0x1FFFF, and all other values are invalid.

To dump the Windows core functions from the KiServiceTable table, we can use the Windbg “dps KiServiceTable” command as follows (note that only the first part of the functions is presented):

Let’s also dump the first part of the functions contained in the W32pServiceTable table. This can be seen below, where we used the “dps W32pServiceTable” command to display the functions:

Did you notice that the KiServiceTable contains core Windows functions, while the W32pServiceTable table contains the graphical functions as we already mentioned? The above outputs confirm that.

Presenting the Example

Below we can see the example we’ll be using in this part of the article. The example is simply calling the ZwQuerySystemInformation function directly from the ntdll.dll library. We can’t call the function directly, which is why we must first load the ntdll.dll library and then get the address of the ZwQuerySystemInformation function. We get back the address in memory where the function is located, so we must apply the function prototype in order to be able to call the function. What the program actually does is detect whether the system debugger is currently debugging the operating system or not.

While running the program under Windbg kernel debugger, we’ll be presented with the following output from the program:

We can see that the program correctly identified that the system debugger is present. The picture below presents the disassembled instruction of the ZwQuerySystemInformation function:

We can see that the ZwQuerySystemInformation function implementation in ntdll.dll library is just a routine that calls into the kernel and doesn’t actually provide the service itself. In the code above, we’re storing the 0xAD hexadecimal number into the EAX register, the system service number we’ve been talking about in the article. Since the 0xAD number is in range of 0x000-0xFFF, we’re effectively using the KeServiceDescriptorTable table to get all the information that we need to call the kernel function.

Let’s first take a look at the address that is loaded into the EDX register:

In the code above, we’re reading the address 0x7c90e510 at the memory of the EDX register and calling it. The 0x7c90e510 address is the address of the KiFastSystemCall function as we can see in the output below:

The KiFastSystemCall is executing the sysenter instruction, which should call the appropriate system function in the kernel. We already know that when we execute the sysenter instruction, the KiFastCallEntry function is called, which is why we need to set a breakpoint on that function as follows:

After that we can run the program with the g command and the function will be hit as shown below:

Let’s disassemble the whole KiFastCallEntry function to figure out what the function does. The disassembled instructions can be seen below:

Let’s take a look at the first instruction in the KiFastCallEntry function where the value in register EAX is being used, which means that the system service number is being used to do something. Those instructions can be seen below:

At first, those instructions may seem weird, but they soon start to make a lot of sense. All of the operations can be seen on the picture below. We start with a whole system service number that is stored in the EAX register and then moved to the EDI register. This can be seen on the first part of the picture where the lower 12 bits is the actual system service number and the middle two bits determine the SSDT table to be used, while the upper 18 bits are not used.

The shr instruction moves all the bits in the EDI register to the right by 8. This is seen on the middle part of the picture where the lower 4 bits are used to represent the now-corrupted system service number, and the middle two bits are still the SSDT number. The upper 26 bits are not used. The next and instruction nulls the lower four bits, thus leaving only the middle two bits unaltered. At the end of the above code, the SSDT number is stored in the 4-5 bit of the ECX register.

Since we’re passing the system service number 0xAD in the EAX register, we should really set a conditional breakpoint in WinDbg, because otherwise we won’t be able to manage the execution the way we want. This is because the KiFastCallEntry function is called so many times by the kernel itself that it doesn’t make sense to manually check whether the EAX register contains the right system service number. We should set the conditional breakpoint on the 0x8053d68d address and check whether the value in the EAX register is 0xAD (our system service number).

After that we should start the program normally and observe what happens. The execution should take considerably more time, since WinDbg must compare the value in the EAX register every time it passed that code point, which happens a lot.

Let’s take a look at an example at a point where it hits the breakpoint, which means that the value in the EAX register should be set to 0xAD:

The first number in EAX register is 0xad (10101101), which we’re shifting to the right for 8 bits. This makes a number 0x0, which is then later AND-ed with the 0x30. The transformations result in the number 0x000000.

Since we’ve just calculated the value stored in the ECX register, it’s advisable that we continue the code observation by looking at the instructions that use the value in the ECX register. We won’t do that now since the article might get too long, but you get the picture.

Conclusion

In this article we’ve seen the internals of what happens when the sysenter instruction is called. We could go a lot deeper, but I didn’t want to make the article too long. The most important things to remember are how the system service number is calculated and how the service is called.

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/.

One response to “The Sysenter Instruction Internals”

Your email address will not be published. Required fields are marked *

Comment

Name *

Email *

Website

Save my name, email, and website in this browser for the next time I comment.

+ = 4

About InfoSec

At Infosec, we believe knowledge is the most powerful tool in the fight against cybercrime. We provide the best certification and skills development training for IT and security professionals, as well as employee security awareness training and phishing simulations. Learn more at infosecinstitute.com.

Connect with us

Join our newsletter

File download

First Name

Last Name

Work Phone Number

Work Email Address

Job Title

Why Take This Training?

How will you fund your training?

What is your training budget?

InfoSec institute respects your privacy and will never use your personal information for anything other than to notify you of your requested course pricing. We will never sell your information to third parties. You will not be spammed.

Comments

What is Skillset?

Skillset

Practice tests & assessments.

Practice for certification success with the Skillset library of over 100,000 practice test questions. We analyze your responses and can determine when you are ready to sit for the test. Along your journey to exam readiness, we will:

1. Determine which required skills your knowledge is sufficient
2. Which required skills you need to work on
3. Recommend specific skills to practice on next
4. Track your progress towards a certification exam