windbg入门之旅:(2)一个简单的integer divide-by-zero exception分析案例

本文通过一个具体的案例详细介绍了如何使用调试工具分析程序崩溃的原因,并逐步展示了如何定位到导致除零错误的具体代码行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

假设该程序名为mydebug,并且使用
cscript.exe adplus.vbs -crash -pn mydebug.exe -o c:\test\crashdump -quiet ,捕获了程序crash时刻的dump文件。下载该程序的crash dump,请点击此处

step1:打开该crash dump之后,立刻可以看到捕获的exception

Loading Dump File [D:\study\mydebug\crashdump\Crash_Mode__Date_11-12-2008__Time_15-30-1818\PID-3512__MYDEBUG.EXE__2nd_chance_IntegerDivide__full_0474_2008-11-12_15-30-35-715_0db8.dmp]
User Mini Dump File with Full Memory: Only application data is available

Comment: '2nd_chance_IntegerDivide_exception_in_MYDEBUG.EXE_running_on_T-RENHE-03'
Symbol search path is: srv*d:\symbolslocal*http://msdl.microsoft.com/download/symbols;D:\symbolslocal
Executable search path is:
Windows Server 2003 Version 3790 (Service Pack 2) UP Free x86 compatible
Product: Server, suite: Enterprise TerminalServer SingleUserTS
Debug session time: Wed Nov 12 15:30:35.000 2008 (GMT+8)
System Uptime: 0 days 6:30:46.419
Process Uptime: 0 days 0:02:46.000
...
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(db8.1678): Integer divide-by-zero - code c0000094 (first/second chance not available)
eax=00000004 ebx=7ffdd000 ecx=00434e40 edx=00000000 esi=00000000 edi=0012fe60
eip=00401490 esp=0012fe04 ebp=0012fe60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
*** WARNING: Unable to verify checksum for mydebug.exe
mydebug!CallFast+0x60:
00401490 f77df4          idiv    eax,dword ptr [ebp-0Ch] ss:0023:0012fe54=00000000

从以上这段信息可以看到,在指令寄存器eip所保存的00401490处的指令出现exception,并且产生异常的instruction和exception information已经列出。

一眼即可以看出,这个程序是因为除零而产生的exception,究竟这个exception如何产生的呢?

下面列出完整debug过程。

0:000> .logopen c:\crahdebug1.log //打开crashdebug1.log开始写入debug log
Opened log file 'c:\crashdebugl.log'
0:000> kb 
/*
该comand显示call stack内容,call stack保存的是exception发生时的瞬时状态,而stack top是出现exception的焦点所在,从以下的callstack可以看出,其调用嵌套过程如下:main-> CallWidhCDel-> CallWithStd ->CallFast (->表示调用关系),当在调用CallFast函数时,运行到00401490处的指令时发生exception.因此,下面需要step into至CallFast函数中,查看其指令。

*/
ChildEBP RetAddr  Args to Child             
0012fe60 00401402 00000006 0012ff18 00000000 mydebug!CallFast+0x60 [D:\study\mydebug\mydebug.cpp @ 58]
0012feb8 00401393 004310d8 00000004 00000006 mydebug!CallwithStd+0x42 [D:\study\mydebug\mydebug.cpp @ 44]
0012ff18 0040130a 0043101c 00000004 00000006 mydebug!CallWithCDecl+0x43 [D:\study\mydebug\mydebug.cpp @ 37]
0012ff80 00401969 00000001 00440e90 00440dc0 mydebug!main+0x9a [D:\study\mydebug\mydebug.cpp @ 28]
0012ffc0 77e6f23b 00000000 00000000 7ffdd000 mydebug!mainCRTStartup+0xe9 [crt0.c @ 206]
0012fff0 00000000 00401880 00000000 78746341 kernel32!BaseProcessStart+0x23

/*

从call stack我们可以清晰的看出函数调用链,在完成所有初始化process之后主函数调用CallWithCDecl,然后CallWithCDecl嵌套调用CallWithStd,然后CallWithStd又嵌套调用CallFast。每次嵌套调用的同时都伴随着一系列寄存器和,各参数的push stack。因此我们才可以顺着call stack看出清晰的调用链:)

下面对call stack中返回的值作简要说明:

ChildEBP:

该值保存的是对应的函数被调用时的EBP寄存器的值e.g.

0:000> dd 0012feb8 L4
0012feb8  0012ff18 00401393 004310d8 00000004
0:000> dd 0012ff80 L4
0012ff80  0012ffc0 00401969 00000001 00440e90

*/

0:000> kn
 # ChildEBP RetAddr 
00 0012fe60 00401402 mydebug!CallFast+0x60 [D:\study\mydebug\mydebug.cpp @ 58]
01 0012feb8 00401393 mydebug!CallwithStd+0x42 [D:\study\mydebug\mydebug.cpp @ 44]
02 0012ff18 0040130a mydebug!CallWithCDecl+0x43 [D:\study\mydebug\mydebug.cpp @ 37]
03 0012ff80 00401969 mydebug!main+0x9a [D:\study\mydebug\mydebug.cpp @ 28]
04 0012ffc0 77e6f23b mydebug!mainCRTStartup+0xe9 [crt0.c @ 206]
05 0012fff0 00000000 kernel32!BaseProcessStart+0x23
0:000> .frame 0  //选择stack top的frame
00 0012fe60 00401402 mydebug!CallFast+0x60 [D:\study\mydebug\mydebug.cpp @ 58]
0:000> dv //然后查看其参数情况
      szMessage = 0x004310d8 "Now in the callwithstd function, parameters are :"
              a = 4
              b = 6
       iDivider = 0 //从名字意义上判断不符合business logic.
        iResult = 0
