SystemInformer驱动开发揭秘:内核模式监控的实现

SystemInformer驱动开发揭秘:内核模式监控的实现

【免费下载链接】systeminformer A free, powerful, multi-purpose tool that helps you monitor system resources, debug software and detect malware. Brought to you by Winsider Seminars & Solutions, Inc. @ http://www.windows-internals.com 【免费下载链接】systeminformer 项目地址: https://gitcode.com/GitHub_Trending/sy/systeminformer

引言:内核监控的技术挑战与解决方案

你是否曾在调试系统级程序时,因缺乏底层进程活动的实时可见性而束手无策?是否在排查恶意软件时,因无法追踪内核态异常行为而功亏一篑?SystemInformer的内核驱动(KSystemInformer)通过精妙的内核模式设计,为这些痛点提供了一站式解决方案。本文将深入剖析其驱动开发的核心技术,包括设备对象管理、驱动信息查询、安全访问控制等关键实现,帮助开发者掌握Windows内核监控的底层逻辑与实践方法。

读完本文你将获得:

  • 内核驱动与用户态通信的完整实现框架
  • 驱动/设备对象操作的安全模式设计
  • 系统资源监控的内核级数据采集技术
  • 跨版本Windows内核兼容的编程实践
  • 内核模式异常处理与安全防护策略

驱动架构概览:从用户请求到内核响应

SystemInformer的内核监控能力源于KSystemInformer驱动的分层设计,其核心架构包含三个关键组件:对象管理层信息查询层安全访问层。以下流程图展示了用户态请求获取驱动信息的完整处理流程:

mermaid

核心数据结构设计

驱动开发的基础是对内核对象模型的深入理解。KSystemInformer定义了多种信息结构体以封装不同维度的驱动数据:

// 驱动基础信息结构体
typedef struct _KPH_DRIVER_BASIC_INFORMATION {
    ULONG Flags;               // 驱动对象标志
    PVOID DriverStart;         // 驱动加载基地址
    SIZE_T DriverSize;         // 驱动映像大小
} KPH_DRIVER_BASIC_INFORMATION, *PKPH_DRIVER_BASIC_INFORMATION;

// 信息类别枚举定义
typedef enum _KPH_DRIVER_INFORMATION_CLASS {
    KphDriverBasicInformation,        // 基础属性信息
    KphDriverNameInformation,         // 驱动名称信息
    KphDriverServiceKeyNameInformation,// 服务键名信息
    KphDriverImageFileNameInformation  // 映像文件路径信息
} KPH_DRIVER_INFORMATION_CLASS;

这些结构体设计遵循了Windows内核的标准内存布局,确保与IoDriverObjectType等系统对象的二进制兼容性。

设备与驱动对象操作:内核模式的安全实践

设备句柄打开:从用户请求到内核验证

KSystemInformer通过KphOpenDevice函数实现设备对象的安全访问,该函数的核心挑战在于如何安全地处理用户态输入并创建内核句柄。其实现采用了双模式验证策略:

