Windows WFP Driver: Getting BSOD when processing packets in ClassifyFn callback

Hi,

I am trying to code a simple firewall application which can allow or block network connection attempts made from userlevel processes.
To do so, following the WFPStarterKit tutorial, I created a WFP Driver which is set to intercept data at FWPM_LAYER_OUTBOUND_TRANSPORT_V4 layer.
The ClassifyFn callback function is responsible for intercepting the connection attempt, and either allow or deny it.
Once the ClassifyFn callback gets hit, the ProcessID of the packet is sent, along with a few other info, to a userlevel process through the FltSendMessage function.
The userlevel process receives the message, checks the ProcessID, and replies a boolean allow/deny command to the driver.

While this approach works when blocking a first packet,
in some cases (expecially when allowing multiple packets) the code generates a BSOD with the INVALID_PROCESS_ATTACH_ATTEMPT error code.
The error is triggered at the call to FltSendMessage.

While I am still unable to pinpoint the exact problem, it seems that making the callout thread wait (through FltSendMessage) for a reply from userlevel can generate this BSOD error on some conditions.

I would be very grateful if you can point me to the right direction.

Here is the function where I register the callout:

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;
}

Here is the callout function:

 /*************************
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;
}

This callback can be called at IRQL DISPATCH_LEVEL, which would make it illegal to use a communication port. Enable Driver Verifier and always post the !analyze -v output if you have a crash.

@“Scott_Noone_(OSR)” said:
This callback can be called at IRQL DISPATCH_LEVEL, which would make it illegal to use a communication port. Enable Driver Verifier and always post the !analyze -v output if you have a crash.

Hello Scott,
thank you for your reply.
Since I cannot use a communication port through FltSendMessage inside this function,
how would you suggest I should communicate with userlevel?
FltSendMessage was the easiest option since it waits for response by the userlevel process.

Here is the !analyze -v log:


  •                                                                         *
    
  •                    Bugcheck Analysis                                    *
    
  •                                                                         *
    

INVALID_PROCESS_ATTACH_ATTEMPT (5)
Arguments:
Arg1: ffffa98d439b2080
Arg2: ffffa98d402581c0
Arg3: 0000000000000001
Arg4: 0000000000000001

Debugging Details:

DUMP_CLASS: 1

DUMP_QUALIFIER: 0

BUILD_VERSION_STRING: 15046.0.amd64fre.rs2_release.170224-1700

DUMP_TYPE: 0

BUGCHECK_P1: ffffa98d439b2080

BUGCHECK_P2: ffffa98d402581c0

BUGCHECK_P3: 1

BUGCHECK_P4: 1

CPU_COUNT: 4

CPU_MHZ: fa8

CPU_VENDOR: GenuineIntel

CPU_FAMILY: 6

CPU_MODEL: 5e

CPU_STEPPING: 3

CPU_MICROCODE: 6,5e,3,0 (F,M,S,R) SIG: BA’00000000 (cache) BA’00000000 (init)

DEFAULT_BUCKET_ID: WIN8_DRIVER_FAULT

BUGCHECK_STR: 0x5

PROCESS_NAME: System

CURRENT_IRQL: 2

ANALYSIS_SESSION_HOST: DESKTOP-NQ55126

ANALYSIS_SESSION_TIME: 02-29-2020 17:41:58.0187

ANALYSIS_VERSION: 10.0.16299.15 amd64fre

LAST_CONTROL_TRANSFER: from fffff802489f6812 to fffff80248972cc0

Use the ALE layers for this. https://docs.microsoft.com/en-us/windows/win32/fwp/application-layer-enforcement--ale-

1 Like

@Jason_Stephenson said:
Use the ALE layers for this. https://docs.microsoft.com/en-us/windows/win32/fwp/application-layer-enforcement--ale-

I already switched to FWPM_LAYER_ALE_AUTH_CONNECT_V4 since a while now:

    m_callout.applicableLayer = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
    //m_callout.applicableLayer = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;

Which is definitely better for my purpose.
Although the issue occurrs more rarely now, sometimes there is still the same BSOD error INVALID_PROCESS_ATTACH_ATTEMPT when calling FltSendMessage. :frowning:
On StackOverflow I was suggested to use the inverted call model instead of FltSendMessage.
This would require some protocol rewriting, but I can go for it, or do you have any other better approach in mind? =)

Thank you

> @Viotto said: > (Quote) > I already switched to FWPM_LAYER_ALE_AUTH_CONNECT_V4 since a while now: > > m_callout.applicableLayer = FWPM_LAYER_ALE_AUTH_CONNECT_V4; //m_callout.applicableLayer = FWPM_LAYER_OUTBOUND_TRANSPORT_V4; > > Which is definitely better for my purpose. > Although the issue occurrs more rarely now, sometimes there is still the same BSOD error INVALID_PROCESS_ATTACH_ATTEMPT when calling FltSendMessage. :frowning: > On StackOverflow I was suggested to use the inverted call model instead of FltSendMessage. > This would require some protocol rewriting, but I can go for it, or do you have any other better approach in mind? =) > > Thank you You expecting current implementation somehow magically start working ?

@Sergey_Pisarev said:

@Viotto said:
(Quote)
I already switched to FWPM_LAYER_ALE_AUTH_CONNECT_V4 since a while now:

m_callout.applicableLayer = FWPM_LAYER_ALE_AUTH_CONNECT_V4;    //m_callout.applicableLayer = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;

Which is definitely better for my purpose.
Although the issue occurrs more rarely now, sometimes there is still the same BSOD error INVALID_PROCESS_ATTACH_ATTEMPT when calling FltSendMessage. :frowning:
On StackOverflow I was suggested to use the inverted call model instead of FltSendMessage.
This would require some protocol rewriting, but I can go for it, or do you have any other better approach in mind? =)

Thank you

You expecting current implementation somehow magically start working ?

If you did read the thread, you would have understood that the implementation usually works, except on some random cases which throw the INVALID_PROCESS_ATTACH_ATTEMPT error.
I opened the thread to get a direction regarding the reason and solution for this error.
Do you have something actually useful to add? =)

@Viotto said:

@Sergey_Pisarev said:

@Viotto said:
(Quote)
I already switched to FWPM_LAYER_ALE_AUTH_CONNECT_V4 since a while now:

m_callout.applicableLayer = FWPM_LAYER_ALE_AUTH_CONNECT_V4;    //m_callout.applicableLayer = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;

Which is definitely better for my purpose.
Although the issue occurrs more rarely now, sometimes there is still the same BSOD error INVALID_PROCESS_ATTACH_ATTEMPT when calling FltSendMessage. :frowning:
On StackOverflow I was suggested to use the inverted call model instead of FltSendMessage.
This would require some protocol rewriting, but I can go for it, or do you have any other better approach in mind? =)

Thank you

You expecting current implementation somehow magically start working ?

If you did read the thread, you would have understood that the implementation usually works, except on some random cases which throw the INVALID_PROCESS_ATTACH_ATTEMPT error.
I opened the thread to get a direction regarding the reason and solution for this error.
Do you have something actually useful to add? =)

No. Bsod = doesn’t work. You solution doesn’t work. You should understand that you can’t use fltsendmessage at dispatch. This is said in function documentation. You implementation is flawed on conceptual level, it can’t be fixed. You need to change your model.
And remember: work most of the time = doesn’t work

I believe at this point that the best thing to do is remove FltSendMessage and use inverted call model for communication instead.
Each time I get an event in the ClassifyFn callback, I will communicate this to userlevel by completing one of the pending IOCTL…
Unless somebody has got some direction for me, that’s the way I’ll go