捉虫笔记(二)之 杀软请你自重点
捉虫笔记(二)之 杀软请你自重点
前一篇文章介绍了如何配置符号,这一篇文章我们来个实战。
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 的一个存储引擎ÿ…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...




