一:背景

1. 讲故事

前两天给训练营里的一位学员分析了一个dump,学员因为弄了一整天也没找到祸根,被我一下子弄出来了,极度想看看我是怎么分析的?由于在微信上不能一言两语表尽,干脆写一篇文章出来详细的讲讲吧,哈哈,训练营里的学员得有求必应哈。。。话不多说,我们一起探索下这个程序的崩溃之路吧。

二:WinDbg分析

1. 为什么会崩溃

这个比较简单,因为默认会自动定位到崩溃的线程,使用 .ecxr 切到崩溃前的上下文即可,然后使用 k 观察崩溃点,参考如下:


0:044> .ecxr
rax=0000000000000000 rbx=00000217a6c78f90 rcx=0000000000000000
rdx=0000000000000000 rsi=000000242c46fdd0 rdi=00000217ff1594a0
rip=00007ffdb35eb699 rsp=000000242c46fb40 rbp=000000242c46fc70
r8=00007ffdb35eb699 r9=00000217ea61fc08 r10=00000000e0434352
r11=0000000000000001 r12=00000217a6c78f90 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl nz na pe nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
KERNELBASE!RaiseException+0x69:
00007ffd`b35eb699 0f1f440000 nop dword ptr [rax+rax]
0:044> k
*** Stack trace for last set context - .thread/.cxr resets it
# Child-SP RetAddr Call Site
00 00000024`2c46fb40 00007ffd`51bf8c6f KERNELBASE!RaiseException+0x69
01 00000024`2c46fc20 00007ffd`51890108 clr!UMThunkStubRareDisableWorker+0x3f
02 00000024`2c46fc50 00007ffc`dc826748 clr!UMThunkStub+0x128
03 00000024`2c46fce0 00007ffc`dc827406 MVCAMSDK_X64+0x6748
04 00000024`2c46fd80 00007ffc`dc9d21eb MVCAMSDK_X64!CameraGrabber_GetCameraDevInfo+0xb96
05 00000024`2c46fe50 00007ffc`dc9d227f MVCAMSDK_X64!CameraGigeEnumerateDevice+0x167f2b
06 00000024`2c46fe80 00007ffd`b5a07374 MVCAMSDK_X64!CameraGigeEnumerateDevice+0x167fbf
07 00000024`2c46feb0 00007ffd`b5ebcc91 kernel32!BaseThreadInitThunk+0x14
08 00000024`2c46fee0 00000000`00000000 ntdll!RtlUserThreadStart+0x21

从卦中可以看到程序是在clr 的 UMThunkStubRareDisableWorker 函数中故意抛出的异常,接下来观察下该函数代码。


extern "C" VOID __stdcall UMThunkStubRareDisableWorker(Thread* pThread, UMEntryThunk* pUMEntryThunk, Frame* pFrame)
{
if (!CanRunManagedCode())
{
pThread->m_fPreemptiveGCDisabled = 0;
COMPlusThrowBoot(E_PROCESS_SHUTDOWN_REENTRY);
}
} BOOL CanRunManagedCode(BOOL fCannotRunIsUserError, HINSTANCE hInst)
{
if (g_fForbidEnterEE == TRUE)
return FALSE; if ((g_fEEShutDown & ShutDown_Finalize2) && !GCHeap::GetGCHeap()->IsCurrentThreadFinalizer())
return FALSE; if (g_pPreallocatedOutOfMemoryException == NULL)
return FALSE; return TRUE;
}

从卦中的代码看是因为 CanRunManagedCode() 返回false导致的,到内存中观察这几个变量即可。


0:044> dp clr!g_fForbidEnterEE L1
00007ffd`5201d548 00000000`00000000 0:044> dp clr!g_fEEShutDown L1
00007ffd`520140b0 00000000`00000007 0:044> dp clr!g_pPreallocatedOutOfMemoryException L1
00007ffd`52013e08 00000217`e4f513d8

看到卦中的几个变量大概就知道了,原来当前程序正在等待 终结器线程 执行完毕的 EEShutDown 状态。

2. 真的处于关闭状态吗

刚才只是通过变量观察,接下来我们观察线程栈来进一步求证,分别切到主线程和终结器线程观察调用栈,参考如下:


0:000> ~0s;k;~2s;k
win32u!NtUserMsgWaitForMultipleObjectsEx+0x14:
00007ffd`b3dfa104 c3 ret
# Child-SP RetAddr Call Site
00 00000024`2773f578 00007ffd`b3f40c8e win32u!NtUserMsgWaitForMultipleObjectsEx+0x14
...
08 00000024`2773f9c0 00007ffd`518828ff clr!CLREventBase::WaitEx+0xab
09 00000024`2773fa30 00007ffd`51882842 clr!WaitForEndOfShutdown_OneIteration+0xb3
0a 00000024`2773fac0 00007ffd`5171f703 clr!WaitForEndOfShutdown+0x1a
0b 00000024`2773faf0 00007ffd`518243f9 clr!EEShutDown+0xd3
0c 00000024`2773fb40 00007ffd`51823f34 clr!HandleExitProcessHelper+0x29
0d 00000024`2773fb70 00007ffd`51823e14 clr!_CorExeMainInternal+0xf8
0e 00000024`2773fc00 00007ffd`96d7d6ea clr!CorExeMain+0x14
0f 00000024`2773fc40 00007ffd`96c3ac42 mscoreei!CorExeMain+0xfa
10 00000024`2773fca0 00007ffd`b5a07374 mscoree!CorExeMain_Exported+0x72
11 00000024`2773fcd0 00007ffd`b5ebcc91 kernel32!BaseThreadInitThunk+0x14
12 00000024`2773fd00 00000000`00000000 ntdll!RtlUserThreadStart+0x21 paddle_inference_c!paddle::CreatePaddlePredictor<paddle::AnalysisConfig,3>+0x180be:
00007ffc`d39a1a9e 488b01 mov rax,qword ptr [rcx] ds:00000217`89df37b0=00007ffcd7791ff0
# Child-SP RetAddr Call Site
00 00000024`27eff080 00007ffc`d39a1a8f paddle_inference_c!paddle::CreatePaddlePredictor<paddle::AnalysisConfig,3>+0x180be
01 00000024`27eff0b0 00007ffc`d39a1a8f paddle_inference_c!paddle::CreatePaddlePredictor<paddle::AnalysisConfig,3>+0x180af
02 00000024`27eff0e0 00007ffc`d39a1a8f paddle_inference_c!paddle::CreatePaddlePredictor<paddle::AnalysisConfig,3>+0x180af
...
0e 00000024`27eff3b0 00007ffc`f20cbbde paddle_inference_c!PD_PredictorDestroy+0x39
0f 00000024`27eff3e0 00007ffc`f943796a 0x00007ffc`f20cbbde
10 00000024`27eff4b0 00007ffc`f9437930 Sdcb_PaddleInference!Sdcb.PaddleInference.PaddlePredictor.Dispose+0x1a
11 00000024`27eff4e0 00007ffd`518915e6 Sdcb_PaddleInference!Sdcb.PaddleInference.PaddlePredictor.Finalize+0x10
12 00000024`27eff510 00007ffd`5173b6c6 clr!FastCallFinalizeWorker+0x6
13 00000024`27eff540 00007ffd`5173bd73 clr!FastCallFinalize+0x5a
14 00000024`27eff580 00007ffd`5173bcae clr!MethodTable::CallFinalizer+0xb7
15 00000024`27eff5c0 00007ffd`5173b8e7 clr!CallFinalizer+0x5e
...
1e 00000024`27effa50 00000000`00000000 ntdll!RtlUserThreadStart+0x21

其实到这里逻辑全部清楚了,用户点击了关闭程序,但此时终结器线程正在执行,所以执行引擎需要等待它执行完,在这期间是禁止除终结器线程之外的任何线程再对托管方法的调用,一旦有就会抛出异常,画个简图如下:

3. call托管方法在哪里

在错误的时间出现了一个对的人,这是一种意难平,接下来把这个对的人给找出来,即 UMThunkStubRareDisableWorker方法的第二个参数pUMEntryThunk,因为这里面的 m_pManagedTarget 即托管方法的入口点,模型如下:


class UMEntryThunk
{
private:
// The start of the managed code
const BYTE* m_pManagedTarget; // This is used for profiling.
PTR_MethodDesc m_pMD;
}

那如何挖呢?观察UMThunkStubRareDisableWorker方法汇编代码即可。

0:044> k
*** Stack trace for last set context - .thread/.cxr resets it
# Child-SP RetAddr Call Site
00 00000024`2c46fb40 00007ffd`51bf8c6f KERNELBASE!RaiseException+0x69
01 00000024`2c46fc20 00007ffd`51890108 clr!UMThunkStubRareDisableWorker+0x3f
02 00000024`2c46fc50 00007ffc`dc826748 clr!UMThunkStub+0x128
03 00000024`2c46fce0 00007ffc`dc827406 MVCAMSDK_X64+0x6748
04 00000024`2c46fd80 00007ffc`dc9d21eb MVCAMSDK_X64!CameraGrabber_GetCameraDevInfo+0xb96
05 00000024`2c46fe50 00007ffc`dc9d227f MVCAMSDK_X64!CameraGigeEnumerateDevice+0x167f2b
06 00000024`2c46fe80 00007ffd`b5a07374 MVCAMSDK_X64!CameraGigeEnumerateDevice+0x167fbf
07 00000024`2c46feb0 00007ffd`b5ebcc91 kernel32!BaseThreadInitThunk+0x14
08 00000024`2c46fee0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 0:044> uf 00007ffd`51890108
00007ffd`5188ffe0 4154 push r12
00007ffd`5188ffe2 55 push rbp
00007ffd`5188ffe3 4883ec78 sub rsp,78h
00007ffd`5188ffe7 488d6c2420 lea rbp,[rsp+20h]
00007ffd`5188ffec c6454000 mov byte ptr [rbp+40h],0
00007ffd`5188fff0 e8abe0ffff call clr!GetThread (00007ffd`5188e0a0)
00007ffd`5188fff5 4885c0 test rax,rax
00007ffd`5188fff8 746f je clr!UMThunkStub+0x89 (00007ffd`51890069) Branch
...
00007ffd`518900f9 4c895548 mov qword ptr [rbp+48h],r10
00007ffd`518900fd 498bcc mov rcx,r12
00007ffd`51890100 498bd2 mov rdx,r10
00007ffd`51890103 e8288b3600 call clr!UMThunkStubRareDisableWorker (00007ffd`51bf8c30)

