Breakpoints, Heavens Gate and Stack


In previous post we implemented return address spoofing with the help of H/W breakpoints by manipulating ESP, as our program resumes following the execution of the exception handler, ntdll function call stub is executed.
In this post we will be taking a more covert route of executing an nt function via Heaven gate.

Windows On Windows (WoW64)

Lets take a look at the syscall stub of NtAllocateVirtualMemory in x64 application. As shown in the image below we can clearly see the syscall instruction.

Examining same function stub in a 32 bit application running on x64 OS, surprisingly syscall is not be seen anywhere! Instead we see there is a call to ntdll.77668F00.

The function at 77668F00 simply executes a jump to WoW64Transition. Interesting eh?!

Following the previous jump we arrive at another jump as shown below. So folks this special kind of jump is famous Heavens Gate. This is a segment selector, 0x33 means transition from 32bit to 64bit environment. [0x23 performs the reverse action]. This instruction is WoW64 version of KiSystemCall in x64 environment.

On userland we cannot go beyond this point in a debugger. The control flow will eventually reach the same function in x64 version of the ntdll.dll, system will execute it and then again comes back to the 32bit environment. This is how 32 bit application are executed on 64bit OS, and this is Windows On Windows (WoW64). I am not going to dive into technical depths of this matter as there are number of articles on the Internet you can read.
The point is x86 stubs in WoW64 ntdll are executed via Heavens Gate, does it mean we can call it directly? Lets find out!

State of CPU registers at the time of Heavens Gate call

once we are inside the WoW64 ntdll, we need to make a note of all the instructions that change the state of the stack starting with “call edx” as shown below. (FYI below stub is of NtAllocateVirtualMemory as shown before).

Lets step into the “call edx” and examine the state of the stack and CPU registers.

As expected the syscall number is moved to EAX as shown below.

Lets check the stack, we can see all the arguments pushed on to the stack, following the arguments we can see the return address for the NtAllocateVirtualMemory, and finally the ESP ie top of stack has the return address for our “call edx”. Nevermind, this stack is a random one used for explanation.

The “call edx” executed two jumps and the second jump will take us to x64 land in a nutshell, which is our Heavens Gate as shown below. Therefore following the instruction “call edx” there are no instructions that change the state of the stack, only two consecutive jumps.

Examining the state of the stack at the time of Heavens Gate execution is very important because to directly call the gate we will need to recreate this exact state to prevent the program from crashing and make the function call a success.

Whatever the state we are in [in terms of stack and registers] before the “call edx” instruction in function call stub in WoW64 ntdll, because of “call edx” we will need to push a return address thus changing the previous state of the stack. This state change is very important for the Heavens Gate transition.

Direct Heavens Gate Invocation

Fetching KiFastSystemCall(Heavens Gate)Address on WoW64

There is a neat trick to fetch the address of the Heavens Gate instruction dynamically via Thread Information Block ie we can simply refer FS:0xC0 or TIB + 0xC0 to retrieve the address of the gate.

Manipulating CPU context and Stack

We need to follow below steps to successfully call the gate(with a spoofed return address):

  • I am not going to explain about return address spoofing and H/W breakpoints again, read Recap section.
  • When the breakpoint hits, we will be in WoW64 ntdll stub, thus we know exactly what state of the stack would be.
  • In our exception handler, as seen in the previous post (go read Recap!), we will manipulate the stack accordingly but this time we will push one more return address (same gadget) to mimic the “call edx” explained in the prior section.
  • Because of this extra push we need to allocate 4 extra bytes when creating a new stack.
  • After doing all stack stuff, we need to place our syscall number in EAX.
  • Finally place the Heavens Gate address in EIP. Thats it, we have made a successful call to gate. This is a prime ingredient in the WoW64 direct system call invocation.
#define STACK_ARGS_LENGTH 5 //total number of args - 1

LONG HWSpoofHandler(EXCEPTION_POINTERS* ExceptionInfo) 
    if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) 
        if (ExceptionInfo->ContextRecord->Eip == (DWORD64)&BreakOnSyscall) 
            ntSyscallStub = CustomGetProc((UINT64)hNtdll, (char*)*(DWORD*)(ExceptionInfo->ContextRecord->Esp + 4));

            // Move breakpoint to the NTAPI function;
            ExceptionInfo->ContextRecord->Dr0 = ntSyscallStub ;

        else if (ExceptionInfo->ContextRecord->Eip == (DWORD64)ntSyscallStub )
            ExceptionInfo->ContextRecord->Esp -= 0x54;
            // Copy the stack arguments from the original stack
            for (size_t i = 0; i < STACK_ARGS_LENGTH; i++)
                const size_t offset = i * STACK_ARGS_LENGTH;
                *(DWORD64*)(ExceptionInfo->ContextRecord->Esp + offset) = *(DWORD64*)(ExceptionInfo->ContextRecord->Esp + offset + 0x54);
//Pushing Original return address after last argument
            *(DWORD*)(ExceptionInfo->ContextRecord->Esp + (4 * STACK_ARGS_LENGTH + 8)) = *(DWORD*)(ExceptionInfo->ContextRecord->Esp);
 //Pushing Gadget address on top          
            *(DWORD*)(ExceptionInfo->ContextRecord->Esp) = retGadgetAddress;
//Simulating call edx by pushing gadget once more and updating the ESP
*(DWORD*)(ExceptionInfo->ContextRecord->Esp - 4) = retGadgetAddress;
            ExceptionInfo->ContextRecord->Esp = ExceptionInfo->ContextRecord->Esp - 4;
             DWORD_PTR gate = NULL;
                mov eax, dword ptr fs : [0xBB + 0x5]
                mov gate, eax
            }//assembly to retrieve Heavens Gate
            //SSN of NtAllocateVirtualMemory on my Windows
            ExceptionInfo->ContextRecord->Eax = 0x18;
            //Direct call to gate
            ExceptionInfo->ContextRecord->Eip = gate;

            ExceptionInfo->ContextRecord->Dr0 = (UINT64)&BreakOnSyscall;


Leave a Reply

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