Alternate Data Streams

NTFS has for a long time supported the concept of alternate data streams. The idea is  that you can store data under a file, not by inserting or appending into it but more like tagging it with data. Even if the feature is not widely know, it is used by Microsoft in many places e.g.  for storing the cached thumb nail images under each thumbs.db file and for marking downloaded files as blocked.

Not all shell commands support the feature, but this example should give you the idea:

echo "hello world" > test.txt
echo "hello you" > test.txt:hidden.txt
notepad test.txt:hidden.txt
dir *.txt

The first line just creates the parent for the alternate data stream created by the second command (<file or directory>:<stream name>). Note that the last dir command only shows the parent file.

Note that if you move a file,  the alternative data streams will only follow as long as the destination device also uses NTFS.

You can work with alternate data streams in C#, but only by using the Windows API as none of the standard .NET components support it directly. I made the following class to easily access the file handle (open/create) for creating and modifying an alternative data stream:

public static class AlternateDataStreams
{
    private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
    private const uint GENERIC_ALL = 0x10000000;
    private const uint FILE_SHARE_READ = 0x00000001;
    private const uint OPEN_ALWAYS = 4;
    public static SafeFileHandle GetHandle(string path, string name)
    {
        if (string.IsNullOrEmpty(path))
            throw new ArgumentException("Invalid path", "path");
        if (string.IsNullOrEmpty(name))
            throw new ArgumentException("Invalid name", "name");
        string streamPath = path + ":" + name;
        SafeFileHandle handle = CreateFile(streamPath,
        GENERIC_ALL, FILE_SHARE_READ, IntPtr.Zero, OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
        if (handle.IsInvalid)
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        return handle;
    }
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
    uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
    uint dwFlagsAndAttributes, IntPtr hTemplateFile);
}

The handle can be passed to a FileStream for reading/writing.

I’m planning to use this for marking files in a way enabling my application to detect changes even if the file is left fully accessible to the users. I plan to achieve this by storing the file path+file hash data as a encrypted alternative data stream.

Advertisements