DLL 中的大向量异常处理程序导致程序在没有调试器的情况下崩溃
Large Vectored Exception Handler in DLL is causing program to crash without debugger
我目前正在开发实现矢量异常处理程序 (VEH) 的 DLL(Windows 10,x64)。它导出函数是为了附加到可执行文件,仅此而已。在附加 VEH 期间,它还使用以下代码片段计算基本偏移量:
uint64_t GetModuleBase(const char *modname)
{
HANDLE hModSnap;
MODULEENTRY32 pe32;
void *bs = NULL;
// Take a snapshot of all processes in the system.
hModSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, GetCurrentProcessId() );
if( hModSnap == INVALID_HANDLE_VALUE )
{
return NULL;
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof( MODULEENTRY32 );
if( !Module32First( hModSnap, &pe32 ) )
{
CloseHandle( hModSnap ); // Must clean up the snapshot object!
return NULL;
}
do
{
if (!StrCmpI(pe32.szModule, modname)) {
bs = pe32.modBaseAddr;
break;
}
} while( Module32Next( hModSnap, &pe32 ) );
CloseHandle( hModSnap );
return (uint64_t)bs;
}
在异常处理程序中,我只处理代码为“0xc000001d”的异常,在其他情况下,VEH 将执行返回给程序。
在VEH中注册的内部处理函数我有以下结构(简化):
#define FUNCTION_1(arg1, arg2, arg3, arg4)
if (exception_offset == arg1) { \
5_lines_of_code .... \
return EXCEPTION_CONTINUE_EXECUTION; \
}
#define FUNCTION_2(arg1, arg2, arg3, arg4)
if (exception_offset == arg1) { \
5_lines_of_code .... \
return EXCEPTION_CONTINUE_EXECUTION; \
}
#define FUNCTION_3(arg1, arg2, arg3, arg4)
if (exception_offset == arg1) { \
5_lines_of_code .... \
return EXCEPTION_CONTINUE_EXECUTION; \
} \
...
# 15 functions like that.
FUNCTION_1(0x192385, CPU_REGISTER1, CPU_REGISTER2);
FUNCTION_2(0x145685, CPU_REGISTER3, CPU_REGISTER4);
FUNCTION_3(0x456685, CPU_REGISTER1, CPU_REGISTER5);
# 1719 lines like that
我收到来自 MSVC 的警告:函数使用“81340”字节的堆栈:超过 /analyze:stacksize“16384”。考虑将一些数据移动到堆中。
使用调试器一切正常。使用 Windbg,一切都按预期工作。
没有调试器 - 应用程序无声地崩溃。
更深入的调查表明它注册了 DLL,开始执行甚至调用处理程序几次,但是有不同的异常代码(从 0x8 开始......这是调试代码),所以 VEH 代码 returns在早期阶段。在正常的应用程序操作期间,我可以看到相同偏移量的相同代码,顺便说一句。
当该函数调用的数量大约为 1400 次左右时,一切都运行良好(如果我评论其中的一部分,不管是哪一个,只要我减少数量)
我试过:
- 注释掉相同的函数调用(以确保它不是函数实现错误)-> 只有数量很重要
- 禁用优化
- 将堆栈大小增加到 140 Mb
- 使用静态 MSVC 分析检查未初始化或归零的变量 - none 个。
- 正在网上阅读类似案例。
- 使用文件日志记录进行调试
这些都没有任何效果。我猜,这是一个超载的堆栈。
因此问题:
- 有没有办法在没有调试模式的情况下从崩溃中获取更多信息?
- 有没有办法表示如此大量的调用,这样代码会很快,但不会使堆栈过载?注意:所有函数调用都有不同的参数,具体取决于发生异常的偏移量,因此分组将减少非常小的调用量。
- 如果您为了调试而遇到此类问题,您的下一步是什么?
谢谢你,希望得到你的帮助。
好的,我能够解决问题:
- 选择了不同的调试器 - x64dbg。惊人的调试器,它附加到启动的进程而不是在调试模式下启动它。
- 这允许在进程初始化期间识别堆栈溢出问题。
- 我已经为此应用了快速修复,因为我正在制作原型:使用 editbin /F stacksize 修改应用程序的 .exe 文件以增加它。
- 一切正常。
我目前正在开发实现矢量异常处理程序 (VEH) 的 DLL(Windows 10,x64)。它导出函数是为了附加到可执行文件,仅此而已。在附加 VEH 期间,它还使用以下代码片段计算基本偏移量:
uint64_t GetModuleBase(const char *modname)
{
HANDLE hModSnap;
MODULEENTRY32 pe32;
void *bs = NULL;
// Take a snapshot of all processes in the system.
hModSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, GetCurrentProcessId() );
if( hModSnap == INVALID_HANDLE_VALUE )
{
return NULL;
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof( MODULEENTRY32 );
if( !Module32First( hModSnap, &pe32 ) )
{
CloseHandle( hModSnap ); // Must clean up the snapshot object!
return NULL;
}
do
{
if (!StrCmpI(pe32.szModule, modname)) {
bs = pe32.modBaseAddr;
break;
}
} while( Module32Next( hModSnap, &pe32 ) );
CloseHandle( hModSnap );
return (uint64_t)bs;
}
在异常处理程序中,我只处理代码为“0xc000001d”的异常,在其他情况下,VEH 将执行返回给程序。
在VEH中注册的内部处理函数我有以下结构(简化):
#define FUNCTION_1(arg1, arg2, arg3, arg4)
if (exception_offset == arg1) { \
5_lines_of_code .... \
return EXCEPTION_CONTINUE_EXECUTION; \
}
#define FUNCTION_2(arg1, arg2, arg3, arg4)
if (exception_offset == arg1) { \
5_lines_of_code .... \
return EXCEPTION_CONTINUE_EXECUTION; \
}
#define FUNCTION_3(arg1, arg2, arg3, arg4)
if (exception_offset == arg1) { \
5_lines_of_code .... \
return EXCEPTION_CONTINUE_EXECUTION; \
} \
...
# 15 functions like that.
FUNCTION_1(0x192385, CPU_REGISTER1, CPU_REGISTER2);
FUNCTION_2(0x145685, CPU_REGISTER3, CPU_REGISTER4);
FUNCTION_3(0x456685, CPU_REGISTER1, CPU_REGISTER5);
# 1719 lines like that
我收到来自 MSVC 的警告:函数使用“81340”字节的堆栈:超过 /analyze:stacksize“16384”。考虑将一些数据移动到堆中。
使用调试器一切正常。使用 Windbg,一切都按预期工作。
没有调试器 - 应用程序无声地崩溃。
更深入的调查表明它注册了 DLL,开始执行甚至调用处理程序几次,但是有不同的异常代码(从 0x8 开始......这是调试代码),所以 VEH 代码 returns在早期阶段。在正常的应用程序操作期间,我可以看到相同偏移量的相同代码,顺便说一句。
当该函数调用的数量大约为 1400 次左右时,一切都运行良好(如果我评论其中的一部分,不管是哪一个,只要我减少数量)
我试过:
- 注释掉相同的函数调用(以确保它不是函数实现错误)-> 只有数量很重要
- 禁用优化
- 将堆栈大小增加到 140 Mb
- 使用静态 MSVC 分析检查未初始化或归零的变量 - none 个。
- 正在网上阅读类似案例。
- 使用文件日志记录进行调试
这些都没有任何效果。我猜,这是一个超载的堆栈。
因此问题:
- 有没有办法在没有调试模式的情况下从崩溃中获取更多信息?
- 有没有办法表示如此大量的调用,这样代码会很快,但不会使堆栈过载?注意:所有函数调用都有不同的参数,具体取决于发生异常的偏移量,因此分组将减少非常小的调用量。
- 如果您为了调试而遇到此类问题,您的下一步是什么?
谢谢你,希望得到你的帮助。
好的,我能够解决问题:
- 选择了不同的调试器 - x64dbg。惊人的调试器,它附加到启动的进程而不是在调试模式下启动它。
- 这允许在进程初始化期间识别堆栈溢出问题。
- 我已经为此应用了快速修复,因为我正在制作原型:使用 editbin /F stacksize 修改应用程序的 .exe 文件以增加它。
- 一切正常。