论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
只需一步,快速开始
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
ToB企服应用市场:ToB评测及商务社交产业平台
»
论坛
›
软件与程序人生
›
后端开发
›
.Net
›
记一次 .NET某新能源检测系统 崩溃分析
记一次 .NET某新能源检测系统 崩溃分析
怀念夏天
金牌会员
|
2023-11-3 11:59:39
|
来自手机
|
显示全部楼层
|
阅读模式
楼主
主题
877
|
帖子
877
|
积分
2631
一:背景
1. 讲故事
前几天有位朋友微信上找到我,说他的程序会偶发性崩溃,一直找不到原因,让我帮忙看一下怎么回事,对于这种崩溃类的程序,最好的办法就是丢dump过来看一下便知,话不多说,上windbg说话。
二:WinDbg 分析
1. 到底是哪里的崩溃
对于一个崩溃类的dump,寻找崩溃点非常重要,常用的命令就是 !analyze -v,输出如下:
0:006> !analyze -v
CONTEXT: 6fbdee65 -- (.cxr 0x6fbdee65)
eax=55d2ebff ebx=5e5f04c0 ecx=e8c434e8 edx=cf8bc35b esi=83008b05 edi=75880000
eip=3d83f98b esp=ce8b0774 ebp=5756ec8b iopl=0 vip ov up ei pl nz na po nc
cs=4040 ss=0010 ds=81f8 es=00e1 fs=4e8b gs=ffdb efl=08758b00
4040:3d83f98b ?? ???
Resetting default scope
EXCEPTION_RECORD: 049bfbd0 -- (.exr 0x49bfbd0)
ExceptionAddress: 00000000
ExceptionCode: 049bfbf8
ExceptionFlags: 6f9b6c38
NumberParameters: 8752248
Parameter[0]: 00000000
Parameter[1]: 6f9c92a0
Parameter[2]: 049bfbdc
Parameter[3]: 00000008
Parameter[4]: 00000000
Parameter[5]: 049bfc34
Parameter[6]: 6f9b6d0d
Parameter[7]: a2cc713a
Parameter[8]: 6f9b6c40
Parameter[9]: 00000000
Parameter[10]: 00844c80
Parameter[11]: a2cc712a
Parameter[12]: 00000000
Parameter[13]: 049bfd2c
Parameter[14]: 049bfc00
PROCESS_NAME: xxxx.exe
ERROR_CODE: (NTSTATUS) 0x80000004 - { }
EXCEPTION_CODE_STR: 80000004
FAULTING_THREAD: ffffffff
复制代码
从卦中的崩溃点来看,很奇怪,怎么 cs:eip 所处的地址没有机器码? 先不管了,看下异常状态 80000004,在微软的官方文档查一查:
从图中信息看,应该是 F11 这种单步跟踪造成的,这就很奇葩了,分析了200+ 的dump,这种崩溃还是第一次遇到,无语,一下子陷入了迷茫。
2. 还有突破口吗
虽然 windbg 的自动化分析给出的信息很不尽如意,但没关系,根据强大的临场经验,我们直接切到异常前的状态,看看异常前的上下文有没有什么新的线索,删减后如下:
0:006> .ecxr
eax=00000000 ebx=049bec60 ecx=027e1374 edx=0b8024a8 esi=00000000 edi=049bebf8
eip=09fb48b1 esp=049beb98 ebp=049bebe0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
xxx!xxx.Program.CurrentDomain_UnhandledException+0x29:
09fb48b1 cc int 3
0:006> k
*** Stack trace for last set context - .thread/.cxr resets it
# ChildEBP RetAddr
00 049bebe0 6f962546 xxx!xxx.Program.CurrentDomain_UnhandledException+0x29
...
11 049bf114 77a48962 clr!_except_handler4+0x29
12 049bf138 77a48934 ntdll!ExecuteHandler2+0x26
13 049bf200 77a34f86 ntdll!ExecuteHandler+0x24
14 049bf6f0 77a32b2c ntdll!KiUserExceptionDispatcher+0x26
15 049bf6f0 76698d7a ntdll!NtClose+0xc
16 049bf6f0 6f96287d KERNELBASE!CloseHandle+0x4a
...
20 049bf970 78a1887b clr!SafeHandle::Finalize+0x7a
21 049bf978 78a187e4 mscorlib_ni!System.Runtime.InteropServices.SafeHandle.Dispose+0x1b [f:\dd\ndp\clr\src\BCL\system\runtime\interopservices\safehandle.cs @ 263]
22 049bf998 6f98df99 mscorlib_ni!System.Runtime.InteropServices.SafeHandle.Finalize+0x24 [f:\dd\ndp\clr\src\BCL\system\runtime\interopservices\safehandle.cs @ 199]
23 049bf9ec 6f98e0a7 clr!FastCallFinalize+0x6d
24 049bfa10 6f98de5c clr!MethodTable::CallFinalizer+0x150
25 049bfa78 6f98ded3 clr!CallFinalizer+0xa6
26 049bfa78 6f9c9263 clr!FinalizerThread::DoOneFinalization+0x132
27 049bfaa8 6f9c9343 clr!FinalizerThread::FinalizeAllObjects+0xa1
28 049bfad4 6f973b24 clr!FinalizerThread::FinalizerThreadWorker+0xbe
29 049bfaec 6f973b9b clr!ManagedThreadBase_DispatchInner+0x71
2a 049bfb74 6f973c4b clr!ManagedThreadBase_DispatchMiddle+0x8f
2b 049bfbd0 6f9b6c38 clr!ManagedThreadBase_DispatchOuter+0x6d
2c 049bfbf8 6f9b6d0d clr!ManagedThreadBase::FinalizerBase+0x33
2d 049bfc34 6f98eb34 clr!FinalizerThread::FinalizerThreadStart+0xe2
2e 049bfcd0 76cdfcc9 clr!Thread::intermediateThreadProc+0x58
2f 049bfce0 77a27b1e kernel32!BaseThreadInitThunk+0x19
30 049bfd3c 77a27aee ntdll!__RtlUserThreadStart+0x2f
31 049bfd4c 00000000 ntdll!_RtlUserThreadStart+0x1b
复制代码
从卦中的线程栈信息来看,逻辑还是非常清楚的,终结器线程析构一个C#的 SafeWaitHandle 对象时,在网关函数 ntdll!NtClose 中抛出了异常,这个函数再往下就是 内核层 了。
线程既然抛了异常,那 C# 层面有没有接到呢?可以用 !t 观察下。
0:006> !t
ThreadCount: 10
UnstartedThread: 0
BackgroundThread: 8
PendingThread: 0
DeadThread: 1
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 4b9c 0085f088 a6028 Preemptive 0B7FF79C:00000000 00858c78 0 STA
6 2 5068 008a3708 ab228 Preemptive 0B8024B8:00000000 00858c78 0 MTA (Finalizer) System.Runtime.InteropServices.SEHException 0b800c88
11 3 293c 0092c0a8 10a9228 Preemptive 00000000:00000000 00858c78 0 MTA (Threadpool Worker)
12 4 2eb0 0602ed48 8029228 Preemptive 00000000:00000000 00858c78 0 MTA (Threadpool Completion Port)
XXXX 5 0 07de70a8 1039820 Preemptive 00000000:00000000 00858c78 0 Ukn (Threadpool Worker)
13 6 7e0c 0a7ada58 102a228 Preemptive 00000000:00000000 00858c78 0 MTA (Threadpool Worker)
14 7 7c60 0a773950 1029228 Preemptive 00000000:00000000 00858c78 0 MTA (Threadpool Worker)
15 8 5c24 0a775f68 10a9228 Preemptive 0B7EB8CC:00000000 00858c78 0 MTA (Threadpool Worker)
16 9 698c 008d5b40 1029228 Preemptive 00000000:00000000 00858c78 0 MTA (Threadpool Worker)
17 10 7de4 008dea10 1029228 Preemptive 0B7ECE80:00000000 00858c78 0 MTA (Threadpool Worker)
0:006> !PrintException /d 0b800c88
Exception object: 0b800c88
Exception type: System.Runtime.InteropServices.SEHException
Message: 外部组件发生异常。
InnerException: <none>
StackTrace (generated):
SP IP Function
00000000 00000000 mscorlib_ni!Microsoft.Win32.Win32Native.CloseHandle(IntPtr)+0x1
049BF760 78ADF5FE mscorlib_ni!Microsoft.Win32.SafeHandles.SafeWaitHandle.ReleaseHandle()+0xe
00000000 00000001 mscorlib_ni!System.Runtime.InteropServices.SafeHandle.InternalFinalize()+0xffffffff90656c91
049BF978 78A1887B mscorlib_ni!System.Runtime.InteropServices.SafeHandle.Dispose(Boolean)+0x1b
049BF980 78A187E4 mscorlib_ni!System.Runtime.InteropServices.SafeHandle.Finalize()+0x24
StackTraceString: <none>
HResult: 80004005
复制代码
从卦中信息看,果然是在析构 SafeHandle.Finalize 时异常了,但这个异常信息 Message:外部组件发生异常 对我们来说一点作用都没有,到这里貌似又进行不下去了。
3. 从 handle 上突破
托管层没法挖了,那就继续挖非托管层,也就是异常前的最后一个函数 ntdll!NtClose,这个函数其实没什么特别的,也就是释放句柄,这个函数一般来说固若金汤,不会有异常的,不管怎么说,先把句柄值找出来看看,签名如下:
NTSTATUS NTAPI NtClose(
HANDLE Handle
);
复制代码
如何提取出 handle 呢?非常简单,用 kb 即可。
0:006> kb
*** Stack trace for last set context - .thread/.cxr resets it
# ChildEBP RetAddr Args to Child
...
13 049bf200 77a34f86 049bf218 049bf268 049bf218 ntdll!ExecuteHandler+0x24
14 049bf6f0 77a32b2c 00000664 049bf730 008a3708 ntdll!KiUserExceptionDispatcher+0x26
15 049bf6f0 76698d7a 00000664 049bf6fc 049bf72c ntdll!NtClose+0xc
16 049bf6f0 6f96287d 00000664 049bf730 008a3708 KERNELBASE!CloseHandle+0x4a
...
0:006> !handle 00000664 f
Handle 00000664
Type <Error retrieving type>
unable to query object information
unable to query object information
No object specific information available
复制代码
我去,卦中显示这个 handle=664 句柄值居然不在进程中,难怪调用 ntdll!NtClose 会报错,接下来的问题就是这个 handle 到底怎么了?要找到这个答案,需要从线程栈上把 _EXCEPTION_RECORD 结构体给提取出来,它的内部记录了 ExceptionCode ,而且刚好线程栈上的 ntdll!ExecuteHandler 方法的第一个参数就是这个结构体。
0:006> dt _EXCEPTION_RECORD 049bf218
VCRUNTIME140_CLR0400!_EXCEPTION_RECORD
+0x000 ExceptionCode : 0xc0000008
+0x004 ExceptionFlags : 0
+0x008 ExceptionRecord : (null)
+0x00c ExceptionAddress : 0x74a70daa Void
+0x010 NumberParameters : 0
+0x014 ExceptionInformation : [15] 0
复制代码
接下来就是找下 ExceptionCode=0xc0000008 代表什么意思,这个简单,网上搜一下便知,截图如下:
到这里就很好理解了,然来是在释放一个已经释放的句柄,这个肯定会报错的,据朋友所说,他们的程序是 C# 和 C++ 混合编程的,那大概率就是 handle=664 被 C++ 给提前释放了。
有些朋友肯定要问了,那我怎么找到释放这个 handle 的代码呢?要寻找这个答案,需要通过 perfview 对 handle 进行全程监控,参见:
https://www.cnblogs.com/huangxincheng/p/17559370.html
三:总结
这个崩溃还是挺有意思的,需要你对 Windows 层面的知识有一定的了解,否则很难找出前因后果,所以请善待做大工控的朋友。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
怀念夏天
金牌会员
这个人很懒什么都没写!
楼主热帖
CVE-2017-12635 Couchdb 垂直权限绕过 ...
WEB安全基础入门—操作系统命令注入(s ...
Redis 原理 - Set
IOS手机Charles抓包
恭喜,成功入坑 GitHub 。。。 ...
数据库(Oracle 11g)使用expdp每周进 ...
【牛客】8 企业真题
KubeSphere3.3 私有云部署,在linux上 ...
java中Long和Integer缓存-128~127的简 ...
程序员不撰写代码注释和文档的十大理由 ...
标签云
存储
挺好的
服务器
快速回复
返回顶部
返回列表