总结
网络犯罪分子越来越多地使用自己的驱动程序——要么利用易受攻击的合法驱动程序,要么使用定制的驱动程序来禁用端点检测和响应 (EDR) 系统并逃避检测或预防功能。
Elastic 安全实验室监控到一项以经济为目的的活动,该活动通过使用HEARTCRYPT打包的加载器部署了 MEDUSA 勒索软件。该加载程序与我们称之为 ABYSSWORKER 的中国供应商提供的已撤销证书签名驱动程序一起部署,它安装在受害者机器上,然后用于定位和抑制不同的 EDR 供应商。ConnectWise最近在另一项活动中报告了此 EDR-killer 驱动程序,它使用了不同的证书和 IO 控制代码,当时讨论了它的一些功能。2022 年,Google Cloud Mandiant 披露了一个名为POORTRY的恶意驱动程序,我们认为这是对该驱动程序的最早提及。
在本文中,我们将深入研究该驱动程序,检查其各种功能和技术。我们还在每个逆向代码截图下提供了相对虚拟地址(RVA),以将研究与参考样本联系起来,并提供了一个小型客户端示例,您可以使用它来进一步试验此恶意软件。
技术分析
PE 头
该二进制文件是一个名为smuol.sys
的 64 位 Windows PE 驱动程序,模仿合法的 CrowdStrike Falcon 驱动程序。
在分析时,我们在 VirusTotal 上发现了十几个样本,时间范围从 2024-08-08 到 2025-02-24。大多数都经过了 VMProtect 保护,但有两个(见下表)未受保护。
所有样本均使用可能被盗或被撤销的中国公司证书进行签名。这些证书广为人知并在不同的恶意软件样本和活动中共享,但并不特定于该驱动程序。证书指纹如下:
fingerprint | 名称 |
---|---|
51 68 1b 3c 9e 66 5d d0 b2 9e 25 71 46 d5 39 dc | 佛山市高明科德宇绝缘材料有限公司 |
7f 67 15 0f bb 0d 25 4e 47 42 84 c7 f7 81 9c 4f | 霏晓 |
72 88 1f 10 cd 24 8a 33 e6 12 43 a9 e1 50 ec 1d | 福州鼎鑫贸易有限公司 |
75 e8 e7 b9 04 3b 13 df 60 e7 64 99 66 30 21 c1 | 长沙恒翔信息技术有限公司 |
03 93 47 e6 1d ec 6f 63 98 d4 d4 6b f7 32 65 6c | 新疆易视联网络科技有限公司 |
4e fa 7e 7b ba 65 ec 1a b7 74 f2 b3 13 57 d5 99 | 深圳市云电科技有限公司 |
混淆
ABYSSWORKER 使用始终返回相同值的函数,依赖于不透明谓词和其他派生函数的组合。例如,下面的零返回函数始终根据硬编码派生值返回0
。
以下是其中一个派生函数:
这些常量返回函数在整个二进制文件中被反复调用,以阻碍静态分析。但是,这样的函数只有三个,并且它们没有在任何谓词中使用,而只是被简单调用。我们可以轻松识别它们,这使得这是一种低效的混淆方案。
初始化
初始化时,驱动程序首先获取指向几个内核模块及其客户端保护功能的指针,这将在以下章节中讨论。
然后,它创建一个路径为\\device\\czx9umpTReqbOOKF
设备和一个路径为\\??\\fqg0Et4KlNt4s1JT
符号链接。
它通过为其主要功能注册回调来完成初始化。
设备打开时的客户端保护
当驱动程序设备打开时,会调用IRP_MJ_CREATE
主回调。此函数负责将进程 ID 添加到要保护的进程列表中,并从正在运行的进程列表中删除目标进程的任何预先存在的句柄。
由于在设备打开时内核回调是在客户端进程的上下文中执行的,因此该函数从当前内核线程中检索进程 ID。
在将进程 ID 添加到保护列表之前,ABYSSWORKER 会在其他正在运行的进程中搜索并删除任何现有的客户端进程句柄。
为了实现这一点,恶意软件通过强制执行其进程 ID (PID) 来迭代现有进程,以避免依赖任何 API。对于每个进程,它会遍历它们的句柄(也会使用强力手段),并检查底层对象是否对应于客户端进程。如果找到匹配项,它将使用作为参数传递的值( 0x8bb
)剥夺访问权限。
最后,它将 PID 添加到受保护进程的全局列表中。
如前所述,驱动程序在初始化阶段设置其保护功能。此保护依赖于使用ObRegisterCallback
API 注册两个pre-operation
回调:一个用于检测对其受保护进程的句柄的打开情况,另一个用于检测对这些受保护进程的线程的句柄的打开情况。
这两个回调的操作方式相同:它们将句柄的所需访问权限设置为零,从而有效地拒绝创建句柄。
DeviceIoControl 处理程序
当收到设备 I/O 控制请求后,ABYSSWORKER 会根据 I/O 控制代码将请求分发给处理程序。这些处理程序涵盖了从文件操作到进程和驱动程序终止的广泛操作,提供了可用于终止或永久禁用 EDR 系统的综合工具集。
我们在下表中详细说明了不同的 IO 控制:
名称 | 代码 |
---|---|
启用恶意软件 | 0x222080 |
复制文件 | 0x222184 |
按模块名称删除回调和设备 | 0x222400 |
用模块名称替换驱动程序主要功能 | 0x222404 |
根据模块名称终止系统线程 | 0x222408 |
拆卸微型过滤装置 | 0x222440 |
删除文件 | 0x222180 |
禁用恶意软件 | 0x222084 |
加载 API | 0x2220c0 |
减少所有驱动程序的引用计数器 | 0x222100 |
减少所有设备引用计数器 | 0x222104 |
终止进程 | 0x222144 |
终止线程 | 0x222140 |
从 Ntfs 和 Pnp 驱动程序的主要函数中删除挂钩 | 0x222444 |
重启 | 0x222664 |
启用恶意软件(0x222080)
正如本篇博文中所讨论的,客户端必须通过向驱动程序发送密码( 7N6bCAoECbItsUR5-h4Rp2nkQxybfKb0F-wgbJGHGh20pWUuN1-ZxfXdiOYps6HTp0X
)来启用驱动程序,在我们的例子中,是通过0x222080
IO 控制。
处理程序只是将用户输入与硬编码密码进行比较。如果正确,它会将全局标志设置为 true (1)。在所有其他处理程序中都会检查此标志,以允许或拒绝执行。
加载 API(0x2220c0)
恶意软件中的大多数处理程序依赖于必须使用此处理程序加载的内核 API。该处理程序使用先前在初始化期间加载的内核模块指针,加载这些全局变量以及几个结构。一旦加载完成,就会设置一个全局标志来表明这些 API 可用。
该处理程序有两种操作模式:完整模式和部分模式。在完整模式下,它使用用户提供的函数名称和 RVA 的映射结构作为 IO 控制的输入来加载 API。在部分模式下,它会自行搜索一些 API,但不会加载在完整模式下加载的所有 API,因此称为部分模式。如果用户由于无法提供这种映射结构而选择部分模式,则某些处理程序将无法执行。在本章中,我们仅介绍完整的操作模式。
我们详细说明下面所使用的结构:
#define AM_NAME_LENGTH 256
typedef struct _struct_435
{
uint64_t rva;
char name[AM_NAME_LENGTH];
} struct_435_t;
#define AM_ARRAY_LENGTH 1024
typedef struct _struct_433
{
struct_435_t array[AM_ARRAY_LENGTH];
uint32_t length;
} struct_433_t;
我们在下面提供了一个简短的使用示例:
struct_433_t api_mapping = {
.length = 25,
.array = {
[0] = {.rva = 0xcec620, .name = "PspLoadImageNotifyRoutine"},
[1] = {.rva = 0xcec220, .name = "PspCreateThreadNotifyRoutine"},
[2] = {.rva = 0xcec420, .name = "PspCreateProcessNotifyRoutine"},
// (...)
[24] = {.rva = 0x250060, .name = "NtfsFsdShutdown"},
}};
uint32_t malware_load_api(HANDLE device)
{
return send_ioctrl(device, IOCTRL_LOAD_API, &api_mapping, sizeof(struct_433_t), NULL, 0);
}
为了加载其 API,该函数首先从不同的内核对象类型加载三个“回调列表”。这些由删除属于特定模块的已注册通知回调的处理程序使用。
然后,它使用提供的结构加载指向函数的指针,只需搜索函数名称并将关联的 RVA 添加到模块的基地址即可。
这是针对以下 25 函数完成的:
PspLoadImageNotifyRoutine
PspCreateThreadNotifyRoutine
PspCreateProcessNotifyRoutine
CallbackListHead
PspSetCreateProcessNotifyRoutine
PspTerminateThreadByPointer
PsTerminateProcess
IopInvalidDeviceRequest
ClassGlobalDispatch
NtfsFsdRead
NtfsFsdWrite
NtfsFsdLockControl
NtfsFsdDirectoryControl
NtfsFsdClose
NtfsFsdCleanup
NtfsFsdCreate
NtfsFsdDispatchWait
NtfsFsdDispatchSwitch
NtfsFsdDispatch
NtfsFsdFlushBuffers
NtfsFsdDeviceControl
NtfsFsdFileSystemControl
NtfsFsdSetInformation
NtfsFsdPnp
NtfsFsdShutdown
文件复制和删除(0x222184、0x222180)
为了复制或删除文件,ABYSSWORKER 依赖于一种虽然不是新鲜但仍然很有趣的策略。不使用像NtCreateFile
这样的通用 API,而是从头创建 I/O 请求包 (IRP) 并将其直接发送到包含目标文件的相应驱动设备。
创建文件
文件创建功能用于展示该机制的工作原理。该函数首先从文件路径获取驱动设备。然后,创建一个新的文件对象并将其链接到目标驱动设备,确保新对象正确链接到驱动器。
然后,它创建一个新的 IRP 对象并设置执行文件创建操作所需的所有数据。此 IRP 所针对的主要功能在MajorFunction
属性中指定,在本例中,该属性设置为IRP_MJ_CREATE
,这与文件创建预期一致。
然后,恶意软件将 IRP 发送到目标驱动设备。尽管它可以使用IoCallDriver
API 来执行此操作,但它通过调用相应设备的主要功能手动发送 IRP。
此时,文件对象已有效,可供进一步使用。处理程序通过增加文件对象的引用计数器并将其分配给其输出参数以供以后使用来完成其工作。
复制文件
要复制文件,ABYSSWORKER 会打开源文件和目标文件,然后从源文件读取( IRP_MJ_READ
)并将( IRP_MJ_WRITE
)写入目标文件。
删除文件
删除处理程序将文件属性设置为ATTRIBUTE_NORMAL
以取消保护任何只读文件,并将文件处置设置为删除( disposition_info.DeleteFile = 1
)以使用IRP_MJ_SET_INFORMATION
IRP 删除文件。
按模块名称删除通知回调(0x222400)
恶意软件客户端可以使用此处理程序来隐藏 EDR 产品及其可见性。它搜索并删除所有已注册的通知回调。目标回调是使用以下 API 注册的回调:
PsSetCreateProcessNotifyRoutine
PsSetLoadImageNotifyRoutine
PsSetCreateThreadNotifyRoutine
ObRegisterCallbacks
CmRegisterCallback
此外,它还会删除通过 MiniFilter 驱动程序注册的回调,并且可以选择删除属于特定模块的设备。
要删除这些通知回调,处理程序会使用各种方法找到它们,例如先前在加载 API 处理程序中加载的三个全局回调列表,其中包含使用ObRegisterCallbacks
和CmRegisterCallback
注册的回调。然后使用相应的 API(如ObUnRegisterCallbacks
和CmUnRegisterCallbacks
删除它们。
使用这些方法的盲目 EDR 值得专门写一篇博客文章来介绍。为了使这篇文章简洁,我们不会在这里提供更多细节,但我们邀请读者在两个有据可查的实施这些技术的项目中探索这些方法:
用模块名称替换驱动程序主要功能 0x222404
干扰驱动程序的另一种方法是使用此处理程序用虚拟函数替换其所有主要函数,从而在给定目标模块名称的情况下禁用与驱动程序的任何交互。
为实现此目的,ABYSSWORKER 会遍历Driver
和Filesystem
对象目录中的驱动程序对象。对于每个驱动程序对象,它将底层模块名称与目标模块进行比较,如果匹配,则用IopInvalidDeviceRequest
替换其所有主要功能。
分离微型过滤设备(0x222440)
该处理程序会遍历在Driver
和FileSystem
对象目录中找到的所有驱动程序对象。对于每个驱动程序,它会探索其设备树并分离与微过滤驱动程序关联的所有设备: FltMgr.sys
。
该函数的工作原理是通过AttachedDevice
和NextDevice
指针遍历驱动程序的设备,检索每个设备关联驱动程序的模块名称,并将其与作为参数传递的目标模块名称 ( ”FltMgr.sys”
) 进行比较。如果名称匹配,它会使用IoDetachDevice
函数取消链接该设备。
根据模块名称终止系统线程(0x222408)
此处理程序通过强制执行线程 ID 来迭代线程,如果线程是系统线程并且其起始地址属于目标模块,则终止它们。
为了终止线程,恶意软件会排队 APC(异步过程调用)以在目标线程的上下文中执行代码。一旦执行,此代码将依次调用PsTerminateSystemThread
。
终止进程并终止线程(0x222144、0x222140)
通过这两个处理程序,您可以使用PsTerminateProcess
和PsTerminateThread
根据其 PID 或线程 ID (TID) 终止任何进程或线程。
从 Ntfs 和 Pnp 驱动程序的主要功能中删除挂钩(0x222444)
除了注册通知回调之外,一些 EDR 还喜欢挂接NTFS
和PNP
驱动程序的主要功能。为了删除这些挂钩,恶意软件可以调用该驱动程序来恢复这些驱动程序原来的主要功能。
ABYSSWORKER 只是迭代每个已注册的主要函数,检查该函数是否属于驱动模块,如果不属于,则意味着该函数已被挂钩,因此它会用原始函数替换它。
重启 0x222664
为了重新启动机器,该处理程序使用未记录的函数HalReturnToFirmware
。
客户端实现示例
在这篇博文中,我们提供了一个小型客户端实现示例。此示例与参考示例一起使用并用于调试它,但并未实现驱动程序的所有 IOCTRL,并且将来不太可能更新。
但是,它包含启用它和加载其 API 的所有功能,因此我们希望任何有动力的读者在本文信息的帮助下,都能够扩展它并进一步试验该恶意软件。
该项目的存储库可在此处获得。
恶意软件和 MITRE ATT&CK
Elastic 使用MITRE ATT&CK框架来记录威胁针对企业网络使用的常见策略、技术和程序。
战术
技术
技术代表对手如何通过采取行动来实现战术目标。
缓解措施
雅拉
Elastic Security 已创建与本文相关的以下 YARA 规则:
观察结果
本研究讨论了以下可观察结果:
可观测 | 类型 | 参考 | 日期 |
---|---|---|---|
6a2a0f9c56ee9bf7b62e1d4e1929d13046cd78a93d8c607fe4728cc5b1e8d050 | SHA256 | ABYSSWORKER 参考样本 | VT 首次出现:2025-01-22 |
b7703a59c39a0d2f7ef6422945aaeaaf061431af0533557246397551b8eed505 | SHA256 | ABYSSWORKER 样本 | VT 首次出现:2025-01-27 |
参考资料
- 谷歌云 Mandiant、Mandiant 智能。我郑重发誓我的司机不怀好意:寻找经过证明签名的恶意软件。https://cloud.google.com/blog/topics/threat-intelligence/hunting-attestation-signed-malware/
- Unit42,杰罗姆·图贾格,丹尼尔·邦斯。Crypted Hearts:揭露 HeartCrypt 打包即服务操作, 13 12 月、 2024 。https://unit42.paloaltonetworks.com/packer-as-a-service-heartcrypt-malware/
- ConnectWise,布莱克·伊金。“攻击者利用 Microsoft Teams 默认设置和快速助手进行社会工程攻击”,2025 年 1 月 31 。https://www.linkedin.com/pulse/attackers-leveraging-microsoft-teams-defaults-quick-assist-p1u5c/
- wavestone-cdt,8 月30 , 2024 。https://github.com/wavestone-cdt/EDRSandblast/tree/master
- myzxcg,5 月24 , 2024 。https://github.com/myzxcg/RealBlindingEDR