Home > General x86, General x86-64, Programming, Reverse Engineering > Reverse Engineering Vectored Exception Handlers: Functionality (2/3)

Reverse Engineering Vectored Exception Handlers: Functionality (2/3)

This post will continue where the first one left off and explain the operations happening on the doubly linked list of exception handlers. To understand anything in this post, you should read the first one.

Finding the Link Relationships

Given the information from part one, there are two structures at work here: _LdrpVectorHandlerList, which is a non-exported named symbol, and _LdrpVectorHandlerEntry, which is the name given to the struct allocated in _RtlpAddVectoredHandler. Each of these structures has two pointers within them that get moved around.

771E3686  cmp         dword ptr [ebp+8],0  
771E368A  je          _RtlpAddVectoredHandler@12+13DF3h (771F7414h)  
    ----> Jump resolved below
    771F7414  mov         eax,dword ptr [edi+4]  
    771F7417  mov         dword ptr [esi],edi  
    771F7419  mov         dword ptr [esi+4],eax  
    771F741C  mov         dword ptr [eax],esi  
    771F741E  mov         dword ptr [edi+4],esi  
    771F7421  jmp         _RtlpAddVectoredHandler@12+7Bh (771E369Ch)  
771E3690  mov         eax,dword ptr [edi]  
771E3692  mov         dword ptr [esi],eax  
771E3694  mov         dword ptr [esi+4],edi  
771E3697  mov         dword ptr [eax+4],esi  
771E369A  mov         dword ptr [edi],esi

The best way to find out what is happening is to dynamically trace adding exception handlers. For example, what goes on in the code when three exception handlers are added in series?vec4Each one will be added to the head of the list, so that if an exception occurs then the call order will be VectoredHandler3 -> VectoredHandler2 -> VectoredHandler1 -> Unhandled exception. For the case of a handler being inserted at the head of the list, the following instructions will be executed:

771E3690  mov         eax,dword ptr [edi]  
771E3692  mov         dword ptr [esi],eax  
771E3694  mov         dword ptr [esi+4],edi  
771E3697  mov         dword ptr [eax+4],esi  
771E369A  mov         dword ptr [edi],esi

The easiest way to see what is going on is to make a table of the runs. Here let X, Y, Z be the different memory addresses of ESI. Let Base be the base address of _LdrpVectorHandlerList, relative to EAX and EDI. I’ve also reproduced the structures and the mappings of registers to fields below.

typedef struct _LdrpVectorHandlerEntry
{
    _LdrpVectorHandlerEntry *pLink1; +0x0 [ESI]
    _LdrpVectorHandlerEntry *pLink2; +0x4 [ESI+0x4]
    DWORD dwAlwaysOne; +0x8
    PVECTORED_EXCEPTION_HANDLER pVectoredHandler; +0xC
} VECTORED_HANDLER_ENTRY, *PVECTORED_HANDLER_ENTRY;

typedef struct _LdrpVectorHandlerList
{
    SRWLOCK srwLock; +0x0
    VECTORED_HANDLER_ENTRY *pLink1; +0x4 [EDI]
    VECTORED_HANDLER_ENTRY *pLink2; +0x8
} VECTORED_HANDLER_LIST, *PVECTORED_HANDLER_LIST; +0xC

First run

[X] [X+4] [*(Base+4)] [Base]
0x77284728 0x77284728 X X

Second run

[Y] [Y+4] [*(Base+4)] [Base]
X 0x77284728 Y Y

Third run

[Z] [Z+4] [*(Base+4)] [Base]
Y 0x77284728 Z Z

Looking at the results of these three adds, you can begin to see a relationship.

[X] = [ESI] Always holds the address of the previous handler

[X+4] = [ESI+0x4] Always holds the address of the base of the table

[*(Base+4)] = [EAX+0x4] Always holds the address of the new handler

[Base] = [EDI] Always holds the address of the new handler

Given that this operation is to insert at the head of the list, it is possible to draw some conclusions. Since [ESI] always contains the address of the previous topmost handler, it can be assumed to be a pointer to the next handler in the chain. [ESI+0x4] can be assumed to be a pointer to the previous handler in the chain, which in the case of inserting a head node, is set as the base of the exception list. Now the struct definition can be completed.

typedef struct _LdrpVectorHandlerEntry
{
    _LdrpVectorHandlerEntry *pNext;
    _LdrpVectorHandlerEntry *pPrev;
     DWORD dwAlwaysOne;
     PVECTORED_EXCEPTION_HANDLER pVectoredHandler;
} VECTORED_HANDLER_ENTRY, *PVECTORED_HANDLER_ENTRY;

[EAX+0x4] is a bit more difficult to discern. EAX holds the value of the address of the second field in _LdrpVectorHandlerList. This is dereferenced and the second item in the dereferenced struct is set to the address of the new handler. What is happening here is that the pPrev field of the current topmost handler prior to inserting a new one is set to the address of the new handler, thus keeping the list chain intact. This may not seem obvious from looking at the assembly but is what is occurring when actually stepping through the instructions with a debugger. Lastly, EDI, which is the first member of _LdrpVectorHandlerList is set to hold the address of the new handler.

Now for the other case: inserting at the back of the vectored exception list. In that scenario, the following instructions will be executed:

771F7414  mov         eax,dword ptr [edi+4]  
771F7417  mov         dword ptr [esi],edi  
771F7419  mov         dword ptr [esi+4],eax  
771F741C  mov         dword ptr [eax],esi  
771F741E  mov         dword ptr [edi+4],esi  
771F7421  jmp         _RtlpAddVectoredHandler@12+7Bh (771E369Ch)

This is a slight variation on the first case. The best way to see what is going on is to step through the assembly code again. Here X, Y, and Z will map to [ESI] like last time. Here Base will be [EDI+0x4], the third member of _LdrpVectorHandlerList — unlike [EDI] in the previous segment, which was the second member. [Base+0x4] will be [EDI + 0x4].

First run

[X] [X+4] [Base] [*(Base+4)]
0x77284728 0x77284728 X X

Second run

[Y] [Y+4] [Base] [*(Base+4)]
0x77284728 X Y Y

Third run

[Z] [Z+4] [Base] [*(Base+4)]
0x77284728 Y Z Z

Again,

[X] = [ESI]  Always holds the address of the base of the table

[X+4] = [ESI+0x4] Holds the address of the previous handler

[Base] = [EAX] Holds the address to the new handler

[*(Base +4)] = [EDI+0x4] Holds the address of the new handler

Here, the mappings that were established for [X] and [X+4] as pNext and pPrev still make sense. For a node inserted at the back of the exception list, pNext will point to the base of the table (end), and pPrev will point to the address of the previous handler. Here [Base] is the third member of _LdrpVectorHandlerList. Given what is known from the previous run and this one, it is possible to draw a conclusion that the two pointers in _LdrpVectorHandlerList are pointers to the first and last exception handlers. The definition of _LdrpVectorHandlerList can now be completed.

typedef struct _LdrpVectorHandlerList
{
    SRWLOCK srwLock;
    VECTORED_HANDLER_ENTRY *pFirstHandler;
    VECTORED_HANDLER_ENTRY *pLastHandler;
} VECTORED_HANDLER_LIST, *PVECTORED_HANDLER_LIST;

That wraps up the implementation details of vectored exception handlers. The full C implementation will be provided in the next post. Follow on Twitter for more updates.

  1. No comments yet.
  1. No trackbacks yet.