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

推荐订阅源

S
SegmentFault 最新的问题
V
Visual Studio Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
量子位
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
GbyAI
GbyAI
爱范儿
爱范儿
Y
Y Combinator Blog
宝玉的分享
宝玉的分享
有赞技术团队
有赞技术团队
罗磊的独立博客
Recent Announcements
Recent Announcements
博客园 - 司徒正美
M
MIT News - Artificial intelligence
小众软件
小众软件
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
B
Blog RSS Feed
A
About on SuperTechFans
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research
雷峰网
雷峰网

博客园 - 一线码农

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

一:背景

1. 讲故事

前段时间有位训练营的学员找到我,说他们的软件在客户那边崩溃了,没找到是什么原因,比较着急,让我帮忙看下是怎么回事?毕竟我的学员是永久的免费dump分析,必须给他上一卦。

二:崩溃分析

1. 为什么会崩溃

关于怎么分析崩溃dump,这个在训练营里面早已整出来了套路,先用 !analyze -v 自动化分析崩溃原因,简化后如下:


0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************
CONTEXT:  (.ecxr)
eax=15c96638 ebx=010fecb0 ecx=00000000 edx=000109a8 esi=000109a8 edi=0000001c
eip=02f1d218 esp=010fec7c ebp=010feca8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
02f1d218 8b410c          mov     eax,dword ptr [ecx+0Ch] ds:002b:0000000c=????????
Resetting default scope

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 02f1d218
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 0000000c
Attempt to read from address 0000000c

STACK_TEXT:  
WARNING: Frame IP not in any known module. Following frames may be wrong.
010feca8 758d139b     000109a8 0000001c 00000000 0x2f1d218
010fecd4 758c836a     15c9664e 000109a8 0000001c user32!_InternalCallWinProc+0x2b
010fedb8 758c7f6a     15c9664e 00000000 0000001c user32!UserCallWinProcCheckWow+0x33a
010fee1c 758cbb2f     01aef180 00000000 0000001c user32!DispatchClientMessage+0xea
010fee58 77a64f5d     010fee74 00000020 010ff110 user32!__fnDWORD+0x3f
010feee0 758cbdca     010fefb8 00000000 00000000 ntdll!KiUserCallbackDispatcher+0x4d
010feee0 758cbd3e     00000000 00000000 00000000 user32!_PeekMessage+0x2a
010fef1c 6f8a707c     010fefb8 00000000 00000000 user32!PeekMessageW+0x16e
010fef68 6f85443a     00000000 00000000 00000000 System_Windows_Forms_ni+0x22707c
010feffc 6f8540d1     00000000 ffffffff 00000000 System_Windows_Forms_ni!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop+0x1b6
010ff050 6f853f23     00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner+0x175
010ff07c 6f82c83d     00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Application.ThreadContext.RunMessageLoop+0x4f
010ff094 02fa0b04     00000000 00000000 00000000 System_Windows_Forms_ni!System.Windows.Forms.Application.Run+0x35
010ff0f8 7337f066     00000000 00000000 00000000 xxx!xxx.Program.Main+0x2bc
...

从卦中的 DispatchClientMessage 来看,这是提取到了消息队列中的消息,在 0x2f1d218 处出现了访问违例,接下来的问题是寻找到底在处理啥消息?

2. 到底在处理什么消息

要想找到这个问题的答案,可以通过 !dso 在调用栈上寻找 MSG 结构体,简化后的输出如下:


0:000> !dso
OS Thread Id: 0x20b0 (0)
ESP/REG  Object   Name
010FEF9C 175ea6ec System.Windows.Forms.NativeMethods+MSG[]

0:000> !mdt -e:2 175ea6ec
175ea6ec (System.Windows.Forms.NativeMethods+MSG[], Elements: 1, ElementMT=6f688e60)
[0] (System.Windows.Forms.NativeMethods+MSG) VALTYPE (MT=6f688e60, ADDR=175ea6f4)
    hwnd:00140488 (System.IntPtr)
    message:0x113 (System.Int32)
    wParam:00000531 (System.IntPtr)
    lParam:00000000 (System.IntPtr)
    time:0xfbf4f32 (System.Int32)
    pt_x:0x118 (System.Int32)
    pt_y:0x42d (System.Int32)

