在Windows Vista中,在指定的Win32最后一个错误值中断

通常,您可能希望在调试器中跟踪的一类问题(除了崩溃)是某个特定函数以某种方式失败。在大多数Win32函数的情况下,通常会得到某种(希望是有意义的)最后的错误代码。有时,您可能需要知道返回错误的原因或错误的来源(在最后一个错误值通过几个函数向上传播的情况下)。
一种可能的方法是使用条件断点,但是SetLastError路径通常会被命中,因此这在性能方面经常是有问题的,即使在本地计算机上的用户模式调试中也是如此。
在Windows Vista上,NTDLL内部有一个未记录的钩子(它现在负责set last error背后的大部分逻辑),允许您配置一个程序,以便在将特定错误代码设置为最后一个错误时进入调试器。这是Vista的新功能,由于没有文档记录(至少在我能看到的任何地方都没有),它可能不会无限期地存在。

不过,现在可以设置ntdll!g_dwLastErrorToBreakOn设置为非零值(通过调试器中的ed命令),以便在NTDLL看到设置了最后一个错误值时要求它执行断点。显然,这不会直接捕获修改TEB中字段的内容,但是使用setlastererror或RtlSetLastWin32Error的任何内容都将根据该值进行检查(在debuggee的上下文中)。
例如,如果要求NTDLL在错误5(错误访问被拒绝)时中断,然后尝试打开您无权访问的文件或目录,您可能会看到类似的情况:

0:002> ed ntdll!g_dwLastErrorToBreakOn 5
0:002> g

[...] Perform an operation to cause ERROR_ACCESS_DENIED

(1864.2774): Break instruction exception
  - code 80000003 (first chance)
ntdll!DbgBreakPoint:
00000000`76d6fdf0 cc              int     3
0:004> k
Call Site
ntdll!DbgBreakPoint
ntdll! ?? ::FNODOBFM::`string'+0x377b
kernel32!BaseSetLastNTError+0x16
kernel32!CreateFileW+0x325
SHELL32!CEnumFiles::InitAndFindFirst+0x7a
SHELL32!CEnumFiles::InitAndFindFirstRetry+0x3e
SHELL32!CFileSysEnum::_InitFindDataEnum+0x5e
SHELL32!CFileSysEnum::Init+0x135
SHELL32!CFSFolder::EnumObjects+0xd3
SHELL32!_GetEnumerator+0x189
SHELL32!CEnumThread::_RunEnum+0x6d
SHELL32!CEnumThread::s_EnumThreadProc+0x13
SHLWAPI!WrapperThreadProc+0xfc
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d

(由于二进制文件被重新组织为函数块,调试器对NTDLL中的符号名有点困惑,但是“ntdll! ?? ::FNODOBFM::`string’+0x377b“是ntdll的一部分!rtlsetlastwin32错误。)

有时,在程序中添加类似这样的“调试器旋钮”可能会很有用,这些旋钮可用于启用特殊的诊断行为,这些行为在调试某些东西时可能很有用。其他几个组件提供了这样的选项;例如,NTDLL中有一个名为ntdll!ShowSnaps的全局变量。当加载程序解析导入的模块和符号时,可以将ShowSnaps设置为1以启用有关符号导入解析过程的大量调试打印spew。
(顺便说一下,调试器可设置全局变量,如ntdll!ShowSnaps是在发布版本中使用调试打印的正确方法的一个很好的例子,尽管确实有许多其他的好方法可以这样做。)

posted on 2020-01-06 15:39  活着的虫子  阅读(408)  评论(0编辑  收藏  举报

导航