2016年8月17日星期三

Windows Filtering Platform 的例子

//////////////////////////////////////////////////////////////////////////////////////////////////



/*
因WFP的资料极度匮乏,特公示。
注释:这是初级的,入门的,更深的等待你挖掘。

这里有两个例子:
1.一个是纯内核驱动的。
2.一个是R0和R3紧密配合使用的。

made by correy
made at 2016.08.17
homepage:http://correy.webs.com
*/



//////////////////////////////////////////////////////////////////////////////////////////////////



//驱动代码如下:

#pragma once

#include <ntifs.h>
#include <Fwpsk.h>
#include <windef.h>
#include <initguid.h> //静态定义UUID用的,否则:error LNK2001。
#include <Fwpmk.h>
#include <Ntstrsafe.h>
#include <ndis.h>
#include <Wsk.h>
#include <ipmib.h>
#include <netpnp.h>
#include <ntintsafe.h>
#include <intrin.h>

#pragma warning(disable:4047)
#pragma warning(disable:4028)

#define TAG 'test' //test

PDEVICE_OBJECT deviceObject;

DEFINE_GUID(CALLOUTKEY, 0x99999999, 0x9999, 0x9999, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99);


VOID NTAPI ClassifyFn(
    IN const FWPS_INCOMING_VALUES0  *inFixedValues,
    IN const FWPS_INCOMING_METADATA_VALUES0  *inMetaValues,
    IN OUT VOID  *layerData,
    IN const FWPS_FILTER0  *filter,
    IN UINT64  flowContext,
    OUT FWPS_CLASSIFY_OUT0  *classifyOut
    )
{
    KdPrint(("ClassifyFn.\r\n"));
}


NTSTATUS NTAPI NotifyFn(IN FWPS_CALLOUT_NOTIFY_TYPE  notifyType, IN const GUID  *filterKey, IN const FWPS_FILTER0  *filter)
{
    return STATUS_SUCCESS;
}


VOID Unload(IN PDRIVER_OBJECT DriverObject)
{
    NTSTATUS status;
    UNICODE_STRING uniWin32NameString;
   
    status = FwpsCalloutUnregisterByKey(&CALLOUTKEY); ASSERT(NT_SUCCESS(status));

    //RtlInitUnicodeString( &uniWin32NameString, L"\\DosDevices\\hpm" );
    //IoDeleteSymbolicLink( &uniWin32NameString );
    IoDeleteDevice(deviceObject);// Delete the device object
}


NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    UNICODE_STRING  ntUnicodeString;
    UNICODE_STRING  ntWin32NameString;
    FWPS_CALLOUT0 sCallout ={0};
    UINT32 CalloutId;// Variable for the run-time callout identifier

    KdBreakPoint();
    __debugbreak();

    DriverObject->DriverUnload = Unload;

    RtlInitUnicodeString( &ntUnicodeString, L"\\Device\\hpm");
    NtStatus = IoCreateDevice(DriverObject, 0, &ntUnicodeString, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);
    ASSERT (NT_SUCCESS( NtStatus ));

    //RtlInitUnicodeString( &ntWin32NameString, L"\\DosDevices\\hpm" );
    //NtStatus = IoCreateSymbolicLink(&ntWin32NameString, &ntUnicodeString );
    //ASSERT (NT_SUCCESS( NtStatus ));

    sCallout.calloutKey = CALLOUTKEY;
    sCallout.classifyFn = ClassifyFn;
    sCallout.notifyFn = NotifyFn;
    NtStatus = FwpsCalloutRegister0(deviceObject, &sCallout, &CalloutId);
    ASSERT (NT_SUCCESS( NtStatus ));

    return NtStatus;
}



//////////////////////////////////////////////////////////////////////////////////////////////////
//应用层的代码如下:



#include <windows.h>
#include <fwpmu.h>
#include <stdio.h>
#include <assert.h>
#include <intrin.h>
#include <conio.h>
#include <initguid.h> //静态定义UUID用的,否则:error LNK2001。

#pragma comment(lib, "fwpuclnt.lib")

const GUID PROVIDER_KEY = {0x5fb216a8, 0xe2e8, 0x4024, { 0xb8, 0x53, 0x39, 0x1a, 0x41, 0x68, 0x64, 0x1e }};
DEFINE_GUID(CALLOUTKEY, 0x99999999, 0x9999, 0x9999, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99);


