GenerateFileName callback

Hello!

I am working on an investigation work that is to check if it is possible to feed different names of a file to different applications based on minifilter. For example, for a file called test.txt.foo, the application explorer.exe will get its name as test.txt.foo, but the application notepad.exe will get its name as test.txt. Can it be done? The task doesn’t require to change the path in the file name but only the final component.

I wonder if using GenerateFileName callback can achieve this.

Also, has the sample of name provider been out yet? If yes, which version Win DDK has it?

Thank you in advance.

Heidi

Presuming you have ways to determine which app will get which name, this is already achievable via directory listing and reparsing. During listing you would give the app the file name it is supposed to use, and during file open, you would reparse to the real file name if the name is different.
(e.g. reparse test.txt to test.txt.foo for Notepad)

xxxxx@yahoo.com wrote:

Hello!

I am working on an investigation work that is to check if it is possible to feed different names of a file to different applications based on minifilter. For example, for a file called test.txt.foo, the application explorer.exe will get its name as test.txt.foo, but the application notepad.exe will get its name as test.txt. Can it be done? The task doesn’t require to change the path in the file name but only the final component.

I wonder if using GenerateFileName callback can achieve this.

Also, has the sample of name provider been out yet? If yes, which version Win DDK has it?


Kind regards, Dejan (MSN support: xxxxx@alfasp.com)
http://www.alfasp.com
File system audit, security and encryption kits.

Thank you.

I have the following questions,

“Presuming you have ways to determine which app will get which name”
Do you mean the file name can be cached and shared between applications (e.g. explorer.exe and notepad.exe in this case)? Therefore, it is not obvious determine the application that retrieves the name.

If the above is true, is there a way to disable name caching? For example, set CacheFileNameInformation to false in GenerateFileName callback.

Thank you.

Heidi

Yes, setting CacheFileNameInformation to FALSE instructs FltMgr to not cache the name you return. However, I’ve always had doubts about how useful this really is in practice. For example, a minifilter above yours could do something similar and generate another name based on the name you provide (let’s say it always wants to change the directory path) and that minifilter doesn’t set CacheFileNameInformation to FALSE. I don’t remember exactly but I think FltMgr will cache that name for that stream, so the name will be cached at a higher level (yes, FltMgr’s name cache has layers and so a name might not be cached at one level but cached at another one).

Another thing to note is that if the name is associated with the stream, then it is possible it has already been cached. So in order to guarantee that this doesn’t happen for a file that you care about your minfilter must be running from boot time and must disable caching for all names, regardless of whether they are generated by you or not, so that your minifilter will always be called to resolve the name.

However, please note that the OS has support for multiple names for the same file, hardlinks. FltMgr also knows about this and does things properly (keeps the name cached at FO level and not at FCB level; similar to caching the name in the StreamHandle context as opposed to the Stream context). Since you want to separate the names that different processes use, I think you might get away with it because presumably these processes will be using different FOs. What you would need to do is simply fake the presence of hardlinks for the stream and then return a different name for each FO, depending on your rules. FltMgr and the IO manager should be ok with it…

If you will always be running on top of NTFS you might not even need to fake hardlinks at all… You might be able to use NTFS and simply create the hardlink and then just filter out the entries from directory enumeration so that in your example notepad.exe never even sees test.txt.foo and explorer.exe never sees test.txt.

Finally, you might need to consider interop issues. For example, an AV filter (mini or otherwise) will query the name for a file in the context of the process that is opening the file. However, in some cases they simply forward the name to some user mode component for scanning. So the user mode scanning service will receive both names to a file, test.txt when notepad.exe is trying to open it and test.txt.foo when explorer is opening it. If you block one of these names then you risk disabling the on-access feature of the AV. I’m not sure what your architecture is but you might need to allow some processes (or all processes even ?) to open either name, and only control the names they get by enumeration and FltGetFileNameInformation (so that if a process somehow gets the “wrong” name, they can still function…).

Thanks,
Alex.

Thank you very much!

I wonder why I need “control the name they get by” “FltGetFileNameInformation”?

I have an experiment driver which intercepts IRP_MJ_DIRECTORY_CONTROL’s IRP_MN_QUERY_DIRECTORY and fake the name (e.g. for notepad.exe, the name is changed to test.txt from its real name “test.txt.foo”). Then, obviously, notepad.exe can’t find the file test.txt and can’t open the file.

I am thinking that I need to intercept IRP_MJ_CREATE to reparse to the real file name. How to do that? I checked FLT_PARAMETERS for IRP_MJ_CREATE, but didn’t find fileName member or any related. Do I have to redirect to the real file at the post create? Or can this be achieved by what you said control FltGetFileNameInformation?

Please advise. Thank you.

“I am thinking that I need to intercept IRP_MJ_CREATE to reparse to the real
file name. How to do that? I checked FLT_PARAMETERS for IRP_MJ_CREATE, but
didn’t find fileName member or any related. Do I have to redirect to the
real file at the post create? Or can this be achieved by what you said
control FltGetFileNameInformation?”

There are some samples in the WDK about obtaining the file name during
IRP_MJ_CREATE.

About intercepting the IRP_MJ_CREATE to open the real file, you have
multiple choices. You could :

  1. reparse from the non-existing name to the existing name (from test.txt to
    test.txt.foo) when you get a wrong name in your filter.
  2. use a shadow file object approach and open the proper file from your
    filter.
  3. use the hardlink approach that I suggested in a previous mail in which
    case you don’t need to do anything about creates since either name will
    work.

