You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows内核驱动卸载触发0x000000ce系统崩溃问题求助

驱动卸载时系统崩溃排查求助

我参考《Windows内核编程》编写了ProcessThreadMonitorDriver.sys驱动,加载后功能正常,但卸载时触发系统崩溃。WinDbg调试得到的错误信息如下:

*** Fatal System Error: 0x000000ce
(0xFFFFF80260A71390,0x0000000000000010,0xFFFFF80260A71390,0x0000000000000000)

Driver at fault: ProcessThreadMonitorDriver.sys.
Break instruction exception - code 80000003 (first chance)
nt_exe!DbgBreakPointWithStatus:

调试中偶尔提示无效内存访问,但未明确根因。以下是驱动核心代码片段,请求协助排查卸载崩溃问题:

DriverEntry函数

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    UNREFERENCED_PARAMETER(RegistryPath);
    auto status = STATUS_SUCCESS;
    InitializeListHead(&g_Globals.ItemsHead);
    g_Globals.Mutex.Init();
    PDEVICE_OBJECT DeviceObject = nullptr;
    UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\sysmon");
    bool symLinkCreated = false;
    do {
        UNICODE_STRING devName = RTL_CONSTANT_STRING(L"\\Device\\sysmon");
        status = IoCreateDevice(DriverObject, 0, &devName,
            FILE_DEVICE_UNKNOWN, 0, TRUE, &DeviceObject);
        if (!NT_SUCCESS(status)) {
            KdPrint((DRIVER_PREFIX "failed to create device (0x%08X)\n",
                status));
            break;
        }
        DeviceObject->Flags |= DO_DIRECT_IO;
        status = IoCreateSymbolicLink(&symLink, &devName);
        if (!NT_SUCCESS(status)) {
            KdPrint((DRIVER_PREFIX "failed to create sym link (0x%08X)\n",
                status));
            break;
        }
        symLinkCreated = true;
        // register for process notifications
        status = PsSetCreateProcessNotifyRoutineEx(OnProcessNotify, FALSE);
        if (!NT_SUCCESS(status)) {
            KdPrint((DRIVER_PREFIX "failed to register process callback (0x%08X)\n",
                status));
            break;
        }
        status = PsSetCreateThreadNotifyRoutine(OnThreadNotify);
        if (!NT_SUCCESS(status)) {
            KdPrint((DRIVER_PREFIX "failed to set thread callbacks (status=%08X)\n", status)\
            );
            break;
        }

    } while (false);
    if (!NT_SUCCESS(status)) {
        if (symLinkCreated)
            IoDeleteSymbolicLink(&symLink);
        if (DeviceObject)
            IoDeleteDevice(DeviceObject);
    }
    DriverObject->DriverUnload = SysMonUnload;
    DriverObject->MajorFunction[IRP_MJ_CREATE] =
        DriverObject->MajorFunction[IRP_MJ_CLOSE] = SysMonCreateClose;
    DriverObject->MajorFunction[IRP_MJ_READ] = SysMonRead;
    return status;
}

SysMonCreateClose函数

