Click here to Skip to main content
15,861,125 members
Articles / Programming Languages / C#
Article

Visual browsing of alternative data-streams in Windows Explorer

Rate me:
Please Sign up or sign in to vote.
4.82/5 (27 votes)
5 Dec 20053 min read 158.8K   5.5K   83   24
ADSDetector information with code examples.

Image 1

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):

C#
/// <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:

C#
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.]

C#
/// <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:

C#
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:

C#
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.]

C#
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:

C#
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


Written By
Web Developer
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionWill this get updated for newer Windows? Pin
casperguy9-Mar-10 9:06
casperguy9-Mar-10 9:06 
QuestionHow to install? Pin
Arrin4-Nov-09 4:40
Arrin4-Nov-09 4:40 
GeneralStandalone does not accept folder Pin
JJ Xu16-Jul-09 11:38
JJ Xu16-Jul-09 11:38 
GeneralDoes not show in explorer even after reboot. Pin
Tattenbach6-Feb-07 8:38
Tattenbach6-Feb-07 8:38 
Questionabout the view panl Pin
goog /\/\@/\/17-Jan-07 3:37
goog /\/\@/\/17-Jan-07 3:37 
hi, the app. is good, but it does not show in the view > explorer bar > Visual ADS Detector!
my os: win xp sp2 + MS .NET Framework version 1.1.4322.573.
and i am not a Programmer.

Cry | :(( helpCry | :((
GeneralUsed installer but ... Pin
DSXVI16-Oct-06 8:43
DSXVI16-Oct-06 8:43 
GeneralRe: Used installer but ... Pin
nashcontrol24-Oct-06 4:56
nashcontrol24-Oct-06 4:56 
GeneralPrint option not yet implemented Pin
nashcontrol6-Dec-05 22:46
nashcontrol6-Dec-05 22:46 
Generallist without view Pin
2-Jun-05 20:57
suss2-Jun-05 20:57 
GeneralRe: list without view Pin
nashcontrol11-Jun-05 20:21
nashcontrol11-Jun-05 20:21 
Generalknown bug Pin
nashcontrol4-Dec-05 21:33
nashcontrol4-Dec-05 21:33 
GeneralRe: Problems, problems Pin
John Hind28-Oct-04 23:49
John Hind28-Oct-04 23:49 
GeneralRe: Problems, problems Pin
Ismaelj26-Jan-05 22:22
Ismaelj26-Jan-05 22:22 
GeneralProblems, problems Pin
John Hind24-Oct-04 1:15
John Hind24-Oct-04 1:15 
GeneralRe: Problems, problems Pin
nashcontrol28-Oct-04 20:47
nashcontrol28-Oct-04 20:47 
Generalknown bug Pin
nashcontrol4-Dec-05 21:34
nashcontrol4-Dec-05 21:34 
QuestionHow to activate Pin
extoxic12-Feb-04 13:01
extoxic12-Feb-04 13:01 
AnswerRe: How to activate Pin
Anonymous20-Feb-04 0:55
Anonymous20-Feb-04 0:55 
GeneralRe: How to activate Pin
Videogamer55513-Sep-10 8:33
Videogamer55513-Sep-10 8:33 
GeneralBrilliant Pin
Dirk Vandenheuvel8-Oct-03 1:18
Dirk Vandenheuvel8-Oct-03 1:18 
GeneralThis is very cool! Pin
Chris Richardson7-Oct-03 14:02
Chris Richardson7-Oct-03 14:02 
GeneralCosmetically Pin
eidylon7-Oct-03 7:54
eidylon7-Oct-03 7:54 
GeneralRe: Cosmetically Pin
Chris Richardson7-Oct-03 14:01
Chris Richardson7-Oct-03 14:01 
GeneralWild! Pin
Stephane Rodriguez.7-Oct-03 3:33
Stephane Rodriguez.7-Oct-03 3:33 

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

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