76.windbg-高效排错的一个小示例(windbg保留的上下文优化后的结果)

由于程序优化,所以不要相信windbg为你自动分析的结果,特别在release下的dump,你会发现,OH,这个自动分析出来的this居然为0,呵呵,自动分析是直接取ECX的,那如果ECX这时都被优化了,当然是错的

所以看到高效排错的一个小示例,摘抄过来:

0:000> .frame 1 
01 0012e2b4 7c16fca0 CuExE!CMainFrame::OnBrokerOutQMsg+0x9d 
[c:\Customer_Code_Folder\CuExE\mainfrm.cpp @ 588] 
.frame 1切换到当前的设备上下文

0:000> x 
@edx this = 0x00000000 
021050cc wParam = 1 
021050d0 lParam = 0 
021050ac WinMsg = struct tagMSG 
x自动分析成员变量。OH,这个自动分析出来的this居然为0!

理智一点,CMainFrame的this指针为0是不可能的,否则程序在调用PeekMessage以前就已经崩掉了。这里很可能是因为编译器优化导致的。看看编译器对OnBrokerOutQMsg  函数是否有什么优化:

0:000> kvn 
# ChildEBP RetAddr Args to Child 
00 0012e284 0046d4ed 021050c8 055232c8 0012e348 CUSTOMERFactory!CClientMessage::isError (FPO: [0,0,0]) 
(CONV: thiscall) [c:\Customer_Code_Folder\CUSTOMERfactory\clientmessage.cpp @ 179] 
01 0012e2b4 7c16fca0 055a0fd0 055232c8 021050c8 CuExE!CMainFrame::OnBrokerOutQMsg+0x9d (FPO: [Uses EBP] 
[2,7,0]) (CONV: thiscall) [c:\Customer_Code_Folder\CuExE\mainfrm.cpp @ 588] 
   这里果然用了FPO优化。正常情况下,函数调用需要创建新的stack frame,函数返回的时候恢复stack frame。EBP寄存器是用来保存callee函数stack起始地址的。当FPO优化激活后,编译器可以优化掉stack frame的创建和恢复,把callee的EBP寄存器节约下来保存一些中间变量

https://blogs.msdn.microsoft.com/oldnewthing/20040116-00/?p=41023

所以,这里的this 指针可能是保存在EBP寄存器上的,所以用dt 直接看this 看不到准确的信息。看看汇编来研究下: 

0:000> uf CuExE!CMainFrame::OnBrokerOutQMsg 
CuExE!CMainFrame::OnBrokerOutQMsg [c:\Customer_Code_Folder\CuExE\mainfrm.cpp @ 541]: 
541 0046d450 83ec1c sub esp,1Ch 
541 0046d453 53 push ebx 
541 0046d454 55 push ebp 
541 0046d455 56 push esi 
541 0046d456 57 push edi 
541 0046d457 8be9 mov ebp,ecx 
552 0046d459 ff1524937d00 call dword ptr [CuExE!_imp_?instanceCMessageHandlerFactorySSAPAV1XZ 
(007d9324)] 
。。。。。。
CuExE!CMainFrame::OnBrokerOutQMsg+0x46 [c:\Customer_Code_Folder\CuExE\mainfrm.cpp @ 563]: 
563 0046d496 8b16 mov edx,dword ptr [esi] 
563 0046d498 ff5220 call dword ptr [edx+20h] 
564 0046d49b eb05 jmp CuExE!CMainFrame::OnBrokerOutQMsg+0x52 (0046d4a2) 
CuExE!CMainFrame::OnBrokerOutQMsg+0x4d [c:\Customer_Code_Folder\CuExE\mainfrm.cpp @ 565]: 
565 0046d49d 8b06 mov eax,dword ptr [esi] 
565 0046d49f ff501c call dword ptr [eax+1Ch] 
Draft version..Microsoft Internal  166
CuExE!CMainFrame::OnBrokerOutQMsg+0x52 [c:\Customer_Code_Folder\CuExE\mainfrm.cpp @ 571]: 
571 0046d4a2 8b4d20 mov ecx,dword ptr [ebp+20h] 
571 0046d4a5 6a01 push 1 
571 0046d4a7 682c050000 push 52Ch 
571 0046d4ac 682c050000 push 52Ch 
571 0046d4b1 51 push ecx 
571 0046d4b2 8d542420 lea edx,[esp+20h] 
571 0046d4b6 52 push edx 
571 0046d4b7 ff1508b47d00 call dword ptr [CuExE!_imp__PeekMessageA (007db408)] 
571 0046d4bd 85c0 test eax,eax 
571 0046d4bf 745e je CuExE!CMainFrame::OnBrokerOutQMsg+0xcf (0046d51f) 
果然,函数已开始就把ecx的值保存到EBP中。在调用PeekMessage以前,传入的第二个参数m_hWnd来自ecx,而ecx来自ebp+20h。因为m_hWnd保存在this指针的一个偏移量上,所以这就可以确认EBP就是CMainFrame的this 指针。

继续检查汇编发现,在调用PeekMessage到发生错误中间的汇编代码中,EBP都没有被修改过,所以直接检查EBP就可以看CMainFrame的详细信息: 

这里有两点:1.汇编分析得出EBP就是CMainFrame的this 指针 2.EBP在后续没有被修 

0:000> r ebp 
ebp=021050c8 
0:000> dt 021050c8 CMainFrame 
+0x000 __VFN_table : 0x7c164734 
=00400000 classCObject : CRuntimeClass 

为了进一步确保EBP就是CMainFrame,看看VTable是否指向MFC上对应的虚函数: 

0:000> dds 0x7c164734 
7c164734 7c157964 MFC71!CWnd::GetRuntimeClass 
7c164738 7c157291 MFC71!CWnd::`vector deleting destructor' 

对了,果然没错。









猜你喜欢

转载自blog.csdn.net/hgy413/article/details/72867381