Strange behavior of PreOperationCallback in Minifilter

Hello!

I’m working on a filesystem minifilter driver that is aimed to catch write operation of any image file and to transfer the name of the file to a user-mode application via communication port. Here is the code of my PreOperationCallback routine:

FLT_PREOP_CALLBACK_STATUS PtPreOperationPassThrough(Inout PFLT_CALLBACK_DATA Data, In PCFLT_RELATED_OBJECTS FltObjects,
Flt_CompletionContext_Outptr PVOID *CompletionContext){
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
NTSTATUS status;
PFILE_OBJECT fileObject;
PSCANNER_NOTIFICATION notification = NULL;
ULONG replyLength;
if (FltObjects->FileObject != NULL && Data != NULL){
fileObject = Data->Iopb->TargetFileObject;
if (fileObject != NULL && Data->Iopb->MajorFunction == IRP_MJ_WRITE){
if (FileFitsInMask(&(fileObject->FileName))){
DbgPrint(“Begin calling!\n”);
_asm int 3;
try {
notification = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCANNER_NOTIFICATION), ‘nncS’);
if (notification == NULL) {
Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
Data->IoStatus.Information = 0;
status = FLT_PREOP_COMPLETE;
leave;
}
PFLT_FILE_NAME_INFORMATION fileInfo;
status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &fileInfo);
DbgPrint(“GetFileInfo status: %08x\n”, status);
if (NT_SUCCESS(status)){
status = FltParseFileNameInformation(fileInfo);
if (!NT_SUCCESS(status))
{
DbgPrint(“Error in getting filename information: %08x\n”, status);
leave;
}
}
try {
notification->BytesToScan = min(fileInfo->Name.Length, SCANNER_READ_BUFFER_SIZE);
RtlCopyMemory(&notification->Contents, fileInfo->Name.Buffer, notification->BytesToScan);
} except(EXCEPTION_EXECUTE_HANDLER) {
Data->IoStatus.Status = GetExceptionCode();
Data->IoStatus.Information = 0;
DbgPrint(“Error while copying memory: %08x\n”, Data->IoStatus.Status);
status = FLT_PREOP_COMPLETE;
leave;
}
DbgPrint(“Sending: %d %ws\n”, sizeof(SCANNER_NOTIFICATION), notification->Contents);
SCANNER_REPLY_MESSAGE reply;
status = FltSendMessage(gFilterHandle, &ClientPort, notification, sizeof(SCANNER_NOTIFICATION), notification, &replyLength, NULL);
if (status == STATUS_SUCCESS) {
status = ((PSCANNER_REPLY)notification)->SafeToOpen;
}
else {
DbgPrint(“Error sending message to user-mode: %08x\n”, status);
}
}
finally {
if (notification != NULL) {
ExFreePoolWithTag(notification, ‘nncS’);
}
}
Data->IoStatus.Status = STATUS_SUCCESS;
}
}
}
return FLT_POSTOP_FINISHED_PROCESSING;
}

The problem is when I’m trying to save an existing image (using the save icon, say, in Paint) the routine is called three times:

  1. first and second, for temp directories, and the filepath in the debug output is:
    \Users\Elf\AppData\Local\Temp\xx1.png
    and
    \Users\Elf\AppData\Local\Temp\xx1 (2).png
  2. but th filepath for the real image is corrupted:
    \Users\Elf\Desktop\xx1.pngemp\xx1.png.png.
    So FltGetFileNameInformation returnes STATUS_FLT_INVALID_NAME_REQUEST error.
    I wonder why it happens and how to solve this.
    I tried pasting this code not in Pre- but in PostOperationCallback, but the result stays the same.
    What am i doing incorrectly?? Please help!

Thanks in advance!

P.S. The FileFitsInMask function is defined as follows:

BOOLEAN FileFitsInMask(PUNICODE_STRING fileName)
{
if (fileName == NULL)return FALSE;
if (fileName->Buffer == NULL)return FALSE;
if (wcslen(fileName->Buffer) >= 3){
if (wcscmp(&(fileName->Buffer[wcslen(fileName->Buffer) - 3]), L"png") == 0){
return TRUE;
}
}
return FALSE;
}

Don’t use the filename member of the fileobject in the Write routine.
I would just create a stream context in the Create routine and save all I
need there including name or if the file fits my name filters and then get
the stream context in the write routine and create my notification there if
necessary .
Good luck , hope this helps
On Apr 17, 2015 12:30, wrote:

