Получить список потоков NTFS на C++
Алгоритм, позволяющий получить список потоков NTFS, прикреплённых к какому-либо файлу, каталогу или диску, можно реализовать несколькими способами. Первый способ заключается в использовании WinAPI-функции BackupRead (функция для резервного копирования файлов) и структуры WIN32_STREAM_ID. Второй способ основан на использовании стандартных функций FindFirstStreamW и FindNextStreamW, которые предназначены непосредственно для получения списка потоков NTFS, правда появились эти функции только в Windows Server 2003 и Windows Vista, то есть приложение не сможет использовать их в более ранних версиях Windows. И, наконец, третий способ это применение Native API функции NtQueryInformationFile, запроса с помощью неё информации FileStreamInformation (используется структура FILE_STREAM_INFORMATION).
Для поиска и редактирования потоков NTFS на компьютере можно воспользоваться программой NTFS Stream Explorer.
Получение списка потоков NTFS с помощью BackupRead
Особенности данного способа таковы. Во-первых этот способ самый универсальный, так как здесь используется стандартная функция WinAPI, доступная на максимальном количестве Windows-систем. Во-вторых, этот способ показывает не одни лишь альтернативные файловые потоки, но и остальные системные типы потоков. В третьих, с использованием этого способа связан баг в функции BackupSeek. Нижеприведённый код уже содержит обход этого бага для корректной работы.
Макрос BackupClose — это корректный способ завершить получение списка потоков. Описание полей структуры WIN32_STREAM_ID смотрите в MSDN.
#define BackupClose(x,y) BackupRead(x, NULL, 0, NULL, TRUE, FALSE, y)
WIN32_STREAM_ID sid;
LPVOID lpContext = NULL;
while (TRUE)
{
BackupRead(hFile, (LPBYTE)&sid, dwStreamHeaderSize,
&dwRead, FALSE, TRUE, &lpContext);
// Условие выхода из цикла
if (0 == dwRead)
{
break;
}
// Тут можно вывести какую-либо информацию о потоке, взятую
// из WIN32_STREAM_ID
// Если поток именованный
if (sid.dwStreamNameSize > 0)
{
// с помощью BackupRead можно прочитать имя потока
}
// Если поток имеет не нулевой размер
if (sid.Size.QuadPart > 0)
{
if (BACKUP_SPARSE_BLOCK == sid.dwStreamId)
{
// Баг в BackupSeek не позволяет пропустить разреженный блок.
// Читаем его, не обрабатывая.
sparse_buf = (LPBYTE) GlobalAlloc(GPTR, BUF_SIZE);
BackupRead(hFile, sparse_buf, sid.Size.LowPart, &dwRead,
FALSE, TRUE, &lpContext);
GlobalFree(sparse_buf);
} else
{
//Перемещаемся к следующему потоку
BackupSeek(hFile, sid.Size.LowPart, sid.Size.HighPart,
&dw1, &dw2, &lpContext);
}
}
//Очищаем структуру перед следующим проходом цикла
ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
}
BackupClose(hFile, &lpContext);
Получение списка потоков NTFS с помощью FindFirstStreamW и FindNextStreamW
Получение потоков с помощью функций FindFirstStreamW и FindNextStreamW здорово напоминает поиск файлов с помощью функций FindFirstFile и FindNextFile. Я не буду пока приводить здесь код, иллюстрирующий этот способ, так как он должен быть достаточно простым. Код примера на C# можно посмотреть на этой странице MSDN. Следует помнить, что эти функции не доступны в операционных системах, вышедших до Win2k3/Vista.
Получение списка потоков NTFS с помощью NtQueryInformationFile
К особенностям этого метода относится то, что здесь используется функция из Native API — NtQueryInformationFile. Список потоков получается через запрос FileStreamInformation. Информация о потоках оказывается в буфере в виде структур FILE_STREAM_INFORMATION. Эта структура имеет следующий формат:
typedef struct _FILE_STREAM_INFORMATION
{
ULONG NextEntryOffset;
ULONG StreamNameLength;
LARGE_INTEGER StreamSize;
LARGE_INTEGER StreamAllocationSize;
WCHAR StreamName[1];
} FILE_STREAM_INFORMATION, *PFILE_STREAM_INFORMATION;
Сам код получения потоков представлен ниже:
IO_STATUS_BLOCK IoStatusBlock;
CHAR buf[STREAM_BUF_LEN];
PFILE_STREAM_INFORMATION fsi = (PFILE_STREAM_INFORMATION) buf;
ULONG cur_pos;
WCHAR str[MAX_PATH];
NTSTATUS ntst =
NtQueryInformationFile(
hFile,
&IoStatusBlock,
fsi,
STREAM_BUF_LEN,
FileStreamInformation
);
if (NT_SUCCESS(ntst) && IoStatusBlock.Information > 0)
{
cur_pos = 0;
while (TRUE)
{
memcpy(str, fsi->StreamName, fsi->StreamNameLength);
str[fsi->StreamNameLength/sizeof(WCHAR)]=L'\0';
// Основное содержимое файла видится как поток ::$DATA
if (0 != wcsncmp(str, L"::", 2))
{
// Обработка имени потока
PWCHAR pTempStreamName = (PWCHAR) GlobalAlloc(GPTR, fsi->StreamNameLength);
wcscpy(pTempStreamName, &str[1]);
ZeroMemory(str, MAX_PATH);
wcsncpy(str, pTempStreamName, wcslen(pTempStreamName) - wcslen(L":$DATA"));
GlobalFree(pTempStreamName);
// Здесь доступно имя найденного альтернативного потока
}
// Условие выхода из цикла
if (0 == fsi->NextEntryOffset)
{
break;
}
// Переход к следующему потоку
cur_pos += fsi->NextEntryOffset;
fsi = (PFILE_STREAM_INFORMATION)&buf[cur_pos];
}
}
CloseHandle(hFile);
По теме файловых потоков также есть следующее:
- NTFS Stream Explorer 2.00 Программа для работы с потоками NTFS и справка по ней.
- Скрытое хранение данных в потоках файла $Repair.
Избранное
Остальное
Лента atom