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

首次开发内核驱动调用FwpsCalloutRegister触发IRQL_NOT_LESS_OR_EQUAL崩溃求助

排查DriverEntry中FwpsCalloutRegister导致的IRQL_NOT_LESS_OR_EQUAL崩溃

刚上手第一个内核驱动就碰到这种崩溃,太闹心了!我来帮你拆解一下这个问题,一步步找出原因。

首先,WinDbg给出的IRQL_NOT_LESS_OR_EQUAL (a)错误本质很明确:当前CPU运行的IRQL级别过高,却尝试访问了只能在更低IRQL下访问的内存(比如可分页内存),或者直接访问了无效地址。

针对你在DriverEntry里调用status = FwpsCalloutRegister(wdfDevice, &sCallout, &Globals.FlowCalloutId);崩溃的情况,重点排查这几个方向:

1. 检查sCallout结构体的初始化问题

sCallout里的回调函数(比如分类回调、通知回调)是崩溃的高发区:

  • 确保所有回调函数都被编译在非分页代码段里。如果你的回调函数用了__declspec(code_seg("PAGE"))标记,那它会被放在可分页内存中,一旦FwpsCalloutRegister内部或后续回调在高于PASSIVE_LEVEL的IRQL下调用它,必然崩溃。去掉这个标记即可(内核驱动默认代码段是非分页的,除非你手动修改了)。
  • 检查结构体里的字符串成员(比如displayName):如果是UNICODE_STRING,要确保它指向的缓冲区是非分页内存,不能用用户态内存或者堆分配的分页内存(比如用ExAllocatePoolWithTag时要指定NonPagedPoolNx类型)。

2. 确认Globals.FlowCalloutId的内存属性

FlowCalloutId是输出参数,FwpsCalloutRegister会往这个地址写数据:

  • 如果Globals是你定义的全局变量,默认是在非分页数据段里,但如果你给它加了__declspec(data_seg("PAGE"))标记,那它会被放到分页内存中。当调用FwpsCalloutRegister时,如果IRQL被提升(比如内部用到了DPC),写入分页内存就会触发崩溃。
  • 绝对不能把FlowCalloutId放在DriverEntry的栈上!内核栈在某些场景下会被分页,把栈变量地址传给需要在高IRQL下访问的函数,大概率会触发崩溃。必须把FlowCalloutId放在全局非分页内存,或者用ExAllocatePoolWithTag分配的非分页内存里。

3. 检查DriverEntry中的IRQL状态

虽然DriverEntry默认是在PASSIVE_LEVEL运行,但如果你在调用FwpsCalloutRegister之前手动提升了IRQL(比如调用了KeRaiseIrqlToDpcLevel),又没调用KeLowerIrql降回去,那此时访问任何分页内存都会触发崩溃。崩溃后可以用WinDbg的!irql命令确认当时的IRQL级别。

额外WinDbg调试技巧

  • 用!address <崩溃地址>命令查看崩溃时访问的地址属性,确认是不是分页内存。
  • 用!pool <地址>检查地址是否属于内核池,以及池的类型(分页/非分页)。
  • 执行k命令查看调用栈,确认FwpsCalloutRegister内部是在哪个环节触发的崩溃,定位到具体代码行。

举个正确的示例参考:

// 全局非分页变量存储CalloutId
FWPS_CALLOUT_ID g_FlowCalloutId;

// 回调函数确保是非分页(默认就是,无需额外标记)
VOID NTAPI MyClassifyFn(
    IN const FWPS_INCOMING_VALUES0* inFixedValues,
    IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
    IN OUT VOID* layerData,
    IN const FWPS_FILTER0* filter,
    IN UINT64 flowContext,
    OUT FWPS_CLASSIFY_OUT0* classifyOut
)
{
    // 回调逻辑实现
}

NTSTATUS DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
)
{
    NTSTATUS status;
    WDFDEVICE wdfDevice;
    FWPS_CALLOUT sCallout = {0};

    // 省略WDF设备初始化逻辑...

    // 初始化sCallout结构体
    sCallout.calloutKey = L"MySampleCallout";
    sCallout.classifyFn = MyClassifyFn;
    // 其他必要成员初始化...

    // 调用注册函数,传入全局非分页变量地址
    status = FwpsCalloutRegister(wdfDevice, &sCallout, &g_FlowCalloutId);
    if (!NT_SUCCESS(status)) {
        // 错误处理逻辑
    }

    return status;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:26:35