一:背景

1. 讲故事

今年年初有位朋友在微信上找到我,说他们的系统在客户这边崩掉了,在代码中也加了全局异常处理但还是崩,不知道咋回事,让朋友在客户那边拿程序dump,拿到dump之后开始分析。

二:崩溃分析

1. 为什么会崩溃

既然是崩溃,那就用 !analyze -v 命令观察下windbg给我们整理的崩溃信息,看看可有蛛丝马迹。


0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
******************************************************************************* CONTEXT: (.ecxr)
eax=006fdb40 ebx=00000005 ecx=00000005 edx=00000000 esi=006fdc04 edi=00000001
eip=768f9132 esp=006fdb40 ebp=006fdb9c iopl=0 nv up ei pl nz ac po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212
KERNELBASE!RaiseException+0x62:
768f9132 8b4c2454 mov ecx,dword ptr [esp+54h] ss:002b:006fdb94=005b1570
Resetting default scope EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 768f9132 (KERNELBASE!RaiseException+0x00000062)
ExceptionCode: e0434352 (CLR exception)
ExceptionFlags: 00000001
NumberParameters: 5
Parameter[0]: 80131604
Parameter[1]: 00000000
Parameter[2]: 00000000
Parameter[3]: 00000000
Parameter[4]: 72e90000 PROCESS_NAME: xxx.exe EXCEPTION_CODE_STR: 8007000e FAULTING_THREAD: ffffffff STACK_TEXT:
006fde30 07a0941c System_Drawing!System.Drawing.Graphics.FromHdcInternal+0x4c
006fde40 1173bbdb System_Drawing!System.Drawing.Font.GetHeight+0x5b
006fde70 1173bb4b System_Drawing!System.Drawing.Font.get_Height+0xb
006fde80 178658d4 DevExpress_Utils_v13_1!DevExpress.Utils.Frames.NotePanel.CalcSizes+0x304
006fdf4c 17864f4f DevExpress_Utils_v13_1!DevExpress.Utils.Frames.NotePanel.OnPaint+0x9f
006fe0b0 0705ab89 System_Windows_Forms!System.Windows.Forms.Control.PaintWithErrorHandling+0x89
006fe0e0 07058d75 System_Windows_Forms!System.Windows.Forms.Control.WmPaint+0x47d
006fe1d8 07a003db System_Windows_Forms!System.Windows.Forms.Control.WndProc+0x39b
006fe218 0707f821 System_Windows_Forms!System.Windows.Forms.Control+ControlNativeWindow.OnMessage+0x11
006fe220 0707f7f8 System_Windows_Forms!System.Windows.Forms.Control+ControlNativeWindow.WndProc+0xa0
006fe234 0707f227 System_Windows_Forms!System.Windows.Forms.NativeWindow.Callback+0x5f

从卦中可以看到 ExceptionCode: e0434352,这很明显是一个 托管异常,既然是 托管异常 那为什么朋友的代码拦截不到呢?这就比较有意思了,那到底是什么类型的托管异常,用 !t 观察一下。


0:000> !t
ThreadCount: 13
UnstartedThread: 0
BackgroundThread: 11
PendingThread: 0
DeadThread: 1
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 68b4 00970248 26020 Preemptive 2BB06608:00000000 0096ad08 1 STA System.Reflection.TargetInvocationException 2baf5884 (nested exceptions)
2 2 8b90 0098a220 2b220 Preemptive 00000000:00000000 0096ad08 0 MTA (Finalizer)
5 4 1cc 05e3f308 202b220 Preemptive 00000000:00000000 0096ad08 0 MTA
...

从卦中的 System.Reflection.TargetInvocationException 可以看到这是一个 调用目标异常,由于卦象上有 (nested exceptions) 标记,使用 !pe -nested 命令观察异常链走势。