> Hello!
>
> I’m working on a filesystem minifilter driver that is aimed to catch write
> operation of any image file and to transfer the name of the file to a
> user-mode application via communication port. Here is the code of my
> PreOperationCallback routine:
>
>
> FLT_PREOP_CALLBACK_STATUS PtPreOperationPassThrough(Inout
> PFLT_CALLBACK_DATA Data, In PCFLT_RELATED_OBJECTS FltObjects,
> Flt_CompletionContext_Outptr PVOID *CompletionContext){
> UNREFERENCED_PARAMETER(FltObjects);
> UNREFERENCED_PARAMETER(CompletionContext);
> NTSTATUS status;
> PFILE_OBJECT fileObject;
> PSCANNER_NOTIFICATION notification = NULL;
> ULONG replyLength;
> if (FltObjects->FileObject != NULL && Data != NULL){
> fileObject = Data->Iopb->TargetFileObject;
> if (fileObject != NULL && Data->Iopb->MajorFunction ==
> IRP_MJ_WRITE){
> if (FileFitsInMask(&(fileObject->FileName))){
> DbgPrint(“Begin calling!\n”);
> _asm int 3;
> try {
> notification =
> ExAllocatePoolWithTag(NonPagedPool, sizeof(SCANNER_NOTIFICATION), ‘nncS’);
> if (notification == NULL) {
> Data->IoStatus.Status =
> STATUS_INSUFFICIENT_RESOURCES;
> Data->IoStatus.Information
> = 0;
> status =
> FLT_PREOP_COMPLETE;
> leave;
> }
> PFLT_FILE_NAME_INFORMATION
> fileInfo;
> status =
> FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED |
> FLT_FILE_NAME_QUERY_DEFAULT, &fileInfo);
> DbgPrint(“GetFileInfo status:
> %08x\n”, status);
> if (NT_SUCCESS(status)){
> status =
> FltParseFileNameInformation(fileInfo);
> if (!NT_SUCCESS(status))
> {
> DbgPrint(“Error in
> getting filename information: %08x\n”, status);
> leave;
> }
> }
> try {
> notification->BytesToScan
> = min(fileInfo->Name.Length, SCANNER_READ_BUFFER_SIZE);
>
> RtlCopyMemory(&notification->Contents, fileInfo->Name.Buffer,
> notification->BytesToScan);
> }
> except(EXCEPTION_EXECUTE_HANDLER) {
> Data->IoStatus.Status =
> GetExceptionCode();
> Data->IoStatus.Information
> = 0;
> DbgPrint(“Error while
> copying memory: %08x\n”, Data->IoStatus.Status);
> status =
> FLT_PREOP_COMPLETE;
> leave;
> }
> DbgPrint(“Sending: %d %ws\n”,
> sizeof(SCANNER_NOTIFICATION), notification->Contents);
> SCANNER_REPLY_MESSAGE reply;
> status =
> FltSendMessage(gFilterHandle, &ClientPort, notification,
> sizeof(SCANNER_NOTIFICATION), notification, &replyLength, NULL);
> if (status == STATUS_SUCCESS) {
> status =
> ((PSCANNER_REPLY)notification)->SafeToOpen;
> }
> else {
> DbgPrint(“Error sending
> message to user-mode: %08x\n”, status);
> }
> }
> finally {
> if (notification != NULL) {
>
> ExFreePoolWithTag(notification, ‘nncS’);
> }
> }
> Data->IoStatus.Status = STATUS_SUCCESS;
> }
> }
> }
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
>
> The problem is when I’m trying to save an existing image (using the save
> icon, say, in Paint) the routine is called three times:
> 1) first and second, for temp directories, and the filepath in the debug
> output is:
> \Users\Elf\AppData\Local\Temp\xx1.png
> and
> \Users\Elf\AppData\Local\Temp\xx1 (2).png
> 2) but th filepath for the real image is corrupted:
> \Users\Elf\Desktop\xx1.pngemp\xx1.png.png.
> So FltGetFileNameInformation returnes STATUS_FLT_INVALID_NAME_REQUEST
> error.
> I wonder why it happens and how to solve this.
> I tried pasting this code not in Pre- but in PostOperationCallback, but
> the result stays the same.
> What am i doing incorrectly?? Please help!
>
> Thanks in advance!
>
> P.S. The FileFitsInMask function is defined as follows:
>
> BOOLEAN FileFitsInMask(PUNICODE_STRING fileName)
> {
> if (fileName == NULL)return FALSE;
> if (fileName->Buffer == NULL)return FALSE;
> if (wcslen(fileName->Buffer) >= 3){
> if (wcscmp(&(fileName->Buffer[wcslen(fileName->Buffer) -
> 3]), L"png") == 0){
> return TRUE;
> }
> }
> return FALSE;
> }
>
> —
> NTFSD is sponsored by OSR
>
> OSR is hiring!! Info at http://www.osr.com/careers
>
> For our schedule of debugging and file system seminars visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

