×

The Story Behind Blackout: Abusing Gmer Driver to Terminate Protected Processes

hqy hqy 发表于2026-04-08 16:46:31 浏览4 评论0

抢沙发发表评论

在一次勒索软件事件响应中,我注意到团队检索到一个奇怪名字的文件。检查后,结果发现这是一个驱动程序。这引发了“驱动程序与勒索软件事件响应有什么关系?”的问题。为了理解这一点,我开始收集关于该二进制文件的信息。

每个驱动器都包含有关于它自己的信息

通过检查,我们可以看到驱动程序的原始名称是gmer64.sys,它属于一家名为GMER的公司。如果我们访问驱动程序描述中提到的网站,我们可以看到GMER是一种用于检测和删除根套件的应用程序。

每个驱动程序都应使用有效的证书进行签名才能加载。此驱动程序是在波兰位于Gmerek的GMEREK Systemy Komputerowe Przemysł Gmerek系统下进行签名的。

然后我想起了在2022年泄露的Conti勒索软件组泄露文件中读到过它。该组织使用程序的客户端GUI终止EDR,这有点混乱且容易被检测到。关于驱动程序没有概念验证(PoC),所以我不得不自己查找。

技术分析

驱动程序在运行时从驱动对象中检索其名称,允许我们控制符号名称。接下来,驱动程序使用 IoCreateDevice 创建设备对象,如果一切顺利,它将返回一个设备对象,并通过调用 IoCreateSymbolicLink创建符号链接,使该设备对象在用户模式下可访问。

由于磁盘上的驱动器名称是Blackout,我们可以看到符号链接是在相同的名称Blackout下创建的。

现在我们需要打开设备的句柄。CreateFile 的文件名应该是以 \\.\\ 开头的符号链接,所以它是 \\.\\Blackout。

所有设备对象都是由不同的驱动程序创建的DEVICE_OBJECT结构,每个驱动程序负责自己的层。这些对象是必不可少的,因为它们使通信成为可能。每个驱动程序都需要至少创建一个设备对象并为其命名,以便客户端可以与之通信。

由于设备上未配置SDDL,非管理员用户可以与其进行交互。

为了缓解这个问题,我们可以使用IoCreateDeviceSecure并指定一个安全描述符,以限制非管理员用户打开设备句柄,更多信息请查看 Controlling-device-access

使用 C 代码

所有驱动程序都必须有调度例程,特别是IRP_MJ_CREATE没有它,无法打开设备句柄。对于gmer64驱动程序,所有的调度例程都指向一个函数,在这个函数中处理主要操作,包括:

  • IRP_MJ_CREATE :创建操作,通常用于CreateFile或ZwCreateFile调用。

  • IRP_MJ_SHUTDOWN : 系统关机时调用。

  • IRP_MJ_CLOSE:关闭操作,通常在调用CloseHandle或ZwClose时使用。

  • IRP_MJ_DEVICE_CONTROL: 通用调用驱动程序,由DeviceIoControl或ZwDeviceIoControlFile调用,是本文中最重要的一个。

该函数接受两个参数 __int64 a1 和 IRP *a2,我们将重点放在 a2 上,因为它包含了用户模式客户端传递的所有数据。这个数据结构用于与驱动程序通信并传递有关 I/O 请求的信息。IRP 主要告诉驱动程序执行什么操作,并提供完成该操作所需的详细信息。

来自Windows内核编程书

以下是IRP结构中的一些其他重要字段:

  • IoStatus:包含IRP的状态(NT_STATUS)和一个信息字段。信息字段是一个多态字段,类型为 ULONG_PTR (32或64位整数),但其含义取决于IRP的类型。例如,对于Read和Write IRP,它表示在操作中传输的字节数。

  • UserBuffer:包含用于相关IRP的用户缓冲区的原始缓冲区指针。例如,Read和Write IRP将用户的缓冲区指针存储在此字段中。在DeviceIoControl IRP中,这指向请求中提供的输出缓冲区。

  • 用户事件:如果调用是异步的,并且提供了事件对象(KEVENT),则这是一个指向由客户端提供的事件对象的指针。从用户模式调用时,此事件可以在OVERLAPPED结构中提供(带有HANDLE),该结构是异步调用I/O操作的必需结构。

  • AssociatedIrp:这个联合体包含三个成员,但一次只有一个(最多)是有效的:*

  • SystemBuffer:最常使用的成员。这指向一个用于缓冲I/O操作的系统分配的非页面池缓冲区。

