记一次 .NET某汗液测试机系统 崩溃分析

一:背景

1. 讲故事

上个月在社区写的文章比较少,一直关注的朋友应该知道那段时间被狗咬了以及一些琐事处理,所以手头上也攒了不少需要分享的案例,这段时间比较空闲,逐个给大
家做个分享吧,刚好年后为新版的 .NET高级调试训练营 做案例储备,所以抓紧时间疯狂输出吧!

这次生产事故的dump是训练营里一位朋友给到我的,由于朋友没有分析出来,让我帮忙看看,毕竟我的修车经验相对来说更丰富一些,算是他们背后坚实的保障吧,话不多说上windbg说话。

二:WinDbg分析

1. 为什么会崩溃

由于windbg默认自动定位到崩溃的线程,而崩溃的dump重点是观察它的崩溃前上下文,这里使用 .ecxrk 命令,输出参考如下:


0:000> .ecxr
eax=00000000 ebx=4d6d8360 ecx=00000003 edx=00000000 esi=4d6f0ca0 edi=4d6f0c74
eip=71a567c7 esp=026fd834 ebp=026fd83c iopl=0         nv up di pl nz na po nc
cs=0000  ss=0000  ds=0000  es=0000  fs=0000  gs=0000             efl=00000000
System_Windows_Forms_ni!System.Windows.Forms.ImageList.ImageCollection.SetKeyName+0x1b:
71a567c7 cc              int     30:000> k*** Stack trace for last set context - .thread/.cxr resets it# ChildEBP RetAddr      
00 026fd83c 0c2c4e7e     System_Windows_Forms_ni!System.Windows.Forms.ImageList.ImageCollection.SetKeyName+0x1b
01 026fe474 0c2c063b     xxx!xxx.MainForm.InitializeComponent+0x198e
02 026fe488 095cb9de     xxx!xxx.MainForm..ctor+0x5fb
03 026fe4e4 0da5bc7a     xxx!xxx.LoginForm.button_OK_Click+0x52e
04 026fe4f8 71a38bdf     xxx!xxx.LoginForm.LoginForm_Load+0x9a
05 026fe528 710b325a     System_Windows_Forms_ni!System.Windows.Forms.Form.OnLoad+0x2f
...

从卦象看是崩溃在 System.Windows.Forms.ImageList.ImageCollection.SetKeyName 方法上,很显然这个方法属于微软的SDK底层库,不管怎么说是一个托管异常,既然是托管异常我们可以用 !t 观察到具体的崩溃信息。


0:000> !t
ThreadCount:      26
UnstartedThread:  0
BackgroundThread: 12
PendingThread:    0
DeadThread:       13
Hosted Runtime:   noLock  ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception0    1  534 0299e700     a6028 Preemptive  4D6F0EFC:00000000 02997bd0 0     STA System.IndexOutOfRangeException 4d6f0ca02    2  5b4 029af278     2b228 Preemptive  00000000:00000000 02997bd0 0     MTA (Finalizer) 3    6  ff0 02a60eb0   102a228 Preemptive  00000000:00000000 02997bd0 0     MTA (Threadpool Worker) ...0:000> !pe
Exception object: 4d6f0ca0
Exception type:   System.IndexOutOfRangeException
Message:          Index was outside the bounds of the array.
InnerException:   <none>
StackTrace (generated):SP       IP       Function026FD834 71A567C7 System_Windows_Forms_ni!System.Windows.Forms.ImageList+ImageCollection.SetKeyName(Int32, System.String)+0x33e157StackTraceString: <none>
HResult: 80131508

从卦象看非常奇怪,怎么底层库中抛了一个数组索引越界异常?难道是底层的bug?一般来说这些代码都是铜墙铁壁,固若金汤,坚如磐石,稳如泰山,无懈可击,不可能有如此低级的bug。。。

2. 真的是底层库bug吗?

要想寻找答案,可以根据线程栈上的函数寻找底层源码,从源码上寻找答案,修剪后的代码如下:

private void InitializeComponent(){this.imageList_btnbg.ImageStream = (System.Windows.Forms.ImageListStreamer)resources.GetObject("imageList_btnbg.ImageStream");this.imageList_btnbg.TransparentColor = System.Drawing.Color.Transparent;this.imageList_btnbg.Images.SetKeyName(0, "normal-main.bmp");this.imageList_btnbg.Images.SetKeyName(1, "focus-main.bmp");this.imageList_btnbg.Images.SetKeyName(2, "select-main.bmp");this.imageList_btnbg.Images.SetKeyName(3, "gray-main.bmp");this.imageList_btnbg.Images.SetKeyName(4, "down_1.png");this.imageList_btnbg.Images.SetKeyName(5, "down_2.png");this.imageList_btnbg.Images.SetKeyName(6, "down_3.png");this.imageList_btnbg.Images.SetKeyName(7, "up_1.png");this.imageList_btnbg.Images.SetKeyName(8, "up_2.png");this.imageList_btnbg.Images.SetKeyName(9, "up_3.png");}public void SetKeyName(int index, string name){if (!IsValidIndex(index)){throw new IndexOutOfRangeException();}if (imageInfoCollection[index] == null){imageInfoCollection[index] = new ImageInfo();}((ImageInfo)imageInfoCollection[index]).Name = name;}private bool IsValidIndex(int index){if (index >= 0){return index < Count;}return false;}public int Count{get{if (owner.HandleCreated){return SafeNativeMethods.ImageList_GetImageCount(new HandleRef(owner, owner.Handle));}int num = 0;foreach (Original original in owner.originals){if (original != null){num += original.nImages;}}return num;}}[Browsable(false)][EditorBrowsable(EditorBrowsableState.Advanced)][DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)][SRDescription("ImageListHandleCreatedDescr")]public bool HandleCreated => nativeImageList != null;

仔细通读卦中的代码逻辑,看样子是 IsValidIndex()=false 导致的手工 IndexOutOfRangeException 异常,而 IsValidIndex()=false 是由于 index < Count 的条件成立,后面的 Count 是取自 ImageList_GetImageCount 或者 owner.originals 值。

代码逻辑我们是分析清楚了,接下来就是看汇编来分析下这个dump的现状,入手点就是从 index 值入手,即对 InitializeComponent() 方法进行反汇编。


0:000> !clrstack
OS Thread Id: 0x534 (0)
Child SP       IP Call Site
026fd784 771316bc [HelperMethodFrame: 026fd784] 
026fd834 71a567c7 System.Windows.Forms.ImageList+ImageCollection.SetKeyName(Int32, System.String)
026fd848 0c2c4e7e xxx.MainForm.InitializeComponent()
026fe47c 0c2c063b xxx.MainForm..ctor()
026fe490 095cb9de xxx.LoginForm.button_OK_Click(System.Object, System.EventArgs)
...0:000> !U /d 0c2c4e7e
Normal JIT generated code
xxx.MainForm.InitializeComponent()
Begin 0c2c34f0, size 5ded
...
0c2c4e62 e8d9b5d864      call    System_Windows_Forms_ni+0x160440 (71050440) (System.Windows.Forms.ImageList.get_Images(), mdToken: 06002599)
0c2c4e67 898514f5ffff    mov     dword ptr [ebp-0AECh],eax
0c2c4e6d ff35f0a07f05    push    dword ptr ds:[57FA0F0h] ("normal-main.bmp")
0c2c4e73 8bc8            mov     ecx,eax
0c2c4e75 33d2            xor     edx,edx
0c2c4e77 3909            cmp     dword ptr [ecx],ecx
0c2c4e79 e8f2374565      call    System_Windows_Forms_ni!System.Windows.Forms.ImageList.ImageCollection.SetKeyName (71718670)
>>> 0c2c4e7e 8b8e74020000    mov     ecx,dword ptr [esi+274h]
...

从卦象看,尼玛。。。执行第一个 SetKeyName(0, "normal-main.bmp"); 就异常啦,这就说明那个 Count=0,无语了,为什么 Count=0 呢? 接下来寻找Count数据源ImageCollection 集合,可以从线程栈中寻找,使用 !dso 命令即可。

0:000> !dso
OS Thread Id: 0x534 (0)
ESP/REG  Object   Name
026FD774 4d6f0c74 System.Windows.Forms.ImageList+ImageCollection
026FD778 4d6f0ca0 System.IndexOutOfRangeException
...0:000> !do 4d6f0c74
Name:        System.Windows.Forms.ImageList+ImageCollection
MethodTable: 71120ff0
EEClass:     70f230ec
Size:        20(0x14) 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
7111ecc0  4003916        4 ...s.Forms.ImageList  0 instance 4d6d97b0 owner
72d909dc  4003917        8 ...ections.ArrayList  0 instance 4d6f0c88 imageInfoCollection
72d8df5c  4003918        c         System.Int32  1 instance       -1 lastAccessedIndex
0:000> !DumpObj /d 4d6d97b0
Name:        System.Windows.Forms.ImageList
...
Fields:MT    Field   Offset                 Type VT     Attr    Value Name
71121b0c  4001013       10 ...t+NativeImageList  0 instance 4d6f0c40 nativeImageList
728e15a0  4001019       1c ...Collections.IList  0 instance 00000000 originals

根据卦中的 nativeImageListoriginals 再配合源代码,应该就是祸首 SafeNativeMethods.ImageList_GetImageCount 方法返回 0 导致的,先观察一下它的签名。


[DllImport("comctl32.dll")]
public static extern int ImageList_GetImageCount(HandleRef himl);

从签名看这是C++写的外部方法,这就沃草了。。。我总不能用 ida 去捋这里面的逻辑吧。。。到这里貌似已经快要撞到南墙了。。。有点慌了。

3. 天要绝人之路吗

经过短暂的恍恍惚惚之后,我突然灵光一现,尼玛这是32bit的内存地址,是不是2G的空间不够用哦?刚好 ImageList_GetImageCount 是一个关于图片的UI控件,用了底层的COM资源,会不会真的是空间不足导致的?有了这个想法之后赶紧 !address -summary 观察提交内存。


