Archive

Archive for the ‘Reverse Engineering’ Category

Heap Tracking

December 23rd, 2015 No comments

This post will cover the topic of finding and inspecting differences in a process heap over time. It will cover two techniques: a non-invasive one that iterates and copies heap entries from a separate process, and an invasive one that uses dynamic binary instrumentation to track all heap writes. Heap tracking is useful if you want to monitor large scale changes in an application over time. For example, looking at the state of the heap and potentially what data structures were modified after pressing a button or performing some complex action.

Non-invasive Heap Diffing

The non-invasive technique relies on remotely reading every allocated heap block in a target process and copying the bytes to the inspecting process. Once this iteration is done, a snapshot of the heap will be created and can then be accurately diffed against another snapshot at a later point in time to see how the heap state changed. This traversal is accomplished with the HeapList32First/HeapList32Next and Heap32First/Heap32Next functions from the Toolhelp API. The traversal code is shown below:

const Heap EnumerateProcessHeap(const DWORD processId, const HANDLE processHandle)
{
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, processId);
    if (snapshot == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "Could not create toolhelp snapshot. "
            "Error = 0x%X\n", GetLastError());
        exit(-1);
    }
 
    Heap processHeapInfo;
 
    (void)NtSuspendProcess(processHandle);
 
    size_t reserveSize = 4096;
    std::unique_ptr<unsigned char[]> heapBuffer(new unsigned char[reserveSize]);
 
    HEAPLIST32 heapList = { 0 };
    heapList.dwSize = sizeof(HEAPLIST32);
    if (Heap32ListFirst(snapshot, &heapList))
    {
        do
        {
            HEAPENTRY32 heapEntry = { 0 };
            heapEntry.dwSize = sizeof(HEAPENTRY32);
 
            if (Heap32First(&heapEntry, processId, heapList.th32HeapID))
            {
                do
                {
                    if (IsReadable(processHandle, heapEntry.dwAddress, heapEntry.dwSize))
                    {
                        ReadHeapData(processHandle, heapEntry.dwAddress, heapEntry.dwSize,
                            processHeapInfo, heapBuffer, reserveSize);
                    }
 
                    heapEntry.dwSize = sizeof(HEAPENTRY32);
                } while (Heap32Next(&heapEntry));
            }
 
            heapList.dwSize = sizeof(HEAPLIST32);
        } while (Heap32ListNext(snapshot, &heapList));
    }
 
    (void)NtResumeProcess(processHandle);
 
    (void)CloseHandle(snapshot);
 
    return processHeapInfo;
}

For every heap list and subsequent heap entry, the heap block is read and its byte contents stored in an address -> byte pair. The remote read is just a call around ReadProcessMemory

void ReadHeapData(const HANDLE processHandle, const DWORD_PTR heapAddress, const size_t size, Heap &heapInfo,
    std::unique_ptr<unsigned char[]> &heapBuffer, size_t &reserveSize)
{
    if (size > reserveSize)
    {
        heapBuffer = std::unique_ptr<unsigned char[]>(new unsigned char[size]);
        reserveSize = size;
    }
 
    SIZE_T bytesRead = 0;
    const BOOL success = ReadProcessMemory(processHandle, (LPCVOID)heapAddress, heapBuffer.get(), size, &bytesRead);
 
    if (success == 0)
    {
        fprintf(stderr, "Could not read process memory at 0x%p "
            "Error = 0x%X\n", (void *)heapAddress, GetLastError());
        return;
    }
    if (bytesRead != size)
    {
        fprintf(stderr, "Could not read process all memory at 0x%p "
            "Error = 0x%X\n", (void *)heapAddress, GetLastError());
        return;
    }
 
    for (size_t i = 0; i < size; ++i)
    {
        heapInfo.emplace_hint(std::end(heapInfo), std::make_pair((heapAddress + i), heapBuffer[i]));
    }
}

At this point a snapshot of the heap is created. A screenshot of an example run shows the address -> byte pairs below.heapdiff

