当前位置: 首页 > news >正文

记一次 .NET 某汽车控制焊接软件 卡死分析

一:背景

1. 讲故事

前些天有位朋友找到我,说他们开发的在客户工厂里的窗体程序出现了卡死情况,并且 Ctrl+C 也退不出来,自己分析了下也没找出是什么原因,后来在网络上就找到了我,让我帮忙看下怎么回事? 毕竟我在这一块是专业的。。。 哈哈,既然有dump,那就拿出来分析一下。

二:卡死分析

1. 为什么会卡死

既然是窗体,那就看主线程呗,使用 ~0s;k 即可,调用栈如下:


0:030> ~0s
ntdll!NtWaitForSingleObject+0x14:
00007ff8`d2f4d5e4 c3              ret
0:000> k# Child-SP          RetAddr               Call Site
00 000000aa`f859e588 00007ff8`d058920e     ntdll!NtWaitForSingleObject+0x14
01 000000aa`f859e590 00007ff8`d18d6765     KERNELBASE!WaitForSingleObjectEx+0x8e
02 000000aa`f859e630 00007ff8`d18d6049     sechost!ScSendResponseReceiveControls+0x149
03 000000aa`f859e770 00007ff8`d18d5b50     sechost!ScDispatcherLoop+0x159
04 000000aa`f859e8b0 00007ff8`bbe1d439     sechost!StartServiceCtrlDispatcherW+0x70
05 000000aa`f859e8e0 00007ff8`bbe204bc     System_ServiceProcess_ni+0x2d439
06 000000aa`f859e990 00007ff8`621d08dd     System_ServiceProcess_ni!System.ServiceProcess.ServiceBase.Run+0x1dc
07 000000aa`f859ea00 00007ff8`c19812c3     AgvTaskService!AgvTaskService.Program.Main+0x4d
08 000000aa`f859ea40 00007ff8`c184961b     clr!CallDescrWorkerInternal+0x83
09 000000aa`f859ea80 00007ff8`c1898b5a     clr!CallDescrWorkerWithHandler+0x47
0a 000000aa`f859eac0 00007ff8`c1808c95     clr!MethodDescCallSite::CallTargetWorker+0xfa
0b 000000aa`f859ebc0 00007ff8`c180899e     clr!RunMain+0x269
0c 000000aa`f859eda0 00007ff8`c1808735     clr!Assembly::ExecuteMainMethod+0xae
0d 000000aa`f859f080 00007ff8`c180968f     clr!SystemDomain::ExecuteMainMethod+0x619
0e 000000aa`f859f670 00007ff8`c180977d     clr!ExecuteEXE+0x3f
0f 000000aa`f859f6e0 00007ff8`c1809b64     clr!_CorExeMainInternal+0xa9
10 000000aa`f859f770 00007ff8`c3b7d6ea     clr!CorExeMain+0x14
11 000000aa`f859f7b0 00007ff8`c3fdac42     mscoreei!CorExeMain+0xfa
12 000000aa`f859f810 00007ff8`d1e27374     mscoree!CorExeMain_Exported+0x72
13 000000aa`f859f840 00007ff8`d2efcc91     kernel32!BaseThreadInitThunk+0x14
14 000000aa`f859f870 00000000`00000000     ntdll!RtlUserThreadStart+0x21

从卦中的 StartServiceCtrlDispatcherW 来看确实响应了 Ctrl+C 事件,并在非托管层等待 NtWaitForSingleObject 事件的完成,接下来的问题是为什么 Ctrl+C 完成不了?很显然有线程没有优雅的退出,所以目光就转移到其他的线程栈,使用 !t 观察下托管线程列表。


0:000> !t
ThreadCount:      37
UnstartedThread:  0
BackgroundThread: 25
PendingThread:    0
DeadThread:       11
Hosted Runtime:   noLock  ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception0    1 154c 000001d5ee9706a0    2a020 Preemptive  0000000000000000:0000000000000000 000001d5ee945930 0     MTA 2    2 57c0 000001d5ee999ba0    2b220 Preemptive  000001D5809EBE88:000001D5809EDDE0 000001d5ee945930 0     MTA (Finalizer) 
...30   42 6c78 000001d5f128e7d0    2b220 Preemptive  000001D5804C0B10:000001D5804C0B10 000001d5ee945930 1     MTA (GC) 

从卦象看很不吉利,有一个 (GC) 字样,表示当前程序触发了 GC,接下来就是切过来观察下 GC 此时正在做什么?使用 ~30s;k 即可。


0:000> ~30s;k
ntdll!NtWaitForAlertByThreadId+0x14:
00007ff8`d2f50f94 c3              ret# Child-SP          RetAddr               Call Site
00 000000aa`f9f3d2d8 00007ff8`d2f14d7d     ntdll!NtWaitForAlertByThreadId+0x14
01 000000aa`f9f3d2e0 00007ff8`d2f14c32     ntdll!RtlpWaitOnAddressWithTimeout+0x81
02 000000aa`f9f3d310 00007ff8`d2f14a4d     ntdll!RtlpWaitOnAddress+0xae
03 000000aa`f9f3d380 00007ff8`d2edfcb4     ntdll!RtlpWaitOnCriticalSection+0xfd
04 000000aa`f9f3d460 00007ff8`d2edfae2     ntdll!RtlpEnterCriticalSectionContended+0x1c4
05 000000aa`f9f3d4c0 00007ff8`d03c1999     ntdll!RtlEnterCriticalSection+0x42
06 000000aa`f9f3d4f0 00007ff8`d03c4e8e     AisEsmUmh+0x1999
07 000000aa`f9f3d560 00007ff8`d05e5c50     AisEsmUmh+0x4e8e
08 000000aa`f9f3d9d0 00007ff8`c19377c6     KERNELBASE!SuspendThread+0x10
09 000000aa`f9f3da00 00007ff8`c1824381     clr!Thread::SuspendThread+0xee
0a 000000aa`f9f3df80 00007ff8`c1823696     clr!ThreadSuspend::SuspendRuntime+0x205
0b 000000aa`f9f3e060 00007ff8`c18220c0     clr!ThreadSuspend::SuspendEE+0xf6
0c 000000aa`f9f3e140 00007ff8`c198f0cb     clr!WKS::GCHeap::GarbageCollectGeneration+0x270
0d 000000aa`f9f3e200 00007ff8`c18f58ae     clr!WKS::gc_heap::try_allocate_more_space+0x4fb
0e 000000aa`f9f3e260 00007ff8`c18ac7a8     clr!WKS::GCHeap::Alloc+0x5e
0f 000000aa`f9f3e2b0 00007ff8`622081cb     clr!JIT_New+0x1b8
10 000000aa`f9f3e540 00007ff8`62207670     System_Core_ni!System.Linq.Enumerable.TakeIterator<byte>+0x1b
11 000000aa`f9f3e580 00007ff8`bf530bf8     DAL!DAL.Request.OnReceivedServerData+0x50
12 000000aa`f9f3e5d0 00007ff8`bf530ae5     mscorlib_ni!System.Threading.ExecutionContext.RunInternal+0x108 [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 980] 
13 000000aa`f9f3e6a0 00007ff8`bf530ab5     mscorlib_ni!System.Threading.ExecutionContext.Run+0x15 [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 928] 
14 000000aa`f9f3e6d0 00007ff8`bfebd5a0     mscorlib_ni!System.Threading.ExecutionContext.Run+0x55 [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 917] 
15 000000aa`f9f3e720 00007ff8`c19812c3     mscorlib_ni!System.Threading.ThreadHelper.ThreadStart+0x60 [f:\dd\ndp\clr\src\BCL\system\threading\thread.cs @ 93] 
16 000000aa`f9f3e760 00007ff8`c184961b     clr!CallDescrWorkerInternal+0x83
17 000000aa`f9f3e7a0 00007ff8`c1898b5a     clr!CallDescrWorkerWithHandler+0x47
18 000000aa`f9f3e7e0 00007ff8`c1b91659     clr!MethodDescCallSite::CallTargetWorker+0xfa
19 000000aa`f9f3e8e0 00007ff8`c186230b     clr!ThreadNative::KickOffThread_Worker+0x2186d9
1a 000000aa`f9f3eb30 00007ff8`c186222f     clr!ManagedThreadBase_DispatchInner+0x33
1b 000000aa`f9f3eb70 00007ff8`c18620fb     clr!ManagedThreadBase_DispatchMiddle+0x83
1c 000000aa`f9f3ec60 00007ff8`c186206f     clr!ManagedThreadBase_DispatchOuter+0x87
1d 000000aa`f9f3ecf0 00007ff8`c1979e11     clr!ManagedThreadBase_FullTransitionWithAD+0x2f
1e 000000aa`f9f3ed50 00007ff8`c19658ea     clr!ThreadNative::KickOffThread+0xe1
1f 000000aa`f9f3ee10 00007ff8`d1e27374     clr!Thread::intermediateThreadProc+0x8a
20 000000aa`f9f3f750 00007ff8`d2efcc91     kernel32!BaseThreadInitThunk+0x14
21 000000aa`f9f3f780 00000000`00000000     ntdll!RtlUserThreadStart+0x21

