Windows驱动_文件系统微小过滤驱动之五MiniFilter例程解析

来源:互联网 发布:淘宝利弊 编辑:程序博客网 时间:2024/05/04 19:38

                今天,上了下看雪,好久没上去了,现在的看雪,跟之前不一样了,刚毕业的那个时候,上面的大牛真是多,现在屈指可数了,可能这些大牛们,因为年纪的增长,已经不在一线了,或许因为家庭,每时间在论坛上逛了,也许都已经在创业了,现在这个APP泛滥的年代,意念和想法,已经不靠技术了,也没有多少人在研究着技术,目前的中国还是有些浮躁,其实好好想想,现在所有的系统,芯片还是掌握在别人手上,不要谈Android,Windows谁号谁坏,其实中国本就没有发言权,因为再好,或者谁好,都不是自己的,看愈演愈烈的网络监听吧。别人现在是赚钱,如果上升到国家安全,好用是好用,但你摆脱不了的时候,它就可以控制你,监听你。其实现在的中国互联网公司已经有很多钱了,为什么不去扶持一下,中国的操作系统,芯片了。


              我们今天来看下,微软的文件系统微过滤驱动的示例,主要的功能,包括,用户模式的应用程序和微小过滤驱动层程序的交流。微小过滤驱动的文件名的分析。一些相关文件信息的上传到应用程序。借此机会,重新来回顾一下,这些知识点。


                 这里首先分配了一个全局的上下文的空间。
  
  typedef struct _MINISPY_DATA {


  //
  //  The object that identifies this driver.
  //


  PDRIVER_OBJECT DriverObject;


  //
  //  The filter that results from a call to
  //  FltRegisterFilter.
  //


  PFLT_FILTER Filter;


  //
  //  Server port: user mode connects to this port
  //


  PFLT_PORT ServerPort;


  //
  //  Client connection port: only one connection is allowed at a time.,
  //


  PFLT_PORT ClientPort;


  //
  //  List of buffers with data to send to user mode.
  //


  KSPIN_LOCK OutputBufferLock;
  LIST_ENTRY OutputBufferList;


  //
  //  Lookaside list used for allocating buffers.
  //


  NPAGED_LOOKASIDE_LIST FreeBufferList;


  //
  //  Variables used to throttle how many records buffer we can use
  //


  LONG MaxRecordsToAllocate;
  __volatile LONG RecordsAllocated;


  //
  //  static buffer used for sending an "out-of-memory" message
  //  to user mode.
  //


  __volatile LONG StaticBufferInUse;


  //
  //  We need to make sure this buffer aligns on a PVOID boundary because
  //  minispy casts this buffer to a RECORD_LIST structure.
  //  That can cause alignment faults unless the structure starts on the
  //  proper PVOID boundary
  //


  PVOID OutOfMemoryBuffer[RECORD_SIZE/sizeof( PVOID )];


  //
  //  Variable and lock for maintaining LogRecord sequence numbers.
  //


  __volatile LONG LogSequenceNumber;


  //
  //  The name query method to use.  By default, it is set to
  //  FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, but it can be overridden
  //  by a setting in the registery.
  //


  ULONG NameQueryMethod;


  //
  //  Global debug flags
  //


  ULONG DebugFlags;


#if MINISPY_VISTA


  //
  //  Dynamically imported Filter Mgr APIs
  //


  PFLT_SET_TRANSACTION_CONTEXT PFltSetTransactionContext;


  PFLT_GET_TRANSACTION_CONTEXT PFltGetTransactionContext;


  PFLT_ENLIST_IN_TRANSACTION PFltEnlistInTransaction;


#endif


 } MINISPY_DATA, *PMINISPY_DATA;
 
 注册操作函数的定义:
 
 CONST FLT_REGISTRATION FilterRegistration = {


    sizeof(FLT_REGISTRATION),               //  Size
    FLT_REGISTRATION_VERSION,               //  Version   
#if MINISPY_WIN8 
    FLTFL_REGISTRATION_SUPPORT_NPFS_MSFS,   //  Flags
#else
    0,                                      //  Flags
#endif // MINISPY_WIN8


    Contexts,                               //  Context
    Callbacks,                              //  Operation callbacks


    SpyFilterUnload,                        //  FilterUnload


    NULL,                                   //  InstanceSetup
    SpyQueryTeardown,                       //  InstanceQueryTeardown
    NULL,                                   //  InstanceTeardownStart
    NULL,                                   //  InstanceTeardownComplete


    NULL,                                   //  GenerateFileName
    NULL,                                   //  GenerateDestinationFileName
    NULL                                    //  NormalizeNameComponent


#if MINISPY_VISTA


    
    SpyKtmNotificationCallback              //  KTM notification callback


#endif // MINISPY_VISTA


 };
 
 这里,还有两个内置的数据结构。
 
 const FLT_CONTEXT_REGISTRATION Contexts[] = {


#if MINISPY_VISTA


    { FLT_TRANSACTION_CONTEXT,
      0,
      SpyDeleteTxfContext,
      sizeof(MINISPY_TRANSACTION_CONTEXT),
      'ypsM' },


#endif // MINISPY_VISTA


    { FLT_CONTEXT_END }
};
 
 这个是定义上下文,这里主要是为VISTA以后出现的传输上下文。
 
 再Callbacks操作的定义:
 
 CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_CREATE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_CREATE_NAMED_PIPE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_CLOSE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_READ,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_WRITE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_QUERY_INFORMATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_SET_INFORMATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_QUERY_EA,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_SET_EA,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_FLUSH_BUFFERS,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_QUERY_VOLUME_INFORMATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_SET_VOLUME_INFORMATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_DIRECTORY_CONTROL,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_FILE_SYSTEM_CONTROL,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_DEVICE_CONTROL,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_INTERNAL_DEVICE_CONTROL,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_SHUTDOWN,
      0,
      SpyPreOperationCallback,
      NULL },                           //post operation callback not supported


    { IRP_MJ_LOCK_CONTROL,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_CLEANUP,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_CREATE_MAILSLOT,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_QUERY_SECURITY,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_SET_SECURITY,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_QUERY_QUOTA,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_SET_QUOTA,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_PNP,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_ACQUIRE_FOR_MOD_WRITE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_RELEASE_FOR_MOD_WRITE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_ACQUIRE_FOR_CC_FLUSH,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_RELEASE_FOR_CC_FLUSH,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


/*    { IRP_MJ_NOTIFY_STREAM_FILE_OBJECT,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },*/


    { IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_NETWORK_QUERY_OPEN,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_MDL_READ,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_MDL_READ_COMPLETE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_PREPARE_MDL_WRITE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_MDL_WRITE_COMPLETE,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_VOLUME_MOUNT,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_VOLUME_DISMOUNT,
      0,
      SpyPreOperationCallback,
      SpyPostOperationCallback },


    { IRP_MJ_OPERATION_END }
 };
 
 所有这里对于所有MajFuncion的预前操作和延后操作的回调函数指针都是一样的SpyPreOperationCallback和SpyPostOperationCallback。这里的Flags可以设置为0,也可以设置为
