
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):
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)
{
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);
string sName = new string(bName);
int i = sName.IndexOf(STREAM_SEP, 1);
if (i>-1) sName = sName.Substring(1, i-1);
else
{
i = sName.IndexOf('\0');
if (i>-1) sName = sName.Substring(1, i-1);
}
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;
BackupSeek(stream, sid.Size.Low, sid.Size.High,
ref l, ref h, ref Context);
}
else break;
}
}
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)
{
Uri URI = new Uri(NTFSADS.FullStreamName,true);
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.]
private static System.Drawing.Icon GetIconFromShell(string Extention)
{
if (Extention[0] != '.') Extention = '.' + Extention;
SHFILEINFO shinfo = new SHFILEINFO();
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 );
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[] ExplorerCutSelect = new byte[4];
((MemoryStream)FileDataObject.GetData("Preferred DropEffect")).
Read(ExplorerCutSelect,0,4);
switch (ExplorerCutSelect[0])
{
case 2:
break;
case 5:
}
}
Finally, copying the stream:
public static bool CopyStream (string Source,string Destination)
{
try
{
FileStream srcStream;
FileStream dstStream;
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);
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);
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
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.