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

news2024/11/17 13:43:57

一:背景

1.讲故事

前段时间有位朋友找到我,说他的程序在客户的机器上跑着跑着会出现偶发卡死,然后就崩掉了,但在本地怎么也没复现,dump也抓到了,让我帮忙看下到底怎么回事,其实崩溃类的dump也有简单的,也有非常复杂的,因为大多情况下都是非托管层面出现的各种故障,非常考验对 C, C++, Win32 API 以及 汇编 的理解,所以能不能解决看运气吧, 不管怎么说,先上 WinDbg。

二:WinDbg分析

1. 查找崩溃点

WinDbg 非常牛的地方在于它拥有一个自动化崩溃分析命令 !analyze -v,它的输出信息非常有参考价值,所以尝试一下看看。


0:136> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************
CONTEXT:  (.ecxr)
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=00000003 edi=00000003
eip=777cf04c esp=22dfd678 ebp=22dfd808 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
ntdll!NtWaitForMultipleObjects+0xc:
777cf04c c21400          ret     14h
Resetting default scope

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 0174ba6d
   ExceptionCode: 00000000
  ExceptionFlags: 00000000
NumberParameters: 0

PROCESS_NAME:  xxx.exe

STACK_TEXT:  
22dfd808 75b23b10     00000003 22dfdc68 00000001 ntdll!NtWaitForMultipleObjects+0xc
22dfd808 75b23a08     00000003 22dfdc68 00000000 KERNELBASE!WaitForMultipleObjectsEx+0xf0
22dfd824 672ff11a     00000003 22dfdc68 00000000 KERNELBASE!WaitForMultipleObjects+0x18
22dfdca4 672ff3ac     672dd150 672d0000 00000003 Faultrep!WerpReportFaultInternal+0x59e
22dfdcc4 672dd17d     22dfdcec 708d0479 22dfdd60 Faultrep!WerpReportFault+0x9e
22dfdccc 708d0479     22dfdd60 00000000 22dfdd60 Faultrep!ReportFault+0x2d
22dfdcec 708d07e9     ec030e28 1c8f7728 00000003 clr!DoReportFault+0x43
22dfdd44 709f3c7e     00000003 22dfe140 2e954594 clr!WatsonLastChance+0x19a
22dfe090 709f3d90     ec0333f0 22dfe140 2e954594 clr!DoWatsonForUserBreak+0xc2
22dfe120 6fdc690f     00000000 00000000 00000000 clr!DebugDebugger::Break+0xc9
22dfe148 0174ba6d     00000000 00000000 00000000 mscorlib_ni!System.Diagnostics.Debugger.Break+0x57
WARNING: Frame IP not in any known module. Following frames may be wrong.
22dfe194 0174b58b     00000000 00000000 00000000 0x174ba6d
22dfe1e8 0174b525     00000000 00000000 00000000 mscorlib_ni!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start<<xxxAsync>d__10>+0x43
22dfe1e8 0174b525     00000000 00000000 00000000 mscorlib_ni!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start<<xxxAsync>d__10>+0x43
22dfe22c 0174b3bd     00000000 00000000 00000000 0x174b525
22dfe27c 0174b33b     00000000 00000000 00000000 0x174b3bd
22dfe2d0 0174b2d5     00000000 00000000 00000000 0x174b33b
...

SYMBOL_NAME:  faultrep!WerpReportFaultInternal+59e

MODULE_NAME: Faultrep

IMAGE_NAME:  Faultrep.dll

STACK_COMMAND:  ~136s; .ecxr ; kb

...

从卦中的调用栈看,有二个非常重要的信息。

  1. Debugger.Break()

这个是 C# 对 int 3 的封装,即 断点中断异常,目的就是将程序的所有线程中断。

  1. Faultrep!ReportFault()

这个是 WER 2.0 ,全称为 Windows Error Reporting Service,用来抓崩溃dump的,前身是 Waston 医生,在 Windows 服务列表中可以看到。

还有一点, Faultrep.dll 是 WER 的一个组件,会在抓取过程中自动加载,我们用 lm 观察进程中的 dll 列表。


0:136> lm
start    end        module name
00fe0000 01034000   xxx C (service symbols: CLR Symbols without PDB)        
0c100000 0c123000   WINMMBASE   (deferred)             
662d0000 662ef000   clrcompression   (deferred)                   
672d0000 67327000   Faultrep   (pdb symbols)          c:\mysymbols\FaultRep.pdb\E16126C7FB9849A8B9AC57D8D62CABB01\FaultRep.pdb
...

汇总以上信息,大概就能推测出代码中用了 Debugger.Break() 函数,因为无catch处理,Windows 启动了 WER 2.0,程序代码在 ntdll!NtWaitForMultipleObjects 处等待第三方组件处理完毕,因为各种原因出现了问题导致无法返回最后崩溃。

通过卦中的信息我们大概知道了前因后果,但代码中为什么会出现 Debugger.Break() 呢?这就需要我们继续深挖。

2. 为什么会有 Debugger.Break()

刚才的输出中有这么一段话: STACK_COMMAND: ~136s; .ecxr ; kb ,它可以让我们找到异常前的调用栈,为了能看到托管栈,这里将 kb 改成 !clrstack


0:136>  ~136s; .ecxr ; !clrstack

OS Thread Id: 0x13ec (136)
Child SP       IP Call Site
22dfe0ac 777cf04c [HelperMethodFrame: 22dfe0ac] System.Diagnostics.Debugger.BreakInternal()
22dfe128 6fdc690f System.Diagnostics.Debugger.Break() [f:\dd\ndp\clr\src\BCL\system\diagnostics\debugger.cs @ 65]
22dfe150 0174ba6d xxx.xxx+d__10.MoveNext()
22dfe19c 0174b58b System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[[xxx.xxx+d__10, xxx.Abstractions]](d__10 ByRef) [f:\dd\ndp\clr\src\BCL\system\runtime\compilerservices\AsyncMethodBuilder.cs @ 316]
22dfe1f0 0174b525 xxx.xxxAsync(System.String, System.String)
22dfe238 0174b3bd xxx.xxxProducer+d__7.MoveNext()
22dfe284 0174b33b System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[[xxx.xxx+d__7, xxx.Abstractions]](d__7 ByRef)
22dfe2d8 0174b2d5 xxx.xxx.xxx(System.String, System.String)

从卦中看,貌似是在一个异步方法中手工调用了 Deubgger.Break() 方法,接下来我们观察下源码,由于比较隐私,这里就简化一下。


internal async Task xxxAsync(string x1, string x2)
{
    if (string.IsNullOrEmpty(x1))
    {
        Debugger.Break();
        return;
    }
    if (string.IsNullOrEmpty(x2))
    {
        Debugger.Break();
        return;
    }
    ...
}

这代码果然有意思,在防御性编程中居然用 Debugger.Break() 来处理,比较少见。

找到了问题源头,解决方法就简单了,大概有两种做法。

  1. 去掉 Debugger.Break() 语句

  2. 关闭 WER 2.0 服务

3. 对 Debugger.Break() 的题外话

在 clr 源码中有对 Debugger.Break() 非常详细的注释。


// This does a user break, triggered by System.Diagnostics.Debugger.Break, or the IL opcode for break.
//
// Notes:
//    If a managed debugger is attached, this should send the managed UserBreak event.
//    Else if a native debugger is attached, this should send a native break event (kernel32!DebugBreak)
//    Else, this should invoke Watson.
//
// Historical trivia:
// - In whidbey, this would still invoke Watson if a native-only debugger is attached.
// - In arrowhead, the managed debugging pipeline switched to be built on the native pipeline.
FCIMPL0(void, DebugDebugger::Break)
{
    ...
}
FCIMPLEND

注释文本: Else, this should invoke Watson 中的 Watson 其实就是本篇聊到的 WER,观察反汇编其实就是对 int 3 的封装。


0:136> uf kernelBase!DebugBreak
KERNELBASE!DebugBreak:
75ba5e40 8bff            mov     edi,edi
75ba5e42 cc              int     3
75ba5e43 c3              ret

在很多反调试机制中,经常会用 int 3 来检测当前程序是否被附加了调试器,参考如下 C++ 代码。


#include <iostream>

int isAttach = 0;

int main()
{
	__try
	{
		__asm {
			int 3
		}

		isAttach = 1;
	}
    __except(1)
	{
		isAttach = 0;
	}

	if (isAttach) {
		printf("不好,发现有调试器 ...");
	}
	else {
		printf("哈哈,一切正常!");
	}

	getchar();
}

如果你用 WinDbg 附加上去, 就会被程序检测到,截图如下:

如果是正常运行,会是如下界面