The next part is to take another snapshot at a later point in time and begin diffing the heaps. Diffing the heaps involves three scenarios: when a heap entry at the same address has changed, when an entry was removed (in first snapshot but not in second), and when a new allocation was made (in second heap snapshot but not in first). The code is pretty straightforward and performs a search and compare in the first heap against the second heap.

const HeapDiff GetHeapDiffs(const Heap &firstHeap, Heap &secondHeap)
{
    HeapDiff heapDiff;
 
    for (auto &heapEntry : firstHeap)
    {
        auto &secondHeapEntry = std::find_if(std::begin(secondHeap), std::end(secondHeap),
            [&](const std::pair<DWORD_PTR, unsigned char> &entry) -> bool
        {
            return entry.first == heapEntry.first;
        });
 
        if (secondHeapEntry != std::end(secondHeap))
        {
            if (heapEntry.second != secondHeapEntry->second)
            {
                //Entries in both heaps but are different
                heapDiff.emplace_hint(std::end(heapDiff),
                    heapEntry.first, std::make_pair(heapEntry.second, secondHeapEntry->second));
            }
            secondHeap.erase(secondHeapEntry);
        }
        else
        {
            //Entries in first heap and not in second heap
            heapDiff.emplace_hint(std::end(heapDiff),
                heapEntry.first, std::make_pair(heapEntry.second, heapEntry.second));
        }
    }
 
    for (auto &newEntries : secondHeap)
    {
        //Entries in second heap and not in first heap
        heapDiff.emplace_hint(std::end(heapDiff),
            newEntries.first, std::make_pair(newEntries.second, newEntries.second));
    }
 
    return heapDiff;
}

A screenshot post-diff is shown below:

heapdiff1

Looking at the above example, you can see that the bytes at heap address 0x003F0200 changed from 0x2B to 0x57, among many others. The last step is to merge contiguous blocks to make things more simple. The code is omitted here, but a final screenshot is shown below showing the final structure of the heap diff.heapdiff2The diff can be inspected for anything deemed interesting and can aid in reverse engineering an application. For example, to see where text is drawn in a text editor, you can write some text in the editor and take a snapshot

heapdiff3Prior to taking a second snapshot, change some of the text around and inspect the heap differences. For this example, some AA‘s were changed to BB.heapdiff4The heap contents beginning at 0x0079201C contained the text and were noted as changing from A -> B. Attaching a debugger and setting a breakpoint on-write at 0x0079201C showed an access from 0x00402CA5, which is a rep movs instruction responsible for copying the text to draw into the buffer.heapdiff5heapdiff6
The usefulness of this technique is obviously predicated on the desired data to reside in the process heap.

Invasive Heap Diffing

The technique described above is useful because it does not disturb the process state, aside from suspending and resuming it. The inspecting process has no access to the address space of the target process and performs all of its actions remotely. This next technique uses Intel’s Pin dynamic binary instrumentation platform to instrument a target process and monitor only heap writes. This means that, unlike the previous technique, the entire state of the heap does not need to be tracked. Pin allows for tracking of memory writes in a process, among many other things. Pin is injected as a DLL into a process, so all code written within it will have access to the process address space. That means that instead of traversing heap lists and heap entries, the HeapWalk function can be used directly to get all valid heap addresses.

In the example, all current heap addresses are kept in a std::set container. These are retrieved when the DLL is loaded in the process and instrumentation beings:

