记一次 .NET某工控WPF程序被人恶搞的 卡死分析

2024-05-28 14:36

本文主要是介绍记一次 .NET某工控WPF程序被人恶搞的 卡死分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一:背景

1. 讲故事

这一期程序故障除了做原理分析,还顺带吐槽一下,熟悉我的朋友都知道我分析dump是免费的,但免费不代表可以滥用我的宝贵时间,我不知道有些人故意恶搞卡死是想干嘛,不得而知,希望后面类似的事情越来越少吧!废话不多说,我们来看看是如何被恶搞的。

二:WinDbg 分析

1. 程序是如何卡死的

既然是窗体程序自然就是看主线程,我们使用 k 命令即可。


0:000> k # Child-SP          RetAddr               Call Site
00 00000036`e1f7da18 00007ffe`70bf30ce     ntdll!NtWaitForSingleObject+0x14
01 00000036`e1f7da20 00007ffd`eff74910     KERNELBASE!WaitForSingleObjectEx+0x8e
...
05 (Inline Function) --------`--------     coreclr!CLREventBase::Wait+0x12 [D:\a\_work\1\s\src\coreclr\vm\synch.cpp @ 412] 
06 00000036`e1f7db20 00007ffd`f00c9afa     coreclr!Thread::WaitSuspendEventsHelper+0xc8 [D:\a\_work\1\s\src\coreclr\vm\threadsuspend.cpp @ 4626] 
07 (Inline Function) --------`--------     coreclr!Thread::WaitSuspendEvents+0x8 [D:\a\_work\1\s\src\coreclr\vm\threadsuspend.cpp @ 4663] 
08 00000036`e1f7dbe0 00007ffd`f0009c80     coreclr!Thread::RareEnablePreemptiveGC+0x7d99a [D:\a\_work\1\s\src\coreclr\vm\threadsuspend.cpp @ 2414] 
09 (Inline Function) --------`--------     coreclr!Thread::EnablePreemptiveGC+0x16 [D:\a\_work\1\s\src\coreclr\vm\threads.h @ 2044] 
0a 00000036`e1f7dc20 00007ffd`f0075d10     coreclr!Thread::RareDisablePreemptiveGC+0xc8 [D:\a\_work\1\s\src\coreclr\vm\threadsuspend.cpp @ 2156] 
0b 00000036`e1f7dca0 00007ffd`f0096a5f     coreclr!JIT_ReversePInvokeEnterRare2+0x18 [D:\a\_work\1\s\src\coreclr\vm\jithelpers.cpp @ 5462] 
0c 00000036`e1f7dcd0 00007ffd`907afe09     coreclr!JIT_ReversePInvokeEnterTrackTransitions+0xaf [D:\a\_work\1\s\src\coreclr\vm\jithelpers.cpp @ 5509] 
0d 00000036`e1f7dd00 00007ffe`72e0e858     0x00007ffd`907afe09
0e 00000036`e1f7dd80 00007ffe`72e0e3dc     user32!UserCallWinProcCheckWow+0x2f8
0f 00000036`e1f7df10 00007ffe`72e20c93     user32!DispatchClientMessage+0x9c
10 00000036`e1f7df70 00007ffe`730f0e64     user32!_fnDWORD+0x33
11 00000036`e1f7dfd0 00007ffe`70b31104     ntdll!KiUserCallbackDispatcherContinue
12 00000036`e1f7e058 00007ffe`72e21c0e     win32u!NtUserGetMessage+0x14
13 00000036`e1f7e060 00007ffe`711e28f2     user32!GetMessageW+0x2e
...
1d 00000036`e1f7e460 00007ffd`f009ba53     xxx!xxx.App.Main+0x5e
...
2a 00000036`e1f7f140 00007ffe`4cd4e1e6     hostfxr!execute_app+0x2fa [D:\a\_work\1\s\src\native\corehost\fxr\fx_muxer.cpp @ 145] 
...
34 00000036`e1f7f890 00000000`00000000     ntdll!RtlUserThreadStart+0x21

从卦中的调用栈可以看到,主线程通过 GetMessageW 从消息队列中拿到了数据,在处理时被 coreclr 押解到 WaitSuspendEventsHelper 处等待一个event事件,接下来看下这个方法的源码到底是怎么写的?简化后如下:


BOOL Thread::WaitSuspendEventsHelper(void)
{if (m_State & TS_DebugSuspendPending){ThreadState oldState = m_State;while (oldState & TS_DebugSuspendPending){ThreadState newState = (ThreadState)(oldState | TS_SyncSuspended);if (InterlockedCompareExchange((LONG*)&m_State, newState, oldState) == (LONG)oldState){result = m_DebugSuspendEvent.Wait(INFINITE, FALSE);break;}oldState = m_State;}}return result != WAIT_OBJECT_0;
}

从上面的代码可以明显的看到当前Thread 处于 TS_DebugSuspendPending 状态,从名字上看貌似和调试有关,接下来查下这个枚举的注解。


enum ThreadState
{TS_DebugSuspendPending = 0x00000008,    // Is the debugger suspending threads?
}

从注解看是因为调试器阻塞了这个线程,我去,哪里来的调试器呢?这引起了我的巨大兴趣。。。

2. 哪里来的调试器

我刚开始是抱有巨大的善意,我以为他的程序被人恶意注入导致卡死,但分析下来我还是太单纯了,继续往下看吧,既然有调试器,一般来说PEB.BeingDebugged=Yes状态,命令的输出结果也得到了证实。


0:000> !peb
PEB at 00000036e1c11000...BeingDebugged:            Yes...Base TimeStamp                     Module7ff7f5230000 65310000 Oct 19 18:08:00 2023 G:\xxx\C#\Projects\NugetApplications\Applications\xxx\bin\Debug\net6.0-windows\xxx.exe...

