当前位置: 首页 > news >正文

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

一:背景

1. 讲故事

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

二:WinDbg 分析

1. 为什么崩溃在 clr

一般来说崩溃在clr里都不是什么好事情,这预示着 clr 在执行自身代码的时候抛了异常,即灾难的 ExecutionEngineException,可以用 !t 验证下。


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

既然是灾难性异常,那为什么会出现呢?可以用 !analyze -v 观察下。


0:000> !analyze -v
CONTEXT:  0115a98c -- (.cxr 0x115a98c)
eax=00000000 ebx=00000000 ecx=00000000 edx=18c364a4 esi=00030000 edi=18998d50
eip=552bfff1 esp=0115ae6c ebp=0115af24 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
clr!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+0x33
0115affc 552c070b     0115b010 1f4222c0 00030000 clr!VSD_ResolveWorker+0x1d2
0115b024 28a3a949     639b0d38 00000000 00000000 clr!ResolveWorkerAsmStub+0x1b
0115b0a4 28a3a8bd     00000000 00000000 00000000 xxxx!xxx
...

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

从汇编上看这个 ecx 其实就是这个方法的 this 指针,那为什么 this =null 呢?这就很奇葩了。

2. 为什么 this =null

要想找到这个答案,只能看clr源代码,简化后如下:


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的前缀找到对应的 虚调用管理器,它的核心逻辑如下:


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 即可。


0:000> dp poi(0115afdc) L1
0c740040  0c7460120:000> u 0c746012
0c746012 50              push    eax
0c746013 6800000300      push    30000h
0c746018 e9d3a6b748      jmp     clr!ResolveWorkerAsmStub (552c06f0)
0c74601d 0000            add     byte ptr [eax],al
0c74601f 0000            add     byte ptr [eax],al
0c746021 005068          add     byte ptr [eax+68h],dl
0c746024 0000            add     byte ptr [eax],al
0c746026 46              inc     esi0:000> dp 0c746012 L1
0c746012  00006850

对比刚才的代码既然都返回来了 SK_LOOKUP 那为什么还是 SK_UNKNOWN 呢? 这个也可以通过在线程栈上找到 &stubKind 变量得到验证。

0:000> uf 552c0698
...
clr!VSD_ResolveWorker+0x1ab:
552c065f 8b85e0ffffff    mov     eax,dword ptr [ebp-20h]
552c0665 83a5ecffffff00  and     dword ptr [ebp-14h],0
552c066c 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    ecx
552c0680 8bcc            mov     ecx,esp
552c0682 8931            mov     dword ptr [ecx],esi
552c0684 ffb5e8ffffff    push    dword ptr [ebp-18h]
552c068a 8d8de0ffffff    lea     ecx,[ebp-20h]
552c0690 51              push    ecx
552c0691 8bc8            mov     ecx,eax
552c0693 e823f9ffff      call    clr!VirtualCallStubManager::ResolveWorker (552bffbb)
552c0698 8bf0            mov     esi,eax
...0:000> dp 0115affc-0x14 L1
0115afe8  00000000

我感觉这逻辑也只有clr团队帮忙解释,我已经搞不清楚了,接下来我们回头看托管方法,看能不能继续下去。

3. 在托管层寻找突破口

高级调试就是这样,一个方向走不通就需要在另一个方向上突破,接下来使用 !clrstack 观察一下。


0:000> !clrstack
OS Thread Id: 0x52e8 (0)
Child SP       IP Call Site
0115af50 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 方法干了什么?简化后的代码如下:


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 之后按图索骥即可。


0:000> !do 1f4222c0
Name:        xxx.xxx.BusinessAppDomainInvoker
MethodTable: 0c73a144
EEClass:     0c6d6f0c
Size:        12(0xc) bytes
File:        E:\xxx\xxx.dll
Fields:MT    Field   Offset                 Type VT     Attr    Value Name
0c73a4e8  400000a        4 ....AppDomainManager  0 instance 1f42236c appDomainManager
0c73a2dc  4000009       18 ..., xxx]]  0   static 1f422214 lazy0:000> !dumpmt -md 0c73a144
EEClass:         0c6d6f0c
Module:          0c7383dc
Name:            xxx.xxx.BusinessAppDomainInvoker
mdToken:         02000006
File:            E:\xxx\xxx.dll
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 10
Number of IFaces in IFaceMap: 1
--------------------------------------
MethodDesc TableEntry MethodDe    JIT Name...
0c6c3400 0c73a110    JIT xxx.xxx.InvokeMothod(System.String, System.Object[])0:000> !do  poi(0c73a144+0x24)
Name:        xxx.IPluginInvoker
MethodTable: 0c739f30
EEClass:     0c6d6d34
Size:        0(0x0) bytes
File:        E:\xxx\xxx.dll
Fields:
None
ThinLock owner 1 (18998d50), Recursive 0

