Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results
The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.
Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/
I'm wondering how to calculate bytes sent and bytes received through TCP connections in kernel mode. What I came up with was creating a callout in FWPM_LAYER_ALE_ESTABLISHED_FLOW_V4 to intercept every new TCP connection that is occuring right now and then getting in ClassifyFn to LayerData, then lower to streamData which has a member called dataLength (size_t which is 8 bytes). But I don't understand how to differentiate between bytes sent and bytes received? There is only ONE member called dataLength for the perticular connection. Do you have any suggestions?
Right now my code looks like this:
static NTSTATUS InitializeCallout(PDEVICE_OBJECT deviceObject)
{
FWPM_SUBLAYER subLayer = {};
subLayer.displayData.name = const_cast<wchar_t*>(L"TcpInterception Sub-Layer");
subLayer.subLayerKey = TCP_INTERCEPTION_SUBLAYER;
NTSTATUS status = FwpmSubLayerAdd(g_engineHandle, &subLayer, nullptr); if (!NT_SUCCESS(status)) { return status; } FWPS_CALLOUT0 sCallout = { TCP_INTERCEPTION_TRANSPORT_V4_CALLOUT, 0, CalloutConnectClassifyFn, CalloutNotifyFn, nullptr }; status = FwpsCalloutRegister0(deviceObject, &sCallout, &g_id); if (!NT_SUCCESS(status)) { return status; } FWPM_CALLOUT mCallout = {}; mCallout.calloutKey = TCP_INTERCEPTION_TRANSPORT_V4_CALLOUT; mCallout.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4; mCallout.displayData.name = const_cast<wchar_t*>(g_displayName); status = FwpmCalloutAdd(g_engineHandle, &mCallout, nullptr, nullptr); if (!NT_SUCCESS(status)) { return status; } FWPM_FILTER_CONDITION filterCondition = {}; filterCondition.fieldKey = FWPM_CONDITION_IP_PROTOCOL; filterCondition.matchType = FWP_MATCH_EQUAL; filterCondition.conditionValue.type = FWP_UINT8; filterCondition.conditionValue.uint16 = IPPROTO_TCP; FWPM_FILTER filter = {}; filter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4; filter.displayData.name = const_cast<wchar_t*>(g_displayName);; filter.displayData.description = filter.displayData.name; filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; filter.action.calloutKey = TCP_INTERCEPTION_TRANSPORT_V4_CALLOUT; filter.filterCondition = &filterCondition; filter.numFilterConditions = 1; filter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL; filter.weight.type = FWP_EMPTY; status = FwpmFilterAdd(g_engineHandle, &filter, NULL, NULL); if (!NT_SUCCESS(status)) { return status; } return status;
}
static void CalloutConnectClassifyFn(
const FWPS_INCOMING_VALUES0* inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
void* layerData,
const FWPS_FILTER0* filter,
UINT64 /flowContext/,
FWPS_CLASSIFY_OUT0* classifyOut)
{
// Allowing the traffic for another filter to make a final decision.
if (FlagOn(classifyOut->rights, FWPS_RIGHT_ACTION_WRITE))
{
classifyOut->actionType = FWP_ACTION_CONTINUE;
}
ProcessTransportData(inFixedValues, inMetaValues, layerData, classifyOut); // Callout function should clear the FWPS_RIGHT_ACTION_WRITE flag when it returns FWP_ACTION_BLOCK for the suggested action // and if FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT flag is set if (FWP_ACTION_BLOCK == classifyOut->actionType || FlagOn(filter->flags, FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)) { ClearFlag(classifyOut->rights, FWPS_RIGHT_ACTION_WRITE); }
}
And in ProcessTransportData I'm printing some info to the debugger about certain connections. And I also want to print info about bytes sent and bytes received through the connection:
static void ProcessTransportData(const FWPS_INCOMING_VALUES0* inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
void* layerData,
FWPS_CLASSIFY_OUT0* classifyOut)
{
ULONGLONG processId = (ULONGLONG)inMetaValues->processId;
UINT32 localAddress = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS].value.uint32; UINT32 remoteAddress = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS].value.uint32; UINT16 localPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT].value.uint16; UINT16 remotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT].value.uint16; if (inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.uint32 == FWP_DIRECTION_INBOUND) { KdPrint(("Inbound connection\n")); } else if (inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.uint32 == FWP_DIRECTION_OUTBOUND) { KdPrint(("Outbound connection\n")); } KdPrint(("Process Id: %I64u\n", processId)); KdPrint(("Local IP: %d.%d.%d.%d\n", localAddress & 0xFF, (localAddress >> 8) & 0xFF, (localAddress >> 16) & 0xFF, (localAddress >> 24) & 0xFF)); KdPrint(("Remote IP: %d.%d.%d.%d\n", remoteAddress & 0xFF, (remoteAddress >> 8) & 0xFF, (remoteAddress >> 16) & 0xFF, (remoteAddress >> 24) & 0xFF)); KdPrint(("Local Port: %d\n", localPort)); KdPrint(("Remote Port: %d\n", remotePort));
}
And there I assume that I can reach to this dataLength member through LayerData, but still I don't understand how to retrieve BYTES SENT and BYTES RECEIVED. I was thinking about Stream V4 Layer, but I had another difficulty with it which was adding a filter for TCPPROTO_IP but this layer does not have the field "IP_PROTOCOL".
I've read somewhere that I can accumulate bytes sent and bytes received through FlowContext that identifies each connection with unique ID and I can check if the flowContext is 0 in each calloutClassifyFn (whether the dataFlow that is being investigated at that point is connected with some context, if not it creates new context). But I don't really know how to retrieve this information about bytes sent and bytes received (even where to look for PACKET DIRECTION, not connection direction)... And the MSDN documentation unfortunately did not answer my questions...
I'm very new to driver development and I need help to come up with a solution and useful suggestions. Thank you in advance.
Upcoming OSR Seminars | ||
---|---|---|
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead! | ||
Kernel Debugging | 16-20 October 2023 | Live, Online |
Developing Minifilters | 13-17 November 2023 | Live, Online |
Internals & Software Drivers | 4-8 Dec 2023 | Live, Online |
Writing WDF Drivers | 10-14 July 2023 | Live, Online |
Comments
You're on the right track. You can use https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/fwpsk/nf-fwpsk-fwpsflowassociatecontext0 to keep byte counters on a per-flow basis. The stream data object has flags you can look at to determine the direction: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/fwpsk/ns-fwpsk-fwps_stream_data0_