根据x64调用协定,参数是放在 r10 寄存器中,而r10刚好放在 rbp+48h 中,所以挖出这个栈位置即可,简单计算为。


0:044> ? 00000024`2c46fce0-0x8-0x8-0x8-0x78+0x20
Evaluate expression: 155361672304 = 00000024`2c46fc70 0:044> dp 00000024`2c46fc70+0x48 L1
00000024`2c46fcb8 00000217`ff1594a0 0:044> dp 00000217`ff1594a0 L1
00000217`ff1594a0 00007ffc`f2f49380 0:044> !U 00007ffc`f2f49380
Unmanaged code
00007ffc`f2f49380 e98bbd0500 jmp 00007ffc`f2fa5110
00007ffc`f2f49385 5f pop rdi
00007ffc`f2f49386 0f ???
00007ffc`f2f49387 25e8e35194 and eax,9451E3E8h
00007ffc`f2f4938c 5e pop rsi
00007ffc`f2f4938d 5e pop rsi
00007ffc`f2f4938e 1124e8 adc dword ptr [rax+rbp*8],esp
00007ffc`f2f49391 db5194 fist dword ptr [rcx-6Ch]
00007ffc`f2f49394 5e pop rsi
00007ffc`f2f49395 5e pop rsi 0:044> !ip2md 00007ffc`f2fa5110
MethodDesc: 00007ffcf33bd9e8
Method Name: baslerCamera.MVCam.CameraGrabberFrameCallback(IntPtr, IntPtr, baslerCamera.tSdkFrameHead ByRef, IntPtr)
Class: 00007ffcf33c1c98
MethodTable: 00007ffcf33be828
mdToken: 0000000006000067
Module: 00007ffcf2352f60
IsJitted: yes
CodeAddr: 00007ffcf2fa5110
Transparency: Critical

从 baslerCamera 来看是一个工业相机,接下来赶紧寻找源代码,截图如下:

图中的 m_FrameCallback 是一个delegate函数,并通过 CameraGrabber_SetRGBCallback 方法传给了C++,所以在关闭程序的时候一定要先释放掉这个 calllback 来和C++撇清关系,否则就会有灾难的降临。

三:总结

在我的dump分析之旅中曾遇到过一次相似的生产故障,本篇文章主要还是对训练营里这位朋友的有求必应吧,不过说实话,多分析几个像这样的dump,会极大的提升你的高级调试能力。

记一次 .NET某工业视觉软件 崩溃分析的更多相关文章

  1. 记一次 .NET 某自动化集采软件 崩溃分析

    一:背景 1.讲故事 前段时间有位朋友找到我,说他的程序在客户的机器上跑着跑着会出现偶发卡死,然后就崩掉了,但在本地怎么也没复现,dump也抓到了,让我帮忙看下到底怎么回事,其实崩溃类的dump也有简 ...

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

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

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

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

  4. 【DSP开发】【计算机视觉】TI 视觉软件开发套件ADAS

    关键字:TI  视觉软件开发套件  ADAS 日前,德州仪器 (TI) 宣布推出其视觉软件开发套件(SDK),从而为开发人员提供了一款灵活的框架.一组丰富齐全的硬件设备驱动程序和一套适用的开发工具,可 ...

  5. VisionPro工业视觉的标定方法

    工业视觉常用的几种标定方式. 计算像素比 有些时候我们需要的检测数据并不需要特别准确,并且手边没有其它标定工具,可以使用这种方法大概算一算每个像素对应多大距离. 找一个知道距离的物体,测出它的像素距离 ...

  6. “深度评测官”——记2020BUAA软工软件案例分析作业

    项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任建) 这个作业的要求在哪里 个人博客作业-软件案例分析 我在这个课程的目标是 完成一次完整的软件开发经历并以博客的方式记录开发 ...

  7. 第五周课后作业——热门软件创新分析+附加题1&附加题3

    鉴于我们寝室都热衷于手游,所以本次热门软件创新分析我就来分析一下几款热门的抽卡型手游.   阴阳师(后文简称YYS)——剧情画风唯美,配音引人入胜 作为网易公司研发的一款3D日式和风回合制游戏,YYS ...

  8. 书评第003篇:《0day安全:软件漏洞分析技术(第2版)》

    本书基本信息 丛书名:安全技术大系 作者:王清(主编),张东辉.周浩.王继刚.赵双(编著) 出版社:电子工业出版社 出版时间:2011-6-1 ISBN:9787121133961 版次:1 页数:7 ...

  9. 软件产品案例分析--K米

    软件产品案例分析--K米 第一部分 调研,评测 评测 个人第一次上手体验 使用的第一款点歌软件,以为就是个遥控而已,使用后发现功能还挺多,能点挺久.觉得很方便,不用挤成一堆点歌了.K米的脸蛋(UI)好 ...

  10. 团队项目2.0软件改进分析MathAPP

    软件改进分析 在此基础上,进行软件的改进. 首先,我们把这个软件理解成一个投入市场的.帮助小朋友进行算术运算练习的APP. 从质量保证的角度,有哪些需要改进的BUG? 从用户的角度(把自己当成小学生或 ...

随机推荐

  1. 会话层技术-session

    会话层技术-session session技术拿下! 一.先整理学习过程中的几个疑惑 cookie和session分别都是怎么创建的? 首先cookie是一个类,它需要java后端开发人员手动创建. ...

  2. PostgresSQL创建一个只读用户

    create user readonlyuser with password 'R3333333341'; grant select on all tables in schema public to ...

  3. house of stom

    完成事项 house of stom学习 未完成事项 wmctf的blineless没打通 如何解决未完成事项 下周待做事项 house of orange house of lore 本周学习的知识 ...

  4. Nuxt.js 应用中的 build:error 事件钩子详解

    title: Nuxt.js 应用中的 build:error 事件钩子详解 date: 2024/11/7 updated: 2024/11/7 author: cmdragon excerpt: ...

  5. Galera_Cluster_Mysql部署

    前言 先来了解下它的身世,Galera Cluster是Codership公司开发的一套免费开源的高可用方案 官网为http://galeracluster.com.Galera Cluster即为安 ...

  6. GetUrlParam:获取Url参数,返回一个对象

    function GetUrlParam(){ let url = document.location.toString(); let arrObj = url.split("?" ...

  7. Failed to load resoure:the serve responded with a status of 405 (Method Not Allowed)

    在项目中 web.config 引入 iis 删除WEBDEV 配置结束后 重启服务器

  8. CSS3实现放大镜效果

    市面上基本上所有的购物平台.商城上的商品详情页,对于商品的图片都是有放大功能.那么这个功能主要是怎么实现的呢?CSS3实现放大镜效果主要依赖于CSS的一些高级特性,如transform.transit ...

  9. java4~6次大作业全面总结

    一:前言: 知识点总结: 面向对象设计: 智能家居强电电路模拟系统:设计了多种控制设备(开关.分档调速器.连续调速器)和受控设备(灯.风扇)的类,并通过继承和多态实现设备的特有行为. 答题判题程序:设 ...

  10. 域渗透之初识LM&NTLM认证过程

    目录 前言 LM Hash NTLM Hash Windows本地认证 LSASS进程 Mimikatz抓取明文密码 Windows网络认证 Net NTLM NTLMv1 & NTLMv2 ...