捉虫笔记(二)之 杀软请你自重点
捉虫笔记(二)之 杀软请你自重点
前一篇文章介绍了如何配置符号,这一篇文章我们来个实战。
1 现象
在我们的程序中利用robocopy
进行文件的复制。但是QA反馈,只要进行了备份操作,整个进程就会卡住。但是奇怪的是只有他的机器能发现。刚开始的时候我没有太重视这个问题。随着内部反馈的人多了,我开始对这个现象感兴趣了。
2 分析过程
2.1 初步猜测
首先我们是利用的python的脚本启动robocopy
,下面是伪代码:
proc = subprocess.Popen("robocopy %s %s /E /MT:32 /XD .* /XF *.gmp *.zip" % (src, dst), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
_, _ = proc.communicate()
robocopy
是windows自带的一个复制程序,我觉得可靠性应该是很高的。所以一开始我觉得出问题的肯定 是在我们的内部程序,我仔细检查了每个参数一无所获。
目标只能转移到robocopy
本身,刚开始觉得卡住可能是因为需要复制的文件过多,导致线程卡住,但是执行了10分钟还是纹丝不动。我就意识到估计死锁或者等待某个事件。
但是需要证据,此时我让QA的同事立马生成robocopy
的dump文件。
2.2 线程堆栈分析
拿到dump之后,立马使用WinDbg打开。
首先就执行了!runaway
看看哪个线程跑的最欢。
发现0s和5s排在前面。0号线程其实是UI线程(windows中0号线程默认是UI线程),用时最长可以理解。但是仔细分析就发现5号线程其实还没有开始执行就已经卡住了。看来我们刚开始的猜测是有出入的。robocopy
刚启动就被按住不动了。
即使卡住了,看看是不是有上锁了,执行!cs -l
,查看发现就没有任何的锁。
再执行.exr -1
看看是不是出现了什么异常。发现最近的异常还是断点异常。说明也没有异常。
思考片刻,决定还是看看每个线程的 堆栈情况如何。执行~*k
,查看所有线程的堆栈,还好线程不多。
因为涉及到一些敏感信息,我把关键点使用softwareXXX来代替。
# Child-SP RetAddr Call Site
00 00000092`21d3f088 00007ffb`ddfc1c4e ntdll!NtWaitForSingleObject+0x14
01 00000092`21d3f090 00007ff7`4c9009fa KERNELBASE!WaitForSingleObjectEx+0x8e
02 00000092`21d3f130 00007ff7`4c90296d Robocopy!WaitMultiThreaded+0x50e
03 00000092`21d3f1b0 00007ff7`4c90f3ad Robocopy!wmain+0x681
04 00000092`21d3f970 00007ffb`dec37344 Robocopy!__wmainCRTStartup+0x14d
05 00000092`21d3f9b0 00007ffb`e051cc91 kernel32!BaseThreadInitThunk+0x14
06 00000092`21d3f9e0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
0:000> ~*k;. 0 Id: 5bf8.4ee4 Suspend: 0 Teb: 00000092`21f62000 Unfrozen# Child-SP RetAddr Call Site
00 00000092`21d3f088 00007ffb`ddfc1c4e ntdll!NtWaitForSingleObject+0x14
01 00000092`21d3f090 00007ff7`4c9009fa KERNELBASE!WaitForSingleObjectEx+0x8e
02 00000092`21d3f130 00007ff7`4c90296d Robocopy!WaitMultiThreaded+0x50e
03 00000092`21d3f1b0 00007ff7`4c90f3ad Robocopy!wmain+0x681
04 00000092`21d3f970 00007ffb`dec37344 Robocopy!__wmainCRTStartup+0x14d
05 00000092`21d3f9b0 00007ffb`e051cc91 kernel32!BaseThreadInitThunk+0x14
06 00000092`21d3f9e0 00000000`00000000 ntdll!RtlUserThreadStart+0x211 Id: 5bf8.4c68 Suspend: 0 Teb: 00000092`21f6a000 Unfrozen# Child-SP RetAddr Call Site
00 00000092`2217fca8 00007ffb`ddfc1c4e ntdll!NtWaitForSingleObject+0x14
01 00000092`2217fcb0 00007ffb`d9776e2b KERNELBASE!WaitForSingleObjectEx+0x8e
02 00000092`2217fd50 00007ffb`dec37344 softwareXXX+0x76e2b
03 00000092`2217fd80 00007ffb`e051cc91 kernel32!BaseThreadInitThunk+0x14
04 00000092`2217fdb0 00000000`00000000 ntdll!RtlUserThreadStart+0x212 Id: 5bf8.5930 Suspend: 0 Teb: 00000092`21f6c000 Unfrozen# Child-SP RetAddr Call Site
00 00000092`221ff478 00007ffb`ddffbea0 ntdll!NtWaitForMultipleObjects+0x14
01 00000092`221ff480 00007ffb`ddffbd9e KERNELBASE!WaitForMultipleObjectsEx+0xf0
02 00000092`221ff770 00007ffb`d977a9c0 KERNELBASE!WaitForMultipleObjects+0xe
03 00000092`221ff7b0 00007ffb`dec37344 softwareXXX+0x7a9c0
04 00000092`221ff810 00007ffb`e051cc91 kernel32!BaseThreadInitThunk+0x14
05 00000092`221ff840 00000000`00000000 ntdll!RtlUserThreadStart+0x213 Id: 5bf8.5dbc Suspend: 0 Teb: 00000092`21f6e000 Unfrozen# Child-SP RetAddr Call Site
00 00000092`2227f648 00007ffb`e051d407 ntdll!NtWaitForWorkViaWorkerFactory+0x14
01 00000092`2227f650 00007ffb`dec37344 ntdll!TppWorkerThread+0x2f7
02 00000092`2227f950 00007ffb`e051cc91 kernel32!BaseThreadInitThunk+0x14
03 00000092`2227f980 00000000`00000000 ntdll!RtlUserThreadStart+0x214 Id: 5bf8.7f4 Suspend: 0 Teb: 00000092`21f70000 Unfrozen# Child-SP RetAddr Call Site
00 00000092`222ff7d8 00007ffb`e051d407 ntdll!NtWaitForWorkViaWorkerFactory+0x14
01 00000092`222ff7e0 00007ffb`dec37344 ntdll!TppWorkerThread+0x2f7
02 00000092`222ffae0 00007ffb`e051cc91 kernel32!BaseThreadInitThunk+0x14
03 00000092`222ffb10 00000000`00000000 ntdll!RtlUserThreadStart+0x215 Id: 5bf8.3694 Suspend: 0 Teb: 00000092`21f7a000 Unfrozen# Child-SP RetAddr Call Site
00 00000092`224fd878 00007ffb`ddfc1c4e ntdll!NtWaitForSingleObject+0x14
01 00000092`224fd880 00007ffb`d978abed KERNELBASE!WaitForSingleObjectEx+0x8e
02 00000092`224fd920 00007ff7`4c90c25f softwareXXX+0x8abed
03 00000092`224fe2a0 00007ff7`4c8ff78f Robocopy!CZEnt::CopyData+0x467
04 00000092`224ff390 00007ff7`4c90014e Robocopy!RoboCopy+0x18b
05 00000092`224ff400 00007ffb`e0533730 Robocopy!RoboCopyWorker+0x6e
06 00000092`224ff430 00007ffb`e051d79a ntdll!TppWorkpExecuteCallback+0x130
07 00000092`224ff480 00007ffb`dec37344 ntdll!TppWorkerThread+0x68a
08 00000092`224ff780 00007ffb`e051cc91 kernel32!BaseThreadInitThunk+0x14
09 00000092`224ff7b0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
接下来就是仔细分析每个线程的堆栈情况了。可以看到0,1,2,以及5号线程都在等待某个内核对象。
3,4号线程就是windows内部的线程池,这里我们暂且不关注。
先分析0号线程,我们查了函数原型NtWaitForSingleObject
,第一个参数就是需要等待的内核对象,第二个参数是否可中断,第三个参数超时时间。根据x64调用协议,前四个非浮点数且小于64位参数都是寄存器rcx
,rdx
,r8
,r9
中。
NTSTATUS NtWaitForSingleObject([in] HANDLE Handle,[in] BOOLEAN Alertable,[in] PLARGE_INTEGER Timeout
);
我们试着找出这个参数,在WinDbg命令中执行r rcx
,就是打印出rcx寄存器中的值。
找到这个值之后,我们需要验证这个值是不是内核对象。再执行!handle xxx f
,此时就会打印出关于此内核对象的具体信息。
如法炮制,分析切换到1,2,5号线程。比如我们切换到1号线程:
这里需要说下2号线程调用的函数NtWaitForMultipleObjects
,这个函数在微软的文档并未公布函数原型。但是我们可以到此线程的第二帧调用的函数KERNELBASE!WaitForMultipleObjectsEx
,而这个函数原型是有的。
从名字可以大致猜出此函数应该是等待多个内核对象。第一个参数就是等待的个数,第二个就是类似数组里面保存了句柄,第三个和第四含义和之前是一样的。
DWORD WaitForMultipleObjects([in] DWORD nCount,[in] const HANDLE *lpHandles,[in] BOOL bWaitAll,[in] DWORD dwMilliseconds
);
我们利用WinDbg来找下句柄值。
一共等待两个句柄值0x128
, 0x130
。我们在验证下找得对不对。看如下的截图发现我们找得没有错。
这里我把句柄做成表格进行对比。
线程 | 句柄 | 信息 |
---|---|---|
0 | 0x194 | ![]() |
1 | x128 | ![]() |
2 | 0x128, 0x130 | ![]() |
5 | 0x738 | ![]() |
从表格中我们大致可以看到等待的句柄是Event类型,以及权限,当前Event的状态信息。
2.3 陷入僵局
从上面的表格分析,此时线程中并没有出现明显的相互等待的现象。此时有点穷途末路的感觉。
我在想是不是还有蛛丝马迹被我忽略了。
对,还有3,4号线程我并没有分析。我立马着手在msdn搜索函数原型,可惜这两个函数在msdn未公布任何信息。但是我还不死心继续Google上搜索关于这个函数的信息,从网络搜索的信息也是相当的少,都是一笔带过简单的连参数信息都没有提及。
我开始深入思考,到底还有什么信息被我遗漏了 。
坐着已经让我无法思考了,需要走两步清空下大脑。
转了一圈回来之后,发现还是需要从基础的堆栈分析,一个帧一个帧过滤下。从上面的堆栈中可以看到5号线程堆栈是最长的。我们再看下5号线程的堆栈信息。
0:000> ~5k;# Child-SP RetAddr Call Site
00 00000092`224fd878 00007ffb`ddfc1c4e ntdll!NtWaitForSingleObject+0x14
01 00000092`224fd880 00007ffb`d978abed KERNELBASE!WaitForSingleObjectEx+0x8e
02 00000092`224fd920 00007ff7`4c90c25f softwareXXX+0x8abed
03 00000092`224fe2a0 00007ff7`4c8ff78f Robocopy!CZEnt::CopyData+0x467
04 00000092`224ff390 00007ff7`4c90014e Robocopy!RoboCopy+0x18b
05 00000092`224ff400 00007ffb`e0533730 Robocopy!RoboCopyWorker+0x6e
06 00000092`224ff430 00007ffb`e051d79a ntdll!TppWorkpExecuteCallback+0x130
07 00000092`224ff480 00007ffb`dec37344 ntdll!TppWorkerThread+0x68a
08 00000092`224ff780 00007ffb`e051cc91 kernel32!BaseThreadInitThunk+0x14
09 00000092`224ff7b0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
5号线程刚开始Robocopy!CZEnt::CopyData
就被按住了。我们反汇编看下Robocopy!CZEnt::CopyData
代码。
反汇编的函数大概有1m A4纸那么长,该怎么看呢。
我们可以直接从02帧那里看到返回值地址00007ff74c90c25f
,这个返回值就是softwareXXX+0x8abed
执行完后返回到Robocopy!CZEnt::CopyData
再从Robocopy!CZEnt::CopyData
反汇编的函数搜索此地址。
搜索的结果如下:
结果发现了点异常,上面的一条语句好像不大对劲。反汇编这里的函数是Robocopy!_imp_CopyFile2
,而实际堆栈里面显示的却另外一个模块的代码。
再看softwareXXX
这个模块加上了一个相当大的偏移0x8abed
,这是不同寻常的,很有可能找的符号不对。
而这个模块的代码在Robocopy
中根本就没有。这是从哪里来的?相当的奇怪,突然感觉有点意思。
看下这个模块的信息,在命令中执行lmvm softwareXXX
,发现了端倪,原来是个杀软,把这个dll注入了robocopy
中,很有可能对函数进行了拦截,导致了这个函数一直无法返回。
0:000> lmvm softwareXXX
Browse full module list
start end module name
00007ffb`d9700000 00007ffb`d9a93000 softwareXXX (no symbols) Loaded symbol image file: softwareXXX.dllImage path: C:\Program Files\softwareXXX\softwareXXX.dllImage name: softwareXXX.dllBrowse all global symbols functions dataTimestamp: Tue Mar 26 22:56:55 2024 (6602E237)CheckSum: 0038B6E1ImageSize: 00393000File version: 1.0.1.622Product version: 1.0.1.622File flags: 0 (Mask 3F)File OS: 40004 NT Win32File type: 2.0 DllFile date: 00000000.00000000Translations: 0804.04b0Information from resource tables:CompanyName: https://www.softwareXXX.cn/ProductName: XDRInternalName: softwareXXX.dllOriginalFilename: softwareXXX.dllProductVersion: 1.0.1.622FileVersion: 1.0.1.622FileDescription: softwareXXX 应用程序监控模块LegalCopyright: Copyright (C) 2021
再回过头看上面的堆栈信息,此dll对系统的其他的关键函数也进行了拦截处理。
2.4 验证问题
接下来就是找IT部门,让他们把robocopy
加入白名单,再进行测试。经过协商之后,我们在进行测试问题就解决了。
相关文章:

捉虫笔记(二)之 杀软请你自重点
捉虫笔记(二)之 杀软请你自重点 前一篇文章介绍了如何配置符号,这一篇文章我们来个实战。 1 现象 在我们的程序中利用robocopy进行文件的复制。但是QA反馈,只要进行了备份操作,整个进程就会卡住。但是奇怪的是只有他…...

python学习之路 - python的函数
目录 一、python函数1、函数介绍2、函数的定义3、函数的参数4、函数的返回值5、函数说明文档6、函数的嵌套调用7、变量的作用域8、综合案例9、函数与方法的区别 二、python函数进阶1、函数多返回值2、函数多种传参方式a、位置参数b、关键字参数c、缺省参数d、不定长参数 3、匿名…...

使用SpringBoot+Vue3开发项目(2)---- 设计文章分类的相关接口及页面
目录 一.所用技术栈: 二.后端开发: 1.文章分类列表渲染: 2.新增文章分类: 3.编辑文章分类: 4.删除文章分类 : 5.完整三层架构后端代码: (1)Controller层:…...
Layui---toolbar与 tool的区别
table.on(toolbar): table.on(toolbar): 这个事件监听器是用来处理表格工具栏的事件。工具栏通常位于表格的上方,可以包含添加、删除、导出等按钮。当用户与这些工具栏中的按钮交互时,比如点击一个按钮来添加新行或者进行搜索操作,…...

U-Net++原理与实现(含Pytorch和TensorFlow源码)
U-Net原理与实现 引言1. U-Net简介1.1 编码器(Encoder)1.2 解码器(Decoder)1.3 跳跃连接(Skip Connections) 2. U-Net详解2.1 密集跳跃连接2.2 嵌套和多尺度特征融合2.3 参数效率和性能2.4 Pytorch代码2.5 …...

产品心理学:啦啦队效应
电视里我们常会看见这样一个场景,一群女孩穿着短裙有说有笑地在大街上走过,把路人们都看傻了,其实单个来看,她们的长相并不出众,可是凑在一起就显得青春貌美,这就是“啦啦队效应”——cheerleader effect。…...

AC+AP组网
配置DHCP Switch1 <Huawei>sys [Huawei]undo in en [Huawei]vlan batch 10 20 30 40[Huawei]int vlan 10 [Huawei-Vlanif10]ip add 192.168.10.1 24 [Huawei-Vlanif10]quit[Huawei]int vlan 20 [Huawei-Vlanif20]ip add 192.168.20.1 24 [Huawei-Vlanif20]quit[Huawei]…...

2024.8.05(glibc的安装及MySQL的安全用户角色权限)
一、glibc的安装 1、清空/etc目录下的my.cnf [rootlocalhost ~]# ls -l /etc/my.cnf -rw-r--r--. 1 root root 570 6月 8 2017 /etc/my.cnf [rootlocalhost ~]# rm -rf /etc/my.cnf 2、删除mariadb [rootlocalhost ~]# yum -y remove mariadb [rootlocalhost ~]# find / -na…...

【精选】6款一键生成论文的软件3000字论文网站
千笔-AIPassPaPer是一款功能强大且全面的AI论文写作工具,特别适合学术研究者和学生使用。它不仅能够一键生成高质量的论文初稿,还涵盖了700多个学科专业方向,满足各种学术需求。 一、千笔-AIPassPaPer 传送门:https://www.aipape…...

如何使用 PHP Simple HTML DOM Parser 轻松获取网页中的特定数据
背景介绍 网页数据的抓取已经成为数据分析、市场调研等领域的重要工具。无论是获取产品价格、用户评论还是其他公开数据,网页抓取技术都能提供极大的帮助。今天,我们将探讨如何使用 PHP Simple HTML DOM Parser 轻松获取网页中的特定数据。PHP Simple H…...

Linux笔记 --- 传统链表
目录 链表 单向链表 单向循环链表 双向链表 设计表 初始化 在auchor后插入节点, 在auchor前插入节点 删除节点 传统链表 通过使用链表我们可以将一个数组中的数据分开到不同位置存放并使用指针指向他们,使之逻辑相连,解决了顺序存储所需要…...

C语言的编译(预处理操作)+链接
目录 翻译环境和执行环境 预定义符号 #define定义标识符 续行符\ #define定义宏 再说一下,#define其实就是替换 #和## 宏和函数的对比 命名约定 #undef 命令行定义 条件编译 文件包含 避免头文件重复引用,否则会增加代码长度 翻译环境和执行环境 在C中存…...

FFmpeg实战 - 解复用与解码
大纲目录 文章目录 前置知识音视频基础概念解复用、解码的流程分析FFMPEG有8个常用库 常见音视频格式的介绍aac格式介绍(ADTS)h264格式分析FLV和MP4格式介绍 FFmpeg解码解封装实战数据包和数据帧(AVPacket/AVFrame)AVPacket/AVFra…...

8.5作业
1.思维导图 2.提示并输入一个字符串,统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数,要求使用C风格字符串完成 #include <iostream>using namespace std;int main() {string str;cout << "请输入一个字符串&quo…...
【问题】C++:有哪些类型的智能指针,区别?
智能指针是一种在 C 中管理动态分配内存的工具,可以帮助避免内存泄漏和提高程序的安全性。在 C11 标准引入之后,C 提供了三种主要类型的智能指针,它们分别是 std::unique_ptr、std::shared_ptr 和 std::weak_ptr。这些智能指针有不同的所有权…...
Go-反射
概念 在Go语言中,反射(reflection)是指在运行时检查程序的结构、变量和接口的机制。可以通过反射获取和修改变量的值、获取变量的类型信息、调用方法等操作。 反射主要由reflect包提供,它定义了两个重要的类型:Type和…...

【深度学习】DeepSpeed,ZeRO 数据并行的三个阶段是什么?
文章目录 ZeRO实验实验设置DeepSpeed ZeRO Stage-2 实验性能比较进一步优化DeepSpeed ZeRO Stage-3 和 CPU 卸载结论ZeRO ZeRO(Zero Redundancy Optimizer)是一种用于分布式训练的大规模深度学习模型的优化技术。它通过分片模型状态(参数、梯度和优化器状态)来消除数据并行…...
代码随想录算法训练营第三十六天 | 1049. 最后一块石头的重量 II、494. 目标和、474.一和零
一、1049. 最后一块石头的重量 II 题目链接:1049. 最后一块石头的重量 II - 力扣(LeetCode) 文章讲解:代码随想录 (programmercarl.com)——1049. 最后一块石头的重量 II 视频讲解:动态规划之背包问题,这个…...
Pandas行列变换指南:数据重塑的艺术
数据分析中,数据的形态至关重要。pandas库提供了一系列工具,让我们能够轻松地重塑数据。以下是一些常见的pandas行列变换方法,每种方法都配有完整的代码示例。 环境准备 首先,确保你的环境中安装了pandas和numpy库: …...

1.MySQL面试题之innodb如何解决幻读
1. 写在前面 在数据库系统中,幻读(Phantom Read)是指在一个事务中,两次读取同一范围的数据集时,由于其他事务的插入操作,导致第二次读取结果集发生变化的问题。InnoDB 作为 MySQL 的一个存储引擎ÿ…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

Linux基础开发工具——vim工具
文章目录 vim工具什么是vimvim的多模式和使用vim的基础模式vim的三种基础模式三种模式的初步了解 常用模式的详细讲解插入模式命令模式模式转化光标的移动文本的编辑 底行模式替换模式视图模式总结 使用vim的小技巧vim的配置(了解) vim工具 本文章仍然是继续讲解Linux系统下的…...

英国云服务器上安装宝塔面板(BT Panel)
在英国云服务器上安装宝塔面板(BT Panel) 是完全可行的,尤其适合需要远程管理Linux服务器、快速部署网站、数据库、FTP、SSL证书等服务的用户。宝塔面板以其可视化操作界面和强大的功能广受国内用户欢迎,虽然官方主要面向中国大陆…...