从卦中的 message:0x113 来看,这是经典的 WM_TIMER 消息,即定时器事件,用 C# 的话术就是窗体的 Timer 控件,参考MSDN截图:

接下来的关注点就是分析崩溃处的汇编代码了,使用 ub 命令反编译,输出如下:


0:000> .ecxr
eax=15c96638 ebx=010fecb0 ecx=00000000 edx=000109a8 esi=000109a8 edi=0000001c
eip=02f1d218 esp=010fec7c ebp=010feca8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
02f1d218 8b410c          mov     eax,dword ptr [ecx+0Ch] ds:002b:0000000c=????????
0:000> ub 02f1d218 La
02f1d200 50              push    eax
02f1d201 107567          adc     byte ptr [ebp+67h],dh
02f1d204 51              push    ecx
02f1d205 83ec04          sub     esp,4
02f1d208 ff7304          push    dword ptr [ebx+4]
02f1d20b ff7308          push    dword ptr [ebx+8]
02f1d20e ff730c          push    dword ptr [ebx+0Ch]
02f1d211 8b13            mov     edx,dword ptr [ebx]
02f1d213 8b4808          mov     ecx,dword ptr [eax+8]
02f1d216 8b09            mov     ecx,dword ptr [ecx]

由于 02f1d218 处没有显示函数名,根据经验猜测,这个应该是 JIT 动态生成的小函数,并且 02f1d204 是函数的入口点,程序崩溃是因为执行了 ecx=0 导致的,接下来根据 ecx 的来源进行反推看看有没有新的发现,输出如下:


0:000> dp 15c96638+0x8 L1
15c96640  015a8658

0:000> dp 015a8658 L1
015a8658  00000000

0:000> !do 015a8658
<Note: this object has an invalid CLASS field>
Invalid object

0:000> !dumpmd 015a8658
015a8658 is not a MethodDesc

0:000> !dumpmt 015a8658
015a8658 is not a MethodTable

从卦中看没有任何发现,015a8658 既不是 obj,也不是 mt,也不是 md ,这一下子就把我打入了黑暗之渊。。。

3. 在绝望中寻找希望

一时也没想到好办法,到门口边抽烟边思考, message:0x113 是一个 Win32 的 Timer,应该是 Timer 的定时回调在JIT的函数中意外崩掉了,按道理说在崩溃处的内存附近应该能找到与之对应的C# Timer,有了这个想法之后就在 015a8658 附近内存查找,还真给找到了,参考如下:


0:000> dp 015a8658 L4
015a8658  00000000 2d61d1a8 2d4ef48c 00000000
0:000> !do 2d61d1a8
Name:        System.Windows.Forms.NativeMethods+WndProc
MethodTable: 6f687200
EEClass:     6f681458
Size:        32(0x20) bytes
File:        C:\windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
71ec2734  40002f3        4        System.Object  0 instance 2d61d164 _target
71ec2734  40002f4        8        System.Object  0 instance 00000000 _methodBase
71ec7b18  40002f5        c        System.IntPtr  1 instance  5b73c34 _methodPtr
71ec7b18  40002f6       10        System.IntPtr  1 instance        0 _methodPtrAux
71ec2734  4000300       14        System.Object  0 instance 00000000 _invocationList
71ec7b18  4000301       18        System.IntPtr  1 instance        0 _invocationCount

