Как правило, программное обеспечение распространяется в виде потока электронов. Но чем больше загружаемый файл, тем выше вероятность ошибки в процессе передачи данных. Поэтому весьма полезно иметь под рукой надежное средство проверки целостности загружаемых файлов. К числу таких средств относятся алгоритмы криптографического хэширования. Подобный алгоритм принимает некоторую последовательность байтов (речь может идти, к примеру, о байтах файла) и выполняет над ними определенные вычисления. В результате получается выходное значение фиксированного размера (скажем, 128-разрядное, 160-разрядное). Смысл применения упомянутых алгоритмов хэширования в том, что если значения на входе различны, то значения на выходе не могут быть идентичными. .

На экранах 1 и 2 представлены практические примеры хэшированных значений. На экране 1 мы видим хэшированное значение SHA1 для файла an.iso с сайта Microsoft TechNet. На экране 2 представлены два хэшированных значения MD5 для программы установки OpenOffice.org. Если вы загрузите эти файлы, то сможете рассчитать хэшированные значения SHA1 и MD5, дабы удостовериться в том, что файлы были загружены без повреждения данных.

Хэш-значение SHA1 для файла an.iso
Экран 1. Хэш-значение SHA1 для файла an.iso
Хэш-значения MD5 для утилит-установщиков OpenOffice.org
Экран 2. Хэш-значения MD5 для утилит-установщиков OpenOffice.org

Знакомимся со сценарием Get-FileHash.ps1

Разработчики Microsoft не предусмотрели специальной команды с целью вычисления хэшированных значений для файлов, поэтому я решил написать сценарий Windows PowerShell, который приводится здесь под именем Get-FileHash.ps1. Этот сценарий обеспечивает с помощью среды Microsoft. NET Framework вычисление хэшированных значений файлов по алгоритмам MD5 и SHA1. Для выполнения сценария необходимо установить в системе среду PowerShell 2.0 или более новую версию. Код сценария представлен в листинге 1. Я рекомендую поместить файл FileHash.ps1 в один из каталогов в системном пути.

Для выполнения сценария используйте одну из следующих синтаксических конструкций:

Get-FileHash [-Path] 
   [-Hashtype ]

или

Get-FileHash -literalPath 
   [-Hashtype ]

Имя параметра -Path применяется факультативно. Он указывает на один или несколько файлов, в которые вы хотите записать хэшированное значение. Допускается использование символов подстановки, а также применение конвейерного ввода вместо параметра -Path.

Если вы хотите указать имя файла, содержащее символы, которые PowerShell обычно интерпретирует как символы экранирования (например, символы квадратных скобок [и]), можете использовать параметр -LiteralPath и указать одно или несколько имен файлов. Ну а коль скоро вы применили параметр -LiteralPath, использование символов подстановки не допускается; сценарий проигнорирует команду конвейерного ввода. Отмечу, что из двух параметров-Path и -LiteralPath разрешается использование либо первого, либо второго; одновременное применение обоих параметров не допускается.

Значением параметра -HashType должна быть строка MD5 или SHA1. Если вы не укажете этот параметр, по умолчанию будет применяться алгоритм MD5.

Выходными данными сценария Get-FileHash.ps1 являются объекты, содержащие маршрут к каждому файлу и его хэшированное значение по алгоритму MD5 или SHA1. На экране 3 представлен пример команды и результат ее выполнения. Имена файлов передаются этой команде по конвейеру.

Образец команды и ее выходные данные
Экран 3. Образец команды и ее выходные данные

Основные сведения о сценарии

Сценарий Get-FileHash.ps1 предполагает использование возможностей, реализованных в PowerShell 2.0 и более новых версиях: речь идет о справочных данных на основе комментариев, а также о дополнительных параметрах функций. Справочные данные на основе комментариев позволяют составной команде Get-Help отображать справочную информацию для сценария. Дополнительные параметры функций дают сценарию возможность выступать в качестве составной команды.

