windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]

windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]栈溢出程序崩溃,调试显示栈溢出c0000409(Securitycheckfailureorstackbufferoverrun)系统在此应用程序中检测到基于堆栈的缓冲区溢出

在开发中遇到用户的反馈的崩溃,显示的栈溢出导致,通过查看资料终于找到了相关的发生问题原因。本文会介绍

1、栈溢出的排查过程,可能引起栈溢出原因

2、遇到栈不正确情况的处理过程

3、运气也是调试的一部分,哈哈

 

问题描述

程序崩溃,调试显示栈溢出

c0000409 (Security check failure or stack buffer overrun)

系统在此应用程序中检测到基于堆栈的缓冲区溢出。此超限可能会允许恶意用户控制此应用程序。

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 08f276bd (nvoglv32!vk_optimusGetInstanceProcAddr+0x000c549d)
   ExceptionCode: c0000409 (Security check failure or stack buffer overrun)
  ExceptionFlags: 00000001
NumberParameters: 1
   Parameter[0]: 00000007


EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 08f276bd (nvoglv32!vk_optimusGetInstanceProcAddr+0x000c549d)
   ExceptionCode: c0000409 (Security check failure or stack buffer overrun)
  ExceptionFlags: 00000001
NumberParameters: 1
   Parameter[0]: 00000007
Subcode: 0x7 FAST_FAIL_FATAL_APP_EXIT 

PROCESS_NAME:  lec_teacher.exe

ERROR_CODE: (NTSTATUS) 0xc0000409 - <Unable to get error code text>

EXCEPTION_CODE_STR:  c0000409

EXCEPTION_PARAMETER1:  00000007

STACK_TEXT:  
WARNING: Stack unwind information not available. Following frames may be wrong.
0813ec98 08ba784f     6e760038 00000000 00000000 nvoglv32!vk_optimusGetInstanceProcAddr+0xc549d
0813ecec 08ba79b0     0c870048 0c870048 00000000 nvoglv32!DrvPresentBuffers+0x2971f
0813ed34 08c830d8     0c870048 00000045 0c870048 nvoglv32!DrvPresentBuffers+0x29880
0813ed50 08c8853a     0000ffff 0c870048 0000005b nvoglv32!DrvPresentBuffers+0x104fa8
0813ed70 08c7739d     0c870048 00000045 0c870048 nvoglv32!DrvPresentBuffers+0x10a40a
0813f030 08c77dbc     01870048 0813fba0 0813f200 nvoglv32!DrvPresentBuffers+0xf926d
0813f1bc 081f85f2     0013f601 0c870048 0813f6a4 nvoglv32!DrvPresentBuffers+0xf9c8c
0813f218 08b56073     00000001 00000001 00000001 nvoglv32+0xb85f2
0813f248 08c5b992     16eac138 0813f6a4 0c870048 nvoglv32!DrvValidateVersion+0x5e93
0813f688 0820ef6d     0c870048 0813f6a4 00000002 nvoglv32!DrvPresentBuffers+0xdd862
0813fdfc 08930061     01000000 00000000 00000000 nvoglv32+0xcef6d
0813fe3c 0830d1d2     00000000 00000000 00000140 nvoglv32+0x7f0061
0813fe64 088fe217     00000000 00000000 00000140 nvoglv32+0x1cd1d2
0813fea8 089ea0fb     0a3b1040 00000000 00000d44 nvoglv32+0x7be217
0813fee0 089e9e2f     0a3b1040 08b5b100 08b5b100 nvoglv32+0x8aa0fb
0813fef8 08b5b122     0a867540 07e40080 0813ff18 nvoglv32+0x8a9e2f
0813ff08 75d26359     0a867540 75d26340 0813ff74 nvoglv32!DrvValidateVersion+0xaf42
0813ff18 778a7c24     0a867540 59857cfe 00000000 kernel32!BaseThreadInitThunk+0x19
0813ff74 778a7bf4     ffffffff 778c8ff3 00000000 ntdll!__RtlUserThreadStart+0x2f
0813ff84 00000000     08b5b100 0a867540 00000000 ntdll!_RtlUserThreadStart+0x1b

 

如何调试栈溢出

引起调用栈溢出错误

堆栈溢出是用户模式线程可能遇到的错误。有三个可能的原因导致此错误:

  • 线程使用为其保留的整个堆栈。这通常是由无限递归引起的。

  • 线程无法扩展堆栈,因为页面文件已用完,因此无法提交任何其他页面来扩展堆栈。

  • 线程无法扩展堆栈,因为系统在用于扩展页面文件的短暂时间内。

排查过程