IRP结构在Windows内核编程书中的重要字段

在主要功能中,我们可以看到它检查 IRP_MJ_CLOSE 和 IRP_MJ_DEVICE_CONTROL 的条件。然而,我们更感兴趣的是 IRP_MJ_DEVICE_CONTROL,因为它包含了驱动程序的核心功能。IRP_MJ_CLOSE 可能包含清理代码。如果调用了其他 IRP,如 IRP_MJ_SHUTDOWN 或 IRP_MJ_CREATE,操作将成功完成。

该 Dispatch 函数接受以下八个参数:

    1. FileObject: 指向 I/O 请求的文件对象的指针。

    2. MasterIrp主I/O请求数据包。

    3. Options: 创建操作的选项。

    4. UserBuffer用户缓冲区数据的指针。

    5. Length: 要读取或写入的数据长度。

    6. LowPart: 用于特定条件或标志的变量。

    7. p_IoStatus: 指向输入/输出操作状态的指针。

    8. a1: 函数上下文或控制的额外参数。

在 dispatch 函数中,它首先检查接收到的 i/o 控制代码是否为 0x9876C004。如果是,它将变量初始化为 1;否则,返回访问被拒绝错误。因此,我们必须首先发送这个 i/o 控制代码以使驱动程序能够接收进一步的 i/o 控制代码,此外,缓冲区大小应大于 4 字节,否则,您将收到访问被拒绝的错误。内容缓冲区无关紧要,只要传递的缓冲区在之后不是 null,它将初始化缓冲区为 1,然后返回成功,并使用给定的状态和缓冲区完成 IRP。

既然我们已经获得了设备的句柄,接下来我们只需要使用 DeviceIoControl API 将 4 字节缓冲区的 I/O 控制代码发送到驱动程序以启用它。从那里,我们将可以完全访问所有其他 I/O 控制代码

发送I/O控制代码后,我们可以看到驱动程序已经返回1给我们,这意味着驱动程序现在已启用。

我们可以看到,这个函数接受一个无符号整数作为参数,这个整数是进程ID,然后它附加到该进程并打开一个句柄,最后使用 ZwTerminateProcess终止该进程。

该函数在接收到 I/O 代码 0x9876C094 时被调用,并在调用终止函数之前,检查缓冲区是否为 null 或大小是否为 4 字节。如果条件满足,它将调用终止函数;否则,它返回 STATUS_INVALID_BUFFER_SIZE。

现在,我们将所有内容结合起来,将进程ID作为参数传递并转换为整数。之后,我们调用DeviceIoControl终止该进程。I/O控制码是0x9876C094,并将进程ID作为缓冲区传递。

执行程序后,我们可以成功终止反恶意软件保护进程或任何其他类型的PPL进程。

在创建 PoC 时,它在完全打补丁的 Windows 11 22H2 系统上未被检测到,尽管它被包含在 Microsoft 驱动程序阻止列表中,因为它每年只更新 1-2 次。

你可以从 这里 找到源代码!

有许多有趣的 IOCTL 可以被滥用。这里还有一份 研究 ,由 Binary Defense 的 Jonny Johnson 进行。这份研究的重点不是终止进程,而是暂停进程线程。

总结

尽管这种技术对勒索软件集团在开始加密阶段之前关闭EDR/AV代理非常有效,但它需要本地管理员权限。然而,如果驱动程序或另一个暴露其功能(如终止或暂停线程)的驱动程序已经加载,并允许非管理员用户获取其设备的句柄,则不需要管理员权限。


打赏

本文链接:https://www.kinber.cn/post/6392.html 转载需授权!

分享到:


推荐本站淘宝优惠价购买喜欢的宝贝:

image.png

 您阅读本篇文章共花了: 

群贤毕至

访客