RE: ZwQueryInformationFile() fails to enumerate all Alternate Data Streams; No Errors

? Hello,

I was discussing this issue in Microsoft Forums, but it has hit a dead end… No one has any ideas, so our project is stuck, so I hope that someone may have some fresh ideas here… Here is the link in case you would like to see the discussion at Microsoft:

http://social.msdn.microsoft.com/Forums/en-US/wdk/thread/f38c0fd3-aa89-420a-9568-aab9dbd90514

So, our kernel mode driver uses ZwQueryInformationFile() to enumerate Alternate Data Streams in a file. I have a test file that has 3 streams in it and when I run lads (www.securityfocus.comhttp:) to enumerate them, it shows two ADS streams in addition to ::$DATA… Which is what I expected…

? Here is what lads shows for the file streams on my test file:

Scanning directory C:\TESTFOLDER<br>
size ADS in file
---------- ---------------------------------
32 C:\TESTFOLDER\alternateFileStreams.txt:stream1.txt
32 C:\TESTFOLDER\alternateFileStreams.txt:stream2.txt

64 bytes in 2 ADS listed

When I execute the following code, there are no NTSTATUS errors, and the buffer is large enough for the enumerated information, but I am only receiving the ::$DATA stream. FILE_STREAM_INFORMATION’s NextEntryOffset is always zero. Is there a special flag that I need when I open the file, or am I missing something?

I’ve read the documentation and it says that “No specific access rights are required to query this information”, so I’d expect my code to work without any special privileges. Arggg… But, the fact that I can get the default stream and the function calls is successful, makes me suspicious about needing to grant a privilege like SE_BACKUP_NAME.

Thank you for your help! The relevant code follows…

status = ZwCreateFile(
theHandle,
(READ_CONTROL | SYNCHRONIZE),
&attributes, &ioStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
FILE_OPEN,
(FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT),
NULL,
0);

.
.
.
.
status = ZwQueryInformationFile(theOutputHandle,
&ioStatus,
fileStreamInfo,
sizeof(FILE_STREAM_INFORMATION) * multiple[attempt],
FileStreamInformation);

? Here is the info that I collected…

FILENAME BEING PROCESSED…

1: kd> dt fileName
Local var @ 0xfffffa600cfd3e08 Type MYUNICODE_STRING*
0xfffff8800a543690<br> +0x000 Length : 0n100<br> +0x002 MaximumLength : 0n100<br> +0x008 Buffer : 0xfffff8800b54d550 -> 0n92
1: kd> db 0xfffff8800b54d550<br>fffff8800b54d550 5c 00 44 00 6f 00 73 00-44 00 65 00 76 00 69 00 .D.o.s.D.e.v.i.
fffff8800b54d560 63 00 65 00 73 00 5c 00-63 00 3a 00 5c 00 74 00 c.e.s.\.c.:.\.t.<br>fffff8800b54d570 65 00 73 00 74 00 66 00-6f 00 6c 00 64 00 65 00 e.s.t.f.o.l.d.e.
fffff8800b54d580 72 00 5c 00 61 00 6c 00-74 00 65 00 72 00 6e 00 r.\.a.l.t.e.r.n.<br>fffff8800b54d590 61 00 74 00 65 00 46 00-69 00 6c 00 65 00 53 00 a.t.e.F.i.l.e.S.
fffff8800b54d5a0 74 00 72 00 65 00 61 00-6d 00 73 00 2e 00 74 00 t.r.e.a.m.s...t.<br>fffff8800b54d5b0 78 00 74 00 00 00 00 00-00 00 00 00 00 00 00 00 x.t…
fffff8800b54d5c0 08 01 03 03 50 66 46 4b-00 b4 b2 fc fe 07 00 00 ....PfFK........<br>------------------------------------------------------------------------------------<br><br>IOSTATUS AFTER ZWQUERYINFORMATIONFILE<br><br>1: kd&gt; dt ioStatus<br>Local var @ 0xfffffa600cfd3e20 Type _IO_STATUS_BLOCK<br> +0x000 Status : 0n0<br> +0x000 Pointer : (null)<br> +0x008 Information : 0n38<br>-----------------------------------------------------------------------------------<br><br>STREAM BUFFER INFO AFTER ZW... NOTE THAT IT INDICATES NO MORE ENTRIES TO PROCESS...<br><br>1: kd&gt; dt fileStreamInfo<br>Local var @ 0xfffffa600cfd3dd8 Type _FILE_STREAM_INFORMATION*<br>0xfffff8800b346d10
+0x000 NextEntryOffset : 0
+0x004 StreamNameLength : 0n14
+0x008 StreamSize : _LARGE_INTEGER 0x23b
+0x010 StreamAllocationSize : _LARGE_INTEGER 0x1000
+0x018 StreamName : [1] “:”

