当前位置:首页 > 科技  > 软件

记一次 .NET某工业设计软件崩溃分析

来源: 责编: 时间:2024-06-05 17:47:08 85观看
导读一、背景1. 讲故事前些天有位朋友找到我,说他的软件在客户那边不知道什么原因崩掉了,从windows事件日志看崩溃在 clr 里,让我能否帮忙定位下,dump 也抓到了,既然dump有了,接下来就上 windbg 分析吧。二、WinDbg 分析1. 为什

一、背景

1. 讲故事

前些天有位朋友找到我,说他的软件在客户那边不知道什么原因崩掉了,从windows事件日志看崩溃在 clr 里,让我能否帮忙定位下,dump 也抓到了,既然dump有了,接下来就上 windbg 分析吧。15328资讯网——每日最新资讯28at.com

二、WinDbg 分析

1. 为什么崩溃在 clr

一般来说崩溃在clr里都不是什么好事情,这预示着 clr 在执行自身代码的时候抛了异常,即灾难的 ExecutionEngineException,可以用 !t 验证下。15328资讯网——每日最新资讯28at.com

0:000> !tThreadCount:      18UnstartedThread:  0BackgroundThread: 7PendingThread:    0DeadThread:       11Hosted Runtime:   no                                                                         Lock         ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception   0    1 52e8 18998d50     24220 Preemptive  639B0D58:00000000 18c361f0 0     STA System.ExecutionEngineException 1f421120   ...

既然是灾难性异常,那为什么会出现呢?可以用 !analyze -v 观察下。15328资讯网——每日最新资讯28at.com

0:000> !analyze -vCONTEXT:  0115a98c -- (.cxr 0x115a98c)eax=00000000 ebx=00000000 ecx=00000000 edx=18c364a4 esi=00030000 edi=18998d50eip=552bfff1 esp=0115ae6c ebp=0115af24 iopl=0         nv up ei pl zr na pe nccs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246clr!VirtualCallStubManager::ResolveWorker+0x33:552bfff1 8bb968020000    mov     edi,dword ptr [ecx+268h] ds:002b:00000268=????????Resetting default scopeREAD_ADDRESS:  00000268 STACK_TEXT:  0115af24 552c0698     0115afdc 1f4222c0 00030000 clr!VirtualCallStubManager::ResolveWorker+0x330115affc 552c070b     0115b010 1f4222c0 00030000 clr!VSD_ResolveWorker+0x1d20115b024 28a3a949     639b0d38 00000000 00000000 clr!ResolveWorkerAsmStub+0x1b0115b0a4 28a3a8bd     00000000 00000000 00000000 xxxx!xxx...

我去,真无语了,我卦中数据看,这是一个接口Stub调用的崩溃,在这里崩溃真的是少之又少,从汇编代码 edi,dword ptr [ecx+268h] ds:002b:00000268=???????? 上看就是因为 ecx =0 导致的,接下来观察下方法的汇编代码。15328资讯网——每日最新资讯28at.com

图片图片15328资讯网——每日最新资讯28at.com

从汇编上看这个 ecx 其实就是这个方法的 this 指针,那为什么 this =null 呢?这就很奇葩了。15328资讯网——每日最新资讯28at.com

2. 为什么 this =null

要想找到这个答案,只能看clr源代码,简化后如下:15328资讯网——每日最新资讯28at.com

PCODE VSD_ResolveWorker(TransitionBlock* pTransitionBlock,                        TADDR siteAddrForRegisterIndirect,                        size_t token                        ){    ...    VirtualCallStubManager::StubKind stubKind = VirtualCallStubManager::SK_UNKNOWN;    VirtualCallStubManager* pMgr = VirtualCallStubManager::FindStubManager(callSiteTarget, &stubKind);        ...    target = pMgr->ResolveWorker(&callSite, protectedObj, representativeToken, stubKind);}

从卦中代码看,问题就是 pMgr=null 导致的,无语了,这个 VirtualCallStubManager::FindStubManager 方法的本意就是根据 callSite的stub的前缀找到对应的 虚调用管理器,它的核心逻辑如下:15328资讯网——每日最新资讯28at.com

StubKind getStubKind(PCODE stubStartAddress, BOOL usePredictStubKind = TRUE){    StubKind predictedKind = (usePredictStubKind) ? predictStubKind(stubStartAddress) : SK_UNKNOWN;    ...    if (predictedKind == SK_LOOKUP)    {        if (isLookupStub(stubStartAddress))            return SK_LOOKUP;    }    ...    return SK_UNKNOWN;}VirtualCallStubManager::StubKind VirtualCallStubManager::predictStubKind(TADDR stubStartAddress){    StubKind stubKind = SK_UNKNOWN;    WORD firstWord = *((WORD*)stubStartAddress);    if (firstWord == 0x05ff)    {        stubKind = SK_DISPATCH;    }    else if (firstWord == 0x6850)    {        stubKind = SK_LOOKUP;    }    else if (firstWord == 0x8b50)    {        stubKind = SK_RESOLVE;    }    return stubKind;}

