A little known feature of the NTFS file system is the ability to add “streams”, which are much like files, but hidden within a “normal” file. This can serve as a way to add custom metadata to a file without any standard tool noticing – the file size reported by standard APIs does not change, even though the added streams consume disk space.
This is also a possible technique for malware to hide literally “in plain sight”, as these streams can be added to any file (PE or not), without anything looking suspicious with most standard tools.
To create such a stream, a normal CreateFile call must be made to open an existing file or create a new one, but with a suffix comprised of a colon, a stream name, another colon, and a stream type. The stream type is optional and defaults to $DATA (all stream types start with a ‘$’ character). The list of stream types is finite and can be found in the MSDN docs. Most are reserved for NTFS, but the $DATA stream type is for anyone to use; several streams of that type $DATA can be created inside the same file, provided the stream names are different.
Here is an example for creating a stream named Author in a file named Hello.txt with the default stream type:
HANDLE hFile = ::CreateFile(L"c:\\Temp\\Hello.txt:Author",
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, 0, nullptr);
Once the handle is obtained, normal WriteFile / ReadFile calls work on the alternate stream.
By the way, if you think tryin it in .NET with a FileStream object – that won’t work. The reason is the implementation of FileStream and File classes rejects illegal characters in file paths, and a colon is one such illegal character. However, it’s not too difficult to call CreateFile with P/Invoke, getting back a SafeFileHandle, and then using this handle with one of the FileStream constructors, for example.
So, how can we check if a file has alternate streams? The Windows API provides stream enumeration functions: FindFirstStreamW and FindNextStreamW. These are similar to the file searching functions FindFirstFile / FindNextFile, but enumerate streams within a single file. If a stream name is known, it can be read programmatically with ReadFile as discussed or by using the more command line tool and redirecting the file and stream. For example:
c:\Temp>more < hello.txt:Author
this is the alternate Author stream!
So I created a GUI tool named NTFS Streams that can enumerate such streams in files and directories, and show their contents as well. Here is a screenshot:
Each file with alternate streams opens in its own tab. Selecting a stream shows its contents (up to 64KB for now, since the control showing the data is rather simple and does not optimize if the size is large; I hope to get around to improving that).
To help with testing and experimentation, I created another simple command line tool that creates or modifies alternate streams. It accepts a file name, stream name and textual data, like so:
AltStream.exe c:\temp\hello.txt dummy “this is some data stored in the dummy stream”