1: kd> db 0xfffff8800b346d10+0x18<br>fffff8800b346d28 3a 00 3a 00 24 00 44 00-41 00 54 00 41 00 00 00 :.:.$.D.A.T.A…
fffff8800b346d38 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................<br>fffff8800b346d48 00 00 00 00 00 00 00 00-05 01 06 03 46 49 76 6e …FIvn
fffff8800b346d58 10 ec 60 04 80 fa ff ff-5c 00 44 00 65 00 76 00 ..….D.e.v.
fffff8800b346d68 69 00 63 00 65 00 5c 00-48 00 61 00 72 00 64 00 i.c.e.\.H.a.r.d.<br>fffff8800b346d78 64 00 69 00 73 00 6b 00-56 00 6f 00 6c 00 75 00 d.i.s.k.V.o.l.u.
fffff8800b346d88 6d 00 65 00 53 00 68 00-61 00 64 00 6f 00 77 00 m.e.S.h.a.d.o.w.<br>fffff8800b346d98 43 00 6f 00 70 00 79 00-34 00 00 00 12 00 00 00 C.o.p.y.4…</http:>

Is this really copy/pasted from your code? If so you’re using two different
handles:

status = ZwCreateFile(
theHandle,

status = ZwQueryInformationFile(theOutputHandle,

Also, have you tried running Process Monitor in both cases and comparing the
results? Something might jump out.

-scott


Scott Noone
Consulting Associate and Chief System Problem Analyst
OSR Open Systems Resources, Inc.
http://www.osronline.com

Hi Scott,

Thank you for responding! This issue is driving me crazy and I have been wrestling with it for about a week.

Oops! Yes, it is the same handle in the code, but the snippets were from two different functions… Sorry for the confusion!

This issue is very strange… What is stange is that I get the default file stream information, but not the other alternate file streams. In user mode, I would suspect some kind of a security/priv issue, but in a kernel mode driver, file security shouldn’t be an issue.

In general, what kind of strange things would I be looking for in process monitor? If you think of any other ideas, I would really appreciate learning them…

Another alternative would be for me to use IRP_MJ_QUERY_INFORMATION to enumerate the streams, but I have not seen any sample code for using this request in kmdf. Do you know of any samples?

… OMG… What if BOTH methods fail!!! Argggg! I don’t want to think about that possibility!

Mike

>>… OMG… What if BOTH methods fail!!! Argggg! I don’t want to think about that possibility!

Well you still have backup APIs left in UM. check BackupRead

>In user mode, I would suspect some kind of a security/priv issue, but in a kernel mode driver, file security shouldn’t be an issue.

Nope, it works just fine with UM as well. I guess you have not specified FILE_FLAG_BACKUP_SEMANTICS in CreateFile or FILE_OPEN_FOR_BACKUP_INTENT for ZwCreateFile perhaps.

>In user mode, I would suspect some kind of a security/priv issue, but in a

kernel mode driver, file security shouldn’t be an issue.

In my experience there is nothing in NTFS that is going to hide the
alternate data streams from you based on the privileges. IOW, there’s no
special, “list streams” priv that you need to enable in your token.

In general, what kind of strange things would I be looking for in process
monitor? If you think of any other ideas, I would really >appreciate
learning them…

Just comparing the two calls to see if you can spot the difference between
them.

Another alternative would be for me to use IRP_MJ_QUERY_INFORMATION to
enumerate the streams, but I have not seen any sample >code for using this
request in kmdf. Do you know of any samples?

I can’t imagine what building the IRP yourself is going to get you.
ZwQueryInformationFile is just a wrapper around building an
IRP_MJ_QUERY_INFORMATION IRP, so if you can’t get it from one you won’t get
it from the other.

To me, everything looks like this file does NOT have streams on it. Any
chance the streams are created after you try to list them?

-scott


Scott Noone
Consulting Associate and Chief System Problem Analyst
OSR Open Systems Resources, Inc.
http://www.osronline.com

>Nope, it works just fine with UM as well. I guess you have not specified

FILE_FLAG_BACKUP_SEMANTICS in CreateFile or >FILE_OPEN_FOR_BACKUP_INTENT
for ZwCreateFile perhaps.

This isn’t necessary, I have code that works just fine to list the streams
that doesn’t set this bit (and, as far as I can tell, neither does
FindFirstStreamW).

-scott


Scott Noone
Consulting Associate and Chief System Problem Analyst
OSR Open Systems Resources, Inc.
http://www.osronline.com

I suspect you may need FILE_READ_EA access also.

>I suspect you may need FILE_READ_EA access also.

Nope. This works just fine (from user mode):

status = ZwCreateFile(&fileHandle,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&oa,
&iosb,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);

status = ZwQueryInformationFile(fileHandle,
&iosb,
streamInfo,
streamInfoSize,
FileStreamInformation);

-scott


Scott Noone
Consulting Associate and Chief System Problem Analyst
OSR Open Systems Resources, Inc.
http://www.osronline.com

Wow! Thank you everyone for responding and discussing my issue! I really appreciate your discussion and ideas! I was going to ask if someone had a ZwCreateFile() and ZwQueryInformationFile() snippet that I could put in our driver to see if we get the file streams, and I see Scott’s code, so I will give it a try and report back what I learn.

I am aware of using the Backup API in UM, and it is an alternative, but I really hope that we can get this code to work in our kernel mode driver. On a personal note, I know that this code should work and so I feel really driven to make it work!!! :stuck_out_tongue:

Thank you again! I will report back soon!

Mike

… One quick question… I reread all the responses and I noted that Scott’s code worked in UM… Has anyone used similar code in kernel mode?

WoooooooooooooooooooHooooooooooooooooooooooooo!!!

Scott’s code worked!!!

THANK YOU!!!

I will post the test code that I used in case others hit this issue, and when I figure out the issue with my code, I will post it, too.

Thank you very much for all your help, everyone! Now, I can sleep again!!!

Mike

//////////////////////////////////////////////////////////////////////////
// Test code that worked in our KMDF driver.
//////////////////////////////////////////////////////////////////////////
BYTE fileStreamInfo[1024];
ULONG streamInfoSize = 1024;
FILE_STREAM_INFORMATION* streamInfo = (FILE_STREAM_INFORMATION*)&fileStreamInfo[0];
OBJECT_ATTRIBUTES attributes;
wchar_t nameBuf[1024];
UNICODE_STRING name;

name.Buffer = nameBuf;
name.Length = 0;
name.MaximumLength = 2048;

// Initialize structures…
RtlSecureZeroMemory(&attributes, sizeof(OBJECT_ATTRIBUTES));
RtlSecureZeroMemory(&ioStatus, sizeof(IO_STATUS_BLOCK));
RtlSecureZeroMemory(streamInfo, streamInfoSize);
RtlSecureZeroMemory(nameBuf, 2048);
RtlInitUnicodeString(&name, L"\DosDevices\c:\testfolder\alternateFileStreams.txt");

// Initialize attributes with the full filename that we want to examine.
InitializeObjectAttributes(&attributes, &name,
(OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), NULL, NULL);

// Open the file…
status = ZwCreateFile(&hFile,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&attributes,
&ioStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);

// Query the file…
status = ZwQueryInformationFile(hFile,
&ioStatus,
streamInfo,
streamInfoSize,
FileStreamInformation);

///////////////////////////////////

You are putting 3K (!) on the stack. Don’t do that. Also, don’t use hard coded constants for sizes outside of the array, ie

name.MaximumLength = 2048;
should be
name.MaximumLength = sizeof(nameBuf);

RtlSecureZeroMemory(nameBuf, 2048);
should be
RtlSecureZeroMemory(nameBuf, sizeof(nameBuf);

Not sure why you think you need to use RtlSecureZeroMemory, RtlZeroMemory or = { 0 } at declaration will do the same thing with less cycles.

d
-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@a-bit-of-help.com
Sent: Wednesday, February 16, 2011 3:03 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] RE: ZwQueryInformationFile() fails to enumerate all Alternate Data Streams; No Errors

WoooooooooooooooooooHooooooooooooooooooooooooo!!!

Scott’s code worked!!!

THANK YOU!!!

I will post the test code that I used in case others hit this issue, and when I figure out the issue with my code, I will post it, too.

Thank you very much for all your help, everyone! Now, I can sleep again!!!

Mike

//////////////////////////////////////////////////////////////////////////
// Test code that worked in our KMDF driver.
//////////////////////////////////////////////////////////////////////////
BYTE fileStreamInfo[1024];
ULONG streamInfoSize = 1024;
FILE_STREAM_INFORMATION* streamInfo = (FILE_STREAM_INFORMATION*)&fileStreamInfo[0];
OBJECT_ATTRIBUTES attributes;
wchar_t nameBuf[1024];
UNICODE_STRING name;

name.Buffer = nameBuf;
name.Length = 0;
name.MaximumLength = 2048;

// Initialize structures…
RtlSecureZeroMemory(&attributes, sizeof(OBJECT_ATTRIBUTES)); RtlSecureZeroMemory(&ioStatus, sizeof(IO_STATUS_BLOCK)); RtlSecureZeroMemory(streamInfo, streamInfoSize); RtlSecureZeroMemory(nameBuf, 2048); RtlInitUnicodeString(&name, L"\DosDevices\c:\testfolder\alternateFileStreams.txt");

// Initialize attributes with the full filename that we want to examine.
InitializeObjectAttributes(&attributes, &name,
(OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), NULL, NULL);

// Open the file…
status = ZwCreateFile(&hFile,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&attributes,
&ioStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);

// Query the file…
status = ZwQueryInformationFile(hFile,
&ioStatus,
streamInfo,
streamInfoSize,
FileStreamInformation);

///////////////////////////////////


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other 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

Hi Doron,

Thank you for your comments! The code that I posted was quick and dirty so I could see if things worked, but your points are well taken. :slight_smile:

In the end, the issue that I posted here and in Microsoft Forums was “cockpit error”. Big surprise, huh?! After getting the code that I posted here to work, I looked at the differences between it and my production code, and discovered that I made one R-E-A-L-L-Y stupid mistake: I was passing the handle to our output file into ZwQueryInformationFile() rather than the file of interest, so no ADS were found! Geez!!! Oh well, live and learn! Hopefully, this posting will help another person…

Thank you again for your ideas and help!

Mike

> Nope, it works just fine with UM as well. I guess you have not specified

FILE_FLAG_BACKUP_SEMANTICS in CreateFile or FILE_OPEN_FOR_BACKUP_INTENT for
ZwCreateFile perhaps.

You need to switch SeBackupPrivilege on for this.


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

>I made one R-E-A-L-L-Y stupid mistake: I was passing the handle to our

output file into ZwQueryInformationFile() rather than the file of
>interest, so no ADS were found!

So you lied to me when you said that they were the same handle :slight_smile:

Glad to hear your problem is solved!

-scott


Scott Noone
Consulting Associate and Chief System Problem Analyst
OSR Open Systems Resources, Inc.
http://www.osronline.com