Windows驱动_WFP之三Callout驱动的操作

本文详细介绍了Windows过滤平台(WFP)中的Callout驱动操作,包括驱动初始化、卸载函数设定、基于WDM和WDF的驱动创建、过滤引擎注册Callouts的步骤,以及处理通知回调。同时,讨论了Callout驱动的安装、卸载和编程注意事项,强调了与IPsec的兼容性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

          还是没最终下那个决定,也许自己真的没有做大事的魄力,或者根本就做不成大事。做大事者,最忌,犹豫不决,婆婆妈妈,拖泥带水。但是,我有那么多的牵绊,我不得不考虑。其实,我心里已经做了决定,现在只是时间的问题。现在,心里的决定还是没有动摇的,这个我确信不疑的。所以,接下来的时间,我要好好准备。WFP差不多了,要开始到文件系统的minifilter,有时间的话,还要开始网络IO,还有javascript。重视基础。

                

          这篇博客,应该是我最最长的一篇了,因为它差不多花了我一个星期左右的时间。我们来看看WFP中的callout驱动的操作。本来 是想分割成几篇的。但是,不太好分,索性就不分了吧。


          Callout Driver Operations


          Initializing a Callout Driver


          callout驱动在DriverEntry函数中初始化自己,主要的初始化任务包含:
    
          指定卸载函数例程。
    
          基于WDM的Callout驱动。
    
          VOID
          Unload(
            IN PDRIVER_OBJECT DriverObject
          );


         NTSTATUS
         DriverEntry(
            IN PDRIVER_OBJECT DriverObject,
            IN PUNICODE_STRING RegistryPath
         )
        {
             ...
 
            // Specify the callout driver's Unload function
            DriverObject->DriverUnload = Unload;


             ...
         }
  
       基于WDF的callout驱动的卸载例程。
  
        VOID
        Unload(
              IN WDFDRIVER Driver
        );


        NTSTATUS
        DriverEntry(
              IN PDRIVER_OBJECT DriverObject,
              IN PUNICODE_STRING RegistryPath
        )
        {
              NTSTATUS status;
              WDF_DRIVER_CONFIG config;
              WDFDRIVER driver;


                ...


              // Initialize the driver config structure
              WDF_DRIVER_CONFIG_INIT(&config, NULL);


              // Indicate that this is a non-PNP driver
              config.DriverInitFlags = WdfDriverInitNonPnpDriver;


              // Specify the callout driver's Unload function
              config.EvtDriverUnload = Unload;


              // Create a WDFDRIVER object
              status =
              WdfDriverCreate(
                      DriverObject,
                      RegistryPath,
                      NULL,
                      &config,
                      &driver
                );


                         ...


                      return status;
        }


       创建设备对象。
    
       基于WDM的Callout驱动:
    
       PDEVICE_OBJECT deviceObject;


       NTSTATUS
       DriverEntry(
          IN PDRIVER_OBJECT DriverObject,
          IN PUNICODE_STRING RegistryPath
       )
       {
          NTSTATUS status;


           ...


           // Create a device object
          status =
          IoCreateDevice(
                 DriverObject,
                 0,
                 NULL,
                 FILE_DEVICE_UNKNOWN,
                 FILE_DEVICE_SECURE_OPEN,
                 FALSE,
                 &deviceObject
            );


                ...


               return status;
        }


        基于WDF的Callout驱动
  
        因为用过滤引擎注册callout,基于WDF的callout驱动需要得到一个指向WDM设备对象的指针,它和框架的设备对象关联。我们可以使用 WdfDeviceWdmGetDeviceObject 这个函数。
  
        WDFDEVICE wdfDevice;


        NTSTATUS
        DriverEntry(
            IN PDRIVER_OBJECT DriverObject,
            IN PUNICODE_STRING RegistryPath
        )
       {
               WDFDRIVER driver;
               PWDFDEVICE_INIT deviceInit;
               PDEVICE_OBJECT deviceObject;
               NTSTATUS status;


                ...


               // Allocate a device initialization structure
               deviceInit =
               WdfControlDeviceInitAllocate(
                         driver;
                         &SDDL_DEVOBJ_KERNEL_ONLY
               );


               // Set the device characteristics
                WdfDeviceInitSetCharacteristics(
                         deviceInit,
                         FILE_DEVICE_SECURE_OPEN,
                         FALSE
               );


               // Create a framework device object
               status =
                WdfDeviceCreate(
                         &deviceInit,
                         WDF_NO_OBJECT_ATTRIBUTES,
                         &wdfDevice
                );


                  // Check status
                if (status == STATUS_SUCCESS) {


                // Initialization of the framework device object is complete
                WdfControlFinishInitializing(
                          wdfDevice
                );


                // Get the associated WDM device object
                      deviceObject = WdfDeviceWdmGetDeviceObject(wdfDevice);
                }


                      ...


              return status;
         }


        使用过滤引擎注册Callouts.
    
        Callout驱动创建设备对象后,它就可以使用过滤引擎注册callout,callout驱动可以在任何时候使用过滤引擎注册callout,甚至过滤引擎不在工作状态。注册callout调用FwpsCalloutRegister0.
    
        // 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
       );


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


        // Callout registration structure
        const FWPS_CALLOUT0 Callout =
       {
       { ... }, // GUID key identifying the callout
       0,       // Callout-specific flags (none set here)
       ClassifyFn,
       NotifyFn,
       FlowDeleteFn
       };


       // Variable for the run-time callout identifier
       UINT32 CalloutId;


       NTSTATUS
       DriverEntry(
       IN PDRIVER_OBJECT DriverObject,
       IN PUNICODE_STRING RegistryPath
       )
     {
          PDEVICE_OBJECT deviceObject;
          NTSTATUS status;


          ...


          status =
          FwpsCalloutRegister0(
                 deviceObject,
                 &Callout,
                 &CalloutId
          );


           ...


          return status;
     }


        如果调用FwpsCalloutRegister0函数成功,函数的最后一个参数包含一个实时标识callout标识符的指针。实时标识符是一个为callout键的GUID.
  
         一个callout驱动可以执行多个callout,如果callout驱动执行多个callout,它必须为每一个callout调用函数
  FwpsCalloutRegister0函数。
  
         Processing Notify Callouts

         过滤引擎调用callout的notifyFn callout函数通知callout 驱动和callout相关联的事件发生。
  
         Filter Addition
  
         当过滤器为过滤动作指定的callout已经加入到过滤引擎中,过滤引擎调用callout的notifyFn callout函数,notifyType指定为FWPS_CALLOUT_NOTIFY_ADD_FILTER.
  
         Callout驱动在过滤器为过滤的动作指定Callout加入到过滤引擎以后,为过滤引擎注册callout.在这种情况下,过滤引擎不会调用callout的notifyFn callout函数,通知关于任何过滤器的callout.
  
          过滤引擎只当新的为过滤动作指定的过滤器callout被加入到过滤引擎中的时候,调用notifyFn callout函数通知相关的callout.在这种条件下,callout的notifyFn callout函数可能不会为过滤动作指定的callout已经加入到过滤引擎中的每个过滤器获得调用。
  
          如果,在过滤引起已经启动以后,callout驱动注册了一个callout,这个callout必须接收到每一个为过滤动作增加的Callout的每一个已经加入到过滤引擎中的过滤器的信息。callout驱动必须调用合适的管理函数来枚举过滤引擎中的所有过滤器。callout驱动必须通过过滤器类表进行分类,从而找到为过滤动作指定callout的过滤器。
  
          Filter Deletion
  
          当为了过滤动作指定的callout的过滤器从过滤引擎中被删除,过滤引擎调用callout的notifyFn callout函数,在参数notifyType参数中传递FWPS_CALLOUT_NOTIFY_DELETE_FILTER,参数filterKey传递NULL.过滤引擎会为每一个为了过滤动作指定callout的过滤器从过滤引擎中被删除的时候调用notifyFn callout函数。这些过滤器也包含那些callout驱动注册callout前已经被加入到过滤引擎的过滤器。因此,callout可能收到过滤器删除通知,但是收到一些过滤器的加入通知。
  
         如果callout的notifyFn函数没有在其notifyType参数中认出通知的类别。驱动应该省略通知,并返回STATUS_SUCCESS.
  
         callout驱动可以在过滤器被加入到过滤引擎的时候,指定和过滤器相关联的一个上下文结构。这个上下文结构对过滤引擎不透明。callout的classifyFn callout函数可以使用这个上下文结构保存一些信息状态给callout的函数下次给过滤引擎调用时使用。当过滤器从过滤引擎中被删除,callout驱动执行一些必须的清除上下文的动作。
  
  // Context structure to be associated with the filters
     typedef struct FILTER_CONTEXT_ {
      .
      .  // Driver-specific content
      .
    } FILTER_CONTEXT, *PFILTER_CONTEXT;


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


     // notifyFn callout function
     NTSTATUS NTAPI
     NotifyFn(
        IN FWPS_CALLOUT_NOTIFY_TYPE  notifyType,
        IN const GUID  *filterKey,
        IN const FWPS_FILTER0  *filter
    )
   {
        PFILTER_CONTEXT context;


        ASSERT(filter != NULL);


        // Switch on the type of notification
        switch(notifyType) {


        // A filter is being added to the filter engine
             case FWPS_CALLOUT_NOTIFY_ADD_FILTER:


        // Allocate the filter context structure
        context =
            (PFILTER_CONTEXT)ExAllocatePoolWithTag(
                                    NonPagedPool,
                                    sizeof(FILTER_CONTEXT),
                                    FILTER_CONTEXT_POOL_TAG
            );


        // Check the result of the memory allocation
        if (context == NULL) {


           // Return error
           return STATUS_INSUFFICIENT_RESOURCES;
        }


         // Initialize the filter context structure
         ...


         // Associate the filter context structure with the filter
          filter->context = (UINT64)context;


          break;


         // A filter is being removed from the filter engine
         case FWPS_CALLOUT_NOTIFY_DELETE_FILTER:


             // Get the filter context structure from the filter
              context = (PFILTER_CONTEXT)filter->context;


             // Check whether the filter has a context
             if (context) {


                // Cleanup the filter context structure
                 ...


                // Free the memory for the filter context structure
                ExFreePoolWithTag(
                      context,
                      FILTER_CONTEXT_POOL_TAG
                 );


        }
         break;


              // Unknown notification
              default:


              // Do nothing
               break;
    }


           return STATUS_SUCCESS;
    }



         Processing Classify Callouts

         当网络数据将被callout进行处理的时候,过滤引擎就会调相应分类callout的函数。这仅仅只发生在指定过滤动作的callout的过滤条件都满足的时候才发生。如果过滤没有过滤条件,过滤引擎就会一直调用分类的callout函数。过滤引擎会传递不同种类的数据给分类callout的函数。这些数据类型包括固定的数据值,原始网络数据,过滤信息,流上下文。过滤引擎传递的部分数据类型要取决于指定的过滤层和分类函数调用的条件。分类的函数也可以使用这些数据元素的复合类型做过滤决定。


         WFP支持异步处理分类的callout函数。
 
         Using a Callout for Deep Inspection

         当callout正在执行一些深度检查,它的分类callout函数可以检查任何传递的固定数据,媒体数据,任何包原始数据组成的数据和存储在和过滤相关联的上下文或数据流的数据。


    // classifyFn callout function
    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_OUT  *classifyOut
    )
   {
     PNET_BUFFER_LIST rawData;
     ...


     // Test for the FWPS_RIGHT_ACTION_WRITE flag to check the rights
     // for this callout to return an action. If this flag is not set,
     // a callout can still return a BLOCK action in order to VETO a
     // PERMIT action that was returned by a previous filter. In this
     // example the function just exits if the flag is not set.
     if (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE))
     {
        // Return without specifying an action
        return;
     }


     // Get the data fields from inFixedValues
      ...


     // Get any metadata fields from inMetaValues
      ...


     // Get the pointer to the raw data
     rawData = (PNET_BUFFER_LIST)layerData;


     // Get any filter context data from filter->context
     ...


     // Get any flow context data from flowContext
     ...


     // Inspect the various data sources to determine
     // the action to be taken on the data
      ...


     // If the data should be permitted...
     if (...) {


       // Set the action to permit the data
       classifyOut->actionType = FWP_ACTION_PERMIT;


       // Check whether the FWPS_RIGHT_ACTION_WRITE flag should be cleared
     if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
     {
       // Clear the FWPS_RIGHT_ACTION_WRITE flag
       classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
     }


       return;
     }


         ...


     // If the data should be blocked...
     if (...) {


     // Set the action to block the data
     classifyOut->actionType = FWP_ACTION_BLOCK;


     // Clear the FWPS_RIGHT_ACTION_WRITE flag
     classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;


      return;
    }


        ...


     // If the decision to permit or block should be passed
     // to the next filter in the filter engine...
     if (...) {


     // Set the action to continue with the next filter
     classifyOut->actionType = FWP_ACTION_CONTINUE;


     return;
     }


      ...
     }
     
         分类callout函数可以查看filter->action.type类型从而决定classifyOut参数的actionType应该返回什么值。我们来看一下这个FWPS_ACTION0的结构体。
  
          typedef struct FWPS_ACTION0_ {
                          FWP_ACTION_TYPE type;
                          UINT32          calloutId;
          } FWPS_ACTION0;


        如果callout需要在决定对数据进行许可还是禁止前要在分类的callout函数外执行额外的对数据包进行检查,它必须先暂停包数据直到数据处理完毕。
   
        在一些过滤层上,传递给分类的callout函数的layerData的值为NULL.
   
        Using a Callout for Deep Inspection of Stream Data
   
        // classifyFn callout function
        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_OUT  *classifyOut
        )
   {
        FWPS_STREAM_CALLOUT_IO_PACKET0 *ioPacket;
        FWPS_STREAM_BUFFER0 *dataStream;
        UINT32 bytesRequired;
        SIZE_T bytesToPermit;
        SIZE_T bytesToBlock;
        ...


        // Get a pointer to the stream callout I/O packet
        ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET0 *)layerData;


        // Get the data fields from inFixedValues
         ...


        // Get any metadata fields from inMetaValues
         ...


        // Get the pointer to the data stream
        dataStream = ioPacket->dataStream;


        // Get any filter context data from filter->context
        ...


        // Get any flow context data from flowContext
        ...


        // Inspect the various data sources to determine
        // the action to be taken on the data
        ...


        // If more stream data is required to make a determination...
        if (...) {


        // Let the filter engine know how many more bytes are needed
        ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA;
        ioPacket->countBytesRequired = bytesRequired;
        ioPacket->countBytesEnforced = 0;


        // Set the action to continue to the next filter
        classifyOut->actionType = FWP_ACTION_CONTINUE;


        return;
        }
        ...


         // If some or all of the data should be permitted...
        if (...) {


            // No stream-specific action is required
            ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;


            // Let the filter engine know how many of the leading bytes
            // in the stream should be permitted
            ioPacket->countBytesRequired = 0;
            ioPacket->countBytesEnforced = bytesToPermit;


            // Set the action to permit the data
            classifyOut->actionType = FWP_ACTION_PERMIT;


            return

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值