从卦象看,在 SuspendThread 的下游有一个 AisEsmUmh.dll,很显然这不是 coreclr 自带的,貌似被人家注入了,并且在 AisEsmUmh 的下游通过cs锁阻塞了当前的执行流,接下来的问题是 AisEsmUmh.dll 到底为何方神圣。

2. AisEsmUmh 到底是什么

要想知道是什么,可以使用 !lmi AisEsmUmh 命令即可,参考如下:


0:030> !lmi AisEsmUmh
Loaded Module Info: [aisesmumh] Module: AisEsmUmhBase Address: 00007ff8d03c0000Image Name: AisEsmUmh.dllMachine Type: 34404 (X64)Time Stamp: 66bc19bf Wed Aug 14 10:43:11 2024Size: 5a000CheckSum: 67da6
Characteristics: 2022  
Debug Data Dirs: Type  Size     VA  PointerCODEVIEW    78, 43e90,   42a90 RSDS - GUID: {08FF691A-7607-413D-9F3C-7DD20738D992}Age: 1, Pdb: e:\xxx\TRUSTONE_EDR\xxx\Win64\AisEsmUmh_X64.pdbVC_FEATURE    10, 43f08,   42b08 [Data not mapped]Image Type: MEMORY   - Image read successfully from loaded memory.Symbol Type: NONE     - PDB not found from symbol server.Load Report: no symbols loaded

