惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
WordPress大学
WordPress大学
C
Check Point Blog
GbyAI
GbyAI
U
Unit 42
Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
Blog — PlanetScale
Blog — PlanetScale
J
Java Code Geeks
I
InfoQ
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Hugging Face - Blog
Hugging Face - Blog
Vercel News
Vercel News
博客园 - 【当耐特】
美团技术团队
小众软件
小众软件
S
SegmentFault 最新的问题
Jina AI
Jina AI
阮一峰的网络日志
阮一峰的网络日志
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The Cloudflare Blog
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog

博客园 - 一线码农

记一次 .NET 某珠宝公司内部管理系统 内存暴涨分析 记一次 .NET 某智慧医保云服务Linux 非托管泄露分析 .NET 高级调试技术:超越基础 Dump 分析 .NET 生产环境调试实战指南 记一次 .NET 某光电成像后端解析系统 内存暴涨分析 记一次 .NET 某电力后台监控系统 内存暴涨分析 记一次 .NET 某注塑模具系统 CPU爆高分析 记一次 .NET 某集群管理软件 内存暴涨分析 记一次 .NET 某智慧工厂视觉程序 崩溃分析 记一次 .NET 某低代码开发框架 内存暴涨分析 记一次 .NET 某MES上位机拍照系统 内存暴涨分析 对 .NET FileSystemWatcher引发内存碎片化的 反思 DotMemory系列:5. 如何实现自动化抓取和应用自托管 DotMemory系列:4. 如何分析进程的转储文件 DotMemory系列:3. 堆碎片化引发的内存暴涨分析 DotMemory系列:2. 事件泄露引发的内存暴涨分析 DotMemory系列:1. 终结队列积压引发的内存暴涨分析 记一次 .NET 某理财管理客户端 OOM溢出分析 记一次 .NET 某医联体管理系统 崩溃分析 记一次 .NET 某光放测试系统 崩溃分析 记一次 .NET 某药品缺陷高速检测系统 卡慢分析 聊一聊 .NET超高内存故障分析方法 的反思 记一次 .NET 某企业ECM内容管理系统 内存暴涨分析 记一次 .NET 某跨境物流系统 内存暴涨分析 记一次 .NET 某中医药附属医院门诊系统 崩溃分析 聊一聊 .NET 中的 CompositeChangeToken 聊一聊 .NET 中的 CancellationTokenSource 记一次 .NET 某CRM物流行业管理系统 崩溃分析
记一次 .NET 某RFID标签打印客户端 崩溃分析
一线码农 · 2026-01-06 · via 博客园 - 一线码农

一:背景

1. 讲故事

去年微信上有位朋友找到我,说他们的RFID标签打印出现了偶发性崩溃,一直没找到原因,让我帮忙看下怎么回事?然后就让这位朋友用procdump抓一个崩溃dump给我,我看看就好。

二:崩溃分析

1. 为什么会崩溃

双击打开dump,windbg会自动定位到崩溃的上下文,这一点我比较喜欢,有的时候也省去了用 !analyze -v 无趣的等待,参考输出如下:


This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(4120.43a0): Access violation - code c0000005 (first/second chance not available)
For analysis of this file, run !analyze -v
clr!WKS::gc_heap::find_first_object+0xea:
00007ffd`9eaa7ecb 833800          cmp     dword ptr [rax],0 ds:30302c30`2c302c30=????????

从卦中的 find_first_object 来看,这是经典的 gc标记阶段,在进行深度优先遍历的时候发现了无效对象,进而引发灾难性后果,可以用 k 8 观察调用栈。


0:006> k 8
 # Child-SP          RetAddr               Call Site
00 0000000e`c103c4e8 00007ffd`9eaa8955     clr!WKS::gc_heap::find_first_object+0xea
01 0000000e`c103c500 00007ffd`9ea298aa     clr!WKS::GCHeap::Promote+0xc7
02 0000000e`c103c570 00007ffd`9eaf2822     clr!GcEnumObject+0x97
03 0000000e`c103c5c0 00007ffd`9ea27f68     clr!GcInfoDecoder::EnumerateLiveSlots+0x1856
04 0000000e`c103ca20 00007ffd`9ea2887f     clr!GcStackCrawlCallBack+0x2bd
05 0000000e`c103ce40 00007ffd`9eaa25d8     clr!GCToEEInterface::GcScanRoots+0x4b6
06 0000000e`c103e300 00007ffd`9eaa0e55     clr!WKS::gc_heap::mark_phase+0x1d9
07 0000000e`c103e3b0 00007ffd`9eaa0d6b     clr!WKS::gc_heap::gc1+0xef

2. 崩溃原因是什么

既然托管堆上有坏对象,那如何找到呢?可以用 !verifyheap 识别就好,参考输出如下:


0:006> !verifyheap 
Could not request method table data for object 0000015A9D59B0D0 (MethodTable: 30302C302C302C30).
Last good object: 0000015A9D59B048.

从卦中可以清晰的看到,Object 0000015A9D59B0D0的 MethodTable 30302C302C302C30 是一个无效值,从形态上看很像一段字符的ascii码,有点意思,接下来我们观察对象附近的内存,使用 dp 0000015A9D59B0D0-0xa0 L30 命令观察。


