-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdump_ntfs_streams.cpp
170 lines (140 loc) · 5 KB
/
dump_ntfs_streams.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#pragma hdrstop
#pragma comment(lib, "advapi32.lib")
void doerr( const char *file, int line )
{
DWORD gle = GetLastError();
if( gle == 0 )
return;
printf( "%s(%d): GetLastError => 0x%X\n", file, line, gle );
exit( 2 );
}
#define err doerr( __FILE__, __LINE__ )
void enableprivs()
{
HANDLE hToken;
if( ! OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
err;
// enable SeBackupPrivilege, SeRestorePrivilege
byte buf[sizeof TOKEN_PRIVILEGES * 2];
TOKEN_PRIVILEGES & tkp = *( (TOKEN_PRIVILEGES *) buf );
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;
if( ! AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, NULL, NULL ) )
err;
}
void dumphdr( WIN32_STREAM_ID & wsi )
{
const char *pid;
switch ( wsi.dwStreamId )
{
case BACKUP_DATA : pid = "BACKUP_DATA" ; break;
case BACKUP_EA_DATA : pid = "BACKUP_EA_DATA" ; break;
case BACKUP_SECURITY_DATA : pid = "BACKUP_SECURITY_DATA" ; break;
case BACKUP_ALTERNATE_DATA : pid = "BACKUP_ALTERNATE_DATA" ; break;
case BACKUP_LINK : pid = "BACKUP_LINK" ; break;
case BACKUP_OBJECT_ID : pid = "BACKUP_OBJECT_ID" ; break;
case BACKUP_PROPERTY_DATA : pid = "BACKUP_PROPERTY_DATA" ; break;
case BACKUP_REPARSE_DATA : pid = "BACKUP_REPARSE_DATA" ; break;
case BACKUP_SPARSE_BLOCK : pid = "BACKUP_SPARSE_BLOCK" ; break;
default: {
static char stbuf[32];
_snprintf( stbuf, sizeof stbuf, "StreamID=0x%08X", wsi.dwStreamId );
pid = stbuf;
break;
}
}
printf( "%-22s %c%c %8I64d bytes"
, pid
, (wsi.dwStreamAttributes & STREAM_MODIFIED_WHEN_READ) ? 'M':'m'
, (wsi.dwStreamAttributes & STREAM_CONTAINS_SECURITY ) ? 'S':'s'
, wsi.Size.QuadPart
);
if( wsi.dwStreamNameSize ) {
// wsi.cStreamName is a UNICODE string that is NOT NUL-TERMINATED
// AND we did not read beyond the unterminated UNICODE string
// "UNICODE string" apparently equates to "UTF-16"
// so we add the UNICODE string NUL-TERMINATION now:
((char *)wsi.cStreamName)[wsi.dwStreamNameSize+0] = 0; // UNICODE/UTF-16 NUL char is 2 bytes of 0?
((char *)wsi.cStreamName)[wsi.dwStreamNameSize+1] = 0;
const int wlen( wcslen( wsi.cStreamName ) );
printf( ", StreamName(%d)='%S'", wlen, wsi.cStreamName );
// printf( ", StreamName='%S'", wsi.cStreamName );
enum { StreamNamechars = 1024 };
if( wsi.dwStreamNameSize <= StreamNamechars ) {
char dest[ StreamNamechars + 1 ];
WideCharToMultiByte( CP_OEMCP, 0, wsi.cStreamName,
wlen // WORKS
// -1 // WORKS
, dest,
StreamNamechars // WORKS
// -1 // does NOT work
, 0, 0 );
const int slen( strlen( dest ) );
assert( slen == wlen );
// 20091129 kgoodwin works, commented out since duplicate info (until I actually NEED a
// standard C string rep of the stream name)
// printf( " OEM(%d)='%s'", slen, dest );
}
}
printf( "\n" );
}
int main( int argc, char *argv[] )
{
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();
HANDLE 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 );
enum { SIZEOF_WIN32_STREAM_ID = 20 };
numtoskip = 0;
while ( 1 )
{
// we are at the start of a stream header. read it.
if( ! BackupRead( fh, buf, SIZEOF_WIN32_STREAM_ID, &numread, FALSE, TRUE, &ctx ) )
err;
if( numread != SIZEOF_WIN32_STREAM_ID )
break;
if( wsi.dwStreamNameSize > 0 )
{
if( wsi.dwStreamNameSize > sizeof buf - SIZEOF_WIN32_STREAM_ID )
{
printf( "wsi.dwStreamNameSize (%d) is too big for my buffer (%d)\n", wsi.dwStreamNameSize, sizeof buf - SIZEOF_WIN32_STREAM_ID );
err;
}
if( ! BackupRead( fh, buf + SIZEOF_WIN32_STREAM_ID, 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;
}