FLTFL_OPERATION_REGISTRATION_SKIP_CACHED_IO和FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO


 再调用status = FltRegisterFilter( DriverObject,
                        &FilterRegistration,
                        &MiniSpyData.Filter );
 完成注册。
 
 下面,定义和用户模式应用程序交流的相关交流PORT的信息。
 
 
        status  = FltBuildDefaultSecurityDescriptor( &sd,
                                                     FLT_PORT_ALL_ACCESS );


        if (!NT_SUCCESS( status )) {
            leave;
        }


        RtlInitUnicodeString( &uniString, MINISPY_PORT_NAME );


        InitializeObjectAttributes( &oa,
                                    &uniString,
                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
                                    NULL,
                                    sd );


        status = FltCreateCommunicationPort( MiniSpyData.Filter,
                                             &MiniSpyData.ServerPort,
                                             &oa,
                                             NULL,
                                             SpyConnect,
                                             SpyDisconnect,
                                             SpyMessage,
                                             1 );


        FltFreeSecurityDescriptor( sd );
  
  MINISPY_PORT_NAME这个需要和用户模式应用程序协调好,这个应用程序在连接PORT的时候需要用到这个名字。
  
  FltCreateCommunicationPort的几个函数指针的解释,也就是这里的SpyConnect,SpyDisconnect和SpyMessage.
  
  NTSTATUS FltCreateCommunicationPort(
  _In_      PFLT_FILTER Filter,
  _Out_     PFLT_PORT *ServerPort,
  _In_      POBJECT_ATTRIBUTES ObjectAttributes,
  _In_opt_  PVOID ServerPortCookie,
  _In_      PFLT_CONNECT_NOTIFY ConnectNotifyCallback,
  _In_      PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback,
  _In_opt_  PFLT_MESSAGE_NOTIFY MessageNotifyCallback,
  _In_      LONG MaxConnections
  );
  
  SpyConnect:过滤管理器在用户模式应用程序调用 FilterConnectCommunicationPort 的时候,调用这个驱动例程。它的参数不能为NULL,必须在IRQL=PASSIVE_LEVEL上被调用。
  
  SpyDisconnect:当用户模式的对于交流PORT的句柄的计数为0或者微小过滤驱动卸载的时候,这个例程个被调用。它的参数不能为NULL,必须在IRQL=PASSIVE_LEVEL上被调用。
  
  SpyMessage:这个例程被在IRQL=PASSIVE_LEVEL上被调用,当用户模式应用程序调用FilterSendMessage通过交流PORT发送消息给微小过滤驱动的时候。其参数不能为NULL.
  
  如果FltCreateCommunicationPort失败,我们必须调用FltCloseCommunicationPort将交流PORT关闭。
  
  最后,我们调用status = FltStartFiltering( MiniSpyData.Filter );告诉过滤管理器,开始过滤。
  
  由于没有注册微小过滤驱动的实例安装例程,这里,我们首先看预前操作的回调例程。
  
