Получить список потоков 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