记一次 .NET某医疗器械清洗系统 卡死分析
一:背景
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 ffffab8ebea75080SessionId: 1 Cid: 0f78 Peb: 009f1000 ParentCid: 1134...THREAD ffffab8ecad14540 Cid 0f78.38f8 Teb: 00000000009f3000 Win32Thread: ffffab8ecd5dabc0 WAIT: (WrUserRequest) UserMode Non-Alertableffffab8ecb31bcc0 QueueObjectIRP List:ffffab8ecad82b20: (0006,0478) Flags: 00060000 Mdl: 00000000Not impersonatingDeviceMap ffffd400aa7eed50Owning Process ffffab8ebea75080 Image: xxxx.exeAttached Process N/A Image: N/AWait Start TickCount 1117311 Ticks: 9265 (0:00:02:24.765)Context Switch Count 60628 IdealProcessor: 2 NoStackSwapUserTime 00:00:10.796KernelTime 00:00:06.593Win32 Start Address 0x00000000006e16aaStack Init ffffa88b5b18fb90 Current ffffa88b5b18e780Base ffffa88b5b190000 Limit ffffa88b5b189000 Call 0000000000000000Priority 10 BasePriority 8 PriorityDecrement 0 IoPriority 2 PagePriority 5Child-SP RetAddr Call Siteffffa88b`5b18e7c0 fffff806`6627e370 nt!KiSwapContext+0x76ffffa88b`5b18e900 fffff806`6627d89f nt!KiSwapThread+0x500ffffa88b`5b18e9b0 fffff806`6627d143 nt!KiCommitThreadWait+0x14fffffa88b`5b18ea50 fffff806`6628679b nt!KeWaitForSingleObject+0x233ffffa88b`5b18eb40 ffffa9d4`bdd32b12 nt!KeWaitForMultipleObjects+0x45bffffa88b`5b18ec50 ffffa9d4`bdd352d9 win32kfull!xxxRealSleepThread+0x362ffffa88b`5b18ed70 ffffa9d4`bdd33f8a win32kfull!xxxInterSendMsgEx+0xdd9ffffa88b`5b18eee0 ffffa9d4`bdd37870 win32kfull!xxxSendTransformableMessageTimeout+0x3eaffffa88b`5b18f030 ffffa9d4`bdf1e088 win32kfull!xxxSendMessage+0x2cffffa88b`5b18f090 ffffa9d4`bdf1e0e9 win32kfull!xxxCompositedTraverse+0x40ffffa88b`5b18f0e0 ffffa9d4`bdf1e0e9 win32kfull!xxxCompositedTraverse+0xa1ffffa88b`5b18f130 ffffa9d4`bdf1e0e9 win32kfull!xxxCompositedTraverse+0xa1ffffa88b`5b18f180 ffffa9d4`bdf1e0e9 win32kfull!xxxCompositedTraverse+0xa1ffffa88b`5b18f1d0 ffffa9d4`bdf1e2a7 win32kfull!xxxCompositedTraverse+0xa1ffffa88b`5b18f220 ffffa9d4`bde5a013 win32kfull!xxxCompositedPaint+0x37ffffa88b`5b18f2b0 ffffa9d4`bdd2e438 win32kfull!xxxInternalDoPaint+0x12bce3ffffa88b`5b18f300 ffffa9d4`bdd2e03a win32kfull!xxxInternalDoPaint+0x108ffffa88b`5b18f350 ffffa9d4`bdd30f1c win32kfull!xxxDoPaint+0x52ffffa88b`5b18f3b0 ffffa9d4`bdd2ff08 win32kfull!xxxRealInternalGetMessage+0xfacffffa88b`5b18f880 ffffa9d4`be1871ce win32kfull!NtUserPeekMessage+0x158ffffa88b`5b18f940 fffff806`6640d8f5 win32k!NtUserPeekMessage+0x2affffa88b`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 卡死问题的再反思】
接下来就是朋友的苦苦调试,终于给找到了,截图如下:
对,就是这么一句 Intptr handle =this.Handle
代码,内核句柄的获取让它在这个线程上生根了。
三:总结
就是这么一句代码,来来回回兜了好几圈,花费了朋友个把星期,终于给解决了,也算是一个好结果吧,这个案例需要实时观察程序的内核态
和用户态
,看 dump 效果不大,造成了这么多时间的浪费。
相信这个案例也让公司老板对他 刮目相看
。
相关文章:

记一次 .NET某医疗器械清洗系统 卡死分析
一:背景 1. 讲故事 前段时间协助训练营里的一位朋友分析了一个程序卡死的问题,回过头来看这个案例比较经典,这篇稍微整理一下供后来者少踩坑吧。 二:WinDbg 分析 1. 为什么会卡死 因为是窗体程序,理所当然就是看主…...
C# 基于Rijndael对文件进行加解密
介绍: Rijndael 是一种对称加密算法,也是 AES(Advanced Encryption Standard)的前身。它用于数据的加密和解密,并提供了安全且高效的加密功能。 在.NET Framework 中,Rijndael 类是一个实现了 Rijndael 算法…...

Elasticsearchr入门
首先在官网下载elasticsearch8.9版本,以及8.9版本的kibana。 解压,点击es8.9bin目录下的elasticsearch.bat文件启动es 如图所示即为成功。 启动之后打开idea,添加依赖 <dependency><groupId>com.fasterxml.jackson.core</g…...
【ARM】imx6ul移植kernel记录,恩智浦github提供的最新kernel(2023年7月31)
❤️作者主页:凉开水白菜 ❤️作者简介:共同学习,互相监督,热于分享,多加讨论,一起进步! ❤️专栏目录: ❤️专栏资料: ❤️点赞 👍 收藏 ⭐再看,养成习惯 订阅的粉丝可通过PC端文末加我微信,可对文章的内容进行一对一答疑! 文章目录 一、简介二、源码下载三、官方…...

eeglab(自用)
目录 1.加载、显示数据 2.绘制脑电头皮图 3.绘制通道光谱图 4.预处理工具 5.ICA去除伪迹 5. 提取数据epoch 1.加载、显示数据 观察事件值(Event values):该数据集中包含2400个事件,每个事件指定了EEG.event结构的字段Type(类型)、position(位置)和…...

Dockerfile构建Tomcat镜像(源码)
Dockerfile构建Tomcat镜像 目录 Dockerfile构建Tomcat镜像 1、建立工作目录 2、编写Dockerfile文件 3、构建镜像 4、测试容器 5、浏览器访问测试: 1、建立工作目录 [roothuyang1 ~]# mkdir tomcat[roothuyang1 ~]# cd tomcat/[roothuyang1 tomcat]# lsapach…...
Frida Error: getPackageInfoNoCheck(): has more than one overload的解决方法
使用frida绕过证书的时候执行代码: frida -U -f de.robv.android.xposed.installer --codeshare akabe1/frida-multiple-unpinning --no-pause遇到这样的错误 Error: getPackageInfoNoCheck(): has more than one overload, use .overload() to choose from: 网上查…...
flutter开发实战-RawKeyboardListener监听键盘事件及keycode。
flutter开发实战-RawKeyboardListener监听键盘事件及keycode。 最近开发过程中遇到外设备的按钮点击触发相应的操作,需要监听对应的keycode来开启游戏或者相关操作。 这里用到了RawKeyboardListener 一、RawKeyboardListener是什么? RawKeyboardListe…...

Temu、希音们全托管引争议,跨境电商应变“工贸一体化”
自7月27日Shopee宣布正式上线全托管模式起,全托管似乎突然又进入了爆发期。 在7月31日至8月1日举行的2023第八届深圳国际跨境电商贸易博览会上,全托管成为SHEIN、Wish、Lazada等平台力推的运营模式。进入8月,跨境圈突然涌现大批传言称&#…...

某科技公司提前批测试岗
文章目录 题目 今天给大家带来一家提前批测试岗的真题,目前已经发offer 题目 1.自我介绍 2.登录页面测试用例设计 3.如何模拟多用户登录 可以使用Jmeter,loadRunner性能测试工具来模拟大量用户登录操作去观察一些参数变化 4.有使用过Jmeter,loadRunner做过性能压…...

一次redis缓存不均衡优化经验
背景 高并发接口,引入redis作为缓存之后,运行一段时间发现redis各个节点在高峰时段的访问量严重不均衡,有的节点访问量7000次/s,有的节点访问量500次/s 此种现象虽然暂时不影响系统使用,但是始终是个安全隐患&#x…...

npm发布包
1.npm 登录 在控制台输入命令 npm login 按提示输入用户名,密码,邮箱后登录 如果出现如下提示 需要将淘宝镜像源切换为npm源,删除或注释以下内容就行 2.发布 进入准备发布的代码的根目录下,输入命令 npm publish 3.删除已发…...

Qt5.13引入QtWebApp的模块后报错: error C2440: “reinterpret_cast”: 无法从“int”转换为“quintptr”
1、开发环境 Win10-64 qt5.13 msvc2015-64bit-release 2、报错 新建一个demo工程。 引入QtWebApp的httpserver、logging、templateengine三个模块后。 直接运行,,此时报错如下: E:\Qt5.13.1\install\5.13.1\msvc2015_64\include\QtCore…...

软件为什么要进行性能压力测试?
软件为什么要进行性能压力测试?随着软件应用的不断增多和复杂度的提高,软件的性能对用户体验和业务成功至关重要。性能问题可能导致软件运行缓慢、崩溃或无响应,给用户带来不便甚至损失。为了确保软件能够在高负载和压力下正常运行࿰…...

阻塞队列BlockingQueue详解
一、阻塞队列介绍 1、队列 队列入队从队首开始添加,直至队尾;出队从队首出队,直至队尾,所以入队和出队的顺序是一样的 Queue接口 add(E) :在指定队列容量条件下添加元素,若成功返回true,若当前…...

pygame贪吃蛇游戏
pygame贪吃蛇游戏 贪吃蛇游戏通过enter键启动,贪吃蛇通过WSAD进行上下左右移动,每次在游戏区域中随机生成一个食物,每次吃完食物后,蛇变长并且获得积分;按空格键暂停。 贪吃蛇 import random, sys, time, pygame from …...

Mac系统下使用远程桌面连接Windows系统
一、远程桌面工具 Microsoft Remote Desktop 二、下载地址 https://go.microsoft.com/fwlink/?linkid868963 三、下载并安装 四、添加远程PC PC name:云服务器IP。 User account: 添加系统用户 PC name:远程桌面 IP 地址User account:可以选择是…...
使用 OpenCV 和深度学习对黑白图像进行着色
在本文中,我们将创建一个程序将黑白图像(即灰度图像)转换为彩色图像。我们将为此程序使用 Caffe 着色模型。您应该熟悉基本的 OpenCV 功能和用法,例如读取图像或如何使用 dnn 模块加载预训练模型等。现在让我们讨论实现该程序所遵循的过程。 给定一张灰度照片作为输入,本文…...

从价值的角度看,为何 POSE 通证值得长期看好
PoseSwap 是 Nautilus Chain 上的首个 DEX,基于 Nautilus Chain 也让其成为了首个以模块化构建的 Layer3 架构的 DEX。该 DEX 本身能够以 Dapp 层(Rollup)的形态,与其他应用层并行化运行。...

pytorch的CrossEntropyLoss交叉熵损失函数默认reduction是平均值
pytorch中使用nn.CrossEntropyLoss()创建出来的交叉熵损失函数计算损失默认是求平均值的,即多个样本输入后获取的是一个均值标量,而不是样本大小的向量。 net nn.Linear(4, 2) loss nn.CrossEntropyLoss() X torch.rand(10, 4) y torch.ones(10, dt…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...