Thank you for reply!
I tried to store the filename in file context using the example from WDK (Ctx File System Minifilter Driver). But that doesn’t still work properly. Am I able to transfer filename to write routine from create routine using file context, or must I use only the stream context?
As for the issues, they are:

  1. In previous version, without using file context, the real image filename was corrupted, and now it is replaced by the name of a temporary file(WinDbg output:
    “Trying to get file context (FileObject = 84BD9DC8, Instance = 84738008)
    Filename stored in fileObject->FileName.Buffer: \Users\Elf\Desktop\11.pngTemp\11.png.png
    PtPreOperationPassThrough: File context info for file (Data = 846E78D8, FileObject = 84BD9DC8, FileContext = 9E8DF770) Name = \Device\HarddiskVolume2\Users\Elf\AppData\Local\Temp\11 (2).png”);
  2. I wonder why the context is recreated sometimes for the same file?
  3. Is it correct, that the file objects for one filename are different?

Looks like a rename operation going on. Look for SL_open_target_directory
flag.
You were using the Name member of the file object which is valid only in
Create.
Regardless though, I would use stream contexts in this case.
2)The context is not recreated, because you create it, assign it and manage
its lifetime through reference/dereference calls. Please pay more attention
to that sample and look at how contexts are assigned and look at what that
set context function does and especially its params. You can replace an
existing context you previously set or get the existing one if any is there.
3) I really don’t know what you mean with point 3.
Each successful CreateFile = new FileObject created by the FS. Keep in mind
that some FSs support more features than others like ADS for example.
On Apr 20, 2015 20:18, wrote:

> Thank you for reply!
> I tried to store the filename in file context using the example from WDK
> (Ctx File System Minifilter Driver). But that doesn’t still work properly.
> Am I able to transfer filename to write routine from create routine using
> file context, or must I use only the stream context?
> As for the issues, they are:
> 1) In previous version, without using file context, the real image
> filename was corrupted, and now it is replaced by the name of a temporary
> file(WinDbg output:
> “Trying to get file context (FileObject = 84BD9DC8, Instance = 84738008)
> Filename stored in fileObject->FileName.Buffer:
> \Users\Elf\Desktop\11.pngTemp\11.png.png
> PtPreOperationPassThrough: File context info for file (Data = 846E78D8,
> FileObject = 84BD9DC8, FileContext = 9E8DF770) Name =
> \Device\HarddiskVolume2\Users\Elf\AppData\Local\Temp\11 (2).png”);
> 2) I wonder why the context is recreated sometimes for the same file?
> 3) Is it correct, that the file objects for one filename are different?
>
> —
> NTFSD is sponsored by OSR
>
> OSR is hiring!! Info at http://www.osr.com/careers
>
> For our schedule of debugging and file system seminars visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