从卦中可以看到,这个 AisEsmUmh_X64 目录中有一个 TRUSTONE_EDR 路径,看到这个 EDR(Endpoint Detection and Response) 我相信大家都知道是什么了,对。。。 就是安全软件,最后在 baidu 上搜一下 AisEsmUmh_X64 即可。

原来是 亚信安全 捣的鬼。。。到这里就彻底水落石出了。

3. 亚信安全 为什么要拦截

最后我们稍微解释下为什么会有拦截行为,大家都知道托管语言都有 GC 这么个东西,GC触发的时候会暂停所有的托管线程,俗称 STW(Stop The World),这里的暂停是一个抽象概念,CLR 会通过各种途径将 托管线程 引流到一个 event 事件上,其中就不乏 HiJack 方式,这个在coreclr源码中都是有痕迹的,截图如下:

这种 线程劫持 行为是被大多数杀毒软件所不容的,有些智能点的杀毒软件会放行,有些则不放,最后就导致这种灾难性的后果。

解决办法也比较简单:

  1. 关闭 亚信安全。
  2. 添加 白名单,或者你懂的的方式

三:总结

这次卡死事故原因是安全软件介入了 GC 过程,让 STW 迟迟得不到结束,哎,窗体类程序的生存环境举步维艰啊。。。

http://www.dtcms.com/a/327842.html

相关文章:

  • 腾讯云terraform学习教程
  • 传输线的效应
  • 【MAUI】在 .NET MAUI 中实现全局异常捕获的完整指南
  • 五、Nginx、RabbitMQ和Redis在Linux中的安装和部署
  • DAY41 简单CNN
  • PostgreSQL——数据查询
  • PyCharm Community 2024.2.3.exe 安装教程(详细步骤,附安装包下载)
  • Docker守护进程安全加固在香港VPS环境的操作标准
  • vue3使用插槽写一个自定义瀑布列表
  • 海康视觉相机驱动软件参数配置
  • 用 Docker 安装并启动 MySQL:从零到实战的完整指南
  • vivo Pulsar 万亿级消息处理实践(2)-从0到1建设 Pulsar 指标监控链路
  • 人工智能与金融:金融行业的革新
  • 计算机网络摘星题库800题笔记 第3章 数据链路层
  • linux Phy驱动开发之mido总线介绍
  • 打印流水号标签
  • 三防手机和防爆手机的本质区别是什么?
  • INSAR数据处理---ENVI5.6(Sarscape)
  • 【从零开始java学习|第三篇】变量与数据类型的关联
  • 秋招笔记-8.9
  • 【网络运维】Linux和自动化: Ansible基础实践
  • SynAdapt:通过合成连续思维链实现大语言模型的自适应推理
  • 机器学习第十课之TF-IDF算法(红楼梦文本分析)
  • 服务器节点技术解析:从架构原理到家庭实践的全维度指南
  • 文件IO函数实现
  • 异或和查询
  • 【报错处理】mount: /boot/efi: unknown filesystem type ‘LVM2_member‘.
  • ARM基础概念 异常处理01 day52
  • 前端项目一键换肤
  • Web 服务详解:HTTP 与 HTTPS 配置