记一次 .NET某防伪验证系统 崩溃分析

一:背景

1. 讲故事

昨晚给训练营里面的一位朋友分析了一个程序崩溃的故障,因为看小伙子昨天在群里问了一天也没搞定,干脆自己亲自上阵吧,抓取的dump也是我极力推荐的用 procdump 注册 AEDebug 的方式,省去了很多沟通成本。

二:WinDbg分析

1. 为什么会崩溃

windbg有一个非常强大的点就是当你双击打开后,会自动帮你切换到崩溃的线程以及崩溃处的汇编代码,省去了 !analyze -v 命令的龟速输出,参考信息如下:


................................................................
...................................................
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(10f4.f58): Access violation - code c0000005 (first/second chance not available)
For analysis of this file, run !analyze -v
eax=00000000 ebx=00000000 ecx=00000040 edx=00000000 esi=004c1b98 edi=07a8ed4c
eip=7008508f esp=07a8ec74 ebp=07a8ec80 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!Thread::GetSafelyRedirectableThreadContext+0x7c:
7008508f 8038eb          cmp     byte ptr [eax],0EBh        ds:002b:00000000=??
...

从卦中可以看到,当前崩溃是因为 eax=0 导致的,那为什么 eax 等于 0 呢?要想寻找这个答案,需要观察崩溃前的线程栈上下文,可以使用命令 .ecxr;k 9 即可。


0:009> .ecxr;k 9
eax=00000000 ebx=00000000 ecx=00000040 edx=00000000 esi=004c1b98 edi=07a8ed4c
eip=7008508f esp=07a8ec74 ebp=07a8ec80 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!Thread::GetSafelyRedirectableThreadContext+0x7c:
7008508f 8038eb          cmp     byte ptr [eax],0EBh        ds:002b:00000000=??# ChildEBP RetAddr      
00 07a8ec80 6fe7f6cd     clr!Thread::GetSafelyRedirectableThreadContext+0x7c
01 07a8f030 6fe7f2f3     clr!Thread::HandledJITCase+0x31
02 07a8f0a4 6fee23da     clr!Thread::SuspendRuntime+0x260
03 07a8f184 6fedf72d     clr!WKS::GCHeap::SuspendEE+0x1fe
04 07a8f1b0 6fe309ca     clr!WKS::GCHeap::GarbageCollectGeneration+0x168
05 07a8f1c0 6fe30a2e     clr!WKS::GCHeap::GarbageCollectTry+0x56
06 07a8f1e4 6fe30a90     clr!WKS::GCHeap::GarbageCollect+0xa5
07 07a8f230 6f058b01     clr!GCInterface::Collect+0x5d
08 07a8f26c 055fa4b1     mscorlib_ni+0x3b8b01

从卦中信息看,尼玛,真无语了 GCInterface::Collect 说明有人用 GC.Collect() 手工触发GC,不知道为什么要这么做来污染GC内部的统计信息,不管怎么说这个肯定不是崩溃的原因。

2. GC正在干什么

我们继续观察线程栈,可以看到它的逻辑大概是这样的,通过 SuspendRuntime 把所有的托管线程进行逻辑上暂停,在暂停其中的一个线程时抛出了异常。

稍微提醒一下,这个 HandledJITCase 方法是用 ip 劫持技术将代码引入到 coreclr 中进行 GC完成等待,这种神操作有些杀毒软件会认为是病毒!!!

有些朋友肯定会说,有没有代码支撑。。。这里我就找一下 coreclr 的源码贴一下吧。


void ThreadSuspend::SuspendRuntime(ThreadSuspend::SUSPEND_REASON reason)
{while ((thread = ThreadStore::GetThreadList(thread)) != NULL){...if (workingOnThreadContext.Acquired() && thread->HandledJITCase()){...}...}
}

结合源码分析思路就非常清晰了,这里的 thread->HandledJITCase() 中的 thread 到底是哪一个线程? 可以观察 kb 输出然后用 !t 去做比对。

从卦中看,当前 GC 正在 Suspend 主线程,并且还看到了主线程有一个 System.AccessViolationException 异常,无语了。。。

3. 主线程到底怎么了

主线程进入到视野之后,那就重点关注一下它,可以用 k 看一下输出。


