0xc0000008 on the FlTCreateFile call - Alternate Data Stream in XXXPostCreate

Hi there,

I am new here. I have a request regarding a post i found here:

http://fsfilters.blogspot.ie/2011/09/opening-alternate-data-stream.html - entitled Opening an ADS

I want to check to see if a file has a particular ADS associated with it prior to opening it. I have placed such an ADS on the test file. Using NirSoft’s ADS tool we can see the following details:
Stream Name — :sensitive:$DATA
Filename: — C:\SensitiveDocuments\CertandBuild-checklist.txt
FullStreamName— C:\SensitiveDocuments\CertandBuild-checklist.txt:sensitive

So, I know it exists.

I have searched the forum high and low. So, I have added the following function whose purpose is to open the stream. If it exists then it sets the hasADS flag TRUE else sets it FALSE. This function is called it in the xxxxPostCreate function:

NTSTATUS CheckForValidADSonFile(__in PCFLT_RELATED_OBJECTS FltObjects, __in PFLT_FILE_NAME_INFORMATION nameInfo, __out PBOOLEAN bHasADS)
{
PFLT_INSTANCE ADSHandle;
NTSTATUS adsStatus;
UNICODE_STRING ADSName = RTL_CONSTANT_STRING(L":sensitive:$DATA");
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatus;
/// Try to open ADS in the file - if it exists then the file is deemed sensitive
/// this is the name of the ADS that we want to open the file.
/// initialize OBJECT_ATTRIBUTES with the handle we have and the name of the ADS.
InitializeObjectAttributes( &objectAttributes, &ADSName, OBJ_KERNEL_HANDLE, FltObjects->FileObject , NULL );

if(nameInfo)///Logging
{
DbgPrint(“FileSentry.sys !!! Check ADS attached to file %wZ%wZ\n”, nameInfo->Name, &ADSName );
}

// and now issue our open for the stream.
adsStatus = FltCreateFile( SentryData.Filter,
FltObjects->Instance,
&ADSHandle,
FILE_READ_DATA | FILE_READ_ATTRIBUTES,
&objectAttributes, &ioStatus,
0,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_OPEN_REPARSE_POINT,
NULL,
0,
0 );

if (NT_SUCCESS( adsStatus ))
{
DbgPrint( “fileSentry.sys — !!!THIS IS A SENSITIVE FILE!!!\n” );
*bHasADS = TRUE;
NT_SUCCESS( FltClose(ADSHandle));
}
else
{
DbgPrint( “fileSentry.sys — Not a sensitive file, status 0x%X\n”, adsStatus );
*bHasADS = FALSE;
}
///Remember to close the handle after use
}

This function is called inside:

FLT_POSTOP_CALLBACK_STATUS
KMDrvPostCreate ( __inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__in_opt PVOID CompletionContext,
__in FLT_POST_OPERATION_FLAGS Flags
){ … }