0:000> !pe -nested
Exception object: 2baf5884
Exception type: System.Reflection.TargetInvocationException
Message: 调用的目标发生了异常。
InnerException: System.ComponentModel.Win32Exception, Use !PrintException 96e70c07 to see more.
StackTrace (generated):
SP IP Function
00000000 00000001 mscorlib_ni!System.RuntimeMethodHandle.SerializationInvoke(System.IRuntimeMethodInfo, System.Object, System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext ByRef)+0xffffffff8d074f41
xxx
006FDFF4 71891968 mscorlib_ni!System.Resources.RuntimeResourceSet.GetObject(System.String, Boolean, Boolean)+0x1e8
006FE074 7189B263 mscorlib_ni!System.Resources.RuntimeResourceSet.GetObject(System.String, Boolean)+0x13
006FE07C 7189331B mscorlib_ni!System.Resources.ResourceManager.GetObject(System.String, System.Globalization.CultureInfo, Boolean)+0x22b
006FE0DC 7194FD5B mscorlib_ni!System.Resources.ResourceManager.GetObject(System.String)+0xf
006FE0E0 07CB92F8 xxx.frmBase.InitializeComponent()+0x68
006FE0F0 0707308A xxx.frmBase..ctor()+0x42
006FE100 05202B3D xxx.frmErrorMessage..ctor()+0xd
006FE10C 05202A03 xxx.ExceptionHandler.HandlerErr(System.Exception)+0x23
006FE11C 052029C9 xxx.UnhandledExceptionManager.Application_ThreadException(System.Object, System.Threading.ThreadExceptionEventArgs)+0x9
006FE120 052027E8 System_Windows_Forms!System.Windows.Forms.Application+ThreadContext.OnThreadException(System.Exception)+0x88
006FE15C 05202746 System_Windows_Forms!System.Windows.Forms.Control.WndProcException(System.Exception)+0x16
006FE168 0520271A System_Windows_Forms!System.Windows.Forms.Control+ControlNativeWindow.OnThreadException(System.Exception)+0xa
006FE16C 0707F256 System_Windows_Forms!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0x8e StackTraceString: <none>
HResult: 80131604 Nested exception -------------------------------------------------------------
Exception object: 2baeb3b0
Exception type: System.OutOfMemoryException
Message: 内存不足。
InnerException: <none>
StackTrace (generated):
SP IP Function
006FDE30 07A0941C System_Drawing!System.Drawing.Graphics.FromHdcInternal(IntPtr)+0x4c
006FDE40 1173BBDB System_Drawing!System.Drawing.Font.GetHeight()+0x5b
006FDE70 1173BB4B System_Drawing!System.Drawing.Font.get_Height()+0xb
006FDE80 178658D4 DevExpress_Utils_v13_1!DevExpress.Utils.Frames.NotePanel.CalcSizes(System.Drawing.Graphics)+0x304
006FDF4C 17864F4F DevExpress_Utils_v13_1!DevExpress.Utils.Frames.NotePanel.OnPaint(System.Windows.Forms.PaintEventArgs)+0x9f
006FE0B0 0705AB89 System_Windows_Forms!System.Windows.Forms.Control.PaintWithErrorHandling(System.Windows.Forms.PaintEventArgs, Int16)+0x89
006FE0E0 07058D75 System_Windows_Forms!System.Windows.Forms.Control.WmPaint(System.Windows.Forms.Message ByRef)+0x47d
006FE1D8 07A003DB System_Windows_Forms!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x39b
006FE218 0707F821 System_Windows_Forms!System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)+0x11
006FE220 0707F7F8 System_Windows_Forms!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0xa0
006FE234 0707F227 System_Windows_Forms!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0x5f StackTraceString: <none>
HResult: 8007000e

从卦象看,程序是先抛出了 OutOfMemoryException 异常,在全局异常处理中再次抛出 TargetInvocationException 引发程序崩溃,接下来看下为什么会抛 OutOfMemoryException 异常呢?导出源代码观察,简化后如下:


public float GetHeight()
{
IntPtr dC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef);
float num = 0f;
try
{
using Graphics graphics = Graphics.FromHdcInternal(dC);
return GetHeight(graphics);
}
finally
{
UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, dC));
}
} public static Graphics FromHdcInternal(IntPtr hdc)
{
IntPtr graphics = IntPtr.Zero;
int num = SafeNativeMethods.Gdip.GdipCreateFromHDC(new HandleRef(null, hdc), out graphics);
if (num != 0)
{
throw SafeNativeMethods.Gdip.StatusException(num);
}
return new Graphics(graphics);
} internal static Exception StatusException(int status)
{
return status switch
{
2 => new ArgumentException(SR.GetString("GdiplusInvalidParameter")),
3 => new OutOfMemoryException(SR.GetString("GdiplusOutOfMemory")),
...
};
}

从卦象看,应该就是 GdipCreateFromHDC 方法返回 num=3,继而代码上手工 throw OutOfMemoryException,这里不是引发程序崩溃的直接原因,所以暂时就不深究了,转头关注核心的 HandlerErr 方法。

2. 全局异常处理崩溃探究

接下来回头看为什么 Application_ThreadException -> HandlerErr 全局异常处理中会再次抛异常?观察源代码发现是在初始化 frmErrorMessage 的时候抛错的。。。无语了。。。难怪拦截不到,截图如下:

细心的朋友会发现上面还有一句话 System.ComponentModel.Win32Exception, Use !PrintException 96e70c07 to see more.,即 TargetInvocationException 的内部还有一个异常 Win32Exception,可以用 !pe 显示出来。


0:000> !PrintException /d 2baf5360
Exception object: 2baf5360
Exception type: System.ComponentModel.Win32Exception
Message: 操作成功完成。
InnerException: <none>
StackTrace (generated):
SP IP Function
006FDA9C 07CBA3BA System_Drawing!System.Drawing.Icon.Initialize(Int32, Int32)+0x83a
006FDB6C 07CB9B49 System_Drawing!System.Drawing.Icon..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)+0xc1 StackTraceString: <none>
HResult: 80004005
There are nested exceptions on this thread. Run with -nested for details

到这里逻辑基本就搞清楚了。

  1. 业务代码在 System.Drawing.Graphics.FromHdcInternal 处抛了一个 OutOfMemoryException 进入全局异常拦截。
  2. 在全局异常拦截中,又不幸在 new frmErrorMessage()System.Drawing.Icon.Initialize 处抛出了 Win32Exception 异常。

最后就是 Icon.Initialize 函数为什么会抛异常?说实话我也搞不清楚,在 stackoverflow https://stackoverflow.com/questions/2356580/system-drawing-icon-constructor-throwing-operation-completed-successfully-exce 上找到了这样的解读,截图如下:

最后给到朋友的建议:

  1. 设置多尺寸的 icon 图片,再观察效果。
  2. 修改 frmErrorMessage,避免双异常。

三:总结

本次事故是 多异常的联合作战 成功在高压的全局异常拦截方法Application_ThreadException中逃逸,是不是非常的有意思,示警大家,警惕被偷家!

