Click here to Skip to main content
Click here to Skip to main content

Visual browsing of alternative data-streams in Windows Explorer

, 5 Dec 2005
Rate this:
Please Sign up or sign in to vote.
ADSDetector information with code examples.

Introduction

Visual Alternative Data Streams Detector Bar is a user control, based on Dino Esposito's article A Programmer's Perspective on NTFS 2000 Part 1: Stream and Hard Link and Pavel Zolnikov's BandObjectLib which makes it an Explorer bar.

VADSD 1.0 is integrated into Windows Explorer thus allowing visual real time viewing of a non encrypted file's alternative data streams. The streams are shown with their icon and name in a ListView object. The bar is activated from the Explorer menu ("View" -> "Explorer Bar" -> "Visual ADS Detector").

Background

While creating a file on NTFS partition, you actually create an unnamed stream, which is the main file. However, NTFS allows you to create multiple streams within this file, making them alternative streams. An alternative stream is not visible to the regular user since it usually contains data such as SummaryInformation, Permissions, Icon etc. But there is a way of creating these streams and manipulating them as if they were regular files on the disk.

Stream enumeration

Non encrypted files can be searched for alternative data streams, using the BackupRead() API function (when performing backup, it's important to backup the alternative streams too..) to enumerate all the streams inside a file, then each stream can be accessed via the CreateFile() API function and act as a regular file.

In this example, ADS is a class that represents a stream (name, size, fullpath and extention):

/// <summary>
/// read all streams in a certain location (files and directories)
/// </summary>
/// <PARAM name="filepath">location on the drive</PARAM>
/// <returns>array of ADS classes, representing each stream found
/// </returns>
public static ADS[] ReadStreams(string filepath)
{
    ArrayList FoundADS = new ArrayList();
    
    
    IntPtr stream = CreateFile(filepath,FileAccessAPI.GENERIC_READ,
                                     FileShare.Read,0,FileMode.Open,
                                     FileFlags.BackupSemantics,0);
    if (stream.ToInt32() == -1) 
        return null;
    try
    {
        WIN32_STREAM_ID sid = new WIN32_STREAM_ID();
        int dwStreamHeaderSize  = Marshal.SizeOf(sid);
        int Context = 0;
    
        bool Continue = true;
        
        while (Continue)
        {
            int lRead = 0;
            Continue = BackupRead(stream,ref sid,dwStreamHeaderSize,
                                   ref lRead,false,false,ref Context);
        
            if (Continue && lRead == dwStreamHeaderSize) 
            {
                
                if (sid.dwStreamNameSize>0)
                {
                    //Read the stream name
                    lRead = 0;
                    IntPtr pName = 
                        Marshal.AllocHGlobal(sid.dwStreamNameSize);
                    try 
                    {
                        BackupRead(stream, pName, sid.dwStreamNameSize, 
                                   ref lRead, false, false, ref Context);
                        char[] bName = new char[sid.dwStreamNameSize];
                        Marshal.Copy(pName,bName,0,sid.dwStreamNameSize);

                        //Name is of the format ":NAME:$DATA\0"
                        string sName = new string(bName);
                        int i = sName.IndexOf(STREAM_SEP, 1);
                        if (i>-1) sName = sName.Substring(1, i-1);
                        else 
                        {
                            //This should never happen. 
                            //Truncate the name at the first null char.
                            i = sName.IndexOf('\0');
                            if (i>-1) sName = sName.Substring(1, i-1);
                        }
                        
                                                

                        //Add the stream to the collection
                        
                        ADS newADS = new ADS();
                        newADS.StreamName = sName;
                        newADS.FullStreamName = filepath + ":" + sName;
                        newADS.Size = sid.Size.QuadPart();
                    

                        FoundADS.Add(newADS);
                    }
                    finally 
                    {
                        Marshal.FreeHGlobal(pName);
                        
                    }
                }    
                int l = 0; int h = 0;
                //Skip the stream contents - keep reading
                BackupSeek(stream, sid.Size.Low, sid.Size.High, 
                                       ref l, ref h, ref Context);
            
            }
            else break; //no more streams
                
        }
        //required - tell the file we are done with backup.
        //BackupRead(stream,ref sid,dwStreamHeaderSize,ref lRead,
        //                                 true,false,ref Context);
                        
    }
    catch
    {
        return null;
    }
    finally
    {
        CloseHandle(stream);
    }
    
    return (ADS[])FoundADS.ToArray(typeof(ADS));    
}

Stream presentation

After having a list of streams, the program searches for the appropriate icon for each extension and then presents it in the ListView object:

ADS[] NTFSStreams = NTFS.ReadStreams(CurrentDir + "\\" + fileSelected);
                    
    foreach (ADS NTFSADS in NTFSStreams)
    {
        //***********************
        //  Tree Item Object 
        // ------------------
        // 1- StreamName
        // 2- Full Stream Name
        // 3- Stream Size
        // 4- Stream Extention
        //***********************

        Uri URI = new Uri(NTFSADS.FullStreamName,true);
        
        //Loading Streams
        
        ListViewItem newItem = 
               new ListViewItem(new string[]{NTFSADS.StreamName,
                    URI.LocalPath,NTFSADS.Size.ToString(),NTFSADS.Extention},
                    IconHandler.GetIcon(NTFSADS.Extention));
        
        
        StreamsView.Items.Add(newItem);
    }

Icon loading

The IconHandler is based on Gil_Schmidt's IconHandler Obtaining (and managing) file and folder icons using SHGetFileInfo code:

[Editor Comment: Line breaks used to avoid scrolling.]

/// <summary>
/// Retrieves information on the extension of a file and shows its Icon
/// </summary>
/// <PARAM name="extension">File extension</PARAM>
/// <returns>Mathing Icon</returns>
private static System.Drawing.Icon GetIconFromShell(string Extention)
{
    //add '.' if nessesry
    if (Extention[0] != '.') Extention = '.' + Extention;

    //struct for getting file shell info
    SHFILEINFO shinfo = new SHFILEINFO();

    //get Shell File Info            
    SHGetFileInfo(Extention, FILE_ATTRIBUTE_NORMAL, ref shinfo,
        (uint)Marshal.SizeOf(shinfo),
        SHGFI_ICON | SHGFI_USEFILEATTRIBUTES | SHGFI_LARGEICON);

    
    System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.
                                Icon.FromHandle(shinfo.hIcon).Clone();
    DestroyIcon( shinfo.hIcon );        // Cleanup
    

    return icon;
}

Stream operations

As mentioned, the stream can be treated as a regular file, allowing the user to edit, delete and copy the stream to another location. To open a stream, the CreateFile() function is used to get a handle to the file:

IntPtr stream = CreateFile(SourceFile,FileAccessAPI.GENERIC_READ,
                                       FileShare.Read,0,FileMode.Open,
                                       FileFlags.BackupSemantics,0);

The copy example

VADSD allows the user to copy streams to and from the Explorer window, by using the Windows Clipboard. The Clipboard is where the data is stored when we copy files, text and images. In .NET the Clipboard object has only two functions: SetDataObject and GetDataObject. When you click "Copy" in the Explorer, Windows fills the Clipboard with information about the location of the file you want to copy and whether you want to delete it after copy (Cut clicked). It uses two formats: FileDrop to store file locations and PreferredDropEffect to store other information.

File locations are stored as a string array and can be retrieved by getting the data on the Clipboard in the FileDrop format:

if (Clipboard.GetDataObject().GetDataPresent("FileDrop"))
    {
        IDataObject FileDataObject = Clipboard.GetDataObject();
        string[] FileLocations =(string[])FileDataObject.GetData("FileDrop");
    }

Determining whether the user has clicked Copy or Cut can be achieved by getting the data on the Clipboard in the PreferredDropEffect format:

[Editor Comment: Line breaks used to avoid scrolling.]

if (Clipboard.GetDataObject().GetDataPresent("Preferred DropEffect"))
{
    //byte array to hold the data in the clipboard
    byte[] ExplorerCutSelect = new byte[4];
    ((MemoryStream)FileDataObject.GetData("Preferred DropEffect")).
                                         Read(ExplorerCutSelect,0,4);
    
    //"check the first byte
    switch (ExplorerCutSelect[0])
    {
            //delete files after paste- Cut selected
        case 2:
            //copy operation
            //delete operation
            break;
            
            //only paste - Copy selected
        case 5:
            //copy operation
    }
}

Finally, copying the stream:

public static bool CopyStream (string Source,string Destination)
{
    try
    {
        
        FileStream srcStream;
        FileStream dstStream;

        //open stream
        IntPtr Sstream = CreateFile(Source,FileAccessAPI.GENERIC_READ,
                                        FileShare.Read,0,FileMode.Open,
                                        FileFlags.BackupSemantics,0);
        IntPtr Dstream = CreateFile(Destination,FileAccessAPI.GENERIC_WRITE,
                                     FileShare.Write,0,FileMode.OpenOrCreate,
                                     FileFlags.BackupSemantics,0);
        
        //make a filestream
        srcStream = new FileStream(Sstream,FileAccess.Read,true);
        dstStream = new FileStream(Dstream,FileAccess.Write,true);

        byte[] srcData = new byte[srcStream.Length];
        srcStream.Read(srcData,0,srcData.Length);
        
        //copy
        dstStream.Write(srcData,0,srcData.Length);

        dstStream.Close();
        srcStream.Close();

        CloseHandle(Sstream);
        CloseHandle(Dstream);

        return true;

    }
    catch (System.IO.IOException ex)
    {
        MessageBox.Show(ex.Message);
        return false;
    }
}

Files included

  • Visual ADS Detector.exe plus a manifest: An executable version of the bar, shows the alternative streams by specifying a filename.

References

Extra reading

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

nashcontrol
Web Developer
Israel Israel
No Biography provided
Follow on   Twitter

Comments and Discussions

 
Generalsome wrong on 2t disk simulator Pinmembernhchmg15-Dec-10 22:56 
QuestionWill this get updated for newer Windows? Pinmembercasperguy9-Mar-10 9:06 
QuestionHow to install? PinmemberArrin4-Nov-09 4:40 
GeneralStandalone does not accept folder PinmemberJJ Xu16-Jul-09 11:38 
GeneralDoes not show in explorer even after reboot. PinmemberTattenbach6-Feb-07 8:38 
Questionabout the view panl Pinmembergoog /\/\@/\/17-Jan-07 3:37 
GeneralUsed installer but ... PinmemberDSXVI16-Oct-06 8:43 
GeneralRe: Used installer but ... Pinmembernashcontrol24-Oct-06 4:56 
GeneralPrint option not yet implemented Pinmembernashcontrol6-Dec-05 22:46 
will be operational in the next version.
 
sorry...
Generallist without view PinmemberCataldo Esposito2-Jun-05 20:57 
GeneralRe: list without view Pinmembernashcontrol11-Jun-05 20:21 
Generalknown bug Pinmembernashcontrol4-Dec-05 21:33 
GeneralRe: Problems, problems PinmemberJohn Hind28-Oct-04 23:49 
GeneralRe: Problems, problems PinmemberIsmaelj26-Jan-05 22:22 
GeneralProblems, problems PinmemberJohn Hind24-Oct-04 1:15 
GeneralRe: Problems, problems Pinmembernashcontrol28-Oct-04 20:47 
Generalknown bug Pinmembernashcontrol4-Dec-05 21:34 
QuestionHow to activate Pinmemberextoxic12-Feb-04 13:01 
AnswerRe: How to activate PinsussAnonymous20-Feb-04 0:55 
GeneralRe: How to activate PinmemberVideogamer55513-Sep-10 8:33 
GeneralBrilliant PinmemberDirk Vandenheuvel8-Oct-03 1:18 
GeneralThis is very cool! PinmemberChris Richardson7-Oct-03 14:02 
GeneralCosmetically PinmemberWhosit7-Oct-03 7:54 
GeneralRe: Cosmetically PinmemberChris Richardson7-Oct-03 14:01 
GeneralWild! PinmemberStephane Rodriguez.7-Oct-03 3:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 6 Dec 2005
Article Copyright 2003 by nashcontrol
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid