dev.nlited.com

>>

NetMon: OOB_EDIT

<<<< prev
next >>>>

2014-12-29 23:32:38 chip Page 1152 📢 PUBLIC

Stream inspection and modification, from WDK samples.

Download as text: [FILE 7033]

oob_edit: /*++ Copyright (c) Microsoft Corporation. All rights reserved Abstract: Stream Edit Callout Driver Sample. This sample demonstrates Out-of-band (OOB) stream inspection/editing via the WFP stream API. Environment: Kernel mode --*/ #include <ntddk.h> #pragma warning(push) #pragma warning(disable:4201) // unnamed struct/union #include <fwpsk.h> #pragma warning(pop) #include <fwpmk.h> #include "inline_edit.h" #include "oob_edit.h" #include "stream_callout.h" #define STREAM_EDITOR_OUTGOING_DATA_TAG 'doeS' #define STREAM_EDITOR_MDL_DATA_TAG 'dmeS' void* gThreadObj; KSTART_ROUTINE StreamOobEditWorker; NTSTATUS OobEditInit( _Out_ STREAM_EDITOR* streamEditor ) { NTSTATUS status = STATUS_SUCCESS; HANDLE threadHandle; streamEditor->editInline = FALSE; KeInitializeSpinLock(&streamEditor->oobEditInfo.editLock); KeInitializeEvent( &streamEditor->oobEditInfo.editEvent, NotificationEvent, FALSE ); streamEditor->oobEditInfo.busyThreshold = 32 * 1024; streamEditor->oobEditInfo.editState = OOB_EDIT_IDLE; InitializeListHead(&streamEditor->oobEditInfo.outgoingDataQueue); status = PsCreateSystemThread( &threadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, StreamOobEditWorker, &gStreamEditor ); if (!NT_SUCCESS(status)) { goto Exit; } status = ObReferenceObjectByHandle( threadHandle, 0, NULL, KernelMode, &gThreadObj, NULL ); NT_ASSERT(NT_SUCCESS(status)); ZwClose(threadHandle); Exit: return status; } void OobEditShutdown( _Out_ STREAM_EDITOR* streamEditor ) { KLOCK_QUEUE_HANDLE editLockHandle; KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); streamEditor->oobEditInfo.shuttingDown = TRUE; switch (streamEditor->oobEditInfo.editState) { case OOB_EDIT_IDLE: { streamEditor->oobEditInfo.editState = OOB_EDIT_SHUT_DOWN; KeSetEvent( &gStreamEditor.oobEditInfo.editEvent, IO_NO_INCREMENT, FALSE ); break; } default: break; }; KeReleaseInStackQueuedSpinLock(&editLockHandle); NT_ASSERT(gThreadObj != NULL); KeWaitForSingleObject( gThreadObj, Executive, KernelMode, FALSE, NULL ); ObDereferenceObject(gThreadObj); } __inline NET_BUFFER_LIST* TailOfNetBufferListChain( _In_ NET_BUFFER_LIST* netBufferListChain ) { NT_ASSERT(netBufferListChain != NULL); while (netBufferListChain->Next != NULL) { netBufferListChain = netBufferListChain->Next; } return netBufferListChain; } void NTAPI StreamOobInjectCompletionFn( _Inout_ void* context, _Inout_ NET_BUFFER_LIST* netBufferList, BOOLEAN dispatchLevel ) /* ++ Injection completion function for injecting an NBL created using FwpsAllocateNetBufferAndNetBufferList. This function frees up resources allocated during StreamOobReinjectData(). -- */ { MDL* mdl = (MDL*)context; UNREFERENCED_PARAMETER(dispatchLevel); FwpsFreeNetBufferList(netBufferList); if (mdl != NULL) { IoFreeMdl(mdl); // // The MDL mapped over a pool alloc which we need to free here. // ExFreePoolWithTag( mdl->MappedSystemVa, STREAM_EDITOR_MDL_DATA_TAG ); } } void NTAPI StreamOobInjectCloneCompletionFn( _Inout_ void* context, _Inout_ NET_BUFFER_LIST* netBufferList, BOOLEAN dispatchLevel ) /* ++ Injection completion function for injecting one of the NBLs cloned via FwpsCloneStreamData. FwpsCloneStreamData can return a chain of cloned NBLs; each NBL will complete separately. -- */ { UNREFERENCED_PARAMETER(context); UNREFERENCED_PARAMETER(dispatchLevel); FwpsFreeCloneNetBufferList(netBufferList, 0); } NTSTATUS StreamOobQueueUpIncomingData( _Inout_ STREAM_EDITOR* streamEditor, _Inout_ FWPS_STREAM_DATA* streamData ) /* ++ This function clones the indicated stream data into a NBL chain and appends the chain at the end of the existing chain (if exists) inside the streamEditor. This function assumes that the oobEditInfo lock inside streamEditor is being held. -- */ { NTSTATUS status; NET_BUFFER_LIST* clonedNetBufferListChain; status = FwpsCloneStreamData( streamData, NULL, NULL, 0, &clonedNetBufferListChain ); if (!NT_SUCCESS(status)) { goto Exit; } // // TCP Fin (EOF) is indicated by an empty NBL with disconnect flag // set, since it does not contain any data we queue it up separately. // if ((streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) || (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)) { NT_ASSERT(streamEditor->oobEditInfo.noMoreData); NT_ASSERT(streamEditor->oobEditInfo.nblEof == NULL); NT_ASSERT(streamData->dataLength == 0); streamEditor->oobEditInfo.nblEof = clonedNetBufferListChain; status = STATUS_SUCCESS; goto Exit; } if (streamEditor->oobEditInfo.nblTail != NULL) { NT_ASSERT(streamEditor->oobEditInfo.nblHead != NULL); NT_ASSERT(streamEditor->oobEditInfo.nblTail->Next == NULL); streamEditor->oobEditInfo.nblTail->Next = clonedNetBufferListChain; } else { NT_ASSERT(streamEditor->oobEditInfo.nblHead == NULL); streamEditor->oobEditInfo.nblHead = clonedNetBufferListChain; } streamEditor->oobEditInfo.nblTail = TailOfNetBufferListChain(clonedNetBufferListChain); streamEditor->oobEditInfo.totalDataLength += streamData->dataLength; streamEditor->oobEditInfo.streamFlags = streamData->flags; Exit: return status; } void StreamOobEdit( _Inout_ STREAM_EDITOR* streamEditor, const FWPS_INCOMING_VALUES* inFixedValues, const FWPS_INCOMING_METADATA_VALUES* inMetaValues, const FWPS_FILTER* filter, _Inout_ FWPS_STREAM_DATA* streamData, _Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket, _Inout_ FWPS_CLASSIFY_OUT* classifyOut ) /* ++ This function queues up incoming data and notifies the worker thread to process them. The incoming data is blocked and removed from the stream while data is pending. If the editor is shutdown (e.g. during driverUnload) as indicated by OOB_EDIT_SHUT_DOWN state, it permits the indicated data inline after flushing all pended data (to be carried out by the caller). -- */ { NTSTATUS status; KLOCK_QUEUE_HANDLE editLockHandle; KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); if (streamEditor->oobEditInfo.nblEof != NULL) { // // A new flow arrives before we finish processing an earlier flow. Production // code should create 1:1 between streamEditor and flow to handle this // condition. See the "MSN Monitor sample" for how that can be implemented. // ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; classifyOut->actionType = FWP_ACTION_NONE; goto Exit; } if (classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA) { NT_ASSERT(streamEditor->oobEditInfo.nblEof == NULL); streamEditor->oobEditInfo.noMoreData = TRUE; } // // Record needed flow information etc for data (re-)injection. // streamEditor->oobEditInfo.calloutId = filter->action.calloutId; streamEditor->oobEditInfo.flowId = inMetaValues->flowHandle; streamEditor->oobEditInfo.layerId = inFixedValues->layerId; switch (streamEditor->oobEditInfo.editState) { case OOB_EDIT_PROCESSING: { if ((streamEditor->oobEditInfo.totalDataLength + streamData->dataLength) > streamEditor->oobEditInfo.busyThreshold) { ioPacket->streamAction = FWPS_STREAM_ACTION_DEFER; classifyOut->actionType = FWP_ACTION_NONE; streamEditor->oobEditInfo.editState = OOB_EDIT_BUSY; } else { status = StreamOobQueueUpIncomingData( streamEditor, streamData ); if (!NT_SUCCESS(status)) { streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; classifyOut->actionType = FWP_ACTION_NONE; } else { // // State remains at OOB_EDIT_PROCESSING state. Since the worker thread // is active there is no need to set the event (to wake it up) // ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; ioPacket->countBytesEnforced = 0; classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; } } break; } case OOB_EDIT_IDLE: { status = StreamOobQueueUpIncomingData( streamEditor, streamData ); if (!NT_SUCCESS(status)) { streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; classifyOut->actionType = FWP_ACTION_NONE; } else { streamEditor->oobEditInfo.editState = OOB_EDIT_PROCESSING; // // The worker thread is idle waiting for more work, now wake it up. // KeSetEvent( &streamEditor->oobEditInfo.editEvent, 0, FALSE ); ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; ioPacket->countBytesEnforced = 0; classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; } break; } case OOB_EDIT_SHUT_DOWN: { ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; ioPacket->countBytesEnforced = 0; classifyOut->actionType = FWP_ACTION_PERMIT; if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) { classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; } break; } case OOB_EDIT_ERROR: { ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; classifyOut->actionType = FWP_ACTION_NONE; break; } default: NT_ASSERT(FALSE); }; Exit: KeReleaseInStackQueuedSpinLock(&editLockHandle); } NTSTATUS StreamOobQueueUpOutgoingData( _Inout_ STREAM_EDITOR* streamEditor, _Inout_ NET_BUFFER_LIST* netBufferList, BOOLEAN isClone, size_t dataLength, DWORD streamFlags, _In_opt_ MDL* mdl ) /* ++ This function queues up processed data (either sections of the indicated data or newly created data) such that they can be (re-)injected back to the data stream during the following context. 1. Before FWP_ACTION_BLOCK is returned from the ClassifyFn, or 2. After EOF is indicated. Under the conditions above, the incoming data (which we pend) and the outgoing data (which we (re-)inject) can be synchronized properly). -- */ { NTSTATUS status = STATUS_SUCCESS; KLOCK_QUEUE_HANDLE editLockHandle; OUTGOING_STREAM_DATA* outgoingStreamData; outgoingStreamData = (OUTGOING_STREAM_DATA*) ExAllocatePoolWithTag( NonPagedPool, sizeof(OUTGOING_STREAM_DATA), STREAM_EDITOR_OUTGOING_DATA_TAG ); if (outgoingStreamData == NULL) { status = STATUS_NO_MEMORY; return status; } RtlZeroMemory(outgoingStreamData, sizeof(OUTGOING_STREAM_DATA)); outgoingStreamData->netBufferList = netBufferList; outgoingStreamData->isClone = isClone; outgoingStreamData->dataLength = dataLength; outgoingStreamData->streamFlags = streamFlags; outgoingStreamData->mdl = mdl; KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); InsertTailList( &streamEditor->oobEditInfo.outgoingDataQueue, &outgoingStreamData->listEntry ); KeReleaseInStackQueuedSpinLock(&editLockHandle); return status; } NTSTATUS StreamOobFlushOutgoingData( _Inout_ STREAM_EDITOR* streamEditor ) { NTSTATUS status = STATUS_SUCCESS; KLOCK_QUEUE_HANDLE editLockHandle; OUTGOING_STREAM_DATA* outgoingStreamData = NULL; for(;;) { KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); if (!IsListEmpty(&streamEditor->oobEditInfo.outgoingDataQueue)) { LIST_ENTRY* listEntry = RemoveHeadList(&streamEditor->oobEditInfo.outgoingDataQueue); outgoingStreamData = CONTAINING_RECORD( listEntry, OUTGOING_STREAM_DATA, listEntry ); } KeReleaseInStackQueuedSpinLock(&editLockHandle); if (outgoingStreamData == NULL) { break; } status = FwpsStreamInjectAsync( gInjectionHandle, NULL, 0, streamEditor->oobEditInfo.flowId, streamEditor->oobEditInfo.calloutId, streamEditor->oobEditInfo.layerId, outgoingStreamData->streamFlags, outgoingStreamData->netBufferList, outgoingStreamData->dataLength, outgoingStreamData->isClone ? StreamOobInjectCloneCompletionFn : StreamOobInjectCompletionFn, outgoingStreamData->mdl ); if (!NT_SUCCESS(status)) { goto Exit; } ExFreePoolWithTag( outgoingStreamData, STREAM_EDITOR_OUTGOING_DATA_TAG ); outgoingStreamData = NULL; } Exit: if (outgoingStreamData != NULL) { NT_ASSERT(!NT_SUCCESS(status)); if (outgoingStreamData->isClone) { FwpsDiscardClonedStreamData( outgoingStreamData->netBufferList, 0, FALSE ); } else { FwpsFreeNetBufferList(outgoingStreamData->netBufferList); if (outgoingStreamData->mdl != NULL) { IoFreeMdl(outgoingStreamData->mdl); ExFreePoolWithTag( outgoingStreamData->mdl->MappedSystemVa, STREAM_EDITOR_MDL_DATA_TAG ); } } ExFreePoolWithTag( outgoingStreamData, STREAM_EDITOR_OUTGOING_DATA_TAG ); } return status; } NTSTATUS StreamOobReinjectData( _Inout_ STREAM_EDITOR* streamEditor, UINT32 streamFlags, const void* data, size_t length ) /* ++ This function injects a section of the original indicated data back to the data stream. An MDL is allocated to describe the data section. -- */ { NTSTATUS status; void* dataCopy = NULL; MDL* mdl = NULL; NET_BUFFER_LIST* netBufferList = NULL; dataCopy = ExAllocatePoolWithTag( NonPagedPool, length, STREAM_EDITOR_MDL_DATA_TAG ); if (dataCopy == NULL) { status = STATUS_NO_MEMORY; goto Exit; } RtlCopyMemory(dataCopy, data, length); mdl = IoAllocateMdl( dataCopy, (ULONG)length, FALSE, FALSE, NULL ); if (mdl == NULL) { status = STATUS_NO_MEMORY; goto Exit; } MmBuildMdlForNonPagedPool(mdl); status = FwpsAllocateNetBufferAndNetBufferList( gNetBufferListPool, 0, 0, mdl, 0, length, &netBufferList ); if (!NT_SUCCESS(status)) { goto Exit; } NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); status = StreamOobQueueUpOutgoingData( streamEditor, netBufferList, FALSE, length, streamFlags, mdl ); if (!NT_SUCCESS(status)) { goto Exit; } dataCopy = NULL; mdl = NULL; netBufferList = NULL; Exit: if (netBufferList != NULL) { FwpsFreeNetBufferList(netBufferList); } if (mdl != NULL) { IoFreeMdl(mdl); } if (dataCopy != NULL) { ExFreePoolWithTag( dataCopy, STREAM_EDITOR_MDL_DATA_TAG ); } return status; } NTSTATUS StreamOobInjectReplacement( _Inout_ STREAM_EDITOR* streamEditor, UINT32 streamFlags, _In_opt_ MDL* data, size_t length ) /* ++ This function injects a section of replacement data (in place of data removed from the stream) into the data stream. The MDL describes the replacement data is allocated during DriverEntry and does not need to be freed during injection completion. -- */ { NTSTATUS status; NET_BUFFER_LIST* netBufferList = NULL; status = FwpsAllocateNetBufferAndNetBufferList( gNetBufferListPool, 0, 0, data, 0, length, &netBufferList ); if (!NT_SUCCESS(status)) { goto Exit; } NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); status = StreamOobQueueUpOutgoingData( streamEditor, netBufferList, FALSE, length, streamFlags, NULL ); if (!NT_SUCCESS(status)) { FwpsFreeNetBufferList(netBufferList); goto Exit; } netBufferList = NULL; Exit: if (netBufferList != NULL) { FwpsFreeNetBufferList(netBufferList); } return status; } NTSTATUS StreamOobCopyDataToFlatBuffer( _Inout_ STREAM_EDITOR* streamEditor, _Inout_ NET_BUFFER_LIST* netBufferListChain, size_t totalDataLength, DWORD streamFlags ) /* ++ This function copies the data described by NBL(s) into a flat buffer. It reuses the FwpsCopyStreamDataToBuffer API (via StreamCopyDataForInspection) by creating a FWPS_STREAM_DATA struct. -- */ { NTSTATUS status = STATUS_SUCCESS; FWPS_STREAM_DATA streamData = {0}; if (totalDataLength > 0) { streamData.netBufferListChain = netBufferListChain; streamData.dataLength = totalDataLength; streamData.flags = streamFlags; streamData.dataOffset.netBufferList = netBufferListChain; streamData.dataOffset.netBuffer = NET_BUFFER_LIST_FIRST_NB(streamData.dataOffset.netBufferList); streamData.dataOffset.mdl = NET_BUFFER_CURRENT_MDL(streamData.dataOffset.netBuffer); streamData.dataOffset.mdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(streamData.dataOffset.netBuffer); if (StreamCopyDataForInspection( streamEditor, &streamData ) == FALSE) { status = STATUS_NO_MEMORY; } } return status; } NTSTATUS StreamOobEditData( _Inout_ STREAM_EDITOR* streamEditor, _Inout_ NET_BUFFER_LIST* netBufferListChain, size_t totalDataLength, DWORD streamFlags ) /* ++ This function first copies the stream data into a flat inspection buffer; it then parses the buffer looking for the matching pattern. For non-matching sections it re-injects the data back; for a match it skips over and injects an replacement section. If a match can not be determined due to lack of data, it injects the non-matching section back and moves the potential match to the beginning of the inspection buffer. When an EOF is reached, it flushes all processed stream sections back and re-injects the FIN back to end the stream. -- */ { NTSTATUS status = STATUS_SUCCESS; UINT i = 0; BOOLEAN streamModified = FALSE; BOOLEAN potentialMatch = FALSE; BYTE* dataStart; UINT findLength = (UINT) strlen(configStringToFind); status = StreamOobCopyDataToFlatBuffer( streamEditor, netBufferListChain, totalDataLength, streamFlags ); if (!NT_SUCCESS(status)) { goto Exit; } dataStart = (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset; for (; i < streamEditor->dataLength; ++i) { if (i + findLength <= streamEditor->dataLength) { if (RtlCompareMemory( dataStart + i, configStringToFind, findLength ) == findLength) { if (i != 0) { status = StreamOobReinjectData( streamEditor, streamFlags, dataStart, i ); if (!NT_SUCCESS(status)) { goto Exit; } streamEditor->dataOffset += i; streamEditor->dataLength -= i; i = 0; } status = StreamOobInjectReplacement( streamEditor, streamFlags, gStringToReplaceMdl, strlen(configStringToReplace) ); if (!NT_SUCCESS(status)) { goto Exit; } streamEditor->dataOffset += findLength; streamEditor->dataLength -= findLength; streamModified = TRUE; if (streamEditor->dataLength > 0) { dataStart = (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset; --i; continue; } else { streamEditor->dataOffset = 0; } } } else { if (streamEditor->oobEditInfo.noMoreData) { break; } if (RtlCompareMemory( dataStart + i, configStringToFind, streamEditor->dataLength - i ) == streamEditor->dataLength - i) { potentialMatch = TRUE; // this is a partial find status = StreamOobReinjectData( streamEditor, streamFlags, dataStart, i ); if (!NT_SUCCESS(status)) { goto Exit; } RtlMoveMemory( (BYTE*)streamEditor->scratchBuffer, dataStart + i, streamEditor->dataLength - i ); streamEditor->dataOffset = 0; streamEditor->dataLength = streamEditor->dataLength - i; break; } } } if (streamModified && streamEditor->dataLength > 0) { status = StreamOobReinjectData( streamEditor, streamFlags, dataStart, streamEditor->dataLength ); if (!NT_SUCCESS(status)) { goto Exit; } streamEditor->dataOffset = 0; streamEditor->dataLength = 0; } if (!streamModified && !potentialMatch) { if (totalDataLength > 0) { NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); status = StreamOobQueueUpOutgoingData( streamEditor, netBufferListChain, TRUE, totalDataLength, streamFlags, NULL ); if (!NT_SUCCESS(status)) { goto Exit; } netBufferListChain = NULL; } else if (streamEditor->dataLength > 0) { status = StreamOobReinjectData( streamEditor, streamFlags, dataStart, streamEditor->dataLength ); if (!NT_SUCCESS(status)) { goto Exit; } } streamEditor->dataOffset = 0; streamEditor->dataLength = 0; } if (streamEditor->oobEditInfo.nblEof != NULL) { status = StreamOobFlushOutgoingData(streamEditor); if (!NT_SUCCESS(status)) { goto Exit; } status = FwpsStreamInjectAsync( gInjectionHandle, NULL, 0, streamEditor->oobEditInfo.flowId, streamEditor->oobEditInfo.calloutId, streamEditor->oobEditInfo.layerId, streamFlags | (configInspectionOutbound ? FWPS_STREAM_FLAG_SEND_DISCONNECT : FWPS_STREAM_FLAG_RECEIVE_DISCONNECT), streamEditor->oobEditInfo.nblEof, 0, StreamOobInjectCompletionFn, NULL ); if (!NT_SUCCESS(status)) { goto Exit; } streamEditor->oobEditInfo.nblEof = NULL; streamEditor->oobEditInfo.noMoreData = FALSE; } Exit: if (netBufferListChain != NULL) { FwpsDiscardClonedStreamData( netBufferListChain, 0, FALSE ); } if (streamEditor->oobEditInfo.nblEof != NULL) { FwpsDiscardClonedStreamData( streamEditor->oobEditInfo.nblEof, 0, FALSE ); streamEditor->oobEditInfo.nblEof = NULL; } return status; } _IRQL_requires_same_ _Function_class_(KSTART_ROUTINE) void StreamOobEditWorker( _In_ void* StartContext ) /* ++ This function waits for an event which gets signalled when there is data waiting to be inspected. Once awaken, the worker thread edits the stream until all stream data is processed (and then it waits for more work again). When requested to shutdown, it will finish the editing task and enters "shutdown" state. -- */ { NTSTATUS status = STATUS_SUCCESS; NET_BUFFER_LIST* netBufferListChain = NULL; size_t totalDataLength; STREAM_EDITOR* streamEditor = (STREAM_EDITOR*)StartContext; DWORD streamFlags; for(;;) { KLOCK_QUEUE_HANDLE editLockHandle; KeWaitForSingleObject( &streamEditor->oobEditInfo.editEvent, Executive, KernelMode, FALSE, NULL ); if (streamEditor->oobEditInfo.editState == OOB_EDIT_ERROR || streamEditor->oobEditInfo.editState == OOB_EDIT_SHUT_DOWN) { break; } KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); NT_ASSERT(streamEditor->oobEditInfo.editState == OOB_EDIT_PROCESSING || streamEditor->oobEditInfo.editState == OOB_EDIT_BUSY); netBufferListChain = streamEditor->oobEditInfo.nblHead; totalDataLength = streamEditor->oobEditInfo.totalDataLength; streamFlags = streamEditor->oobEditInfo.streamFlags; streamEditor->oobEditInfo.nblHead = NULL; streamEditor->oobEditInfo.nblTail = NULL; streamEditor->oobEditInfo.totalDataLength = 0; KeReleaseInStackQueuedSpinLock(&editLockHandle); _Analysis_assume_(netBufferListChain != NULL); status = StreamOobEditData( streamEditor, netBufferListChain, totalDataLength, streamFlags ); if (!NT_SUCCESS(status)) { streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; break; } if (streamEditor->oobEditInfo.editState == OOB_EDIT_BUSY) { NTSTATUS streamContinueStatus; streamEditor->oobEditInfo.editState = OOB_EDIT_PROCESSING; streamContinueStatus = FwpsStreamContinue( streamEditor->oobEditInfo.flowId, streamEditor->oobEditInfo.calloutId, streamEditor->oobEditInfo.layerId, streamEditor->oobEditInfo.streamFlags ); if (!NT_SUCCESS(streamContinueStatus)) { streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; break; } } KeAcquireInStackQueuedSpinLock( &streamEditor->oobEditInfo.editLock, &editLockHandle ); if (streamEditor->oobEditInfo.nblHead == NULL) { if (!streamEditor->oobEditInfo.shuttingDown) { streamEditor->oobEditInfo.editState = OOB_EDIT_IDLE; KeClearEvent(&streamEditor->oobEditInfo.editEvent); } else { streamEditor->oobEditInfo.editState = OOB_EDIT_SHUT_DOWN; } } KeReleaseInStackQueuedSpinLock(&editLockHandle); } PsTerminateSystemThread(status); } #if(NTDDI_VERSION >= NTDDI_WIN7) void NTAPI StreamOobEditClassify( _In_ const FWPS_INCOMING_VALUES* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, _Inout_ void* layerData, _In_ const void* classifyContext, _In_ const FWPS_FILTER* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT* classifyOut ) #else void NTAPI StreamOobEditClassify( _In_ const FWPS_INCOMING_VALUES* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, _Inout_ void* layerData, _In_ const FWPS_FILTER* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT* classifyOut ) #endif /// (NTDDI_VERSION >= NTDDI_WIN7) /* ++ This is the ClassifyFn function registered by the OOB stream edit callout. An OOB stream modification callout blocks all indicated data after cloning them for processing by a kernel mode worker thread (or marshalling the data to user mode for inspection); the resultant/edited data will then be put back to the stream via the stream injection API. For such a callout, the processed data must be (re-)injected back to the stream from within the ClassifyFn. -- */ { FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket; FWPS_STREAM_DATA* streamData; UINT findLength = (UINT) strlen(configStringToFind); ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)layerData; NT_ASSERT(ioPacket != NULL); streamData = ioPacket->streamData; NT_ASSERT(streamData != NULL); #if(NTDDI_VERSION >= NTDDI_WIN7) UNREFERENCED_PARAMETER(classifyContext); #endif /// (NTDDI_VERSION >= NTDDI_WIN7) UNREFERENCED_PARAMETER(flowContext); RtlZeroMemory(classifyOut, sizeof(FWPS_CLASSIFY_OUT)); // // Let go the traffic that the editor does not care about. // if ((configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_RECEIVE)) || (!configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_SEND))) { ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; classifyOut->actionType = FWP_ACTION_PERMIT; if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) { classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; } goto Exit; } // // In this sample we don't edit TCP urgent data // if ((streamData->flags & FWPS_STREAM_FLAG_SEND_EXPEDITED) || (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_EXPEDITED)) { ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; classifyOut->actionType = FWP_ACTION_PERMIT; if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) { classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; } goto Exit; } if ((streamData->dataLength < findLength) && !(classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)) { ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA; ioPacket->countBytesRequired = findLength; classifyOut->actionType = FWP_ACTION_NONE; goto Exit; } StreamOobEdit( &gStreamEditor, inFixedValues, inMetaValues, filter, streamData, ioPacket, classifyOut ); if (classifyOut->actionType == FWP_ACTION_BLOCK || classifyOut->actionType == FWP_ACTION_PERMIT) { if (!(streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && !(streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)) { NTSTATUS status = StreamOobFlushOutgoingData( &gStreamEditor); if (!NT_SUCCESS(status)) { ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; classifyOut->actionType = FWP_ACTION_NONE; } } } Exit: return; }


WebV7 (C)2018 nlited | Rendered by tikope in 60.217ms | 3.137.169.14