0:009> ~0s
eax=00000000 ebx=0029ea50 ecx=0029ea90 edx=00000000 esi=7efdb800 edi=000d0000
eip=00000000 esp=0029ea4c ebp=75146381 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210202
00000000 ??              ???
0:000> k
00 75146381 7efdb800     0x0
01 75146381 7517fa04     0x7efdb800
02 0029ea80 7736013a     user32!__fnHkINLPKBDLLHOOKSTRUCT+0x28
03 0029eae4 7514908d     ntdll!KiUserCallbackDispatcher+0x2e
04 0029eae4 076e3912     user32!CallNextHookEx+0x84
05 0029eb28 076e3064     0x76e3912
06 0029eb5c 0011d48f     xxx!xxx.ScanerHook.KeyboardHookProc+0xe4
07 0029eb8c 75146381     0x11d48f
08 0029eba8 7517fa04     user32!DispatchHookW+0x38
09 0029ebd8 7736013a     user32!__fnHkINLPKBDLLHOOKSTRUCT+0x28
0a 0029ec3c 751406eb     ntdll!KiUserCallbackDispatcher+0x2e
0b 0029ec3c 75140751     user32!_PeekMessage+0x88
0c 0029ec68 6d8af3bf     user32!PeekMessageW+0x108
...

从卦象看,这卦非常奇怪,有如下两点信息:

  • eip=00000000,这个很无语,线程已经疯了
  • KeyboardHookProc ,居然有键盘钩子

熟悉 eip 的朋友应该知道,它相当于一辆车的方向盘,一辆高速行驶的车突然没了方向盘,真的太可怕了,最后必然车毁人亡。

4. 是 eip=0 导致的崩溃吗

在汇编中是因为eax=0导致,而这里eip恰好也等于0,仿佛冥冥之中自有牵连,带着强烈的好奇心我们来反汇编下 GetSafelyRedirectableThreadContext 方法逻辑,简化后如下:


0:000> uf 7008508f
clr!Thread::GetSafelyRedirectableThreadContext:
6fe7f60e 55              push    ebp
6fe7f60f 8bec            mov     ebp,esp
6fe7f611 53              push    ebx
6fe7f612 56              push    esi
6fe7f613 57              push    edi
6fe7f614 8bf1            mov     esi,ecx
...
7008506d ffe9            jmp     rcx
7008506f fd              std
70085070 c1daff          rcr     edx,0FFh
70085073 f6450801        test    byte ptr [ebp+8],1
70085077 0f84efa5dfff    je      clr!Thread::GetSafelyRedirectableThreadContext+0xcc (6fe7f66c)
7008507d 8b8604010000    mov     eax,dword ptr [esi+104h]
70085083 3987b8000000    cmp     dword ptr [edi+0B8h],eax
70085089 0f85dda5dfff    jne     clr!Thread::GetSafelyRedirectableThreadContext+0xcc (6fe7f66c)
7008508f 8038eb          cmp     byte ptr [eax],0EBh  

从上面的汇编代码看eax的取值链条是: eax <- esi+104h <- ecx ,很显然这里的 ecx 是 thiscall 协议中的 Thread=004c1b98 参数,可以用 dp 验证下。


0:000> dp 004c1b98+0x104 L1
004c1c9c  00000000

从卦中看果然是 0,有些朋友好奇这个 104 偏移到底是个什么东西,参考 coreclr 源码其实就是 m_LastRedirectIP 字段,参考如下:


BOOL Thread::GetSafelyRedirectableThreadContext(DWORD dwOptions, CONTEXT* pCtx, REGDISPLAY* pRD)
{if (!EEGetThreadContext(this, pCtx)){return FALSE;}... if (GetIP(pCtx) == m_LastRedirectIP){const BYTE short_jmp = 0xeb;const BYTE self = 0xfe;BYTE* ip = (BYTE*)m_LastRedirectIP;if (ip[0] == short_jmp && ip[1] == self)m_LastRedirectIP = 0;return FALSE;}
}

结合汇编代码其实我们崩溃在 ip[0] == short_jmp 这一句上,仔细分析上面的C++代码会发现一个很奇怪的信息,那就是为什么 GetIP(pCtx)= 0,接下来用 dt 观察下寄存器上下文。


0:009> kb 2# ChildEBP RetAddr      Args to Child              
00 07a8ec80 6fe7f6cd     00000003 07a8ed4c 07a8ecf0 clr!Thread::GetSafelyRedirectableThreadContext+0x7c
01 07a8f030 6fe7f2f3     004c1b98 0b367326 76a016a1 clr!Thread::HandledJITCase+0x310:009> dt _CONTEXT 07a8ed4c
ntdll!_CONTEXT+0x000 ContextFlags     : 0x10007...+0x01c FloatSave        : _FLOATING_SAVE_AREA+0x08c SegGs            : 0x2b+0x090 SegFs            : 0x53+0x094 SegEs            : 0x2b+0x098 SegDs            : 0x2b+0x09c Edi              : 0xd0000+0x0a0 Esi              : 0x7efdb800+0x0a4 Ebx              : 0x29ea50+0x0a8 Edx              : 0+0x0ac Ecx              : 0x29ea90+0x0b0 Eax              : 0+0x0b4 Ebp              : 0x75146381+0x0b8 Eip              : 0+0x0bc SegCs            : 0x23+0x0c0 EFlags           : 0x210202+0x0c4 Esp              : 0x29ea4c...

