I'll start with my final goal. I want that every document (doc,docx,pdf,txt, etc.) on my system will have a fixed (and transperent to the user) header. For example, the string "abcde" will be prepended in every document.
In order to do so I wrote a minifilter driver that does the following:
IRP_MJ_WRITE
- if header exists change offset to start of file.
IRP_MJ_READ
- if header exists change offset to start of file.
IRP_MJ_QUERY_INFORMATION
- if header exists change returned size of file.
IRP_MJ_DIRECTORY_CONTROL
- if header exists change returned size of file.
IRP_MJ_CREATE
- if header doesn't exists prepened header to file.
This works well, except on MS Word 2003 documents (doc,xls,ppt) and notepad. I just seem to not catch some of the read and write operations and notepad shows the header as well as the file.
I have read A LOT in http://www.osronline.com/ and every one who asks there is told to read some Nagar book or look at their archives (which are disaster to search in). I think I've read there everything related to my issue.
It seems notepad uses Memory Mapped files, Fast IO, Pagged IO and god knows what else.
I've tried to hook NtMapViewOfSection
, MapViewOfFile
and MapViewOfFileEx
with mHook, but when I opened some file in notepad and tried to find the mapped data I had no luck (but I found every other byte that was mapped to the memory).
Then I read that what I'm trying to accomplish isn't possible with a hook, only with minifilter driver and from what I figgured I'm missing some flags to set.
I would really appriciate it if someone could just tell me what to do in order to catch notepad's operations.
here's some code example for read:
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
checks for file name and requesting process. (Some app's could see the header)
I would really appriciate it if someone could just tell me what to do in order to catch notepad's operations.
Thanks.
As @Harry jonhston pointed out your check of only intercepting IRPs with MdlAddress == NULL
is incorrect.
if(Data->Iopb->Parameters.Read.MdlAddress != NULL)
{
return FLT_POSTOP_FINISHED_PROCESSING;
}
Most likely notepad and MS Office application are failing due to this.
Few points more:
FltReadFile
should only be called IRQL PASSIVE_LEVEL
.