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的检查。后续还可以看下复杂的例子。
- Windows驱动_文件系统微小过滤驱动之五MiniFilter例程解析
- Windows驱动_文件系统微小过滤驱动之一初识MiniFilter
- Windows驱动_文件系统微小过滤驱动之三微小过滤驱动的操作
- Windows驱动_文件系统微小过滤驱动之二驱动的安装和加载
- Windows驱动_文件系统微小过滤驱动之四上下文空间
- Windows驱动_文件系统过滤驱动之五
- 文件系统Minifilter驱动(五)
- 文件系统Minifilter驱动(五)
- 文件系统Minifilter驱动(五)
- Windows驱动_文件系统过滤驱动之二
- Windows驱动_文件系统过滤驱动之三
- Windows驱动_文件系统过滤驱动之四
- Windows驱动_文件系统过滤驱动之六
- Windows驱动_文件系统过滤驱动之七
- Windows驱动_文件系统过滤驱动之八
- Windows驱动_文件系统过滤驱动之九
- Windows驱动_文件系统过滤驱动之十
- Windows驱动_文件系统过滤驱动之一
- Proguard 使用
- 不用第三个变量就能交换两个变量值的五个方法
- Python 编程注意事项
- 理解make的解析行为
- 最近工作笔试 没做出来的。。。
- Windows驱动_文件系统微小过滤驱动之五MiniFilter例程解析
- android提高——自定义控件(button)
- Octopress博客搭建及目录结构
- JSP-JSTL连接数据库
- Using UCI on Ubuntu
- C++第一坑:当cin读取整数时,将回车键生成的换行符留在了输入队列中
- C++基础-声明和定义
- 推荐一款优秀的代码编辑软件--Source Insight
- Delphi