In addition to what Gabriel said, yes, you can use file context as well.
But be aware that using a File Context means you’ll get the same context
for all streams of a file (see
http://blogs.technet.com/b/askcore/archive/2013/03/24/alternate-data-streams-in-ntfs.aspx).
Using a stream context won’t have this issue, BUT you’ll get the same
context even when a file has hardlinks (see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365006(v=vs.85).aspx;
this means that if a stream has two names , C:\foo.txt and C:\bar.txt,
you’ll get the same stream so if you cache the name there you might the
wrong one sometimes). You could use a StreamHandle context and you’ll only
get the context for each specific FILE_OBJECT. It’s not clear to me what
you do with the name after you pass it to the user mode component (scan it
? log it ?) but that might matter for your choice of context:see
http://fsfilters.blogspot.com/2010/02/names-and-file-systems-filters.html.

Thanks,
Alex.

On Mon, Apr 20, 2015 at 11:43 AM, Gabriel Bercea wrote:

> Looks like a rename operation going on. Look for SL_open_target_directory
> flag.
> You were using the Name member of the file object which is valid only in
> Create.
> Regardless though, I would use stream contexts in this case.
> 2)The context is not recreated, because you create it, assign it and
> manage its lifetime through reference/dereference calls. Please pay more
> attention to that sample and look at how contexts are assigned and look at
> what that set context function does and especially its params. You can
> replace an existing context you previously set or get the existing one if
> any is there.
> 3) I really don’t know what you mean with point 3.
> Each successful CreateFile = new FileObject created by the FS. Keep in
> mind that some FSs support more features than others like ADS for example.
> On Apr 20, 2015 20:18, wrote:
>
>> Thank you for reply!
>> I tried to store the filename in file context using the example from WDK
>> (Ctx File System Minifilter Driver). But that doesn’t still work properly.
>> Am I able to transfer filename to write routine from create routine using
>> file context, or must I use only the stream context?
>> As for the issues, they are:
>> 1) In previous version, without using file context, the real image
>> filename was corrupted, and now it is replaced by the name of a temporary
>> file(WinDbg output:
>> “Trying to get file context (FileObject = 84BD9DC8, Instance = 84738008)
>> Filename stored in fileObject->FileName.Buffer:
>> \Users\Elf\Desktop\11.pngTemp\11.png.png
>> PtPreOperationPassThrough: File context info for file (Data = 846E78D8,
>> FileObject = 84BD9DC8, FileContext = 9E8DF770) Name =
>> \Device\HarddiskVolume2\Users\Elf\AppData\Local\Temp\11 (2).png”);
>> 2) I wonder why the context is recreated sometimes for the same file?
>> 3) Is it correct, that the file objects for one filename are different?
>>
>> —
>> NTFSD is sponsored by OSR
>>
>> OSR is hiring!! Info at http://www.osr.com/careers
>>
>> For our schedule of debugging and file system seminars visit:
>> http://www.osr.com/seminars
>>
>> To unsubscribe, visit the List Server section of OSR Online at
>> http://www.osronline.com/page.cfm?name=ListServer
>>
> — NTFSD is sponsored by OSR OSR is hiring!! Info at
> http://www.osr.com/careers For our schedule of debugging and file system
> seminars visit: http://www.osr.com/seminars To unsubscribe, visit the
> List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

Many thanks you for your replies!
I’ve just tried using the stream context instead of file context, and it works fine. Thank you also for usefull links, I’ll study them as soon as my reply is done.
As for the purpose of getting the filename in user mode, i need it to open the image as a bitmap using gdiplus library. The aim is to put into the image a watermark after it was modified.
I know that this is not the best solution, because there will become a cycle of write requests, but i expect to track the name of the process performing the write request to prevent it. Becides, i don’t know yet if it would be allowed to write the file right after the PreCallback routine of the driver is finished. But i also expect to wait for a while in order to let the filesystem manager finish with the file.
There seems to be another way of watermarking the image - to pass Data->Iopb->Parameters.Write struct to user mode and treat it as a bitmap. But the size of this struct (which includes a bitmap of several MB size) probably exceeds the limit FltSendMessage function could hold. In addition, i didn’t find any examples of considering that buffer as bitmap and I’m not sure I could implement that.

Hmm, I’d say you might be better off by tracking when an image file gets
modified (see a boolean to true when you see a write to the file (or the
region of the file you care about)) and wait for the handle(s) to be closed
and then message your user mode component to watermark it. Trying to do the
watermark inline with the write operation is going to be tricky (for one,
you’ll only see chunks of the image being written, there isn’t necessarily
ONE big contiguous buffer write that holds the image contents; also, you
might need to change the size (if the image format is compressed) which is
also tricky to do)…

Thanks,
Alex.

On Tue, Apr 21, 2015 at 11:13 AM, wrote:

> Many thanks you for your replies!
> I’ve just tried using the stream context instead of file context, and it
> works fine. Thank you also for usefull links, I’ll study them as soon as my
> reply is done.
> As for the purpose of getting the filename in user mode, i need it to open
> the image as a bitmap using gdiplus library. The aim is to put into the
> image a watermark after it was modified.
> I know that this is not the best solution, because there will become a
> cycle of write requests, but i expect to track the name of the process
> performing the write request to prevent it. Becides, i don’t know yet if it
> would be allowed to write the file right after the PreCallback routine of
> the driver is finished. But i also expect to wait for a while in order to
> let the filesystem manager finish with the file.
> There seems to be another way of watermarking the image - to pass
> Data->Iopb->Parameters.Write struct to user mode and treat it as a bitmap.
> But the size of this struct (which includes a bitmap of several MB size)
> probably exceeds the limit FltSendMessage function could hold. In addition,
> i didn’t find any examples of considering that buffer as bitmap and I’m not
> sure I could implement that.
>
> —
> NTFSD is sponsored by OSR
>
> OSR is hiring!! Info at http://www.osr.com/careers
>
> For our schedule of debugging and file system seminars visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>