void WalkHeaps(WinApi::HANDLE *heaps, const size_t size)
{
    using namespace WinApi;
 
    fprintf(stderr, "Walking %i heaps.\n", size);
 
    for(size_t i = 0; i < size; ++i)
    {
        if(HeapLock(heaps[i]) == FALSE)
        {
            fprintf(stderr, "Could not lock heap 0x%X"
                "Error = 0x%X\n", heaps[i], GetLastError());
            continue;
        }
 
        PROCESS_HEAP_ENTRY heapEntry = { 0 };
        heapEntry.lpData = NULL;
        while(HeapWalk(heaps[i], &heapEntry) != FALSE)
        {
            for(size_t j = 0; j < heapEntry.cbData; ++j)
            {
                heapAddresses.insert(std::end(heapAddresses),
                    (DWORD_PTR)heapEntry.lpData + j);
            }
        }
 
        fprintf(stderr, "HeapWalk finished with 0x%X\n", GetLastError());
 
        if(HeapUnlock(heaps[i]) == FALSE)
        {
            fprintf(stderr, "Could not unlock heap 0x%X"
                "Error = 0x%X\n", heaps[i], GetLastError());
        }
    }
 
    size_t numHeapAddresses = heapAddresses.size();
    fprintf(stderr, "Found %i (0x%X) heap addresses.\n",
        numHeapAddresses, numHeapAddresses);
 
}

An instrumentation function is then added, which is called on every instruction execution:

INS_AddInstrumentFunction(OnInstruction, 0);

The OnInstruction function checks to see if it is a memory write. If it is then a call to our inspection function is added and subsequently invoked. This function checks if the address that is being written to is in the heap and logs it if that is the case.

VOID OnMemoryWriteBefore(VOID *ip, VOID *addr)
{
    if(IsInHeap(addr))
    {
        fprintf(trace, "Heap entry 0x%p has been modified.\n", addr);
    }
}

Testing this is pretty simple; create a simple application that allocates some data on the heap and performs constant writes to it:

int main(int argc, char *argv[])
{
    int *heapData = new int;
    *heapData = 0;
 
    fprintf(stdout, "Heap address: 0x%p", heapData);
 
    while(true)
    {
        *heapData = (*heapData + 1) % INT_MAX;
        Sleep(500);
    }
 
    return 0;
}

Running the instrumentation against the a compiled version of the code above produces the following output, showing successful instrumentation and heap tracking.heapdiff9

Heap entry 0x007B4B58 has been modified.
Heap entry 0x007B4B6C has been modified.
Heap entry 0x007B4B70 has been modified.
Heap entry 0x007B4B68 has been modified.
Heap entry 0x007B27C8 has been modified.
Heap entry 0x007B27C8 has been modified.
Heap entry 0x007B27C8 has been modified.
Heap entry 0x007B27C8 has been modified.
Heap entry 0x007B27C8 has been modified.
...

The Pin framework provides a lot more functionality than what is covered in the example code provided. The code can further be expanded to disassemble and interpret the writing address and get the current heap value and the value that will be written as in the first example.

Final Notes

This post presented a couple of techniques for finding differences in process heaps. The example code shows basic examples, but has some issues in terms of scaling; a 100MB heap diff takes about 15 minutes with the current implementation due to the large number of lookups. The code should serve as a good starting point to build on if the target application allocates a large amount of heap space.

Code

The Visual Studio 2015 project for this example can be found here. The source code is viewable on Github here. Thanks for reading and follow on Twitter for more updates.

Reverse Engineering Vectored Exception Handlers: Implementation (3/3)

April 13th, 2015 No comments

Here an implementation of AddVectoredExceptionHandler as it was reverse engineered.

