Visual browsing of alternative data-streams in Windows Explorer






4.82/5 (25 votes)
Oct 7, 2003
3 min read

162275

5509
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
- Dino Esposito's article
- Pavel Zolnikov's BandObjectLib
- Gil_Schmidt's Icon Handler