Ms word WinDbg:如何从核心转储中获取Word对话框中显示的消息?
我正在用C#应用程序中的MS Word实现办公自动化,我发现Word有时会挂起。我无法在开发人员环境中重现挂起,因此我希望通过获取一个核心转储,然后使用WinDbg对其进行分析,能够准确地诊断Word挂起的原因 如果我运行kb,我会得到这个堆栈跟踪(我已经删除了警告之后的所有内容,因为它可能与此无关): 这似乎表明Word正在挂起,因为它正在显示一个对话框如何获取该对话框的内容? 如果我查看地址0089aa30处的内存,我会看到:Ms word WinDbg:如何从核心转储中获取Word对话框中显示的消息?,ms-word,office-interop,windbg,Ms Word,Office Interop,Windbg,我正在用C#应用程序中的MS Word实现办公自动化,我发现Word有时会挂起。我无法在开发人员环境中重现挂起,因此我希望通过获取一个核心转储,然后使用WinDbg对其进行分析,能够准确地诊断Word挂起的原因 如果我运行kb,我会得到这个堆栈跟踪(我已经删除了警告之后的所有内容,因为它可能与此无关): 这似乎表明Word正在挂起,因为它正在显示一个对话框如何获取该对话框的内容? 如果我查看地址0089aa30处的内存,我会看到: ........................3.... .M
........................3....
.M.i.c.r.o.s.o.f.t. .W.o.r.d.
........T.a.h.o.m.a..........
....P#.!.*...........O.K.....
...........PW.!.*...........&
.H.e.l.p..................P..
.............................
....P+...r.......M.S.O.U.N.I.
S.T.A.T...W.o.r.d. .c.a.n.n.o
.t. .o.p.e.n. .t.h.e. .e.x.i.
s.t.i.n.g. .f.i.l.e..... .(.N
.o.r.m.a.l.)................@
..+.........M.S.O.U.N.I.S.T.A
.T...2.0.0.5.2.1.............
所以对我来说,对话框中的消息是“Word无法打开现有文件(正常)”
我走对了吗?我看到的是正确的记忆吗
有没有办法得到消息的确切存储地址?(我觉得我是在猜测,因为上面的消息正好在内存中接近DialogBoxIndirectParam的一个参数。)我已经查看了MSDN文档,希望能够准确地找出我应该在内存中看到对话框消息的位置,但没走多远
编辑:在看到blabb(绝对难以置信)的答案后,我尝试在WinDbg中为我的MS Word core转储执行相同的步骤。以下是输出:
0:000> ub 762ed98a
user32!DialogBoxIndirectParamAorW+0x1f:
762ed972 83c801 or eax,1
762ed975 50 push eax
762ed976 ff7518 push dword ptr [ebp+18h]
762ed979 ff7514 push dword ptr [ebp+14h]
762ed97c ff7510 push dword ptr [ebp+10h]
762ed97f ff750c push dword ptr [ebp+0Ch]
762ed982 ff7508 push dword ptr [ebp+8]
762ed985 e809000000 call user32!InternalDialogBox (762ed993)
0:000> .frame /r 2
02 003bc9b4 762ed98a user32!InternalDialogBox+0xe5
eax=00000000 ebx=00037b72 ecx=00000000 edx=00000000 esi=003bc97c edi=003bc918
eip=762eda5c esp=003bc990 ebp=003bc9b4 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
user32!InternalDialogBox+0xe5:
762eda5c 5f pop edi
0:000> dc /c 1 003bc990 l8
003bc990 00047b12 .{..
003bc994 00037b72 r{..
003bc998 00000008 ....
003bc99c 00000000 ....
003bc9a0 00000000 ....
003bc9a4 00037b72 r{..
003bc9a8 003bcb98 ..;.
003bc9ac 00000000 ....
我知道我看到了错误的内存位(即,我传递给dc的地址不正确),但我不知道为什么。我使用“.frame/r 2”获取esp的地址,我做错了什么?InternalDialogBox Api包含六个参数
C:\>cdb -c ".fnent user32!InternalDialogbox;q" cdb | grep Params
Params: 0n6 (0x18 bytes)
您可以尝试通过对堆栈上的返回地址进行反向反汇编来破译此调用
0:000> kb 1
# ChildEBP RetAddr Args to Child
00 0017fad8 778be0d5 77860000 001ec4f0 00000000 USER32!InternalDialogBox
0:000> ub @$ra
USER32!SoftModalMessageBox+0x66d:
778be0b8 e87c48fdff call USER32!MessageBeep (77892939)
778be0bd 56 push esi
778be0be 53 push ebx
778be0bf 6848d68b77 push offset USER32!MB_DlgProc (778bd648)
778be0c4 ff75ac push dword ptr [ebp-54h]
778be0c7 ff75e4 push dword ptr [ebp-1Ch]
778be0ca ff35d0908c77 push dword ptr [USER32!hmodUser (778c90d0)]
778be0d0 e8a059fdff call USER32!InternalDialogBox (77893a75)
在崩溃转储中,可以用地址代替寄存器,也可以
do.frame/r{frame number}获取esp的地址
0:000> dc /c 1 @esp l8
0017fadc 778be0d5 ...w
0017fae0 77860000 ...w
0017fae4 001ec4f0 ....
0017fae8 00000000 ....
0017faec 778bd648 H..w
0017faf0 0017fcd8 ....
0017faf4 00000000 ....
0017faf8 00000001 ....
1) 第一个参数是全局变量hModUser
2) 第四个参数是记录在案的DialogProc回调
3) 第三个和第六个参数为空
第二个参数由一个数组组成,后跟一个结构,用于读取此可变大小数组格式的文档
第五个参数是MSGBOXPARAMS结构
示例转储,并对可变大小数组的转储进行解密,如下所示
0:000> db 1ec4f0 l f8
001ec4f0 c5 01 c8 80 00 00 00 00-02 00 1a 01 9b 00 a7 00 ................
001ec500 3e 00 00 00 00 00 54 00-68 00 69 00 73 00 20 00 >.....T.h.i.s. .
001ec510 69 00 73 00 20 00 4d 00-79 00 20 00 43 00 61 00 i.s. .M.y. .C.a.
001ec520 70 00 74 00 69 00 6f 00-6e 00 20 00 46 00 6f 00 p.t.i.o.n. .F.o.
001ec530 72 00 20 00 32 00 30 00-31 00 35 00 20 00 43 00 r. .2.0.1.5. .C.
001ec540 6f 00 6d 00 6d 00 75 00-6e 00 69 00 74 00 79 00 o.m.m.u.n.i.t.y.
001ec550 20 00 76 00 73 00 00 00-ff 7f 00 00 01 00 03 50 .v.s..........P
001ec560 00 00 00 00 71 00 2a 00-32 00 0e 00 01 00 ff ff ....q.*.2.......
001ec570 80 00 4f 00 4b 00 00 00-00 00 00 00 80 20 02 50 ..O.K........ .P
001ec580 00 00 00 00 07 00 0e 00-8c 00 09 00 ff ff ff ff ................
001ec590 82 00 54 00 68 00 69 00-73 00 20 00 69 00 73 00 ..T.h.i.s. .i.s.
001ec5a0 20 00 6d 00 79 00 20 00-66 00 69 00 72 00 73 00 .m.y. .f.i.r.s.
001ec5b0 74 00 20 00 54 00 65 00-73 00 74 00 20 00 77 00 t. .T.e.s.t. .w.
001ec5c0 69 00 74 00 68 00 20 00-32 00 30 00 31 00 35 00 i.t.h. .2.0.1.5.
001ec5d0 20 00 63 00 6f 00 6d 00-6d 00 75 00 6e 00 69 00 .c.o.m.m.u.n.i.
001ec5e0 74 00 79 00 00 00 00 00 t.y.....
0:000> dt ConsoleApplication1!DLGTEMPLATE 1ec4f0
+0x000 style : 0x80c801c5
+0x004 dwExtendedStyle : 0
+0x008 cdit : 2
+0x00a x : 0x11a
+0x00c y : 0x9b
+0x00e cx : 0xa7
+0x010 cy : 0x3e
0:000> du 1ec504
001ec504 ""
0:000> du 1ec506
001ec506 "This is My Caption For 2015 Comm"
001ec546 "unity vs"
0:000> dt ConsoleApplication1!DLGITEMTEMPLATE 1ec55c
+0x000 style : 0x50030001
+0x004 dwExtendedStyle : 0
+0x008 x : 0x71
+0x00a y : 0x2a
+0x00c cx : 0x32
+0x00e cy : 0xe
+0x010 id : 1
0:000> $$ 80 is a predfined button and the text is OK
0:000> dt ConsoleApplication1!DLGITEMTEMPLATE 1ec57c
+0x000 style : 0x50022080
+0x004 dwExtendedStyle : 0
+0x008 x : 7
+0x00a y : 0xe
+0x00c cx : 0x8c
+0x00e cy : 9
+0x010 id : 0xffff
0:000> $$ 82 is a predfined static text and the text is
0:000> du 1ec592
001ec592 "This is my first Test with 2015 "
001ec5d2 "community"
这是一个MSGBOXPARAMSW转储
0:000> dt ConsoleApplication1!MSGBOXPARAMSW 0017fcd8
+0x000 cbSize : 0x28
+0x004 hwndOwner : (null)
+0x008 hInstance : (null)
+0x00c lpszText : 0x01172150 "This is my first Test with 2015 community"
+0x010 lpszCaption : 0x011720f8 "This is My Caption For 2015 Community vs"
+0x014 dwStyle : 0
+0x018 lpszIcon : (null)
+0x01c dwContextHelpId : 0
+0x020 lpfnMsgBoxCallback : (null)
+0x024 dwLanguageId : 0
编辑
从taskmanager创建转储并将其加载
0:000> .shell -ci "version" grep DMP
Full memory user mini dump: C:\Users\HP\Desktop\cons.DMP
command line: 'windbg -z cons.DMP' Debugger Process 0x17CC
.shell: Process exited
0:000> .frame /c /r 04
04 0028f7fc 778be659 user32!SoftModalMessageBox+0x68a
eax=00000001 ebx=0028f958 ecx=0028f458 edx=77ad70f4 esi=005fab18 edi=00000001
eip=778be0d5 esp=0028f760 ebp=0028f7fc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
user32!SoftModalMessageBox+0x68a:
778be0d5 8945e8 mov dword ptr [ebp-18h],eax ss:0023:0028f7e4=00000000
checking esp you can use esp as it is instead of groping for address
0:000> dc /c 1 @esp l 8
0028f760 77860000 ...w
0028f764 002f63f0 .c/.
0028f768 00000000 ....
0028f76c 778bd648 H..w
0028f770 0028f958 X.(.
0028f774 00000000 ....
0028f778 00000001 ....
0028f77c 0028f958 X.(.
只是为了确保重置上下文记录
0:000> .cxr
Resetting default scope
0:000> dt *!*DLGTEMPLATE*
0:000> $$ no the thype info is not available
0:000> $$ we know ole32.dll has it
0:000> $$ so lets hack load it
转储堆栈(此处兴趣框架不在顶部)
覆盖感兴趣帧号的本地上下文
0:000> .shell -ci "version" grep DMP
Full memory user mini dump: C:\Users\HP\Desktop\cons.DMP
command line: 'windbg -z cons.DMP' Debugger Process 0x17CC
.shell: Process exited
0:000> .frame /c /r 04
04 0028f7fc 778be659 user32!SoftModalMessageBox+0x68a
eax=00000001 ebx=0028f958 ecx=0028f458 edx=77ad70f4 esi=005fab18 edi=00000001
eip=778be0d5 esp=0028f760 ebp=0028f7fc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
user32!SoftModalMessageBox+0x68a:
778be0d5 8945e8 mov dword ptr [ebp-18h],eax ss:0023:0028f7e4=00000000
checking esp you can use esp as it is instead of groping for address
0:000> dc /c 1 @esp l 8
0028f760 77860000 ...w
0028f764 002f63f0 .c/.
0028f768 00000000 ....
0028f76c 778bd648 H..w
0028f770 0028f958 X.(.
0028f774 00000000 ....
0028f778 00000001 ....
0028f77c 0028f958 X.(.
转储DLGTEMPLATE第二个参数
0:000> db 2f63f0 lf8
002f63f0 c5 01 c8 80 00 00 00 00-02 00 1a 01 9b 00 a7 00 ................
002f6400 3e 00 00 00 00 00 54 00-68 00 69 00 73 00 20 00 >.....T.h.i.s. .
002f6410 69 00 73 00 20 00 4d 00-79 00 20 00 43 00 61 00 i.s. .M.y. .C.a.
002f6420 70 00 74 00 69 00 6f 00-6e 00 20 00 46 00 6f 00 p.t.i.o.n. .F.o.
002f6430 72 00 20 00 32 00 30 00-31 00 35 00 20 00 43 00 r. .2.0.1.5. .C.
002f6440 6f 00 6d 00 6d 00 75 00-6e 00 69 00 74 00 79 00 o.m.m.u.n.i.t.y.
002f6450 20 00 76 00 73 00 00 00-ff 7f 00 00 01 00 03 50 .v.s..........P
002f6460 00 00 00 00 71 00 2a 00-32 00 0e 00 01 00 ff ff ....q.*.2.......
002f6470 80 00 4f 00 4b 00 00 00-00 00 00 00 80 20 02 50 ..O.K........ .P
002f6480 00 00 00 00 07 00 0e 00-8c 00 09 00 ff ff ff ff ................
002f6490 82 00 54 00 68 00 69 00-73 00 20 00 69 00 73 00 ..T.h.i.s. .i.s.
002f64a0 20 00 6d 00 79 00 20 00-66 00 69 00 72 00 73 00 .m.y. .f.i.r.s.
002f64b0 74 00 20 00 54 00 65 00-73 00 74 00 20 00 77 00 t. .T.e.s.t. .w.
002f64c0 69 00 74 00 68 00 20 00-32 00 30 00 31 00 35 00 i.t.h. .2.0.1.5.
002f64d0 20 00 63 00 6f 00 6d 00-6d 00 75 00 6e 00 69 00 .c.o.m.m.u.n.i.
002f64e0 74 00 79 00 00 00 00 00 t.y.....
typeinfo(你需要合适的私有pdb,或者将二进制文件编译到地址空间,或者将DLGTEMPLATE结构添加到官方pdb中,用于ms的user32(我想我已经有一篇文章在se中解释了这一点,但我似乎找不到)如果我在woodmann.com网站上找到它,我会稍后链接它,或者在谷歌搜索如何将typeinfo添加到pdb
0:000> dt cons!DLGTEMPLATE poi(@esp+4)
+0x000 style : 0x80c801c5
+0x004 dwExtendedStyle : 0
+0x008 cdit : 2
+0x00a x : 0n282
+0x00c y : 0n155
+0x00e cx : 0n167
+0x010 cy : 0n62
0:000> du poi(@esp+4)+16
002f6406 "This is My Caption For 2015 Comm"
002f6446 "unity vs"
编辑2这适用于转储模式下的实时会话,您需要修改pdb,因为您无法使用执行命令
0:000> r eip = 77b305a6
0:000> dt *!*DLGTEMPLATE*
ole32!LPDLGTEMPLATEA
ole32!LPDLGTEMPLATE
ole32!LPDLGTEMPLATEW
ole32!LPCDLGTEMPLATE
ole32!LPCDLGTEMPLATEA
ole32!LPCDLGTEMPLATEW
ole32!DLGTEMPLATE
ole32!DLGTEMPLATE
我们正在进行实时dbg会话,我们可以使用dmp模式下不可用的step命令
0:000> kb 5
# ChildEBP RetAddr Args to Child
00 0028f6fc 778766c9 7789382a 00000000 00000000 ntdll!KiFastSystemCallRet
01 0028f700 7789382a 00000000 00000000 00000000 user32!NtUserWaitMessage+0xc
02 0028f734 77893b27 00aa0350 00000000 00000000 user32!DialogBox2+0x207
03 0028f758 778be0d5 77860000 002f63f0 00000000 user32!InternalDialogBox+0xcb
04 0028f7fc 778be659 00000000 69d52104 69d52108 user32!SoftModalMessageBox+0x68a
0:000> .tlist -c -v
0n3324 Msgbox.exe
Session: 1 User: HP-PC\HP Command Line: Msgbox.exe
0:000> a 20000
00020000 push 20100
push 20100
00020005 call kernel32!LoadLibraryA
call kernel32!LoadLibraryA
0002000a
让我们搜索一些我们需要的类型信息
0:000> .cxr
Resetting default scope
0:000> dt *!*DLGTEMPLATE*
0:000> $$ no the thype info is not available
0:000> $$ we know ole32.dll has it
0:000> $$ so lets hack load it
分配一些内存
0:000> .dvalloc 1000
Allocated 1000 bytes starting at 00020000
save the current eip
0:000> ? @eip
Evaluate expression: 2008221094 = 77b305a6
0:000> ea 20100 "ole32.dll"
0:000> db 20100 l20
00020100 6f 6c 65 33 32 2e 64 6c-6c 00 00 00 00 00 00 00 ole32.dll.......
00020110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
在分配的内存中的某个地址嵌入modulename字符串
0:000> .dvalloc 1000
Allocated 1000 bytes starting at 00020000
save the current eip
0:000> ? @eip
Evaluate expression: 2008221094 = 77b305a6
0:000> ea 20100 "ole32.dll"
0:000> db 20100 l20
00020100 6f 6c 65 33 32 2e 64 6c-6c 00 00 00 00 00 00 00 ole32.dll.......
00020110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
组装一个LoadLibrary内联调用
0:000> kb 5
# ChildEBP RetAddr Args to Child
00 0028f6fc 778766c9 7789382a 00000000 00000000 ntdll!KiFastSystemCallRet
01 0028f700 7789382a 00000000 00000000 00000000 user32!NtUserWaitMessage+0xc
02 0028f734 77893b27 00aa0350 00000000 00000000 user32!DialogBox2+0x207
03 0028f758 778be0d5 77860000 002f63f0 00000000 user32!InternalDialogBox+0xcb
04 0028f7fc 778be659 00000000 69d52104 69d52108 user32!SoftModalMessageBox+0x68a
0:000> .tlist -c -v
0n3324 Msgbox.exe
Session: 1 User: HP-PC\HP Command Line: Msgbox.exe
0:000> a 20000
00020000 push 20100
push 20100
00020005 call kernel32!LoadLibraryA
call kernel32!LoadLibraryA
0002000a
将eip更改为迂回地址
0:000> r eip = 20000
single step to load a dll into the address space
0:000> p
eax=00000000 ebx=00000000 ecx=0026f80c edx=77ad70f4 esi=fffffffe edi=00000000
eip=00020005 esp=0026f824 ebp=0026f854 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
00020005 e852395877 call kernel32!LoadLibraryA (775a395c)
0:000> p
ModLoad: 77930000 77a8c000 C:\Windows\system32\ole32.dll
ModLoad: 75ee0000 75f81000 C:\Windows\system32\RPCRT4.dll
ModLoad: 77530000 7754f000 C:\Windows\system32\IMM32.DLL
ModLoad: 76030000 760fc000 C:\Windows\system32\MSCTF.dll
eax=77930000 ebx=00000000 ecx=77ae6570 edx=002b0174 esi=fffffffe edi=00000000
eip=0002000a esp=0026f828 ebp=0026f854 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
0002000a 0000 add byte ptr [eax],al ds:0023:77930000=4d
重置eip返回
0:000> r eip = 77b305a6
0:000> dt *!*DLGTEMPLATE*
ole32!LPDLGTEMPLATEA
ole32!LPDLGTEMPLATE
ole32!LPDLGTEMPLATEW
ole32!LPCDLGTEMPLATE
ole32!LPCDLGTEMPLATEA
ole32!LPCDLGTEMPLATEW
ole32!DLGTEMPLATE
ole32!DLGTEMPLATE
我们研究了typeinfo,宾果游戏我们有了它
0:000> r eip = 77b305a6
0:000> dt *!*DLGTEMPLATE*
ole32!LPDLGTEMPLATEA
ole32!LPDLGTEMPLATE
ole32!LPDLGTEMPLATEW
ole32!LPCDLGTEMPLATE
ole32!LPCDLGTEMPLATEA
ole32!LPCDLGTEMPLATEW
ole32!DLGTEMPLATE
ole32!DLGTEMPLATE
通过从Andrew Richards下载和使用,您的生活变得更加轻松。在Windbg中,将其加载为
.load\pde.dll
,并执行!dpx
。它将向您显示可以从堆栈中检索的所有字符串。由于您很可能已经在正确的方向上查看了错误,因此可能更容易执行e从系统内部下载procmon
。启动它,运行你的代码,停止它,然后转到tools-计数发生次数-列:result-count
,然后过滤错误或拒绝访问的值。谢谢Lieven。鉴于错误是间歇性的,我认为procmon可能具有挑战性。我不知道pde,所以我现在就尝试一下。我已经运行了!dpx使用PDE,在内存中看不到可以在Word对话框中显示的任何其他字符串。因此,感谢您,这似乎证实了我在找对了位置。不过,我还是希望有一种方法可以直接从堆栈跟踪中找到对话框的消息,而不必查看内存中的每个字符串。感觉如果我理解DialogBoxIndirectParam如何更好地工作,那么这应该是可能的…使用!pde.dpx-du和!pde.dpx-da转储unicode和ansi字符串首先,非常感谢您在这方面的所有努力,当我第一次读到这篇文章时,我完全被吓坏了。我试图完成与您相同的步骤,并编辑了问题以包括e我的WinDbg输出。对我来说,InternalDialogBox不在调用堆栈的顶部,所以我尝试按照你的建议使用.frame/r获取正确的esp地址。但不幸的是,dc的输出没有给我InternalDialogBox的参数。你能从我的编辑中看出我做错了什么吗?这取决于我用c添加了一些信息在task manager中创建一个正在运行的进程的转储并对其进行注释,看看这是否有帮助(我似乎无法将您以前的转储地址与最新的编辑进行对比(aslr不适用于转储iirc,但您的帖子中的地址不同是相同的转储还是不同的转储)。对不起,我切换了转储。:)我会看看我是否能找到原始的并更新问题。编辑顺便说一句,你可以跳过所有这些废话,只需使用kb:)a中的第二个参数