PVOID RtlAddVectoredExceptionHandler(ULONG FirstHandler, PVECTORED_EXCEPTION_HANDLER VectoredHandler, int Unknown)
{
    PPEB pPeb = GetPEB();
 
    VECTORED_HANDLER_ENTRY *pVecNewEntry =
        (VECTORED_HANDLER_ENTRY *)HeapAlloc((HANDLE)pPeb->ProcessHeap, 0, sizeof(VECTORED_HANDLER_ENTRY));
    if(pVecNewEntry == nullptr)
    {
        return nullptr;
    }
    pVecNewEntry->dwAlwaysOne = 1;
 
    PVOID pEncodedHandler = EncodePointer(VectoredHandler);
    VECTORED_HANDLER_LIST *pVecHandlerBase = (VECTORED_HANDLER_LIST *)(VectorHandlerListBase);
 
    AcquireSRWLockExclusive(&pVecHandlerBase->srwLock);
 
    pVecNewEntry->pVectoredHandler = (PVECTORED_EXCEPTION_HANDLER)pEncodedHandler;
 
    //If the list is empty then set the CrossProcessFlags fields
    if(pVecHandlerBase->pFirstHandler == (VECTORED_HANDLER_ENTRY *)&pVecHandlerBase->pFirstHandler)
    {
        InterlockedBitTestAndSet((LONG *)&pPeb->CrossProcessFlags, 2);
    }
 
    if(FirstHandler)
    {
        //Insert new node at the head of the VEH list
        pVecNewEntry->pNext = pVecHandlerBase->pFirstHandler;
        pVecNewEntry->pPrev = (VECTORED_HANDLER_ENTRY *)&pVecHandlerBase->pFirstHandler;
        pVecHandlerBase->pFirstHandler->pPrev = pVecNewEntry;
        pVecHandlerBase->pFirstHandler = pVecNewEntry;
    }
    else
    {
        //Insert new node at the end of the VEH list
        pVecNewEntry->pNext = (VECTORED_HANDLER_ENTRY *)&pVecHandlerBase->pFirstHandler;
        pVecNewEntry->pPrev = pVecHandlerBase->pLastHandler;
        pVecHandlerBase->pLastHandler->pNext = pVecNewEntry;
        pVecHandlerBase->pLastHandler = pVecNewEntry;
    }
 
    ReleaseSRWLockExclusive(&pVecHandlerBase->srwLock);
 
    return (PVOID)pVecNewEntry;
}

You can download the full Visual Studio 2013 project here. Follow on Twitter for more updates.

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

April 11th, 2015 No comments

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.

Reverse Engineering Vectored Exception Handlers: Structures (1/3)

April 8th, 2015 No comments

This series of posts will cover the details of reverse engineering the AddVectoredExceptionHandler function, a Windows API function responsible for registering a special type of exception handler at runtime. The series will be split in to three parts: first identifying key structures that are used, second understanding the implementation, and lastly re-implementing the reverse engineered assembly to working C code. This reverse engineered implementation will behave identically with the original function, and presumably under the same compiler options, would produce a very close assembly listing. The reverse engineering was done on Windows 7, so there will be slight differences in assembly listings if you are following along on a different version. The re-implementation code (part 3) was tested on Windows 7 and 8.1 on x86 and x64, so the high-level details should not change.

Starting out

The goal is to see how AddVectoredExceptionHandler works. This means tracing it through from an example program over in to kernel32.dll, where the implementation resides. Naturally, the best way to go about doing this is with a debugger. The Visual Studio debugger will be the debugger of choice for this series since we’ll be debugging our own code.

vec1

Stepping in to the disassembly shows that AddVectoredExceptionHandler calls _RtlAddVectoredExceptionHandler, which in turn is a wrapper for _RtlpAddVectoredHandler. The assembly listing for _RtlAddVectoredExceptionHandler is shown below:

_RtlAddVectoredExceptionHandler@8:
771F742B  mov         edi,edi  
771F742D  push        ebp  
771F742E  mov         ebp,esp  
771F7430  push        0  
771F7432  push        dword ptr [ebp+0Ch]  
771F7435  push        dword ptr [ebp+8]  
771F7438  call        _RtlpAddVectoredHandler@12 (771E3621h)  
771F743D  pop         ebp  
771F743E  ret         8

This  code simply pushes an extra constant parameter and invokes _RtlpAddVectoredHandler(FirstHandler, VectoredHandler, 0). The actual details reside in _RtlpAddVectoredHandler, reproduced in its entirety, below:

