Windows WFP 驱动程序:在 ClassifyFn 回调中处理数据包时出现蓝屏
Windows WFP Driver: Getting BSOD when processing packets in ClassifyFn callback
我正在尝试编写一个简单的防火墙应用程序,它可以允许或阻止从用户级进程进行的网络连接尝试。
为此,按照 WFPStarterKit 教程,我创建了一个 WFP 驱动程序,该驱动程序设置为在 FWPM_LAYER_OUTBOUND_TRANSPORT_V4 层拦截数据。
ClassifyFn 回调函数负责拦截连接尝试,并允许或拒绝它。
一旦 ClassifyFn 回调被命中,数据包的 ProcessID 连同一些其他信息将通过 FltSendMessage 函数发送到用户级进程。
用户级进程接收消息,检查 ProcessID,并向驱动程序回复布尔 allow/deny 命令。
虽然这种方法在阻止第一个数据包时有效,但在某些情况下(特别是当允许多个数据包时)代码会生成带有 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;
}
WFP 驱动程序使用 inverted call model 与用户模式应用程序通信。在这种方法中,您在内核模式驱动程序实例中保持来自用户模式的 IRP 挂起,并且每当您想要将数据发送回用户模式时,您完成 IRP 以及您想要发送回的数据。
问题是有时可以在 IRQL DISPATCH_LEVEL 调用 ClassifyFn 回调函数。
FltSendMessage 不支持 DISPATCH_LEVEL,因为在 IRQL <= APC_LEVEL 时它只能是 运行。
运行 at DISPATCH_LEVEL 会导致此函数生成 BSOD。
我通过从 运行 处于 IRQL PASSIVE_LEVEL 的工作线程调用 FltSendMessage 解决了这个问题。
工作线程可以使用IoQueueWorkItem创建。
我正在尝试编写一个简单的防火墙应用程序,它可以允许或阻止从用户级进程进行的网络连接尝试。
为此,按照 WFPStarterKit 教程,我创建了一个 WFP 驱动程序,该驱动程序设置为在 FWPM_LAYER_OUTBOUND_TRANSPORT_V4 层拦截数据。
ClassifyFn 回调函数负责拦截连接尝试,并允许或拒绝它。
一旦 ClassifyFn 回调被命中,数据包的 ProcessID 连同一些其他信息将通过 FltSendMessage 函数发送到用户级进程。
用户级进程接收消息,检查 ProcessID,并向驱动程序回复布尔 allow/deny 命令。
虽然这种方法在阻止第一个数据包时有效,但在某些情况下(特别是当允许多个数据包时)代码会生成带有 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;
}
WFP 驱动程序使用 inverted call model 与用户模式应用程序通信。在这种方法中,您在内核模式驱动程序实例中保持来自用户模式的 IRP 挂起,并且每当您想要将数据发送回用户模式时,您完成 IRP 以及您想要发送回的数据。
问题是有时可以在 IRQL DISPATCH_LEVEL 调用 ClassifyFn 回调函数。 FltSendMessage 不支持 DISPATCH_LEVEL,因为在 IRQL <= APC_LEVEL 时它只能是 运行。 运行 at DISPATCH_LEVEL 会导致此函数生成 BSOD。
我通过从 运行 处于 IRQL PASSIVE_LEVEL 的工作线程调用 FltSendMessage 解决了这个问题。
工作线程可以使用IoQueueWorkItem创建。