cwindowsdriverwindows-kernelwfp

Windows Filtering Platform: ClassifyFn BSOD at DISPATCH_LEVEL


I'm trying to implement a simple firewall which filters network connections made by Windows processes.

The firewall should either allow/block the connection.

In order to intercept connections by any process, I created a kernel driver which makes use of Windows Filtering Platform.

I registered a ClassifyFn (FWPS_CALLOUT_CLASSIFY_FN1) callback at the filtering layer FWPM_LAYER_ALE_AUTH_CONNECT_V4:

FWPM_CALLOUT m_callout = { 0 };
m_callout.applicableLayer = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
...
status = FwpmCalloutAdd(filter_engine_handle, &m_callout, NULL, NULL);

The decision regarding connection allow/block should be taken by userlevel.

I communicate with Userlevel using FltSendMessage, which cannot be used at IRQL DISPATCH_LEVEL.

Following the instructions of the Microsoft documentation regarding how to process callouts asynchronously, I do call FwpsPendOperation0 before calling FltSendMessage.

After the call to FltSendMessage, I resume packet processing by calling FwpsCompleteOperation0.

FwpsPendOperation0 documentation states that calling this function should make possible to operate calls at PASSIVE_LEVEL:

A callout can pend the current processing operation on a packet when the callout must perform processing on one of these layers that may take a long interval to complete or that should occur at IRQL = PASSIVE_LEVEL if the current IRQL > PASSIVE_LEVEL.

However, when the ClassifyFn callback is called at DISPATCH_LEVEL, I do sometimes still get a BSOD on FltSendMessage (INVALID_PROCESS_ATTACH_ATTEMPT).

I don't understand what's wrong.

Thank you in advance for any advice which could point me to the right direction.

Here is the relevant code of the ClassifyFn callback:

/*************************
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)
{

    NTSTATUS status;
    BOOLEAN bIsReauthorize = FALSE;
    BOOLEAN SafeToOpen = TRUE; // Value returned by userlevel which signals to allow/deny packet
    classifyOut->actionType = FWP_ACTION_PERMIT;

    remote_address = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
    remote_port = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;

    bIsReauthorize = IsAleReauthorize(inFixedValues);
    if (!bIsReauthorize)
    {
        // First time receiving packet (not a reauthorized packet)
        // Communicate with userlevel asynchronously

        HANDLE hCompletion;
        status = FwpsPendOperation0(inMetaValues->completionHandle, &hCompletion);

        //
        // FltSendMessage call here
        // ERROR HERE:
        // INVALID_PROCESS_ATTACH_ATTEMP BSOD on FltMessage call when at IRQL DISPATCH_LEVEL
        //

        FwpsCompleteOperation0(hCompletion, NULL);
    }

    if (!SafeToOpen) {
        // Packet blocked
        classifyOut->actionType = FWP_ACTION_BLOCK;
    }
    else {
        // Packet allowed
    }

    return;
}

Solution

  • You need to invoke FltSendMessage() on another thread running at PASSIVE_LEVEL. You can use IoQueueWorkItem() or implement your own mechanism to process it on a system worker thread created via PsCreateSystemThread().