0

Supposedly it is possible to actually open and read directories on NTFS volumes. However, my code to try this wasn't working, so I tried google, which found me this.

The key observation there seems to be that you must use FILE_FLAG_BACKUP_SEMANTICS. So, trimming that down, I basically get:

HANDLE hFile = CreateFile(L"C:\\temp", GENERIC_READ, FILE_SHARE_READ,
    0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);

DWORD dwFileSize = GetFileSize(hFile, 0);
char* buf = new char[dwFileSize];

DWORD dwBytesRead = 0;
BOOL b = ReadFile(hFile, buf, dwFileSize, &dwBytesRead, 0);

Seems pretty straight-forward. Unfortunately, it doesn't work.

The CreateFile and GetFileSize both work (handle is not INVALID_HANDLE_VALUE, non-zero and plausible file size), but the ReadFile returns FALSE, dwBytesRead is zero, and GetLastError returns 1 ("Incorrect function"). Huh.

While I was typing this question, the 'Similar Questions' prompt showed me this. That business about using AdjustTokenPrivileges made a lot of sense. However, it didn't help. Adding ReadFile (and using c:\temp) to that example gives the same behavior. A closer reading of the CreateFile docs shows that even without the SE_BACKUP_NAME privilege, I should be able to open the file due to admin privileges.

I've tried a number of permutations:

  • Different ways of specifying the directory name (c:\temp, c:\temp\, \\.\c:\temp, \\?\c:\temp\, etc).
  • Different directories
  • Different drives
  • Different share options (0, FILE_SHARE_READ, FILE_SHARE_READ | FILE_SHARE_WRITE)
  • Different access permissions (GENERIC_READ, FILE_LIST_DIRECTORY, FILE_LIST_DIRECTORY + FILE_READ_EA + FILE_READ_ATTRIBUTES, FILE_LIST_DIRECTORY + FILE_READ_EA + FILE_READ_ATTRIBUTES + FILE_TRAVERSE)
  • I can't see any flags that might apply other than FILE_FLAG_BACKUP_SEMANTICS (which I assume is required), but I tried FILE_FLAG_NO_BUFFERING and a 4096 byte aligned buffer. Nope.

I'm (currently) trying 152 permutations, and none of the ReadFiles are working. What am I missing?

Is my original assumption here incorrect? Is it not really possible to 'read' from a directory? Or is there just some trick I'm still missing?

What else should I mention?

  • I'm running as an admin, and can do a CreateFile on the volume.
  • My program is 64bit, built for unicode.
  • Windows 7 x64
  • NTFS 3.1 volume
  • It's cloudy outside (Hey, you never know what might matter...)
Community
  • 1
  • 1
David Wohlferd
  • 7,110
  • 2
  • 29
  • 56
  • 4
    You can open a handle to a directory to do certain things, but calling `ReadFile` on it isn't one of them. – Jonathan Potter Mar 08 '17 at 03:21
  • @JonathanPotter Is that just experience talking? Or do you have a source? – David Wohlferd Mar 08 '17 at 03:30
  • 2
    https://msdn.microsoft.com/en-us/library/windows/desktop/aa365258(v=vs.85).aspx has a list of functions that you can pass a directory handle to. – Jonathan Potter Mar 08 '17 at 03:33
  • Huh. That does seem pretty definitive. – David Wohlferd Mar 08 '17 at 03:35
  • 1
    What do you expect to read from a directory? A file list? – Anders Mar 08 '17 at 03:37
  • If that *is* what you were after, `GetFileInformationByHandleEx` can do it. – Harry Johnston Mar 08 '17 at 03:39
  • @Anders Based on [this](https://msdn.microsoft.com/en-us/library/windows/desktop/aa364404), I was expecting to get the contents of ::$INDEX_ALLOCATION. – David Wohlferd Mar 08 '17 at 03:40
  • 1
    The goal of this question was to be able to do ReadFile on a directory. The answer seems to be pretty clear: You can't. If you want to read the raw $INDEX_ALLOCATION data for a directory, you need to do it another way (which I have already started writing). @JonathanPotter If you want to post your comments as an answer, I'll accept it. – David Wohlferd Mar 08 '17 at 04:51
  • `BackupRead` claims to be able to read from a directory, though I don't know whether the data it reads includes $INDEX_ALLOCATION. – Harry Johnston Mar 08 '17 at 06:33
  • 1
    file system drivers (ntfs also) always return error code on `IRP_MJ_READ` request for directory file. usually `STATUS_INVALID_DEVICE_REQUEST` or `STATUS_INVALID_PARAMETER` – RbMm Mar 08 '17 at 08:54

2 Answers2

0

If you want to open a stream then you need to include the stream name and/or type as part of the path:

  • c:\foo:bar A.K.A. c:\foo:bar:$DATA
  • c:\foo::$INDEX_ALLOCATION

The default $DATA stream is used if you don't specify a stream. $DATA stores a files "normal data".

If you want the list of files in the directory then you can use GetFileInformationByHandleEx(FileIdBothDirectoryInfo) (and NtQueryDirectoryFile on older systems).

Anders
  • 97,548
  • 12
  • 110
  • 164
  • I assume you meant INDEX_ALLOCATION. However it doesn't matter, since ReadFile fails for both. So even if you ignore when the docs say *so "DirName", "DirName::$INDEX_ALLOCATION", and "DirName:$I30:$INDEX_ALLOCATION" are all equivalent.*, I think the answer here is what Jonathan said: ReadFile is just a no-go for directories – David Wohlferd Mar 08 '17 at 03:53
  • `dir C:\Windows:$I30:$INDEX_ALLOCATION` does work for me, you could debug cmd.exe and see what it does. It is certainly possible that you cannot use ReadFile, and even if you could, the binary format is probably undocumented. – Anders Mar 08 '17 at 04:27
  • "you could debug cmd.exe and see what it does" I'm not sure what this would tell me. Yes, this works, but it's not like it needs to do a ReadFile to succeed. It probably just does the GetFileInformationByHandleEx that Harry mentioned. As for the documentation, there probably isn't anything from MS. But there are some discussions [here](https://flatcap.org/linux-ntfs/ntfs/concepts/tree/index.html) and [there](http://www.williballenthin.com/forensics/indx/) that talk about it. – David Wohlferd Mar 08 '17 at 04:46
0

It looks like Jonathan Potter has given the correct answer. Despite prompting, he has elected not to post his comments as an answer. So I'm going to create one based on his responses in order to close the question.

In short: "You can open a handle to a directory to do certain things, but calling ReadFile on it isn't one of them."

What things? These things. This list includes:

  • BackupRead
  • BackupSeek
  • BackupWrite
  • GetFileInformationByHandle
  • GetFileSize
  • GetFileTime
  • GetFileType
  • ReadDirectoryChangesW
  • SetFileTime

In summary: While you can "open" directories and "read" certain information about them, you can't actually use ReadFile. If you want to read the DirName::$INDEX_ALLOCATION information, you'll have to use a different approach.

David Wohlferd
  • 7,110
  • 2
  • 29
  • 56