记一次 .NET 某企业审批系统 崩溃分析的更多相关文章

  1. 记一次 .NET 某医疗住院系统 崩溃分析

    一:背景 1. 讲故事 最近收到了两起程序崩溃的dump,查了下都是经典的 double free 造成的,蛮有意思,这里就抽一篇出来分享一下经验供后面的学习者避坑吧. 二:WinDbg 分析 1. ...

  2. 记一次 .NET 某企业 ERP网站系统 崩溃分析

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

  3. 记一次 .NET 某医疗器械 程序崩溃分析

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

  4. MiinCMP1.0 SAE 新浪云版公布, 开源企业站点系统

    MiinCMP是一款开源企业站点系统,除可执行于256M左右100元的国内IDC外,JUULUU聚龙软件团队最近开发了面向新浪云的版本号,该版本号可将站点免费布署到新浪云SAE上.MiinCMP採用j ...

  5. OSCHina技术导向:开源企业ERP系统Opentaps

    opentaps Open Source ERP + CRM 基于 Apache OFBiz (The Open For Business Project ) 构建, 是一款设计良好, 逐渐流行起来的 ...

  6. 制药企业BI系统方案整体设计分享

    制药企业全面预算系统蓝图 全面掌控企业的各种业务活动,及时准确的展现它们的状况与趋势,评估其达成的效果.存在的问题与风险.支持数据的导入,多级上报等多种特色功能,同时通过统一的报表平台实现精细话的权限 ...

  7. 企业BI系统应用的切入点及五大策略

    从技术的角度来看,BI的技术正在走向成熟,处于一个发展的阶段,但它促使了BI的应用在成本方面开始逐步的降低,越来越多的企业在BI应用方面取得了成功.从实施的角度来出发,实施商业智能系统是一项复杂的系统 ...

  8. 浅析互联网系统和传统企业IT系统的异同

    前不久,一则中行宕机的消息引起了网上IT人士的热议.其中对于大型机或者RISC系统的稳定性可靠性的质疑更是热议中的主流声音,很多人拿现在互联网系统做对比,认为大型机所谓的几个9都是吹出来的云云.在这里 ...

  9. spring security 一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中 配置的Bean,充分利用了Spring ...

  10. Prometheus+Grafana企业监控系统

    Prometheus+Grafana企业监控系统 作者 刘畅 实验配置: 主机名称 Ip地址 controlnode 172.16.1.70/24 slavenode1 172.16.1.71/24 ...

随机推荐

  1. Spring注解中@Resource和@Authwired的区别

    Spring注解中@Resource和@Authwired的区别 @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 by ...

  2. 03 - LayoutPanels例子 - TextBox

    C# Maui暂时还没有TextBox,因为这个可以通过xaml样式实现,但是为了长期使用,自己写一个TextBox. 定义一个TextEventArgs public class TextEvent ...

  3. vscode GDB远程调试安卓

    如果是比较新的android ndk的版本,建议使用lldb进行调试,参考:vscode lldb远程调试 - OpenFDE - OpenFDE Docs,将lldbserver push到移动端, ...

  4. Kafka入门实战教程(2)基于Docker搭建Kafka环境

    1 准备工作 这里我们使用一台Linux CentOS系统的服务器来模拟三个Kafka Broker的伪集群(即一台server上开三个不同端口)环境用于学习测试,大概的准备工作有两个: 安装Dock ...

  5. mac提示软件提示已损坏,需要移到废纸篓的解决方法

    方式1 允许任何来源的应用.在系统偏好设置里,打开"安全性和隐私",将"允许从以下位置下载的应用程序"设置为"任何来源". 并打开终端,执行 ...

  6. vue-infinite-scroll支持vue3

    vue3-infinite-scroll-good 简介 (introduce) vue-infinite-scroll的vue3版本,所有用法和vue-infinite-scroll一致. vue3 ...

  7. DXF 最简单的一个文件生成两个直线一条直线放入BLOCKS中通过INSERT插入 (2)

    把#注解删除 0 SECTION 2 HEADER 9 $ACADVER 1 AC1009 9 $INSBASE 10 0.000000 20 0.000000 30 0.000000 9 $EXTM ...

  8. iga 入门之 泛函

    简介 变分法理解 参考链接 https://zhuanlan.zhihu.com/p/41573146 核心摘要 这里把泛函和函数.算子的概念区别理解一下: 算子是一个函数到另一个函数的映射,它是从向 ...

  9. 从崩溃到稳定:前端开发者必学的 Node.js 守护进程实战指南

    本文原创首发于公众号[我做开发那些年]与网站[乔文小屋],现同步转载至本平台,点击阅读原文 声明:如需转载本文至其他平台,请注明文章来源及公众号信息,感谢您对原创内容的尊重与支持! 说到守护进程,绝大 ...

  10. 如何选择最适合企业的ETL解决方案?

    在今天的大数据时代,企业的数据管理和处理变得愈发重要.企业也越来越依赖于数据仓库和数据湖来提取.转换和加载(ETL)关键业务信息.一个高效.灵活的ETL解决方案不仅能提升数据处理能力,还能为企业决策提 ...