Created
June 9, 2022 23:18
-
-
Save hugsy/6e5a6766e81928d3e647844283db6039 to your computer and use it in GitHub Desktop.
NTFS streams
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// http://web.archive.org/web/20150617011338/http://win32.mvps.org/ntfs/dump_ntfs_streams.cpp | |
// | |
#include <windows.h> | |
#include <stdio.h> | |
#pragma hdrstop | |
#define err doerr( __FILE__, __LINE__ ) | |
void doerr( const char *file, int line ) | |
{ | |
DWORD e; | |
e = GetLastError(); | |
if ( e == 0 ) | |
return; | |
printf( "%s(%d): gle = %lu\n", file, line, e ); | |
exit( 2 ); | |
} | |
void enableprivs() | |
{ | |
HANDLE hToken; | |
byte buf[sizeof TOKEN_PRIVILEGES * 2]; | |
TOKEN_PRIVILEGES & tkp = *( (TOKEN_PRIVILEGES *) buf ); | |
if ( ! OpenProcessToken( GetCurrentProcess(), | |
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) ) | |
err; | |
// enable SeBackupPrivilege, SeRestorePrivilege | |
if ( !LookupPrivilegeValue( NULL, SE_BACKUP_NAME, &tkp.Privileges[0].Luid ) ) | |
err; | |
if ( !LookupPrivilegeValue( NULL, SE_RESTORE_NAME, &tkp.Privileges[1].Luid ) ) | |
err; | |
tkp.PrivilegeCount = 2; | |
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
tkp.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; | |
AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, | |
NULL, NULL ); | |
} | |
void dumphdr( WIN32_STREAM_ID & wsi ) | |
{ | |
const char *p; | |
printf( "\nstream [%lu] \"%S\":\n", wsi.dwStreamNameSize, | |
wsi.dwStreamNameSize? wsi.cStreamName: L"" ); | |
switch ( wsi.dwStreamId ) | |
{ | |
case BACKUP_DATA: | |
p = "data"; | |
break; | |
case BACKUP_EA_DATA: | |
p = "extended attributes"; | |
break; | |
case BACKUP_SECURITY_DATA: | |
p = "security"; | |
break; | |
case BACKUP_ALTERNATE_DATA: | |
p = "other streams"; | |
break; | |
case BACKUP_LINK: | |
p = "link"; | |
break; | |
default: | |
p = "unknown"; | |
break; | |
} | |
printf( " type: %s\n", p ); | |
printf( " size: %I64d\n", wsi.Size.QuadPart ); | |
} | |
int main( int argc, char *argv[] ) | |
{ | |
HANDLE fh; | |
if ( argc != 2 ) | |
{ | |
printf( "usage: dump_ntfs_streams {file}\n" ); | |
return 1; | |
} | |
// SeBackupPrivilege is not necessary to enumerate streams -- | |
// but it helps if you are an admin/backup-operator and need | |
// to scan files to which you have no permissions | |
enableprivs(); | |
fh = CreateFile( argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, | |
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL ); | |
if ( fh == INVALID_HANDLE_VALUE || fh == NULL ) | |
err; | |
byte buf[4096]; | |
DWORD numread, numtoskip; | |
void *ctx = NULL; | |
WIN32_STREAM_ID & wsi = *( (WIN32_STREAM_ID *) buf ); | |
numtoskip = 0; | |
while ( 1 ) | |
{ | |
// we are at the start of a stream header. read it. | |
if ( ! BackupRead( fh, buf, 20, &numread, FALSE, TRUE, &ctx ) ) | |
err; | |
if ( numread == 0 ) | |
break; | |
if ( wsi.dwStreamNameSize > 0 ) | |
{ | |
if ( ! BackupRead( fh, buf + 20, wsi.dwStreamNameSize, &numread, FALSE, TRUE, &ctx ) ) | |
err; | |
if ( numread != wsi.dwStreamNameSize ) | |
break; | |
} | |
dumphdr( wsi ); | |
// skip stream data | |
if ( wsi.Size.QuadPart > 0 ) | |
{ | |
DWORD lo, hi; | |
BackupSeek( fh, 0xffffffffL, 0x7fffffffL, &lo, &hi, &ctx ); | |
} | |
} | |
// make NT release the context | |
BackupRead( fh, buf, 0, &numread, TRUE, FALSE, &ctx ); | |
CloseHandle( fh ); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// http://web.archive.org/web/20150616195443/http://win32.mvps.org/ntfs/streams.cpp | |
// | |
#ifndef UNICODE | |
#define UNICODE | |
#endif | |
#ifndef UNICODE | |
#define _UNICODE | |
#endif | |
#include <stdio.h> | |
#include <windows.h> | |
#include <wchar.h> | |
#include <string.h> | |
#include <ntsecapi.h> // for LsaNtStatusToWinError() | |
#pragma hdrstop | |
#define IN | |
#define OUT | |
// | |
// Define the file information class values | |
// | |
// WARNING: The order of the following values are assumed by the I/O system. | |
// Any changes made here should be reflected there as well. | |
// | |
typedef enum _FILE_INFORMATION_CLASS { | |
FileDirectoryInformation = 1, | |
FileFullDirectoryInformation, | |
FileBothDirectoryInformation, | |
FileBasicInformation, | |
FileStandardInformation, | |
FileInternalInformation, | |
FileEaInformation, | |
FileAccessInformation, | |
FileNameInformation, | |
FileRenameInformation, | |
FileLinkInformation, | |
FileNamesInformation, | |
FileDispositionInformation, | |
FilePositionInformation, | |
FileFullEaInformation, | |
FileModeInformation, | |
FileAlignmentInformation, | |
FileAllInformation, | |
FileAllocationInformation, | |
FileEndOfFileInformation, | |
FileAlternateNameInformation, | |
FileStreamInformation, | |
FilePipeInformation, | |
FilePipeLocalInformation, | |
FilePipeRemoteInformation, | |
FileMailslotQueryInformation, | |
FileMailslotSetInformation, | |
FileCompressionInformation, | |
FileCopyOnWriteInformation, | |
FileCompletionInformation, | |
FileMoveClusterInformation, | |
FileOleClassIdInformation, | |
FileOleStateBitsInformation, | |
FileNetworkOpenInformation, | |
FileObjectIdInformation, | |
FileOleAllInformation, | |
FileOleDirectoryInformation, | |
FileContentIndexInformation, | |
FileInheritContentIndexInformation, | |
FileOleInformation, | |
FileMaximumInformation | |
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; | |
// | |
// Define the various structures which are returned on query operations | |
// | |
typedef struct _FILE_BASIC_INFORMATION { | |
LARGE_INTEGER CreationTime; | |
LARGE_INTEGER LastAccessTime; | |
LARGE_INTEGER LastWriteTime; | |
LARGE_INTEGER ChangeTime; | |
ULONG FileAttributes; | |
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; | |
typedef struct _FILE_STANDARD_INFORMATION { | |
LARGE_INTEGER AllocationSize; | |
LARGE_INTEGER EndOfFile; | |
ULONG NumberOfLinks; | |
BOOLEAN DeletePending; | |
BOOLEAN Directory; | |
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION; | |
typedef struct _FILE_POSITION_INFORMATION { | |
LARGE_INTEGER CurrentByteOffset; | |
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION; | |
typedef struct _FILE_ALIGNMENT_INFORMATION { | |
ULONG AlignmentRequirement; | |
} FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION; | |
typedef struct _FILE_NETWORK_OPEN_INFORMATION { | |
LARGE_INTEGER CreationTime; | |
LARGE_INTEGER LastAccessTime; | |
LARGE_INTEGER LastWriteTime; | |
LARGE_INTEGER ChangeTime; | |
LARGE_INTEGER AllocationSize; | |
LARGE_INTEGER EndOfFile; | |
ULONG FileAttributes; | |
} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION; | |
typedef struct _FILE_DISPOSITION_INFORMATION { | |
BOOLEAN DeleteFile; | |
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; | |
typedef struct _FILE_END_OF_FILE_INFORMATION { | |
LARGE_INTEGER EndOfFile; | |
} FILE_END_OF_FILE_INFORMATION, *PFILE_END_OF_FILE_INFORMATION; | |
typedef struct _FILE_FULL_EA_INFORMATION { | |
ULONG NextEntryOffset; | |
UCHAR Flags; | |
UCHAR EaNameLength; | |
USHORT EaValueLength; | |
CHAR EaName[1]; | |
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION; | |
struct FILE_STREAM_INFORMATION | |
{ | |
ULONG NextEntryOffset; | |
ULONG StreamNameLength; | |
LARGE_INTEGER StreamSize; | |
LARGE_INTEGER StreamAllocationSize; | |
WCHAR StreamName[1]; | |
}; | |
// | |
// Define the base asynchronous I/O argument types | |
// | |
typedef struct _IO_STATUS_BLOCK { | |
NTSTATUS Status; | |
ULONG Information; | |
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; | |
DWORD __stdcall NtQueryInformationFile( | |
IN HANDLE FileHandle, | |
OUT PIO_STATUS_BLOCK IoStatusBlock, | |
OUT PVOID FileInformation, | |
IN ULONG Length, | |
IN FILE_INFORMATION_CLASS FileInformationClass | |
); | |
typedef DWORD (__stdcall *NQIF)( | |
IN HANDLE FileHandle, | |
OUT PIO_STATUS_BLOCK IoStatusBlock, | |
OUT PVOID FileInformation, | |
IN ULONG Length, | |
IN FILE_INFORMATION_CLASS FileInformationClass | |
); | |
NQIF nqif = NULL; | |
const int MAXLEN = 2048; // arbitrary filename length limit | |
void checkOneFile( const wchar_t *file, int& numFilesWithStreams, int& numStreams ) | |
{ | |
HANDLE h; | |
DWORD rc; | |
IO_STATUS_BLOCK iosb; | |
static byte fsibuf[32768]; | |
FILE_STREAM_INFORMATION *fsi; | |
bool mustPrintName = true; | |
static const wchar_t * const DefaultStreamName = L"::$DATA"; | |
static const ULONG DefaultStreamNameLength = 7; | |
// note that while we do have to open the file, not even read access is needed | |
h = CreateFile( file, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | |
OPEN_EXISTING, 0, NULL ); | |
if ( h == INVALID_HANDLE_VALUE ) | |
{ | |
wprintf( L"Error %d opening \"%s\".\n", GetLastError(), file ); | |
return; | |
} | |
rc = nqif( h, &iosb, &fsibuf, sizeof( fsibuf ), FileStreamInformation ); | |
if ( rc != 0 ) | |
{ | |
wprintf( L"Error %d [NTSTATUS %ld/%08lxh] on NQIF() of \"%s\".\n", | |
LsaNtStatusToWinError( rc ), rc, rc, file ); | |
CloseHandle( h ); | |
return; | |
} | |
CloseHandle( h ); | |
fsi = (FILE_STREAM_INFORMATION *) fsibuf; | |
while ( 1 ) | |
{ | |
if ( fsi->StreamNameLength != 2 * DefaultStreamNameLength || | |
wcsncmp( fsi->StreamName, DefaultStreamName, DefaultStreamNameLength ) != 0 ) | |
{ | |
// non-default stream found! | |
if ( mustPrintName ) | |
{ | |
// first stream for this file, bump file count and show file name | |
++ numFilesWithStreams; | |
wprintf( L"\n%s\n", file ); | |
mustPrintName = false; | |
} | |
// bump stream count, show stream info | |
++ numStreams; | |
wprintf( L" %8I64u %-*.*s\n", fsi->StreamSize, fsi->StreamNameLength / 2, | |
fsi->StreamNameLength / 2, fsi->StreamName ); | |
} | |
if ( fsi->NextEntryOffset == 0 ) | |
break; | |
fsi = (FILE_STREAM_INFORMATION *) ( fsi->NextEntryOffset + (byte *) fsi ); | |
} | |
return; | |
} | |
void doFilespec( const wchar_t *srchPath, int& numFilesWithStreams, int& numStreams ) | |
{ | |
wchar_t path[MAXLEN] = L""; | |
wchar_t *p, *endOfPath; | |
WIN32_FIND_DATA wfd; | |
HANDLE ffh; | |
if ( srchPath == NULL || *srchPath == L'\0' || wcslen( srchPath ) >= MAXLEN ) | |
return; | |
wcscpy( path, srchPath ); | |
for ( p = path; *p != L'\0'; ++ p ) | |
{ | |
if ( *p == L'/' ) | |
*p = L'\\'; | |
} | |
p = wcsrchr( path, L'\\' ); // isolate last backslash | |
if ( p == NULL ) // no backslash, only a filespec | |
endOfPath = path; | |
else | |
{ | |
endOfPath = p + 1; | |
if ( *endOfPath == L'\0' ) // no filespec? | |
wcscpy( endOfPath, L"*" ); // invent one | |
} | |
// loop over files | |
ffh = FindFirstFile( path, &wfd ); | |
if ( ffh != INVALID_HANDLE_VALUE ) | |
{ | |
BOOL ok; | |
do | |
{ | |
if ( ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 ) | |
{ | |
// it's a directory -- may want to recurse | |
} | |
else // it's a file | |
{ | |
wcscpy( endOfPath, wfd.cFileName ); // tack file onto path | |
checkOneFile( path, numFilesWithStreams, numStreams); | |
} | |
ok = FindNextFile( ffh, &wfd ); | |
} while ( ok ); | |
FindClose( ffh ); | |
} | |
else | |
wprintf( L"Error %d when searching for \"%s\".\n", GetLastError(), path ); | |
return; | |
} | |
extern "C" | |
int __cdecl wmain( int argc, wchar_t *argv[], wchar_t *envp[] ) | |
{ | |
int numFilesWithStreams = 0, numStreams = 0, i; | |
HINSTANCE hNtdll; | |
if ( argc == 1 ) | |
{ | |
_putws( L"usage: streams filespec [...]" ); | |
_putws( L"To specify all files in a directory, use \"dir\\\" or \"dir\\*\"." ); | |
_putws( L"streams _only_ reports on files having non-default streams" ); | |
return 1; | |
} | |
hNtdll = LoadLibrary( L"ntdll.dll" ); | |
if ( hNtdll < (HINSTANCE) 33 ) | |
{ | |
wprintf( L"Error %d [%d] loading NTDLL.DLL. Huh?\n", (int) hNtdll, GetLastError() ); | |
return -2; | |
} | |
nqif = (NQIF) GetProcAddress( hNtdll, "NtQueryInformationFile" ); | |
if ( nqif == NULL ) | |
{ | |
wprintf( L"NQIF() missing from NTDLL.DLL. Huh?\n" ); | |
FreeLibrary( hNtdll ); | |
return -2; | |
} | |
for ( i = 1; i < argc; ++ i ) | |
doFilespec( argv[i], numFilesWithStreams, numStreams ); | |
FreeLibrary( hNtdll ); | |
return numStreams; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment