【问题标题】:Tracing VB6 runtime error by debugging it通过调试跟踪 VB6 运行时错误
【发布时间】:2023-03-28 08:41:01
【问题描述】:

我开始使用 WinDbg 调试 VB6 运行时错误,但不能真正让它工作,我只需要知道错误的来源(错误来自的过程)。

我创建了一个小应用程序进行测试,并让它抛出这样的溢出运行时错误:

Private Sub Command1_Click()
    Dim a As Byte
    a = 1000
End Sub

我编译它时选中了“创建符号调试信息”选项以创建 PDB 文件。

然后我将应用程序附加到 WinDbg 并单击按钮以引发错误,但是当我检查调用堆栈时,我没有找到 Command1_Click 过程的任何痕迹。我得到的只是以下内容:

0:001> ~* k

   0  Id: 56c.173c Suspend: 1 Teb: 7ffde000 Unfrozen
ChildEBP RetAddr  
0012ea08 7e419418 ntdll!KiFastSystemCallRet
0012ea40 7e4249c4 USER32!NtUserWaitMessage+0xc
0012ea68 7e43a956 USER32!InternalDialogBox+0xd0
0012ed28 7e43a2bc USER32!SoftModalMessageBox+0x938
0012ee78 7e43a10b USER32!MessageBoxWorker+0x2ba
0012eee4 729af829 USER32!MessageBoxIndirectA+0xb8
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ef24 729af6a5 MSVBVM60!IID_IVbaHost+0x411e9
0012ef4c 729af9a0 MSVBVM60!IID_IVbaHost+0x41065
0012ef7c 729a3d68 MSVBVM60!IID_IVbaHost+0x41360
0012efe0 729a3db6 MSVBVM60!IID_IVbaHost+0x35728
0012f000 72a0c411 MSVBVM60!IID_IVbaHost+0x35776
0012f01c 72a0c6f3 MSVBVM60!_vbaOnGoCheck+0xba
0012f05c 7c9032a8 MSVBVM60!EbGetErrorInfo+0x115
0012f080 7c90327a ntdll!ExecuteHandler2+0x26
0012f130 7c90e46a ntdll!ExecuteHandler+0x24
0012f130 7c812aeb ntdll!KiUserExceptionDispatcher+0xe
0012f484 72a10dcf kernel32!RaiseException+0x53
0012f4a4 72a0e228 MSVBVM60!EbGetHandleOfExecutingProject+0x22b3
0012f4b8 72a0e28c MSVBVM60!rtcDoEvents+0x131
0012f4c8 72a219ee MSVBVM60!rtcDoEvents+0x195
0012f644 72992667 MSVBVM60!_vbaUI1I2+0x12
0012f668 729f4657 MSVBVM60!IID_IVbaHost+0x24027
0012f698 7299ce49 MSVBVM60!DllCanUnloadNow+0x149a5
0012f6c0 7299f97d MSVBVM60!IID_IVbaHost+0x2e809
0012f71c 7299e22c MSVBVM60!IID_IVbaHost+0x3133d
0012f740 7299dc6d MSVBVM60!IID_IVbaHost+0x2fbec
0012f7ac 729c223a MSVBVM60!IID_IVbaHost+0x2f62d
0012f92c 7299ce49 MSVBVM60!IID_IVbaHost+0x53bfa
0012f954 7299f97d MSVBVM60!IID_IVbaHost+0x2e809
0012f9b0 7e418734 MSVBVM60!IID_IVbaHost+0x3133d
0012f9dc 7e418816 USER32!InternalCallWinProc+0x28
0012fa44 7e42927b USER32!UserCallWinProcCheckWow+0x150
0012fa80 7e4292e3 USER32!SendMessageWorker+0x4a5
0012faa0 7e44ff7d USER32!SendMessageW+0x7f
0012fab8 7e4465d2 USER32!xxxButtonNotifyParent+0x41
0012fad4 7e425e94 USER32!xxxBNReleaseCapture+0xf8
0012fb58 7e43b082 USER32!ButtonWndProcWorker+0x6df
0012fb78 7e418734 USER32!ButtonWndProcA+0x5d
0012fba4 7e418816 USER32!InternalCallWinProc+0x28
0012fc0c 7e42a013 USER32!UserCallWinProcCheckWow+0x150
0012fc3c 7e42a998 USER32!CallWindowProcAorW+0x98
0012fc5c 7299d082 USER32!CallWindowProcA+0x1b
0012fcc8 729f492d MSVBVM60!IID_IVbaHost+0x2ea42
0012fcf0 7299ce49 MSVBVM60!DllCanUnloadNow+0x14c7b
0012fd18 7299f97d MSVBVM60!IID_IVbaHost+0x2e809
0012fd74 7e418734 MSVBVM60!IID_IVbaHost+0x3133d
0012fda0 7e418816 USER32!InternalCallWinProc+0x28
0012fe08 7e4189cd USER32!UserCallWinProcCheckWow+0x150
0012fe68 7e4196c7 USER32!DispatchMessageWorker+0x306
0012fe78 7294a6c8 USER32!DispatchMessageA+0xf
0012feb8 7294a63f MSVBVM60!_vbaStrToAnsi+0x2f1
0012fefc 7294a51d MSVBVM60!_vbaStrToAnsi+0x268
0012ff18 7294a4e8 MSVBVM60!_vbaStrToAnsi+0x146
0012ff3c 72943644 MSVBVM60!_vbaStrToAnsi+0x111
0012ffb8 00401246 MSVBVM60!ThunRTMain+0xa0
0012fff0 00000000 Project1!__vbaS+0xa

#  1  Id: 56c.10a8 Suspend: 1 Teb: 7ffdd000 Unfrozen
ChildEBP RetAddr  
00f0ffc8 7c950010 ntdll!DbgBreakPoint
00f0fff4 00000000 ntdll!DbgUiRemoteBreakin+0x2d

请注意,WinDbg 不会因错误而中断,因此我在收到错误消息后并在单击“确定”之前手动中断它,因为如果单击“确定”,应用程序将关闭。

我是 WinDbg 的新手,但我读到它应该会因错误而中断,但事实并非如此,我得到的只是“第一次机会异常”,当我单击“确定”时我没有得到“第二次机会异常”让它崩溃。

我认为 WinDbg 以某种方式认为 VB6 处理了错误,即使它没有处理它。我在这里缺少什么吗?如何获取调用过程的错误痕迹?

【问题讨论】:

  • 这是一个微软人的2006 blog post,关于在 VB6 中使用 Windbg。我能问你为什么必须使用WinDbg吗?其他选项为VB6自带的调试器或later versions of Visual Studio
  • 我已经阅读了该内容和其他内容,但它对我的问题没有帮助,我不必使用 WinDbg,我什至尝试过 msvc++ 调试器但同样的问题,我需要的是能够当客户调用并抱怨我的应用程序中未处理的错误并且我无法从他那里获得足够的信息来知道错误可能在哪里时检测错误的来源(过程)。所以我打算让应用程序采取出现错误时转储图像并使用调试器进行分析。

标签: debugging vb6 runtime-error windbg callstack


【解决方案1】:

在 VB6 中运行应用程序本身会导致 VB6 运行时错误 6,它代表“溢出”。

第一次机会例外

在WinDbg(6.2.9200)下运行应用,我首先看到了一个

(6cc.6d4): Unknown exception - code c000008f (first chance)

这可能是最常见的 VB6 异常。默认情况下,WinDbg 不会中断这种类型的第一次机会异常。如果你想让它这样做,你需要明确地启用它

sxe c000008f

二次机会异常

第一次机会异常后跟一个消息框

此消息框是异常必须已被捕获的第一个指示符,否则之后会立即出现第二次机会异常。

事实上,VB6 创建了一个异常处理程序:

0:000> u
Project1!Form1::Command_Click [Form1 @ 5]:
004019a0 55              push    ebp
004019a1 8bec            mov     ebp,esp
004019a3 83ec0c          sub     esp,0Ch
004019a6 68b6104000      push    offset Project1!__vbaExceptHandler (004010b6)
004019ab 64a100000000    mov     eax,dword ptr fs:[00000000h]
...
004019df b9e8030000      mov     ecx,3E8h
004019e4 ff1530104000    call    dword ptr [Project1!_imp___vbaUI1I2 (00401030)]
...

004019a6,您会看到VB6 正在使用__vbaExceptHandlermov dword ptr fs:[0],esp 是 try/catch 块的开始。在004019df,它将 0x3E8(十六进制)或 1000(十进制)存储到 ECX。这就是您的代码所在的位置。

确认消息后,进程终止,但不是由于第二次机会异常,而是由于ExitProcess() 调用。这就是 VB6“处理”异常的方式。

0:000> k
ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ff28 7c81bfb6 ntdll!KiFastSystemCallRet
0012ff3c 73393657 kernel32!ExitProcess+0x14
0012ffb8 0040113a MSVBVM60!ThunRTMain+0xb3
0012fff0 00000000 image00400000+0x113a

符号

要查看符号是否已正确加载,请键入 lm(列出模块)。它应该是这样的:

0:000> lm
start    end        module name
00400000 00404000   Project1   (private pdb symbols)  C:\Programme\Microsoft Visual Studio\VB98\Project1.pdb

如果您看到(deferred),则符号尚未加载。输入 ld Project1 以加载它们。

使用符号可以设置断点,首先找到x的方法,然后设置断点bp

0:000> x Project1!Form1*
004019a0          Project1!Form1::Command_Click (void)

0:000> bp Project1!Form1::Command_Click

0:000> bl
 0 e 004019a0     0001 (0001)  0:**** Project1!Form1::Command_Click

[...]

Breakpoint 0 hit
eax=004016b4 ebx=00000001 ecx=00000000 edx=733a3dd8 esi=0012f5e4 edi=0012f514
eip=004019a0 esp=0012f50c ebp=0012f514 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
Project1!Form1::Command_Click:
004019a0 55              push    ebp

0:000> k
ChildEBP RetAddr  
0012f508 733e1ce3 Project1!Form1::Command_Click [Form1 @ 5]
WARNING: Stack unwind information not available. Following frames may be wrong.
0012f524 733e1fe4 MSVBVM60!IID_IVbaHost+0x23703
...

但是断点不是你想要的......

为什么方法不在调用栈中

不幸的是,启用第一次机会异常和加载符号仍然无助于在引发异常时查看调用堆栈上的方法。它在DoEvents()的某个地方:

0:000> .exr -1
ExceptionAddress: 7c812fd3 (kernel32!RaiseException+0x00000052)
   ExceptionCode: c000008f
  ExceptionFlags: 00000001
NumberParameters: 2
   Parameter[0]: deadcafe
   Parameter[1]: deadcafe

0:000> k
ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
0012f484 73460c29 kernel32!RaiseException+0x52
0012f4a4 7345e082 MSVBVM60!EbGetHandleOfExecutingProject+0x22b3
0012f4b8 7345e0e6 MSVBVM60!rtcDoEvents+0x131
...

您是否注意到有关堆栈展开信息的警告?让我们手动查看堆栈:

0:000> dps @ebp
0012f484  0012f4a4
0012f488  73460c29 MSVBVM60!EbGetHandleOfExecutingProject+0x22b3
0012f48c  c000008f
0012f490  00000001
0012f494  00000002
0012f498  0012f49c
0012f49c  deadcafe
0012f4a0  deadcafe
...
0012f4d8  004019e7 Project1!Form1::Command_Click+0x53 [Form1 @ 7]
0012f4dc  0012f514
0012f4e0  0012f5e4
0012f4e4  00000001
0012f4e8  00000000
0012f4ec  00000000
0012f4f0  00000000
0012f4f4  0012fa34
0012f4f8  004010b6 Project1!__vbaExceptHandler

我猜你对此无能为力。 VB6 不擅长创建符号,也不擅长维护良好的调用堆栈。

【讨论】:

  • 这是福是祸,在情人眼里。作为一名开发人员,我很欣赏 VB6 所做的一切(有意或无意地)有助于击败破解和其他逆向工程工作。如果您寻求软目标,请查看 .Net 代码。
  • @Bob77:有趣的观点。您在 x64 上尝试过 C++ 吗?这是一个全新的调用约定。事物在寄存器中传递,并且可以由编译器进行优化。很难把事情做好。
  • 很好解释..但是你能告诉我命令dps @ebp到底是做什么的吗?以及它如何与转储图像一起使用..或者如果不是..有没有办法从转储图像中获取我的应用程序(不是 api)调用的最后一个方法?
  • @MohamedGharib: dps 是“dump pointer-sized data and resolve s的命令符号”。 @ebp 指的是指向堆栈的寄存器(“extended base pointer”)
  • @ThomasWaller:感谢您的解释。我在一个倾倒的图像上尝试过但没有工作,所以我放弃了 WinDbg 的想法,尽管我真的希望它工作,现在我正在工作在 vb6 的某种 CallStack 上帮助我调试未处理的错误。
猜你喜欢
  • 2010-11-09
  • 2010-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多