Алгоритм получения списка потоков NTFS (original) (raw)
Получить список потоков 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.
При копировании материалов хорошим тоном будет указание авторства и ссылка на сайт. По поводу рекламы обращайтесь на почту [email protected]