_RtlpAddVectoredHandler@12:
771E3621  mov         edi,edi  
771E3623  push        ebp  
771E3624  mov         ebp,esp  
771E3626  mov         eax,dword ptr fs:[00000018h]  
771E362C  mov         eax,dword ptr [eax+30h]  
771E362F  push        esi  
771E3630  push        10h  
771E3632  push        0  
771E3634  push        dword ptr [eax+18h]  
771E3637  call        _RtlAllocateHeap@12 (771AE026h)  
771E363C  mov         esi,eax  
771E363E  test        esi,esi  
771E3640  je          _RtlpAddVectoredHandler@12+83h (771E36A4h)  
771E3642  push        ebx  
771E3643  push        edi  
771E3644  push        dword ptr [ebp+0Ch]  
771E3647  mov         dword ptr [esi+8],1  
771E364E  call        _RtlEncodePointer@4 (771C0FCBh)  
771E3653  mov         ebx,dword ptr [ebp+10h]  
771E3656  imul        ebx,ebx,0Ch  
771E3659  add         ebx,77284724h  
771E365F  push        ebx  
771E3660  mov         dword ptr [esi+0Ch],eax  
771E3663  lea         edi,[ebx+4]  
771E3666  call        _RtlAcquireSRWLockExclusive@4 (771B29F1h)  
771E366B  cmp         dword ptr [edi],edi  
771E366D  jne         _RtlpAddVectoredHandler@12+65h (771E3686h)  
771E366F  mov         ecx,dword ptr fs:[18h]  
771E3676  mov         eax,dword ptr [ebp+10h]  
771E3679  mov         ecx,dword ptr [ecx+30h]  
771E367C  add         eax,2  
771E367F  add         ecx,28h  
771E3682  lock bts    dword ptr [ecx],eax  
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  
771E369C  push        ebx  
771E369D  call        _RtlReleaseSRWLockExclusive@4 (771B29ABh)  
771E36A2  pop         edi  
771E36A3  pop         ebx  
771E36A4  mov         eax,esi  
771E36A6  pop         esi  
771E36A7  pop         ebp  
771E36A8  ret         0Ch  

Decoding the Assembly

Don’t mind the gratuitous highlighting above; it is there to highlight individual pieces of the function and make it more manageable to understand. The function begins by performing a call to RtlAllocateHeap, highlighted in orange. The three parameters provided are [EAX+18], 0, and 16 (0x10). EAX is initially loaded with the address of the TIB structure (light pink). From this structure, the PEB structure is then retrieved. The member at [PEB+0x18], which is documented as ProcessHeap is then given to RtlAllocateHeap. Everything here seems to make sense so far.

Next, in green, comes a call to RtlEncodePointer, which is the implementation of EncodePointer. The address of the vectored handler, at [EBP+0xC] is given as the argument here. This function, as its name implies, is responsible for encoding the provided pointer. It does this by performing an XOR with a cookie value generated at runtime.

From earlier, it should be noticed that the requested allocation size provided to RtlAllocateHeap was 16 bytes (0x10). The next few instructions give some information about how this returned memory is accessed. The instructions in black move two values into this memory region, one at 0x8 and one at 0xC. Given this information, it is safe to assume that what is being allocated is a 16 byte struct. The third field at +0x8 is always set to 1 in this function, and the fourth at +0xC is set to hold the encoded handler address. It’s possible to write out a basic definition for this struct at this point:

struct MysteryStruct
{
    DWORD dwUnknown1; +0x0
    DWORD dwUnknown2; +0x4
    DWORD dwAlwaysOne; +0x8
    PVECTORED_EXCEPTION_HANDLER pVectoredHandler; +0xC
};

This definition will be revisited and completed later.

The next block of code, in teal, performs some arithmetic operations. It loads [EBP+0x10], which was shown to be always 0 (from _RtlAddVectoredExceptionHandler) into EBX. This value is multipled by 12 (0xC), which still yields a zero. Then the value 0x77284724 is added to it. Checking what resides at this address in a debugger shows something interesting:

_LdrpVectorHandlerList:
77284724 01 00                add         dword ptr [eax],eax  
77284726 00 00                add         byte ptr [eax],al  
77284728 28 47 28             sub         byte ptr [edi+28h],al  
7728472B 77 28                ja          _RtlpProcessHeapsListBuffer+15h (77284755h)  
7728472D 47                   inc         edi  
7728472E 28 77 00             sub         byte ptr [edi],dh  
...

