记一次 .NET 某企业审批系统 崩溃分析
一:背景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=0023ss=002bds=002bes=002bfs=0053gs=002b efl=00000212
KERNELBASE!RaiseException+0x62:
768f9132 8b4c2454 mov ecx,dword ptr 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: 80131604
Parameter: 00000000
Parameter: 00000000
Parameter: 00000000
Parameter: 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 ContextDomain Count Apt Exception
0 1 68b4 00970248 26020 Preemptive2BB06608:00000000 0096ad08 1 STA System.Reflection.TargetInvocationException 2baf5884 (nested exceptions)
2 2 8b90 0098a220 2b220 Preemptive00000000:00000000 0096ad08 0 MTA (Finalizer)
5 41cc 05e3f308 202b220 Preemptive00000000: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到这里逻辑基本就搞清楚了。
[*]业务代码在 System.Drawing.Graphics.FromHdcInternal 处抛了一个 OutOfMemoryException 进入全局异常拦截。
[*]在全局异常拦截中,又不幸在 new frmErrorMessage() 下 System.Drawing.Icon.Initialize 处抛出了 Win32Exception 异常。
最后就是 Icon.Initialize 函数为什么会抛异常?说实话我也搞不清楚,在 stackoverflowhttps://stackoverflow.com/questions/2356580/system-drawing-icon-constructor-throwing-operation-completed-successfully-exce 上找到了这样的解读,截图如下:
最后给到朋友的建议:
[*]设置多尺寸的 icon 图片,再观察效果。
[*]修改 frmErrorMessage,避免双异常。
三:总结
本次事故是 多异常的联合作战 成功在高压的全局异常拦截方法Application_ThreadException中逃逸,是不是非常的有意思,示警大家,警惕被偷家!
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]