可以在 C# 中通过 Pinvoke 引入,这种动态方式,反反调试会有不小的难度。

三:总结

这次事故是朋友在开发过程中为了方便调试,使用了 Debugger.Break() 方法,但在生产环境下并没有删除,导致在某些客户机器上因为 WER 的开启,被 Waston 捕获导致的事故。

本次教训是:发给客户的版本,内含的调试信息该关闭的一定要关闭,以免生出此乱。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/22600.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java项目-第155期ssm班级同学录网站-java毕业设计_计算机毕业设计

java项目-第155期ssm班级同学录网站-java毕业设计_计算机毕业设计 【源码请到资源专栏下载】 今天分享的项目是《ssm班级同学录网站》 该项目分为2个角色&#xff0c;管理员、用户。 用户可以浏览前台,包含功能有&#xff1a; 首页、公告信息、校友风采、论坛信息&#xff0c;用…

vue项目中使用 NProgress 进度加载插件

场景&#xff1a;每次进入页面时&#xff0c;页面顶部都有一个加载条 下来说下这个效果怎么实现的 第一步&#xff1a;下载 NProgress 插件 npm install --save nprogress 第二步&#xff1a;导入 nprogress 并配置 切记&#xff1a;NP 都必须是大写 我是在路由页面中导入的 im…

搜索引擎项目开发过程以及重难点整理

目录认识搜索引擎搜索的核心思路倒排索引介绍项目简介模块管理构建索引模块数据库设计正排索引倒排索引程序入口类Indexer类扫描文档子模块FileScanner类构建文档子模块构建标题parseTitle方法构建urlparseUrl方法构建内容parseContent方法构建正排索引子模块正排索引实体类map…

Nginz静态资源缓存

缓存 缓存&#xff08;cache&#xff09;&#xff0c;原始意义是指访问速度比一般随机存取存储器&#xff08;RAM&#xff09;快的一种高速存储器&#xff0c;通常它不像系统主存那样使用DRAM技术&#xff0c;而使用昂贵但较快速的SRAM技术。缓存的设置是所有现代计算机系统发…

【前端-TypeScript】TypeScript学习思维导图-一图看完《TypeScript编程》

目录前言文章已收录至https://lichong.work&#xff0c;转载请注明原文链接。 ps&#xff1a;欢迎关注公众号“Fun肆编程”或添加我的私人微信交流经验&#x1f91d; 前言 现在&#xff0c;TypeScript 正在逐渐成为与前端框架以及 ES6 语法同一地位的基础工具&#xff0c;更多…

计算机毕业设计node+vue基于微信小程序的西餐外卖系统 uniapp 小程序

项目介绍 随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代,西餐外卖系统就是信息时代变革中的产物之一。 任何系统都要遵循系统…

力扣每日一题:808. 分汤 【dp动态规划】

有 A 和 B 两种类型 的汤。一开始每种类型的汤有 n 毫升。有四种分配操作&#xff1a; 提供 100ml 的 汤A 和 0ml 的 汤B 。提供 75ml 的 汤A 和 25ml 的 汤B 。提供 50ml 的 汤A 和 50ml 的 汤B 。提供 25ml 的 汤A 和 75ml 的 汤B 。 当我们把汤分配给某人之后&#xff0c;汤…

CentOS8 安装 erlang 和 RabbitMQ

哈喽大家好&#xff0c;我是阿Q&#xff01; 最近正好用到了消息队列中的 RabbitMQ ,今天就先来个前味菜&#xff0c;总结一下它在 centos 内的安装。 环境&#xff1a;CentOS 8.0 64位 安装erlang 由于 rabbitmq 是基于 erlang 语言开发的&#xff0c;所以必须先安装 erlang…

大数据项目之电商数仓、日志采集Flume、source、channel、 sink、Kafka的三个架构

文章目录4. 用户行为数据采集模块4.3 日志采集Flume4.3.1 Kafka的三个架构4.3.1.1 source4.3.1.2 channel4.3.1.3 sink4.3.1.4 kafka source4.3.1.5 kafka sink4.3.1.6 kafka channel4.3.1.6.1 第一个结构4.3.1.6.2 第二个结构4.3.1.6.3 第三个结构4. 用户行为数据采集模块 4.…

CentOS7 离线部署 Python 项目

