c++ - 有没有办法将整个文件从文件系统微型过滤器驱动程序(内核模式)传递到用户模式应用程序?

标签 c++ c windows driver minifilter

我有一个使用 WDK 8.1 示例的扫描仪文件系统微型过滤器驱动程序。

我想知道我是否可以将整个文件发送到用户端应用程序,这样我就可以进行更复杂的计算,例如 MD5 哈希或其他任何东西,这样我就不必在 mini 中编写更复杂的操作过滤器驱动程序,而不是在用户应用程序中,我不介意引入 windows.h 并且我可以在堆上分配内存而不是使用 ExAllocatePoolWithTag 和类似的东西。

我可以在一次通知中将整个文件传递到用户空间模式吗?

如果不是,我将如何进行分块和同步。

这是 8.1 扫描仪文件系统微型过滤器驱动程序示例的接口(interface),它指示微型过滤器驱动程序与用户端应用程序之间的通信:

/*++

Copyright (c) 1999-2002  Microsoft Corporation

Module Name:

    scanuk.h

Abstract:

    Header file which contains the structures, type definitions,
    constants, global variables and function prototypes that are
    shared between kernel and user mode.

Environment:

    Kernel & user mode

--*/

#ifndef __SCANUK_H__
#define __SCANUK_H__

//
//  Name of port used to communicate
//

const PWSTR ScannerPortName = L"\\ScannerPort";


#define SCANNER_READ_BUFFER_SIZE   1024

typedef struct _SCANNER_NOTIFICATION {

    ULONG BytesToScan;
    ULONG Reserved;             // for quad-word alignement of the Contents structure
    UCHAR Contents[SCANNER_READ_BUFFER_SIZE];

} SCANNER_NOTIFICATION, *PSCANNER_NOTIFICATION;

typedef struct _SCANNER_REPLY {

    BOOLEAN SafeToOpen;

} SCANNER_REPLY, *PSCANNER_REPLY;

#endif //  __SCANUK_H__

注意传递的缓冲区大小有 1024 的限制。这意味着我无法在用户端应用程序中对整个文件执行 MD5 哈希,然后将被迫在微型过滤器驱动程序中执行此操作,我希望尽可能避免这种情况。

下面是迷你过滤器驱动程序中的函数,它使用上面的接口(interface)将消息发送到用户端应用程序:

//////////////////////////////////////////////////////////////////////////
//  Local support routines.
//
/////////////////////////////////////////////////////////////////////////

NTSTATUS
ScannerpScanFileInUserMode (
    _In_ PFLT_INSTANCE Instance,
    _In_ PFILE_OBJECT FileObject,
    _Out_ PBOOLEAN SafeToOpen
    )
/*++

Routine Description:

    This routine is called to send a request up to user mode to scan a given
    file and tell our caller whether it's safe to open this file.

    Note that if the scan fails, we set SafeToOpen to TRUE.  The scan may fail
    because the service hasn't started, or perhaps because this create/cleanup
    is for a directory, and there's no data to read & scan.

    If we failed creates when the service isn't running, there'd be a
    bootstrapping problem -- how would we ever load the .exe for the service?

Arguments:

    Instance - Handle to the filter instance for the scanner on this volume.

    FileObject - File to be scanned.

    SafeToOpen - Set to FALSE if the file is scanned successfully and it contains
                 foul language.

Return Value:

    The status of the operation, hopefully STATUS_SUCCESS.  The common failure
    status will probably be STATUS_INSUFFICIENT_RESOURCES.

--*/

