【问题标题】:SEH Handlers using RtlAddFunctionTable使用 RtlAddFunctionTable 的 SEH 处理程序
【发布时间】:2015-04-17 10:57:40
【问题描述】:

我一直在尝试使用 gcc 通过调用 RtlAddFunctionTable 在 x64 Windows 上设置 SEH。不幸的是,API 调用返回成功,但我的处理程序似乎从未被调用过。而且我不知道出了什么问题。我的小例子是:

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);
    }
}

但是,当我调用时,我得到的输出是:

> .\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..

我的处理程序中的消息永远不会被打印出来。

任何帮助/指针将不胜感激。

【问题讨论】:

  • RtlAddFunctionTable: “函数表在 64 位 Windows 上用于确定如何展开或遍历堆栈。” 64 位 Windows 使用基于表的异常处理. 32 位 Windows 使用基于帧的异常处理。
  • 对于 x86,您必须在堆栈上设置一个 EXCEPTION_REGISTRATION 对象,以将异常帧排入异常处理程序链。 A Crash Course on the Depths of Win32™ Structured Exception Handling 提供对 32 位 Windows 的 SEH 实现的深入了解。
  • 对不起,描述有错别字,这是针对x64的,我会更正描述
  • 你在用mingw-w64吗?我相信他们已经内置了对 SEH 的支持:sourceforge.net/projects/mingw-w64/files/… 我自己对 SEH 了解不多,但是如果您想查看他们调用 RtlAddFunctionTable 的源以与您的比较:sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/…
  • @DavidWohlferd 我正在使用 mingw-w64,我之前看过它,并且 virtualbox 源代码也设置了一些 SEH 处理程序,但找不到任何真正的区别。它们表示结构的方式略有不同,例如将版本和标志组合到一个变量中。我会再看看它,并尝试他们在做什么。但我怀疑因为函数返回 OK 结构是正确的。

标签: winapi gcc error-handling 64-bit seh


【解决方案1】:

您是否忘记注册您的处理程序并调用SetUnhandledExceptionFilter(如果您使用帖子中所述的SEH)或AddVectoredExceptionHandler(如果您决定切换到VEH)?在您的代码中添加有关处理程序的信息,但不要注册它。

我已经用处理程序本身的变化测试了你的样本:

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

并添加代码:

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;
}

就在致电RtlAddFunctionTable之前。

现在打印来自处理程序的消息。

要删除处理程序,请使用:

::RemoveVectoredExceptionHandler(catchDivZero);

希望对你有帮助。

注意: 作为替代方案,您可以使用SetUnhandledExceptionFilter(catchDivZero))。请记住,它是not that useful for debugging

调用此函数后,如果进程发生异常 没有被调试,异常导致未处理 异常过滤器,该过滤器将调用异常过滤器函数 由 lpTopLevelExceptionFilter 参数指定。

使用 VEH 方式,我们可以直接在 IDE 中调试处理程序函数,而使用 SEH 我们不能(可能有解决方案,但我不知道)所以我建议将 VEH 作为主要解决方案。

【讨论】:

  • 我最终已经选择了 VEH。但我仍然不知道为什么 SEH 处理程序不起作用。您的示例根本不使用 SEH 处理程序,因为您注册了一个被命中的 VEH 处理程序。除非我弄错了SetUnhandledExceptionFilter 设置了 1 个全局未处理的异常过滤器,而RtlAddFunctionTable 允许我为特定的代码区域注册一个处理程序。如果我没记错的话,这个不是用来注册表函数的。 crashrpt.sourceforge.net/docs/html/exception_handling.html
  • 查看此链接:sourceforge.net/p/mingw-w64/support-requests/29。似乎这是 mingw64 特有的错误。
  • 嗯,据说一旦 SEH 在 Mingw-w64 中实现,它应该已经解决了。但它仍然是开放的:?
【解决方案2】:

RtlAddFunctionTable() 添加动态函数表;如果基地址已经存在静态函数表(.pdata 部分),则 RtlAddFunctionTable() 调用成功,但静态函数表仍然优先。

您需要在图像范围之外分配内存,例如使用 VirtualAlloc(),并在那里有你的代码和运行时表和展开信息。分配的内存地址是表中所有RVA的基地址,需要传递给RtlAddFunctionTable()。

您可以尝试使用 RtlLookupFunctionEntry() 来查看是否找到给定地址的函数表条目。

显示 RtlAddFunctionTable() 的示例代码位于 https://pmeerw.net/blog/programming/RtlAddFunctionTable.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-04
    • 2012-01-14
    • 1970-01-01
    相关资源
    最近更新 更多