一、概述
Mimikatz通过其中包含的Mimidrv驱动程序,提供了利用内核模式的功能。Mimidrv是已经签名的Windows驱动模型(WDM)内核模式软件驱动程序,在相关命令前加上感叹号(!)即可与标准Mimikatz可执行文件共同使用。Mimidrv没有相应文档,并且很少被人使用,但它却提供了一个非常值得关注的方式,让我们可以在ring 0执行一些操作。
本文详细分析Mimidrv提供的功能,提出了一些可供大家参考的文档,同时向不太了解内核的读者介绍一些核心概念,最后将提出用于缓解基于驱动的威胁的防范建议。
二、为什么要使用Mimidrv?
简而言之,拥有了内核就相当于拥有了一切。实际上,一些Windows功能往往无法从用户模式调用,例如修改正在运行进程的属性,或直接与其它已加载的驱动程序进行交互等等。而驱动程序为我们提供了一种通过用户模式应用程序来调用这些函数的方法,我们也将在本文的后面部分进行深入探讨。
三、加载Mimidrv
要使用Mimikatz驱动程序,第一步可以使用命令!+,该命令会从用户模式启动驱动程序,并请求为当前令牌分配SeLoadDriverPrivilege。
Mimikatz首先检查驱动程序在当前工作目录中是否存在,如果找到磁盘上的驱动程序,则开始创建服务。服务的创建是通过服务控制管理器(SCM)API函数来完成的。具体而言,advapi32!ServiceCreate将用于注册具有以下属性的服务:
CreateService(
hSC, //Handle to the SCM database provided by OpenSCManager
'mimidrv', //Service name
'mimikatz driver (mimidrv)', //Service display name
READ_CONTROL | WRITE_DAC | SERVICE_START, //Desired access
SERVICE_KERNEL_DRIVER, //Kernel driver service type
SERVICE_AUTO_START, //Start the service automatically on boot
SERVICE_ERROR_NORMAL, //Log driver errors that occur during startup to the event log
'C:\\path\\to\\mimidrv.sys', //Absolute path of the driver on disk
NULL, //Load order group (unused)
NULL, //Not used because the previous argument is NULL
NULL, //No dependencies for the driver
NULL, //Use NT AUTHORITY\SYSTEM to start the service
NULL //Unused because we are using the SYSTEM account
);
如果成功创建了服务,则“Evervone”组将被授予对该服务的访问权限,从而允许系统上的任何用户与该服务进行交互。例如,低特权的用户可以停止该服务。
注意:我们特别要强调后期进行清理的重要性。在完成全部操作后,请记得删除驱动程序(!-),以免植入被其他人滥用。
如果成功完成,最终将通过StartService来启动服务。
四、加载后操作
服务启动后,就应该进入到Mimidrv来完成设置。该驱动程序在启动过程中没有进行任何不同寻常的操作,但如果对于没有WDM驱动程序开发经验的人来说,应该会比较复杂。
每个驱动程序都必须具有已经定义的DriverEntry函数,该函数在加载驱动程序后立即被调用,并且用于设置驱动程序要运行所需满足的要求。我们可以将其类比于用户模式代码中的main()函数。在Mimidrv的DriverEntry函数中,主要进行4项工作。
1、创建设备对象
客户端不会直接与驱动程序进行通信,而是与设备对象进行通信,因此内核模式驱动程序至少必须创建1个设备对象。然而,如果没有符号链接,用户模式代码仍然无法直接访问该设备对象。我们将在稍后详细介绍符号链接,第一步首先是要创建设备对象。
要创建设备对象,需要对nt!IoCreateDevice进行一些关键的调用。最值得关注的是第三个参数DeviceName。在globals.h中,将其设置为“mimidrv”。
在WinObj中,可以看到这个新创建的设备对象。
2、设置DispatchDeviceControl和Unload函数
如果该设备对象创建成功,则会定义DispatchDeviceControl函数,在MajorFunction分配表的IRP_MJ_DEVICE_CONTROL索引处注册,并使用MimiDispatchDeviceControl函数。这意味着,每当其接收到IRP_MJ_DEVICE_CONTROL请求(例如来自kernel32!DeviceIoControl)时,Mimidrv都会调用其内部的MimiDispatchDeviceControl函数,该函数将处理这一请求。我们将在后文的“通过MimiDispatchDeviceControl进行用户模式交互”一节中详细介绍其工作原理。
就如同每个驱动程序都必须制定一个DriveryEntry一样,在这里必须定义一个相应的Unload函数,该函数将在驱动程序被卸载时执行。
Mimidrv的DriverUnload函数非常简单,其唯一作用是删除符号链接,然后删除设备对象。
3、创建符号链接
如前文所述,如果驱动程序需要允许用户模式代码与其交互,那么就必须创建一个符号链接。用户模式应用程序将使用这个符号链接,例如通过调用nt!CreateFile和kernel32!DeviceIoControl来替代“常规”文件,以向驱动程序发送数据,或从驱动程序接收数据。
要创建符号链接,Mimidrv使用符号链接的名称和设备对象作为参数,来调用nt!IoCreateSymbolicLink。我们可以在WinObj中看到新创建的设备对象和相关的符号链接。
4、初始化Aux_klib
最后,使用AuxKlibInitialize初始化Aux_klib库。必须先完成初始化操作,然后才能调用该库中的任意函数(详细细节请参考“模块”一节)。
五、通过MimiDispatchDeviceControl进行用户模式交互
在初始化之后,驱动程序的任务仅仅是处理对其发出的请求。具体而言,是通过被称为I/O请求包(IRP)的一些不透明功能来实现的。这些IRP中包含映射到函数代码的I/O控制代码(IOCTL)。IOCTL通常是从0x8000开始,但是Mimikatz是从0x000开始,这一点与Microsoft的推荐用法相违背。Mimikatz在ioctl.h中定义了23个IOCTL,每一个IOCTL都映射到一个函数。当Mimidrv收到其中一个定义的IOCTL之后,它将会调用映射函数。这就是Mimidrv的核心功能所在。
发送IRP
为了让驱动程序执行映射到IOCTL的功能,我们必须借助此前创建的符号链接从用户模式发送IRP。Mimikatz在kuhl_m_kernel_do函数中处理该问题,该函数对nt!CreateFile进行调用,以获取设备对象的句柄,并通过kernel32!DeviceIoControl来发送IRP。这一过程将命中定义为MimiDispatchDeviceControl的IRP_MJ_DEVICE_CONTROL核心函数,并通过其IOCTL代码来查看内部定义的函数列表。在输入包含前缀“!”的命令后,将会检查KUHL_K_C结构kuhl_k_c_kernel,以获取与该命令相关联的IOCTL。该结构定义为:
typedef struct _KUHL_K_C {
const PKUHL_M_C_FUNC pCommand;
const DWORD ioctlCode;
const wchar_t * command;
const wchar_t * description;
} KUHL_K_C, *PKUHL_K_C;
在该结构中,有19个命令,其定义如下:
函数 IOCTL 命令 描述
kuhl_m_kernel_add_mimidrv N/A + 安装和(或)启动Mimikatz驱动程序
kuhl_m_kernel_remove_mimidrv N/A - 删除Mimikatz驱动程序(Mimidrv)
N/A IOCTL_MIMIDRV_PING ping Ping驱动程序
N/A IOCTL_MIMIDRV_BSOD bsod 产生BSOD蓝屏
N/A IOCTL_MIMIDRV_PROCESS_LIST process 列出进程
kuhl_m_kernel_processProtect N/A processProtect 保护进程
kuhl_m_kernel_processToken N/A processToken 复制进程令牌
kuhl_m_kernel_processPrivilege N/A processPrivilege 设置进程的所有特权
N/A IOCTL_MIMIDRV_MODULE_LIST modules 列出模块
N/A IOCTL_MIMIDRV_SSDT_LIST ssdt 列出SSDT
N/A IOCTL_MIMIDRV_NOTIFY_PROCESS_LIST notifProcess 列出进程通知回调
N/A IOCTL_MIMIDRV_NOTIFY_THREAD_LIST notifThread 列出线程通知回调
N/A IOCTL_MIMIDRV_NOTIFY_IMAGE_LIST notifImage 列出映像通知回调
N/A IOCTL_MIMIDRV_NOTIFY_REG_LIST notifReg 列出注册表通知回调
N/A IOCTL_MIMIDRV_NOTIFY_OBJECT_LIST notifObject 列出对象通知回调
N/A IOCTL_MIMIDRV_FILTER_LIST filters 列出FS过滤器
N/A IOCTL_MIMIDRV_MINIFILTER_LIST minifilters 列出minifilter