Алгоритм получения списка потоков 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 APINtQueryInformationFile. Список потоков получается через запрос 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);

По теме файловых потоков также есть следующее:

При копировании материалов хорошим тоном будет указание авторства и ссылка на сайт. По поводу рекламы обращайтесь на почту [email protected]