记一次.net某医疗器械清洗系统卡死分析(代码片段)

author author     2023-04-18     524

关键词:

一:背景

1. 讲故事

前段时间协助训练营里的一位朋友分析了一个程序卡死的问题,回过头来看这个案例比较经典,这篇稍微整理一下供后来者少踩坑吧。

二:WinDbg 分析

1. 为什么会卡死

因为是窗体程序,理所当然就是看主线程此时正在做什么? 可以用 ~0s ; k 看一下便知。


0:000> k
 # ChildEBP RetAddr      
00 00aff168 75e3bb0a     win32u!NtUserPeekMessage+0xc
01 00aff168 75e3ba7e     USER32!_PeekMessage+0x2a
02 00aff1a4 6a5d711c     USER32!PeekMessageW+0x16e
03 00aff1f0 6a5841a6     System_Windows_Forms_ni+0x23711c
...
17 00afffbc 00000000     ntdll!_RtlUserThreadStart+0x1b

从线程栈来看,当前的方法卡在 win32u!NtUserPeekMessage 上, 熟悉 Windows 窗体消息的朋友都知道这是提取 消息队列 的常规逻辑,这个方法的下一步就是通过 Wow64SystemServiceCall 进入到 Windows内核态,可以用 u 命令验证一下。


0:000> ub win32u!NtUserPeekMessage+0xc
761d1010 b801100000      mov     eax,1001h
761d1015 ba10631d76      mov     edx,offset win32u!Wow64SystemServiceCall (761d6310)
761d101a ffd2            call    edx

朋友也给我截了图,确实出现了卡死,那接下来的问题就是看下当前线程在 内核态 到底在做什么?

2. 真的卡在内核态吗

幸好朋友可以在卡死的机器上安装 windbg,让朋友在卡死的时候使用 Attch to kernel 的方式观察内核态,截图如下:

附加成功后,可以用 !process 0 f xxxx.exe 看到主线程的线程栈。


lkd> !process 0 f xxxx.exe
PROCESS ffffab8ebea75080
    SessionId: 1  Cid: 0f78    Peb: 009f1000  ParentCid: 1134
        ...
        THREAD ffffab8ecad14540  Cid 0f78.38f8  Teb: 00000000009f3000 Win32Thread: ffffab8ecd5dabc0 WAIT: (WrUserRequest) UserMode Non-Alertable
            ffffab8ecb31bcc0  QueueObject
        IRP List:
            ffffab8ecad82b20: (0006,0478) Flags: 00060000  Mdl: 00000000
        Not impersonating
        DeviceMap                 ffffd400aa7eed50
        Owning Process            ffffab8ebea75080       Image:         xxxx.exe
        Attached Process          N/A            Image:         N/A
        Wait Start TickCount      1117311        Ticks: 9265 (0:00:02:24.765)
        Context Switch Count      60628          IdealProcessor: 2  NoStackSwap
        UserTime                  00:00:10.796
        KernelTime                00:00:06.593
        Win32 Start Address 0x00000000006e16aa
        Stack Init ffffa88b5b18fb90 Current ffffa88b5b18e780
        Base ffffa88b5b190000 Limit ffffa88b5b189000 Call 0000000000000000
        Priority 10 BasePriority 8 PriorityDecrement 0 IoPriority 2 PagePriority 5
        Child-SP          RetAddr               Call Site
        ffffa88b`5b18e7c0 fffff806`6627e370     nt!KiSwapContext+0x76
        ffffa88b`5b18e900 fffff806`6627d89f     nt!KiSwapThread+0x500
        ffffa88b`5b18e9b0 fffff806`6627d143     nt!KiCommitThreadWait+0x14f
        ffffa88b`5b18ea50 fffff806`6628679b     nt!KeWaitForSingleObject+0x233
        ffffa88b`5b18eb40 ffffa9d4`bdd32b12     nt!KeWaitForMultipleObjects+0x45b
        ffffa88b`5b18ec50 ffffa9d4`bdd352d9     win32kfull!xxxRealSleepThread+0x362
        ffffa88b`5b18ed70 ffffa9d4`bdd33f8a     win32kfull!xxxInterSendMsgEx+0xdd9
        ffffa88b`5b18eee0 ffffa9d4`bdd37870     win32kfull!xxxSendTransformableMessageTimeout+0x3ea
        ffffa88b`5b18f030 ffffa9d4`bdf1e088     win32kfull!xxxSendMessage+0x2c
        ffffa88b`5b18f090 ffffa9d4`bdf1e0e9     win32kfull!xxxCompositedTraverse+0x40
        ffffa88b`5b18f0e0 ffffa9d4`bdf1e0e9     win32kfull!xxxCompositedTraverse+0xa1
        ffffa88b`5b18f130 ffffa9d4`bdf1e0e9     win32kfull!xxxCompositedTraverse+0xa1
        ffffa88b`5b18f180 ffffa9d4`bdf1e0e9     win32kfull!xxxCompositedTraverse+0xa1
        ffffa88b`5b18f1d0 ffffa9d4`bdf1e2a7     win32kfull!xxxCompositedTraverse+0xa1
        ffffa88b`5b18f220 ffffa9d4`bde5a013     win32kfull!xxxCompositedPaint+0x37
        ffffa88b`5b18f2b0 ffffa9d4`bdd2e438     win32kfull!xxxInternalDoPaint+0x12bce3
        ffffa88b`5b18f300 ffffa9d4`bdd2e03a     win32kfull!xxxInternalDoPaint+0x108
        ffffa88b`5b18f350 ffffa9d4`bdd30f1c     win32kfull!xxxDoPaint+0x52
        ffffa88b`5b18f3b0 ffffa9d4`bdd2ff08     win32kfull!xxxRealInternalGetMessage+0xfac
        ffffa88b`5b18f880 ffffa9d4`be1871ce     win32kfull!NtUserPeekMessage+0x158
        ffffa88b`5b18f940 fffff806`6640d8f5     win32k!NtUserPeekMessage+0x2a
        ffffa88b`5b18f990 00007ffe`1816ff74     nt!KiSystemServiceCopyEnd+0x25 (TrapFrame @ ffffa88b`5b18fa00)
        00000000`0077e558 00000000`00000000     0x00007ffe`1816ff74

如果线程信息很少的话,可以用 .process 将此进程作为当前上下文,然后加载用户符号,输出如下:


lkd> .process ffffab8ebea75080
Implicit process is now ffffab8e`bea75080
lkd> .reload
Connected to Windows 10 19041 x64 target at (Tue Mar 21 13:21:21.213 2023 (UTC + 8:00)), ptr64 TRUE
Loading Kernel Symbols
...............................................................
................................................................
................................................................
.................
Loading User Symbols
PEB is paged out (Peb.Ldr = 00000000`009f1018).  Type ".hh dbgerr001" for details
Loading unloaded module list

从刚才的线程栈上看,很明显有一个 win32kfull!xxxSendMessage+0x2c 方法,熟悉 SendMessage 的朋友都知道这个是用来向某个窗体发消息的,那到底是哪一个窗体呢?

3. 到底给哪个窗体发消息

要想获取发送窗体的句柄,需要提取 win32kfull!xxxSendMessage 方法的第一个参数,在 x64 的调用协定下,它是用 rcx 传递的,需要分析下汇编代码,如果 rcx 没有放到栈里,那就无法提取了。

为了少点麻烦,建议让朋友看下 32bit 的操作系统上是否也有这个问题?结果反馈说也存在,使用 !thread xxx 切到目标线程,使用 kb 提取第一个参数地址上的值,即:00010598,截图如下:

丢了一个 sdbgext 插件让朋友看下窗体句柄信息,发现是个 64bit 的,其实除了它还可以用 Spy++ 观察窗体句柄,重点就是找到这个神秘窗体 是由哪个进程下的线程创建的,当把句柄号丢进去后还真给找到了,有点黑暗中寻找到了曙光。截图如下:

从 Spy++ 看当前窗体是由进程号:000016E0下的线程号0000109C 创建的,经过比对,这个线程就是本进程的某个线程号。

分析到这里其实就很明朗了,是因为这个线程 0000109C 创建了一个用户控件,导致内核态 在某种情况下给它发消息,接下来就是寻找到底是什么控件创建的。

4. 罪魁祸首

关于非主线程创建用户控件导致的卡死,我感觉都已经说破嘴皮了,还是有非常多的人犯这个毛病,无语哈,解决办法就是用 bp 去拦截 System.Windows.Forms.Application+MarshalingControl..ctor 方法,具体方案可参考我的文章:【一个超经典 WinForm 卡死问题的再反思】https://www.cnblogs.com/huangxincheng/p/16868486.html

接下来就是朋友的苦苦调试,终于给找到了,截图如下:

对,就是这么一句 Intptr handle =this.Handle 代码,内核句柄的获取让它在这个线程上生根了。

三:总结

就是这么一句代码,来来回回兜了好几圈,花费了朋友个把星期,终于给解决了,也算是一个好结果吧,这个案例需要实时观察程序的内核态用户态,看 dump 效果不大,造成了这么多时间的浪费。

相信这个案例也让公司老板对他 刮目相看

记一次.net某医疗器械程序崩溃分析(代码片段)

一:背景1.讲故事前段时间有位朋友在微信上找到我,说他的程序偶发性崩溃,让我帮忙看下怎么回事,上面给的压力比较大,对于这种偶发性崩溃,比较好的办法就是利用AEDebug在程序崩溃的时候自动抽一... 查看详情

记一次.net某医疗住院系统崩溃分析(代码片段)

一:背景1.讲故事最近收到了两起程序崩溃的dump,查了下都是经典的doublefree造成的,蛮有意思,这里就抽一篇出来分享一下经验供后面的学习者避坑吧。二:WinDbg分析1.崩溃点在哪里windbg带了一个自动化分析... 查看详情

记一次.net某化妆品webapi卡死分析(代码片段)

一:背景1.讲故事10月份星球里的一位老朋友找到我,说他们公司的程序在一个网红直播带货下给弄得无响应了,无响应期间有大量的RabbitMQ超时,寻求如何找到根源,聊天截图我就不发了。既然无响应了,那必然是程序的大量线... 查看详情