0:000> r
eax=00000004 ebx=7ffdd000 ecx=00434e40 edx=00000000 esi=00000000 edi=0012fe60
eip=00401490 esp=0012fe04 ebp=0012fe60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mydebug!CallFast+0x60:
00401490 f77df4          idiv    eax,dword ptr [ebp-0Ch] ss:0023:0012fe54=00000000
0:000> u mydebug!CallFast //查看该function的指令 默认显示10行
mydebug!CallFast [D:\study\mydebug\mydebug.cpp @ 48]:
00401430 55              push    ebp
00401431 8bec            mov     ebp,esp
00401433 83ec50          sub     esp,50h
00401436 53              push    ebx
00401437 56              push    esi
00401438 57              push    edi
00401439 51              push    ecx
0040143a 8d7db0          lea     edi,[ebp-50h]
0:000> u mydebug!CallFast L40  //显示40个单元块的指令集合
/*

00401490处的指令肯定包含其中

*/
mydebug!CallFast [D:\study\mydebug\mydebug.cpp @ 48]:
00401430 55              push    ebp
00401431 8bec            mov     ebp,esp
00401433 83ec50          sub     esp,50h
00401436 53              push    ebx
00401437 56              push    esi
00401438 57              push    edi
00401439 51              push    ecx
0040143a 8d7db0          lea     edi,[ebp-50h]
0040143d b914000000      mov     ecx,14h
00401442 b8cccccccc      mov     eax,0CCCCCCCCh
00401447 f3ab            rep stos dword ptr es:[edi]
00401449 59              pop     ecx
0040144a 8955f8          mov     dword ptr [ebp-8],edx
0040144d 894dfc          mov     dword ptr [ebp-4],ecx
00401450 8b4508          mov     eax,dword ptr [ebp+8]
00401453 50              push    eax
00401454 8b4df8          mov     ecx,dword ptr [ebp-8]
00401457 51              push    ecx
00401458 8b55fc          mov     edx,dword ptr [ebp-4]
0040145b 52              push    edx
0040145c 6814114300      push    offset mydebug!`string' (00431114)
00401461 e89a030000      call    mydebug!printf (00401800)
00401466 83c410          add     esp,10h
00401469 c745f400000000  mov     dword ptr [ebp-0Ch],0
00401470 c745f000000000  mov     dword ptr [ebp-10h],0
00401477 837d0806        cmp     dword ptr [ebp+8],6
0040147b 7509            jne     mydebug!CallFast+0x56 (00401486)
0040147d c745f400000000  mov     dword ptr [ebp-0Ch],0
00401484 eb06            jmp     mydebug!CallFast+0x5c (0040148c)
00401486 8b4508          mov     eax,dword ptr [ebp+8]
00401489 8945f4          mov     dword ptr [ebp-0Ch],eax
0040148c 8b45f8          mov     eax,dword ptr [ebp-8]
0040148f 99              cdq
00401490 f77df4          idiv    eax,dword ptr [ebp-0Ch]  //这就是罪魁祸首
00401493 8945f0          mov     dword ptr [ebp-10h],eax
00401496 8b4df0          mov     ecx,dword ptr [ebp-10h]
00401499 51              push    ecx
0040149a 8b55f4          mov     edx,dword ptr [ebp-0Ch]
0040149d 52              push    edx
0040149e 8b45f8          mov     eax,dword ptr [ebp-8]
004014a1 50              push    eax
004014a2 6824114300      push    offset mydebug!`string' (00431124)
004014a7 e854030000      call    mydebug!printf (00401800)
004014ac 83c410          add     esp,10h
004014af b803000000      mov     eax,3
004014b4 5f              pop     edi
004014b5 5e              pop     esi
004014b6 5b              pop     ebx
004014b7 83c450          add     esp,50h
004014ba 3bec            cmp     ebp,esp
004014bc e87f010000      call    mydebug!_chkesp (00401640)
004014c1 8be5            mov     esp,ebp
004014c3 5d              pop     ebp
004014c4 c20400          ret     4
004014c7 cc              int     3
004014c8 cc              int     3
004014c9 cc              int     3
004014ca cc              int     3
004014cb cc              int     3
004014cc cc              int     3
004014cd cc              int     3
004014ce cc              int     3
004014cf cc              int     3
004014d0 cc              int     3
0:000> dd ebp-0Ch L1  //看看指定的内存单元地址表示的被除数的值,当然为0了
0012fe54  00000000
0:000> .logclose
Closing open log file c:\crashdebugl.log


至于这个0是怎么来的,你可以联系你的vendor,告诉他当前实参为dv中所指定的值时,在CallFast函数中会抛出divide by zero exception,然后让他们去进行修改business logic了,vendor在有源代码的情况下debug就轻松多了。

当然如果你有兴趣,可以去研究以上列出的出现exception的instruction set所表明的business logic。

 

 

转载于:https://www.cnblogs.com/Winston/archive/2008/11/13/1332888.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值