0:000> !address -summary
...
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_COMMIT                             1933          6e768000 (   1.726 GB)  93.52%   86.30%
MEM_FREE                                631           9e01000 ( 158.004 MB)            7.72%
MEM_RESERVE                             607           7a87000 ( 122.527 MB)   6.48%    5.98%
...

尼玛。。。卦象中的 MEM_COMMIT=1.72G, %ofBusy= 93.52% 早已超过了1.2G的临界值,终于真相大白。。。

解决办法就比较简单了,开启大地址,让程序吃 4G 的内存,后来朋友反馈这个问题已不再出来。。。

三:总结

分析完这个dump之后其实我挺感慨的,人生也如此dump一样,在真相和假象之间不断的交织穿梭,有些人走出来了,有些人永远留在了里面。。。
图片名称

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/861814.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Abp vNext 扩展属性

扩展属性我们发现abp的默认都会有一个ExtraProperties属性,那么他的作用是什么呢?当我们使用abp的时候,需要对其原有的表进行扩展或者添加我们想要的字段,又不想改源码,以最小的方式实现,这个时候就体现它的价值了。 我们添加的数据都会在ExtraProperties以JSON对象方式进…

DL00230-基于Transformer的高光谱解混完整实现python

完整链接: https://item.taobao.com/item.htm?ft=t&id=794567466608&spm=a21dvs.23580594.0.0.3bc12c1bIJwjmP

WinCC动态修改画面窗口变量前缀

include"apdefao.h" void OnLButtonDown(char* lpszPictureName,char* lpszObjectName,char* lpszPropertyName) { setPropBOOL(lps) }

低代码必备:90%的企业都在使用的9款项目管理软件

引言 在数字化转型的浪潮中,低代码技术逐渐成为企业提升项目管理效率的重要工具。这9款项目管理软件涵盖了不同类型的工具,包括传统的项目管理软件、新兴的云平台以及行业特定的解决方案。它们在企业的项目管理中发挥着关键作用,为企业带来了更高的效率、更灵活的业务流程以…

【数字化转型】 IT如何做规划?

IT部门规划1. IT未来的愿景是什么? 2. 如何构建架构蓝图? 3. IT部门应该怎么规划?4. XD历次IT规划实例IT规划是基于公司战略来制定的,而企业架构衔接战略与项目实施落地的桥梁——承载IT的战略,指导IT实施。那么,到底应该怎么做IT规划,IT规划与企业架构之间应该如何关联…

4.4圆角边框

圆形边框实现图片居中

DVWA靶场Command Injection(命令注入) 漏洞low(低),medium(中等),high(高)所有级别通关教程及源码审计

命令注入 命令注入漏洞是一种安全漏洞,攻击者可以通过向应用程序输入恶意命令,诱使系统执行这些命令,从而达到未授权访问、数据篡改、系统控制等目的。该漏洞通常出现在应用程序未对用户输入进行充分验证和清理时 常见管道符: ; 前面的执行完执行后面的 | 上一条命…

车身舒适系统HIL仿真测试解决方案

车身舒适控制系统是指为驾驶人员提供舒适性控制的装置,伴随着车身舒适系统日益复杂化,系统级的自动测试手段对于保证现今复杂车身舒适系统的功能稳定性验证非常重要,经纬恒润基于HIRAIN TESTBASE平台开发的车身舒适系统测试系统,针对车身舒适域的电控单元实现了在实验室条件…

【数据清洗秘籍】如何避免Pandas中的科学计数法陷阱

在数据分析的世界里,数据清洗是一项不可或缺的工作。我们经常需要将数据从一种格式转换为另一种格式,以适应分析的需求。然而,在处理数值数据时,一个常见的问题就是数值被自动转换为科学计数法,尤其是当数值非常大时。这不仅影响了数据的可读性,还可能对后续的分析造成影…

线性化注意力综述:突破Softmax二次复杂度瓶颈的高效计算方案

大型语言模型在各个领域都展现出了卓越的性能,但其核心组件之一——softmax注意力机制在计算资源消耗方面存在显著局限性。本文将深入探讨如何通过替代方案实现线性时间复杂度,从而突破这一计算瓶颈。注意力机制基础理论 本文假设读者已经熟悉ChatGPT、Claude等模型及其底层的…

shadcn 表格有间距,然后给某一行加边框显示不出来,用div覆盖

`pointer-events-none` 到边框 `div` 上,以确保它不会干扰用户与表格的交互。 {isToday && ( <div className="absolute inset-0 border-2 border-yellow-400 pointer-events-none" /> )}

[241230]相较于NSAID和csD, TNFi显著降低axSpA髋和脊柱的骨折风险

该研究最早展示于ACR2024年会, 目前已提前在线发表于ACR官刊A&RDriscoll D, et al. Association of Therapies for Axial Spondyloarthritis on the Risk of Hip and Spine Fractures. Arthritis Rheumatol. 2024 Dec 16. https://pubmed.ncbi.nlm.nih.gov/39679763/ ◀