Bypassing Intel SMEP on Windows 8 x64 Using Return-oriented Programming

Authors: Artem Shishkin, Ilya Smit (Positive Research)

This article presents a way to bypass Intel SMEP security feature on x64 version of Windows 8. It is performed by using return-oriented programming. A way to build a suitable ROP chain is demonstrated below.

SMEP feature doesn’t allow executing a code from a user-mode page in supervisor mode (CPL = 0). Any attempt of executing a code under these circumstances on Windows 8 ends up with a blue screen of death with a bugcheck code “ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY”. For more details on how SMEP is implemented in Windows 8 please refer to [1].

In order to disable SMEP, the 20th bit of CR4 register has to be reset. There are two steps in bypassing SMEP — firstly, we’ll need to find out the value of CR4 register, and secondly, we’ll need a way to set a new value of CR4 register. The first step is needed because we have to preserve the original value of the other CR4 bits. The point is that various bits of this register are responsible for enabling or disabling certain processor features. The OS enables those features only once during the system startup and they are not supposed to be modified in a runtime. Modifying various bits of CR4 register can lead to undefined behavior or a system crash.

The preliminary requirement of a successful attack on SMEP is making the shellcode (or a ROP chain in our case) dynamic, that is, all of the needed code offsets have to be calculated in a runtime. For this, a certain kernel-mode information disclosure is needed, e.g. when determining the base address of a module with ROP gadgets [2]. A code for parsing PE file format is also needed to ensure that the found gadgets are located in the executable section of the exploited module.

There are two approaches that can be used for getting the value of CR4 register. The first one is using a ROP chain. There is a suitable function KiSaveInitialProcessorControlState() present in the “ntoskrnl” module. The body of this function is provided below.

mov     rax, cr0
mov     [rcx], rax
mov     rax, cr2
mov     [rcx+8], rax
mov     rax, cr3
mov     [rcx+10h], rax
mov     rax, cr4
mov     [rcx+18h], rax
mov     rax, cr8
mov     [rcx+0A0h], rax
sgdt    fword ptr [rcx+56h]
sidt    fword ptr [rcx+66h]
str     word ptr [rcx+70h]
sldt    word ptr [rcx+72h]
stmxcsr dword ptr [rcx+74h]

Listing 1. KiSaveInitialProcessorControlState() function

As we can see, this function can be successfully used for retrieving various interesting information about the processor control state. It is also not guarded with stack cookies and uses volatile registers RAX and RCX.

That’s grand!

We can fill in the values of RAX and RCX registers with another ROP gadgets just like at the end of the  HvlEndSystemInterrupt() function shown in listing 2.


pop     rdx
pop     rax
pop     rcx

 Listing 2. HvlEndSystemInterrupt() function ROP gadget

The problem of this method is that it depends mostly on the situation. There are certain cases when it is difficult to restore the original control flow of the exploiting program. In our case, we also need to reset the 20th bit of CR4 value, but there is no suitable ROP gadget that can be found in the “ntoskrnl” module for that, so some user mode code has to be executed which is impossible due to the fact that SMEP is still enabled. However, you can look for a suitable ROP gadget in other loaded modules in a runtime.

The other approach is to emulate the initialization of CR4 register. Most of the bits in CR4 can be set or reset with the help of “cpuid” instruction which defines supported features for the current processor. This method is more convenient although less reliable.

The second step of bypassing SMEP is using a gadget that will set the new CR4 register value. For that KiConfigureDynamicProcessor() function can be used.


mov     cr4, rax
add     rsp, 28h

Listing 3. KiConfigureDynamicProcessor() function ROP gadget

Once SMEP is disabled, we can jump to the user-mode buffer with a shellcode. Luckily, there is no stack cookie security feature in the exploited ROP gadgets. Here goes out an obvious mitigation: adding a stack cookie security feature to the functions with ROP gadgets could significantly complicate SMEP bypassing using a ROP chain.


[1] Artem Shishkin: Intel SMEP overview and partial bypass on Windows 8.
[2] Mateusz “j00ru” Jurczyk: Windows Security Hardening Through Kernel Address Protection.

66 thoughts on “Bypassing Intel SMEP on Windows 8 x64 Using Return-oriented Programming

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.