我以为到这里就可以告诉朋友答案,你的程序可能被人恶意注入了,但眼尖的我发现了一点异常,他的 xxx.exe 启动目录怎么会有 \bin\Debug\net6.0-windows\ 呢? 很明显这是用 VS 直接跑起来的呀。。。。

3. 真的是 VS 跑起来的吗

首先大家要知道 Visual Studio 是托管调试器,托管调试器在调试程序的时候会在 coreclr 层面设置一个全局变量 g_CORDebuggerControlFlags=0x0200,这也是 Debugger.IsAttached 的底层判断依据,不相信的朋友可以看下这个方法的底层实现:


FCIMPL0(FC_BOOL_RET, DebugDebugger::IsDebuggerAttached)
{CORDebuggerAttached();
}inline bool CORDebuggerAttached()
{return (g_CORDebuggerControlFlags & DBCF_ATTACHED);
}enum DebuggerControlFlag
{DBCF_ATTACHED                   = 0x0200,
};

有了这些基础知识之后,接下来就可以用 x 命令去验证下。

看到上面的答案基本就可以实锤了,有些朋友可能还是不大相信,我们可以这样看,VisualStudio 是 WPF 写的,如果用 VS 来调试,那么线程栈里面应该会有很多类似的 VisualStudio 字符串。

从卦中看,你说正常发布的程序怎么可能会有 9 个 Microsoft.VisualStudio.DesignTools 字符串呢?

4. 这个dump是如何生成的

这个问题我相信可能有一些朋友会比较疑惑,其实没什么疑惑的,Visual Studio 有生成Dump的功能,操作步骤为: Debug -> Save Dump As... , 接下来通过一个小例子观察一下。

internal class Program{static void Main(string[] args){Console.WriteLine($"托管调试器:{Debugger.IsAttached}");Console.WriteLine($"非托管调试器:{IsDebuggerPresent()}");Console.ReadLine();}[DllImport("kernel32.dll")]static extern bool IsDebuggerPresent();}

调试起来后,截图如下:

最后打开生成好的 Dump 文件,使用 kc 观察主线程的输出:


0:000> kc# Call Site
00 ntdll!NtWaitForSingleObject
01 KERNELBASE!WaitForSingleObjectEx
02 coreclr!CLREventWaitHelper2
03 coreclr!CLREventWaitHelper
04 coreclr!CLREventBase::WaitEx
05 coreclr!CLREventBase::Wait
06 coreclr!Thread::WaitSuspendEventsHelper
07 coreclr!Thread::WaitSuspendEvents
08 coreclr!Thread::RareEnablePreemptiveGC
09 coreclr!Thread::EnablePreemptiveGC
0a coreclr!Thread::RareDisablePreemptiveGC
0b coreclr!Thread::DisablePreemptiveGC
...
19 ConsoleApp4!ConsoleApp4.Program.Main
...
30 ntdll!RtlUserThreadStart

从卦中看,正是停留在 WaitSuspendEventsHelper 函数里,一摸一样,彻底被360°无死角的证实。。。。

三:总结

这是一次人为的故意恶搞,大概率就是想看下我到底是骡子是马。。。我分析的结果对他和他所在的公司来说没有任何价值,只能白白的浪费我的时间,真是分析多了,什么样的人都能遇到,谨以此文告知后来者,免费但请不要滥用!

这篇关于记一次 .NET某工控WPF程序被人恶搞的 卡死分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1010844

相关文章

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Python多线程应用中的卡死问题优化方案指南

《Python多线程应用中的卡死问题优化方案指南》在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助... 目录问题描述优化方案1. 网络请求优化2. 多线程架构优化3. 全局异常处理4. 配置管理优化优化效果1.

基于Python编写自动化邮件发送程序(进阶版)

《基于Python编写自动化邮件发送程序(进阶版)》在数字化时代,自动化邮件发送功能已成为企业和个人提升工作效率的重要工具,本文将使用Python编写一个简单的自动化邮件发送程序,希望对大家有所帮助... 目录理解SMTP协议基础配置开发环境构建邮件发送函数核心逻辑实现完整发送流程添加附件支持功能实现htm

C#控制台程序同步调用WebApi实现方式

《C#控制台程序同步调用WebApi实现方式》控制台程序作为Job时,需同步调用WebApi以确保获取返回结果后执行后续操作,否则会引发TaskCanceledException异常,同步处理可避免异... 目录同步调用WebApi方法Cls001类里面的写法总结控制台程序一般当作Job使用,有时候需要控制

Go语言使用net/http构建一个RESTful API的示例代码

《Go语言使用net/http构建一个RESTfulAPI的示例代码》Go的标准库net/http提供了构建Web服务所需的强大功能,虽然众多第三方框架(如Gin、Echo)已经封装了很多功能,但... 目录引言一、什么是 RESTful API?二、实战目标:用户信息管理 API三、代码实现1. 用户数据

在ASP.NET项目中如何使用C#生成二维码

《在ASP.NET项目中如何使用C#生成二维码》二维码(QRCode)已广泛应用于网址分享,支付链接等场景,本文将以ASP.NET为示例,演示如何实现输入文本/URL,生成二维码,在线显示与下载的完整... 目录创建前端页面(Index.cshtml)后端二维码生成逻辑(Index.cshtml.cs)总结

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

MySQL中读写分离方案对比分析与选型建议

《MySQL中读写分离方案对比分析与选型建议》MySQL读写分离是提升数据库可用性和性能的常见手段,本文将围绕现实生产环境中常见的几种读写分离模式进行系统对比,希望对大家有所帮助... 目录一、问题背景介绍二、多种解决方案对比2.1 原生mysql主从复制2.2 Proxy层中间件:ProxySQL2.3