1、~* 显示崩溃线程,线程号前面的 . 表示当前的线程,#表示异常线程,然后切换到崩溃线程 ~1s ,切换到1号线程。

0:002> ~*
   0  Id: 1658.c64 Suspend: 1 Teb: 00d5d000 Unfrozen ""
      Start: lec_teacher!WinMainCRTStartup (00367c52)
      Priority: 0  Priority class: 32  Affinity: ff
#  1  Id: 1658.d44 Suspend: 0 Teb: 00d78000 Unfrozen
      Start: nvoglv32!DrvValidateVersion+0xaf20 (08b5b100)
      Priority: 0  Priority class: 32  Affinity: ff
.  2  Id: 1658.213c Suspend: 1 Teb: 00d7b000 Unfrozen
      Start: nvoglv32!vk_optimusGetInstanceProcAddr+0xc6b3f (08f28d5f)



0:002> ~1s
eax=00000001 ebx=00000000 ecx=00000007 edx=000001e3 esi=10e8b840 edi=00000007
eip=08f276bd esp=0813e884 ebp=0813ec98 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
nvoglv32!vk_optimusGetInstanceProcAddr+0xc549d:
08f276bd cd29            int     29h

2、查看TEB (Thread Environment Block),如果已经具备pdb的情况下可以直接使用!teb,如果没有符号表,可以展示内存进行映射 dd 00d78000 L4

0:001> !teb
TEB at 00d78000
    ExceptionList:        0813ff64
    StackBase:            08140000
    StackLimit:           0812d000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 00d78000
    EnvironmentPointer:   00000000
    ClientId:             00001658 . 00000d44
    RpcHandle:            00000000
    Tls Storage:          355531b8
    PEB Address:          00d5a000
    LastErrorValue:       203
    LastStatusValue:      c0000100
    Count Owned Locks:    0
    HardErrorMode:        0


0:001> dd 00d78000 L4
00d78000  0813ff64 08140000 0812d000 00000000

3、要对此进行解释,您需要查找TEB数据结构的定义,网上可以查一下就可以找到。

typedef struct _TEB {
    NT_TIB NtTib;
    PVOID  EnvironmentPointer;
    CLIENT_ID ClientId;
    PVOID ActiveRpcHandle;
    PVOID ThreadLocalStoragePointer;
    PPEB ProcessEnvironmentBlock;
    ULONG LastErrorValue;
    .....
    PVOID DeallocationStack;
    .....
} TEB;

typedef struct _NT_TIB {
    struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
    PVOID StackBase;
    PVOID StackLimit;
    .....
} NT_TIB; 

这表明TEB结构中的第二个和第三个DWORD分别指向堆栈的底部和顶部。在这种情况下,这些地址是0x08140000 和0x0812d000。(堆栈在内存中向下增长。)您可以使用计算堆栈大小。

0:001> ? 08140000 - 0812d000
Evaluate expression: 77824 = 00013000

这表明堆栈大小为76K。最大堆栈大小存储在字段DeallocationStack中。经过一些计算,确定该字段的偏移量是0xE0C。如果有符号表,可以使用使用dt teb 完成上面的计算,直接可以找到偏移量

windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]

 

windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]

 

查看最大的栈大小是1048576 = 1M,所以最大的栈大小是1M ,这意味着剩余了足够的堆栈空间。

0:001> dd 00d78000+e0c L1
00d78e0c  08040000

0:001> ? 08140000 - 08040000
Evaluate expression: 1048576 = 00100000

此外,此过程看起来很干净-通过使用基于堆栈的数据结构过大,它不会无限递归或超出其堆栈空间。

 

4、现在进入KD,并使用!vm extension命令查看整个系统的内存使用情况:但是需要设置权限才可以

0:002> .breakin 
Break instruction exception - code 80000003 (first chance)
ntoskrnl!_DbgBreakPointWithStatus+4:
80148f9c cc               int     3

kd> !vm 

*** Virtual Memory Usage ***
        Physical Memory:     16268   (   65072 Kb)
        Page File: \??\C:\pagefile.sys
           Current:    147456Kb Free Space:     65988Kb
           Minimum:     98304Kb Maximum:       196608Kb
        Available Pages:      2299   (    9196 Kb)
        ResAvail Pages:       4579   (   18316 Kb)
        Locked IO Pages:        93   (     372 Kb)
        Free System PTEs:    42754   (  171016 Kb)
        Free NP PTEs:         5402   (   21608 Kb)
        Free Special NP:       348   (    1392 Kb)
        Modified Pages:        757   (    3028 Kb)
        NonPagedPool Usage:    811   (    3244 Kb)
        NonPagedPool Max:     6252   (   25008 Kb)
        PagedPool 0 Usage:    1337   (    5348 Kb)
        PagedPool 1 Usage:     893   (    3572 Kb)
        PagedPool 2 Usage:     362   (    1448 Kb)
        PagedPool Usage:      2592   (   10368 Kb)
        PagedPool Maximum:   13312   (   53248 Kb)
        Shared Commit:        3928   (   15712 Kb)
        Special Pool:         1040   (    4160 Kb)
        Shared Process:       3641   (   14564 Kb)
        PagedPool Commit:     2592   (   10368 Kb)
        Driver Commit:         887   (    3548 Kb)
        Committed pages:     45882   (  183528 Kb)
        Commit limit:        50570   (  202280 Kb)

        Total Private:       33309   (  133236 Kb)
         .....

首先,查看非分页和分页池的使用情况。两者都在限制范围内,因此这不是问题的原因。

接下来,查看已提交的页面数:202280中的183528。这非常接近限制。尽管此显示未显示此数字是完全处于极限,但是请记住,在执行用户模式调试时,系统上正在运行其他进程。每次执行NTSD命令时,这些其他进程也在分配和释放内存。这意味着您不完全了解堆栈溢出发生时的内存状态。鉴于已提交的页数与限制的接近程度,可以合理地得出结论,该页面文件在某个时刻已用完,这会导致堆栈溢出。

这并非罕见,而且目标应用程序也不会因此而真正出错。如果经常发生,您可能要考虑提高失败应用程序的初始堆栈承诺。这种情况概率非常小。

 

5、查看最后函数最后一次申请的栈空间大小

函数开始都是保存基地址,将esp 作为 ebp,下一条指令就是当前局部变量需要的申请的空间,sub esp, 10h ,这个表示的意思就局部变量占用16字节的大小,由于栈是从高地址向低地址,所以是sub。

最后申请的空间也不大,16字节

08f27608 55             push    ebp
08f27609 8bec           mov     ebp, esp
08f2760b 83ec10         sub     esp, 10h

通过上面可以基本排查出引起调用栈溢出的错误原因

 

调用栈不正确

还存在一种调用栈不正确的情况,需要找出的正确的调用栈,才能判断是否存在问题

WARNING: Stack unwind information not available. Following frames may be wrong.

此警告表明调试器不确定此消息之后列出的调用堆栈中的帧是否正确。

0:001> kb
  *** Stack trace for last set context - .thread/.cxr resets it
 # ChildEBP RetAddr      Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0813ec98 08ba784f     6e760038 00000000 00000000 nvoglv32!vk_optimusGetInstanceProcAddr+0xc549d
01 0813ecec 08ba79b0     0c870048 0c870048 00000000 nvoglv32!DrvPresentBuffers+0x2971f
02 0813ed34 08c830d8     0c870048 00000045 0c870048 nvoglv32!DrvPresentBuffers+0x29880
03 0813ed50 08c8853a     0000ffff 0c870048 0000005b nvoglv32!DrvPresentBuffers+0x104fa8
04 0813ed70 08c7739d     0c870048 00000045 0c870048 nvoglv32!DrvPresentBuffers+0x10a40a
05 0813f030 08c77dbc     01870048 0813fba0 0813f200 nvoglv32!DrvPresentBuffers+0xf926d
06 0813f1bc 081f85f2     0013f601 0c870048 0813f6a4 nvoglv32!DrvPresentBuffers+0xf9c8c
07 0813f218 08b56073     00000001 00000001 00000001 nvoglv32+0xb85f2
08 0813f248 08c5b992     16eac138 0813f6a4 0c870048 nvoglv32!DrvValidateVersion+0x5e93
09 0813f688 0820ef6d     0c870048 0813f6a4 00000002 nvoglv32!DrvPresentBuffers+0xdd862
0a 0813fdfc 08930061     01000000 00000000 00000000 nvoglv32+0xcef6d
0b 0813fe3c 0830d1d2     00000000 00000000 00000140 nvoglv32+0x7f0061
0c 0813fe64 088fe217     00000000 00000000 00000140 nvoglv32+0x1cd1d2
0d 0813fea8 089ea0fb     0a3b1040 00000000 00000d44 nvoglv32+0x7be217
0e 0813fee0 089e9e2f     0a3b1040 08b5b100 08b5b100 nvoglv32+0x8aa0fb
0f 0813fef8 08b5b122     0a867540 07e40080 0813ff18 nvoglv32+0x8a9e2f
10 0813ff08 75d26359     0a867540 75d26340 0813ff74 nvoglv32!DrvValidateVersion+0xaf42
11 0813ff18 778a7c24     0a867540 59857cfe 00000000 kernel32!BaseThreadInitThunk+0x19
12 0813ff74 778a7bf4     ffffffff 778c8ff3 00000000 ntdll!__RtlUserThreadStart+0x2f
13 0813ff84 00000000     08b5b100 0a867540 00000000 ntdll!_RtlUserThreadStart+0x1b

 

