#define UNICODE 1
#include <windows.h>
#include <stdio.h>
#include "streams.h"
//
// Native functions we use
//
NTSTATUS (__stdcall *NtQueryInformationFile)(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
);
ULONG (__stdcall *RtlNtStatusToDosError) (
IN NTSTATUS Status
);
//
// Globals
//
ULONG FilesProcessed = 0;
ULONG DotsPrinted = 0;
BOOLEAN PrintDirectoryOpenErrors = FALSE;
//----------------------------------------------------------------------
//
// PrintNtError
//
// Formats an error message for the last native error.
//
//----------------------------------------------------------------------
void PrintNtError( NTSTATUS status )
{
WCHAR *errMsg;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, RtlNtStatusToDosError( status ),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &errMsg, 0, NULL );
wprintf(L"%s\n", errMsg );
LocalFree( errMsg );
}
//--------------------------------------------------------------------
//
// PrintWin32Error
//
// Translates a Win32 error into a text equivalent
//
//--------------------------------------------------------------------
void PrintWin32Error( DWORD ErrorCode )
{
LPVOID lpMsgBuf;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, ErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL );
wprintf(L"%s\n", lpMsgBuf );
LocalFree( lpMsgBuf );
}
//----------------------------------------------------------------------
//
// EnableTokenPrivilege
//
// Enables the load driver privilege
//
//----------------------------------------------------------------------
BOOL EnableTokenPrivilege( PTCHAR PrivilegeName )
{
TOKEN_PRIVILEGES tp;
LUID luid;
HANDLE hToken;
TOKEN_PRIVILEGES tpPrevious;
DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);
//
// Get debug privilege
//
if(!OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken )) {
return FALSE;
}
if(!LookupPrivilegeValue( NULL, PrivilegeName, &luid )) return FALSE;
//
// first pass. get current privilege setting
//
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
&tpPrevious,
&cbPrevious
);
if (GetLastError() != ERROR_SUCCESS) return FALSE;
//
// second pass. set privilege based on previous setting
//
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
AdjustTokenPrivileges(
hToken,
FALSE,
&tpPrevious,
cbPrevious,
NULL,
NULL
);
return GetLastError() == ERROR_SUCCESS;
}
//--------------------------------------------------------------------
//
// ProcessFile
//
// Queries a file to obtain stream information.
//
//--------------------------------------------------------------------
VOID ProcessFile( WCHAR *FileName, BOOLEAN IsDirectory )
{
PFILE_STREAM_INFORMATION streamInfo, streamInfoPtr;
ULONG streamInfoSize = 0;
BOOLEAN printedFile = FALSE;
NTSTATUS status;
HANDLE fileHandle;
WCHAR streamName[MAX_PATH];
IO_STATUS_BLOCK ioStatus;
//
// Open the file
//
fileHandle = CreateFile( FileName, GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, 0 );
if( fileHandle == INVALID_HANDLE_VALUE ) {
if( !IsDirectory || PrintDirectoryOpenErrors ) {
wprintf(L"\rFailed to open %s: ", FileName );
PrintWin32Error( GetLastError() );
}
return;
}
if( !(++FilesProcessed % 500)) {
if( DotsPrinted == 3 ) {
wprintf(L"\r \r");
DotsPrinted = 0;
} else {
DotsPrinted++;
wprintf(L".");
}
fflush( stdout );
}
streamInfoSize = 16384;
streamInfo = malloc( streamInfoSize );
status = STATUS_BUFFER_OVERFLOW;
while( status == STATUS_BUFFER_OVERFLOW ) {
status = NtQueryInformationFile( fileHandle, &ioStatus,
streamInfo, streamInfoSize,
FileStreamInformation );
if( status == STATUS_BUFFER_OVERFLOW ) {
free( streamInfo );
streamInfoSize += 16384;
streamInfo = malloc( streamInfoSize );
} else {
break;
}
}
//
// If success, dump the contents
//
if( NT_SUCCESS( status ) && ioStatus.Information ) {
streamInfoPtr = streamInfo;
while( 1 ) {
memcpy( streamName,
streamInfoPtr->Name,
streamInfoPtr->NameLength );
streamName[ streamInfoPtr->NameLength/2 ] = 0;
//
// Skip the standard Data stream
//
if( wcsicmp( streamName, L"::$DATA" )) {
if( !printedFile ) {
wprintf(L"\r%s:\n", FileName );
printedFile = TRUE;
}
wprintf(L" %20s\t%I64d\n", streamName, streamInfoPtr->Size.QuadPart );
}
if( !streamInfoPtr->NextEntry ) break;
streamInfoPtr = (PFILE_STREAM_INFORMATION) ((char *) streamInfoPtr +
streamInfoPtr->NextEntry );
}
} else if( !NT_SUCCESS( status )) {
wprintf(L"\rError on %s: ", FileName );
PrintNtError( status );
}
free( streamInfo );
CloseHandle( fileHandle );
}
//--------------------------------------------------------------------
//
// ProcessDirectory
//
// Recursive routine that passes files to the stream analyzing
// function.
//
//--------------------------------------------------------------------
void ProcessDirectory( WCHAR *PathName, WCHAR *SearchPattern, BOOLEAN Recurse )
{
WCHAR subName[MAX_PATH], fileSearchName[MAX_PATH], searchName[MAX_PATH];
HANDLE dirHandle, patternHandle;
static BOOLEAN firstCall = TRUE;
WIN32_FIND_DATA foundFile;
//
// Scan the files and/or directories if this is a directory
//
if( firstCall ) {
if( wcsrchr( PathName, '*' ) ) {
if( wcsrchr( PathName, '\\' ) ) {
swprintf( SearchPattern, wcsrchr( PathName, '\\' )+1 );
wcscpy( searchName, PathName );
wcscpy( wcsrchr( searchName, '\\')+1, L"*.*" );
} else {
swprintf( SearchPattern, PathName );
wcscpy( searchName, PathName );
}
swprintf( fileSearchName, L"%s", PathName );
} else {
swprintf( SearchPattern, L"*.*" );
if( Recurse ) {
swprintf( searchName, L"%s\\*.*", PathName );
swprintf( fileSearchName, L"%s\\*.*", PathName );
} else {
swprintf( searchName, L"%s", PathName );
swprintf( fileSearchName, L"%s", PathName );
}
}
} else {
swprintf( searchName, L"%s\\*.*", PathName );
swprintf( fileSearchName, L"%s\\%s", PathName, SearchPattern );
}
//
// Process all the files, according to the search pattern
//
if( (patternHandle = FindFirstFile( fileSearchName, &foundFile )) !=
INVALID_HANDLE_VALUE ) {
do {
if( wcscmp( foundFile.cFileName, L"." ) &&
wcscmp( foundFile.cFileName, L".." )) {
wcscpy( subName, searchName );
if( wcsrchr( subName, '\\' ) )
wcscpy( wcsrchr( subName, '\\')+1, foundFile.cFileName );
else
wcscpy( subName, foundFile.cFileName );
//
// Do this file/directory
//
ProcessFile( subName,
(BOOLEAN) (foundFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
}
} while( FindNextFile( patternHandle, &foundFile ));
FindClose( patternHandle );
}
//
// Now recurse if we're supposed to
//
if( Recurse ) {
if( firstCall && !wcsrchr( searchName, L'\\') ) {
if( wcsrchr( searchName, L'*' )) {
if( (dirHandle = FindFirstFile( L"*.*", &foundFile )) ==
INVALID_HANDLE_VALUE ) {
//
// Nothing to process
//
return;
}
} else {
if( (dirHandle = FindFirstFile( searchName, &foundFile )) ==
INVALID_HANDLE_VALUE ) {
//
// Nothing to process
//
return;
}
}
} else {
if( (dirHandle = FindFirstFile( searchName, &foundFile )) ==
INVALID_HANDLE_VALUE ) {
//
// Nothing to process
//
return;
}
}
firstCall = FALSE;
do {
if( (foundFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
wcscmp( foundFile.cFileName, L"." ) &&
wcscmp( foundFile.cFileName, L".." )) {
wcscpy( subName, searchName );
if( wcsrchr( subName, '\\' ) )
wcscpy( wcsrchr( subName, '\\')+1, foundFile.cFileName );
else
wcscpy( subName, foundFile.cFileName );
//
// Go into this directory
//
ProcessDirectory( subName, SearchPattern, Recurse );
}
} while( FindNextFile( dirHandle, &foundFile ));
}
FindClose( dirHandle );
}
int Usage( WCHAR *ProgramName )
{
wprintf(L"usage: %s [-s] <file or directory>\n", ProgramName );
wprintf(L"-s Recurse subdirectories\n\n");
return -1;
}
int wmain( int argc, WCHAR *argv[] )
{
BOOLEAN recurse;
WCHAR *name;
PWCHAR filePart;
WCHAR searchPattern[MAX_PATH];
WCHAR searchPath[MAX_PATH];
//
// Print banner and perform parameter check
//
wprintf(L"\nStreams v1.3 - Enumerate alternate NTFS data streams\n" );
wprintf(L"Copyright (C) 1999-2001 Mark Russinovich\n");
wprintf(L"Sysinternals - [url]www.sysinternals.com\n\n[/url]");
if( argc != 2 && argc !=3 ) {
return Usage( argv[0] );
}
if( argc == 3 &&
wcsicmp( argv[1], L"/s" ) &&
wcsicmp( argv[1], L"-s" )) {
return Usage( argv[0] );
}
//
// Enable backup privilege if we can
//
if( EnableTokenPrivilege( SE_BACKUP_NAME )) {
PrintDirectoryOpenErrors = TRUE;
}
//
// Load the NTDLL entry point we need
//
if( !(NtQueryInformationFile = (void *) GetProcAddress( GetModuleHandle(L"ntdll.dll"),
"NtQueryInformationFile" )) ) {
wprintf(L"\nCould not find NtQueryInformationFile entry point in NTDLL.DLL\n");
exit(1);
}
if( !(RtlNtStatusToDosError = (void *) GetProcAddress( GetModuleHandle(L"ntdll.dll"),
"RtlNtStatusToDosError" )) ) {
wprintf(L"\nCould not find RtlNtStatusToDosError entry point in NTDLL.DLL\n");
exit(1);
}
//
// Read the name argument
//
if( argc == 3 ) {
name = argv[2];
recurse = TRUE;
} else {
name = argv[1];
recurse = FALSE;
}
//
// Now go and process directories
//
GetFullPathName( argv[argc-1], MAX_PATH, searchPath, &filePart );
ProcessDirectory( searchPath, searchPattern, recurse );
return 0;
}