Windows WFP 驱动程序:在 ClassifyFn 回调中处理数据包时出现 BSOD

Posted

技术标签:

【中文标题】Windows WFP 驱动程序:在 ClassifyFn 回调中处理数据包时出现 BSOD【英文标题】:Windows WFP Driver: Getting BSOD when processing packets in ClassifyFn callback 【发布时间】:2019-11-23 18:14:34 【问题描述】:

我正在尝试编写一个简单的防火墙应用程序,它可以允许或阻止来自用户级进程的网络连接尝试。

为此,按照WFPStarterKit 教程,我创建了一个 WFP 驱动程序,该驱动程序设置为在 FWPM_LAYER_OUTBOUND_TRANSPORT_V4 层拦截数据。

ClassifyFn 回调函数负责拦截连接尝试,并允许或拒绝它。

一旦 ClassifyFn 回调被命中,数据包的 ProcessID 以及一些其他信息就会通过FltSendMessage 函数发送到用户级进程。

用户级进程接收到消息,检查 ProcessID,并向驱动程序回复一个布尔允许/拒绝命令。

虽然这种方法在阻止第一个数据包时有效,但在某些情况下(特别是在允许多个数据包时),代码会生成带有 INVALID_PROCESS_ATTACH_ATTEMPT 错误代码的 BSOD。 该错误在调用 FltSendMessage 时触发。

虽然我仍然无法确定确切的问题, 似乎让调用线程等待(通过 FltSendMessage)来自用户级别的回复可能会在某些情况下生成此 BSOD 错误。

如果您能指出正确的方向,我将不胜感激。

这是我注册标注的函数:

    NTSTATUS register_example_callout(DEVICE_OBJECT * wdm_device)

    NTSTATUS status = STATUS_SUCCESS;
    FWPS_CALLOUT s_callout =  0 ;
    FWPM_CALLOUT m_callout =  0 ;
    FWPM_DISPLAY_DATA display_data =  0 ;

    if (filter_engine_handle == NULL)
        return STATUS_INVALID_HANDLE;

    display_data.name = EXAMPLE_CALLOUT_NAME;
    display_data.description = EXAMPLE_CALLOUT_DESCRIPTION;

    // Register a new Callout with the Filter Engine using the provided callout functions
    s_callout.calloutKey = EXAMPLE_CALLOUT_GUID;
    s_callout.classifyFn = example_classify;
    s_callout.notifyFn = example_notify;
    s_callout.flowDeleteFn = example_flow_delete;
    status = FwpsCalloutRegister((void *)wdm_device, &s_callout, &example_callout_id);
    if (!NT_SUCCESS(status)) 
        DbgPrint("Failed to register callout functions for example callout, status 0x%08x", status);
        goto Exit;
    

    // Setup a FWPM_CALLOUT structure to store/track the state associated with the FWPS_CALLOUT
    m_callout.calloutKey = EXAMPLE_CALLOUT_GUID;
    m_callout.displayData = display_data;
    m_callout.applicableLayer = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
    m_callout.flags = 0;
    status = FwpmCalloutAdd(filter_engine_handle, &m_callout, NULL, NULL);
    if (!NT_SUCCESS(status)) 
        DbgPrint("Failed to register example callout, status 0x%08x", status);
    
    else 
        DbgPrint("Example Callout Registered");
    

Exit:
    return status;

这里是标注函数:

    /*************************
ClassifyFn Function
**************************/
void example_classify(
    const FWPS_INCOMING_VALUES * inFixedValues,
    const FWPS_INCOMING_METADATA_VALUES * inMetaValues,
    void * layerData,
    const void * classifyContext,
    const FWPS_FILTER * filter,
    UINT64 flowContext,
    FWPS_CLASSIFY_OUT * classifyOut)

    UNREFERENCED_PARAMETER(layerData);
    UNREFERENCED_PARAMETER(classifyContext);
    UNREFERENCED_PARAMETER(flowContext);
    UNREFERENCED_PARAMETER(filter);
    UNREFERENCED_PARAMETER(inMetaValues);

    NETWORK_ACCESS_QUERY AccessQuery;
    BOOLEAN SafeToOpen = TRUE;
    classifyOut->actionType = FWP_ACTION_PERMIT;

    AccessQuery.remote_address = inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value.uint32;
    AccessQuery.remote_port = inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_PORT].value.uint16;

    // Get Process ID
    AccessQuery.ProcessId = (UINT64)PsGetCurrentProcessId();

    if (!AccessQuery.ProcessId) 
    
        return;
    

    // Here we connect to our userlevel application using FltSendMessage.
    // Some checks are done and the SafeToOpen variable is populated with a BOOLEAN which indicates if to allow or block the packet.
    // However, sometimes, a BSOD is generated with an INVALID_PROCESS_ATTACH_ATTEMPT error on the FltSendMessage call
    QueryUserLevel(QUERY_NETWORK, &AccessQuery, sizeof(NETWORK_ACCESS_QUERY), &SafeToOpen, NULL, 0);

    if (!SafeToOpen) 
        classifyOut->actionType = FWP_ACTION_BLOCK;
    

    return;

【问题讨论】:

【参考方案1】:

WFP 驱动程序使用inverted call model 与用户模式应用程序通信。在这种方法中,您可以在内核模式驱动程序实例中保持用户模式的 IRP 处于挂起状态,并且无论何时要将数据发送回用户模式,您都可以完成 IRP 以及要发送回的数据。

【讨论】:

【参考方案2】:

问题在于,有时可以在 IRQL DISPATCH_LEVEL 调用 ClassifyFn 回调函数。 FltSendMessage 不支持 DISPATCH_LEVEL,因为它只能在 IRQL

我通过从运行在 IRQL PASSIVE_LEVEL 的工作线程调用 FltSendMessage 解决了这个问题。

可以使用IoQueueWorkItem创建工作线程。

【讨论】:

以上是关于Windows WFP 驱动程序:在 ClassifyFn 回调中处理数据包时出现 BSOD的主要内容,如果未能解决你的问题,请参考以下文章

Windows XP SP3环境下如何关闭WFP(windows file protection)功能

NDIS LWF 驱动程序导致网络堆栈中的 WFP 驱动程序出现问题?

安装程序无法自动安装网络自检wfp驱动程序

WFP之WFP简介

Lync NotInitializedException 在 Windows 8 上无法捕获

使用 Dev C++ 开发 WFP