首先来看调用栈为什么不正确

手动推动调用栈,有关调用栈的堆栈信息可以参考我另一篇文章:windbg – 调试栈溢出及一个崩溃示例

通过打印ebp 的内存,我们知道ebp + 1 的值就是上一层的函数eip。所以推导出来上层函数是08ba784f

windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]

和自动推导的调用栈相同,其实调用栈的自动推动也是按照上面堆栈的ebp 找到每一帧eip推导出来的。所以在01栈之前是正确的。

windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]

 

2、所以从01帧开始正向推导调用栈,发现最新的调用栈不正确

因为在函数开始位置081ed2a0 和 当前异常的eip = 08f276bd 相差太远(0xD3 A41D)肯定不在同一个函数中,存在异常跳转。

windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]

 

异常代码分析

我在来看一崩溃的信息,代码中08f276b0 行,调用函数,返回值eax 是否等于0,如果等于等于0 不会触发软中断( int 29h)。所以问题在于08f276b0 函数中,没有正确返回0,发送了中断 29H。

 

windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]

 

查看资料找到,软中断 int 29h 是由__fastfail 方法产生的,说明是程序主动结束。

fastfail – windows系统的快速失败机制,是一种用于“快速失败”请求的机制,使用最小的消耗来结束进程 ,https://docs.microsoft.com/zh-cn/cpp/intrinsics/fastfail?view=msvc-160

文中特别强调了,User-mode fast fail requests appear as a second chance non-continuable exception with exception code 0xC0000409 ,在用户态fast fail 请求,作为一种“第二次机会非持续异常”,异常码0xC0000409 和本次异常相同。

  
    11: 	__fastfail(7);
005D1093 B9 07 00 00 00       mov         ecx,7  
005D1098 CD 29                int         29h 

windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]

 

知道异常退出,但是不清楚具体原因

知道是在函数,接下来我们在分析下esp中有什么内容

0:001> dd esp
0813e884  08b5ac64 00000000 0c870048 6e760038
0813e894  44542041 61682052 65622073 64206e65
0813e8a4  63657465 2e646574 6568540a 70706120
0813e8b4  6163696c 6e6f6974 73756d20 6c632074
0813e8c4  2e65736f 450a0a0a 726f7272 646f6320
0813e8d4  37203a65 7028200a 353d6469 20303237
0813e8e4  3d646974 36393333 63656c20 6165745f
0813e8f4  72656863 6578652e 62323320 0a297469
0:001> dd
0813e904  7369560a 68207469 3a707474 766e2f2f
0813e914  61696469 7375632e 6c656874 6f632e70
0813e924  70612f6d 6e612f70 72657773 65642f73
0813e934  6c696174 695f612f 36332f64 66203333
0813e944  6d20726f 2065726f 6f666e69 74616d72
0813e954  2e6e6f69 00000000 00000000 00000000
0813e964  00000000 00000000 00000000 00000000
0813e974  00000000 00000000 00000000 00000000

对值进行分类

整数:大多数整数将是较小的值,这意味着当显示为DWORD时,它们将大部分为零(例如0x00000270)。

指针:指向本地地址的大多数指针都位于堆栈指针附近(例如fe4cca78)。

状态码:通常以ac(c00000d6)开头。

字符串:可以通过以下事实来识别Unicode和ASCII字 符串:每个字符的范围为20-7f。dc 命令将在右侧显示字符。

windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]

柳岸花明,奇迹出现

显示的内容:
A TDR has been detected.The Application must close.同时可以看到内容是error code :7 ,进程id和线程id也是正确的,说明此时确实发生错误。

可以在栈上显示的字符串是字符串数组,例如下面图中展示

https://nvidia.custhelp.com/app/answers/detail/a_id/3633

windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]

TDR 的介绍

https://docs.microsoft.com/zh-cn/windows-hardware/drivers/display/timeout-detection-and-recovery

由于通过上面的错误,可以知道是由于接受到TDR的异常导致,所以出现调用栈,中间发送跳转到异常栈位置,所以不正确

 

解决方案

https://www.drivereasy.com/knowledge/a-tdr-has-been-detected-nvidia-opengl-driver-error-solved/

1、更新显卡驱动

2、增加TRD的超时时长

 

 

 

 

今天的文章windbg 调试程序崩溃_windbg分析dump文件命令[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/62924.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注