A brief Reverse-Engineering note on Structured Exception Handling after Stack Pivoting

A few days ago Nahuel and me took a look at a piece of shellcode that wasn’t working.
After performing a stack pivoting and successfully executing a ROP chain, the shellcode was supposed to setup an Structured Exception Handler in order to catch memory access errors when scanning the address space of the process. But for some unknown reason, the Exception Handler wasn’t being called when an exception was triggered.

The Test Case

This is a minimal test case to reproduce the issue:

1) allocate some memory for a new stack
2) make ESP point to this new memory region
3) setup a Structured Exception Handler (0x00401040 in this example)
4) generate an exception

00401000      6A 04              PUSH 4                             ;PAGE_READWRITE
00401002      68 00300000        PUSH 3000                          ;MEM_COMMIT|MEM_RESERVE
00401007      68 00001000        PUSH 100000                        ;Size
0040100C      6A 00              PUSH 0                             ;Address
0040100E      E8 E5E10A00        CALL <JMP.&KERNEL32.VirtualAlloc>  ;Call VirtualAlloc
00401013      09C0               OR EAX,EAX                         ;Was VirtualAlloc successful?
00401015      75 07              JNZ SHORT test.0040101E         
00401017      6A 01              PUSH 1
00401019      E8 CADF0A00        CALL <JMP.&KERNEL32.ExitProcess>   ;if VirtualAlloc failed, then exit
0040101E      8DA0 FCFF0F00      LEA ESP,DWORD PTR DS:[EAX+FFFFC]   ;Make ESP point to the bottom (highest address) of the stack
00401024      68 40104000        PUSH test.00401040                 ;Setup our EXCEPTION_REGISTRATION_RECORD: Address of the Exception Handler
00401029      6A FF              PUSH -1                            ;Setup our EXCEPTION_REGISTRATION_RECORD: last EXCEPTION_REGISTRATION_RECORD
0040102B      64:8925 00000000   MOV DWORD PTR FS:[0],ESP           ;Set the current SEH chain
00401032      A3 00000000        MOV DWORD PTR DS:[0],EAX           ;Generate an exception

If you run this code, you’ll notice that, after the access violation is generated on purpose at address 0x00401032, the execution won’t be transferred to the Exception Handler function at address 0x00401040. But why?

Digging into the exception dispatcher mechanism

To answer this question we need to look at the KiUserExceptionDispatcher function from the ntdll library:

.text:77F06FE8 ; __stdcall KiUserExceptionDispatcher(x, x)
.text:77F06FE8                 public _KiUserExceptionDispatcher@8
.text:77F06FE8 _KiUserExceptionDispatcher@8 proc near  ; DATA XREF: .text:off_77EF61B8o
.text:77F06FE8 var_C           = dword ptr -0Ch
.text:77F06FE8 var_8           = dword ptr -8
.text:77F06FE8 var_4           = dword ptr -4
.text:77F06FE8 arg_0           = dword ptr  4
.text:77F06FE8                 cld
.text:77F06FE9                 mov     ecx, [esp+arg_0]
.text:77F06FED                 mov     ebx, [esp+0]
.text:77F06FF0                 push    ecx
.text:77F06FF1                 push    ebx
.text:77F06FF2                 call    _RtlDispatchException@8 ; RtlDispatchException(x,x)

KiUserExceptionDispatcher calls RtlDispatchException.

RtlDispatchException calls the Vectored Exception Handlers in the first place, and then it calls RtlpGetStackLimits in order to read the stack’s top address (highest memory address of the stack, stored at TEB + 4) and the stack’s bottom address (lowest memory address of the stack, stored at TEB + 8) from the TEB:

RtlDispatchException(x,x)+23                   lea     eax, [ebp+stack_top]
RtlDispatchException(x,x)+26                   push    eax             ; stack_top
RtlDispatchException(x,x)+27                   lea     eax, [ebp+stack_bottom]
RtlDispatchException(x,x)+2A                   push    eax             ; stack_bottom
RtlDispatchException(x,x)+2B                   call    _RtlpGetStackLimits@8 ; RtlpGetStackLimits(x,x)

RtlpGetStackLimits just reads those two fields from the TEB:

RtlpGetStackLimits(x,x)+6                    mov     eax, large fs:18h
RtlpGetStackLimits(x,x)+C                    mov     ecx, [ebp+stack_top]
RtlpGetStackLimits(x,x)+F                    mov     edx, [ebp+stack_bottom]
RtlpGetStackLimits(x,x)+12                   mov     [ebp+teb], eax
RtlpGetStackLimits(x,x)+15                   mov     eax, [ebp+teb]
RtlpGetStackLimits(x,x)+18                   mov     eax, [eax+4]       ;grab the stack's top address
RtlpGetStackLimits(x,x)+1B                   mov     [ecx], eax
RtlpGetStackLimits(x,x)+1D                   mov     eax, [ebp+teb]
RtlpGetStackLimits(x,x)+20                   mov     eax, [eax+8]       ;grab the stack's bottom address
RtlpGetStackLimits(x,x)+23                   mov     [edx], eax

A few instructions later, the RtlDispatchException function checks if the address of the first (and only, in this case) EXCEPTION_REGISTRATION_RECORD (located in the new stack) is located between the stack’s bottom address and the stack’s top address as defined in the thread’s TEB:

;at this point, EBX == address of the first and only EXCEPTION_REGISTRATION_RECORD in the new stack

RtlDispatchException(x,x)+80                   cmp     ebx, [ebp+stack_bottom] ; if addr of EXCEPTION_REGISTRATION_RECORD < stack_bottom, then exit
RtlDispatchException(x,x)+83                   jb      loc_77F3A885
RtlDispatchException(x,x)+89                   lea     eax, [ebx+8]
RtlDispatchException(x,x)+8C                   cmp     eax, [ebp+stack_top]    ; if addr of EXCEPTION_REGISTRATION_RECORD + 8 > stack_top, then exit
RtlDispatchException(x,x)+8F                   ja      loc_77F3A885    

So, if the address of the first EXCEPTION_REGISTRATION_RECORD is not within the bounds of the thread’s stack as defined in the thread’s TEB, then RtlDispatchException will exit without calling RtlpExecuteHandlerForException, which ultimately should call our Exception Handler.

The End

These notes apply, at least, to the 32-bit versions of Windows Seven SP1 and Windows XP SP3. I haven’t checked other OS versions.

By the way, note that a similar check is performed by EMET 4.0 to detect stack pivoting, as mentioned in this Defcon 21 presentation.

One thought on “A brief Reverse-Engineering note on Structured Exception Handling after Stack Pivoting

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s