|
|
|
@@ -1,6 +1,6 @@
|
|
|
|
|
/*
|
|
|
|
|
* windivert.c
|
|
|
|
|
* (C) 2019, all rights reserved,
|
|
|
|
|
* (C) 2022, all rights reserved,
|
|
|
|
|
*
|
|
|
|
|
* This file is part of WinDivert.
|
|
|
|
|
*
|
|
|
|
@@ -123,7 +123,6 @@ typedef enum
|
|
|
|
|
WINDIVERT_CONTEXT_STATE_OPEN = 0xB1, // Context is open.
|
|
|
|
|
WINDIVERT_CONTEXT_STATE_CLOSING = 0xC2, // Context is closing.
|
|
|
|
|
WINDIVERT_CONTEXT_STATE_CLOSED = 0xD3, // Context is closed.
|
|
|
|
|
WINDIVERT_CONTEXT_STATE_INVALID = 0xE4 // Context is invalid.
|
|
|
|
|
} context_state_t;
|
|
|
|
|
struct context_s
|
|
|
|
|
{
|
|
|
|
@@ -274,8 +273,6 @@ struct flow_s
|
|
|
|
|
UINT64 flow_id; // WFP flow ID.
|
|
|
|
|
UINT32 callout_id; // WFP callout ID.
|
|
|
|
|
UINT16 layer_id; // WFP layout ID.
|
|
|
|
|
BOOL inserted:1; // Flow inserted into context?
|
|
|
|
|
BOOL deleted:1; // Flow deleted from context?
|
|
|
|
|
BOOL outbound:1; // Flow is outound?
|
|
|
|
|
BOOL loopback:1; // Flow is loopback?
|
|
|
|
|
BOOL ipv6:1; // Flow is ipv6?
|
|
|
|
@@ -333,7 +330,7 @@ extern VOID windivert_worker(IN WDFWORKITEM item);
|
|
|
|
|
static void windivert_read_service(context_t context);
|
|
|
|
|
extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request,
|
|
|
|
|
IN WDFFILEOBJECT object);
|
|
|
|
|
static NTSTATUS windivert_install_provider();
|
|
|
|
|
static NTSTATUS windivert_install_provider(void);
|
|
|
|
|
static NTSTATUS windivert_install_sublayer(layer_t layer);
|
|
|
|
|
static NTSTATUS windivert_install_callouts(context_t context, UINT8 layer,
|
|
|
|
|
UINT64 flags);
|
|
|
|
@@ -1166,12 +1163,14 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("failed to begin WFP transaction", status);
|
|
|
|
|
FwpmTransactionAbort0(engine_handle);
|
|
|
|
|
goto driver_entry_exit;
|
|
|
|
|
}
|
|
|
|
|
status = windivert_install_provider();
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("failed to install provider", status);
|
|
|
|
|
FwpmTransactionAbort0(engine_handle);
|
|
|
|
|
goto driver_entry_exit;
|
|
|
|
|
}
|
|
|
|
|
status = windivert_install_sublayer(WINDIVERT_LAYER_INBOUND_NETWORK_IPV4);
|
|
|
|
@@ -1283,6 +1282,7 @@ driver_entry_sublayer_error:
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("failed to commit WFP transaction", status);
|
|
|
|
|
FwpmTransactionAbort0(engine_handle);
|
|
|
|
|
goto driver_entry_exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1359,6 +1359,7 @@ static void windivert_driver_unload(void)
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("failed to begin WFP transaction", status);
|
|
|
|
|
FwpmTransactionAbort0(engine_handle);
|
|
|
|
|
FwpmEngineClose0(engine_handle);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@@ -1409,6 +1410,7 @@ static void windivert_driver_unload(void)
|
|
|
|
|
status = FwpmTransactionCommit0(engine_handle);
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
FwpmTransactionAbort0(engine_handle);
|
|
|
|
|
DEBUG_ERROR("failed to commit WFP transaction", status);
|
|
|
|
|
}
|
|
|
|
|
FwpmEngineClose0(engine_handle);
|
|
|
|
@@ -1568,7 +1570,7 @@ windivert_create_exit:
|
|
|
|
|
// Clean-up on error:
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
context->state = WINDIVERT_CONTEXT_STATE_INVALID;
|
|
|
|
|
context->state = WINDIVERT_CONTEXT_STATE_CLOSED;
|
|
|
|
|
if (context->read_queue != NULL)
|
|
|
|
|
{
|
|
|
|
|
WdfObjectDelete(context->read_queue);
|
|
|
|
@@ -1577,14 +1579,7 @@ windivert_create_exit:
|
|
|
|
|
{
|
|
|
|
|
WdfObjectDelete(context->worker);
|
|
|
|
|
}
|
|
|
|
|
if (context->process != NULL)
|
|
|
|
|
{
|
|
|
|
|
ObDereferenceObject(context->process);
|
|
|
|
|
}
|
|
|
|
|
if (context->engine_handle != NULL)
|
|
|
|
|
{
|
|
|
|
|
FwpmEngineClose0(context->engine_handle);
|
|
|
|
|
}
|
|
|
|
|
// process/engine_handle handled by windivert_destroy()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WdfRequestComplete(request, status);
|
|
|
|
@@ -1603,15 +1598,15 @@ static NTSTATUS windivert_install_callouts(context_t context, UINT8 layer,
|
|
|
|
|
accept, close;
|
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
inbound = ((flags & WINDIVERT_FILTER_FLAG_INBOUND) != 0);
|
|
|
|
|
outbound = ((flags & WINDIVERT_FILTER_FLAG_OUTBOUND) != 0);
|
|
|
|
|
ipv4 = ((flags & WINDIVERT_FILTER_FLAG_IP) != 0);
|
|
|
|
|
ipv6 = ((flags & WINDIVERT_FILTER_FLAG_IPV6) != 0);
|
|
|
|
|
bind = ((flags & WINDIVERT_FILTER_FLAG_EVENT_SOCKET_BIND) != 0);
|
|
|
|
|
connect = ((flags & WINDIVERT_FILTER_FLAG_EVENT_SOCKET_CONNECT) != 0);
|
|
|
|
|
listen = ((flags & WINDIVERT_FILTER_FLAG_EVENT_SOCKET_LISTEN) != 0);
|
|
|
|
|
accept = ((flags & WINDIVERT_FILTER_FLAG_EVENT_SOCKET_ACCEPT) != 0);
|
|
|
|
|
close = ((flags & WINDIVERT_FILTER_FLAG_EVENT_SOCKET_CLOSE) != 0);
|
|
|
|
|
inbound = ((flags & WINDIVERT_FILTER_FLAG_INBOUND) != 0);
|
|
|
|
|
outbound = ((flags & WINDIVERT_FILTER_FLAG_OUTBOUND) != 0);
|
|
|
|
|
ipv4 = ((flags & WINDIVERT_FILTER_FLAG_IP) != 0);
|
|
|
|
|
ipv6 = ((flags & WINDIVERT_FILTER_FLAG_IPV6) != 0);
|
|
|
|
|
bind = ((flags & WINDIVERT_FILTER_FLAG_EVENT_SOCKET_BIND) != 0);
|
|
|
|
|
connect = ((flags & WINDIVERT_FILTER_FLAG_EVENT_SOCKET_CONNECT) != 0);
|
|
|
|
|
listen = ((flags & WINDIVERT_FILTER_FLAG_EVENT_SOCKET_LISTEN) != 0);
|
|
|
|
|
accept = ((flags & WINDIVERT_FILTER_FLAG_EVENT_SOCKET_ACCEPT) != 0);
|
|
|
|
|
close = ((flags & WINDIVERT_FILTER_FLAG_EVENT_SOCKET_CLOSE) != 0);
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
switch (layer)
|
|
|
|
@@ -1803,8 +1798,7 @@ static NTSTATUS windivert_install_callout(context_t context, UINT idx,
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("failed to begin WFP transaction", status);
|
|
|
|
|
FwpsCalloutUnregisterByKey0(&callout_guid);
|
|
|
|
|
return status;
|
|
|
|
|
goto windivert_install_callout_error;
|
|
|
|
|
}
|
|
|
|
|
status = FwpmCalloutAdd0(engine, &mcallout, NULL, NULL);
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
@@ -1822,8 +1816,7 @@ static NTSTATUS windivert_install_callout(context_t context, UINT idx,
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("failed to commit WFP transaction", status);
|
|
|
|
|
FwpsCalloutUnregisterByKey0(&callout_guid);
|
|
|
|
|
return status;
|
|
|
|
|
goto windivert_install_callout_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
|
|
|
|
@@ -1878,6 +1871,7 @@ windivert_uninstall_callouts_error:
|
|
|
|
|
// RPC handle was closed first. So, this path is "normal" if
|
|
|
|
|
// the user's app crashed or never closed the WinDivert handle.
|
|
|
|
|
DEBUG_ERROR("failed to begin WFP transaction", status);
|
|
|
|
|
FwpmTransactionAbort0(engine);
|
|
|
|
|
goto windivert_uninstall_callouts_unregister;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++)
|
|
|
|
@@ -1922,6 +1916,7 @@ windivert_uninstall_callouts_error:
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("failed to commit WFP transaction", status);
|
|
|
|
|
FwpmTransactionAbort0(engine);
|
|
|
|
|
// continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1984,17 +1979,20 @@ windivert_cleanup_error:
|
|
|
|
|
context->state = WINDIVERT_CONTEXT_STATE_CLOSING;
|
|
|
|
|
sniff_mode = ((context->flags & WINDIVERT_FLAG_SNIFF) != 0);
|
|
|
|
|
forward = (context->layer == WINDIVERT_LAYER_NETWORK_FORWARD);
|
|
|
|
|
while (!IsListEmpty(&context->flow_set))
|
|
|
|
|
entry = context->flow_set.Flink;
|
|
|
|
|
if (entry != &context->flow_set)
|
|
|
|
|
{
|
|
|
|
|
entry = RemoveHeadList(&context->flow_set);
|
|
|
|
|
flow = CONTAINING_RECORD(entry, struct flow_s, entry);
|
|
|
|
|
flow->deleted = TRUE;
|
|
|
|
|
KeReleaseInStackQueuedSpinLock(&lock_handle);
|
|
|
|
|
status = FwpsFlowRemoveContext0(flow->flow_id, flow->layer_id,
|
|
|
|
|
flow->callout_id);
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
for (; entry != &context->flow_set; entry = entry->Flink)
|
|
|
|
|
{
|
|
|
|
|
windivert_free(flow);
|
|
|
|
|
flow = CONTAINING_RECORD(entry, struct flow_s, entry);
|
|
|
|
|
status = FwpsFlowRemoveContext0(flow->flow_id, flow->layer_id,
|
|
|
|
|
flow->callout_id);
|
|
|
|
|
if (!NT_SUCCESS(status) && status != STATUS_UNSUCCESSFUL)
|
|
|
|
|
{
|
|
|
|
|
// For STATUS_UNSUCCESSFUL, flow_delete() is still called.
|
|
|
|
|
WdfObjectDereference((WDFOBJECT)object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
|
|
|
|
|
}
|
|
|
|
@@ -2089,6 +2087,8 @@ extern VOID windivert_destroy(IN WDFOBJECT object)
|
|
|
|
|
KLOCK_QUEUE_HANDLE lock_handle;
|
|
|
|
|
context_t context = windivert_context_get((WDFFILEOBJECT)object);
|
|
|
|
|
const WINDIVERT_FILTER *filter;
|
|
|
|
|
PLIST_ENTRY entry;
|
|
|
|
|
flow_t flow;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
DEBUG("DESTROY: destroying WinDivert context (context=%p)", context);
|
|
|
|
@@ -2104,9 +2104,21 @@ extern VOID windivert_destroy(IN WDFOBJECT object)
|
|
|
|
|
filter = context->filter;
|
|
|
|
|
KeReleaseInStackQueuedSpinLock(&lock_handle);
|
|
|
|
|
windivert_uninstall_callouts(context, WINDIVERT_CONTEXT_STATE_CLOSED);
|
|
|
|
|
FwpmEngineClose0(context->engine_handle);
|
|
|
|
|
if (context->engine_handle != NULL)
|
|
|
|
|
{
|
|
|
|
|
FwpmEngineClose0(context->engine_handle);
|
|
|
|
|
}
|
|
|
|
|
windivert_free((PVOID)filter);
|
|
|
|
|
ObDereferenceObject(context->process);
|
|
|
|
|
while (!IsListEmpty(&context->flow_set))
|
|
|
|
|
{
|
|
|
|
|
entry = RemoveHeadList(&context->flow_set);
|
|
|
|
|
flow = CONTAINING_RECORD(entry, struct flow_s, entry);
|
|
|
|
|
windivert_free(flow);
|
|
|
|
|
}
|
|
|
|
|
if (context->process != NULL)
|
|
|
|
|
{
|
|
|
|
|
ObDereferenceObject(context->process);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -4176,22 +4188,11 @@ static void windivert_flow_established_classify(context_t context,
|
|
|
|
|
flow->flow_id = flow_id;
|
|
|
|
|
flow->callout_id = callout_id;
|
|
|
|
|
flow->layer_id = layer_id;
|
|
|
|
|
flow->inserted = FALSE;
|
|
|
|
|
flow->deleted = FALSE;
|
|
|
|
|
flow->outbound = outbound;
|
|
|
|
|
flow->loopback = loopback;
|
|
|
|
|
flow->ipv6 = !ipv4;
|
|
|
|
|
RtlCopyMemory(&flow->data, flow_data, sizeof(flow->data));
|
|
|
|
|
|
|
|
|
|
status = FwpsFlowAssociateContext0(flow_id, layer_id, callout_id,
|
|
|
|
|
(UINT64)flow);
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
windivert_free(flow);
|
|
|
|
|
WdfObjectDereference(object);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
|
|
|
|
|
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN ||
|
|
|
|
|
context->shutdown_recv)
|
|
|
|
@@ -4201,19 +4202,16 @@ static void windivert_flow_established_classify(context_t context,
|
|
|
|
|
WdfObjectDereference(object);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!flow->deleted)
|
|
|
|
|
status = FwpsFlowAssociateContext0(flow_id, layer_id, callout_id,
|
|
|
|
|
(UINT64)flow);
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
InsertTailList(&context->flow_set, &flow->entry);
|
|
|
|
|
flow->inserted = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Flow was deleted before insertion; we are responsible for cleanup.
|
|
|
|
|
KeReleaseInStackQueuedSpinLock(&lock_handle);
|
|
|
|
|
windivert_free(flow);
|
|
|
|
|
WdfObjectDereference(object);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
InsertTailList(&context->flow_set, &flow->entry);
|
|
|
|
|
KeReleaseInStackQueuedSpinLock(&lock_handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -4240,18 +4238,16 @@ static void windivert_flow_delete_notify(UINT16 layer_id, UINT32 callout_id,
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
timestamp = KeQueryPerformanceCounter(NULL).QuadPart;
|
|
|
|
|
context = flow->context;
|
|
|
|
|
|
|
|
|
|
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
|
|
|
|
|
object = (WDFOBJECT)context->object; // referenced in flow_established.
|
|
|
|
|
if (flow->inserted && !flow->deleted)
|
|
|
|
|
cleanup = (context->state == WINDIVERT_CONTEXT_STATE_OPEN);
|
|
|
|
|
if (cleanup)
|
|
|
|
|
{
|
|
|
|
|
RemoveEntryList(&flow->entry);
|
|
|
|
|
}
|
|
|
|
|
flow->deleted = TRUE;
|
|
|
|
|
cleanup = flow->inserted;
|
|
|
|
|
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN ||
|
|
|
|
|
context->shutdown_recv)
|
|
|
|
|
{
|
|
|
|
@@ -4276,12 +4272,12 @@ static void windivert_flow_delete_notify(UINT16 layer_id, UINT32 callout_id,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
windivert_flow_delete_notify_exit:
|
|
|
|
|
|
|
|
|
|
if (cleanup)
|
|
|
|
|
{
|
|
|
|
|
// If context->state != OPEN, then destroy() will free the flow.
|
|
|
|
|
windivert_free(flow);
|
|
|
|
|
WdfObjectDereference(object);
|
|
|
|
|
}
|
|
|
|
|
WdfObjectDereference(object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|