It turns out that 0x77284724 is the address of the symbol _LdrpVectorHandlerList. The non-sense assembly instructions there are simply mnemonic representations of _LdrpVectorHandlerList‘s  data members. The base of this structure is used as an argument for _RtlAcquireSRWLockExclusive, which is the implementation of AcquireSRWLockExclusive. This function takes a PSRWLOCK argument. Given this, it is immediately possible to deduce that the first member of _LdrpVectorHandlerList is an SRWLOCK structure. More about this structure will be revealed later.

The code in bright pink begins by loading the second field in the _LdrpVectorHandlerList structure in to EDI. This value is then dereferenced and compared to its own address — basically a check if a pointer is pointing to itself. If that is the case then the rest of the pink block will be executed. The code once again retrieves the PEB structure similar to light pink. Expect this time, [PEB+0x28] will be the value that ends up being used. Additionally, it loads [EBP+0x10] (always 0) into EAX, and adds 2 to it. There it an atomic bit test and set instruction that is carried out between [PEB+0x28] and 2. [PEB+0x28] has been documented as “CrossProcessFlags” and is a bit of a mystery in the context of this function.

Lastly, the block in red is where the actual interesting code happens. It begins by checking to see if the first parameter to the function, the flag saying whether the handler is to be the first or last in the chain, is zero. In either case, there are a lot of pointers moving around from looking at the instructions. One would guess that from implementing an exception handler list that there would be pointers to next/previous nodes. Lets begin investigating the case where an exception handler will be added to front of the chain (FirstHandler parameter does not equal 0). Starting at 0x771E3690, [EDI] is moved into EAX. From earlier, [EDI] holds the second member of the _LdrpVectorHandlerList structure. This is then moved in to [ESI], which is the first member of the structure allocated with RtlAllocateHeap (MysteryStruct above). Then EDI (not dereferenced) is moved in to [ESI+0x4].

This completes finding references to the allocated structure. RtlAllocateHeap had a request for 16 bytes, and 16 bytes have now been used/written to. ESI is then moved in to [EAX+4] and [EDI], which relate to two pointers in _LdrpVectorHandlerList. The part where the handler is added to the back over the list won’t be covered in this post, since it’s basically the same thing except for which pointers get rearranged.

Finalizing Structure Definitions

Going through the code revealed two main structures at work here. There is the 16 byte structure that was allocated in the beginning and the _LdrpVectorHandlerList structure. The MysteryStruct from earlier can be better defined now. I’ve renamed it as _LdrpVectorHandlerEntry to be consistent with the known _LdrpVectorHandlerList symbol.

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

Also, from studying the pointer swapping operations between the new entry and the list, it is possible to define _LdrpVectorHandlerList a bit more clearly as well:

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

The types in these structures have been defined. The next part of this series will cover how the links behave. Follow on Twitter for more updates.

Hiding Functionality with Exception Handlers (2/2)

April 4th, 2015 No comments

This post will cover the second part of hiding functionality with exception handlers. Unlike the technique presented in the previous post, which modified the SEH record for the local thread, the aim here is to modify the SEH record for another thread in order to better hide what is actually going on. By the end of the post, there should be enough information to put together a working application capable of modifying the SEH list of any thread (barring some exceptions) and causing it to raise an exception to execute your code. The sample application will be a DLL that is injected into a process and hijacks one of its threads to perform some task.

What is the purpose of doing all of this if you’re injecting into a process anyway? After all, you can simply spawn your own thread or likely use the one created during the injection (if CreateRemoteThread was used) and just begin executing your code. I’d argue that this technique gives more obscurity to what is happening during static analysis and is something out of the norm. Plus its fun!

