迷你过滤驱动程序,内存映射和记事本

4
我将从最终目标开始。 我希望系统上的每个文档(doc,docx,pdf,txt等)都有一个固定的头部(对用户透明)。 例如,在每个文档中都会添加字符串“abcde”。
为了实现这一点,我编写了一个迷你过滤器驱动程序,执行以下操作:
IRP_MJ_WRITE - 如果存在头部,则更改偏移量以使其位于文件开头。
IRP_MJ_READ - 如果存在头部,则更改偏移量以使其位于文件开头。
IRP_MJ_QUERY_INFORMATION - 如果存在头部,则更改返回的文件大小。
IRP_MJ_DIRECTORY_CONTROL - 如果存在头部,则更改返回的文件大小。
IRP_MJ_CREATE - 如果不存在头部,则在文件前面添加头部。
这很有效,但是在MS Word 2003文档(doc,xls,ppt)和记事本上除外。 我似乎无法捕获某些读取和写入操作,并且记事本显示标题以及文件。
我在http://www.osronline.com/上阅读了很多,每个人都被告知要阅读一些Nagar书籍或查看他们的档案(这些档案很难搜索)。我认为我已经阅读了与我的问题相关的所有内容。
看起来记事本使用内存映射文件、快速IO、分页IO和其他诸如此类的技术。我已经尝试使用mHook钩取NtMapViewOfSectionMapViewOfFileMapViewOfFileEx,但是当我打开记事本中的某个文件并尝试查找映射的数据时,我没有成功(但我找到了映射到内存的每个其他字节)。
然后我读到,我正在尝试实现的目标无法通过钩子实现,只能通过minifilter驱动程序实现,而且我发现我缺少一些需要设置的标志。
如果有人能告诉我如何捕获记事本操作,我将非常感激。
以下是一些读取的代码示例:
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_WRITE,
    0,
    PreWrite,
    PostWrite },

    { IRP_MJ_READ,
    0,
    PreRead,
    PostRead },

    { IRP_MJ_QUERY_INFORMATION,
    0,
    NULL,
    PostQueryInfo },

    { IRP_MJ_DIRECTORY_CONTROL,
    0,
    NULL,
    PostQueryDir },

    { IRP_MJ_CREATE,
    0,
    NULL,
    PostCreate },

    { IRP_MJ_OPERATION_END }
};