DWORD Install()
    /*
    功能:开启内核的WFP的Callout。

    参考:
    1.https://msdn.microsoft.com/en-us/library/windows/desktop/bb427376(v=vs.85).aspx
    2.Windows-driver-samples-master\network\trans\msnmntr
    */
{
   HANDLE engine = NULL;
   FWPM_SESSION0 session;
   memset(&session, 0, sizeof(session));  
   session.displayData.name = L"SDK Examples";// The session name isn't required but may be useful for diagnostics.  
   session.txnWaitTimeoutInMSec = INFINITE;// Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT errors while waiting to acquire the transaction lock.  
   session.flags = FWPM_SESSION_FLAG_DYNAMIC;// Let the Base Filtering Engine cleanup after us.
   DWORD result = FwpmEngineOpen0(NULL, RPC_C_AUTHN_DEFAULT, NULL, &session, &engine);// The authentication service should always be RPC_C_AUTHN_DEFAULT.
   assert(ERROR_SUCCESS == result);
 
   result = FwpmTransactionBegin0(engine, 0);// We add the provider and sublayer from within a single transaction to make it easy to clean up partial results in error paths.
   assert(ERROR_SUCCESS == result);

   /*
   此处不是必要的调用:
   1.FwpmProviderAdd0
   2.FwpmSubLayerAdd0
   3.如果加上更好。
   */

   //不添加过滤条件,WFP的内核Callout汗函数也不会调用。
   FWPM_FILTER filter;
   RtlZeroMemory(&filter, sizeof(FWPM_FILTER));
   filter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
   filter.displayData.name = L"name";
   filter.displayData.description = L"description";
   filter.action.type = FWP_ACTION_CALLOUT_INSPECTION; // We're only doing inspection.
   filter.action.calloutKey = CALLOUTKEY;
   filter.weight.type = FWP_EMPTY; // auto-weight.    
   filter.numFilterConditions = 0;
   result = FwpmFilterAdd(engine, &filter, NULL, NULL);
   assert(ERROR_SUCCESS == result);

   // Once all the adds have succeeded, we commit the transaction to persist the new objects.
   result = FwpmTransactionCommit0(engine);
   assert(ERROR_SUCCESS == result);

   _getch();//一调用FwpmEngineClose0效果就没有了,就是内核的ClassifyFn就不运行了。

   // FwpmEngineClose0 accepts null engine handles, so we needn't precheck for null.
   // Also, when closing an engine handle, any transactions still in progress are automatically aborted, so we needn't explicitly abort the transaction in error paths.
   FwpmEngineClose0(engine);
   return result;
}


int _tmain(int argc, _TCHAR* argv[])
{
    DebugBreak();
    __debugbreak();

    //运行/测试前确保驱动加载。
    DWORD D = Install();

    return 0;
}



//////////////////////////////////////////////////////////////////////////////////////////////////
//如果需要SOURCES文件,SOURCES文件内容如下:



TARGETNAME=wfp
TARGETTYPE=DRIVER

LINKER_FLAGS = $(LINKER_FLAGS)/INTEGRITYCHECK

INCLUDES=\
   $(DDK_INC_PATH);

TARGETLIBS=\
    $(DDK_LIB_PATH)\ntoskrnl.lib \
    $(DDK_LIB_PATH)\ndis.lib \
    $(DDK_LIB_PATH)\fwpkclnt.lib \
    $(SDK_LIB_PATH)\uuid.lib
   
TARGETLIBS=$(TARGETLIBS) $(DDK_LIB_PATH)\Ntstrsafe.lib

C_DEFINES=$(C_DEFINES) -DBINARY_COMPATIBLE=0 -DNT -DUNICODE -D_UNICODE -DNDIS60 -DNDIS_SUPPORT_NDIS6

SOURCES= wfp.c



//////////////////////////////////////////////////////////////////////////////////////////////////
/*
纯驱动版的。

注释:可以没有用FwpmSubLayerAdd添加自己的SubLayer,而是用FWPM_SUBLAYER_UNIVERSAL设置subLayerKey
*/


#pragma once

#include <ntifs.h>
#include <Fwpsk.h>
#include <windef.h>
#include <initguid.h> //静态定义UUID用的,否则:error LNK2001。
#include <Fwpmk.h>


// Context structure to be associated with the filters
typedef struct FILTER_CONTEXT_ {
    //.
    //.  // Driver-specific content
    //.
    int test;
} FILTER_CONTEXT, *PFILTER_CONTEXT;