Can anyone help me shine a light on why I am getting a status error 0xc0000008 on the FLtCreateFile call (according to http://www.osronline.com/showThread.CFM?link=101794, this is STATUS_INVALID_HANDLE. Since I am trying to use a relative

DebugViews gives me these two entries for one explorer-based file access:

FileSentry.sys !!! name:“\Device\HarddiskVolume3\SensitiveDocuments\CertandBuild-checklist.txt” volume:“\SensitiveDocuments\CertandBuild-checklist.txt” final:“CertandBuild-checklist.txt” stream:“(null) FinalComponent:“CertandBuild-checklist.txt” Share:”(null)" ParentDir:"SensitiveDocuments"

FileSentry.sys !!! Check for ADS attached to file \Device\HarddiskVolume3\SensitiveDocuments\CertandBuild-checklist.txt:sensitive.txt

fileSentry.sys — Not a sensitive file, status 0xC0000008

I appreciate any help/guidance that people may have.

Regards,
J

Well, the FltObjects->FileObject that you use as the RootDirectory HANDLE parameter in InitializeObjectAttributes() is not actually a HANDLE, it’s a FILE_OBJECT.

I’d say since you’re in postCreate, just use the file name (FltGetFileNameInformation(…, FLT_FILE_NAME_OPENED,…)) and concatenate the stream to it (if there isn’t already a stream) and then open it by name.

Thanks,
Alex.

Actually, upon re-reading your email I am confused… You say that “I want to check to see if a file has a particular ADS associated with it prior to opening it.” but then you mention that you call the ADS function from xxxxPostCreate.

If you just want to check if the ADS exists (as opposed to opening it and reading things from it) and you want to do this in PostCreate (which is the only reliable way to do it; otherwise how can you guarantee that by the time the IRP_MJ_CREATE actually reaches the file system it will actually refer to the file you’ve checked the ADS for ?) then i’d suggest you use FltQueryInformationFile (…, FileStreamInformation, …) and just look at the stream names. In this case you can use FltObjects->FileObject and FltObjects->Instance …

Thanks,
Alex.

Thanks Alex, very useful replies. I had a feeling I was using the wrong handle. How did you imagine it working in your original blog post? Is there a small change that I could make to what i did to make it work?

when I said “I want to check to see if a file has a particular ADS associated with it prior to opening it.” I meant in the same sense that the example scanner FSFilter does when it peeks into the file before the actual file contents are released to the calling process.

So, I can either use approach:

  1. (FltGetFileNameInformation(…, FLT_FILE_NAME_OPENED,…)) or,
  2. enumerate the streams attached to a file FltQueryInformationFile (…, FileStreamInformation, …)

Option 1 is better if I eventually want to interrogate the actual stream data though, right?

Also, the documentation for step 2 is a bit…obtuse. Here’s what I have for option 2:

NTSTATUS status;
FILE_STREAM_INFORMATION fileStreamInformation = {0};
PFILE_STREAM_INFORMATION pFileStreamInformation;
UNICODE_STRING ADSName = RTL_CONSTANT_STRING(L":sensitive:$DATA");

//Ensure that structure can support most returns from function
WCHAR szFileName[32767];
LONGLONG nameBuffer[(32767+sizeof(FILE_STREAM_INFORMATION))/sizeof(LONGLONG)];
PFILE_STREAM_INFORMATION pFullInfo = (PFILE_STREAM_INFORMATION)nameBuffer;

status = FltQueryInformationFile(
FltObjects->Instance,
FltObjects->FileObject,
&fileStreamInformation, ///Output!
sizeof(FILE_STREAM_INFORMATION),
FileStreamInformation,
NULL);

pFileStreamInformation = &fileStreamInformation;
if(NT_SUCCESS(status))
{
///What goes in here??
///I presume this is a blobk list of streams that I must traverse - comparing the stream name to the stream name of interest: ADSName

}
else
DbgPrint( “!!! FileSentry.sys — couldn’t queryFileInformation, status 0x%X\n”, status );

Does that make sense?

I’d say use the second approach, it’s likely better in terms of performance than trying to open the stream (IRP_MJ_CREATE is a rather heavy operation).

Once FltQueryInformationFile returns STATUS_SUCCESS, you would need to parse the FILE_STREAM_INFORMATION structure you get and figure out if your stream name is there or not. Easiest way to get a visual representation for what the buffer looks like is to use the FileTest tool (http://www.zezula.net/en/fstools/filetest.html) and just query this information class and see what the buffer looks like for a file that has your ADS and for one that doesnt…

Thanks,
Alex.

Thanks again Alex.

Ok, ran FileTest on the files that have alternative data streams, it return STATUS_NO_EAS_ON_FILE when i “Query Streams”. I know the ADS exists. I put it there and it is verified by NirSoft’s “Alternate Stream View” tool. The same occurs with downloaded files that have the zone transfer stream.

So, as you suggested, I have tried to get the FileStreamInformation structure back using FltQueryInformationFile. Some questions arise:

  1. how do you know how much space to allocate for the full data? Do you call FltQueryInformationFile with no output param and check the last parameter, the length? Doesn’t seem to work. Do you just guess?? Hopefully not.

  2. Once you know how much space you require, do you allocate it using ExAllocatePoolWithTag & Co?

I have spent all day with this function - and quite a few BSoD :slight_smile: I would dearly like some pointers here. There is almost nothing on the web about this issue.

Here’s what I have so far:

NTSTATUS CheckForValidADSonFile(__in PCFLT_RELATED_OBJECTS FltObjects, __in PFLT_FILE_NAME_INFORMATION nameInfo, __out PBOOLEAN bHasADS)
{ int streamCnt=0;
NTSTATUS status;
WCHAR name[MAX_NAME_SPACE/sizeof(WCHAR)];
UNICODE_STRING ADSName = RTL_CONSTANT_STRING(L":sensitive:$DATA");
int bufLength = 0;
ULONG LengthReturned;
PVOID buffer[200000];

PFILE_STREAM_INFORMATION pFileStreamInformation = (PFILE_STREAM_INFORMATION)buffer;
try {
status = FltQueryInformationFile(
FltObjects->Instance,
FltObjects->FileObject,
pFileStreamInformation, ///Output
bufLength,
FileStreamInformation,
&LengthReturned);

if(NT_SUCCESS(status))
{
///Literally “carve” the stream name out of the returned pFileStreamInformation structure
for(;:wink:
{
++streamCnt;

if(pFileStreamInformation && pFileStreamInformation->StreamNameLength > 0)
{
///Extract the name from the “blob” - null terminate
wcsncpy(name, pFileStreamInformation->StreamName, min(pFileStreamInformation->StreamNameLength, MAX_NAME_SPACE/sizeof(WCHAR)));
name[min(pFileStreamInformation->StreamNameLength, MAX_NAME_SPACE/sizeof(WCHAR))] = L’\0’;

DbgPrint( “!!! FileSentry.sys — Stream (%d): %s %d\n”, streamCnt, pFileStreamInformation->StreamName, pFileStreamInformation->StreamNameLength);

// Exit - when we hit the last one
if(pFileStreamInformation->NextEntryOffset == 0)
{
DbgPrint( “!!! FileSentry.sys — Last Stream\n” );
break;
}
}
pFileStreamInformation = (PFILE_STREAM_INFORMATION)((char*)pFileStreamInformation + pFileStreamInformation->NextEntryOffset);
}
}
else
{
DbgPrint( “!!! FileSentry.sys — couldn’t queryFileInformation, bufLength: %d status 0x%X\n”, LengthReturned, status );
*bHasADS = FALSE;
}
}
finally
{

}

*bHasADS = FALSE;

return status;
}

I really hope someone can shine a light here.

Finally, I am based in Ireland. Is there any OSR-like training for devs here? User groups etc… I have 17 years xp in c/c++ programming (AI/graphics/image processing/machine learning) and this is driving me nuts. A plea for help :slight_smile:

Thanks,
Jason

> Thanks again Alex.

Ok, ran FileTest on the files that have alternative data streams, it
return STATUS_NO_EAS_ON_FILE when i “Query Streams”. I know the ADS
exists. I put it there and it is verified by NirSoft’s “Alternate Stream
View” tool. The same occurs with downloaded files that have the zone
transfer stream.

So, as you suggested, I have tried to get the FileStreamInformation
structure back using FltQueryInformationFile. Some questions arise:

  1. how do you know how much space to allocate for the full data? Do you
    call FltQueryInformationFile with no output param and check the last
    parameter, the length? Doesn’t seem to work. Do you just guess?? Hopefully
    not.

  2. Once you know how much space you require, do you allocate it using
    ExAllocatePoolWithTag & Co?

I have spent all day with this function - and quite a few BSoD :slight_smile: I would
dearly like some pointers here. There is almost nothing on the web about
this issue.

Here’s what I have so far:

NTSTATUS CheckForValidADSonFile(__in PCFLT_RELATED_OBJECTS FltObjects,
__in PFLT_FILE_NAME_INFORMATION nameInfo, __out PBOOLEAN bHasADS)
{ int streamCnt=0;
NTSTATUS status;
WCHAR name[MAX_NAME_SPACE/sizeof(WCHAR)];
UNICODE_STRING ADSName = RTL_CONSTANT_STRING(L":sensitive:$DATA");
int bufLength = 0;
ULONG LengthReturned;
PVOID buffer[200000];
This allocates 200000*sizeof(PVOID) bytes on the stack. You might,
optimistically, have 6K of stack. Lose this line.

The standard technique for calls where you don’t know how much space to
allocate but which have “buffer too small” return codes is:

  1. start with some whimsically-determined allocation. Let’s say for a
    specific number, ask for 4K
  2. Allocate (ExAllocatePoolWithTag) a buffer of the current size
  3. Call the function with the current buffer pointer and size, and get the
    actual bytes used or required
  4. Determine if the buffer you provided was sufficient
  5. If insufficient, free the old buffer allocate a new buffer twice the
    size, iterate from step 2
  6. If sufficient, process the data

Step4 varies with the characteristics of the call/return protocol.

Some calls, given N bytes, will fill the N bytes with data, silently
truncating any additional information, and will tell you they have used N
bytes. For these calls, if the bytes used == bytes allocated, assume the
buffer was insufficient.

Some calls, given N bytes, will return a failure code for buffer too
smsll; these are easy and obvious.

Some calls let you provide 0, NULL for the buffer and tell you how big the
buffer needs to be at this instant. Do not assume that the buffer you
allocate will be sufficient, and plan on looping-until-sufficient, because
concurrent activities can change the actual number of bytes required
between the two calls. No harm if the need gets smaller, but the changes
may require more bytes than you had allocated. Deal with it. And do not
assume the entire buffer is valid; if the data got shorter, you cannot use
the allocated size in determining loop completion; you must only use the
actual bytes returned.

Note that some calls will return bytes-needed but not return an error
code, just fill in bytes-available. Watch out for these.

These are true for a host of Win32 APIs; it is lie there was no design
oversight on how the APIs used or returned information. So I can only
imagine similar idiosyncracies exist in the kernel.
joe

PFILE_STREAM_INFORMATION pFileStreamInformation =
(PFILE_STREAM_INFORMATION)buffer;
try {
status = FltQueryInformationFile(
FltObjects->Instance,
FltObjects->FileObject,
pFileStreamInformation, ///Output
bufLength,
FileStreamInformation,
&LengthReturned);

if(NT_SUCCESS(status))
{
///Literally “carve” the stream name out of the returned
pFileStreamInformation structure
for(;:wink:
{
++streamCnt;

if(pFileStreamInformation && pFileStreamInformation->StreamNameLength
> 0)
{
///Extract the name from the “blob” - null terminate
wcsncpy(name, pFileStreamInformation->StreamName,
min(pFileStreamInformation->StreamNameLength,
MAX_NAME_SPACE/sizeof(WCHAR)));
name[min(pFileStreamInformation->StreamNameLength,
MAX_NAME_SPACE/sizeof(WCHAR))] = L’\0’;

DbgPrint( “!!! FileSentry.sys — Stream (%d): %s %d\n”, streamCnt,
pFileStreamInformation->StreamName,
pFileStreamInformation->StreamNameLength);

// Exit - when we hit the last one
if(pFileStreamInformation->NextEntryOffset == 0)
{
DbgPrint( “!!! FileSentry.sys — Last Stream\n” );
break;
}
}
pFileStreamInformation =
(PFILE_STREAM_INFORMATION)((char*)pFileStreamInformation +
pFileStreamInformation->NextEntryOffset);
}
}
else
{
DbgPrint( “!!! FileSentry.sys — couldn’t queryFileInformation,
bufLength: %d status 0x%X\n”, LengthReturned, status );
*bHasADS = FALSE;
}
}
finally
{

}

*bHasADS = FALSE;

return status;
}

I really hope someone can shine a light here.

Finally, I am based in Ireland. Is there any OSR-like training for devs
here? User groups etc… I have 17 years xp in c/c++ programming
(AI/graphics/image processing/machine learning) and this is driving me
nuts. A plea for help :slight_smile:

Thanks,
Jason


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

> Thanks Alex, very useful replies. I had a feeling I was using the wrong

handle. How did you imagine it working in your original blog post? Is
there a small change that I could make to what i did to make it work?

when I said “I want to check to see if a file has a particular ADS
associated with it prior to opening it.” I meant in the same sense that
the example scanner FSFilter does when it peeks into the file before the
actual file contents are released to the calling process.

So, I can either use approach:

  1. (FltGetFileNameInformation(…, FLT_FILE_NAME_OPENED,…)) or,
  2. enumerate the streams attached to a file FltQueryInformationFile (…,
    FileStreamInformation, …)

Option 1 is better if I eventually want to interrogate the actual stream
data though, right?

Also, the documentation for step 2 is a bit…obtuse. Here’s what I have
for option 2:

NTSTATUS status;
FILE_STREAM_INFORMATION fileStreamInformation = {0};
PFILE_STREAM_INFORMATION pFileStreamInformation;
UNICODE_STRING ADSName = RTL_CONSTANT_STRING(L":sensitive:$DATA");

//Ensure that structure can support most returns from function
WCHAR szFileName[32767];

I believe that should be 32768 to allow for a terminating NUL. Note,
however, that like the prvious code example, this requires more bytes than
a kernel stack possesses.

LONGLONG
nameBuffer[(32767+sizeof(FILE_STREAM_INFORMATION))/sizeof(LONGLONG)];

And then this compounds the problem.

Hint: if your local declarations start exceeding a few hundred bytes (to
pick a completely random number, say, 256) you really have to be careful.
Kernel stacks are TINY and whatever you call is going to call stuff which
calls stuff which… anyway, these allocations are completely absurd.

Note to PreFast team: PreFast should barf all over code like this. I
don’t know if it does because I learned early on not to use large amounts
of stack space, so would never have seen how it reacts to this.

PFILE_STREAM_INFORMATION pFullInfo = (PFILE_STREAM_INFORMATION)nameBuffer;

status = FltQueryInformationFile(
FltObjects->Instance,
FltObjects->FileObject,
&fileStreamInformation, ///Output!
sizeof(FILE_STREAM_INFORMATION),
FileStreamInformation,
NULL);

pFileStreamInformation = &fileStreamInformation;
if(NT_SUCCESS(status))
{
///What goes in here??
///I presume this is a blobk list of streams that I must traverse -
comparing the stream name to the stream name of interest: ADSName

}
else
DbgPrint( “!!! FileSentry.sys — couldn’t queryFileInformation,
status 0x%X\n”, status );

Does that make sense?


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

Thanks Joseph. Great answer. Much appreciated.

My stack allocation was rash I admit. It got late and I got desperate. I will try this tomorrow.

Thanks again.

Jason,

the “streams” tab in filetest is not what you want. Use NtQueryInformationFile on the NtFileInfo tab. You need to open the file first.

L.

Thanks Ladislav. Found it. Don’t know how I missed it.

Thanks for the answers everyone. I am there, finally :slight_smile:

wrote in message news:…
>Note to PreFast team: PreFast should barf all over code like this. I
>don’t know if it does because I learned early on not to use large amounts
>of stack space, so would never have seen how it reacts to this.

It does. See the docs for the /stackhogthreshold switch to learn how to
customize the value (1024 is the default, which is still pretty high).

-scott


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

Love the name of the option! And yes, I’d probably set it to 128.

And isn’t there a lesson here, that PreFast is your New Best Friend?
joe

wrote in message news:…
>>Note to PreFast team: PreFast should barf all over code like this. I
>>don’t know if it does because I learned early on not to use large amounts
>>of stack space, so would never have seen how it reacts to this.
>
> It does. See the docs for the /stackhogthreshold switch to learn how to
> customize the value (1024 is the default, which is still pretty high).
>
> -scott
>
> –
> Scott Noone
> Consulting Associate and Chief System Problem Analyst
> OSR Open Systems Resources, Inc.
> http://www.osronline.com
>
>
> —
> 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
>