SEH Handlers using RtlAddFunctionTable

1

I've been trying to setup SEH on x64 windows using gcc by calling the RtlAddFunctionTable. Unfortunately, the API call returns success but my handler doesn't seem to ever be called. And I can't find out what's wrong. My small example is:

EXCEPTION_DISPOSITION catchDivZero( struct _EXCEPTION_RECORD* rec
                                  , void* arg1 __attribute__((unused))
                                  , struct _CONTEXT* ctxt __attribute__((unused))
                                  , void* arg2 __attribute__((unused))
                                  )
{
    printf("Exception will be handled!\n");
    return ExceptionContinueSearch;
}

HMODULE GetCurrentModule()
{ // NB: XP+ solution!
    HMODULE hModule = NULL;
    GetModuleHandleEx(
        GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
        (LPCTSTR)GetCurrentModule,
        &hModule);

    return hModule;
}

typedef struct {
    UINT8  Version : 3;
    UINT8  Flags : 5;
    UINT8  SizeOfProlog;
    UINT8  CountOfUnwindCodes;
    UINT8  FrameRegister : 4;
    UINT8  FrameRegisterOffset : 4;
    ULONG  ExceptionHandler;
} UNWIND_INFO;

/* Hack, for bug in ld.  Will be removed soon.  */
#if defined(__GNUC__)
#define __ImageBase __MINGW_LSYMBOL(_image_base__)
#endif

/* Get the end of the text section.  */
extern char etext[] asm("etext");

/* Get the base of the module.       */
/* This symbol is defined by ld.     */
extern IMAGE_DOS_HEADER __ImageBase;

static UNWIND_INFO info[1];
static RUNTIME_FUNCTION handlers[1];

#define base (ULONG)((HINSTANCE)&__ImageBase)

int main()
{
    HANDLE hProcess = GetCurrentProcess();
    HMODULE hModule = GetCurrentModule();

    MODULEINFO mi;
    GetModuleInformation(hProcess, hModule, &mi, sizeof(mi));

    printf( "Module: 0x%.8X (0x%.8X) 0x%.8X |0x%.8X| [0x%.8X] {0x%.8X}\n\n"
          , mi.lpBaseOfDll
          , base
          , (char*)etext
          , mi.SizeOfImage
          , &catchDivZero
          , (ULONG)(&catchDivZero - base)
          );

    printf("Building UNWIND_INFO..\n");

    info[0].Version             = 1;
    info[0].Flags               = UNW_FLAG_EHANDLER;
    info[0].SizeOfProlog        = 0;
    info[0].CountOfUnwindCodes  = 0;
    info[0].FrameRegister       = 0;
    info[0].FrameRegisterOffset = 0;
    info[0].ExceptionHandler    = (ULONG)(&catchDivZero - base);

    printf("Created UNWIND_INFO at {0x%.8X}\n", info[0].ExceptionHandler);

    printf("Building SEH handlers...\n");

    handlers[0].BeginAddress = 0;
    handlers[0].EndAddress   = (ULONG)(etext - base);
    handlers[0].UnwindData   = (ULONG)((char*)info - base);

    printf("Adding SEH handlers to .pdata..\n");
    printf("Handler Unwind: 0x%.8X\n", &info);
    printf( "Handler Info:: s: 0x%.8X, e: 0x%.8X, u: 0x%.8X\n"
          , handlers[0].BeginAddress
          , handlers[0].EndAddress
          , handlers[0].UnwindData
          );

    if (RtlAddFunctionTable(handlers, 1, (DWORD64)base))
    {
        printf("Hook succeeded.\nTesting..\n");
        printf("Things to do: %i\n", 12 / 0);
    }
    else 
    {
        printf("Hook failed\n");
        DWORD result = GetLastError();
        printf("Error code: 0x%.8X\n", result);
    }
}

However when called the output I get is:

> .\a.exe
Module: 0x00400000 (0x00400000) 0x00402FF0 |0x00022000| [0x00401530] {0x00001530}

Building UNWIND_INFO..
Created UNWIND_INFO at {0x00001530}
Building SEH handlers...
Adding SEH handlers to .pdata..
Handler Unwind: 0x00407030
Handler Info:: s: 0x00000000, e: 0x00002FF0, u: 0x00007030
Hook succeeded.
Testing..

The message in my handler is never printed.

Any help/pointers would be greatly appreciated.

winapi
gcc
error-handling
64-bit
seh
asked on Stack Overflow Feb 16, 2015 by Phyx • edited Feb 16, 2015 by Phyx

2 Answers

2

RtlAddFunctionTable() adds a dynamic function table; if there already is a static function table (.pdata section) for the base address, the RtlAddFunctionTable() calls succeeds, but the static function table still takes precedence.

You need to allocate memory outside the image range, e.g. using VirtualAlloc(), and have your code and runtime table and unwind info there. The address of allocated memory is the base address for all the RVAs in the tables, and needs to be passed to RtlAddFunctionTable().

You can experiment with RtlLookupFunctionEntry() to see if the function table entry is found for a given address.

Sample code showing RtlAddFunctionTable() in action is at https://pmeerw.net/blog/programming/RtlAddFunctionTable.html.

answered on Stack Overflow Oct 3, 2019 by pmeerw
0

Didn't you forget to register your handler with call to SetUnhandledExceptionFilter (if you use SEH as stated in your post) or AddVectoredExceptionHandler (if you decide to switch to VEH)? In your code you add information about the handler but do not register it.

I have tested your sample with the change of the handler itself:

LONG WINAPI catchDivZero(EXCEPTION_POINTERS * ExceptionInfo)
{
    printf("Exception will be handled!\n");
    return ExceptionContinueSearch;
}

and adding the code:

if (::AddVectoredExceptionHandler(TRUE, catchDivZero))
{
    printf("Set exception handler.\nContinuing..\n");
}
else
{
    printf("Setting exception handler failed\n");
    DWORD result = GetLastError();
    printf("Error code: 0x%.8X\n", result);

    return 1;
}

just before the call to RtlAddFunctionTable.

Now the message from the handler is printed.

To remove the handler use:

::RemoveVectoredExceptionHandler(catchDivZero);

Hope it helps.

Note: as an alternative you may use SetUnhandledExceptionFilter(catchDivZero)). Keep in mind that it's not that useful for debugging:

After calling this function, if an exception occurs in a process that is not being debugged, and the exception makes it to the unhandled exception filter, that filter will call the exception filter function specified by the lpTopLevelExceptionFilter parameter.

With VEH way we can debug the handler function right in IDE, with SEH we can not (there is probably a solution to this but I do not know about it) so I've proposed VEH as the main solution.

answered on Stack Overflow Jun 1, 2016 by greenpiece • edited Jun 1, 2016 by greenpiece

User contributions licensed under CC BY-SA 3.0