我实现了关于抛出异常的日志信息(信息包括调用堆栈)。我最近问如何实现调用堆栈记录https://stackoverflow.com/questions/48924449/reliable-way-to-print-exception-backtrace-in-catch-handler。我决定检查 x86 和 x64 平台上的堆栈。比较的结果让我有点吃惊。这是程序:
#include <iostream>
#include <Windows.h>
using namespace std;
struct A
{
};
void foo3()
{
throw A();
}
void foo2()
{
foo3();
}
void foo1()
{
foo2();
}
void foo()
{
try
{
foo1();
}
catch(...)
{
cout << "exception rethrowing" << endl;
throw;
}
}
int seh_filter(_EXCEPTION_POINTERS* exception)
{
cout << "SEH FILTER" << endl;
return EXCEPTION_EXECUTE_HANDLER;
}
int main(void)
{
__try
{
foo();
}
__except(seh_filter(GetExceptionInformation()))
{
cout << "SEH catch handler" << endl;
}
return 0;
}
设置断点seh_filter
以查看堆栈(启动的调试版本)。以下是图片:
我不明白为什么堆栈如此不同(尤其是在 x64 下)。刚刚听说x64下seh异常的处理方式发生了变化。我在 windows 10 下使用 msvc2013。
在 MSVC C++ 中,异常是在结构化异常处理 (SEH) 的基础上实现的。
就其本身而言,SEH 在 x86 和 x64 上的实现完全不同。
x86 SEH 在程序执行期间使用机器指令形成异常处理程序堆栈,调用堆栈包含对处理程序的引用。
IMAGE_DIRECTORY_ENTRY_EXCEPTION
x64 在可执行文件( )的单独区域中包含用于异常处理的元数据。对于运行时生成的代码,有一个用于添加此元数据的 API - 请参阅RtlAddFunctionTable。因此,x64 方法更有效(在没有异常的情况下的性能方面)和更安全 - 攻击者更改异常处理程序堆栈的机会更少。
同时,x86 方法并没有被放弃,显然是由于向后兼容性——已经编译的程序包含“旧”的异常处理方法。(为了降低异常处理程序堆栈替换的风险,使用了 SAFESEH - 关于处理程序的可选元信息,如果有的话,在展开之前检查它)
这完全是关于堆栈的“系统”部分。
foo1
//在不知道编译foo2
器foo3
选项的情况下很难判断差异。影响因素:更具体地说,可以说是程序的反汇编。