NTSTATUS SysMonCreateClose(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) { 
    UNREFERENCED_PARAMETER(DeviceObject);
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

SysMonUnload函数

void SysMonUnload(PDRIVER_OBJECT DriverObject) {
    // unregister process notifications
    PsSetCreateProcessNotifyRoutineEx(OnProcessNotify, TRUE);
    UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\sysmon");
    IoDeleteSymbolicLink(&symLink);
    IoDeleteDevice(DriverObject->DeviceObject);
    // free remaining items
    while (!IsListEmpty(&g_Globals.ItemsHead)) {
        auto entry = RemoveHeadList(&g_Globals.ItemsHead);
        ExFreePool(CONTAINING_RECORD(entry, FullItem<ItemHeader>, Entry));
    }
}

SysMonRead函数

NTSTATUS SysMonRead(PDEVICE_OBJECT, PIRP Irp) {
    UNREFERENCED_PARAMETER(Irp);
    auto stack = IoGetCurrentIrpStackLocation(Irp);
    auto len = stack->Parameters.Read.Length;
    auto status = STATUS_SUCCESS;
    auto count = 0;
    NT_ASSERT(Irp->MdlAddress); // we're using Direct I/O
    auto buffer = (UCHAR*)MmGetSystemAddressForMdlSafe(Irp->MdlAddress,
        NormalPagePriority);
    if (!buffer) {
        status = STATUS_INSUFFICIENT_RESOURCES;
    }
    else {
        AutoLock lock(g_Globals.Mutex);
        while (true) {
            if (IsListEmpty(&g_Globals.ItemsHead)) // can also check g_Globals.ItemCount
                break;
            auto entry = RemoveHeadList(&g_Globals.ItemsHead);
            auto info = CONTAINING_RECORD(entry, FullItem<ItemHeader>, Entry);
            auto size = info->Data.Size;
            if (len < size) {
                // user's buffer is full, insert item back
                InsertHeadList(&g_Globals.ItemsHead, entry);
                break;
            }
            g_Globals.ItemCount--;
            ::memcpy(buffer, &info->Data, size);
            len -= size;
            buffer += size;
            count += size;
            // free data after copy
            ExFreePool(info);
        }
    }
    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = count;
    IoCompleteRequest(Irp, 0);
    return status;
}

崩溃根因分析与修复方案

1. 遗漏线程回调注销

SysMonUnload中仅注销了进程回调,未调用PsRemoveCreateThreadNotifyRoutine(OnThreadNotify)注销线程回调。驱动卸载后若有线程事件触发,系统会尝试调用已释放的OnThreadNotify函数,直接导致0xCE类型的无效内存访问崩溃。

2. 资源释放顺序错误

当前卸载流程先注销进程回调,再删除设备和符号链接,最后释放列表项。正确顺序应优先注销所有回调,避免回调在后续资源释放过程中触发;再持有锁释放全局列表资源;最后清理设备和符号链接。

3. 全局列表访问未加锁

SysMonUnload遍历g_Globals.ItemsHead时未持有互斥锁,而SysMonRead是加锁访问的。卸载时若有并发的Read操作,会引发列表结构损坏或内存重复释放问题。

4. 注销回调未检查返回状态

调用PsSetCreateProcessNotifyRoutineEx和PsRemoveCreateThreadNotifyRoutine时未验证返回值,若注销失败(如回调未注册),可能引发后续逻辑异常。

修复后的SysMonUnload函数示例

void SysMonUnload(PDRIVER_OBJECT DriverObject) {
    // 先注销所有回调,避免后续资源释放时触发回调
    NTSTATUS status;
    status = PsSetCreateProcessNotifyRoutineEx(OnProcessNotify, TRUE);
    if (!NT_SUCCESS(status)) {
        KdPrint((DRIVER_PREFIX "failed to unregister process callback (0x%08X)\n", status));
    }
    status = PsRemoveCreateThreadNotifyRoutine(OnThreadNotify);
    if (!NT_SUCCESS(status)) {
        KdPrint((DRIVER_PREFIX "failed to unregister thread callback (0x%08X)\n", status));
    }

    // 加锁安全释放全局列表资源
    AutoLock lock(g_Globals.Mutex);
    while (!IsListEmpty(&g_Globals.ItemsHead)) {
        auto entry = RemoveHeadList(&g_Globals.ItemsHead);
        auto info = CONTAINING_RECORD(entry, FullItem<ItemHeader>, Entry);
        ExFreePool(info);
        g_Globals.ItemCount--; // 同步更新计数
    }

    // 清理设备和符号链接
    UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\sysmon");
    IoDeleteSymbolicLink(&symLink);
    IoDeleteDevice(DriverObject->DeviceObject);
}

内容的提问来源于stack exchange,提问作者Omer Cohen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 00:30:40