{
    NTSTATUS status = STATUS_SUCCESS;
    PVOID buffer = NULL;
    ULONG bytesRead;
    PSCANNER_NOTIFICATION notification = NULL;
    FLT_VOLUME_PROPERTIES volumeProps;
    LARGE_INTEGER offset;
    ULONG replyLength, length;
    PFLT_VOLUME volume = NULL;

    *SafeToOpen = TRUE;

    //
    //  If not client port just return.
    //

    if (ScannerData.ClientPort == NULL) {

        return STATUS_SUCCESS;
    }

    try {

        //
        //  Obtain the volume object .
        //

        status = FltGetVolumeFromInstance( Instance, &volume );

        if (!NT_SUCCESS( status )) {

            leave;
        }

        //
        //  Determine sector size. Noncached I/O can only be done at sector size offsets, and in lengths which are
        //  multiples of sector size. A more efficient way is to make this call once and remember the sector size in the
        //  instance setup routine and setup an instance context where we can cache it.
        //

        status = FltGetVolumeProperties( volume,
                                         &volumeProps,
                                         sizeof( volumeProps ),
                                         &length );
        //
        //  STATUS_BUFFER_OVERFLOW can be returned - however we only need the properties, not the names
        //  hence we only check for error status.
        //

        if (NT_ERROR( status )) {

            leave;
        }

        length = max( SCANNER_READ_BUFFER_SIZE, volumeProps.SectorSize );

        //
        //  Use non-buffered i/o, so allocate aligned pool
        //

        buffer = FltAllocatePoolAlignedWithTag( Instance,
                                                NonPagedPool,
                                                length,
                                                'nacS' );

        if (NULL == buffer) {

            status = STATUS_INSUFFICIENT_RESOURCES;
            leave;
        }

        notification = ExAllocatePoolWithTag( NonPagedPool,
                                              sizeof( SCANNER_NOTIFICATION ),
                                              'nacS' );

        if(NULL == notification) {

            status = STATUS_INSUFFICIENT_RESOURCES;
            leave;
        }

        //
        //  Read the beginning of the file and pass the contents to user mode.
        //

        offset.QuadPart = bytesRead = 0;
        status = FltReadFile( Instance,
                              FileObject,
                              &offset,
                              length,
                              buffer,
                              FLTFL_IO_OPERATION_NON_CACHED |
                              FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET,
                              &bytesRead,
                              NULL,
                              NULL );

        if (NT_SUCCESS( status ) && (0 != bytesRead)) {

            notification->BytesToScan = (ULONG) bytesRead;

            //
            //  Copy only as much as the buffer can hold
            //

            RtlCopyMemory( &notification->Contents,
                           buffer,
                           min( notification->BytesToScan, SCANNER_READ_BUFFER_SIZE ) );

            replyLength = sizeof( SCANNER_REPLY );

            status = FltSendMessage( ScannerData.Filter,
                                     &ScannerData.ClientPort,
                                     notification,
                                     sizeof(SCANNER_NOTIFICATION),
                                     notification,
                                     &replyLength,
                                     NULL );

            if (STATUS_SUCCESS == status) {

                *SafeToOpen = ((PSCANNER_REPLY) notification)->SafeToOpen;

            } else {

                //
                //  Couldn't send message
                //

                DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status );
            }
        }

    } finally {

        if (NULL != buffer) {

            FltFreePoolAlignedWithTag( Instance, buffer, 'nacS' );
        }

        if (NULL != notification) {

            ExFreePoolWithTag( notification, 'nacS' );
        }

        if (NULL != volume) {

            FltObjectDereference( volume );
        }
    }

    return status;
}

最佳答案

没有必要出现这样的并发症。 Microsoft 已经在内核中支持加密。查看CNG内核本身用来进行散列、 key 、安全相关加密处理的库。 如果您真的坚持将文件内容发送到用户模式,我会提出一个更优雅的解决方案,即在用户模式的进程地址空间中读取文件

  1. 打开文件并查询其大小。
  2. 调用ZwAllocateVirtualMemory使用您的用户模式进程的进程句柄。
  3. 读取分配空间中的文件内容。
  4. 向您的用户模式进程发送分配的地址以及大小和其他信息,例如文件名等。
  5. 等待用户模式进程完成处理并获得结果。
  6. 调用ZwFreeVirtualMemory释放你的内存,或者如果你有一个异步模型让用户模式进程通过调用释放它自己的内存VirtualFree .

我会考虑的另一种选择是在内核中自行打开文件,然后调用 ZwDuplicateObject并为我的用户模式进程创建文件句柄。最后向用户模式进程发送句柄,让它与句柄一起工作,读取/查询做它想做的事。同样,根据您的实现,用户模式进程可以关闭句柄,或者您可以等待并在用户模式进程的上下文中关闭它(例如,使用 KeStackAttachProcess 附加到其地址空间)。

不过,我会在内核中完成我能做的所有处理,因为一直切换上下文的成本很高。

祝你好运,
加布里埃尔

关于c++ - 有没有办法将整个文件从文件系统微型过滤器驱动程序(内核模式)传递到用户模式应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29281410/

相关文章:

C++ 通过 Outlook 发送 HTML 邮件

相同基础类型的 C++ 变体

c - 结构、strtok、段错误

c - -fcatch-undefined-behavior 只捕获大于 1 个元素的本地数组访问

c - 字符串C中的奇怪字符

windows - 存储库损坏 + 基于存储库名称的错误

c++ - 如何使用 substr 和 find 在 C++ 中将用户定义的句子拆分为单词?

c++ - 如何使用 Memcpy() 函数

c++ - DirectX 问题

windows - 如果我的存储库在 Windows 机器上,我可以让 git 发送一封带有 post-receive-email 脚本的电子邮件吗?