hex.pp.ua

Алгоритм получения списка потоков NTFS

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

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

система комментирования CACKLE
Разделы сайта
Главная
Блог
Native API
NTFS и ReFS
Микроконтроллеры
Справочник NTDLL
Коды NTSTATUS
Разное

Избранное
NTFS Stream Explorer
Native Shell
Тенгвар

Остальное
nvpnhcknn (архив)
English pages
Контакты

Ленты atom
Лента Atom сайта Лента Atom блога



При копировании материалов хорошим тоном будет указание авторства и ссылка на сайт.