从卦中看果然 eip=0,这是一个非常错误的信息,还有一点就是 m_LastRedirectIP 字段一般用来处理一些比较诡异的兼容性问题,所以这里两个字段都是 0 导致崩溃的产生。

有了上面的信息,我们就知道了前因后果,原来是主线程车毁人亡(eip=0),导致GC无法暂停它,在内部抛出了代码异常,你可以说是 CLR 的bug,也可以说是主线程的Bug,所以给到的解决方案就是:

  1. 屏蔽掉 键盘钩子 的业务逻辑,肯定是它造成的。
  2. 不去掉的话,要重点观察 键盘盘子 ,是否是代码改动引发的。

三:总结

说实话要想解释这个程序为什么会崩溃,需要分析者对GC的SuspendRuntime运作逻辑有一定的了解,否则真抓瞎了,所以.NET调试训练营中的GC理论知识一定是分析这些 dump 的基石。

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

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

相关文章

Linux离线安装mysql,node,forever

PS:本文是基于centos7实现的,要求系统能够查看ifconfig和unzip解压命令, 实现无网络可安装运行 首先现在百度网盘的离线文件包****安装Xftp 和 Xshell 把机房压缩包传到 home目录下****解压unzip 包名.zip 获取IP先获取到 linux 主机的ip ifconfig Xftp 连接输入IP,然后按照…

Nginx-记

Nginx是一个高性能的web服务器和反向代理服务器&#xff0c;用于HTTP、HTTPS、SMTP、POP3和IMAP协议。因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。 &#xff08;1&#xff09;更快 这表现在两个方面&#xff1a;一方面&#xff0c;在正常情况下&…

Go的数据结构与实现【Stack】

介绍 栈是存放值的一种特殊容器&#xff0c;在插入与删除值时&#xff0c;这种结构遵循后进先出&#xff08;Last-in-first-out&#xff0c;LIFO&#xff09;的原则&#xff0c;也就是说&#xff0c;值可以任意插入栈中&#xff0c;但每次取出的都是此前插入的最后一个值。 实…

STM32第十节(中级篇):EXTI(第一节)——EXTI功能框图及初始化结构体讲解(包括STM32中断应用总结)

目录 前言 STM32第十节&#xff08;中级篇&#xff09;&#xff1a;EXTI&#xff08;第一节&#xff09;——EXTI功能框图及初始化结构体讲解&#xff08;包括STM32中断应用总结&#xff09; EXTI功能框图 EXTI初始化结构体讲解 STM32中断应用总结 NVIC介绍 优先级 优先…

安卓Activity上滑关闭效果实现

最近在做一个屏保功能&#xff0c;需要支持如图的上滑关闭功能。 因为屏保是可以左右滑动切换的&#xff0c;内部是一个viewpager 做这个效果的时候&#xff0c;关键就是要注意外层拦截触摸事件时&#xff0c;需要有条件的拦截&#xff0c;不能影响到内部viewpager的滑动处理…

数据结构:静态链表(编程技巧)

文章目录 一、理解二、静态链表2.1、结构定义2.2、静态链表的初始化2.3、操作2.4、示例2.5、优点2.6、缺点 三、例题 ​ 链表的元素用数组存储&#xff0c; 用数组的下标模拟指针。 一、理解 如果有些程序设计语言没有指针类型&#xff0c;如何实现链表&#xff1f;   在使用…

电气识图基础

1 电气系统组成 电气系统分为强电系统和弱电系统。 强电系统有变配电系统,照明系统,动力系统,接地系统。弱电系统有消防报警系统,安全防范系统,楼宇自控系统等等。强电和弱电的区别? 在非电力工程领域。强电和弱电是以电压分界的,工作在交流220伏以上为强电,以下为弱电…

【目标跟踪】红绿灯跟踪

文章目录 一、前言二、结果三、跟踪3.1、检测输入3.2、预测与运动补偿3.3、第一次匹配3.4、第二次匹配3.5、第三次匹配3.6、航迹的起始与信息的发布 四、后记 一、前言 红绿灯场景对当前无人驾驶来说是个灾难性的挑战。暂且不说复杂的十字路口&#xff0c;譬如简单的人行道红绿…

马上蓝桥杯了,干货总结动态规划专题,祝你考场爆杀(拔高篇)最佳课题选择 书本整理 打鼹鼠 吃吃吃 非零字段划分

