【发布时间】: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