对比那个 token=30000h 发现什么地方都没有问题,奇葩的就是一个简单接口调用就出现了问题,仔细观察代码之后发现了两个和别人不一样的地方。

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

第一个是他的程序是多 AppDomain 的,可以用 !dumpdomain 观察。


0:000> !dumpdomain
--------------------------------------
System Domain:      55a6caa0
...
--------------------------------------
Shared Domain:      55a6c750
LowFrequencyHeap:   55a6cdc4
Stage:              OPEN
--------------------------------------
Domain 1:           18b04690
LowFrequencyHeap:   18b04afc
Name:               DefaultDomain
--------------------------------------
Domain 2:           18c361f0
LowFrequencyHeap:   18c3665c
...

第二个是我发现托管调用栈上还有很多 托管C++,这种混合编程真的是无语了。

到这里我想到了三个办法:

1)如果可以先把接口方法预热,clr会直接把方法入口塞到汇编里,就不会再走clr底层逻辑了。

2)能否将 托管C++ 和 C# 隔离,不要混合编程。

3)重点观察下多Domain下这个托管调用是不是有什么问题。

三:总结

这种 多domain + 托管C++混合C# 编程,真出问题了基本上就是无解,一般人hold不住,无语了。

相关文章:

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

一&#xff1a;背景 1. 讲故事 前些天有位朋友找到我&#xff0c;说他的软件在客户那边不知道什么原因崩掉了&#xff0c;从windows事件日志看崩溃在 clr 里&#xff0c;让我能否帮忙定位下&#xff0c;dump 也抓到了&#xff0c;既然dump有了&#xff0c;接下来就上 windbg …...

2020 6.s081——Lab5:Lazy page allocation

再来是千年的千年 不变是眷恋的眷恋 飞越宇宙无极限 我们永不说再见 ——超兽武装 完整代码见&#xff1a;SnowLegend-star/6.s081 at lazy (github.com) Eliminate allocation from sbrk() (easy) 顾名思义&#xff0c;就是去掉sbrk()中调用growproc()的部分。1s完事儿。 Laz…...

华为认证学习笔记:生成树

以太网交换网络中为了进行链路备份&#xff0c;提高网络可靠性&#xff0c;通常会使用冗余链路。但是使用冗余链路会在交换网络上产生环路&#xff0c;引发广播风暴以及MAC地址表不稳定等故障现象&#xff0c;从而导致用户通信质量较差&#xff0c;甚至通信中断。为解决交换网络…...

leetcode 97.交错字符串

思路&#xff1a;LCS 其实也是同一个类型的题目&#xff0c;一般涉及到这种子序列的字符串问题的时候&#xff0c;状态的设置基本上都应该是以...结尾为状态的。这里同样&#xff0c;设置用dp[i][j]为s1,s2字符以i&#xff0c;j结尾能否拼接成s3[ij]。 那么&#xff0c;首先就…...

The Missing Semester ( Shell 工具和脚本 和 Vim)

管道符号 &#xff08;1&#xff09;管道符号 | 将前一个命令的输出作为下一个命令的输入 例如&#xff1a; 以下为 ./semester输出中提取包含 "Last-Modified" 的行并写入文件 last-modified.txt./semester | grep "Last-Modified" > ~/last-modif…...

【Uniapp微信小程序】自定义水印相机、微信小程序地点打卡相机

效果图 template 下方的image图片自行寻找替换&#xff01; <template><view><camerav-if"!tempImagePath && cameraHeight ! 0":resolution"high":frame-size"large":device-position"device":flash"f…...

SimPO: Simple Preference Optimization with a Reference-Free Reward

https://github.com/princeton-nlp/SimPO 简单代码 class simpo(paddle.nn.Layer):def __init__(self):super(OrPoLoss, self).__init__()self.loss paddle.nn.CrossEntropyLoss()def forward(self,neg_logit, neg_lab, pos_logit, pos_lab,beta,gamma):neg_logit paddle.n…...

CDH6.3.2安装文档

前置环境&#xff1a; 操作系统&#xff1a; CentOS Linux release 7.7 java JDK &#xff1a; 1.8.0_231 1、准备工作 准备以下安装包&#xff1a; Cloudera Manager: cloudera-manager-agent-6.3.1-1466458.el7.x86_64.rpm cloudera-manager-daemons-6.3.1-1466458.el…...

Java实战入门:深入解析Java中的 `Arrays.sort()` 方法