The overall code is very similar to what the first part showed, but now there need to be a few steps added in order to get the TIB of another thread. There are usually a few different approaches, of varying complexity and reliability.

  • Do it directly. Suspend the thread and gets its context. Change the instruction pointer to point to your code which changes the SEH list and raises an interrupt and resume. Perform your task and restore the original context in your SEH handler.
  • Do it indirectly. Suspend the thread, queue an asynchronous procedure call (APC) which changes the SEH list and raises an interrupt (with QueueUserAPC), and resume the thread. The thread must be in an alertable state (waiting on something) for this to work, which is typically the case for most threads in a process.
  • Take the middle ground. Suspend the thread and get the address of its FS segment directly using GetThreadSelectorEntry. Change the SEH list from within your thread and queue an APC to raise the interrupt, resume the thread.

The easiest approach is to do it indirectly with an APC. The code is really straightforward and looks like the following:

void InstallExceptionHandler(DWORD dwThreadId)
{
    auto handle = ThreadHandleTable[dwThreadId];
 
    DWORD dwError = SuspendThread(handle);
    if (dwError == -1)
    {
        fprintf(stderr, "Could not suspend thread. Error = %X.\n",
            GetLastError());
        return;
    }
 
    CONTEXT ctx = { CONTEXT_ALL };
    GetThreadContext(handle, &ctx);
    LDT_ENTRY ldtEntry = { 0 };
 
    GetThreadSelectorEntry(handle, ctx.SegFs, &ldtEntry);
    const DWORD dwFSAddress =
        (ldtEntry.HighWord.Bits.BaseHi << 24) |
        (ldtEntry.HighWord.Bits.BaseMid << 16) |
        (ldtEntry.BaseLow);
 
    fprintf(stderr, "FS segment address of target thread should be: %X.\n",
        dwFSAddress);
 
    dwError = QueueUserAPC(APCProc, handle, 0);
    if (dwError == 0)
    {
        fprintf(stderr, "Could not queue APC to thread. Error = %X.\n",
            GetLastError());
    }
 
    dwError = ResumeThread(handle);
    if (dwError == -1)
    {
        fprintf(stderr, "Could not resume thread. Error = %X.\n",
            GetLastError());
    }
}

Here the suspend/queue/resume wording is put directly in to code (with extra debug comments). When the thread resumes, APCProc will be invoked. APCProc will be running in the context of the target thread and is responsible for modifying the SEH list to add in a new handler. Because of this, APCProc can obtain the TIB without any extra overhead code to write and the code basically becomes a copy/paste from part one.

void CALLBACK APCProc(ULONG_PTR dwParam)
{
    fprintf(stderr, "APC callback invoked. Raising exception to trigger exception handler.\n");
 
    EXCEPTION_REGISTRATION *pHandlerBase = (EXCEPTION_REGISTRATION *)__readfsdword(0x18);
 
    fprintf(stderr, "Segment address of target thread: %X.\n", pHandlerBase);
 
    EXCEPTION_REGISTRATION NewHandler = { pHandlerBase->pPrevHandler,
        (EXCEPTION_REGISTRATION::pFncHandler)(MyTestHandler) };
 
    pHandlerBase->pPrevHandler = &NewHandler;
 
    RaiseException(STATUS_ACCESS_VIOLATION, 0, 0, nullptr);
}

The handler, NewHandler, being independent of all of this, doesn’t change much either.

EXCEPTION_DISPOSITION __cdecl MyTestHandler(EXCEPTION_RECORD *pExceptionRecord, void *pEstablisherFrame,
    CONTEXT *pContextRecord, void *pDispatcherContext)
{
    if (pExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION)
    {
        MessageBox(0, L"Some hidden functionality can go here.",
            L"Test", 0);
        return ExceptionContinueExecution;
    }
 
    return ExceptionContinueSearch;
}

Below are some screenshots of this at work on a 32-bit Notepad++ instance.

np1
Thread 5504 is chosen here.
np2The MessageBox in the exception handler successfully pops ups. Hitting the “OK” button resumes execution as normal.

The source for the projects (Visual Studio 2013, Update 4) presented in these parts can be found here. Thanks for reading and follow on Twitter for more updates.