#define FILTER_CONTEXT_POOL_TAG 'fcpt'// Memory pool tag for filter context structures


// Context structure to be associated with data flows
typedef struct FLOW_CONTEXT_ {
    //...
    int test;
} FLOW_CONTEXT, *PFLOW_CONTEXT;

#define FLOW_CONTEXT_POOL_TAG 'fcpt'

PDEVICE_OBJECT deviceObject;
UINT32 CalloutId;// Variable for the run-time callout identifier
FWPS_CALLOUT0 Callout ={0};
HANDLE injectionHandle;// Injection handle
HANDLE gEngineHandle;

DEFINE_GUID(WFP_TEST_GUID, 0x99999999, 0x9999, 0x9999, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99);
DEFINE_GUID(WFP_TEST_LAYER, 0x88888888, 0x8888, 0x8888, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88);


// Prototypes for the callout's callout functions
VOID NTAPI ClassifyFn(
    IN const FWPS_INCOMING_VALUES0  *inFixedValues,
    IN const FWPS_INCOMING_METADATA_VALUES0  *inMetaValues,
    IN OUT VOID  *layerData,
    IN const FWPS_FILTER0  *filter,
    IN UINT64  flowContext,
    OUT FWPS_CLASSIFY_OUT0  *classifyOut
    )
{

}


NTSTATUS NTAPI NotifyFn(
    IN FWPS_CALLOUT_NOTIFY_TYPE  notifyType,
    IN const GUID  *filterKey,
    IN const FWPS_FILTER0  *filter
    )
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    PFILTER_CONTEXT context;

    ASSERT(filter != NULL);
   
    switch(notifyType)// Switch on the type of notification
    {        
    case FWPS_CALLOUT_NOTIFY_ADD_FILTER:// A filter is being added to the filter engine      
        //context = (PFILTER_CONTEXT)ExAllocatePoolWithTag(NonPagedPool, sizeof(FILTER_CONTEXT), FILTER_CONTEXT_POOL_TAG);// Allocate the filter context structure      
        //if (context == NULL) {// Check the result of the memory allocation          
        //    return STATUS_INSUFFICIENT_RESOURCES;// Return error
        //}

        // Initialize the filter context structure
        //...
       
        //filter->context = (UINT64)context;// Associate the filter context structure with the filter
        break;      
    case FWPS_CALLOUT_NOTIFY_DELETE_FILTER:// A filter is being removed from the filter engine      
        context = (PFILTER_CONTEXT)filter->context;// Get the filter context structure from the filter      
        if (context) {// Check whether the filter has a context

            // Cleanup the filter context structure
            //...

            //ExFreePoolWithTag(context, FILTER_CONTEXT_POOL_TAG);// Free the memory for the filter context structure
        }
        break;  
    case FWPS_CALLOUT_NOTIFY_ADD_FILTER_POST_COMMIT:

        break;
    default:// Unknown notification
        // Do nothing
        break;
    }

    return STATUS_SUCCESS;
    return NtStatus;
}


VOID NTAPI FlowDeleteFn(
    IN UINT16  layerId,
    IN UINT32  calloutId,
    IN UINT64  flowContext
    )
{
    PFLOW_CONTEXT context;

    context = (PFLOW_CONTEXT)flowContext;// Get the flow context structure

    // Cleanup the flow context structure
    //...

    //ExFreePoolWithTag(context, FLOW_CONTEXT_POOL_TAG);// Free the memory for the flow context structure
}


VOID Unload(IN PDRIVER_OBJECT DriverObject)
{
    NTSTATUS status;
 
    FwpmEngineClose0(gEngineHandle);

    status = FwpsCalloutUnregisterById0(CalloutId);// Unregister the callout
    if (status == STATUS_DEVICE_BUSY)// Check result
    {
        // For each data flow that is being processed by the callout that has an associated context,
        // clean up the context and then call FwpsFlowRemoveContext0 to remove the context from the data flow.
        //...

        status = FwpsCalloutUnregisterById0(CalloutId);// Finish unregistering the callout
    }

    if (status != STATUS_SUCCESS)// Check status
    {
        // Handle error
        //...
    }

    IoDeleteDevice(deviceObject);// Delete the device object

    status = FwpsInjectionHandleDestroy0(injectionHandle);// Destroy the injection handle
    if (status != STATUS_SUCCESS)// Check status
    {
        // Handle error
        //...
    }
}


NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    FWPM_SUBLAYER0 SubLayer;
    FWPM_SESSION0 session = {0};
    FWPM_FILTER0 filter = {0};
    FWPM_FILTER_CONDITION0 filterConditions[3] = {0};
    FWPM_CALLOUT0 mCallout = {0};
    FWPM_DISPLAY_DATA0 displayData = {0};

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;// Specify the callout driver's Unload function

    NtStatus = IoCreateDevice(DriverObject, 0, NULL, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);// Create a device object
    if (!NT_SUCCESS( NtStatus )) {
        return NtStatus;
    }

    //WDK文档的Registering Callouts with the Filter Engine章节没有这个函数,可是卸载里有用这个变量。DDPROXY有。
    NtStatus = FwpsInjectionHandleCreate0(AF_INET, FWPS_INJECTION_TYPE_TRANSPORT, &injectionHandle);
    if (!NT_SUCCESS(NtStatus)) {
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    session.flags = FWPM_SESSION_FLAG_DYNAMIC;
    NtStatus = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &gEngineHandle);
    if (!NT_SUCCESS(NtStatus)) {      
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    NtStatus = FwpmTransactionBegin0(gEngineHandle, 0);
    if (!NT_SUCCESS(NtStatus)) {      
        FwpmEngineClose0(gEngineHandle);
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

   RtlZeroMemory(&SubLayer, sizeof(FWPM_SUBLAYER0));
   SubLayer.subLayerKey = WFP_TEST_LAYER;
   SubLayer.displayData.name = L"WFP TEST NAME";
   SubLayer.displayData.description = L"WFP TEST DESCRIPTION";
   SubLayer.flags = 0;
   SubLayer.weight = FWP_EMPTY; // auto-weight.;
   NtStatus = FwpmSubLayerAdd0(gEngineHandle, &SubLayer, NULL);
   if (!NT_SUCCESS(NtStatus)) {
       FwpmEngineClose0(gEngineHandle);
       FwpsInjectionHandleDestroy0(injectionHandle);
       IoDeleteDevice(deviceObject);
       return NtStatus;
   }

    Callout.calloutKey = WFP_TEST_GUID;
    Callout.flags = 0;
    Callout.classifyFn = ClassifyFn;
    Callout.notifyFn = NotifyFn;
    Callout.flowDeleteFn = FlowDeleteFn;
    NtStatus = FwpsCalloutRegister0(deviceObject, &Callout, &CalloutId);
    if (!NT_SUCCESS( NtStatus )) {
        FwpmEngineClose0(gEngineHandle);
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    displayData.name = L"XXX name";
    displayData.description = L"XXX description";
    mCallout.calloutKey = WFP_TEST_GUID;
    mCallout.displayData = displayData;
    mCallout.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
    NtStatus = FwpmCalloutAdd0(gEngineHandle, &mCallout, NULL, NULL);
    if (!NT_SUCCESS(NtStatus)) {
        FwpsCalloutUnregisterById0(CalloutId);
        FwpmEngineClose0(gEngineHandle);
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    filterConditions[0].fieldKey = FWPM_CONDITION_DIRECTION;
    filterConditions[0].matchType = FWP_MATCH_EQUAL;
    filterConditions[0].conditionValue.type = FWP_UINT32;
    filterConditions[0].conditionValue.uint32 = FWP_DIRECTION_OUTBOUND;

    filter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
    filter.displayData.name = L"WFP TEST NAME";
    filter.displayData.description = L"WFP TEST DESCRIPTION";
    filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    filter.action.calloutKey = WFP_TEST_GUID;
    filter.filterCondition = filterConditions;
    filter.subLayerKey = WFP_TEST_LAYER;
    filter.weight.type = FWP_EMPTY; // auto-weight.
    filter.rawContext = 0;
    filter.numFilterConditions = 1;
    NtStatus = FwpmFilterAdd0(gEngineHandle, &filter, NULL, NULL);
    if (!NT_SUCCESS(NtStatus)) {
        FwpsCalloutUnregisterById0(CalloutId);
        FwpmEngineClose0(gEngineHandle);
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    NtStatus = FwpmTransactionCommit0(gEngineHandle);
    if (!NT_SUCCESS(NtStatus)) {
        FwpsCalloutUnregisterById0(CalloutId);
        FwpmEngineClose0(gEngineHandle);
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    return NtStatus;
}


//////////////////////////////////////////////////////////////////////////////////////////////////

没有评论:

发表评论