0:006> dp 0000015A9D59B0D0-0xa0 L30
0000015a`9d59b030  00000000`00000002 00000000`003a002f
0000015a`9d59b040  00000000`00000000 00007ffd`9cd985e0
0000015a`9d59b050  00000000`0000001c 00000002`00000001
0000015a`9d59b060  00000000`00000008 00000000`00000000
0000015a`9d59b070  00000000`00000000 00000000`00000000
0000015a`9d59b080  00000000`00000000 00000000`00000000
0000015a`9d59b090  00000000`00000000 00000000`00000000
0000015a`9d59b0a0  00000000`00000000 65636976`6564227b
0000015a`9d59b0b0  74735f74`736f682e 30223a22`73757461
0000015a`9d59b0c0  312c302c`302c3033 2c383330`2c383132
0000015a`9d59b0d0  30302c30`2c302c30 5c302c30`2c302c30
0000015a`9d59b0e0  302c3130`306e5c72 322c312c`302c302c
0000015a`9d59b0f0  3030302c`302c362c 2c312c31`30303030
0000015a`9d59b100  306e5c72`5c313030 007d2230`2c303030
0000015a`9d59b110  0000002e`00000001 00000000`00000000
0000015a`9d59b120  00000000`00000000 00007ffd`9cd95a68
0000015a`9d59b130  0073006d`00000005 00000073`006e0074
0000015a`9d59b140  00000000`00000000 00000000`00000000
0000015a`9d59b150  0000015a`9b4ddbb0 00000000`00000000
0000015a`9d59b160  00000000`00000000 00007ffd`9cd95a68
0000015a`9d59b170  00720050`00000013 00650074`006e0069
0000015a`9d59b180  00700061`00430072 006c0069`00620061
0000015a`9d59b190  00650069`00740069 00000000`00000073
0000015a`9d59b1a0  00000000`00000000 00007ffd`9cd96878

从卦中可以看到 0000015A9D59B0D0 附近被一段字符串覆盖了,看样子是有域外代码将string写溢出了。。。。接下来使用 da 把这段内容给 dig 出来。


0:006> da /c100 0000015a`9d59b0a0+0x8
0000015a`9d59b0a8  "{"device.host_status":"030,0,0,1218,038,0,0,0,000,0,0,0\r\n001,0,0,0,1,2,6,0,00000001,1,001\r\n0000,0"}"

从卦中看是一段json字符串,看样子应该是非托管代码回写string溢出了,但这个对象生前是不是string呢?这个只能在当前破坏现场寻找了,使用 !lno 观察附近的好对象。


0:006> !lno 0000015a9d59b128
Before:  0000015a9d59b048          136 (0x88)	System.Int32[]
Current: 0000015a9d59b128           40 (0x28)	System.String
After:   0000015a9d59b150           24 (0x18)	Free
Heap local consistency not confirmed.

0:006> !lno 0000015a9d59b150
Before:  0000015a9d59b048          136 (0x88)	System.Int32[]
Current: 0000015a9d59b150           24 (0x18)	Free
After:   0000015a9d59b168           64 (0x40)	System.String
Heap local consistency not confirmed.

0:006> !mdt -e:2 0000015a9d59b048
0000015a9d59b048 (System.Int32[], Elements: 28)
[0] 0x1
....
[16] 0x0
[17] 0x0
[18] 0x0
[19] 0x0
[20] 0x6564227b
[21] 0x65636976
[22] 0x736f682e
[23] 0x74735f74
[24] 0x73757461
[25] 0x30223a22
[26] 0x302c3033
[27] 0x312c302c

0:006> !do 0000015a9d59b168
Name:        System.String
MethodTable: 00007ffd9cd95a68
EEClass:     00007ffd9cd72ec0
Size:        64(0x40) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      PrinterCapabilities
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ffd9cd98648  4000283        8         System.Int32  1 instance               19 m_stringLength
00007ffd9cd968e0  4000284        c          System.Char  1 instance               50 m_firstChar
00007ffd9cd95a68  4000288       e0        System.String  0   shared           static Empty
                                 >> Domain:Value  0000015a9b506c70:NotInit  <<

0:006> !do 0000015a9d59b128
Name:        System.String
MethodTable: 00007ffd9cd95a68
EEClass:     00007ffd9cd72ec0
Size:        36(0x24) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      mstns
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ffd9cd98648  4000283        8         System.Int32  1 instance                5 m_stringLength
00007ffd9cd968e0  4000284        c          System.Char  1 instance               6d m_firstChar
00007ffd9cd95a68  4000288       e0        System.String  0   shared           static Empty
                                 >> Domain:Value  0000015a9b506c70:NotInit  <<

从卦中可以看到,这个 json 把 前面的 int[28] 也给部分破坏了,后面跟着字符串 PrinterCapabilitiesmstns,看样子这块和打印操作有关,将这些信息告诉朋友,让朋友重点关注下。

由于当前看到的是第二现场,无法知道谁导致的第一现场,如果想知道,需要上各种黑科技,这个在我之前的文章中多有涉及。

三:总结

这次生产事故还是挺有意思,比较考验你对托管堆以及对内存的敏感度。

图片名称