文章目录 一、方法定义参数说明返回值 二、使用场景三、实现原理四、示例代码示例一&#xff1a;对整型数组排序示例二&#xff1a;对字符串数组排序示例三&#xff1a;对自定义对象数组排序 五、注意事项六、总结 在Java编程中&#xff0c;Arrays.sort() 方法是一个非常常用的…...

JavaScript的垃圾回收机制

No.内容链接1Openlayers 【入门教程】 - 【源代码示例300】 2Leaflet 【入门教程】 - 【源代码图文示例 150】 3Cesium 【入门教程】 - 【源代码图文示例200】 4MapboxGL【入门教程】 - 【源代码图文示例150】 5前端就业宝典 【面试题详细答案 1000】 文章目录 一、垃圾…...

小程序使用Canvas设置文字竖向排列

在需要使用的js页面引入js文件,传入对应参数即可 /** * 文本竖向排列 */ function drawTextVertical(context, text, x, y) {var arrText text.split();var arrWidth arrText.map(function (letter) {return 26; // 字体间距,需要自定义可以自己加参数,根据传入参数进行…...

GPT-4o:重塑人机交互的未来

一个愿意伫立在巨人肩膀上的农民...... 一、推出 在人工智能&#xff08;AI&#xff09;领域&#xff0c;自然语言处理&#xff08;NLP&#xff09;技术一直被视为连接人类与机器的桥梁。近年来&#xff0c;随着深度学习技术的快速发展&#xff0c;NLP领域迎来了前所未有的变革…...

大语言模型拆解——Tokenizer

1. 认识Tokenizer 1.1 为什么要有tokenizer&#xff1f; 计算机是无法理解人类语言的&#xff0c;它只会进行0和1的二进制计算。但是呢&#xff0c;大语言模型就是通过二进制计算&#xff0c;让你感觉计算机理解了人类语言。 举个例子&#xff1a;单1&#xff0c;双2&#x…...

Linux自动挂载服务autofs讲解

1.产生原因 2.配置文件讲解 总结&#xff1a;配置客户端&#xff0c;先构思好要挂载的目录如&#xff1a;/abc/cb 然后在autofs.master中编辑&#xff1a; /abc&#xff08;要挂载的主目录&#xff09; /etc/qwe&#xff08;在这个文件里去找要挂载的副目录&#xff0c;这个名…...

堆结构知识点复习——玩转堆结构

前言:堆算是一种相对简单的数据结构&#xff0c; 本篇文章将详细的讲解堆中的知识点&#xff0c; 包括那些我们第一次学习堆的时候容易忽略的内容&#xff0c; 本篇文章会作为重点详细提到。 本篇内容适合已经学完C语言数组和函数部分的友友们观看。 目录 什么是堆 建堆算法…...

JS数据类型运算符标准库

目录 数据类型运算符标准库对象Object对象属性描述对象Array对象包装对象Boolean对象Number对象String对象Math对象Date对象...

单片机之从C语言基础到专家编程 - 4 C语言基础 - 4.13数组

C语言中&#xff0c;有一类数据结构&#xff0c;它可以存储一组相同类型的元素&#xff0c;并且可以通过索引访问这些元素&#xff0c;没错&#xff0c;这类数据结构就是数组。数组可以说是C语言中非常重要的数据结构之一了。使用数组可以是程序逻辑更加清晰&#xff0c;也更加…...

【码银送书第二十期】《游戏运营与出海实战:策略、方法与技巧》

市面上的游戏品种繁杂&#xff0c;琳琅满目&#xff0c;它们是如何在历史的长河中逐步演变成今天的模式的呢&#xff1f;接下来&#xff0c;我们先回顾游戏的发展史&#xff0c;然后按照时间轴来叙述游戏运营的兴起。 作者&#xff1a;艾小米 本文经机械工业出版社授权转载&a…...

String 类

目录&#xff1a; 一. 认识 String 类 二. String 类的基本用法 三. String对象的比较 四.字符串的不可变性 五. 认识 StringBuffer 和 StringBuilder 一. 认识 String 类&#xff1a; 在C语言中已经涉及到字符串了&#xff0c;但是在C语言中要表示字符串只能使用字符数组或者…...

Chromebook Plus中添加了Gemini?

Chromebook Plus中添加了Gemini&#xff1f; 前言 就在5月29日&#xff0c;谷歌宣布了一项重大更新&#xff0c;将其Gemini人工智能技术集成到Chromebook Plus笔记本电脑中。这项技术此前已应用于谷歌的其他设备。华硕和惠普已经在市场上销售的Chromebook Plus机型&#xff0c;…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

LangFlow技术架构分析

&#x1f527; LangFlow 的可视化技术栈 前端节点编辑器 底层框架&#xff1a;基于 &#xff08;一个现代化的 React 节点绘图库&#xff09; 功能&#xff1a; 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...