NTSTATUS KphOpenDevice(
    _Out_ PHANDLE DeviceHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes,
    _In_ KPROCESSOR_MODE AccessMode
) {
    // 用户模式下捕获并验证输入参数
    if (AccessMode != KernelMode) {
        __try {
            ProbeOutputType(DeviceHandle, HANDLE);
            ProbeInputType(ObjectAttributes, OBJECT_ATTRIBUTES);
            // 复制并修正对象属性
            RtlCopyVolatileMemory(&capturedAttributes, ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode(); // 捕获并返回内存访问异常
        }
        // 设置内核句柄标志并强制访问检查
        SetFlag(capturedAttributes.Attributes, OBJ_KERNEL_HANDLE | OBJ_FORCE_ACCESS_CHECK);
    }
    // 创建文件对象以打开设备
    status = KphCreateFile(&fileHandle, DesiredAccess, objectAttributesPtr, 
                          &ioStatusBlock, NULL, 0, 
                          FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
                          FILE_NON_DIRECTORY_FILE, NULL, 0, options, KernelMode);
    // 从文件对象获取关联设备对象
    status = ObReferenceObjectByHandle(fileHandle, 0, *IoFileObjectType, 
                                      KernelMode, &fileObject, NULL);
    // 通过设备对象打开驱动句柄
    status = ObOpenObjectByPointer(IoGetRelatedDeviceObject(fileObject), ...);
}

这段代码展示了三个关键安全实践:

  1. 用户输入验证:使用Probe函数族验证用户态指针有效性
  2. 句柄属性修正:强制设置OBJ_KERNEL_HANDLE确保句柄隔离
  3. 访问模式分离:根据AccessMode执行不同安全检查逻辑

驱动对象关联:设备到驱动的层级访问

设备对象与驱动对象是一对多的关联关系。KphOpenDeviceDriver函数通过设备句柄获取其关联的驱动对象,实现了从设备到驱动的层级访问:

NTSTATUS KphOpenDeviceDriver(
    _In_ HANDLE DeviceHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _Out_ PHANDLE DriverHandle,
    _In_ KPROCESSOR_MODE AccessMode
) {
    // 获取设备对象引用
    status = ObReferenceObjectByHandle(DeviceHandle, DesiredAccess, 
                                      *IoDeviceObjectType, AccessMode, 
                                      &deviceObject, NULL);
    // 通过设备对象的DriverObject成员获取驱动对象
    status = ObOpenObjectByPointer(deviceObject->DriverObject, 
                                  (AccessMode ? 0 : OBJ_KERNEL_HANDLE),
                                  NULL, DesiredAccess, *IoDriverObjectType, 
                                  AccessMode, &driverHandle);
}

这里需要特别注意内核对象的引用计数管理:ObReferenceObjectByHandle会增加对象引用计数,必须在使用完毕后通过ObDereferenceObject释放,否则会导致内核内存泄漏。

驱动信息查询:多维度数据采集实现

基础信息查询:驱动对象属性提取

KphQueryInformationDriver是信息采集的核心函数,它根据不同的信息类别执行针对性的数据提取。对于基础信息(KphDriverBasicInformation),直接读取驱动对象的核心属性:

case KphDriverBasicInformation:
    if (DriverInformationLength < sizeof(KPH_DRIVER_BASIC_INFORMATION)) {
        status = STATUS_INFO_LENGTH_MISMATCH;
        break;
    }
    basicInfo = (PKPH_DRIVER_BASIC_INFORMATION)DriverInformation;
    basicInfo->Flags = driverObject->Flags;          // 提取驱动标志
    basicInfo->DriverStart = driverObject->DriverStart; // 驱动加载地址
    basicInfo->DriverSize = driverObject->DriverSize;   // 驱动映像大小
    returnLength = sizeof(KPH_DRIVER_BASIC_INFORMATION);
    status = STATUS_SUCCESS;
    break;

驱动路径获取:跨版本兼容实现

获取驱动完整路径是一个典型的跨版本兼容难题。Windows 10 16299版本引入了IoQueryFullDriverPath函数,而早期版本需要使用替代方案:

case KphDriverImageFileNameInformation:
    // 版本检查:仅在Win10 16299+支持安全调用
    if ((KphOsVersionInfo.dwMajorVersion < 10) ||
        (KphOsVersionInfo.dwMajorVersion == 10 && 
         KphOsVersionInfo.dwBuildNumber < 16299)) {
        status = STATUS_NOINTERFACE;
        break;
    }
    // 调用系统API获取完整路径
    status = IoQueryFullDriverPath(driverObject, &fullDriverPath);
    if (!NT_SUCCESS(status)) break;
    // 复制路径到用户缓冲区
    string->Length = 0;
    string->MaximumLength = (USHORT)(DriverInformationLength - sizeof(UNICODE_STRING));
    string->Buffer = (PWSTR)Add2Ptr(DriverInformation, sizeof(UNICODE_STRING));
    RtlCopyUnicodeString(string, &fullDriverPath);
    KphFreePool(fullDriverPath.Buffer); // 释放临时分配的内存
    break;

版本兼容性处理是内核开发的关键挑战。KSystemInformer通过KphOsVersionInfo全局变量维护系统版本信息,在编译时和运行时双重检查API可用性。

服务键名提取:驱动扩展信息获取

驱动的服务键名存储在DriverExtension成员中,这是一个可选成员,需要进行存在性检查:

case KphDriverServiceKeyNameInformation:
    if (!driverObject->DriverExtension) {
        // 驱动扩展不存在时返回空字符串
        RtlZeroMemory(DriverInformation, sizeof(UNICODE_STRING));
        returnLength = sizeof(UNICODE_STRING);
        status = STATUS_SUCCESS;
        break;
    }
    // 检查缓冲区大小
    if (DriverInformationLength < 
        (sizeof(UNICODE_STRING) + driverObject->DriverExtension->ServiceKeyName.Length)) {
        status = STATUS_BUFFER_TOO_SMALL;
        returnLength = sizeof(UNICODE_STRING) + driverObject->DriverExtension->ServiceKeyName.Length;
        break;
    }
    // 复制服务键名
    string = (PUNICODE_STRING)DriverInformation;
    string->Buffer = (PWSTR)Add2Ptr(DriverInformation, sizeof(UNICODE_STRING));
    RtlCopyUnicodeString(string, &driverObject->DriverExtension->ServiceKeyName);
    break;

异常处理与安全防护:内核稳定性保障

内核模式下的异常可能导致系统崩溃,因此KSystemInformer实现了多层次的异常防护机制。每个用户输入处理路径都包含结构化异常处理(SEH):

if (AccessMode != KernelMode) {
    __try {
        if (DriverInformation) {
            ProbeOutputBytes(DriverInformation, DriverInformationLength);
        }
        if (ReturnLength) {
            ProbeOutputType(ReturnLength, ULONG);
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        status = GetExceptionCode();
        goto Exit;
    }
}

安全最佳实践总结

  1. 输入验证:对所有用户态指针使用Probe函数验证
  2. 权限检查:严格控制DesiredAccess权限集合
  3. 异常隔离:使用__try/__except捕获内存访问异常
  4. 资源管理:确保所有内核对象引用正确释放
  5. 版本适配:针对不同Windows版本调整实现策略

实战案例:驱动信息采集完整流程

以下代码展示了用户态程序获取驱动详细信息的完整调用序列:

// 1. 初始化对象属性
UNICODE_STRING deviceName = RTL_CONSTANT_STRING(L"\\Device\\KSystemInformer");
OBJECT_ATTRIBUTES objAttr;
InitializeObjectAttributes(&objAttr, &deviceName, 
                          OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 
                          NULL, NULL);

// 2. 打开设备句柄
HANDLE hDevice;
NTSTATUS status = KphOpenDevice(&hDevice, GENERIC_READ, &objAttr, UserMode);
if (!NT_SUCCESS(status)) {
    printf("打开设备失败: 0x%X\n", status);
    return status;
}

// 3. 获取驱动句柄
HANDLE hDriver;
status = KphOpenDeviceDriver(hDevice, GENERIC_READ, &hDriver, UserMode);

// 4. 查询驱动映像路径
WCHAR buffer[1024];
ULONG returnLength;
status = KphQueryInformationDriver(hDriver, KphDriverImageFileNameInformation,
                                  buffer, sizeof(buffer), &returnLength, UserMode);

// 5. 输出结果
PUNICODE_STRING imagePath = (PUNICODE_STRING)buffer;
wprintf(L"驱动路径: %s\n", imagePath->Buffer);

// 6. 清理资源
ZwClose(hDriver);
ZwClose(hDevice);

跨版本兼容性:从Windows 7到Windows 11

内核驱动开发的一大挑战是处理不同Windows版本的API差异。KSystemInformer采用以下策略实现跨版本兼容:

Windows版本驱动路径获取方法兼容性处理
Win7/8.x不支持返回STATUS_NOINTERFACE
Win10 1507-16299部分支持限制仅查询自身驱动
Win10 1709+ / Win11完全支持直接调用IoQueryFullDriverPath

版本检测的核心实现:

// 全局版本信息结构体
RTL_OSVERSIONINFOW KphOsVersionInfo = {0};

// 初始化版本信息
KphOsVersionInfo.dwOSVersionInfoSize = sizeof(KphOsVersionInfo);
RtlGetVersion(&KphOsVersionInfo);

// 使用示例
if ((KphOsVersionInfo.dwMajorVersion < 10) ||
    (KphOsVersionInfo.dwMajorVersion == 10 && 
     KphOsVersionInfo.dwBuildNumber < 16299)) {
    // 不支持的版本路径
}

总结与展望:内核监控技术的演进

SystemInformer的驱动实现展示了现代Windows内核监控的核心技术范式:通过安全的内核-用户态通信精细化的对象访问控制全面的异常防护,构建了高效可靠的系统监控基础。随着硬件虚拟化技术的发展,未来内核监控可能会更多地结合VBS(Virtualization-based Security)和HVCI(Hypervisor-enforced Code Integrity)等机制,进一步提升监控的安全性和可靠性。

KSystemInformer的设计哲学对驱动开发者有以下启示:

  1. 最小权限原则:仅申请必要的内核权限
  2. 防御性编程:假设所有用户输入都是不可信的
  3. 资源严格管理:任何对象引用必须有对应的释放
  4. 全面异常处理:捕获所有可能的内核异常

通过本文介绍的技术框架和实现细节,开发者可以构建自己的内核监控工具,深入理解Windows系统内部运作机制,为系统调试、安全分析等场景提供强大的技术支持。

扩展学习资源

  1. Windows内核编程基础:《Windows Internals》(Mark Russinovich著)
  2. 驱动开发工具链:Windows Driver Kit (WDK) 10
  3. 内核调试环境:DebugView + VirtualKD
  4. 开源驱动示例:Windows Driver Samples (GitHub)
  5. 系统调用参考:NTAPI文档(Phnt项目)

掌握内核模式监控技术不仅能够提升系统调试能力,更能深入理解Windows安全模型,为构建更安全、更可靠的系统软件奠定基础。随着SystemInformer的持续迭代,其内核驱动架构也将不断演进,为用户提供更强大的系统监控能力。

【免费下载链接】systeminformer A free, powerful, multi-purpose tool that helps you monitor system resources, debug software and detect malware. Brought to you by Winsider Seminars & Solutions, Inc. @ http://www.windows-internals.com 【免费下载链接】systeminformer 项目地址: https://gitcode.com/GitHub_Trending/sy/systeminformer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值