//--------------------------------------------------------------- FLT_PREOP_CALLBACK_STATUS FltPreCreate(IN OUT PFLT_CALLBACK_DATA Data, IN PCFLT_RELATED_OBJECTS FltObjects, OUT PVOID *CompletionContext) { char tProcessName[512] = {0}; PEPROCESS tCurProc = PsGetCurrentProcess(); char* tNamePtr = (char*)tCurProc + ProcessNameOffset; memcpy(tProcessName, tNamePtr, 15); tProcessName[15] = 0; KdPrint(("process name:%s\n",tProcessName)); NTSTATUS tRetStatus; char FileName[512] = {0}; PFLT_FILE_NAME_INFORMATION tNameInfo; PAGED_CODE(); tRetStatus = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &tNameInfo); if(NT_SUCCESS(tRetStatus)) { FltParseFileNameInformation(tNameInfo); KdPrint(("<Pre> file name: %wZ\n", &(tNameInfo->Name))); FltReleaseFileNameInformation(tNameInfo); } // return FLT_PREOP_COMPLETE; return FLT_PREOP_SUCCESS_WITH_CALLBACK; }
static FLT_PREOP_CALLBACK_STATUS on_pre_op (PFLT_CALLBACK_DATA data, PCFLT_RELATED_OBJECTS fltobj, PVOID *context) { struct event *event; if (ESCAPE_CONDITION) return FLT_PREOP_SUCCESS_NO_CALLBACK; event = event_buffer_start_add(); if (event == NULL) return FLT_PREOP_SUCCESS_NO_CALLBACK; /* We retrieve name information in "pre" for every operation except IRP_MJ_CREATE. * For IRP_MJ_CREATE, we do it in post. */ if (data->Iopb->MajorFunction != IRP_MJ_CREATE) { FLT_FILE_NAME_INFORMATION *name_info = NULL; if (FltGetFileNameInformation(data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, &name_info) != STATUS_SUCCESS) { event_buffer_cancel_add(event); return FLT_PREOP_SUCCESS_NO_CALLBACK; } event->path_length = MAX_PATH_SIZE - 1 < name_info->Name.Length / 2 ? MAX_PATH_SIZE - 1 : name_info->Name.Length / 2; RtlCopyMemory(event->path, name_info->Name.Buffer, event->path_length * 2); event->path[event->path_length] = 0; FltReleaseFileNameInformation(name_info); } event->time_pre = get_timestamp(); *context = event; return FLT_PREOP_SUCCESS_WITH_CALLBACK; }
NTSTATUS GetDirectoryName(PFLT_CALLBACK_DATA Data, PUNICODE_STRING directoryName) { PFLT_FILE_NAME_INFORMATION nameInfo; NTSTATUS status = FltGetFileNameInformation(Data, (FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT), &nameInfo); if (NT_SUCCESS(status)) { status = FltParseFileNameInformation(nameInfo); if (NT_SUCCESS(status)) { directoryName->Length = 0; directoryName->Buffer = (WCHAR*)ExAllocatePoolWithTag(PagedPool, nameInfo->Name.Length * sizeof(WCHAR), UNICODE_STRING_TAG); directoryName->MaximumLength = nameInfo->Name.Length; status = RtlUnicodeStringCopy(directoryName, &nameInfo->Name); } } if (nameInfo != NULL) { FltReleaseFileNameInformation(nameInfo); } return status; }
FLT_PREOP_CALLBACK_STATUS PRE_MJ_CREATE(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID *CompletionContext) { NTSTATUS status; PFLT_FILE_NAME_INFORMATION nameInfo = NULL; status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); if (!NT_SUCCESS(status)) { status = FltGetFileNameInformationUnsafe(FltObjects->FileObject, FltObjects->Instance, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); } if (nameInfo) { // block access if (wcsstr(nameInfo->Name.Buffer, L"your_file.exe") != NULL) { Data->IoStatus.Status = STATUS_NO_SUCH_FILE; Data->IoStatus.Information = 0; return FLT_PREOP_COMPLETE; } FltReleaseFileNameInformation(nameInfo); } return FLT_PREOP_SUCCESS_NO_CALLBACK; }
NTSTATUS UcaGetFileNameInformation(_In_ PFLT_CALLBACK_DATA Data, _Out_ PFLT_FILE_NAME_INFORMATION *FileNameInformation) { NTSTATUS Status; /* Get the file name info */ Status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, FileNameInformation); if (!NT_SUCCESS(Status)) { /* If we failed to get the normalized, the opened should succeed */ Status = FltGetFileNameInformation(Data, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, FileNameInformation); } /* Exit if we failed to get a filename */ if (!NT_SUCCESS(Status)) return Status; /* Fill in the rest of the structure */ Status = FltParseFileNameInformation(*FileNameInformation); if (!NT_SUCCESS(Status)) { FltReleaseFileNameInformation(*FileNameInformation); } return Status; }
FLT_PREOP_CALLBACK_STATUS FLTAPI DenyCreateFileRoutine( __inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __deref_out_opt PVOID *CompletionContext ) { NTSTATUS nsStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; PFLT_FILE_NAME_INFORMATION pFileName = NULL; UNREFERENCED_PARAMETER(FltObjects); UNREFERENCED_PARAMETER(CompletionContext); if ( Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION && Data->Iopb->Parameters.SetFileInformation.FileInformationClass == FileRenameInformation) { if (((FILE_RENAME_INFORMATION*)Data->Iopb->Parameters.SetFileInformation.InfoBuffer)->FileName != NULL) { UNICODE_STRING uszFileNtPath = {0x00}; PUNICODE_STRING puszStoreFilePath = NULL; uszFileNtPath.Buffer = ((FILE_RENAME_INFORMATION*)Data->Iopb->Parameters.SetFileInformation.InfoBuffer)->FileName; uszFileNtPath.Length = (USHORT)((FILE_RENAME_INFORMATION*)Data->Iopb->Parameters.SetFileInformation.InfoBuffer)->FileNameLength; uszFileNtPath.MaximumLength = uszFileNtPath.Length; nsStatus = BDKitVolumeNameToDosName (&uszFileNtPath, &puszStoreFilePath); if ( NT_SUCCESS(nsStatus) && BDKitFindListNodeWithHandler (g_DenyFileHashList, puszStoreFilePath, findDenyFilePath) != NULL && !NT_SUCCESS(BDKitIsAuthorizedProcess(PsGetCurrentProcess()))) { Data->IoStatus.Status = STATUS_ACCESS_DENIED; Data->IoStatus.Information = 0; nsStatus = FLT_PREOP_COMPLETE; } BDKitFreePool(puszStoreFilePath); } } else if ( Data->Iopb->MajorFunction == IRP_MJ_CREATE ) { do { nsStatus = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED, &pFileName); BDKit_If_Not_Break(NT_SUCCESS(nsStatus)); if ( BDKitFindListNodeWithHandler(g_DenyFileHashList, &pFileName->Name, findDenyFilePath) != NULL ) { Data->IoStatus.Status = STATUS_ACCESS_DENIED; Data->IoStatus.Information = 0; nsStatus = FLT_PREOP_COMPLETE; } } while (FALSE); BDKit_If_DoAction(pFileName != NULL, FltReleaseFileNameInformation (pFileName)); } else if ( Data->Iopb->MajorFunction == IRP_MJ_UNREGISTER) { BDKitCloseListHandleWithHandler (g_DenyFileHashList, freeDenyListNode); g_DenyFileHashList = NULL; } return nsStatus; }
VOID FLTAPI UcaStreamContextCleanupCallback(_In_ PUCA_STREAM_CONTEXT StreamContext, _In_ FLT_CONTEXT_TYPE ContextType) { PAGED_CODE(); //__debugbreak(); FLT_ASSERT(ContextType == FLT_STREAM_CONTEXT || ContextType == FLT_FILE_CONTEXT); if (StreamContext->FileNameInfo) { FltReleaseFileNameInformation(StreamContext->FileNameInfo); StreamContext->FileNameInfo = NULL; } }
FLT_PREOP_CALLBACK_STATUS precreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID *CompletionContext) { UNREFERENCED_PARAMETER(Data); UNREFERENCED_PARAMETER(FltObjects); UNREFERENCED_PARAMETER(CompletionContext); PFLT_FILE_NAME_INFORMATION info; PAGED_CODE(); __try{ FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &info); FltParseFileNameInformation(info); DbgPrint("检测到目标打开文件:%wZ\n", info->Name); FltReleaseFileNameInformation(info); } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint("error cant execute the try\n"); } return FLT_PREOP_SUCCESS_WITH_CALLBACK; }
//--------------------------------------------------------------- FLT_POSTOP_CALLBACK_STATUS FltPostCreate( __inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __in_opt PVOID CompletionContext, __in FLT_POST_OPERATION_FLAGS Flags ) { NTSTATUS tRetStatus; char FileName[512] = {0}; PFLT_FILE_NAME_INFORMATION tNameInfo; PAGED_CODE(); tRetStatus = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &tNameInfo); if(NT_SUCCESS(tRetStatus)) { FltParseFileNameInformation(tNameInfo); KdPrint(("<post> file name: %wZ\n", &(tNameInfo->Name))); FltReleaseFileNameInformation(tNameInfo); } return FLT_POSTOP_FINISHED_PROCESSING; }
FLT_PREOP_CALLBACK_STATUS NPPreCreate ( __inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __deref_out_opt PVOID *CompletionContext ) { NTSTATUS status; PFLT_FILE_NAME_INFORMATION nameInfo; ANSI_STRING ansiStr; UNREFERENCED_PARAMETER( FltObjects ); UNREFERENCED_PARAMETER( CompletionContext ); PAGED_CODE(); __try { status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo ); if ( !NT_SUCCESS( status ) ) leave; status = FltParseFileNameInformation( nameInfo ); if ( !NT_SUCCESS( status ) ) leave; RtlUnicodeStringToAnsiString(&ansiStr, &nameInfo->Name, TRUE); PT_DBG_PRINT( PTDBG_TRACE_OPERATION_STATUS, ("minifilter0!NPPretCreate: create [%Z]\n", &ansiStr) ); RtlFreeAnsiString( &ansiStr ); FltReleaseFileNameInformation( nameInfo ); } __except(EXCEPTION_EXECUTE_HANDLER) { DbgPrint("NPPreCreate EXCEPTION_EXECUTE_HANDLER\n"); } return FLT_PREOP_SUCCESS_WITH_CALLBACK; }
NTSTATUS NcGenerateFileName ( _In_ PFLT_INSTANCE Instance, _In_ PFILE_OBJECT FileObject, _In_opt_ PFLT_CALLBACK_DATA Data, _In_ FLT_FILE_NAME_OPTIONS NameOptions, _Out_ PBOOLEAN CacheFileNameInformation, _Inout_ PFLT_NAME_CONTROL OutputNameControl ) { // // Status vars // NTSTATUS Status; // // State lookup vars // BOOLEAN Opened = (BOOLEAN)(FileObject->FsContext != NULL); // True if file object is opened. BOOLEAN ReturnShortName = (BOOLEAN)(FltGetFileNameFormat(NameOptions) == FLT_FILE_NAME_SHORT); // True if the user is requesting short name BOOLEAN ReturnOpenedName = (BOOLEAN)(FltGetFileNameFormat(NameOptions) == FLT_FILE_NAME_OPENED); // True if user is requesting opened name. BOOLEAN ReturnNormalizedName = (BOOLEAN)(FltGetFileNameFormat(NameOptions) == FLT_FILE_NAME_NORMALIZED); // True if user is requesting normalized name. BOOLEAN IgnoreCase; FLT_FILE_NAME_OPTIONS NameQueryMethod = FltGetFileNameQueryMethod( NameOptions ); FLT_FILE_NAME_OPTIONS NameFlags = FLT_VALID_FILE_NAME_FLAGS & NameOptions; // // File name information // PFLT_FILE_NAME_INFORMATION LowerNameInfo = NULL; // File name as reported by lower name provider. Will always be down real mapping. PFLT_FILE_NAME_INFORMATION ShortInfo = NULL; // We will use ShortInfo to store the short name if needed. // // Contexts // PNC_INSTANCE_CONTEXT InstanceContext = NULL; // // Overlap // NC_PATH_OVERLAP RealOverlap; UNICODE_STRING RealRemainder = EMPTY_UNICODE_STRING; // // Temp storage // UNICODE_STRING MungedName = EMPTY_UNICODE_STRING; // // Temp pointer // PUNICODE_STRING Name = NULL; // Pointer to the name we are going to use. PAGED_CODE(); FLT_ASSERT( IoGetTopLevelIrp() == NULL ); // // This should never happen, but let's be safe. // if (!ReturnShortName && !ReturnOpenedName && !ReturnNormalizedName) { FLT_ASSERT( FALSE ); Status = STATUS_NOT_SUPPORTED; goto NcGenerateFileNameCleanup; } RealOverlap.EntireFlags = 0; // // To prevent infinite recursion, calls to FltGetFileNameInformation // from generate file name callbacks should not target current provider. // ClearFlag( NameFlags, FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER ); // // Fetch the instance context. // Status = FltGetInstanceContext( Instance, &InstanceContext ); if (!NT_SUCCESS( Status )) { goto NcGenerateFileNameCleanup; } // // We need to know what the name provider under us thinks the file is called. // If the caller wants the normalized name we query that, otherwise we query // the opened name because we have to compare the full path of the file vs. // the real mapping to determine if the file is mapped. // Status = NcGetFileNameInformation( Data, FileObject, Instance, (ReturnNormalizedName ? FLT_FILE_NAME_NORMALIZED : FLT_FILE_NAME_OPENED) | NameQueryMethod | NameFlags, &LowerNameInfo ); if (!NT_SUCCESS( Status )) { goto NcGenerateFileNameCleanup; } Status = FltParseFileNameInformation( LowerNameInfo ); if (!NT_SUCCESS( Status )) { goto NcGenerateFileNameCleanup; } // // Issues With Pre-open path: // // 1) Poison name cache below name provider: // If a filter above a name provider calls FltGetFileNameInformation on a // file object in his precreate callback, fltmgr will call the name // provider's generate name callback before the name provider's pre create // callback is invoked. Name providers by their nature change names in their // pre-create. Because the name provider has not had the opportunity to // modify the name yet, we need to make sure that fltmgr does not cache the name we // return below us, so we set the FLT_FILE_NAME_DO_NOT_CACHE flag. // //TODO: TRY TO GET ACROSS THAT THIS IS A NAME CHANGER PROBLEM, NOT ALL NAME PROVIDERS NEED TO. // if (!Opened) { SetFlag( NameFlags, FLT_FILE_NAME_DO_NOT_CACHE ); if (Data) { // // NT supports case sensitive and non-case sensitive naming in file systems. // This is handled on a per-open basis. Weather an open is case senstive is // determined by the FO_OPENED_CASE_SENSITIVE flag on the file object. // In pre-create the SL_CASE_SENSITIVE flag on the create IRP specifies the mode. // // If this is on an unopened FileObject, it had better be pre-create so we know // how to process the operation. If we are queried on an unopened FileObject // at any other time we have no way to handle the request. // FLT_ASSERT( Data->Iopb->MajorFunction == IRP_MJ_CREATE || Data->Iopb->MajorFunction == IRP_MJ_NETWORK_QUERY_OPEN ); IgnoreCase = !BooleanFlagOn( Data->Iopb->OperationFlags, SL_CASE_SENSITIVE ); } else { // // If people do unsafe queries on preopened IOs, we cannot // determine if the open is case sensitive or not. // So we cannot determine if this open is down the mapping. // fail. // FLT_ASSERT( FALSE ); Status = STATUS_INVALID_PARAMETER; goto NcGenerateFileNameCleanup; } } else { // // After a file has been opened, the case sensitivity is stored in the file object. // IgnoreCase = !BooleanFlagOn( FileObject->Flags, FO_OPENED_CASE_SENSITIVE ); } // // Calculate the overlap with the real mapping. // NcComparePath( &LowerNameInfo->Name, &InstanceContext->Mapping.RealMapping, &RealRemainder, IgnoreCase, TRUE, &RealOverlap ); // // Whether we munge depends on what name is requested. // if (ReturnOpenedName || ReturnNormalizedName) { if (Opened && RealOverlap.InMapping) { // // We munge the opened name if it overlaps with the real mapping. // The returned path will be down the user mapping. // Status = NcConstructPath( &InstanceContext->Mapping.UserMapping, &RealRemainder, TRUE, &MungedName); if (!NT_SUCCESS( Status )) { goto NcGenerateFileNameCleanup; } Name = &MungedName; } else { // // We return the queried result if the path is not in the // mapping. // Name = &LowerNameInfo->Name; } } else if (ReturnShortName) { // // Note that unlike opened names, a query for a shortname only returns // the final component, not the full path. // // TODO: Assert not preopen if (RealOverlap.Match) { // // The opened path is the mapping path. // This means that if we queried the filesystem // he would return the wrong path. // // Luckily, we can just use the mapping. // Name = &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName; } else { // // We have to query below us to get the short name. // Status = NcGetFileNameInformation( Data, FileObject, Instance, FLT_FILE_NAME_SHORT | NameQueryMethod | NameFlags, &ShortInfo ); if (!NT_SUCCESS( Status )) { goto NcGenerateFileNameCleanup; } Status = FltParseFileNameInformation( ShortInfo ); if (!NT_SUCCESS(Status)) { goto NcGenerateFileNameCleanup; } // // Set name to returned name. // Name = &ShortInfo->Name; } } FLT_ASSERT( Name != NULL ); // // Try to grow the namechanger's record to accommodate the result. // Status = FltCheckAndGrowNameControl( OutputNameControl, Name->Length ); if (NT_SUCCESS( Status )) { // // Copy the new name into the buffer. // RtlCopyUnicodeString( &OutputNameControl->Name, Name ); *CacheFileNameInformation = TRUE; } NcGenerateFileNameCleanup: if (LowerNameInfo != NULL) { FltReleaseFileNameInformation( LowerNameInfo ); } if (ShortInfo != NULL) { FltReleaseFileNameInformation( ShortInfo ); } if (InstanceContext != NULL) { FltReleaseContext( InstanceContext ); } if (MungedName.Buffer != NULL) { ExFreePoolWithTag( MungedName.Buffer, NC_GENERATE_NAME_TAG ); } return Status; }
FLT_POSTOP_CALLBACK_STATUS CtxPostCreate ( _Inout_ PFLT_CALLBACK_DATA Cbd, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Inout_opt_ PVOID CbdContext, _In_ FLT_POST_OPERATION_FLAGS Flags ) { PCTX_FILE_CONTEXT fileContext = NULL; PCTX_STREAM_CONTEXT streamContext = NULL; PCTX_STREAMHANDLE_CONTEXT streamHandleContext = NULL; PFLT_FILE_NAME_INFORMATION nameInfo = NULL; UNICODE_STRING fileName; NTSTATUS status; BOOLEAN fileContextCreated, streamContextCreated, streamHandleContextReplaced; UNREFERENCED_PARAMETER( FltObjects ); UNREFERENCED_PARAMETER( Flags ); UNREFERENCED_PARAMETER( CbdContext ); PAGED_CODE(); DebugTrace( DEBUG_TRACE_ALL_IO, ("[Ctx]: CtxPostCreate -> Enter (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); // // Initialize defaults // status = STATUS_SUCCESS; // // If the Create has failed, do nothing // if (!NT_SUCCESS( Cbd->IoStatus.Status )) { goto CtxPostCreateCleanup; } // // Get the file name // status = FltGetFileNameInformation( Cbd, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo ); if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Failed to get name information (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); goto CtxPostCreateCleanup; } // // Find or create a stream context // status = CtxFindOrCreateStreamContext(Cbd, TRUE, &streamContext, &streamContextCreated); if (!NT_SUCCESS( status )) { // // This failure will most likely be because stream contexts are not supported // on the object we are trying to assign a context to or the object is being // deleted // DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Failed to find or create stream context (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); goto CtxPostCreateCleanup; } DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Getting/Creating stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p. StreamContextCreated = %x)\n", &nameInfo->Name, Cbd, FltObjects->FileObject, streamContext, streamContextCreated) ); // // Acquire write acccess to the context // CtxAcquireResourceExclusive(streamContext->Resource); // // Increment the create count // streamContext->CreateCount++; // // Update the file name in the context // status = CtxUpdateNameInStreamContext( &nameInfo->Name, streamContext); DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Stream context info for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", &nameInfo->Name, Cbd, FltObjects->FileObject, streamContext, &streamContext->FileName, streamContext->CreateCount, streamContext->CleanupCount, streamContext->CloseCount) ); // // Relinquish write acccess to the context // CtxReleaseResource(streamContext->Resource); // // Quit on failure after we have given up // the resource // if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Failed to update name in stream context for file %wZ (Cbd = %p, FileObject = %p)\n", &nameInfo->Name, Cbd, FltObjects->FileObject) ); goto CtxPostCreateCleanup; } // // Create or replace a stream handle context // status = CtxCreateOrReplaceStreamHandleContext(Cbd, TRUE, &streamHandleContext, &streamHandleContextReplaced); if (!NT_SUCCESS( status )) { // // This failure will most likely be because stream contexts are not supported // on the object we are trying to assign a context to or the object is being // deleted // DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Failed to find or create stream handle context (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); goto CtxPostCreateCleanup; } DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Creating/Replacing stream handle context for file %wZ (Cbd = %p, FileObject = %p StreamHandleContext = %p, StreamHandleContextReplaced = %x)\n", &nameInfo->Name, Cbd, FltObjects->FileObject, streamHandleContext, streamHandleContextReplaced) ); // // Acquire write acccess to the context // CtxAcquireResourceExclusive( streamHandleContext->Resource ); // // Update the file name in the context // status = CtxUpdateNameInStreamHandleContext( &nameInfo->Name, streamHandleContext); DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Stream handle context info for file %wZ (Cbd = %p, FileObject = %p, StreamHandleContext = %p) \n\tName = %wZ\n", &nameInfo->Name, Cbd, FltObjects->FileObject, streamHandleContext, &streamHandleContext->FileName) ); // // Relinquish write acccess to the context // CtxReleaseResource(streamHandleContext->Resource); // // Quit on failure after we have given up // the resource // if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Failed to update name in stream handle context for file %wZ (Cbd = %p, FileObject = %p)\n", &nameInfo->Name, Cbd, FltObjects->FileObject) ); goto CtxPostCreateCleanup; } // // After FltParseFileNameInformation, nameInfo->Name also // contains the stream name. We need only the filename and do // not want to include the stream name in the file context // fileName.Buffer = nameInfo->Name.Buffer; fileName.Length = nameInfo->Name.Length - nameInfo->Stream.Length; fileName.MaximumLength = fileName.Length; // // Find or create a file context // status = CtxFindOrCreateFileContext( Cbd, TRUE, &fileName, &fileContext, &fileContextCreated); if (!NT_SUCCESS( status )) { // // This failure will most likely be because file contexts are not supported // on the object we are trying to assign a context to or the object is being // deleted // DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Failed to find or create file context (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); goto CtxPostCreateCleanup; } DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> Getting/Creating file context for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p. FileContextCreated = %x)\n", &nameInfo->Name, Cbd, FltObjects->FileObject, fileContext, fileContextCreated) ); DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostCreate -> File context info for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p) \n\tName = %wZ\n", &nameInfo->Name, Cbd, FltObjects->FileObject, fileContext, &fileContext->FileName) ); CtxPostCreateCleanup: // // Release the references we have acquired // if (nameInfo != NULL) { FltReleaseFileNameInformation( nameInfo ); } if (fileContext != NULL) { FltReleaseContext( fileContext ); } if (streamContext != NULL) { FltReleaseContext( streamContext ); } if (streamHandleContext != NULL) { FltReleaseContext( streamHandleContext ); } if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_ERROR, ("[Ctx]: CtxPostCreate -> Failed with status 0x%x \n", status) ); // // It doesn't make sense to udate Cbd->IoStatus.Status on failure since the // file system has successfully completed the operation // } DebugTrace( DEBUG_TRACE_ALL_IO, ("[Ctx]: CtxPostCreate -> Exit (Cbd = %p, FileObject = %p, Status = 0x%x)\n", Cbd, FltObjects->FileObject, Cbd->IoStatus.Status) ); return FLT_POSTOP_FINISHED_PROCESSING; }
FLT_POSTOP_CALLBACK_STATUS CtxPostSetInfo ( _Inout_ PFLT_CALLBACK_DATA Cbd, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Inout_opt_ PVOID CbdContext, _In_ FLT_POST_OPERATION_FLAGS Flags ) { PCTX_INSTANCE_CONTEXT instanceContext = NULL; PCTX_FILE_CONTEXT fileContext = NULL; PCTX_STREAM_CONTEXT streamContext = NULL; PCTX_STREAMHANDLE_CONTEXT streamHandleContext = NULL; PFLT_FILE_NAME_INFORMATION nameInfo = NULL; NTSTATUS status; BOOLEAN streamContextCreated, fileContextCreated, streamHandleContextReplaced; UNREFERENCED_PARAMETER( Flags ); UNREFERENCED_PARAMETER( FltObjects ); UNREFERENCED_PARAMETER( CbdContext ); // // The pre-operation callback will return FLT_PREOP_SYNCHRONIZE if it needs a // post operation callback. In this case, the Filter Manager will call the // minifilter's post-operation callback in the context of the pre-operation // thread, at IRQL <= APC_LEVEL. This allows the post-operation code to be // pagable and also allows it to access paged data // PAGED_CODE(); DebugTrace( DEBUG_TRACE_ALL_IO, ("[Ctx]: CtxPostSetInfo -> Enter (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); // // Initialize defaults // status = STATUS_SUCCESS; // // If the SetInfo has failed, do nothing // if (!NT_SUCCESS( Cbd->IoStatus.Status )) { goto CtxPostSetInfoCleanup; } // // Get the instance context for the target instance // DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Trying to get instance context (TargetInstance = %p, Cbd = %p, FileObject = %p)\n", Cbd->Iopb->TargetInstance, Cbd, FltObjects->FileObject) ); status = FltGetInstanceContext( Cbd->Iopb->TargetInstance, &instanceContext ); if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, ("[Ctx]: CtxPostSetInfo -> Failed to get instance context (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); goto CtxPostSetInfoCleanup; } DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Instance context info for volume %wZ (Cbd = %p, FileObject = %p, InstanceContext = %p) \n\tVolumeName = %wZ \n\tInstance = %p \n\tVolume = %p\n", &instanceContext->VolumeName, Cbd, FltObjects->FileObject, instanceContext, &instanceContext->VolumeName, &instanceContext->Instance, &instanceContext->Volume) ); // // Get the directory name // status = FltGetFileNameInformation( Cbd, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo ); if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Failed to get file name information (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); goto CtxPostSetInfoCleanup; } // // Get the stream context // status = CtxFindOrCreateStreamContext(Cbd, FALSE, // do not create if one does not exist &streamContext, &streamContextCreated); if (!NT_SUCCESS( status )) { // // This failure will most likely be because stream contexts are not supported // on the object we are trying to assign a context to or the object is being // deleted // DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Failed to find stream context (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); goto CtxPostSetInfoCleanup; } DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Getting stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p. StreamContextCreated = %x)\n", &nameInfo->Name, Cbd, FltObjects->FileObject, streamContext, streamContextCreated) ); // // Acquire write acccess to the context // CtxAcquireResourceExclusive(streamContext->Resource); DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Old info in stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", &nameInfo->Name, Cbd, FltObjects->FileObject, streamContext, &streamContext->FileName, streamContext->CreateCount, streamContext->CleanupCount, streamContext->CloseCount) ); // // Update the file name in the context // status = CtxUpdateNameInStreamContext( &nameInfo->Name, streamContext); DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> New info in stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", &nameInfo->Name, Cbd, FltObjects->FileObject, streamContext, &streamContext->FileName, streamContext->CreateCount, streamContext->CleanupCount, streamContext->CloseCount) ); // // Relinquish write acccess to the context // CtxReleaseResource(streamContext->Resource); // // Quit on failure after we have given up // the resource // if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Failed to update name in stream context for file %wZ (Cbd = %p, FileObject = %p)\n", &nameInfo->Name, Cbd, FltObjects->FileObject) ); goto CtxPostSetInfoCleanup; } // // Create or replace a stream handle context // status = CtxCreateOrReplaceStreamHandleContext(Cbd, TRUE, &streamHandleContext, &streamHandleContextReplaced); if (!NT_SUCCESS( status )) { // // This failure will most likely be because stream contexts are not supported // on the object we are trying to assign a context to or the object is being // deleted // DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Failed to find or create stream handle context (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); goto CtxPostSetInfoCleanup; } DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Creating/Replacing stream handle context for file %wZ (Cbd = %p, FileObject = %p StreamHandleContext = %p, StreamHandleContextReplaced = %x)\n", &nameInfo->Name, Cbd, FltObjects->FileObject, streamHandleContext, streamHandleContextReplaced) ); // // Acquire write acccess to the context // CtxAcquireResourceExclusive(streamHandleContext->Resource); // // Update the file name in the context // status = CtxUpdateNameInStreamHandleContext( &nameInfo->Name, streamHandleContext); DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Stream handle context info for file %wZ (Cbd = %p, FileObject = %p, StreamHandleContext = %p) \n\tName = %wZ\n", &nameInfo->Name, Cbd, FltObjects->FileObject, streamHandleContext, &streamHandleContext->FileName) ); // // Relinquish write acccess to the context // CtxReleaseResource( streamHandleContext->Resource ); // // Quit on failure after we have given up // the resource // if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Failed to update name in stream handle context for file %wZ (Cbd = %p, FileObject = %p)\n", &nameInfo->Name, Cbd, FltObjects->FileObject) ); goto CtxPostSetInfoCleanup; } // // Get the file context // status = CtxFindOrCreateFileContext( Cbd, FALSE, // do not create if one does not exist NULL, &fileContext, &fileContextCreated); if (!NT_SUCCESS( status )) { // // This failure will most likely be because file contexts are not supported // on the object we are trying to assign a context to or the object is being // deleted // DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Failed to find file context (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); goto CtxPostSetInfoCleanup; } DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> Getting file context for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p. FileContextCreated = %x)\n", &nameInfo->Name, Cbd, FltObjects->FileObject, fileContext, fileContextCreated) ); DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, ("[Ctx]: CtxPostSetInfo -> File context info for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p) \n\tName = %wZ\n", &nameInfo->Name, Cbd, FltObjects->FileObject, fileContext, &fileContext->FileName) ); CtxPostSetInfoCleanup: // // Release the references we have acquired // if (instanceContext != NULL) { FltReleaseContext( instanceContext ); } if (fileContext != NULL) { FltReleaseContext( fileContext ); } if (streamContext != NULL) { FltReleaseContext( streamContext ); } if (streamHandleContext != NULL) { FltReleaseContext( streamHandleContext ); } if (nameInfo != NULL) { FltReleaseFileNameInformation( nameInfo ); } if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_ERROR, ("[Ctx]: CtxPostSetInfo -> Failed with status 0x%x \n", status) ); // // It doesn't make sense to udate Cbd->IoStatus.Status on failure since the // file system has suceesfully completed the operation // } DebugTrace( DEBUG_TRACE_ALL_IO, ("[Ctx]: CtxPostSetInfo -> Exit (Cbd = %p, FileObject = %p)\n", Cbd, FltObjects->FileObject) ); return FLT_POSTOP_FINISHED_PROCESSING; }
EXTERN_C static NTSTATUS ScvnpReadFile(_In_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Out_ void *Buffer, _In_ ULONG BufferSize) { PAGED_CODE(); // Use an existing file object when it is NOT IRP_MJ_CLEANUP. if (Data->Iopb->MajorFunction != IRP_MJ_CLEANUP) { LARGE_INTEGER byteOffset = {}; auto status = FltReadFile(FltObjects->Instance, FltObjects->FileObject, &byteOffset, BufferSize, Buffer, FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET, nullptr, nullptr, nullptr); if (!NT_SUCCESS(status)) { LOG_ERROR_SAFE("FltReadFile failed (%08x)", status); return status; } return status; } PFILE_OBJECT fileObject = nullptr; // Make a new file object since the file is already out of the current IO // path. PFLT_FILE_NAME_INFORMATION fileNameInformation = nullptr; auto status = FltGetFileNameInformationUnsafe( FltObjects->FileObject, FltObjects->Instance, FLT_FILE_NAME_NORMALIZED, &fileNameInformation); if (!NT_SUCCESS(status)) { return status; } OBJECT_ATTRIBUTES objAttr = RTL_INIT_OBJECT_ATTRIBUTES( &fileNameInformation->Name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE); HANDLE fileHandle = nullptr; IO_STATUS_BLOCK ioStatus = {}; status = FltCreateFile( FltObjects->Filter, FltObjects->Instance, &fileHandle, GENERIC_READ, &objAttr, &ioStatus, nullptr, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN_IF, FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, nullptr, 0, 0); if (!NT_SUCCESS(status)) { LOG_ERROR_SAFE("FltCreateFile failed (%08x) for %wZ", status, &fileNameInformation->Name); goto End; } status = ObReferenceObjectByHandle(fileHandle, 0, nullptr, KernelMode, reinterpret_cast<void **>(&fileObject), nullptr); if (!NT_SUCCESS(status)) { LOG_ERROR_SAFE("ObReferenceObjectByHandle failed (%08x) for %wZ", status, &fileNameInformation->Name); goto End; } status = FltReadFile(FltObjects->Instance, fileObject, nullptr, BufferSize, Buffer, 0, nullptr, nullptr, nullptr); if (!NT_SUCCESS(status)) { LOG_ERROR_SAFE("FltReadFile failed (%08x) for %wZ", status, &fileNameInformation->Name); goto End; } End: if (fileObject) { ObDereferenceObject(fileObject); } if (fileHandle) { FltClose(fileHandle); } if (fileNameInformation) { FltReleaseFileNameInformation(fileNameInformation); } return status; }
/* * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ** * * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ** */ FLT_POSTOP_CALLBACK_STATUS PostSetInformation( IN OUT PFLT_CALLBACK_DATA Data, IN PCFLT_RELATED_OBJECTS FltObjects, IN PVOID CompletionContext, IN FLT_POST_OPERATION_FLAGS Flags ) { PFLT_FILE_NAME_INFORMATION FNameInfo; NTSTATUS Status; FILE_RENAME_INFORMATION FRI; // If we have an error then just exit if ( !NT_SUCCESS( Data->IoStatus.Status ) || ( STATUS_REPARSE == Data->IoStatus.Status ) ) { return FLT_POSTOP_FINISHED_PROCESSING; } Status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &FNameInfo ); // If we could not get the name information then exit if (!NT_SUCCESS( Status )) { return FLT_POSTOP_FINISHED_PROCESSING; } FltParseFileNameInformation( FNameInfo ); switch ( Data->Iopb->Parameters.SetFileInformation.FileInformationClass ) { case FileRenameInformation: { DBG_PRINT( DbgOutput, DBG_IRP_MJ_SET_INFORMATION, (PRINT_TAG_SETINFO "PostSetInfo()-RENAME [%wZ] \n", &FNameInfo->Name ) ); break; } case FileAllocationInformation: { DBG_PRINT( DbgOutput, DBG_IRP_MJ_SET_INFORMATION, (PRINT_TAG_SETINFO "PostSetInfo()-ALLOCATION [%wZ] \n", &FNameInfo->Name ) ); break; } case FileBasicInformation: { DBG_PRINT( DbgOutput, DBG_IRP_MJ_SET_INFORMATION, (PRINT_TAG_SETINFO "PostSetInfo()-BASIC[%wZ] \n", &FNameInfo->Name ) ); break; } case FileDispositionInformation: { DBG_PRINT( DbgOutput, DBG_IRP_MJ_SET_INFORMATION, (PRINT_TAG_SETINFO "PostSetInfo()-DISPOSITION [%wZ] \n", &FNameInfo->Name ) ); break; } case FileEndOfFileInformation: { DBG_PRINT( DbgOutput, DBG_IRP_MJ_SET_INFORMATION, (PRINT_TAG_SETINFO "PostSetInfo()-EOF [%wZ] \n", &FNameInfo->Name ) ); break; } case FileLinkInformation: { DBG_PRINT( DbgOutput, DBG_IRP_MJ_SET_INFORMATION, (PRINT_TAG_SETINFO "PostSetInfo()-LINK [%wZ] \n", &FNameInfo->Name ) ); break; } case FilePositionInformation: { DBG_PRINT( DbgOutput, DBG_IRP_MJ_SET_INFORMATION, (PRINT_TAG_SETINFO "PostSetInfo()-POSITION [%wZ] \n", &FNameInfo->Name ) ); break; } case FileValidDataLengthInformation: { DBG_PRINT( DbgOutput, DBG_IRP_MJ_SET_INFORMATION, (PRINT_TAG_SETINFO "PostSetInfo()-VALIDATEDATALENGTH [%wZ] \n", &FNameInfo->Name ) ); break; } default : { DBG_PRINT( DbgOutput, DBG_IRP_MJ_SET_INFORMATION, (PRINT_TAG_SETINFO "BAD MOJO\n") ); break; } } // End switch ( Data->Iopb->Parameters.SetFileInformation.FileInformationClass ) // Free FltReleaseFileNameInformation( FNameInfo ); return FLT_POSTOP_FINISHED_PROCESSING; }
FLT_PREOP_CALLBACK_STATUS PreFileOperationCallback ( __inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __deref_out_opt PVOID *CompletionContext ) { NTSTATUS status; PFLT_FILE_NAME_INFORMATION pFileNameInformation; PFILE_EVENT pFileEvent; LARGE_INTEGER CurrentSystemTime; LARGE_INTEGER CurrentLocalTime; ULONG returnedLength; //HANDLE hThread; //HANDLE handle5; TIME_FIELDS TimeFields; BOOLEAN pathNameFound = FALSE; UNICODE_STRING filePath; FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; /* If this is a callback for a FS Filter driver then we ignore the event */ if(FLT_IS_FS_FILTER_OPERATION(Data)) { return FLT_PREOP_SUCCESS_NO_CALLBACK; } /* Allocate a large 64kb string ... maximum path name allowed in windows */ filePath.Length = 0; filePath.MaximumLength = NTSTRSAFE_UNICODE_STRING_MAX_CCH * sizeof(WCHAR); filePath.Buffer = ExAllocatePoolWithTag(NonPagedPool, filePath.MaximumLength, FILE_POOL_TAG); if(filePath.Buffer == NULL) { return FLT_PREOP_SUCCESS_NO_CALLBACK; } if (FltObjects->FileObject != NULL && Data != NULL) { status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, &pFileNameInformation ); if(NT_SUCCESS(status)) { if(pFileNameInformation->Name.Length > 0) { RtlUnicodeStringCopy(&filePath, &pFileNameInformation->Name); //RtlStringCbCopyUnicodeString(pFileEvent->filePath, 1024, &pFileNameInformation->Name); pathNameFound = TRUE; } /* Backup the file if it is marked for deletion */ CopyFileIfBeingDeleted (Data,FltObjects,pFileNameInformation); /* Release the file name information structure if it was used */ if(pFileNameInformation != NULL) { FltReleaseFileNameInformation(pFileNameInformation); } } else { NTSTATUS lstatus; PFLT_FILE_NAME_INFORMATION pLFileNameInformation; lstatus = FltGetFileNameInformation( Data, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, &pLFileNameInformation); if(NT_SUCCESS(lstatus)) { if(pLFileNameInformation->Name.Length > 0) { RtlUnicodeStringCopy(&filePath, &pLFileNameInformation->Name); //RtlStringCbCopyUnicodeString(pFileEvent->filePath, 1024, &pLFileNameInformation->Name); pathNameFound = TRUE; } /* Backup the file if it is marked for deletion */ CopyFileIfBeingDeleted (Data,FltObjects,pFileNameInformation); /* Release the file name information structure if it was used */ if(pLFileNameInformation != NULL) { FltReleaseFileNameInformation(pLFileNameInformation); } } } } /* If path name could not be found the file monitor uses the file name stored in the FileObject. The documentation says that we shouldn't use this info as it may not be correct. It does however allow us get some nice file events such as the system process writing to the $MFT file etc. Remove this code if problems start to occur */ if( (pathNameFound == FALSE) && (FltObjects->FileObject != NULL) && (FltObjects->FileObject->RelatedFileObject == NULL) && (FltObjects->FileObject->FileName.Length > 0)) { NTSTATUS status; ULONG size; UNICODE_STRING szTempPath; UNICODE_STRING szDevice; UNICODE_STRING szFileNameDevice; /* Check the FileObject->FileName isn't already a complete filepath */ szFileNameDevice.Length = FltObjects->FileObject->FileName.Length; szFileNameDevice.MaximumLength = FltObjects->FileObject->FileName.MaximumLength; szFileNameDevice.Buffer = ExAllocatePoolWithTag(NonPagedPool, szFileNameDevice.MaximumLength, FILE_POOL_TAG); RtlInitUnicodeString(&szDevice, L"\\Device"); if(FltObjects->FileObject->FileName.Length >= szDevice.Length) { RtlUnicodeStringCchCopyN(&szFileNameDevice, &FltObjects->FileObject->FileName, 7); } if(RtlEqualUnicodeString(&szDevice, &szFileNameDevice, TRUE)) { RtlUnicodeStringCopy(&filePath, &FltObjects->FileObject->FileName); pathNameFound = TRUE; } else { szTempPath.Length = 0; szTempPath.MaximumLength = FltObjects->FileObject->FileName.MaximumLength + 2; /* Get the volume name of where the event came from */ status = FltGetVolumeName( FltObjects->Volume, NULL, &size ); if(status == STATUS_BUFFER_TOO_SMALL) { szTempPath.MaximumLength += (USHORT)size; szTempPath.Buffer = ExAllocatePoolWithTag(NonPagedPool, szTempPath.MaximumLength, FILE_POOL_TAG); if(szTempPath.Buffer != NULL) { status = FltGetVolumeName( FltObjects->Volume, &szTempPath, &size ); if(NT_SUCCESS(status)) { /* Append the file event to the volume name */ RtlUnicodeStringCat(&szTempPath, &FltObjects->FileObject->FileName); RtlUnicodeStringCopy(&filePath, &szTempPath); pathNameFound = TRUE; } ExFreePoolWithTag(szTempPath.Buffer, FILE_POOL_TAG); } } } ExFreePoolWithTag(szFileNameDevice.Buffer, FILE_POOL_TAG); } if(!pathNameFound) { RtlUnicodeStringCatString(&filePath, L"UNKNOWN"); } /* Allocate file event and put the values into it */ /* NOTE this is freed in the post op callback (which should always get called) */ pFileEvent = ExAllocatePoolWithTag(NonPagedPool, sizeof(FILE_EVENT)+filePath.Length+sizeof(WCHAR), FILE_POOL_TAG); if(pFileEvent == NULL) { ExFreePoolWithTag(filePath.Buffer, FILE_POOL_TAG); return FLT_PREOP_SUCCESS_NO_CALLBACK; } /* Copy file path into file event */ pFileEvent->filePathLength = filePath.Length+sizeof(WCHAR); RtlStringCbCopyUnicodeString(pFileEvent->filePath, pFileEvent->filePathLength, &filePath); /* Free the allocated storage for a filepath */ ExFreePoolWithTag(filePath.Buffer, FILE_POOL_TAG); pFileEvent->majorFileEventType = Data->Iopb->MajorFunction; pFileEvent->minorFileEventType = Data->Iopb->MinorFunction; pFileEvent->processId = 0; if (FltObjects->FileObject != NULL) { pFileEvent->flags = FltObjects->FileObject->Flags; } if(Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION) { if(Data->Iopb->Parameters.SetFileInformation.FileInformationClass == FileDispositionInformation) { PFILE_DISPOSITION_INFORMATION pFileInfo = (PFILE_DISPOSITION_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer; /* If the file is marked for deletion back it up */ if(pFileInfo->DeleteFile) { pFileEvent->majorFileEventType = 0x99; } } } /* Get the process id of the file event */ /* NOTE we are kinda using an undocumented function here but its all available on the interweb. Plus its much better than accessing the PETHREAD structure which could change at any time. Also, this one is available to userspace programs so it should be safe to use. We have to use this function because a file I/O may either be processed in the context of the userspace program or the system context. This uses the thread data from FLT_CALLBACK_DATA to determine which process it actually came from. We default back to getting the current process id if all else fails. */ /* SECOND NOTE FltGetRequestorProcessId does not get the correct process id, it looks like it still get the proces id of the context the pre callback gets called in */ /* status = ObOpenObjectByPointer(Data->Thread, OBJ_KERNEL_HANDLE, NULL, 0, 0, KernelMode, &hThread); if(NT_SUCCESS(status)) { THREAD_BASIC_INFORMATION threadBasicInformation; status = ZwQueryInformationThread(hThread, ThreadBasicInformation, &threadBasicInformation, sizeof(THREAD_BASIC_INFORMATION), &returnedLength ); if(NT_SUCCESS(status)) { pFileEvent->processId = (HANDLE)threadBasicInformation.UniqueProcessId; handle5 = pFileEvent->processId; //DbgPrint("Process4: %i\n", pFileEvent->processId); } else { DbgPrint("ZwQueryInformationThread FAILED: %08x\n", status); } ZwClose(hThread); } else { DbgPrint("ObOpenObjectByPointer FAILED: %08x\n", status); } */ /* New safe get correct process id. One above causes blue screen in some cases */ if(Data->Thread != NULL) { PEPROCESS pProcess = IoThreadToProcess( Data->Thread ); pFileEvent->processId = PsGetProcessId(pProcess); } else { pFileEvent->processId = PsGetCurrentProcessId(); DbgPrint("CaptureFileMonitor: Process id may be incorrect\n"); } /* DbgPrint("%i [%i %i] %s %i %i (%i, %i, %i) %i %i %i %i %i : %ls\n", KeGetCurrentIrql(), PsIsSystemThread(Data->Thread), PsIsSystemThread(PsGetCurrentThread()), FltGetIrpName(Data->Iopb->MajorFunction), Data->Iopb->MajorFunction, Data->Iopb->MinorFunction, pFileEvent->processId, PsGetCurrentProcessId(), FltGetRequestorProcessId(Data), FLT_IS_FASTIO_OPERATION(Data), FLT_IS_FS_FILTER_OPERATION(Data), FLT_IS_IRP_OPERATION(Data), FLT_IS_REISSUED_IO(Data), FLT_IS_SYSTEM_BUFFER(Data), pFileEvent->filePath); */ //ASSERT(pFileEvent->processId != 0); /* Get the time this event occured */ KeQuerySystemTime(&CurrentSystemTime); ExSystemTimeToLocalTime(&CurrentSystemTime,&CurrentLocalTime); RtlTimeToTimeFields(&CurrentLocalTime,&TimeFields); pFileEvent->time = TimeFields; /* Pass the created file event to the post operation of this pre file operation */ if (Data->Iopb->MajorFunction == IRP_MJ_SHUTDOWN) { PostFileOperationCallback( Data, FltObjects, pFileEvent, 0 ); return FLT_PREOP_SUCCESS_NO_CALLBACK; } else { *CompletionContext = pFileEvent; return FLT_PREOP_SUCCESS_WITH_CALLBACK; } }
static FLT_POSTOP_CALLBACK_STATUS on_post_op (PFLT_CALLBACK_DATA data, PCFLT_RELATED_OBJECTS fltobj, struct event *event, FLT_POST_OPERATION_FLAGS flags) { if (event == NULL) return FLT_POSTOP_FINISHED_PROCESSING; event->time_post = get_timestamp(); event->status = data->IoStatus.Status; if (data->Iopb->MajorFunction == IRP_MJ_CREATE) { FLT_FILE_NAME_INFORMATION *name_info = NULL; if (FltGetFileNameInformation(data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, &name_info) != STATUS_SUCCESS) { event_buffer_cancel_add(event); return FLT_POSTOP_FINISHED_PROCESSING; } event->path_length = MAX_PATH_SIZE - 1 < name_info->Name.Length / 2 ? MAX_PATH_SIZE - 1 : name_info->Name.Length / 2; RtlCopyMemory(event->path, name_info->Name.Buffer, event->path_length * 2); event->path[event->path_length] = 0; FltReleaseFileNameInformation(name_info); } switch (data->Iopb->MajorFunction) { case IRP_MJ_CLOSE: event->type = ET_FILE_CLOSE; break; case IRP_MJ_CREATE: event->type = ET_FILE_CREATE; event->file_create.desired_access = data->Iopb->Parameters.Create.SecurityContext->DesiredAccess; event->file_create.share_mode = data->Iopb->Parameters.Create.ShareAccess; event->file_create.attributes = data->Iopb->Parameters.Create.FileAttributes; event->file_create.creation_disposition = data->Iopb->Parameters.Create.Options >> 24; event->file_create.create_options = data->Iopb->Parameters.Create.Options & 0x00ffffff; event->file_create.status_information = data->IoStatus.Information; break; case IRP_MJ_READ: event->type = ET_FILE_READ; event->file_rw.offset = data->Iopb->Parameters.Read.ByteOffset; event->file_rw.req_length = data->Iopb->Parameters.Read.Length; event->file_rw.ret_length = data->IoStatus.Information; break; case IRP_MJ_WRITE: event->type = ET_FILE_WRITE; event->file_rw.offset = data->Iopb->Parameters.Write.ByteOffset; event->file_rw.req_length = data->Iopb->Parameters.Write.Length; event->file_rw.ret_length = data->IoStatus.Information; break; case IRP_MJ_CREATE_MAILSLOT: event->type = ET_FILE_CREATE_MAILSLOT; break; case IRP_MJ_CREATE_NAMED_PIPE: event->type = ET_FILE_CREATE_NAMED_PIPE; break; case IRP_MJ_QUERY_INFORMATION: event->type = ET_FILE_QUERY_INFORMATION; if (data->IoStatus.Status == STATUS_SUCCESS || data->IoStatus.Status == STATUS_BUFFER_OVERFLOW) { event->file_info.info_type = data->Iopb->Parameters.QueryFileInformation.FileInformationClass; event->file_info.info_size = data->Iopb->Parameters.QueryFileInformation.Length < sizeof(event->file_info.info_data) ? data->Iopb->Parameters.QueryFileInformation.Length : sizeof(event->file_info.info_data); // NOTE: string inside the info structure is not zero terminated. RtlCopyMemory(&event->file_info.info_data, data->Iopb->Parameters.QueryFileInformation.InfoBuffer, event->file_info.info_size); } break; case IRP_MJ_SET_INFORMATION: event->type = ET_FILE_SET_INFORMATION; event->file_info.info_type = data->Iopb->Parameters.SetFileInformation.FileInformationClass; event->file_info.info_size = data->Iopb->Parameters.SetFileInformation.Length < sizeof(event->file_info.info_data) ? data->Iopb->Parameters.SetFileInformation.Length : sizeof(event->file_info.info_data); // NOTE: string inside the info structure is not zero terminated. RtlCopyMemory(&event->file_info.info_data, data->Iopb->Parameters.SetFileInformation.InfoBuffer, event->file_info.info_size); break; default: DbgPrint("resmon: unknown post MajorFunction %d\n", data->Iopb->MajorFunction); event_buffer_cancel_add(event); return FLT_POSTOP_FINISHED_PROCESSING; } event_buffer_finish_add(event); return FLT_POSTOP_FINISHED_PROCESSING; }
FLT_POSTOP_CALLBACK_STATUS claimsmanPostOperation( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_opt_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags ) /*++ Routine Description: This routine is the post-operation completion routine for this miniFilter. This is non-pageable because it may be called at DPC level. Arguments: Data - Pointer to the filter callbackData that is passed to us. FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing opaque handles to this filter, instance, its associated volume and file object. CompletionContext - The completion context set in the pre-operation routine. Flags - Denotes whether the completion is successful or is being drained. Return Value: The return value is the status of the operation. --*/ { UNREFERENCED_PARAMETER(Data); UNREFERENCED_PARAMETER(CompletionContext); UNREFERENCED_PARAMETER(Flags); NTSTATUS status; PFLT_FILE_NAME_INFORMATION nameInfo = NULL; PUNICODE_STRING fileName = NULL; PTOKEN_USER pTokenUser = NULL; UNICODE_STRING sidString; LARGE_INTEGER Timeout; Timeout.QuadPart = (LONGLONG)1 * 10 * 1000 * 1000; // If there is no client registered, bail out immediately! // If the event is from kernel, bail out immediately! // If the event check for existence of file, bail out immediately! if ( (ClaimsmanData.ClientPort == NULL) || (Data->RequestorMode == KernelMode) || (Data->IoStatus.Information == FILE_DOES_NOT_EXIST) || (Data->IoStatus.Information == FILE_EXISTS) ) { return FLT_POSTOP_FINISHED_PROCESSING; } // We got a log record, if there is a file object, get its name. if (FltObjects->FileObject != NULL) { status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); } else { // Can't get a name when there's no file object status = STATUS_UNSUCCESSFUL; } if (NT_SUCCESS(status)) { FltParseFileNameInformation(nameInfo); fileName = &nameInfo->Name; // Produces way too much logging //PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, // ("claimsman!claimsmanPostOperation: fileName=%wZ\n", fileName)); } else { // No point continuing because we obviously did not get a filename anyways return FLT_POSTOP_FINISHED_PROCESSING; } //The only IRP you can trust for user information is IRP_MJ_CREATE. Things //like write can be in arbitrary thread context, and even if the call works //you can get the wrong SID. status = SeQueryInformationToken(SeQuerySubjectContextToken(&(Data->Iopb->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext)), TokenUser, &pTokenUser); if (STATUS_SUCCESS == status && RtlValidSid(pTokenUser->User.Sid)) { // Interesting extension? if (ClaimsmanCheckExtension(&nameInfo->Extension)) { CLAIMSMAN_MESSAGE msg; status = RtlConvertSidToUnicodeString(&sidString, pTokenUser->User.Sid, TRUE); if (NT_SUCCESS(status)) { PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, ("claimsman!claimsmanPostOperation: SID=%wZ\n", &sidString)); } else { // No point continuing because we obviously did not get a valid SID FltReleaseFileNameInformation(nameInfo); ExFreePool(pTokenUser); return FLT_POSTOP_FINISHED_PROCESSING; } if (ClaimsmanCheckUserIgnore(&sidString)) { // Ignored user! Bail out! FltReleaseFileNameInformation(nameInfo); ExFreePool(pTokenUser); RtlFreeUnicodeString(&sidString); return FLT_POSTOP_FINISHED_PROCESSING; } LONGLONG size; LONGLONG modified; getSizeModified(FltObjects->Instance, fileName, &size, &modified); InitializeMessage(&msg, &sidString, fileName, FltObjects->FileObject->ReadAccess, FltObjects->FileObject->WriteAccess, FltObjects->FileObject->DeleteAccess, size, modified, Data->IoStatus.Status); // Ready, send the message! // But only if there's a client connected if (ClaimsmanData.ClientPort != NULL) { FltSendMessage(ClaimsmanData.Filter, &ClaimsmanData.ClientPort, &msg, sizeof(msg), NULL, 0, &Timeout ); PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, ("claimsman!claimsmanPostOperation: sent message=%d\n", status)); } else { PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, ("claimsman!claimsmanPostOperation: no client connected!")); } RtlFreeUnicodeString(&sidString); } } FltReleaseFileNameInformation(nameInfo); if (pTokenUser != NULL) { ExFreePool(pTokenUser); } return FLT_POSTOP_FINISHED_PROCESSING; }
FLT_PREOP_CALLBACK_STATUS NcEnumerateDirectory ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ) /*++ Routine Description: Routine is invoked when a directory enumeration is issued by the user. Arguments: Data - Pointer to the filter CallbackData that is passed to us. FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing opaque handles to this filter, instance, its associated volume and file object. CompletionContext - The context for the completion routine for this operation. Return Value: The return value is the Status of the operation. --*/ { //TODO WE SHOULD CONSIDER MOVING THIS TO POST BECAUSE NTFS WILL TAKE CARE // OF SYNC. FLT_PREOP_CALLBACK_STATUS ReturnValue; NTSTATUS Status; PNC_INSTANCE_CONTEXT InstanceContext = NULL; PNC_STREAM_HANDLE_CONTEXT HandleContext = NULL; PNC_DIR_QRY_CONTEXT DirCtx = NULL; PFLT_FILE_NAME_INFORMATION FileNameInformation = NULL; NC_PATH_OVERLAP RealOverlap; NC_PATH_OVERLAP UserOverlap; BOOLEAN Reset = BooleanFlagOn( Data->Iopb->OperationFlags, SL_RESTART_SCAN ); BOOLEAN FirstQuery; BOOLEAN Single = BooleanFlagOn( Data->Iopb->OperationFlags, SL_RETURN_SINGLE_ENTRY ); BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, FO_OPENED_CASE_SENSITIVE ); FILE_INFORMATION_CLASS InformationClass = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass; PVOID UserBuffer; ULONG BufferSize; //size for user and system buffers. BOOLEAN Unlock = FALSE; //Vars for moving data into user buffer. ULONG NumEntriesCopied; ULONG UserBufferOffset; ULONG LastEntryStart; BOOLEAN MoreRoom; PNC_CACHE_ENTRY NextEntry; DIRECTORY_CONTROL_OFFSETS Offsets; BOOLEAN FoundStructureOffsets; UNREFERENCED_PARAMETER( CompletionContext ); PAGED_CODE(); FLT_ASSERT( IoGetTopLevelIrp() == NULL ); FoundStructureOffsets = NcDetermineStructureOffsets( &Offsets, InformationClass ); if (!FoundStructureOffsets) { Status = STATUS_INVALID_PARAMETER; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // Get our instance context. // Status = FltGetInstanceContext( FltObjects->Instance, &InstanceContext ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // Get the directory's name. // Status = NcGetFileNameInformation( Data, NULL, NULL, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, &FileNameInformation ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } Status = FltParseFileNameInformation( FileNameInformation ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // See if the directory is parent of either mapping. // NcComparePath( &FileNameInformation->Name, &InstanceContext->Mapping.UserMapping, NULL, IgnoreCase, TRUE, &UserOverlap ); NcComparePath( &FileNameInformation->Name, &InstanceContext->Mapping.RealMapping, NULL, IgnoreCase, TRUE, &RealOverlap ); if (!(UserOverlap.Parent || RealOverlap.Parent )) { // // We are not interested in this directory // because it is not the parent of either // mapping. This means we can just passthrough. // Status = STATUS_SUCCESS; ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; goto NcEnumerateDirectoryCleanup; } Status = NcStreamHandleContextAllocAndAttach( FltObjects->Filter, FltObjects->Instance, FltObjects->FileObject, &HandleContext ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } FLT_ASSERT( HandleContext != NULL ); DirCtx = &HandleContext->DirectoryQueryContext; _Analysis_assume_( DirCtx != NULL ); // // Before looking at the context, we have to acquire the lock. // NcLockStreamHandleContext( HandleContext ); Unlock = TRUE; // // We don't allow multiple outstanding enumeration requests on // a single handle. // // TODO: This needs to change. // if (DirCtx->EnumerationOutstanding) { Status = STATUS_UNSUCCESSFUL; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } DirCtx->EnumerationOutstanding = TRUE; // // Now drop the lock. We're protected by the EnumerationOutstanding // flag; nobody else can muck with the enumeration context structure. // NcUnlockStreamHandleContext( HandleContext ); Unlock = FALSE; // // Now we need to initialize or clear the cache and query options. // Status = NcStreamHandleContextEnumSetup( DirCtx, InstanceContext, &Offsets, Data, FltObjects, UserOverlap, &FirstQuery ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // Prepare to populate the user buffer. // UserBuffer = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer; BufferSize = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length; // // Lets copy data into the user buffer. // NumEntriesCopied = 0; UserBufferOffset = 0; do { // // If there is no cache entry, populate it. // if (DirCtx->Cache.Buffer == NULL) { Status = NcPopulateCacheEntry( FltObjects->Instance, FltObjects->FileObject, Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length, Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass, Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileName, Reset, &DirCtx->Cache); // // We only want to reset the cache once. // Reset = FALSE; // // There was a problem populating cache, pass up to user. // if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } } NextEntry = NcDirEnumSelectNextEntry( DirCtx, &Offsets, IgnoreCase ); if (NextEntry == NULL) { // // There are no more entries. // break; } if (NcSkipName( &Offsets, DirCtx, RealOverlap, &InstanceContext->Mapping, IgnoreCase )) { // // This entry is the real mapping path. That means we have to mask it... // We will say there is more room and continue. // MoreRoom = TRUE; } else { // // We are keeping this entry! // try { LastEntryStart = UserBufferOffset; UserBufferOffset = NcCopyDirEnumEntry( UserBuffer, UserBufferOffset, BufferSize, NextEntry, &Offsets, &MoreRoom ); } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { Status = STATUS_INVALID_USER_BUFFER; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } if (MoreRoom) { NumEntriesCopied++; } }// end of "we are copying entry" } while (MoreRoom && (Single ? (NumEntriesCopied < 1) : TRUE)); if (NumEntriesCopied > 0) { // // Now we know what the last entry in the user buffer is going to be. // Set its NextEntryOffset to 0, so that the user knows its the last element. // try { NcSetNextEntryOffset( Add2Ptr(UserBuffer, LastEntryStart), &Offsets, TRUE ); } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { Status = STATUS_INVALID_USER_BUFFER; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } } // // We finished copying data. // ReturnValue = FLT_PREOP_COMPLETE; if (NumEntriesCopied == 0) { if (FirstQuery) { Status = STATUS_NO_SUCH_FILE; } else { Status = STATUS_NO_MORE_FILES; } } else { Status = STATUS_SUCCESS; } ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; NcEnumerateDirectoryCleanup: if (ReturnValue == FLT_PREOP_COMPLETE) { // // We need to write back results of query. // Data->IoStatus.Status = Status; if (NT_SUCCESS( Status )) { //success Data->IoStatus.Information = UserBufferOffset; } else { //failure Data->IoStatus.Information = 0; } } if (InstanceContext != NULL) { FltReleaseContext( InstanceContext ); } if (DirCtx != NULL) { if (!Unlock) { NcLockStreamHandleContext( HandleContext ); Unlock = TRUE; } FLT_ASSERT( DirCtx->EnumerationOutstanding ); DirCtx->EnumerationOutstanding = FALSE; NcUnlockStreamHandleContext( HandleContext ); Unlock = FALSE; FltReleaseContext( HandleContext ); } FLT_ASSERT( !Unlock ); if (FileNameInformation != NULL) { FltReleaseFileNameInformation( FileNameInformation ); } return ReturnValue; }
NTSTATUS GenerateFileName(IN PFLT_INSTANCE Instance, IN PFILE_OBJECT FileObject, IN PFLT_CALLBACK_DATA CallbackData, IN FLT_FILE_NAME_OPTIONS NameOptions, OUT PBOOLEAN CacheFileNameInformation, OUT PFLT_NAME_CONTROL FileName ) //上层的minifilter过滤驱动的名字请求进行处理 { NTSTATUS Status = STATUS_UNSUCCESSFUL; PFILE_OBJECT StreamObject = FileObject; PFLT_FILE_NAME_INFORMATION FileNameInformation = NULL; BOOLEAN bEncryptResource = FALSE; PFCB Fcb = FileObject->FsContext; PCCB Ccb = FileObject->FsContext2; FsRtlEnterFileSystem(); try { if(IsMyFakeFcb(FileObject)) { ExAcquireResourceSharedLite(Fcb->EncryptResource,TRUE); bEncryptResource = TRUE; if(BooleanFlagOn(Fcb->FcbState,SCB_STATE_SHADOW_CLOSE) || Ccb->StreamFileInfo.StreamObject == NULL) { try_return (Status = STATUS_FILE_DELETED); } else { StreamObject = Ccb->StreamFileInfo.StreamObject; } } ClearFlag(NameOptions,FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER); if(FlagOn(NameOptions,FLT_FILE_NAME_NORMALIZED)) { ClearFlag(NameOptions,FLT_FILE_NAME_NORMALIZED); SetFlag(NameOptions,FLT_FILE_NAME_OPENED); } if (CallbackData) { PFILE_OBJECT TemFileObject = CallbackData->Iopb->TargetFileObject; CallbackData->Iopb->TargetFileObject = StreamObject; FltSetCallbackDataDirty(CallbackData); Status = FltGetFileNameInformation(CallbackData,NameOptions, &FileNameInformation); CallbackData->Iopb->TargetFileObject = TemFileObject; FltClearCallbackDataDirty(CallbackData); } else { Status = FltGetFileNameInformationUnsafe(StreamObject,Instance, NameOptions, &FileNameInformation); } if(!NT_SUCCESS(Status)) { try_return (Status); } Status = FltCheckAndGrowNameControl(FileName, FileNameInformation->Name.Length); if(!NT_SUCCESS(Status)) { try_return (Status); } RtlCopyUnicodeString(&FileName->Name, &FileNameInformation->Name); if(FileNameInformation != NULL) { FltReleaseFileNameInformation(FileNameInformation); } Status = STATUS_SUCCESS; try_exit: NOTHING; } finally { if(bEncryptResource) { ExReleaseResourceLite( Fcb->EncryptResource ); } } FsRtlExitFileSystem(); return Status; }
static FLT_POSTOP_CALLBACK_STATUS FileCreateFilterPostCallback ( PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID CompletionContext, FLT_POST_OPERATION_FLAGS Flags ) { FLT_POSTOP_CALLBACK_STATUS status = FLT_POSTOP_FINISHED_PROCESSING; KLOCK_QUEUE_HANDLE hMutex = { 0 }; PFLT_FILE_NAME_INFORMATION fileInfo = NULL; RU32 pid = 0; RU64 ts = 0; RU32 createOptions = 0; RU32 createDispositions = 0; _fileContext* context = NULL; UNREFERENCED_PARAMETER( FltObjects ); UNREFERENCED_PARAMETER( CompletionContext ); UNREFERENCED_PARAMETER( Flags ); // We only care about user mode for now. if( UserMode != Data->RequestorMode || STATUS_SUCCESS != Data->IoStatus.Status ) { return status; } if( FILE_CREATED == Data->IoStatus.Information ) { pid = (RU32)FltGetRequestorProcessId( Data ); ts = rpal_time_getLocal(); if( !NT_SUCCESS( FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, &fileInfo ) ) ) { rpal_debug_kernel( "Failed to get file name info" ); fileInfo = NULL; } else { //rpal_debug_kernel( "NEW: %wZ", fileInfo->Name ); } if( NULL != ( context = _getOrSetContext( Data ) ) ) { context->isNew = TRUE; FltReleaseContext( (PFLT_CONTEXT)context ); } KeAcquireInStackQueuedSpinLock( &g_collector_2_mutex, &hMutex ); g_files[ g_nextFile ].pid = pid; g_files[ g_nextFile ].ts = ts; g_files[ g_nextFile ].uid = KERNEL_ACQ_NO_USER_ID; g_files[ g_nextFile ].action = KERNEL_ACQ_FILE_ACTION_ADDED; if( NULL != fileInfo ) { copyUnicodeStringToBuffer( &fileInfo->Name, g_files[ g_nextFile ].path ); FltReleaseFileNameInformation( fileInfo ); } g_nextFile++; if( g_nextFile == _NUM_BUFFERED_FILES ) { g_nextFile = 0; } KeReleaseInStackQueuedSpinLock( &hMutex ); } createOptions = Data->Iopb->Parameters.Create.Options & 0x00FFFFFF; createDispositions = ( Data->Iopb->Parameters.Create.Options & 0xFF000000 ) >> 24; if( IS_FLAG_ENABLED( createOptions, FILE_DELETE_ON_CLOSE ) ) { if( NULL != ( context = _getOrSetContext( Data ) ) ) { context->isDelete = TRUE; FltReleaseContext( (PFLT_CONTEXT)context ); } } return status; }
static FLT_POSTOP_CALLBACK_STATUS FileSetInfoFilterPostCallback ( PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID CompletionContext, FLT_POST_OPERATION_FLAGS Flags ) { FLT_POSTOP_CALLBACK_STATUS status = FLT_POSTOP_FINISHED_PROCESSING; KLOCK_QUEUE_HANDLE hMutex = { 0 }; PFLT_FILE_NAME_INFORMATION fileInfoSrc = NULL; PFLT_FILE_NAME_INFORMATION fileInfoDst = NULL; RU32 pid = 0; RU64 ts = 0; RU32 createOptions = 0; RU32 createDispositions = 0; PFILE_RENAME_INFORMATION renameInfo = NULL; _fileContext* context = NULL; UNREFERENCED_PARAMETER( FltObjects ); UNREFERENCED_PARAMETER( CompletionContext ); UNREFERENCED_PARAMETER( Flags ); // We only care about user mode for now. if( UserMode != Data->RequestorMode || STATUS_SUCCESS != Data->IoStatus.Status ) { return status; } if( FileRenameInformation == Data->Iopb->Parameters.SetFileInformation.FileInformationClass ) { if( NULL != ( fileInfoSrc = (PFLT_FILE_NAME_INFORMATION)CompletionContext ) ) { //rpal_debug_kernel( "MOVE OLD: %wZ", fileInfoSrc->Name ); } else { rpal_debug_kernel( "Failed to get src file name info" ); } renameInfo = (PFILE_RENAME_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer; if( !NT_SUCCESS( FltGetDestinationFileNameInformation( FltObjects->Instance, FltObjects->FileObject, renameInfo->RootDirectory, renameInfo->FileName, renameInfo->FileNameLength, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, &fileInfoDst ) ) ) { rpal_debug_kernel( "Failed to get dst file name info" ); } else { //rpal_debug_kernel( "MOVE TO: %wZ", fileInfoDst->Name ); } pid = (RU32)FltGetRequestorProcessId( Data ); ts = rpal_time_getLocal(); createOptions = Data->Iopb->Parameters.Create.Options & 0x00FFFFFF; createDispositions = ( Data->Iopb->Parameters.Create.Options & 0xFF000000 ) >> 24; KeAcquireInStackQueuedSpinLock( &g_collector_2_mutex, &hMutex ); g_files[ g_nextFile ].pid = pid; g_files[ g_nextFile ].ts = ts; g_files[ g_nextFile ].uid = KERNEL_ACQ_NO_USER_ID; // For compability with the user mode API we report file moves // as two different operations. // First we report the old file name. g_files[ g_nextFile ].action = KERNEL_ACQ_FILE_ACTION_RENAME_OLD; if( NULL != fileInfoSrc ) { copyUnicodeStringToBuffer( &fileInfoSrc->Name, g_files[ g_nextFile ].path ); FltReleaseFileNameInformation( fileInfoSrc ); } g_nextFile++; if( g_nextFile == _NUM_BUFFERED_FILES ) { g_nextFile = 0; } // Now report the new file name. g_files[ g_nextFile ].action = KERNEL_ACQ_FILE_ACTION_RENAME_NEW; if( NULL != fileInfoDst ) { copyUnicodeStringToBuffer( &fileInfoDst->Name, g_files[ g_nextFile ].path ); FltReleaseFileNameInformation( fileInfoDst ); } g_nextFile++; if( g_nextFile == _NUM_BUFFERED_FILES ) { g_nextFile = 0; } KeReleaseInStackQueuedSpinLock( &hMutex ); } else if( FileDispositionInformationEx == Data->Iopb->Parameters.SetFileInformation.FileInformationClass ||
FLT_PREOP_CALLBACK_STATUS FilemonPreCreate (__inout PFLT_CALLBACK_DATA Data , __in PCFLT_RELATED_OBJECTS FltObjects , __deref_out_opt PVOID *CompletionContext) { PFLT_FILE_NAME_INFORMATION nameInfo; NTSTATUS status; LONG nType; UNREFERENCED_PARAMETER( FltObjects ); UNREFERENCED_PARAMETER( CompletionContext ); PAGED_CODE(); if (FALSE == IsGuardStart()) { KdPrint(( "!!! scanner.sys -- allowing create for trusted process \n" )); return FLT_PREOP_SUCCESS_NO_CALLBACK; } if (!NT_SUCCESS( Data->IoStatus.Status ) || (STATUS_REPARSE == Data->IoStatus.Status)) { KdPrint(("[ScannerPreCreate] Data->IoStatus.Status:%d.\n", Data->IoStatus.Status)); return FLT_PREOP_SUCCESS_NO_CALLBACK; } status = FltGetFileNameInformation(Data , FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT , &nameInfo); if (!NT_SUCCESS( status )) return FLT_POSTOP_FINISHED_PROCESSING; FltParseFileNameInformation( nameInfo ); KdPrint(("[ScannerPreCreate] monitoer:%wZ \n", &nameInfo->Name)); if(IsFilemonGuardPath(nameInfo->Name.Buffer , FlagOn(FILE_DIRECTORY_FILE, Data->Iopb->Parameters.Create.Options) > 0 , &nType)) { WCHAR szPath[MAX_PATH] = {0}; wcsncpy(szPath, nameInfo->Name.Buffer, min(MAX_PATH-1, nameInfo->Name.Length)); FltReleaseFileNameInformation( nameInfo ); // 获取通过与否 if(FALSE != CheckRequestIsAllowed(MAKEGUARDTYPE(MASK_GUARDLITE_FILEMON, nType) , szPath , L"" , L"")) { return FLT_PREOP_SUCCESS_NO_CALLBACK; } // 未通过 if(FlagOn( (FILE_CREATE<<24), Data->Iopb->Parameters.Create.Options )) { Data->IoStatus.Information = 0; Data->IoStatus.Status = STATUS_ACCESS_DENIED; return FLT_PREOP_COMPLETE; } return FLT_PREOP_SUCCESS_WITH_CALLBACK; } FltReleaseFileNameInformation( nameInfo ); return FLT_PREOP_SUCCESS_NO_CALLBACK; }