ObjectID / FileIndex NUmbers

Hi there,

New to the list, and WINTEL driver dev so please be patient.

I am trying to get at the Windows unique identifiers for disk based objects (files/dirs)

I have found that there exists the following:

  1. FILE_OBJECTID_INFORMATION -> ObjectId;
    used by ZwQueryDirectoryFile()
  2. FILE_INTERNAL_INFORMATION -> IndexNumber;
    used by FltQueryInformationFile() or ZwQueryInformationFile()
  3. BY_HANDLE_FILE_INFORMATION -> nFileIndex(High|Low);
    used by GetFileInformationByHandle();

I hope there are more but my problem is this.

I am trying to get the target objects unique file id while INSIDE the Pre OP of IRP_MJ_CREATE.

So, is it possible? Or do I need to do this in the Post OP phase?

I have the following code but it BSODs every time on the ZwCreateFile:

static BOOLEAN RTDG_GetObjectInfo(PCFLT_RELATED_OBJECTS FrObjs, LPCWSTR Path)
{
NTSTATUS Status;
FILE_INTERNAL_INFORMATION FileInfo;
UNICODE_STRING FilePath;
OBJECT_ATTRIBUTES FileObjAttr;
HANDLE FileHandle;
IO_STATUS_BLOCK FileISB;
ULONG FileInfoLen;

if(FrObjs == NULL)
return FALSE;

RtlInitUnicodeString(&FilePath, Path);

InitializeObjectAttributes(&FileObjAttr, &FilePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

if(KeGetCurrentIrql() != PASSIVE_LEVEL)
return FALSE;

Status = ZwCreateFile(&FileHandle,
GENERIC_READ,
&FileObjAttr,
&FileISB,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);

if(!NT_SUCCESS(Status))
return FALSE;

Status = ZwQueryInformationFile(FileHandle, &FileISB, &FileInfo, sizeof(FILE_INTERNAL_INFORMATION), FileInternalInformation);
if(!NT_SUCCESS(Status))
{
ZwClose(FileHandle);
return FALSE;
}

ZwClose(FileHandle);

// use the IDs for stuff here…

return TRUE;
}

Any help would really be welcome…

Thx,

Jerry

Well, the file isn’t really opened in preCreate (it might not even exist
yet) so I’d say you’d be better off doing this in postCreate. In that case
you don’t need to open the file at all, you can just call
FltQueryInformationFile with FileInternalInformation from postCreate using
the FILE_OBJECT that was just opened.

If you want help with the bugcheck you must to post the !analyze -v output.

Thanks,
Alex.

Alex,

thanks.

I have one question though, can the filter deny the Create? ie: Permission not allowed?

I will ultimately want to do this for rule based access check on ANY fs access request.

Can this ‘permission denied’ be enforced there?

Jerry

Also, can you prevent the file from be created in the first place at the PostCreate phase?

Jerry

Alex, sorry but, (NEWBIE) can you point me at the relevant docs on how to do that ‘bugcheck’ you refer to.

Jerry

Yes the filter can deny the create in postCreate. It is more complicated
considering that the file has already been opened (and thus possibly
overwritten) but it is possible.

However, blocking a create is usually easier in preCreate. Preventing a file
from being created on the file system is also only possible in preCreate. In
postCreate the file has been created and while you might try to delete the
file, it will still exist on the file system for a while.

Since the answers to your questions depend heavily on what you are trying to
do, could you please tell us what that is ?

Bugcheck is another term for a BSOD.

Thanks,
Alex.

OK…

First, what is the command to run to be able to do that " !analyze -v output" that you refer to.

I usually work in UNIX and its quite different there.

So, I am trying to ‘recreate’ a unix filesystem filter that has rules about what can/cannot be done based on the unix triplet for path. It does not use the path but rather loads the Inode/Major/Minor triplet.

Inodes are unique IDs for objects on the Unix FS and the Major/Minor indicate the storage device where the inode is located.

If the file that a user is accessing has a ‘no change’ rule, then I can deny permission.
Likewise, ‘no create’ means. Sorry the create must fail.

So, the Windows FileIndex(High/Low) or ObjectID seems like the same as the Inode and the Volume Serial # seems like the Major/Minor.

All I need to do is trap the FS access request(create/delete/change/etc…) and check the rule set.

Does this explain enough?

Jerry

Also, the rules can be based on the directory/folders so if the rule does not allow creation of new objects in some directory, I can look at the directory, or parent all the way up, to see if there are any higher up rules that deny.

So if the file does not exist, thats ok, I look up the tree until pareent dir does, and I have a matching rule.

Jerry

Well, !analyze -v is the command you run in the debugger when you hit a
bugcheck (BSOD). If you don’t use a debugger when developing this then you
should definitely do it, it’s almost impossible to write anything kernel
without one, especially if you’re just learning the ropes.

Then if you want to do this by ID you need to map the file name (which is
generally used in the IRP_MJ_CREATE) to the file id. Doing this in preCreate
is tricky because the name of the file is no associated with the stream
until the actual IRP_MJ_CREATE reaches the file system. Moreover, you need
to do this for every directory up the path to get their ID and see what the
rules are. This is complicated because file systems are very asynchronous.

To prevent a file from being created you must deny the request in preCreate,
it will be too late in postCreate.

To prevent a file from being modified postCreate might be too late,
depending on the create disposition (overwrite and supersede will open and
modify the file in one operation so you don’t get a chance to cancel just
the open). It would be safer to do it in preCreate, but you still have the
issue of reliably associating the file name with ID.

I wrote a blog post about some of the issues here
http://fsfilters.blogspot.com/2010/02/names-and-file-systems-filters.html,
maybe it will help.

Thanks,
Alex.

Alex,
Thanks.

This just confirms that I was looking at the correct path (pre-create).

So, when you are talking about the debugger, do you mean entering "!analyze -v " while the BSOD is visible?

also, any thoughts on mapping file name to id??? is there a lib/system call for this?

I know that if the file does not exist, I must walk up the tree. This is expected.

Jerry

Well, in my experience you don’t see the BSOD if you have a debugger
attached. I would suggest you start by making sure you have the debugger set
up properly.

I know of two ways to get the fileID. If you have an open file you can query
for FileInternalInformation. Alternatively, you can open the parent
directory for the file and query the FileObjectIdInformation,
FileIdBothDirectoryInformation or FileIdFullDirectoryInformation information
class and they all should have the fileID.

“I know that if the file does not exist, I must walk up the tree. This is
expected.” -> Don’t you always have to walk up the tree anyway in case some
parent at some level has some “deny write” flag ?

Thanks,
Alex.

Correct, in part 3 you are right. I always go up the tree starting at the user specified path until I hit a deny…

As for the debugger, I am clearly not asking the question in the right way.

What is the name of the debugger and how do I start it?.. I know they exist for Windows but, I have no idea where to find it.

Thanks,
Jerry

Also, a simple enough question but, maybe not so simple an answer.

Can you open a file with ZwCreateFile() while inside the PreCreate() ?

I know the file may not exist, in which case I look at the directory, and up, and up…until I and able to get the ObjectID for something that does exist.

I am assuming that I can open/read a directory while in the PreCreate().

Jerry

You get some debuggers from Microsoft with the WDK. The one I use is WinDBG
and it does come with the WDK.

You can open a file inside preCreate, but you should make sure to layer the
IRP_MJ_CREATE request properly (ZwCreateFile sends the request to the top of
the IO stack) (there is another blog post,
http://fsfilters.blogspot.com/2011/02/filter-layering-and-io-targeting-in.ht
ml). You should probably use IoCreateFileEx or
IoCreateFileSpecifyDeviceObjectHint if writing a legacy filter or
FltCreateFile if using a minifilter (which I would recommend).

Thanks,
Alex.

As Alex states, WinDbg and several variants, all based on the same core, are installed in the path “Debugging Tools for Windows (x86/x64)” and will be in the version paths of the WDK.

Gary G. Little

----- Original Message -----
From: “mtb daily”
To: “Windows File Systems Devs Interest List”
Sent: Wednesday, March 30, 2011 1:27:03 PM
Subject: RE:[ntfsd] ObjectID / FileIndex NUmbers

Correct, in part 3 you are right. I always go up the tree starting at the user specified path until I hit a deny…

As for the debugger, I am clearly not asking the question in the right way.

What is the name of the debugger and how do I start it?.. I know they exist for Windows but, I have no idea where to find it.

Thanks,
Jerry


NTFSD is sponsored by OSR

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

> First, what is the command to run to be able to do that " !analyze -v output" that you refer to.

!analyze -v is in WinDbg debugger, this command prints lots of info about the crash which has already occured.

Works for both dumps and live debugging.

Try calling FltCreateFile(Ex)(2) in preCreate for the same file with FILE_READ_ATTRIBUTES, and read the inode (in Windows, it is called “MFT record”) number aka NTFS file ID.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

> also, any thoughts on mapping file name to id??? is there a lib/system call for this?

No, except opening the file and querying its file ID by handle or FILE_OBJECT.

Note that not all Windows file systems support file IDs, I think SMB redirector does not.

So, to solve you task in Windows, you will need to parse the pathname and check the rules by its Dir1, Dir1\Dir2, Dir1\Dir2\Dir3 parts.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

First off, thanks to all that helped me out.

Alex, your blogs are very informative.

Now, it seems to work and I must go get the Volume Serial Number now but, I have another question.

here is the code:
//
// Check if an object exists on disk and get its ID if it does.
// return TRUE if we got it
// return FALSE if we did not
// set ObjExists = TRUE if the target exists.
// set ObjExists = FALSE if we does not.
//
static BOOLEAN GetTargetID(PCFLT_RELATED_OBJECTS FrObjs, PUNICODE_STRING UniPath, BOOLEAN *ObjExists)
{
NTSTATUS Status;
FILE_INTERNAL_INFORMATION FileInfo;
OBJECT_ATTRIBUTES FileObjAttr;
HANDLE FileHandle;
IO_STATUS_BLOCK FileISB;
ULONG FileInfoLen;

*ObjExists = FALSE; // default to ‘does not exist’…

InitializeObjectAttributes(&FileObjAttr, UniPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

if(IoGetTopLevelIrp() != NULL) // If IoGetTopLevelIrp() returns != NULL, do not call as FltQueryInformationFile() can cause a system deadlock.
{
LogWrite(“GetTargetID: ???(BAD IRP) %.*ls\r\n”, UniPath->Length/sizeof(WCHAR), UniPath->Buffer);
*ObjExists = FALSE;
goto GetTargetID_FALSE;
}

Status = FltCreateFile( gFilterHandle, FrObjs->Instance, &FileHandle, FILE_READ_ATTRIBUTES, &FileObjAttr, &FileISB, NULL,
FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0, IO_FORCE_ACCESS_CHECK);
if(!NT_SUCCESS(Status))
{
if(FileISB.Information == FILE_DOES_NOT_EXIST) // this does not seem to work. But, just in case.
{
*ObjExists = FALSE;
LogWrite(“GetTargetID: no %.*ls\r\n”, UniPath->Length/sizeof(WCHAR), UniPath->Buffer);
goto GetTargetID_FALSE;
}

if(Status == STATUS_OBJECT_NAME_NOT_FOUND) // this is expected if the target is not there.
{
*ObjExists = FALSE;
LogWrite(“GetTargetID: NO %.*ls\r\n”, UniPath->Length/sizeof(WCHAR), UniPath->Buffer);
goto GetTargetID_FALSE;
}
else if(Status == STATUS_SHARING_VIOLATION) // Hmm, cant get object ID… Bummer.
{
*ObjExists = TRUE;
LogWrite(“GetTargetID: YES STATUS_SHARING_VIOLATION %.*ls\r\n”, UniPath->Length/sizeof(WCHAR), UniPath->Buffer);
goto GetTargetID_FALSE;
}
else
LogWrite(“GetTargetID: ???(%X) %.*ls\r\n”, Status, UniPath->Length/sizeof(WCHAR), UniPath->Buffer);

*ObjExists = FALSE; // got some unexpected error.
goto GetTargetID_FALSE;
}

*ObjExists = TRUE;
FileInfoLen = 0;

memset(&FileInfo, 0, sizeof(FileInfo));

//Status = FltQueryInformationFile(FrObjs->Instance, FrObjs->FileObject, &FileInfo, sizeof(FileInfo), FileInternalInformation, &FileInfoLen);

Status = ZwQueryInformationFile(FileHandle, &FileISB, &FileInfo, sizeof(FileInfo), FileInternalInformation);
if(!NT_SUCCESS(Status))
{
if(Status == STATUS_INVALID_DEVICE_REQUEST)
LogWrite(“GetTargetID: YES STATUS_INVALID_DEVICE_REQUEST %.*ls\r\n”, UniPath->Length/sizeof(WCHAR), UniPath->Buffer);
else if(Status == STATUS_INVALID_PARAMETER)
LogWrite(“GetTargetID: YES STATUS_INVALID_PARAMETER %.*ls\r\n”, UniPath->Length/sizeof(WCHAR), UniPath->Buffer);
else
LogWrite(“GetTargetID: YES(%X) %.*ls\r\n”, Status, UniPath->Length/sizeof(WCHAR), UniPath->Buffer);

FltClose(FileHandle);
goto GetTargetID_FALSE;
}

FltClose(FileHandle);

LogWrite(“GetTargetID: ID(%8.8X-%8.8X) %.*ls\r\n”, FileInfo.IndexNumber.HighPart, FileInfo.IndexNumber.LowPart, UniPath->Length/sizeof(WCHAR), UniPath->Buffer);

return TRUE;

GetTargetID_FALSE:
return FALSE;
}

The out put from Zwxxx is below.

GetTargetID: ID(003F0000-000022C5) \Device\HarddiskVolume1\WINDOWS\Prefetch\FLTMC.EXE-14D13
GetTargetID: ID(00020000-0000F724) \Device\HarddiskVolume1\WINDOWS\AppPatch\acgenral.dll
GetTargetID: ID(00020000-0000F1F7) \Device\HarddiskVolume1\WINDOWS\AppPatch\drvmain.sdb
GetTargetID: ID(003D0000-00004D6A) \Device\HarddiskVolume1\WINDOWS\AppPatch\sysmain.sdb
GetTargetID: ID(00070000-00001D0A) \Device\HarddiskVolume1\WINDOWS\system32\advapi32.dll
GetTargetID: ID(00060000-00006F0A) \Device\HarddiskVolume1\WINDOWS\system32\comctl32.dll
GetTargetID: ID(00010000-000000ED) \Device\HarddiskVolume1\WINDOWS\system32\ctype.nls
GetTargetID: ID(000D0000-00014C30) \Device\HarddiskVolume1\WINDOWS\system32\drivers\VIEWINT
GetTargetID: ID(00020000-0001007B) \Device\HarddiskVolume1\WINDOWS\system32\fltlib.dll
GetTargetID: ID(00020000-0001007A) \Device\HarddiskVolume1\WINDOWS\system32\fltmc.exe
GetTargetID: ID(00070000-00005B57) \Device\HarddiskVolume1\WINDOWS\system32\gdi32.dll
GetTargetID: ID(000C0000-0000575C) \Device\HarddiskVolume1\WINDOWS\system32\kernel32.dll
GetTargetID: ID(00040000-00009144) \Device\HarddiskVolume1\WINDOWS\system32\locale.nls
GetTargetID: ID(00020000-0000F46A) \Device\HarddiskVolume1\WINDOWS\system32\msacm32.dll
GetTargetID: ID(00020000-0000F414) \Device\HarddiskVolume1\WINDOWS\system32\msvcrt.dll
GetTargetID: ID(000D0000-00005D56) \Device\HarddiskVolume1\WINDOWS\system32\netapi32.dll
GetTargetID: ID(00350000-0000254D) \Device\HarddiskVolume1\WINDOWS\system32\ntdll.dll
GetTargetID: ID(00170000-00006117) \Device\HarddiskVolume1\WINDOWS\system32\ole32.dll
GetTargetID: ID(00020000-0000F1DB) \Device\HarddiskVolume1\WINDOWS\system32\oleaut32.dll
GetTargetID: ID(00020000-0000F38C) \Device\HarddiskVolume1\WINDOWS\system32\psapi.dll
GetTargetID: ID(00020000-0000F366) \Device\HarddiskVolume1\WINDOWS\system32\rdpsnd.dll
GetTargetID: ID(00240000-00005251) \Device\HarddiskVolume1\WINDOWS\system32\rpcrt4.dll
GetTargetID: ID(00080000-00006A21) \Device\HarddiskVolume1\WINDOWS\system32\secur32.dll
GetTargetID: ID(000F0000-000028C9) \Device\HarddiskVolume1\WINDOWS\system32\shell32.dll
GetTargetID: ID(00020000-0000F318) \Device\HarddiskVolume1\WINDOWS\system32\shimeng.dll
GetTargetID: ID(00060000-00006D67) \Device\HarddiskVolume1\WINDOWS\system32\shlwapi.dll
GetTargetID: ID(00010000-000000EE) \Device\HarddiskVolume1\WINDOWS\system32\sortkey.nls
GetTargetID: ID(00040000-00009124) \Device\HarddiskVolume1\WINDOWS\system32\sorttbls.nls
GetTargetID: ID(00010000-000000DD) \Device\HarddiskVolume1\WINDOWS\system32\unicode.nls
GetTargetID: ID(00020000-0000F2A9) \Device\HarddiskVolume1\WINDOWS\system32\user32.dll
GetTargetID: ID(00020000-0000F2A8) \Device\HarddiskVolume1\WINDOWS\system32\userenv.dll
GetTargetID: ID(00020000-0000F2A4) \Device\HarddiskVolume1\WINDOWS\system32\uxtheme.dll
GetTargetID: ID(00020000-0000F29C) \Device\HarddiskVolume1\WINDOWS\system32\version.dll
GetTargetID: ID(00020000-0000F27E) \Device\HarddiskVolume1\WINDOWS\system32\winmm.dll
GetTargetID: ID(00020000-0000F277) \Device\HarddiskVolume1\WINDOWS\system32\winsta.dll

As yo can see, I was trying to use FltQueryInformationFile() but is ALWAYS returned STATUS_INVALID_PARAMETER
so, I switched to ZwQueryInformationFile() and now it works fine.

But, why was Ftlxxx returning the error ?

Thanks,
Jerry

Hi Jerry,

Thanks.

A couple of quick comments. Are you calling this in preCreate ? In that case
I believe that FltQueryInformationFile fails because you call it with
FrObjs->FileObject (which is not open yet) instead of calling it with the
FILE_OBJECT you’ve just got a handle to. If you call FltCreateFileEx(2) you
can also get a reference to the FILE_OBJECT you’ve just opened instead of
just the handle. If you must use FltCreateFile (why?) than you’ll need to
call ObReferenceObjectByHandle(). However, using the handle you got from
FltCreateFile when calling ZwQueryInformationFile (like you already do)
should work as well. You don’t have to always use Flt APIs in a minifilter,
I have yet another post on that topic on my blog.

You can use the IO_IGNORE_SHARE_ACCESS_CHECK to open the file and it will
ignore any other sharing flags on the file (so you don’t get
STATUS_SHARING_VIOLATION). Also, you should probably open with
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE so as not to prevent
any access to the file while you have it open. Windows is really
asynchronous and you’ll run into this sooner or later.

Finally, you should use SYNCHRONIZE in DesiredAccess or be prepared to
handle STATUS_PENDING.

Thanks,
Alex.

Alex,

yes, it is being opened in PreCreate().

Are you saying that even though I open the target successfully with FltCreate the FltQuery will still fail?

Jerry