2014年7月16日星期三

minifilter之删除和改名操作

#include <fltKernel.h>
#include <windef.h>
#include <ntddk.h>

PFLT_FILTER gFilterHandle;

#define TAG 'tset' //test

/*
文件名:minifilter之删除和改名操作。

一般的用户的删除的操作的设置是移动到回收站。
也可以设置为不移动到回收站而直接删除。
选中文件再按SHITF+DELETE是直接删除而不是移动到回收站。
所以普通的删除操作要走两个地方:一是删除,而是改名。一个想法是能区分/识别这两个操作为一个操作不?

参考:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff549366(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff544789(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff550799(v=vs.85).aspx

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


FLT_PREOP_CALLBACK_STATUS SetInformationPreOperation(__inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __out PVOID *CompletionContext)
{
    PFLT_FILE_NAME_INFORMATION    pfni;
    NTSTATUS                      status;

    UNREFERENCED_PARAMETER( CompletionContext );  

    if(!NT_SUCCESS(Data->IoStatus.Status))
    {
        return FLT_PREOP_SUCCESS_WITH_CALLBACK;//The filter driver must pass this IRP down to the next-lower driver on the stack.
    } 

    /*
    FltGetFileNameInformation cannot get file name information if the TopLevelIrp field of the current thread is not NULL, 
    because the resulting file system recursion could cause deadlocks or stack overflows. (For more information about this issue, see IoGetTopLevelIrp.) 
    FltGetFileNameInformation cannot get file name information in the paging I/O path. 
    FltGetFileNameInformation cannot get file name information in the post-close path. 
    FltGetFileNameInformation cannot get the short name of a file in the pre-create path. 
    */
    if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO) || FlagOn(Data->Iopb->IrpFlags, IRP_SYNCHRONOUS_PAGING_IO) || IoGetTopLevelIrp()) //IRP_NOCACHE
    {
        return FLT_PREOP_SUCCESS_WITH_CALLBACK;
    }

    status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pfni);    
    if (!NT_SUCCESS( status )) 
    {
        return FLT_PREOP_SUCCESS_WITH_CALLBACK;
    }

    status = FltParseFileNameInformation(pfni);
    if (!NT_SUCCESS( status )) 
    {
        FltReleaseFileNameInformation(pfni); 
        return FLT_PREOP_SUCCESS_WITH_CALLBACK;
    }
    
    //FltReleaseFileNameInformation(pfni); 

    switch(Data->Iopb->Parameters.SetFileInformation.FileInformationClass)
    {  
    case FileDispositionInformation://删除。
        {
            PFILE_DISPOSITION_INFORMATION pfdi = (PFILE_DISPOSITION_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;

            /*
            Indicates whether the operating system file should delete the file when the file is closed.
            Set this member to TRUE to delete the file when it is closed.
            Otherwise, set to FALSE. 
            Setting this member to FALSE has no effect if the handle was opened with FILE_FLAG_DELETE_ON_CLOSE.

            严格的说还可以判断pfdi->DeleteFile的值。上面的是条件。
            */
            if (pfdi->DeleteFile == TRUE) 
            {
                KdPrint(("删除文件\n"));
            }

            /*
            #define FO_DELETE_ON_CLOSE              0x00010000
            FILE_FLAG_DELETE_ON_CLOSE 0x04000000    //http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
            到底该用哪个呢?
            */
            if (FlagOn(Data->Iopb->TargetFileObject->Flags, FO_DELETE_ON_CLOSE))//FO_DELETE_ON_CLOSE OR FILE_FLAG_DELETE_ON_CLOSE?
            {
                KdPrint(("打开时就已经指定删除此文件了\n"));
            }  
            
            /*
            For rename or link operations. 
            If InfoBuffer->FileName contains a fully qualified file name, or if InfoBuffer->RootDirectory is non-NULL, 
            this member is a file object pointer for the parent directory of the file that is the target of the operation. 
            Otherwise it is NULL.

            还可以根据情况判断ParentOfTarget是否可用,然后再根据ParentOfTarget获取被删除文件的所在的目录。
            */

            /*
            A file marked for deletion is not actually deleted until all open handles for the file object have been closed and the link count for the file is zero.
            注意:文件不会立即删除。是延迟的。
            */

            KdPrint(("删除文件/目录:%wZ\n", &pfni->Name));
        }

        KdPrint(("\n"));
        break;
    case FileRenameInformation://改名。 
        {
            PFILE_RENAME_INFORMATION  pfri = (PFILE_RENAME_INFORMATION )Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
            UNICODE_STRING new_name;

            /*
            注意有一些重命名的规则和注意事项,包括目录的,见msdn.
            */

            /*
            RootDirectory
            If the file is not being moved to a different directory, or if the FileName member contains the full pathname, this member is NULL. 
            Otherwise, it is a handle for the root directory under which the file will reside after it is renamed.

            pfri->RootDirectory不是判断改名和移动的标志,仅仅用于获取路径。因为FileName大多是全路径。
            */
            if (pfri->RootDirectory == NULL)
            {
                //本目录改名,FileName是全路径。
                //KdPrint(("改名!\n"));
            }
            else
            {
                /*
                就是移动,普通的删除(移动到回收站)。
                根据RootDirectory和FileName获取全路径。
                */
                //KdPrint(("移动!\n"));
            }

            /*
            关于原来的文件名字的获取的思路:
            1.Data->Iopb->TargetFileObject 或者 FltObjects->FileObject,其实这二者是相等的一样的。
              其实这个值是和pfni->Name一致的。
            2.Data->Iopb->TargetFileObject->FsContext和Data->Iopb->TargetFileObject->FsContext2。
            不过思路2有很多未公开的结构,ccb,fcb等,可参考:
            http://dokan.googlecode.com/svn/trunk/sys/dokan.h
            http://dokan.googlecode.com/svn/trunk/sys/fileinfo.c
            */
            //KdPrint(("改名/移动前的文件/目录:%wZ\n", &Data->Iopb->TargetFileObject->FileName));
            KdPrint(("改名/移动前的文件/目录:%wZ\n", &pfni->Name));

            /*
            FileName是改名后的路径,和pfni->Name的值在此时是不一样的。
            */
            new_name.Buffer = pfri->FileName;
            new_name.Length = (USHORT)pfri->FileNameLength;
            new_name.MaximumLength = new_name.Length;
            KdPrint(("改名/移动后的文件/目录:%wZ\n", &new_name));
        }

        KdPrint(("\n"));
        break;
    default://其实还有更多,不信你看头文件,注意对应的结构的处理。
        KdPrint(("设置信息的类型:%d\n\n", Data->Iopb->Parameters.SetFileInformation.FileInformationClass));
        break;
    }   

    FltReleaseFileNameInformation(pfni); 
   
    return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}


FLT_POSTOP_CALLBACK_STATUS SetInformationPostOperation (__inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __in_opt PVOID CompletionContext, __in FLT_POST_OPERATION_FLAGS Flags)
    /*
    这个无用,暂时不用。
    */
{
    PFLT_FILE_NAME_INFORMATION    pfni;
    NTSTATUS                      status;

    UNREFERENCED_PARAMETER( CompletionContext );  

    if(!NT_SUCCESS(Data->IoStatus.Status))
    {
        return FLT_POSTOP_FINISHED_PROCESSING;//The filter driver must pass this IRP down to the next-lower driver on the stack.
    } 

    /*
    FltGetFileNameInformation cannot get file name information if the TopLevelIrp field of the current thread is not NULL, 
    because the resulting file system recursion could cause deadlocks or stack overflows. (For more information about this issue, see IoGetTopLevelIrp.) 
    FltGetFileNameInformation cannot get file name information in the paging I/O path. 
    FltGetFileNameInformation cannot get file name information in the post-close path. 
    FltGetFileNameInformation cannot get the short name of a file in the pre-create path. 
    */
    if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO) || FlagOn(Data->Iopb->IrpFlags, IRP_SYNCHRONOUS_PAGING_IO) || IoGetTopLevelIrp()) //IRP_NOCACHE
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pfni);    
    if (!NT_SUCCESS( status )) 
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    status = FltParseFileNameInformation(pfni);
    if (!NT_SUCCESS( status )) 
    {
        FltReleaseFileNameInformation(pfni); 
        return FLT_POSTOP_FINISHED_PROCESSING;
    }
    
    //FltReleaseFileNameInformation(pfni); 

    switch(Data->Iopb->Parameters.SetFileInformation.FileInformationClass)
    {  
    case FileDispositionInformation://删除。
        {
            PFILE_DISPOSITION_INFORMATION pfdi = (PFILE_DISPOSITION_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;

            /*
            Indicates whether the operating system file should delete the file when the file is closed.
            Set this member to TRUE to delete the file when it is closed.
            Otherwise, set to FALSE. 
            Setting this member to FALSE has no effect if the handle was opened with FILE_FLAG_DELETE_ON_CLOSE.

            严格的说还可以判断pfdi->DeleteFile的值。上面的是条件。
            */
            if (pfdi->DeleteFile == TRUE) 
            {
                //KdPrint(("删除文件\n"));
            }

            /*
            #define FO_DELETE_ON_CLOSE              0x00010000
            FILE_FLAG_DELETE_ON_CLOSE 0x04000000    //http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
            到底该用哪个呢?
            */
            if (FlagOn(Data->Iopb->TargetFileObject->Flags, FO_DELETE_ON_CLOSE))//FO_DELETE_ON_CLOSE OR FILE_FLAG_DELETE_ON_CLOSE?
            {
                //KdPrint(("打开时就已经指定删除此文件了\n"));
            }  
            
            /*
            For rename or link operations. 
            If InfoBuffer->FileName contains a fully qualified file name, or if InfoBuffer->RootDirectory is non-NULL, 
            this member is a file object pointer for the parent directory of the file that is the target of the operation. 
            Otherwise it is NULL.

            还可以根据情况判断ParentOfTarget是否可用,然后再根据ParentOfTarget获取被删除文件的所在的目录。
            */

            /*
            A file marked for deletion is not actually deleted until all open handles for the file object have been closed and the link count for the file is zero.
            注意:文件不会立即删除。是延迟的。
            */

            //KdPrint(("删除文件/目录:%wZ\n", &pfni->Name));
        }

        //KdPrint(("\n"));
        break;
    case FileRenameInformation://改名。 
        {
            PFILE_RENAME_INFORMATION  pfri = (PFILE_RENAME_INFORMATION )Data->Iopb->Parameters.SetFileInformation.InfoBuffer;

            /*
            注意有一些重命名的规则和注意事项,包括目录的,见msdn.
            */

            /*
            RootDirectory
            If the file is not being moved to a different directory, or if the FileName member contains the full pathname, this member is NULL. 
            Otherwise, it is a handle for the root directory under which the file will reside after it is renamed.

            pfri->RootDirectory不是判断改名和移动的标志,仅仅用于获取路径。因为FileName大多是全路径。
            */
            if (pfri->RootDirectory == NULL)
            {
                //本目录改名,FileName是全路径。
                //KdPrint(("改名!\n"));
            }
            else
            {
                /*
                就是移动,普通的删除(移动到回收站)。
                根据RootDirectory和FileName获取全路径。
                */
                //KdPrint(("移动!\n"));
            }

            /*
            关于原来的文件名字的获取的思路:
            1.Data->Iopb->TargetFileObject 或者 FltObjects->FileObject,其实这二者是相等的一样的。其实这个值是和pfni->Name一致的。
              这个有时是改名后的路径有时是改名前的路径。此办法不可信,不可用。还得组合路径是必须的。
            2.Data->Iopb->TargetFileObject->FsContext和Data->Iopb->TargetFileObject->FsContext2。
            不过思路2有很多未公开的结构,ccb,fcb等,可参考:
            http://dokan.googlecode.com/svn/trunk/sys/dokan.h
            http://dokan.googlecode.com/svn/trunk/sys/fileinfo.c

            获取改名前的名字在和路径在前操作中进行。
            */
            //KdPrint(("改名/移动前的文件/目录:%wZ\n", &Data->Iopb->TargetFileObject->FileName));

            /*
            FileName是改名后的路径,和pfni->Name是一样的。\Device\HarddiskVolume1\和\??\之别。
            */
            //KdPrint(("改名/移动后的文件/目录:%wZ\n", &pfni->Name));
        }

        //KdPrint(("\n"));
        break;
    default://其实还有更多,不信你看头文件,注意对应的结构的处理。
        //KdPrint(("设置信息的类型:%d\n\n", Data->Iopb->Parameters.SetFileInformation.FileInformationClass));
        break;
    }   

    FltReleaseFileNameInformation(pfni); 

    return FLT_POSTOP_FINISHED_PROCESSING;
}


CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_SET_INFORMATION,  0, SetInformationPreOperation, SetInformationPostOperation},
    { IRP_MJ_OPERATION_END }
};


#pragma PAGEDCODE
NTSTATUS InstanceQueryTeardown (__in PCFLT_RELATED_OBJECTS FltObjects,__in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)
{
    return STATUS_SUCCESS;
}


#pragma PAGEDCODE//#pragma alloc_text(PAGE, PtUnload)
NTSTATUS MiniFilterUnload (__in FLT_FILTER_UNLOAD_FLAGS Flags)
{
    FltUnregisterFilter( gFilterHandle );
    return STATUS_SUCCESS;
}


CONST FLT_REGISTRATION FilterRegistration = {
    sizeof( FLT_REGISTRATION ),         //  Size
    FLT_REGISTRATION_VERSION,           //  Version
    0,                                  //  Flags
    NULL,                               //  Context
    Callbacks,                          //  Operation callbacks
    MiniFilterUnload,                   //  MiniFilterUnload
    NULL,                               //  InstanceSetup
    InstanceQueryTeardown,              //  InstanceQueryTeardown
    NULL,                               //  InstanceTeardownStart
    NULL,                               //  InstanceTeardownComplete
    NULL,                               //  GenerateFileName
    NULL,                               //  GenerateDestinationFileName
    NULL                                //  NormalizeNameComponent
};


DRIVER_INITIALIZE DriverEntry;
#pragma alloc_text(INIT, DriverEntry)//#pragma INITCODE
NTSTATUS DriverEntry (_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;

    UNREFERENCED_PARAMETER( RegistryPath );

    KdBreakPoint();

    status = FltRegisterFilter( DriverObject, &FilterRegistration, &gFilterHandle );
    if (!NT_SUCCESS( status )) //FLT_ASSERT( NT_SUCCESS( status ) );
    {        
        return status;
    }

    status = FltStartFiltering( gFilterHandle );
    if (!NT_SUCCESS( status )) {
        FltUnregisterFilter( gFilterHandle );
    }

    return status;
}

没有评论:

发表评论