Associate original process ID for IRP_MJ_CREATE from localhost UNC file access

I’m trying to protect a file from modification by certain processes using a filesystem mini filter driver. To do this I’m relying on being able to determine the process name and a normalized local file path of the form \Device\HarddiskVolumeN\path\to\file Currently there is a workaround for the protection that allows access to a protected file using a UNC path like \localhost\c$\protected.txt Let’s say, for example, I want to prevent notepad.exe on the local system from opening that file (I’m not trying to prevent notepad on a remote system from opening the file…I understand I won’t be able to get the process name from a remote system). When notepad opens the file normally I can see the file path normalized with FltGetFileNameInformation and I can get the process name as well since the IRP happens in the context of notepad. If notepad opens the file by UNC path like \localhost\c$\protected.txt I get an IRP_MJ_NETWORK_QUERY_OPEN in the context of notepad but the file path is the UNC path and I need the \Device\HardiskVolumeN version. If I return FLT_PREOP_DISALLOW_FASTIO the IRP comes back down as an IRP_MJ_CREATE also from notepad.exe but still with the UNC path which I don’t need and don’t know how to convert. Eventually I do see an IRP_MJ_CREATE happen on the local file with the path I need but at this point the request is in the context of the SYSTEM process and I can’t figure out how to get the original process. My current thought is I can cache the PID and filename when I see the IRP_MJ_CREATE or IRP_MJ_NETWORK_QUERY_OPEN that happen in the context of notepad. Then, when the IRP_MJ_CREATE happens in the context of SYSTEM, I can lookup in the cache based on filename/share name/etc. since the IRP_MJ_CREATE that happens as system also comes with a PSRV_OPEN_ECP_CONTEXT I have the share name and the socket address in the context of SYSTEM. This has some drawbacks however: I will have to expire entries in this cache, I’ll have to ignore the IP/hostname in the path since I can’t rely on it being “localhost” (it could be, the IP of the machine itself, or anything if the hosts file is modified). Overall I feel like my idea above is to hacky. There must be a way to: A) make some function call to convert \Device\Mup\localhost\c$\protected.txt to \Device\HarddiskVolumeN\protected.txt myself if it truly is a file on the local system or B) Store the PID with the file in a way where I can query for it in the IRP_MJ_CREATE that happens as SYSTEM after the path has been converted to \Device\HarddiskVolumeN\protected.txt for me. I’d rather just store the original PID with the file object but I tried that and the file object is different once I’m in the IRP_MJ_CREATE that runs in the context of SYSTEM.

You want this to be in the NTFSD section, not the NTDEV section.

I’ll move it for you…


1 Like

I figured I’d reply to my post with what I ended up doing to “solve” this problem (still don’t love the solution, but it works for my purposes):

Going off the example above, I want to stop notepad.exe from reading C:\protected.txt even if it’s opened by UNC path like: \\localhost\C$\protected.txt

When opened by UNC path I notice an IRP_MJ_NETWORK_QUERY_OPEN, followed by an IRP_MJ_CREATE both with the UNC path and both in the context of notepad.exe. Some short time later I get another IRP_MJ_CREATE from the SYSTEM process (PID 4) (most likely on behalf of MUP) to open the file by NT device path (e.g. \Device\HarddiskVolumeN\protected.txt). When the SYSTEM IRP_MJ_CREATE happens, the create operation has extra create parameters (ECPs) set. One of them is PSRV_OPEN_ECP_CONTEXT which, when parsed, contains the socket address and share name.

So for my solution, I look for IRP_MJ_CREATE or IRP_MJ_NETWORK_QUERY_OPEN that have the Volume set to \Device\Mup in the FLT_FILE_NAME_INFORMATION structure parsed by calling FltGetFileNameInformation. If I see that, I check to see if any of the files I’m trying to protect have the same FinalComponent. If a request comes in to access \Device\Mup\localhost\C$\protected.txt and I have \Device\HarddiskVolumeN\protected.txt in my “protect these files list”, both of those share the same FinalComponent of protected.txt and so I count that as a match.

That isn’t enough however, because I’m not sure at this point if it really is the file I want to protect. So, I store the file I “think” it is (\Drive\HarddiskVolumeN\protected.txt) along with the share name and process information (e.g. notepad.exe, PID, and TID) in a linked list. Then, when I see the IRP_MJ_CREATE come in as the SYSTEM PID, if it has the PSRV_OPEN_ECP_CONTEXT set and the share name and full path match any item in the list, then I pull the process information from the list and use that instead of SYSTEM. In this case I’d find \Device\HarddiskVolumeN\protected.txt in the list and I’d pull out notepad.exe as the process and then I can check and say “notepad can’t access that” and set the IoStatus to STATUS_ACCESS_DENIED for the IRP. This works great. It does have some drawbacks however. I have to expire entries from the list after some time since there are plenty of cases where items will end up in the list that won’t ever resolve to a local file (e.g. for real network file access on remote systems). There’s also a slim but real chance for false positives but I’m alright with that for now.

I’m still interested if anyone has the “correct” way to solve this problem. It seems like I should be able to ask “Hey MUP - did this network open originate on the local machine, and if it did, tell me the process” - but who knows…hopefully someone here.