Hostwinds Blog


Windows NTFS Streams Featured Image

Windows NTFS Streams

by: Karlito Bonnevie  /  May 24, 2022


Since 1995, the Microsft Windows NTFS file system has supported streams. In fact, all NTFS "files" are actually streams. What we normally think of as a file in NTFS is more precisely called a default data stream. A default data stream is unnamed. What's an unnamed data stream? Consider the NTFS fully qualified filename format:

file-name:stream-name:stream-type

From left to right, we have:

  • file-name: The name of the "file" (i.e., default data stream).
  • stream-name: The name of any alternate (named) data streams attached to file-name (a file can have multiple streams).
  • stream-type: For any stream, indicates its type, with $DATA being the most common. A stream of type $DATA can contain anything a normal file can contain. For a list of possible stream types, see Stream Types.

An example follows:

myTextFile.txt

This is a default data stream. As mentioned, default data streams are unnamed. This can be seen in its fully qualified filename form:

myTextFile.txt::$DATA

The fact that there's nothing between the two colons (::) indicates that this is an unnamed data stream. For a default data stream, we do not need to use the fully qualified filename, although we can. For example, from the Windows command prompt, the following two commands open precisely the same default data stream:

notepad myTextFile.txt
notepad myTextFile.txt::$DATA

To simplify things, we will use the term "file" in place of the more accurate "default data stream".

You can create a named stream in a file using normal NTFS naming conventions. For example, from the command prompt, run the following:

notepad foo.docx:bar.txt

Select Yes when asked if you want to create a new file. Next, type the following content:

This is the named stream "bar.txt" that is in (attached to) the file "foo.docx".

Save and exit Notepad.

When you run the dir command, you will see a file named foo.docx whose size, interestingly, is 0 bytes. However, when you run the dir /r command, you will see the named data stream you just created, whose size is 80 bytes:

These two stream sizes make sense in that stream foo.docx has no content while stream foo.docx:bar.txt has 80 bytes worth of content. To help elucidate this, add some content to the default data stream, as follows:

notepad foo.docx

Enter the following text:

This is the default data stream (file) named "foo.docx", which has one named data stream attached to it, called "bar.txt".

Save and exit Notepad.

Run dir /r again and note that the default data stream foo.docx now contains 120 bytes of data:

Because of this, we can tell that the two streams consume 200 bytes on the volume. That said, running the unadorned dir command does provide somewhat misleading information - it implies that foo.docx only contains 120 bytes of data, which is inaccurate in the sense that when you delete foo.docx, you're actually releasing 200 bytes back to volume as available free space:

del foo.docx
dir /r

The moral of the story, perhaps, is to always use the /r switch. Which begs the question, how do you find all files that contain "hidden" streams? That topic is discussed next.

Using PowerShell to find streams

If we let the term "file" mean "default data stream" and "stream" mean "named data stream", then we can use PowerShell to find all files containing streams as follows.

Open a PowerShell command prompt and optionally run the following command (this suppresses possibly annoying file access denied error messages):

$ErrorActionPreference = "SilentlyContinue"

Next, run the following commands:

Get-ChildItem -Path \ -Recurse | Get-Item -Stream * | Where-Object Stream -ne ':$DATA' | Convert-Path

For the current volume, this returns a list of all files containing one or more streams. This PowerShell pipeline is explained as follows:

  • Get-ChildItem -Path \ -Recurse
    Starting from the root directory (folder), gets all files on the volume.
  • Get-Item -Stream *
    Gets all data streams associated with a file. Files (unnamed data streams) will return ":$DATA" as the value of the Stream property. Streams (named data streams) will return the name of the stream as the value of the Stream property (and ":$DATA will not be returned).
  • Where-Object Stream -ne ':$DATA'
    Removes all files whose Stream property is ":$DATA", thereby leaving only files containing one or more named streams.
  • Convert-Path
    Outputs nicely formatted file paths.

Using PowerShell to delete streams

From a PowerShell command prompt, you can remove the stream bar.txt from file foo.docx as follows:

Remove-Item -Path .\foo.docx -Stream bar.txt

If the stream name has spaces, you must use quotes, as in:

Remove-Item -Path ".\Bob's Grandfather.png" -Stream "Image Info.txt"

Using streams

The original intent of NTFS streams was to enable Apple Macintosh file interoperability but NTFS streams can be quite handy. For example, you may want to "embed" detailed information within family photograph files:

If the above image file were named "Bob's Grandfather.png", then you could add a stream (using PowerShell) containing detailed image information, as follows:

Set-Content -Path ".\Bob's Grandfather.png" -Stream "image-info.txt" -Value "This is Bob's grandfather (on his mother's side) in the Gold Rush Bar and Restaurant in Seattle, WA - circa 1856."

To view the image metadata you just set, run:

Get-Content -Path ".\Bob's Grandfather.png" -Stream "image-info.txt"

Note that the -Stream argument requires that you know the exact name of the stream (wildcards are not permitted). Recall that you can always obtain stream names using Get-Item (which accepts wildcards):

Get-Item -Path '.\Bob''s Grandfather.png' -Stream *

For the sought after stream name, look at the Stream property in the resulting output:

Other uses for NTFS streams can include:

  • Storing keywords related to a file.
  • Providing summary information for a file.
  • Associating fonts and/or sounds with a file.
  • Pretty much any file metadata you can think of.

Lastly, you may have noted that your volume contains a number of streams named Zone.Identifier. These streams are generally used by Windows for the storage of URL security zones, which determine whether a file should be trusted or not. An example showing how to view the contents of a Zone.Identifier stream follows:

Get-Content -Path .\sanders.net-May-2022.gz -Stream Zone.Identifier

And via the Windows command prompt:

notepad sanders.net-May-2022.gz:Zone.Identifier

Hopefully, you've found this article interesting; and perhaps you'll find some interesting uses for NTFS streams to boot.

Written by Karlito Bonnevie  /  May 24, 2022