接下来需要找到 stubStartAddress 的地址是多少?这个只需要提取 ResolveWorker 方法的第一个参数 callSite 即可。15328资讯网——每日最新资讯28at.com

0:000> dp poi(0115afdc) L10c740040  0c7460120:000> u 0c7460120c746012 50              push    eax0c746013 6800000300      push    30000h0c746018 e9d3a6b748      jmp     clr!ResolveWorkerAsmStub (552c06f0)0c74601d 0000            add     byte ptr [eax],al0c74601f 0000            add     byte ptr [eax],al0c746021 005068          add     byte ptr [eax+68h],dl0c746024 0000            add     byte ptr [eax],al0c746026 46              inc     esi0:000> dp 0c746012 L10c746012  00006850

对比刚才的代码既然都返回来了 SK_LOOKUP 那为什么还是 SK_UNKNOWN 呢?这个也可以通过在线程栈上找到 &stubKind 变量得到验证。15328资讯网——每日最新资讯28at.com

0:000> uf 552c0698...clr!VSD_ResolveWorker+0x1ab:552c065f 8b85e0ffffff    mov     eax,dword ptr [ebp-20h]552c0665 83a5ecffffff00  and     dword ptr [ebp-14h],0552c066c 8d95ecffffff    lea     edx,[ebp-14h]552c0672 8b08            mov     ecx,dword ptr [eax]552c0674 e858feffff      call    clr!VirtualCallStubManager::FindStubManager (552c04d1)552c0679 ffb5ecffffff    push    dword ptr [ebp-14h]552c067f 51              push    ecx552c0680 8bcc            mov     ecx,esp552c0682 8931            mov     dword ptr [ecx],esi552c0684 ffb5e8ffffff    push    dword ptr [ebp-18h]552c068a 8d8de0ffffff    lea     ecx,[ebp-20h]552c0690 51              push    ecx552c0691 8bc8            mov     ecx,eax552c0693 e823f9ffff      call    clr!VirtualCallStubManager::ResolveWorker (552bffbb)552c0698 8bf0            mov     esi,eax...0:000> dp 0115affc-0x14 L10115afe8  00000000

我感觉这逻辑也只有clr团队帮忙解释,我已经搞不清楚了,接下来我们回头看托管方法,看能不能继续下去。15328资讯网——每日最新资讯28at.com

3. 在托管层寻找突破口

高级调试就是这样,一个方向走不通就需要在另一个方向上突破,接下来使用 !clrstack 观察一下。15328资讯网——每日最新资讯28at.com

0:000> !clrstackOS Thread Id: 0x52e8 (0)Child SP       IP Call Site0115af50 775c2aac [GCFrame: 0115af50] 0115afac 775c2aac [StubDispatchFrame: 0115afac]xxx.GetListDrawerType(System.String)0115b02c 28a3a949 xxx.PluginInvoker.InvokeMothod[[System.__Canon, mscorlib]](System.String, System.Object[])0115b0b0 28a3a8bd xxx.xxx.OnFinishSizeCheck(Int64)...

从调用栈来看,貌似是用反射来实现功能增强,不管怎么说先看下xxxCheck 方法干了什么?简化后的代码如下:15328资讯网——每日最新资讯28at.com

public string OnFinishSizeCheck(long uuid){    return PluginInvoker.InvokeMothod<string>("xxxCheck", new object[1] { uuid });}public static T InvokeMothod<T>(string methodName, params object[] args){    IPluginInvoker pluginInvoker = GetPluginInvoker();    return (T)pluginInvoker.InvokeMothod(methodName, args);}

从代码上可以看到原来是使用 (T)pluginInvoker.InvokeMothod(methodName, args); 实现的接口调用,在coreclr层面也能观察得到,找到对象 1f4222c0 之后按图索骥即可。15328资讯网——每日最新资讯28at.com

0:000> !do 1f4222c0Name:        xxx.xxx.BusinessAppDomainInvokerMethodTable: 0c73a144EEClass:     0c6d6f0cSize:        12(0xc) bytesFile:        E:/xxx/xxx.dllFields:      MT    Field   Offset                 Type VT     Attr    Value Name0c73a4e8  400000a        4 ....AppDomainManager  0 instance 1f42236c appDomainManager0c73a2dc  4000009       18 ..., xxx]]  0   static 1f422214 lazy0:000> !dumpmt -md 0c73a144EEClass:         0c6d6f0cModule:          0c7383dcName:            xxx.xxx.BusinessAppDomainInvokermdToken:         02000006File:            E:/xxx/xxx.dllBaseSize:        0xcComponentSize:   0x0Slots in VTable: 10Number of IFaces in IFaceMap: 1--------------------------------------MethodDesc Table   Entry MethodDe    JIT Name   ...0c6c3400 0c73a110    JIT xxx.xxx.InvokeMothod(System.String, System.Object[])0:000> !do  poi(0c73a144+0x24)Name:        xxx.IPluginInvokerMethodTable: 0c739f30EEClass:     0c6d6d34Size:        0(0x0) bytesFile:        E:/xxx/xxx.dllFields:NoneThinLock owner 1 (18998d50), Recursive 0