FLT_PREOP_CALLBACK_STATUS
    PreRead (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    )
{
    NTSTATUS status = 0;
    ULONG bytesRead;
    PVOID readBuffer;
    LARGE_INTEGER zero;
    zero.QuadPart = 0;

    UNREFERENCED_PARAMETER(FltObjects);
    UNREFERENCED_PARAMETER(Data);
    UNREFERENCED_PARAMETER(CompletionContext);

    if(Data->Iopb->Parameters.Read.MdlAddress != NULL){
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }

    if(!IsFileNeedProccessing(&FltObjects->FileObject->FileName, Data)){
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }


    readBuffer = ExAllocatePool(
        NonPagedPool,
        prefixSize);
    if(readBuffer == NULL)
    {
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }

    status = FltReadFile(
        FltObjects->Instance,
        FltObjects->FileObject,
        &zero,
        (ULONG)prefixSize,
        readBuffer,
        FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET,
        &bytesRead,
        NULL,
        NULL);

    if(NT_SUCCESS(status))
    {
        if(IsBuffAPrefixOfBuffB(prefix, readBuffer, prefixSize, (SIZE_T)bytesRead))
        {
            Data->Iopb->Parameters.Read.ByteOffset.QuadPart += prefixSize;
            FltSetCallbackDataDirty(Data);
        }
    }
    ExFreePool(readBuffer);

    return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}

 FLT_POSTOP_CALLBACK_STATUS
    PostRead (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_opt_ PVOID CompletionContext,
    _In_ FLT_POST_OPERATION_FLAGS Flags
    )
 {
    NTSTATUS status;
    ULONG bytesRead;
    PVOID readBuffer;
    FILE_STANDARD_INFORMATION info;
    LONGLONG* currOffset = &Data->Iopb->TargetFileObject->CurrentByteOffset.QuadPart;
    LARGE_INTEGER zero;
    zero.QuadPart = 0;

    UNREFERENCED_PARAMETER(CompletionContext);
    UNREFERENCED_PARAMETER(Flags);
    UNREFERENCED_PARAMETER(Data);
    UNREFERENCED_PARAMETER(FltObjects);

    if(Data->Iopb->Parameters.Read.MdlAddress != NULL)
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    if(!IsFileNeedProccessing(&FltObjects->FileObject->FileName, Data))
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    status = FltQueryInformationFile(
        FltObjects->Instance,
        FltObjects->FileObject,
        &info,
        sizeof(info),
        FileStandardInformation,
        NULL);

    if(NT_SUCCESS(status)
        && info.EndOfFile.QuadPart != *currOffset
        && *currOffset >= (LONGLONG)prefixSize)
    {
        readBuffer = ExAllocatePool(NonPagedPool,
            prefixSize);
        if(readBuffer == NULL)
        {
            return FLT_POSTOP_FINISHED_PROCESSING;
        }

        status = FltReadFile(
            FltObjects->Instance,
            FltObjects->FileObject,
            &zero,
            (ULONG)prefixSize,
            readBuffer,
            FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET,
            &bytesRead,
            NULL,
            NULL);

        if(NT_SUCCESS(status))
        {
            if(IsBuffAPrefixOfBuffB(prefix, readBuffer, prefixSize, (SIZE_T)bytesRead))
            {
                *currOffset -= prefixSize;
                FltSetCallbackDataDirty(Data);
            }
        }
        ExFreePool(readBuffer);
    }

    return FLT_POSTOP_FINISHED_PROCESSING;
 }

IsFileNeedProccessing 检查文件名和请求进程。(一些应用程序可以看到标头)

如果有人能告诉我如何捕获记事本的操作,我将非常感激。

谢谢。


1
为什么你不拦截 Parameters.Read.MdlAddress != NULL 的调用呢?这似乎是错误的。 - Harry Johnston
你说得对!我会去检查一下。谢谢。 - assafmo
1个回答

2
正如@Harry jonhston所指出的那样,您仅拦截MdlAddress == NULL的IRP检查是不正确的。
if(Data->Iopb->Parameters.Read.MdlAddress != NULL)
{
    return FLT_POSTOP_FINISHED_PROCESSING;
}

很可能记事本和Microsoft Office应用程序由于此原因而无法正常工作。

另外几点:

  • 您必须适当地处理页面IO。
  • FltReadFile只能在IRQLPASSIVE_LEVEL下调用。
  • 更重要的是,更改文件大小并将其保持对Windows文件系统和用户透明相当复杂。建议不要这样做。
  • 要为文件添加特殊数据,可能最好使用备用数据流

感谢您的输入。IsFileNeedProccessing 还会检查 IRQL == PASSIVE_LEVEL。您认为如何适当地处理分页 IO 呢? - assafmo
1
@gfgqtmakia 我认为你需要跳过分页IO,因为在此期间可能无法获取文件名。虽然我建议你进行一些研究,找出你需要做什么。 - Rohan
жҲ‘еҸҜд»Ҙд»ҺFileObjectе’ҢIoQueryFileDosDeviceNameдёӯиҺ·еҸ–ж–Ү件еҗҚгҖӮPagging IOжҳҜMDLеҗ—пјҹдҪ иғҪиҝӣдёҖжӯҘи§ЈйҮҠдёҖдёӢеҗ—пјҹ - assafmo
2
FileObject获取文件名不是合适的方法。我建议阅读MSDN和osronline.com上与minifilters相关的文档。简短回答这个问题有点困难! - Rohan
它不一定非得很短。 :-) - assafmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接