记一次 .NET 某工控MES程序 崩溃分析

news2024/10/3 8:27:52

一:背景

1.讲故事

前几天有位朋友找到我,说他的程序出现了偶发性崩溃,已经抓到了dump文件,Windows事件日志显示的崩溃点在 clr.dll 中,让我帮忙看下是怎么回事,那到底怎么回事呢? 上 WinDbg 说话。

二:WinDbg 分析

1. 崩溃点在哪里

如果是托管代码引发的崩溃,在线程列表上会有一个异常信息,可以用 !t 来验证下。


0:000> !t
ThreadCount:      7
UnstartedThread:  0
BackgroundThread: 6
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                                                                                        Lock  
       ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1  cb4 000000000077fbd0    26020 Preemptive  0000000000000000:0000000000000000 0000000000734050 0     STA (GC) System.ExecutionEngineException 00000000028a11f8
   2    2  890 0000000000782ea0    2b220 Preemptive  0000000000000000:0000000000000000 0000000000734050 0     MTA (Finalizer) 
   5    3  f6c 0000000021301f50  1029220 Preemptive  0000000000000000:0000000000000000 0000000000734050 0     Ukn (Threadpool Worker) 
  12    5  a38 00000000213dc090    2b220 Preemptive  0000000000000000:0000000000000000 0000000000734050 0     MTA 
  15    6  cb8 0000000021430740    2b220 Preemptive  0000000000000000:0000000000000000 0000000000734050 0     MTA 
  16    7  ce4 00000000318421c0    2b220 Preemptive  0000000000000000:0000000000000000 0000000000734050 0     MTA 
  17    4  f1c 00000000370edab0  102a220 Preemptive  0000000000000000:0000000000000000 0000000000734050 0     MTA (Threadpool Worker) 
...

从卦中看,主线程正在触发 GC,并且抛出了一个 System.ExecutionEngineException 异常,这个异常属于灾难性的,表示 CLR 自己出问题了,那 CLR 在哪里出问题了呢?我们观察下主线程的非托管栈。


0:000> ~0s
clr!WKS::gc_heap::find_first_object+0xea:
000007fe`ea17644b 833800          cmp     dword ptr [rax],0 ds:000007fe`00000000=????????

0:000> r
rax=000007fe00000000 rbx=000000000051a830 rcx=0000000000000018
rdx=000000000303f160 rsi=0000000000000000 rdi=000000000051a340
rip=000007feea17644b rsp=000000000051aa58 rbp=00000000000028a1
 r8=0000000000000001  r9=000000000303f160 r10=0000000003040000
r11=000000000000303f r12=0000000000000001 r13=000000000051c860
r14=00000000033b8c59 r15=00000000033b8c58
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010204
clr!WKS::gc_heap::find_first_object+0xea:
000007fe`ea17644b 833800          cmp     dword ptr [rax],0 ds:000007fe`00000000=????????

0:000> k 10
 # Child-SP          RetAddr               Call Site
00 00000000`0051aa58 000007fe`ea175d7b     clr!WKS::gc_heap::find_first_object+0xea
01 00000000`0051aa70 000007fe`ea1040d4     clr!WKS::GCHeap::Promote+0xc7
02 00000000`0051aae0 000007fe`ea1001b8     clr!GcInfoDecoder::EnumerateLiveSlots+0x103a
03 00000000`0051af50 000007fe`ea100e16     clr!GcStackCrawlCallBack+0x2bd
04 00000000`0051b370 000007fe`ea16e35c     clr!GCToEEInterface::GcScanRoots+0x4d6
05 00000000`0051c830 000007fe`ea16ee9b     clr!WKS::gc_heap::mark_phase+0x17f
06 00000000`0051c8d0 000007fe`ea16edaf     clr!WKS::gc_heap::gc1+0xa3
07 00000000`0051c920 000007fe`ea170c0f     clr!WKS::gc_heap::garbage_collect+0x193
08 00000000`0051c960 000007fe`ea173be6     clr!WKS::GCHeap::GarbageCollectGeneration+0xef
09 00000000`0051c9b0 000007fe`ea1d5ccf     clr!AllocateArrayEx+0x69c
0a 00000000`0051cb40 000007fe`ea24c480     clr!FieldMarshaler_FixedArray::UpdateCLRImpl+0x40
0b 00000000`0051cb80 000007fe`ea24c3ac     clr!FieldMarshaler::UpdateCLR+0x68
0c 00000000`0051cca0 000007fe`ea24c74c     clr!LayoutUpdateCLR+0x213
0d 00000000`0051cd80 000007fe`ea24c6aa     clr!FmtValueTypeUpdateCLR+0x50
0e 00000000`0051cdb0 000007fe`e2c88134     clr!StubHelpers::ValueClassMarshaler__ConvertToManaged+0x9a
0f 00000000`0051cf20 000007fe`e2c7e335     System_Drawing_ni+0x78134

从卦中的线程栈上看,GC 处于三阶段中的 标记阶段,正在各个线程栈上寻找用户根遇到了一个异常地址 000007fe00000000,最后抛出异常了,那这个地址属于什么内存属性呢?可以用 !address 000007fe00000000 观察一下。


0:000> !address 000007fe00000000

Usage:                  Free
Base Address:           00000001`801d3000
End Address:            000007fe`8a9f0000
Region Size:            000007fd`0a81d000 (   7.988 TB)
State:                  00010000          MEM_FREE
Protect:                00000001          PAGE_NOACCESS
Type:                   <info not present at the target>

Content source: 0 (invalid), length: 8a9f0000

从卦中可以看到,在这个地址是 PAGE_NOACCESS 的,理所当然会抛出访问违例,既然 gc 在托管堆上用 find_first_object 遇到了一个异常地址,说明这块内存被破坏了,可以用 !VerifyHeap 去验证下托管堆。


0:000> !VerifyHeap
Could not request method table data for object 000000000303F160 (MethodTable: 000007FE00000000).
Last good object: 000000000303F140.

从卦中可以清晰的看到,地址 000000000303F160 上的方法表地址 000007FE00000000 被破坏了,这个地址刚好就是汇编代码显示的这个。

2. 方法表地址为什么会被损坏

一般来说这个损坏是在崩溃前的某一次 托管和非托管 交互时产生的,在后续的某个时候 GC 在清洗托管堆时才发现家里被偷继而报警,但此时已经错过了第一时间,画个图大概是这样。

由于 dump 只是一个快照,无法追踪曾经发生了什么事?只能死马当活马医,看看目前的破坏现场,可以用 !lno 000000000303F160 观察破坏对象的前后对象和附近内存。


0:000> !lno 000000000303F160
Before:  000000000303f140           32 (0x20)	System.Byte[]
After:   000000000303f198           24 (0x18)	System.Int32
Heap local consistency not confirmed.

0:000> dp 000000000303F160 - 0n144 L20
00000000`0303f0d0  00000000`00000000 000007fe`e794aaa0
00000000`0303f0e0  00000000`0000000e 33313231`32323032
00000000`0303f0f0  00003835`34323131 00000000`00000000
00000000`0303f100  000007fe`e794aaa0 00000000`00000001
00000000`0303f110  00000000`00000000 00000000`00000000
00000000`0303f120  000007fe`e794aaa0 00000000`00000001
00000000`0303f130  00000000`00000000 00000000`00000000
00000000`0303f140  000007fe`e794aaa0 00000000`00000001
00000000`0303f150  39353932`30303038 00000000`36373135
00000000`0303f160  000007fe`00000000 00700055`0000000e
00000000`0303f170  00640061`006f006c 00750073`00650052
00000000`0303f180  0050003a`0074006c 00000000`00000000
00000000`0303f190  00000000`00000000 000007fe`e79485a0
00000000`0303f1a0  00000000`00000004 00000000`00000000
00000000`0303f1b0  000007fe`e79459c0 00000034`00000001
00000000`0303f1c0  00000000`00000000 00000000`00000000

仔细观察 000000000303f160 处的内存布局,很明显这是一个 string 类型, 地址 007000550000000e 上的低八位的 0xe = 0n13 表示 string 的长度,但 string 的高八位 00700055 理应是对齐的 00000000,看样子被非托管代码纂改了,并且把原来正确的方法表地址 000007fee79459c0 的低八位给覆盖成了 000007fe00000000,导致最后的崩溃。

如果再仔细观察,你会发现 000000000303f150 处也是一个 unicode 字符,但它不属于 000000000303f140 处的 byte[] 对象,可以用 !do 验证。


0:000> !do 000000000303f140
Name:        System.Byte[]
MethodTable: 000007fee794aaa0
EEClass:     000007fee7ab6c78
Size:        25(0x19) bytes
Array:       Rank 1, Number of elements 1, Type Byte (Print Array)
Content:     8
Fields:
None

这也就说明 000000000303f150 ~ 000000000303f170 附近的内存全部被破坏了,不过庆幸的是:这些看起来都是一些字符,接下来用 db 显示一下。


0:000> db 000000000303f150
00000000`0303f150  38 30 30 30 32 39 35 39-35 31 37 36 00 00 00 00  800029595176....
00000000`0303f160  00 00 00 00 fe 07 00 00-0e 00 00 00 55 00 70 00  ............U.p.
00000000`0303f170  6c 00 6f 00 61 00 64 00-52 00 65 00 73 00 75 00  l.o.a.d.R.e.s.u.
00000000`0303f180  6c 00 74 00 3a 00 50 00-00 00 00 00 00 00 00 00  l.t.:.P.........
00000000`0303f190  00 00 00 00 00 00 00 00-a0 85 94 e7 fe 07 00 00  ................
00000000`0303f1a0  04 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`0303f1b0  c0 59 94 e7 fe 07 00 00-01 00 00 00 34 00 00 00  .Y..........4...
00000000`0303f1c0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

从卦中的 UploadResult:P 来看,貌似是一个上传结果,在结合 !lno 显示的前后对象分别是 Byte[]Int32,应该是 朋友用 class 的方式实现 C# 和 C++ 的交互,C++ 在操作 class 下的某一个 string 时 指针溢出, 破坏了托管堆的 string 对象。

将这些信息告诉朋友后,朋友说也已经定位到这里了,正和对方的工程师做对接,对方反馈过来是托管层要自己预留足够的长度。

三: 总结

其实崩溃类的 dump 最能考验基本功,需要你对 C# 对象的内存布局有一个深度的理解,否则也很难发现出端倪,当然本篇还属于崩溃类中较容易的。

相信有朋友肯定要问,如何找到破坏的第一现场,这当然是可以的,需要借助微软的 MDA 助手, 配置 gcManagedToUnmanaged gcUnmanagedToManaged 两项,让双方交互之后立即触发 GC,具体参见: https://learn.microsoft.com/zh-cn/dotnet/framework/debug-trace-profile/gcunmanagedtomanaged-mda

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/95111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux文件权限概念

目录 前言 1、Linux 文件属性 1.1、档案类型权限 1.2、连结数 1.3、档案拥有者 1.4、档案所属群组 1.5、档案容量 1.6、档案最后被修改的时间 1.7、档名&#xff08;文件名&#xff09; 2、如何改变文件属性和权限 2.1、改变所属群组, chgrp 2.2、改变档案拥有者, c…

Codeforces Round #838 (Div. 2) E. Tree Sum(组合数学 prufer序列 枚举边算贡献)

题目 n(n<5e5)个点的树&#xff0c;边只能选-1或1&#xff0c; 若对于每个点i来说&#xff0c;i相连的所有边的乘积值为-1&#xff0c;则称这棵树是「good tree」 根据Cayley公式&#xff0c;有种树形&#xff0c; 对于每一种树形&#xff0c;每条边都有2种选择&#xff…

什么是飞书机器人?如何定时发送飞书机器人消息?

什么是飞书机器人&#xff1f; 机器人是飞书应用的一种能力类型。基于飞书的机器人能力&#xff0c;开发者能够以较低的开发成本&#xff08;只需在服务端开发&#xff09;&#xff0c;实现在飞书单聊或群组中的消息推送和简单互动&#xff0c;完成企业系统数据与飞书的互联互通…

42_CAN列表模式接收中断实验

目录 CAN结构体 CAN_InitTypeDef初始化结构体详解 发送及接收结构体: CanTxMsg及CanRxMsg详解 CAN_FilterlnitTypeDef筛选器结构体讲解 硬件连接图 实验源码 CAN结构体 从STM32的CAN外设我们了解到它的功能非常多,控制涉及的寄存器也非常丰富,而使用STM32标准库提供的各…

pandas数据分析总结

Pandas 是 Python 语言的一个扩展程序库&#xff0c;用于数据分析。其中DataFrame&#xff0c;Json&#xff0c;数据筛选的使用频次很高&#xff0c;值得重点将基本用法学习并总结。 版本信息&#xff1a;python 3.7 pandas 1.3.5 一、DataFrame DataFrame 是一个表格型的数…

【持续更新】计算机类PDF整理

计算机相关的书籍太多太多了&#xff0c;整理太多反而很乱&#xff0c;这边我根据自己的经验和理解选了一些典型的书籍&#xff0c;大部分是自己看过觉得很有用的&#xff0c;有些是听说很nice的&#xff0c;有些是经典的&#xff0c;大家可以自行下载。 持续更新中&#xff0…

Kaggle竞赛——Titanic泰坦尼克之灾(0.76315==>0.79186)

Kaggle竞赛网址&#xff1a;https://www.kaggle.com/c/titanic 上一章&#xff1a;Kaggle竞赛——Titanic泰坦尼克之灾&#xff08;保姆级基础版&#xff09; 本次Kaggle泰坦尼克之灾分析过程大致分为&#xff1a; 第1步&#xff1a;了解数据&#xff08;上一章&#xff09; …

19_SpringBoot拓展注解@SpringBootApplication和@Configuration

文章目录Spring Boot拓展注解SpringBootApplication和Configuration0x01_SpringBootApplicationSpringBootConfigurationEnableAutoConfigurationComponentScan0x02_ConfigurationFull 全模式&#xff0c;Lite 轻量级模式Spring Boot拓展注解SpringBootApplication和Configurat…

计算机网络原理 课后习题(作业)持续更新ing

【1-03 】试从多个方面比较电路交换、报文交换和分组交换的主要优缺点 &#xff08;1&#xff09;电路交换的主要特点: ①电路交换三个阶段&#xff1a;建立连接、通信、释放连接&#xff1b; ②在整个通信过程中&#xff0c;通信的双方自始至终占用着所使用的物理信道。 由…

多线程知识笔记(五)_单例模式

文章目录1、什么是单例模式2、饿汉模式3、懒汉模式1、什么是单例模式 单例模式是一种设计模式。 什么是设计模式呢&#xff1f;设计模式就是一种模板&#xff0c;就像做饭的时候&#xff0c;新手做饭什么调料都是一把放进锅里&#xff0c;有的菜就讲究调料的先后顺序&#xff…

Java web 2022跟学尚硅谷(十一) Vue基础和Axios基础和json基础

Java web 2022跟学尚硅谷十一后端基础 书城Vue在js中定义对象的2种方式方式一方式二简单的Vue代码绑定元素的属性方式一 v-bind:value方式二 :value双向数据绑定 v-model:方式一方式二 :value可以省略&#xff0c;trim去除首尾空格条件渲染 v-if条件渲染 v-else条件渲染 特别注…

[附源码]Python计算机毕业设计高校医务管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

极地土活性层和永久冻土的比较宏基因组学

北极地区有大约87%由低有机碳矿物土壤组成&#xff0c;但对低碳永久冻土&#xff08;PF&#xff09;和活性层&#xff08;AL&#xff09;土壤中微生物活性的了解仍然有限。多种物理和生物因素可能影响碳循环&#xff0c;包括土壤化学、氮等养分的可用性以及现有微生物群落的结构…

论文解析[9] Swin Transformer: Hierarchical Vision Transformer using Shifted Windows

发表时间&#xff1a;2021 论文地址&#xff1a;https://arxiv.org/abs/2103.14030 代码地址&#xff1a;https://github.com/microsoft/Swin-Transformer 文章目录摘要3 方法3.1 总体框架3.2 基于自注意力的移动窗口3.3 结构变形5 结论摘要 这篇论文提出了一个新的ViT叫作Swi…

大数据培训kettle安装部署

kettle安装部署和使用 2.1 kettle安装地址 官网地址 https://community.hitachivantara.com/docs/DOC-1009855 下载地址 https://sourceforge.net/projects/pentaho/files/Data%20Integration/ 2.2 Windows下安装使用 2.2.1 概述 在实际企业开发中&#xff0c;都是在本…

Html 小功能总结二

文章目录一、Html 实现弹窗的实例二、Html 实现流程图三、制作一个简单的菜单功能一、Html 实现弹窗的实例 <!DOCTYPE html> <html> <head><meta charset"utf-8" /><title></title><style type"text/css">body…

C++ Reference: Standard C++ Library reference: Containers: map: map: crbegin

C官网参考链接&#xff1a;https://cplusplus.com/reference/map/map/crbegin/ 公有成员函数 <map> std::map::crbegin const_reverse_iterator crbegin() const noexcept;返回指向反向开始的const_reverse_iterator 返回一个指向容器中最后一个元素的const_reverse_ite…

Nacos 消息通知系统 源码讲解

目录 1. 介绍 2. Nacos 中的生产者 3. Nacos 中的消费者 4. Nacos 中的事件 5. 统一事件通知中心 NotifyCenter 6. 总结 1. 介绍 Nacos 节点内部使用了大量的事件机制进行通信&#xff0c;这样各种操作进行了解耦&#xff0c;提高了性能。 接下来就介绍。 所谓事件机制…

virtualenv系列 (5) · 打通virtualenvwrapper和PyCharm/IntelliJ IDEA的虚拟环境

PyCharm/IntelliJ IDEA已经内置了对virtualenv的支持&#xff0c;当我们为一个项目创建一个新的Python SDK时&#xff0c;就会进入到Add Python Interpreter对话框&#xff0c;选择Virtualenv Environment -> New environment就将创建一套新的virtualenv环境。这里&#xff…

关于长三角某高校能效管理平台应用成效探讨

更多请关注&#xff1a;安科瑞电气网 摘要&#xff1a; 随着现代科学技术的发展&#xff0c;在高校中开始广泛应用智能化技术&#xff0c;改善学生宿舍的用电管理模式&#xff0c;提高宿舍的管理水平&#xff0c;有利于实现高校宿舍用电管理的科学化。本文主要阐述传统高校宿…