在Visual Studio中,通过Debug> Exceptions ...中的对话框,可以设置特定的C++异常类型以打开或跳过。在Windbg中,使用sxe eh
打开C++异常是完全没有。在Windbg中,当抛出特定的C++异常时,我可以跳过这个过程吗?
有什么办法可以跳过特定C++异常类型的突破吗?相反,是否有办法打破特定类型?
在Visual Studio中,通过Debug> Exceptions ...中的对话框,可以设置特定的C++异常类型以打开或跳过。在Windbg中,使用sxe eh
打开C++异常是完全没有。在Windbg中,当抛出特定的C++异常时,我可以跳过这个过程吗?
有什么办法可以跳过特定C++异常类型的突破吗?相反,是否有办法打破特定类型?
注意:这个答案是32位具体的,因为我还没有做过很多的64位调试。我不知道64位是多少。
假设下面的代码:
class foo_exception : public std::exception {};
void throw_foo()
{
throw foo_exception();
}
而且让我们假设你已经打开突破在第一次机会异常的C++异常:sxe eh
现在,当调试器中断,你的异常记录将在堆栈的顶部。其中,这个东西是坐在
0:000> .exr @esp
ExceptionAddress: 751dc42d (KERNELBASE!RaiseException+0x00000058)
ExceptionCode: e06d7363 (C++ EH exception)
ExceptionFlags: 00000001
NumberParameters: 3
Parameter[0]: 19930520
Parameter[1]: 0027f770
Parameter[2]: 0122ada0
pExceptionObject: 0027f770
_s_ThrowInfo : 0122ada0
Type : class foo_exception
Type : class std::exception
看看当前的堆栈,你可以看到::所以,如果你只是想看看是什么类型,可以显示异常记录信息
0027f6c4 e06d7363 0027f6c8 00000001 0027f6cc 00000000 0027f6d0 751dc42d KERNELBASE!RaiseException+0x58 0027f6d4 00000003 0027f6d8 19930520 0027f6dc 0027f770 0027f6e0 0122ada0 langD!_TI2?AVfoo_exception ...
所以在这个例子中,异常本身就是0027f770
,正如你可以从旁边的pExceptionObject
看到的那样。您可以在0027f6dc
的堆栈上看到该值,或者从堆栈的顶部偏移0x18,因此可以看到@esp+18
。让我们看看调试器告诉我们关于该位置的内容。
0:000> dpp @esp+18 L1
0027f6dc 0027f770 01225ffc langD!foo_exception::`vftable'
该命令表示:起始于@esp+18
,d UMP一个p ointer尺寸值,然后DEREF发现那里作为p ointer,也和写任何符号的名称的值匹配那个第二个地址。在这种情况下,它找到了foo_exception
类的vtable。这告诉我们地址0027f770
上的对象是foo_exception
。我们可以使用这些信息为条件断点创建一个表达式。
我们需要一种方式来获得直接的虚函数表的地址,看起来像这样:
@!"langD!foo_exception::`vftable'"
我们必须引用它,因为反勾和apostraphe的。我们还需要拉所需的堆栈值:
poi(poi(@esp+18))
的poi
操作需要一个地址,并返回存储有一个指针大小的值。第一次评估将堆栈地址转换为对象地址,第二次评估将对象地址转换为我们需要比较的vtable地址。整个条件是这样的:
@!"langD!foo_exception::`vftable'" == poi(poi(@esp+18))
现在,我们就可以知道它是一个foo_exception
,我们可以对它们跳过打破了由设置命令时自动运行的C++异常的中断调试:
sxe -c".if (@!\"langD!foo_exception::`vftable'\" == poi(poi(@esp+18))) {gc}" eh
翻译:
foo_exception
虚函数表的地址对象的虚函数表的地址在@esp+18
gc
命令,继续当这个命令达到如果你想只的foo_exception
打破,条件改变从==
到!=
。
需要注意的是,有时会将异常作为指针而不是按值引发,这意味着您需要围绕表达式的@esp
部分再添加一个poi()
。您可以知道,因为当您使用.exr
转储异常记录时,Type
将为class foo_expression *
。这完全依赖于引发异常的代码,而不是异常类型本身,因此您可能需要根据情况调整.if
-条件。
最后,如果你想打破或跳过几种异常类型,它是可行的。我建议writing a script与链接.if
,.elsif
命令并将sxe
自动命令设置为$$><path\to\script
。在一条生产线上进行大量的if-condition链接可能会非常难以阅读和正确使用,尤其是在额外逃脱的情况下。脚本不需要额外的转义。这里有一个小例子:
.if (@!"langD!foo_exception::`vftable'" == poi(poi(@esp+0x18)))
{
$$ skip foo_exceptions
gc
}
.elsif (@!"langD!bar_exception::`vftable'" == poi(poi(@esp+0x18)))
{
$$ dump the exception to see the error message, then continue running
dt poi(@esp+18) langD!bar_exception
gc
}
.elsif (@!"langD!baz_exception::`vftable'" == poi(poi(@esp+0x18)))
{
$$ show the top 10 frames of the stack and then break (because we don't `gc`)
kc 10
}
(注:WinDBG的会抱怨一个脚本错误,每当这个运行,因为它不喜欢gc
命令后面加上任何东西,但它仍然运行良好。)
哇。真是一团糟。 :-)似乎VS调试器对于某些事情是有益的。 – 2014-10-10 18:56:23
是的,手动操作有点复杂,但至少有这样的能力存在。如果Visual Studio尚未拥有此版本,那么它甚至可以自己添加它吗?另外,有人可以在这里获取信息并将其打包到Windbg扩展中,这与Visual Studio一样易于使用。 – 2014-10-12 01:38:35