记一次.net某金融企业wpf程序卡死分析(代码片段)

一:背景1.讲故事前段时间遇到了一个难度比较高的dump,经过几个小时的探索,终于给找出来了,在这里做一下整理,希望对大家有所帮助,对自己也是一个总结,好了,老规矩,上WinDBG说话。... 查看详情

记一次.net某智能服装智造系统内存泄漏分析(代码片段)

一:背景1.讲故事上个月有位朋友找到我,说他的程序出现了内存泄漏,不知道如何进一步分析,截图如下:朋友这段话已经说的非常言简意赅了,那就上windbg说话吧。二:Windbg分析1.到底是哪一方面的泄漏根据朋友描述,程序... 查看详情

记一次.net某新能源系统线程疯涨分析(代码片段)

一:背景1.讲故事前段时间收到一个朋友的求助,说他的程序线程数疯涨,寻求如何解决。等我分析完之后,我觉得这个问题很有代表性,所以拿出来和大家分享下,还是上老工具WinDbg。二:WinDbg分析1.... 查看详情

记一次.net某制造业mes系统崩溃分析(代码片段)

一:背景1.讲故事前段时间有位朋友微信找到我,说他的程序偶尔会出现内存溢出崩溃,让我帮忙看下是怎么回事,咨询了下程序是x86部署,听到这个词其实心里已经有了数,不管怎么样还是用windbg分析一... 查看详情

记一次.net某新能源汽车锂电池检测程序ui挂死分析(代码片段)

一:背景1.讲故事这世间事说来也奇怪,近两个月有三位朋友找到我,让我帮忙分析下他的程序hangon现象,这三个dump分别涉及:医疗,新能源,POS系统。截图如下:那这篇为什么要拿其中的新能源... 查看详情

记一次.net某纺织工厂mes系统api挂死分析(代码片段)

一:背景1.讲故事这个月中旬,有位朋友加我wx求助他的程序线程占有率很高,寻求如何解决,截图如下:说实话,和不同行业的程序员聊天还是蛮有意思的,广交朋友,也能扩大自己的圈子,... 查看详情

记一次.net某wms仓储打单系统内存暴涨分析(代码片段)

一:背景1.讲故事七月中旬有一位朋友加wx求助,他的程序在生产上跑着跑着内存就飙起来了,貌似没有回头的趋势,询问如何解决,截图如下:和这位朋友聊下来,感觉像是自己在小县城当了个小老板... 查看详情

记一次.net某电厂web系统内存泄漏分析(代码片段)

一:背景1.讲故事前段时间有位朋友找到我,说他的程序内存占用比较大,寻求如何解决,截图就不发了,分析下来我感觉除了程序本身的问题之外,.NET5在内存管理方面做的也不够好,所以有必要给大... 查看详情

记一次.net某企业erp网站系统崩溃分析(代码片段)

一:背景1.讲故事前段时间收到了一个朋友的求助,说他的ERP网站系统会出现偶发性崩溃,找了好久也没找到是什么原因,让我帮忙看下,其实崩溃好说,用procdump自动抓一个就好,拿到dump之后,接下来就是一顿分析了。二:WinD... 查看详情

记一次.net某智慧物流wcs系统cpu爆高分析(代码片段)

一:背景1.讲故事哈哈,再次见到物流类软件,上个月有位朋友找到我,说他的程序出现了CPU爆高,让我帮忙看下什么原因,由于那段时间在苦心研究C++,分析和经验分享也就懈怠了,今天就... 查看详情

记一次.net某工控mes程序崩溃分析(代码片段)

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

记一次.net某手术室行为信息系统内存泄露分析(代码片段)

一:背景1.讲故事昨天有位朋友找到我,说他的程序内存存在泄露导致系统特别卡,大地址也开了,让我帮忙看一下怎么回事?今天上午看了下dump,感觉挺有意思,在我的分析之旅中此类问题也蛮少见,算是完善一下体系吧。二... 查看详情

记一次.net某机械臂智能机器人控制系统mrscpu爆高分析(代码片段)

一:背景1.讲故事这是6月中旬一位朋友加wx求助dump的故事,他的程序cpu爆高➕UI卡死,问如何解决,截图如下:在拿到这个dump后,我发现这是一个关于机械臂的MRS程序,哈哈,在机械臂这种智能机... 查看详情

记一次.net某电厂web系统内存泄漏分析

一:背景1.讲故事前段时间有位朋友找到我,说他的程序内存占用比较大,寻求如何解决,截图就不发了,分析下来我感觉除了程序本身的问题之外,.NET5在内存管理方面做的也不够好,所以有必要给大家分享一下。二:WinDbg分... 查看详情

记一次.net某家装erp系统内存暴涨分析(代码片段)

一:背景1.讲故事前段时间微信上有一位老朋友找到我,说他的程序跑着跑着内存会突然爆高,有时候会下去,有什么会下不去,怀疑是不是某些情况下存在内存泄露,让我帮忙分析一下,其实内存泄露方面的问题还是比较好解... 查看详情