FLT_PREOP_CALLBACK_STATUS
#pragma warning(suppress: 6262) // higher than usual stack usage is considered safe in this case
SpyPreOperationCallback (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    )
/*++


Routine Description:


    This routine receives ALL pre-operation callbacks for this filter.  It then
    tries to log information about the given operation.  If we are able
    to log information then we will call our post-operation callback  routine.


    NOTE:  This routine must be NON-PAGED because it can be called on the
           paging path.


Arguments:


    Data - Contains information about the given operation.


    FltObjects - Contains pointers to the various objects that are pertinent
        to this operation.


    CompletionContext - This receives the address of our log buffer for this
        operation.  Our completion routine then receives this buffer address.


Return Value:


    Identifies how processing should continue for this operation


--*/
{
    FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; //assume we are NOT going to call our completion routine
    PRECORD_LIST recordList;
    PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
    UNICODE_STRING defaultName;
    PUNICODE_STRING nameToUse;
    NTSTATUS status;


#if MINISPY_VISTA


    PUNICODE_STRING ecpDataToUse = NULL;
    UNICODE_STRING ecpData;
    WCHAR ecpDataBuffer[MAX_NAME_SPACE/sizeof(WCHAR)];


#endif


#if MINISPY_NOT_W2K


    WCHAR name[MAX_NAME_SPACE/sizeof(WCHAR)];


#endif


    //
    //  Try and get a log record
    //


    recordList = SpyNewRecord();


    if (recordList) {


        //
        //  We got a log record, if there is a file object, get its name.
        //
        //  NOTE: By default, we use the query method
        //  FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP
        //  because MiniSpy would like to get the name as much as possible, but
        //  can cope if we can't retrieve a name.  For a debugging type filter,
        //  like Minispy, this is reasonable, but for most production filters
        //  who need names reliably, they should query the name at times when it
        //  is known to be safe and use the query method
        //  FLT_FILE_NAME_QUERY_DEFAULT.
        //


        if (FltObjects->FileObject != NULL) {


            status = FltGetFileNameInformation( Data,
                                                FLT_FILE_NAME_NORMALIZED |
                                                    MiniSpyData.NameQueryMethod,
                                                &nameInfo );


        } else {


            //
            //  Can't get a name when there's no file object
            //


            status = STATUS_UNSUCCESSFUL;
        }


        //
        //  Use the name if we got it else use a default name
        //


        if (NT_SUCCESS( status )) {


            nameToUse = &nameInfo->Name;


            //
            //  Parse the name if requested
            //


            if (FlagOn( MiniSpyData.DebugFlags, SPY_DEBUG_PARSE_NAMES )) {


#ifdef DBG


                FLT_ASSERT( NT_SUCCESS( FltParseFileNameInformation( nameInfo ) ) );


#else


                FltParseFileNameInformation( nameInfo );


#endif


            }


        } else {


#if MINISPY_NOT_W2K


            NTSTATUS lstatus;
            PFLT_FILE_NAME_INFORMATION lnameInfo;


            //
            //  If we couldn't get the "normalized" name try and get the
            //  "opened" name
            //


            if (FltObjects->FileObject != NULL) {


                //
                //  Get the opened name
                //


                lstatus = FltGetFileNameInformation( Data,
                                                     FLT_FILE_NAME_OPENED |
                                                            FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP,
                                                     &lnameInfo );




                if (NT_SUCCESS(lstatus)) {


#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage")
                (VOID)_snwprintf( name,
                                      sizeof(name)/sizeof(WCHAR),
                                      L"<%08x> %wZ",
                                      status,
                                      &lnameInfo->Name );


                    FltReleaseFileNameInformation( lnameInfo );


                } else {


                    //
                    //  If that failed report both NORMALIZED status and
                    //  OPENED status
                    //


#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage")
                    (VOID)_snwprintf( name,
                                      sizeof(name)/sizeof(WCHAR),
                                      L"",
                                      status,
                                      lstatus );
                }


            } else {


#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage")
                (VOID)_snwprintf( name,
                                  sizeof(name)/sizeof(WCHAR),
                                  L"" );


            }


            //
            //  Name was initialized by _snwprintf() so it may not be null terminated
            //  if the buffer is insufficient. We will ignore this error and truncate
            //  the file name. 
            //


            name[(sizeof(name)/sizeof(WCHAR))-1] = L'\0';


            RtlInitUnicodeString( &defaultName, name );
            nameToUse = &defaultName;


#else


            //
            //  We were unable to get the String safe routine to work on W2K
            //  Do it the old safe way
            //


            RtlInitUnicodeString( &defaultName, L"" );
            nameToUse = &defaultName;


#endif //MINISPY_NOT_W2K


#if DBG


            //
            //  Debug support to break on certain errors.
            //


            if (FltObjects->FileObject != NULL) {
                NTSTATUS retryStatus;


                if ((StatusToBreakOn != 0) && (status == StatusToBreakOn)) {


                    DbgBreakPoint();
                }


                retryStatus = FltGetFileNameInformation( Data,
                                                         FLT_FILE_NAME_NORMALIZED |
                                                             MiniSpyData.NameQueryMethod,
                                                         &nameInfo );


                if (!NT_SUCCESS( retryStatus )) {


                    //
                    //  We always release nameInfo, so ignore return value.
                    //


                    NOTHING;
                }
            }


#endif


        }


#if MINISPY_VISTA


        //
        //  Look for ECPs, but only if it's a create operation
        //


        if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {


            //
            //  Initialize an empty string to receive an ECP data dump
            //


            RtlInitEmptyUnicodeString( &ecpData,
                                       ecpDataBuffer,
                                       MAX_NAME_SPACE/sizeof(WCHAR) );


            //
            //  Parse any extra create parameters
            //


            SpyParseEcps( Data, recordList, &ecpData );


            ecpDataToUse = &ecpData;
        }


        //
        //  Store the name and ECP data (if any)
        //


        SpySetRecordNameAndEcpData( &(recordList->LogRecord), nameToUse, ecpDataToUse );


#else


        //
        //  Store the name
        //


        SpySetRecordName( &(recordList->LogRecord), nameToUse );


#endif


        //
        //  Release the name information structure (if defined)
        //


        if (NULL != nameInfo) {


            FltReleaseFileNameInformation( nameInfo );
        }


        //
        //  Set all of the operation information into the record
        //


        SpyLogPreOperationData( Data, FltObjects, recordList );


        //
        //  Pass the record to our completions routine and return that
        //  we want our completion routine called.
        //


        if (Data->Iopb->MajorFunction == IRP_MJ_SHUTDOWN) {


            //
            //  Since completion callbacks are not supported for
            //  this operation, do the completion processing now
            //


            SpyPostOperationCallback( Data,
                                      FltObjects,
                                      recordList,
                                      0 );


            returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;


        } else {


            *CompletionContext = recordList;
            returnStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;
        }
    }


    return returnStatus;
}
 
 这里首先,我们为其记录分配一个空间。
 
 typedef struct _LOG_RECORD {




    ULONG Length;           // Length of log record.  This Does not include
    ULONG SequenceNumber;   // space used by other members of RECORD_LIST


    ULONG RecordType;       // The type of log record this is.
    ULONG Reserved;         // For alignment on IA64


    RECORD_DATA Data;
    WCHAR Name[];           //  This is a null terminated string


 } LOG_RECORD, *PLOG_RECORD;
 
 typedef struct _RECORD_DATA {


    LARGE_INTEGER OriginatingTime;
    LARGE_INTEGER CompletionTime;


    FILE_ID DeviceObject;
    FILE_ID FileObject;
    FILE_ID Transaction;


    FILE_ID ProcessId;
    FILE_ID ThreadId;


    ULONG_PTR Information;


    NTSTATUS Status;


    ULONG IrpFlags;
    ULONG Flags;


    UCHAR CallbackMajorId;
    UCHAR CallbackMinorId;
    UCHAR Reserved[2];      // Alignment on IA64


    PVOID Arg1;
    PVOID Arg2;
    PVOID Arg3;
    PVOID Arg4;
    PVOID Arg5;
    LARGE_INTEGER Arg6;


    ULONG EcpCount;
    ULONG KnownEcpMask;


 } RECORD_DATA, *PRECORD_DATA;
 
 typedef struct _RECORD_LIST {


    LIST_ENTRY List;


    //
    // Must always be last item.  See MAX_LOG_RECORD_LENGTH macro below.
    // Must be aligned on PVOID boundary in this structure. This is because the
    // log records are going to be packed one after another & accessed directly
    // Size of log record must also be multiple of PVOID size to avoid alignment
    // faults while accessing the log records on IA64
    //


    LOG_RECORD LogRecord;


 } RECORD_LIST, *PRECORD_LIST;
 
 通过一个链表连接起来。这个定义的空间,是跟应用程序约定好的。
 
 因为要通过某些预前操作的回调例程的参数,我们索性就将其参数都看一下。
 
 typedef FLT_PREOP_CALLBACK_STATUS ( *PFLT_PRE_OPERATION_CALLBACK)(
  _Inout_  PFLT_CALLBACK_DATA Data,
  _In_     PCFLT_RELATED_OBJECTS FltObjects,
  _Out_    PVOID *CompletionContext
 );
 
 typedef struct _FLT_CALLBACK_DATA {


    //
    //  Flags
    //


    FLT_CALLBACK_DATA_FLAGS Flags;


    //
    //  Thread that initiated this operation.
    //


    PETHREAD CONST Thread;


    //
    //  Pointer to the changeable i/o parameters
    //


    PFLT_IO_PARAMETER_BLOCK CONST Iopb;


    //
    //  For pre-op calls: if filter returns STATUS_IO_COMPLETE, then it should
    //  set the return i/o status here.  For post-operation calls, this is set
    //  by filter-manager indicating the completed i/o status.
    //


    IO_STATUS_BLOCK IoStatus;




    struct _FLT_TAG_DATA_BUFFER *TagData;


    union {
        struct {


            //
            //  Queue links if the FltMgr queue is used to
            //  pend the callback
            //


            LIST_ENTRY QueueLinks;


            //
            //  Additional context
            //


            PVOID QueueContext[2];
        };


        //
        //  The following are available to filters to use
        //  in whatever manner desired if not using the filter manager
        //  queues.
        //  NOTE:  These fields are only valid while the filter is
        //         processing this operation which is inside the operation
        //         callback or while the operation is pended.
        //


        PVOID FilterContext[4];
    };


    //
    //  Original requester mode of caller
    //


    KPROCESSOR_MODE RequestorMode;


} FLT_CALLBACK_DATA, *PFLT_CALLBACK_DATA;


  再来看FLT_CALLBACK_DATA_FLAGS 这个结构体:
  
      #define FLTFL_CALLBACK_DATA_REISSUE_MASK           0x0000FFFF


  //
  //  The below 3 flags are mutually exclusive.
  //  i.e. only ONE and exacly one hould be set for the callback data.
  //  Once set they should never change
  //
   #define FLTFL_CALLBACK_DATA_IRP_OPERATION           0x00000001    // Set for Irp operations
   #define FLTFL_CALLBACK_DATA_FAST_IO_OPERATION       0x00000002    // Set for Fast Io operations
   #define FLTFL_CALLBACK_DATA_FS_FILTER_OPERATION     0x00000004    // Set for Fs Filter operations
  //
  //  In principle this flag can be set for any operation. Once set it shouldn't change
  //
   #define FLTFL_CALLBACK_DATA_SYSTEM_BUFFER           0x00000008    // Set if the buffer passed in for the i/o was a system buffer






  //
  //  Below flags are relevant only for IRP-based i/o - i.e. only
  //  if FLTFL_CALLBACK_DATA_IRP_OPERATION was set. If the i/o was reissued
  //  both flags will necessarily be set
  //
  #define FLTFL_CALLBACK_DATA_GENERATED_IO            0x00010000    // Set if this is I/O generated by a mini-filter
  #define FLTFL_CALLBACK_DATA_REISSUED_IO             0x00020000    // Set if this I/O was reissued


  //
  //  Below 2 flags are set only for post-callbacks.
  //
  #define FLTFL_CALLBACK_DATA_DRAINING_IO             0x00040000    // set if this operation is being drained. If set,
  #define FLTFL_CALLBACK_DATA_POST_OPERATION          0x00080000    // Set if this is a POST operation


  //
  //  This flag can only be set by Filter Manager, only for an IRP based operation
  //  and only for a post callback. When set, it specifies that a lower level driver
  //  allocated a buffer for AssociatedIrp.SystemBuffer in which the data for
  //  the operation will be returned to the IO manager. Filters need to know this
  //  because when they were called in the PRE operation AssociatedIrp.SystemBuffer
  //  was null and as such their buffer is set to UserBuffer and they have no way of
  //  getting the real data from SystemBuffer. Check the IRP_DEALLOCATE_BUFFER flag for
  //  more details on how this is used by file systems.
  //


  #define FLTFL_CALLBACK_DATA_NEW_SYSTEM_BUFFER       0x00100000


  //
  //  Flags set by mini-filters: these are set by the minifilters and may be reset
  //  by filter manager.
  //
  #define FLTFL_CALLBACK_DATA_DIRTY                   0x80000000    // Set by caller if parameters were changed


  PETHREAD 这个结构体,是不透明的,不过,在很多书籍上,都有介绍,因为在Windbg中我们看到其所有的成员。
  
  typedef struct _FLT_IO_PARAMETER_BLOCK {
   ULONG          IrpFlags;
   UCHAR          MajorFunction;
   UCHAR          MinorFunction;
   UCHAR          OperationFlags;
   UCHAR          Reserved;
   PFILE_OBJECT   TargetFileObject;
   PFLT_INSTANCE  TargetInstance;
   FLT_PARAMETERS Parameters;
  } FLT_IO_PARAMETER_BLOCK, *PFLT_IO_PARAMETER_BLOCK;
  
  这个结构体中的,关于对象的域都是不透明的。
  
  typedef struct _IO_STATUS_BLOCK {
   union {
    NTSTATUS Status;
    PVOID    Pointer;
   };
   ULONG_PTR Information;
  } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
  
  这个是跟状态值相关的。
  
  typedef struct _FLT_TAG_DATA_BUFFER {
   ULONG  FileTag;
   USHORT TagDataLength;
   USHORT UnparsedNameLength;
   union {
    struct {
     USHORT SubstituteNameOffset;
     USHORT SubstituteNameLength;
     USHORT PrintNameOffset;
     USHORT PrintNameLength;
     ULONG  Flags;
     WCHAR  PathBuffer[1];
    } SymbolicLinkReparseBuffer;
    struct {
     USHORT SubstituteNameOffset;
     USHORT SubstituteNameLength;
     USHORT PrintNameOffset;
     USHORT PrintNameLength;
     WCHAR  PathBuffer[1];
    } MountPointReparseBuffer;
    struct {
     UCHAR DataBuffer[1];
    } GenericReparseBuffer;
    struct {
     GUID  TagGuid;
     UCHAR DataBuffer[1];
    } GenericGUIDReparseBuffer;
   };
  } FLT_TAG_DATA_BUFFER, *PFLT_TAG_DATA_BUFFER;
  
  这个跟数据空间相关,我们后面会看到。
  
  再看另一个参数的结构体:
  
  typedef struct _FLT_RELATED_OBJECTS {
   const USHORT        Size;
   const USHORT        TransactionContext;
   const PFLT_FILTER   Filter;
   const PFLT_VOLUME   Volume;
   const PFLT_INSTANCE Instance;
   const PFILE_OBJECT  FileObject;
   const PKTRANSACTION Transaction;
  } FLT_RELATED_OBJECTS, *PFLT_RELATED_OBJECTS;
  
  这些域基本也是不透明的。我们在代码中来看:
  
  分配了相关的记录空间以后,我们首先来查看,操作的文件对象是否为NULL,
  
       if (FltObjects->FileObject != NULL) {


            status = FltGetFileNameInformation( Data,
                                                FLT_FILE_NAME_NORMALIZED |
                                                    MiniSpyData.NameQueryMethod,
                                                &nameInfo );
            
   NTSTATUS FltGetFileNameInformation(
    _In_   PFLT_CALLBACK_DATA CallbackData,
    _In_   FLT_FILE_NAME_OPTIONS NameOptions,
    _Out_  PFLT_FILE_NAME_INFORMATION *FileNameInformation
   );
   
   这里我们指定的FLT_FILE_NAME_NORMALIZED,指定FileNameInformation参数得到一个文件的名字。
   
   看下,这个PFLT_FILE_NAME_INFORMATION的结构体定义:
   
   typedef struct _FLT_FILE_NAME_INFORMATION {


    USHORT Size;


    //
    //  For each bit that is set in the NamesParsed flags field, the
    //  corresponding substring from Name has been appropriately
    //  parsed into one of the unicode strings below.
    //


    FLT_FILE_NAME_PARSED_FLAGS NamesParsed;


    //
    //  The name format that this FLT_FILE_NAME_INFORMATION structure
    //  represents.
    //


    FLT_FILE_NAME_OPTIONS Format;


    //
    //  For normalized and opened names, this name contains the version of
    //  name in the following format:
    //
    //    [Volume name][Full path to file][File name][Stream Name]
    //
    //    For example, the above components would map to this example name as
    //    follows:
    //
    //    \Device\HarddiskVolume1\Documents and Settings\MyUser\My Documents\Test Results.txt:stream1
    //
    //    [Volume name] = "\Device\HarddiskVolume1"
    //    [Full path to file] = "\Documents and Settings\MyUser\My Documents\"
    //    [File name] = "Test Results.txt"
    //    [Stream name] = ":stream1"
    //
    //  For short names, only the short name for the final name component is
    //  returned in the Name unicode string.  Therefore, if you requested
    //  the short name of the file object representing an open on the file:
    //
    //    \Device\HarddiskVolume1\Documents and Settings\MyUser\My Documents\Test Results.txt
    //
    //  The name returned in Name will be at most 8 characters followed by a '.'
    //  then at most 3 more characters, like:
    //
    //    testre~1.txt
    //


    UNICODE_STRING Name;


    //
    //  The Volume is only filled in for name requested in normalized and opened
    //  formats.
    //


    UNICODE_STRING Volume;


    //
    //  The share component of the file name requested.  This will only be
    //  set for normalized and opened name formats on files that opened across
    //  redirectors.  For local files, this string will always be 0 length.
    //


    UNICODE_STRING Share;


    //
    //  To exemplify what each of the following substrings refer to, let's
    //  look again at the first example string from above:
    //
    //    \Device\HarddiskVolume1\Documents and Settings\MyUser\My Documents\Test Results.txt:stream1
    //
    //  Extension = "txt"
    //  Stream = ":stream1"
    //  FinalComponent = "Test Results.txt:stream1"
    //  ParentDir = "\Documents and Settings\MyUser\My Documents\"
    //


    //
    //  This can be parsed from a normalized, opened, or short name.
    //


    UNICODE_STRING Extension;


    //
    //  The following parse formats are only available for normalized and
    //  opened name formats, but not short names.
    //


    UNICODE_STRING Stream;
    UNICODE_STRING FinalComponent;
    UNICODE_STRING ParentDir;


   } FLT_FILE_NAME_INFORMATION, *PFLT_FILE_NAME_INFORMATION;
   
   nameToUse = &nameInfo->Name;
   
   下面是分析这个名字的结构体:
   
   FltParseFileNameInformation( nameInfo );
   
   远程文件举例:
   
   \Device\LanManRedirector\MyServer\MyShare\Documents and Settings\MyUser\My Documents\Test Results.txt:stream1
   
   Volume: "\Device\LanManRedirector"


   Share: "\MyServer\MyShare"


   Extension: "txt"


   Stream: ":stream1"


   FinalComponent: "Test Results.txt:stream1"


   ParentDir: "\Documents and Settings\MyUser\My Documents\" 
   
   本地文件举例:
   
   \Device\HarddiskVolume1\Docume~1\MyUser\My Documents\TestRe~1.txt:stream1:$DATA


   FltParseFileNameInformation parses this opened name as follows:


   Volume: "\Device\HarddiskVolume1"


   Share: NULL


   Extension: "txt"


   Stream: ":stream1:$DATA"


   FinalComponent: "TestRe~1.txt:stream1:$DATA"


   ParentDir: "\Docume~1\MyUser\My Documents\" 
   
   下面来检查额外创建参数:
   
   NTSTATUS FltGetEcpListFromCallbackData(
    _In_   PFLT_FILTER Filter,
    _In_   PFLT_CALLBACK_DATA Data,
    _Out_  PECP_LIST *EcpList
   );
   
   这个EcpList也是一个不透明的域,我们要用这个域来得到一些关于ECP的GUID等信息:
   
   NTSTATUS FltGetNextExtraCreateParameter(
    _In_       PFLT_FILTER Filter,
    _In_       PECP_LIST EcpList,
    _In_opt_   PVOID CurrentEcpContext,
    _Out_opt_  LPGUID NextEcpType,
    _Out_opt_  PVOID *NextEcpContext,
    _Out_opt_  ULONG *NextEcpContextSize
   );
   
   这里可以得到关于ECP的GUID.也就是其第三个参数:NextEcpType;
   
   系统本身定义了一套ECP的类型,详细的可以看:http://msdn.microsoft.com/en-us/library/windows/hardware/ff556779(v=vs.85).aspx
   
   GUID_ECP_OPLOCK_KEY


   GUID_ECP_NETWORK_OPEN_CONTEXT


   GUID_ECP_PREFETCH_OPEN


   GUID_ECP_NFS_OPEN


   GUID_ECP_SRV_OPEN


   GUID_ECP_DUAL_OPLOCK_KEY


   GUID_ECP_IO_DEVICE_HINT


   GUID_ECP_NETWORK_APP_INSTANCE


   这些分别有定义好的上下文结构,装入一下额外创建操作的参数。
   
   举例来说,对于NFS的
   typedef struct _NFS_OPEN_ECP_CONTEXT {
    PUNICODE_STRING       ExportAlias;
    PSOCKADDR_STORAGE_NFS ClientSocketAddress;
   } NFS_OPEN_ECP_CONTEXT, *PNFS_OPEN_ECP_CONTEXT, **PPNFS_OPEN_ECP_CONTEXT;
   
   typedef struct sockaddr_storage {
    ADDRESS_FAMILY ss_family;
    CHAR           __ss_pad1[_SS_PAD1SIZE];
    __int64        __ss_align;
    CHAR           __ss_pad2[_SS_PAD2SIZE];
   } SOCKADDR_STORAGE, *PSOCKADDR_STORAGE;
   
   typedef struct sockaddr_in {
    ADDRESS_FAMILY sin_family;
    USHORT         sin_port;
    IN_ADDR        sin_addr;
    CHAR           sin_zero[8];
   } SOCKADDR_IN, *PSOCKADDR_IN;
   
   typedef struct sockaddr_in {
    ADDRESS_FAMILY sin6_family;
    USHORT         sin6_port;
    ULONG          sin6_flowinfo;
    IN6_ADDR       sin6_addr;
    union {
     ULONG    sin6_scope_id;
     SCOPE_ID sin6_scope_struct;
    };
   } SOCKADDR_IN6, *PSOCKADDR_IN6;




   这里的两个地址如果ss_family是AF_INET,AF_INET6, 和SOCKADDR_IN SOCKADDR_IN6相对应。这个例子是个非常简单的例子,功能很简单,只是对文件名的检查和ECP的检查。后续还可以看下复杂的例子。

原创粉丝点击