对比那个 token=30000h 发现什么地方都没有问题,奇葩的就是一个简单接口调用就出现了问题,仔细观察代码之后发现了两个和别人不一样的地方。15328资讯网——每日最新资讯28at.com

4. 与众不同的地方在哪里

第一个是他的程序是多 AppDomain 的,可以用 !dumpdomain 观察。15328资讯网——每日最新资讯28at.com

0:000> !dumpdomain--------------------------------------System Domain:      55a6caa0...--------------------------------------Shared Domain:      55a6c750LowFrequencyHeap:   55a6cdc4Stage:              OPEN--------------------------------------Domain 1:           18b04690LowFrequencyHeap:   18b04afcName:               DefaultDomain--------------------------------------Domain 2:           18c361f0LowFrequencyHeap:   18c3665c...

第二个是我发现托管调用栈上还有很多 托管C++,这种混合编程真的是无语了。15328资讯网——每日最新资讯28at.com

到这里我想到了三个办法:15328资讯网——每日最新资讯28at.com

1)如果可以先把接口方法预热,clr会直接把方法入口塞到汇编里,就不会再走clr底层逻辑了。15328资讯网——每日最新资讯28at.com

2)能否将 托管C++ 和 C# 隔离,不要混合编程。15328资讯网——每日最新资讯28at.com

3)重点观察下多Domain下这个托管调用是不是有什么问题。15328资讯网——每日最新资讯28at.com

三、总结

这种 多domain + 托管C++混合C# 编程,真出问题了基本上就是无解,一般人hold不住,无语了。15328资讯网——每日最新资讯28at.com

本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-92192-0.html记一次 .NET某工业设计软件崩溃分析

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: .NET Core中的DDD设计模式与分层架构

下一篇: 如何在 .NET Core 中执行 Linux 命令

标签:
  • 热门焦点
  • 俄罗斯:将审查iPhone等外国公司设备 保数据安全

    俄罗斯:将审查iPhone等外国公司设备 保数据安全

    iPhone和特斯拉都属于在各自领域领头羊的品牌,推出的产品也也都是数一数二的,但对于一些国家而言,它们的产品可靠性和安全性还是在限制范围内。近日,俄罗斯联邦通信、信息技术
  • 三言两语说透设计模式的艺术-单例模式

    三言两语说透设计模式的艺术-单例模式

    写在前面单例模式是一种常用的软件设计模式,它所创建的对象只有一个实例,且该实例易于被外界访问。单例对象由于只有一个实例,所以它可以方便地被系统中的其他对象共享,从而减少
  • 在线图片编辑器,支持PSD解析、AI抠图等

    在线图片编辑器,支持PSD解析、AI抠图等

    自从我上次分享一个人开发仿造稿定设计的图片编辑器到现在,不知不觉已过去一年时间了,期间我经历了裁员失业、面试找工作碰壁,寒冬下一直没有很好地履行计划.....这些就放在日
  • 只需五步,使用start.spring.io快速入门Spring编程

    只需五步,使用start.spring.io快速入门Spring编程

    步骤1打开https://start.spring.io/,按照屏幕截图中的内容创建项目,添加 Spring Web 依赖项,并单击“生成”按钮下载 .zip 文件,为下一步做准备。请在进入步骤2之前进行解压。图
  • “又被陈思诚骗了”

    “又被陈思诚骗了”

    作者|张思齐 出品|众面(ID:ZhongMian_ZM)如今的国产悬疑电影,成了陈思诚的天下。最近大爆电影《消失的她》票房突破30亿断层夺魁暑期档,陈思诚再度风头无两。你可以说陈思诚的
  • 猿辅导与新东方的两种“归途”

    猿辅导与新东方的两种“归途”

    作者|卓心月 出品|零态LT(ID:LingTai_LT)如何成为一家伟大企业?答案一定是对&ldquo;势&rdquo;的把握,这其中最关键的当属对企业战略的制定,且能够站在未来看现在,即使这其中的
  •  首发天玑9200+ iQOO Neo8系列发布首销售价2299元起

    首发天玑9200+ iQOO Neo8系列发布首销售价2299元起

    2023年5月23日晚,iQOO Neo8系列正式发布。其中,Neo系列首款Pro之作——iQOO Neo8 Pro强悍登场,限时售价3099元起;价位段最强性能手机iQOO Neo8同期上市
  • 荣耀Magicbook V 14 2021曙光蓝版本正式开售,拥有触摸屏

    荣耀Magicbook V 14 2021曙光蓝版本正式开售,拥有触摸屏

    荣耀 Magicbook V 14 2021 曙光蓝版本正式开售,搭载 i7-11390H 处理器与 MX450 显卡,配备 16GB 内存与 512GB SSD,重 1.48kg,厚 14.5mm,具有 1.5mm 键盘键程、
  • 电博会与软博会实现

    电博会与软博会实现"线下+云端"的双线融合

    在本次“电博会”与“软博会”双展会利好条件的加持下,既可以发挥展会拉动人流、信息流、资金流实现快速交互流动的作用,继而推动区域经济良性发展;又可以聚
Top
Baidu
map