1.前言 主要过程如下&#xff1a; &#xff08;1&#xff09;创建项目环境&#xff0c;生成requirements.txt文件。&#xff08;如果已有可跳出&#xff09; &#xff08;2&#xff09;新建一个跟目标机器一样的操作系统&#xff0c;python环境的测试服务器&#xff0c;并下载…

【考研英语语法】名词性从句

0 导言 名词性从句&#xff0c;是指一个句子相当于名词来使用&#xff0c;放到另外一个句子中。通常情况下&#xff0c;名词在句子中主要作四种成分&#xff1a;宾语、表语、主语、同位语。因此&#xff0c;名词性从句就分成四种&#xff1a;宾语从句、表语从句、主语从句、同…

迅为iTOP3568开发板Android11获取root权限关闭selinux

本文档所需资料在网盘资料“iTOP-3568 开发板\02_【iTOP-RK3568 开发板】开发资料\ 06_Android 系统开发配套资料\02_Android11 获取 root 权限配套资料”目录下。本文档参考瑞 芯微官方文档&#xff0c;在源码“Android11/rk_android11.0_sdk/RKDocs/android/patches/root”目…

SpringBoot整合Swagger2

整合Swagger2 1.Swagger介绍 前后端分离开发模式中&#xff0c;api文档是最好的沟通方式。 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。 1、及时性 (接口变更后&#xff0c;能够及时准确地通知相关前后端开发人员)…

Kubernetes云原生实战00 何为云原生?

大家好&#xff0c;我是飘渺。 从今天开始我将开启第二个专栏系列&#xff0c;即 Kubernetes云原生实战 何为云原生 都2022年了&#xff0c;我相信各位肯定听过 云原生 这个词&#xff0c;但是究竟什么是云原生&#xff0c;却很少有人能真正说清楚。 如果找资料来看&#x…

广通远驰亮相2022 C-V2X“四跨”(苏州)应用示范活动

2022年11月3-4日&#xff0c;2022 C-V2X“四跨”&#xff08;苏州&#xff09;先导应用示范活动在苏州举办&#xff0c;9家国内外整车企业、22家终端企业、9家芯片模组企业、7家信息安全企业共计23个车队、47家单位参与活动演示。 深圳市广通远驰科技有限公司&#xff08;以下简…

请求转发与重定向到底有哪些区别?

一. 前言 我们知道&#xff0c;Web开发中的页面跳转&#xff0c;通常有两种跳转方式&#xff1a;重定向与请求转发。但可能有很多同学并不知道这两种跳转在细节上的区别&#xff0c;只知道都能跳转到目标页面。但实际上这两种跳转方式深入研究后&#xff0c;你会发现有很多不同…

SAML2.0 笔记(二)

文章目录一、前言二、共通内容1.1、引入依赖1.2、初始化SAML部分1.2.1、检查JCE环境1.2.2、初始化服务1.3、拦截器部分1.3.1、构建AuthnRequest1.3.2、AuthRequest解析1.3.3、SP模式选择1.3.4、IDP模式选择1.4、涉及的工具类1.4.1、OpenSAMLUtils工具类1.4.2、sp/idp Credentia…

Life-long Mapping

0.引言 主要参考自某某学院的激光slam课程。 1.Lifelong Mapping 的概念和应用 在长时间的建图过程中&#xff0c;基于图优化的 SLAM 的方法存在以下问题&#xff1a;PoseGraph 中的节点随着机器人走过的距离越来越多&#xff0c;以至于求解规模不断增大&#xff0c;影响优化…

密码学系列之九:密钥管理

密钥管理1. 密钥管理概述1.1 密钥管理的主要内容1.2 密钥管理的原则1.3 密钥管理的层次结构2. 密钥的生命周期3. 密钥分发3.1 无中心的密钥分发3.2 有中心的密钥分发4. 密钥协商技术4.1 Diffie-Hellman密钥交换协议4.2 端-端协议5. 密钥托管5.1 密钥托管密码体制基本组成5.2 托…

社区便利店销售微信APP的设计与实现(源码+论文)_kaic

目 录 1 绪论 1.1 研究背景、研究目的和研究意义 1.1.1 研究背景 1.1.2 研究目的与研究意义 1.2 国内外研究现状 2 系统开发环境 2.1 系统功能分析 2.2 系统开发平台 2.3 平台开发相关技术 2.3.1 B/S结构 2.3.2 Java技术介绍 2.3.3 Mysql数据库 2.3.4 SSM框架 2.4 微信开发者工…