Справочные данные на основе комментариев представляют собой серию строк комментария (начинающихся с символа #) или блок комментариев (текст, помещенный между символами <# и #>); эти комментарии содержат особые ключевые слова, на основе которых PowerShell генерирует справочные данные. Если вы запустите команду

Get-Help Get-FileHash

оболочка PowerShell сгенерирует справочный текст с помощью особых ключевых слов (например,. SYNOPSIS,.DESCRIPTION,. PARAMETER). Справочные средства на основе комментариев — замечательное нововведение, реализованное в версии PowerShell 2.0. Они существенно облегчают составление собственных комментариев к функциям и сценариям. Чтобы получить дополнительные сведения о том, как использовать справочные данные на основе комментариев, следует в окне PowerShell выполнить команду

Get-Help about_Comment_Based_Help.

Дополнительные параметры дают администратору возможность анализировать используемые в сценарии параметры командной строки с помощью правил, построенных по схеме составных команд. В сценарии Get-FileHash.ps1 применяются наборы параметров, позволяющие этому сценарию принимать взаимоисключающие параметры.

В листинге 2 представлены используемые в сценарии Get-FileHash.ps1 атрибут CmdletBinding и инструкция param. CmdletBinding обеспечивает функционирование параметров сценария в режиме составных команд, а также определяет применяемый по умолчанию набор параметров. Инструкция param включает три параметра, которые определяются с помощью инструкций Parameter. В состав каждой инструкции Parameter входят атрибуты, которые задают режим работы соответствующего параметра. Речь идет о следующих атрибутах.

  • ParameterSetName="Name". Указывает на набор параметров, к которому относится данный параметр (это либо Path, либо LiteralPath). Если в параметре не содержится указания на тот или иной набор параметров, данный параметр может быть включен в любой набор. Свойство ParameterSetName объекта $PSCmdlet содержит имя текущего набора параметров.
  • Position=n. Позиция параметра в командной строке. При значении Position=0 соответствующий параметр должен быть указан первым, в случае Position=1 — вторым и т. д.
  • Mandatory=$TRUE. Использова­ние данного параметра обязательно. Если параметр не указывается, PowerShell предлагает ввести его.
  • ValueFromPipeline=$TRUE. Входные данные соответствующего параметра могут быть получены из конвейера.

Чтобы ознакомиться с более подробными сведениями о вышеупомянутых атрибутах, выполните в окне PowerShell следующие команды:

Get-Help about_Functions_advanced
Get-Help
   about_Functions_advanced_Parameters
Get-Help
   about_Functions_
   CmdletBindingattribute

Хотя в тексте статьи две последние команды размещены не в одной строке, помните, что в консоли PowerShell каждую команду нужно вводить одной строкой.

После инструкции param сценарий Get-FileHash.ps1 выполняет блоки begin и process. Таким образом обеспечивается работа сценария в режиме составных команд. Блок сценария begin выполняется однократно до начала обработки конвейера, а блок process выполняется по одному разу для каждого элемента конвейера. В случае если данные по конвейеру не передаются, блоки сценария begin и process выполняются по одному разу.

При выполнении сценарного блока begin сценарий получает подтверждение, что параметр -HashType имеет значение либо MD5, либо SHA1, и создает переменную $Provider, содержащую криптографический объект. NET, который вычисляет значения хэшей файлов. Далее сценарий определяет, указан ли в командной строке параметр -Path и присвоено ли ему какое-либо значение. Если этот параметр указан, но значение ему не присвоено, сценарий заключает, что входные данные будут поступать через конвейер, и присваивает переменной $PIPELINEINPUT значение true.

Кроме того, в блок сценария begin входит функция get-filehash2 — «рабочая лошадка» данного сценария. Функцию get-filehash2 я опишу чуть позже.

В ходе выполнения блока сценария process сценарий определяет, активен ли набор параметров Path (то есть был ли использован параметр -Path). Если набор параметров Path активен, сценарий считывает значение переменной $PIPELINEINPUT, чтобы определить, следует ли получать входные данные из конвейера или из содержимого параметра -Path. При этом, если входные данные поступают по конвейеру, сценарий выполняет функцию getfilehash2 для каждого входящего объекта. Если же передача данных по конвейеру не предусмотрена, сценарий передает входные данные функции get-filehash2 с помощью составных команд Get-Item и ForEach-Object.

Если набор параметров Path не является активным (иначе говоря, в качестве активного фигурирует набор параметров LiteralPath), сценарий извлекает нужный файл с помощью составной команды Get-Item, содержащей параметр -LiteralPath. Если команда Get-Item выполняется успешно (то есть переменная $file не является пустой), сценарий передает переменную $file в виде параметра функции get-filehash2.

Функция get-filehash2

Как я уже отмечал, функция get-filehash2, представленная в листинге 3, является «рабочей лошадкой» рассматриваемого сценария. Она выполняет три задачи.

  1. Определяет, соответствует ли значение параметра $file реальному файлу. Выяснить это необходимо, поскольку в среде PowerShell пути могут указывать на объекты, отличные от файлов, — например, на подразделы и каталоги реестра.
  2. Рассчитывает хэш-значение соответствующего файла по алгоритму MD5 или SHA1. Функция вызывает метод ComputeHash провайдера служб шифрования, который рассчитывает хэш-значение на основе потока байтов (в данном случае имеется в виду содержимое файла). Данный результат возвращается в виде строки байтов, поэтому функция с помощью объекта. NET StringBuilder генерирует строку, содержащую эти байты, в формате шестнадцатеричной строки.
  3. Формирует на выходе специальный объект, содержащий полное имя файла и его хэш-значение. Для создания упомянутого объекта функция использует составную команду Select-Object.

Хэширование файлов без проблем

Сценарий Get-FileHash.ps1 предоставляет в распоряжение администратора всю мощь реализованных в среде. NET Framework алгоритмов хэширования файлов MD5 и SHA1. Наконец-то мы получаем простое в использовании средство для расчетов в командной строке PowerShell файловых хэшей MD5 и SHA1. Дополнительная информация во врезке «Обратное преобразование хэша MD5 или SHA1».

Обратное преобразование хэша MD5 или SHA1 в текст

В общем случае выполнить обратное преобразование хэша MD5 или SHA1 в обычный текст можно только методом перебора. Но есть одно средство, которое может облегчить вам работу. Зайдите на md5.rednoize.com, введите значение хэша и, если вам повезет, вы получите искомый результат. Этот сайт содержит базу данных значений 49 313 614 хэшей с их эквивалентами в виде простого текста, и время на угадывание методом перебора может быть сведено практически к нулю, если ваш хэш уже содержится в базе данных.

Марк Джозеф Эдвардс (mark@ntshop.net) — старший редактор Windows IT Pro, ведет еженедельную рассылку Security UPDATE

Листинг 1. Код сценария Get-FileHash.ps1

# Get-FileHash.ps1
# Written by Bill Stewart (bstewart@iname.com)

#requires -version 2

<#
.SYNOPSIS
Outputs the MD5 or SHA1 hash for one or more files.

.DESCRIPTION
Outputs the MD5 or SHA1 hash for one or more files.

.PARAMETER Path
Specifies the path to one or more files. Wildcards are permitted.

.PARAMETER LiteralPath
Specifies a path to the file. Unlike Path, the value of LiteralPath is used exactly as it is typed. No
characters are interpreted as wildcards. If the path includes escape characters, enclose it in single
quotation marks.

.PARAMETER HashType
The hash type to compute; either MD5 or SHA1. The default is MD5.

.INPUTS
System.String, System.IO.FileInfo

.OUTPUTS
PSObjects containing the file paths and hash values

.EXAMPLE
PS C:\> Get-FileHash C:\Windows\Notepad.exe
Outputs the MD5 hash for the specified file.

.EXAMPLE
PS C:\> Get-FileHash C:\Windows\Explorer.exe,C:\Windows\Notepad.exe -HashType SHA1
Outputs the SHA1 hash for the specified files.

.EXAMPLE
PS C:\> Get-ChildItem C:\Scripts\*.ps1 | Get-FileHash
Outputs the MD5 hash for the specified files.

.EXAMPLE
PS C:\> Get-FileHash Download1.exe,Download2.exe -HashType SHA1
Outputs the SHA1 hash for two files. You can compare the hash values to determine if the files are
identical.
#>

[CmdletBinding(DefaultParameterSetName="Path")]
param(
  [Parameter(ParameterSetName="Path",Position=0,Mandatory=$TRUE,ValueFromPipeline=$TRUE)]
    [String[]] $Path,
  [Parameter(ParameterSetName="LiteralPath",Position=0,Mandatory=$TRUE)]
    [String[]] $LiteralPath,
  [Parameter(Position=1)]
    [String] $HashType="MD5"
)

begin {
  switch ($HashType) {
    "MD5" {
      $Provider = new-object System.Security.Cryptography.MD5CryptoServiceProvider
      break
    }
    "SHA1" {
      $Provider = new-object System.Security.Cryptography.SHA1CryptoServiceProvider
      break
    }
    default {
      throw «HashType must be one of the following: MD5 SHA1»
    }
  }

  # If the Path parameter is not bound, assume input comes from the pipeline.
  if ($PSCMDLET.ParameterSetName -eq "Path") {
    $PIPELINEINPUT = -not $PSBOUNDPARAMETERS.ContainsKey("Path")
  }

  # Returns an object containing the file's path and its hash as a hexadecimal string.
  # The Provider object must have a ComputeHash method that returns an array of bytes.
  function get-filehash2($file) {
    if ($file -isnot [System.IO.FileInfo]) {
      write-error "'$($file)' is not a file."
      return
    }
    $hashstring = new-object System.Text.StringBuilder
    $stream = $file.OpenRead()
    if ($stream) {
      foreach ($byte in $Provider.ComputeHash($stream)) {
        [Void] $hashstring.Append($byte.ToString("X2"))
      }
      $stream.Close()
    }
    "" | select-object @{Name="Path"; Expression={$file.FullName}},
      @{Name="$($Provider.GetType().BaseType.Name) Hash"; Expression={$hashstring.ToString()}}
  }
}

process {
  if ($PSCMDLET.ParameterSetName -eq "Path") {
    if ($PIPELINEINPUT) {
      get-filehash2 $_
    }
    else {
      get-item $Path -force | foreach-object {
        get-filehash2 $_
      }
    }
  }
  else {
    $file = get-item -literalpath $LiteralPath
    if ($file) {
      get-filehash2 $file
    }
  }
} 

Листинг 2. Атрибут CmdletBinding и инструкция param

[CmdletBinding (DefaultParametersetName="Path")]
param (
   [Parameter (ParametersetName="Path", Position=0, Mandatory=$tRue,
      ValueFromPipeline=$tRue)]
      [String[]] $Path,
   [Parameter (ParametersetName="literalPath", Position=0, Mandatory=$tRue)]
      [String[]] $literalPath,
   [Parameter (Position=1)]
      [String] $Hashtype="MD5"
)

Листинг 3. Функция get-filehash2

# Возвращает объект, содержащий путь к файлу, а также его хэш в виде шестнадцатеричной строки.
# Объект Provider должен иметь метод ComputeHash, возвращающий массив байтов.
function get-filehash2 ($file) {
   if ($file -isnot [system.iO.Fileinfo]) {
      write-error "'$($file)' is not a file".
      return
   }
   $hashstring = new-object system. text.stringBuilder
   $stream = $file.OpenRead ()
   if ($stream) {
      foreach ($byte in $Provider. ComputeHash ($stream)) {
         [Void] $hashstring. append ($byte.tostring ("X2"))
      }
      $stream.Close ()
   }
   "" | select-object @{Name="Path"; expression={$file.FullName}},
      @{Name="$($Provider.Gettype ().Basetype.Name) Hash";
         Expression={$hashstring.tostring ()}}
}

Билл Стюарт (bill.stewart@frenchmortuary.com) — системный и сетевой администратор компании French Mortuary, Нью-Мехико