0:000> !do 2d61d164
Name:        System.Windows.Forms.Timer+TimerNativeWindow
MethodTable: 6f6995e4
EEClass:     6f6ede04
Size:        56(0x38) bytes
File:        C:\windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
71ec2734  40005ba        4        System.Object  0 instance 00000000 __identity
71ec7b18  4001cf9       18        System.IntPtr  1 instance        0 handle
6f687200  4001cfa        8 ...veMethods+WndProc  0 instance 2d61d1a8 windowProc
71ec7b18  4001cfb       1c        System.IntPtr  1 instance 15ca1bee windowProcPtr
71ec7b18  4001cfc       20        System.IntPtr  1 instance 77a77f70 defWindowProc
71ec878c  4001cfd       28       System.Boolean  1 instance        1 suppressedGC
71ec878c  4001cfe       29       System.Boolean  1 instance        0 ownHandle
6f685da8  4001cff        c ...orms.NativeWindow  0 instance 00000000 previousWindow
6f685da8  4001d00       10 ...orms.NativeWindow  0 instance 00000000 nextWindow
71ec6018  4001d01       14 System.WeakReference  0 instance 2d61d19c weakThisPtr
70229854  4001d02       24         System.Int32  1 instance        0 windowDpiAwarenessContext
713fe7cc  4001ce3      b88 ...stics.TraceSwitch  0   static 00000000 WndProcChoice
71ec426c  4001ce4      b8c       System.Int32[]  0   static 03111988 primes
71ec878c  4001ceb     1312       System.Boolean  1   static        1 anyHandleCreatedInApp
71ec42a8  4001ced     1304         System.Int32  1   static     1786 handleCount
71ec42a8  4001cee     1308         System.Int32  1   static     2915 hashLoadSize
6f685e9c  4001cef      b90 ...ow+HandleBucket[]  0   static 2c7b5f14 hashBuckets
71ec7b18  4001cf0     130c        System.IntPtr  1   static 77a77f70 userDefWindowProc
71ec3a08  4001cf3     1313          System.Byte  1   static        0 userSetProcFlagsForApp
71ec882c  4001cf4     1310         System.Int16  1   static        1 globalID
71f1c594  4001cf5      b94 ...ntPtr, mscorlib]]  0   static 03111bc8 hashForIdHandle
71f1c6d0  4001cf6      b98 ...Int16, mscorlib]]  0   static 03111c3c hashForHandleId
71ec2734  4001cf7      b9c        System.Object  0   static 03111b90 internalSyncObject
71ec2734  4001cf8      ba0        System.Object  0   static 03111b9c createWindowSyncObject
71ec878c  4001cea      979       System.Boolean  1 TLstatic  anyHandleCreated
    >> Thread:Value 20b0:1 <<
71ec3a08  4001cf1      97a          System.Byte  1 TLstatic  wndProcFlags
    >> Thread:Value 20b0:1 <<
71ec3a08  4001cf2      97b          System.Byte  1 TLstatic  userSetProcFlags
    >> Thread:Value 20b0:1 <<
6f69ad98  400415e       2c ...ndows.Forms.Timer  0 instance 14462858 _owner
71ec42a8  400415f       30         System.Int32  1 instance        0 _timerID
71ec878c  4004161       2a       System.Boolean  1 instance        0 _stoppingTimer
71ec42a8  4004160     190c         System.Int32  1   static     2462 TimerID

冥冥之中自有天意。。。一顿欣喜若狂之后,赶紧看看这个 Timer 来自于哪里,使用 !gcroot 2d61d164 即可。


0:000> !gcroot 2d61d164
Thread 20b0:
    010fef80 6f85443a System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)
        ebx:  (interior)
            ->  040e5568 System.Object[]
            ->  031ced2c System.Windows.Forms.FormCollection
            ->  031ced44 System.Collections.ArrayList
            ->  1df9aaec System.Object[]
            ...
            ->  144625fc DevComponents.DotNetBar.Controls.ComboBoxEx
            ->  14462858 System.Windows.Forms.Timer
            ->  2d61d164 System.Windows.Forms.Timer+TimerNativeWindow

从卦中的引用链来看,原来它是挂在 DevComponents.DotNetBar.Controls.ComboBoxEx 控件之下的,赶紧反向寻找源代码,截图如下:

尼玛居然是加密的,也是无语了,由于是 DevComponents 组件中的代码,赶紧看看组件的版本,结果发现是 2002 年的第一场雪,距今 23年,没有bug也是奇怪了。。。截图如下:

最后给到朋友的建议就是升级 DevComponents 或者寻找替代品。

三:总结

有人说bug分析就是一门法医学,不断的在绝望中寻找希望,千淘万漉虽辛苦,吹尽狂沙始到金!

图片名称