The reason you care about FltGetFileNameInformation is because filters above
yours and some system components use FltGetFileNameInformation to get the
file name and if you don’t virtualize it (if you don’t implement the name
provider callback) then they might get the wrong file name. Some things that
might be broken by this are anti-virus products (they might get the wrong
file name and either try to scan a non-existing file or scan a different
file altogether), tools like procmon and other filters that log file
accesses from a filter, network applications that might fail because the
firewall relies on FltMgr’s name resolution to get the file path and so
might not open ports for an application because it gets the wrong path and
so on.

Thanks,
Alex.

Thank you, Alex.

Just want to confirm that implementing the name provider callback will make sure FltGetFileNameInformation provides the real name of the file to system components. Please correct me if I misunderstand.

I just notice the SimRep minifilter example and it does reparsing the file at IRP_MJ_CREATE. Thank you.

Yep, SimRep uses a reparse mechanism. However, please note that there are
all sorts of things that don’t work well with SimRep, since its purpose was
only to show how to return STATUS_REPARSE from a minifilter and not how to
deal with all potential additional problems that approach introduces.

Thanks,
Alex.

Alex,

Thank you.

Can you tell me what kind of additional problems that reparsing approach causes?

Regards.

Heidi

Assuming you have implemented name provider callbacks there aren’t really many problems. This is a standard NT mecanism so it mostly just works. Some additional issues you might run into with STATUS_REPARSE are:

  1. some clients might be able to see the real file path (unless you take over IRP_MJ_QUERY_INFORMATION and all the other places where paths are returned)
  2. any call to IoCreateFileSpecifyDeviceObjectHint and IoCreateFileEx with a device hint above your filter will fail if you reparse to a different volume (which i don’t think applies to your scenario ?).

Anyway, in general it’s a pretty stable and well tested approach.

Thanks,
Alex.

Hello!

Thank you for the information.

I am implementing the name provider callbacks but stuck at coding the callback of NormalizeNameComponent. I think that in my case I don’t need to do any manipulation but just forward the request to the driver component below and send it back to the upper driver. However, I don’t know how/where to apply “FLT_NORMALIZE_NAME_FLAGS Flags” in the code.

The following is my code. Please advise. Thank you.

(P.S. It would be really nice if MS can provide a simple name provider example in its DDK.)

==============================================================

NTSTATUS MyNormalizeNameComponentCallback(
__in PFLT_INSTANCE Instance,
__in PCUNICODE_STRING ParentDirectory,
__in USHORT VolumeNameLength,
__in PCUNICODE_STRING Component,
__out PFILE_NAMES_INFORMATION ExpandComponentName,
__in ULONG ExpandComponentNameLength,
__in FLT_NORMALIZE_NAME_FLAGS Flags,
__inout PVOID *NormalizationContext
)
{
FILE_NAMES_INFORMATION fileNameInfo;
OBJECT_ATTRIBUTES oa = {0};
IO_STATUS_BLOCK iosb = {0};
PFLT_FILTER filter;
HANDLE dirHandle;
ULONG lengthRet=0;
PFILE_OBJECT dirObject = NULL;
NTSTATUS status=FltGetFilterFromInstance(Instance,
&filter);

if(status != STATUS_SUCCESS)
return status;

InitializeObjectAttributes(&oa,
ParentDirectory,
OBJ_KERNEL_HANDLE,
NULL,
NULL);

status = FltCreateFile(filter,
Instance,
&dirHandle,
GENERIC_READ,
&oa,
&iosb,
NULL,
0 , // File Attribs
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
FILE_OPEN,
FILE_DIRECTORY_FILE |FILE_SYNCHRONOUS_IO_NONALERT,
NULL, //EaBuffer
0, // EaBuffer length
IO_IGNORE_SHARE_ACCESS_CHECK); // Flags

if(status != STATUS_SUCCESS)
return status;

status = ObReferenceObjectByHandle(dirHandle, //Handle
FILE_GENERIC_READ, //DesiredAccess
*IoFileObjectType, //FileObjectType
KernelMode, //AccessMode
&dirObject, //object
NULL);
if(status != STATUS_SUCCESS) {
FltClose(dirHandle);
return status;
}

status=FltQueryDirectoryFile(Instance,
dirObject,
ExpandComponentName,
sizeof(FILE_NAMES_INFORMATION),
FileNamesInformation,
TRUE,
Component,
TRUE,
&lengthRet);
if(status != STATUS_SUCCESS) {
PT_DBG_PRINT(PTDBG_TRACE_OPERATION_STATUS,
(“PT!MyNormalizeNameComponent: FltQueryDirectoryfile(0x%x)\n”,
status));
ObDereferenceObject(dirObject);
FltClose(dirHandle);
return status;
}

ObDereferenceObject(dirObject);
FltClose(dirHandle);
return STATUS_SUCCESS;
}

I don’t think you need to care about
FLTFL_NORMALIZE_NAME_DESTINATION_FILE_NAME . However, based on the code
fragment below you should set OBJ_CASE_INSENSITIVE if
FLTFL_NORMALIZE_NAME_CASE_SENSITIVE is not set. Otherwise all your requests
are going to be case sensitive and you will fail normalization requests that
would have worked if they were case insensitive.

Thanks,
Alex.

Alex,

Thank you for your patients and help!

Heidi