目录 最佳课题选择 思路&#xff1a; 书本整理 思路&#xff1a; 打鼹鼠 思路&#xff1a; 吃吃吃 思路&#xff1a; 非零字段划分 最佳课题选择 思路&#xff1a; 根本还是论文的分配&#xff0c;每个课题分配多少个论文是不确定的&#xff0c;这个也是很影响转…

天梯算法Day3整理

浮点数解析 炸鱼题掠过 冲突值 题面 解析 方法一 —— 并查集 按照边值排序&#xff0c;然后按边值从大到小遍历&#xff0c;通过并查集判断能否将所有点无冲突地归于两个集合。在判断时&#xff0c;若有两个点不得不产生冲突&#xff0c;则输出这两个点之间的边值并结束。…

PLC通讯时如何判断选用MODBUS方式还是现场总线方式?

在工业自动化领域&#xff0c;PLC扮演着至关重要的角色。然而&#xff0c;许多人在初次接触PLC通讯时&#xff0c;常因其复杂性而感到困扰。事实上&#xff0c;PLC的通讯并不如人们想象中的那么神秘&#xff0c;它主要只有两种类型&#xff1a;一种是需要编写代码的通讯方式&am…

Verilog语法之assign语句学习

assign语法主要是对组合逻辑的变量进行赋值的&#xff0c;就是把一个变量赋值给另一个变量&#xff0c;被复制的变量必须是wire类型的参数。 从仿真结果可以看出&#xff0c;data_in变量的值赋值给了data_out,assign语法就是赋值没有任何延迟&#xff0c;data_in是什么值&#…

服务器被挖矿了怎么办,实战清退

当我们发现服务器资源大量被占用的时候&#xff0c;疑似中招了怎么办 第一时间重启服务是不行的&#xff0c;这些挖矿木马一定是会伴随着你的重启而自动重启&#xff0c;一定时间内重新霸占你的服务器资源 第一步检查高占用进程 top -c ps -ef 要注意这里%CPU&#xff0c;如果…

[Python人工智能] 四十五.命名实体识别 (6)利用keras构建CNN-BiLSTM-ATT-CRF实体识别模型(注意力问题探讨)

从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前文讲解融合Bert的实体识别研究,使用bert4keras和kears包来构建Bert+BiLSTM-CRF模型。这篇文章将详细结合如何利用keras和tensorflow构建基于注意力机制的CNN-BiLSTM-ATT-CRF模型,并实现中文实体识别…

【MySQL】16.事务管理(重点) -- 2

1. 事务隔离级别 如何理解隔离性1 MySQL服务可能会同时被多个客户端进程(线程)访问&#xff0c;访问的方式以事务方式进行一个事务可能由多条SQL构成&#xff0c;也就意味着&#xff0c;任何一个事务&#xff0c;都有执行前&#xff0c;执行中&#xff0c;执行后的阶段。而所…

Linux 动静态库的制作,使用和加载

Linux 动静态库的制作,使用和加载 一.前置说明1.mylib.h2.mylib.c3.mymath.h mymath.c4.如何制作库 二.动静态库的制作1.静态库的制作1.制作2.使用一下静态库,验证是否成功打包 2.动态库的制作1.编译.c源文件文件生成.o目标文件2.打包生成动态库3.编写makefile文件,自动化制作动…

【SpringCloud】Ribbon负载均衡

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《深入浅出SpringBoot》《java对AI的调用开发》 《RabbitMQ》《Spring》《SpringMVC》《项目实战》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 …

警惕.360勒索病毒:如何预防.360勒索病毒攻击

导言&#xff1a; 在网络安全领域&#xff0c;勒索病毒是一种非常危险的恶意软件&#xff0c;它以其独特的加密方式和高昂的赎金要求&#xff0c;给个人和企业带来了严重的损失。.360勒索病毒便是其中之一&#xff0c;它属于BeijingCrypt勒索病毒家族&#xff0c;具有高度的隐…

NO12 蓝桥杯单片机之DS1302的使用

1 DS1302是什么 DS1302由两块存储器组成&#xff0c;一个是日历时钟寄存器还有一个是31位的静态RAM存储器。 而在蓝桥杯中常考的就是日历时钟寄存器&#xff0c;故这里只介绍日历时钟寄存器。简单来说&#xff0c;其就是一个“电子表”&#xff0c;他会自动的实时记录时间&am…

Suno - AI自动作曲

文章目录 关于 Suno创作歌词结构曲风 关于 Suno Suno 是一款自动编曲工具。 官网 &#xff1a;https://www.suno.ai Suno is building a future where anyone can make great music. Whether you’re a shower singer or a charting artist, we break barriers between you …