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

Все, кому доводилось хоть немного заниматься управлением системами Windows, знают, что такое реестр. Первоначально реестр был включен в операционную систему как средство для хранения сведений о типах файлов. Но с выходом в свет версий Windows NT 3.1 и Windows 95 он превратился в стандартное хранилище, в которое операционная система и приложения могут быстро помещать (и быстро извлекать из него) данные конфигурации. Разработчики Windows 95 включили в систему программу regedit, стандартное средство для редактирования реестра на базе графического интерфейса, а в системе Windows NT был реализован другой графический редактор реестра — regedt32; он обеспечивал возможность работы с большим, чем regedit, числом типов реестра. В системах Windows NT 4.0 и Windows 2000 были реализованы оба редактора. В версии Windows XP разработчики Microsoft обновили программу regedit, которая стала совместимой с теми же типами данных, что и редактор regedt32. В результате последний вышел из употребления.

Regedit — чрезвычайно полезный инструмент, но средства поиска данных не относятся к числу самых выдающихся его возможностей. Функция поиска, показанная на экране 1, обеспечивает решение лишь самых необходимых задач. Пользователь нажимает комбинацию клавиш Ctrl+F, вводит текстовую строку, выставляет один или несколько из четырех предлагаемых флажков и нажимает кнопку Find Next. Нажимать клавишу F3 для повтора поиска быстро надоедает, если искомый элемент часто встречается в тексте.

 

Диалоговое окно поиска редактора regedit
Экран 1. Диалоговое окно поиска редактора regedit

Я решил расширить возможности поиска и написал сценарий Windows PowerShell, который назвал Search-Registry.ps1 (см. листинг 1). Он дает возможность осуществлять поиск данных в реестре с большей гибкостью, чем программа regedit. Сценарий Search-Registry.ps1 превосходит функцию поиска редактора regedit во многих отношениях:

— сценарий выполняет процедуры поиска с использованием регулярных выражений;

— он может осуществлять поиск на удаленных системах;

— он способен ограничивать число возвращаемых результатов поиска;

— на выходе сценария генерируются объекты, которые можно отфильтровывать, сортировать, экспортировать в файлы значений с разделителями-запятыми (файлы CSV) и т.д.

Многие читатели, хорошо знакомые со средой PowerShell, обратят внимание на то, что для организации доступа к реестру в PowerShell используются «ветви» реестра (например, HKLM). Однако важно отметить, что эти ветви функционируют только на локальном компьютере, но не на удаленных системах. Я рассматривал возможность дистанционной работы со средой PowerShell. Но для организации дистанционного взаимодействия необходимо, чтобы на всех удаленных системах был установлен и активирован пакет PowerShell 2.0, а это условие выполняется далеко не всегда.

Первое знакомство с файлом Search-Registry.ps1

В командной строке сценарий Search-Registry.ps1 использует следующий синтаксис:

Search-Registry [-StartKey]  [-Pattern]  [-MatchKey] [-MatchValue]
[-MatchData] [-MaximumMatches ] [-ComputerName ]

Параметр -StartKey указывает расположение раздела реестра, с которого следует начинать процедуру поиска. Указание этого параметра эквивалентно выбору ветви на левой панели редактора regedit, предшествующему нажатию комбинации клавиш Ctrl+F. В строке -StartKey используется формат

subtree:\key

где subtree — это либо сокращенное имя ветви в PowerShell, либо полное имя ветви, как оно представлено в regedit:

— HKCR или HKEY_CLASSES_ROOT
— HKCU или HKEY_CURRENT_USER
— HKLM или HKEY_LOCAL_MACHINE
— HKU или HKEY_USERS

Двоеточие (:) после имени ветви реестра можно опустить. Раздел задает каталог, с которого начинается поиск. Если вы опустите имя раздела или введете символ обратной косой черты (\), сценарий проведет поиск по всему пространству ветви реестра. Ниже приводятся три строки, которые при использовании с параметром -StartKey дают один и тот же результат:

HKLM\SOFTWARE
HKLM:\SOFTWARE
HKEY_LOCAL_MACHINE\SOFTWARE

По замыслу разработчиков параметр -StartKey при перечислении должен занимать первую позицию, так что если в командной строке аргумент этого параметра указывается первым, имя параметра можно опустить. Если же строка аргументов параметра -StartKey содержит пробелы, эту строку следует заключать в кавычки («).

Параметр -Pattern указывает шаблон регулярного выражения, которое вы хотите отыскать. Этот параметр должен указываться вторым, так что его имя можно опустить, если его аргумент в командной строке занимает второе место. Если строка аргумента параметра -Pattern содержит пробелы, используйте кавычки, как показано выше. Шаблон регулярного выражения нечувствителен к регистру символов. Для получения более подробной информации о создании шаблона регулярного выражения введите в окне командной строки PowerShell следующую команду:

help about_Regular_Expressions

Параметры -MatchKey, -MatchValue и -MatchData указывают на соответствия, которые должен отыскивать сценарий. Эти параметры соответствуют флажкам Keys, Values и Data в диалоговом окне Find редактора regedit, как показано на экране 1. Экран 2 иллюстрирует соответствие этих элементов элементам реестра. Я решил применять эти же имена и не переходить на новые (такие, как path, property, value), используемые средой PowerShell в провайдере реестра; мне нужно было, чтобы в сценарии применялись те же термины, что и в диалоговом окне Find утилиты regedit. Пользователь должен указать по меньшей мере один из трех названных параметров, но можно задавать и большее их число. Ключ -MatchKey выявляет соответствия в именах подразделов, ключ -MatchValue — в именах параметров реестра, а ключ -MatchData — в данных значений.

 

Соответствие элементов сценария элементам реестра
Экран 2. Соответствие элементов сценария элементам реестра

Ключ -MaximumMatches задает максимальное число результатов на один просканированный компьютер. По умолчанию его значение задается равным нулю, и в результате система возвращает максимальное число возможных соответствий. Данный параметр полезно задействовать при выполнении операций поиска в реестрах удаленных систем, это позволяет сводить к минимуму объем сетевого трафика.

Параметр -ComputerName предписывает выполнить поиск в реестре указанного компьютера или списка компьютеров. Можно задать имя одной системы или целый массив имен. Этот параметр предусматривает возможность конвейерного ввода данных. По умолчанию поиск выполняется в реестре локального компьютера.

Выходные данные сценария Search-Registry.ps1 представляют собой объекты, содержащие свойства, которые перечислены в таблице. На экране 3 представлен образец команды сценария Search-Registry.ps1 в окне консоли PowerShell. Сценарий, показанный на экране 3, направляет свои выходные данные команде Select-Object, которая отбирает только свойства Key, Value и Data (определять свойство ComputerName не требуется, поскольку операция поиска выполняется в реестре локальной системы). Далее Select-Object пересылает свои выходные данные команде Format-List. Список содержит только два совпадения в соответствии с заданным параметром -MaximumMatches.

 

Свойства выходных объектов сценария Search-Registry.ps1

 

Выполнение сценария Search-Registry.ps1
Экран 3. Выполнение сценария Search-Registry.ps1

В некоторых операционных системах отдельные каталоги реестра могут быть недоступными (к примеру, по причине недостаточности разрешений). Если сценарий Search-Registry.ps1 попытается обратиться к таким каталогам реестра, система выдаст сообщение об ошибке. Чтобы эти ошибки игнорировались, задайте в команде сценария ключ -Error-Action SilentlyContinue.

Как действует сценарий?

Сценарий Search-Registry.ps1 допускает возможность конвейерного ввода данных, поэтому в нем используются блоки сценария begin и process. Блок begin содержит код инициализации сценария: декларации глобальных переменных, средства проверки параметров и определения функций. Блок сценария process последовательно перебирает все имена компьютеров, которые передаются сценарию, и передает очередное имя компьютера функции search-registry2. Последняя с помощью статического метода OpenRemoteBaseKey класса. NET Microsoft.Win32.RegistryKey открывает ветвь реестра, запрошенную в командной строке, и затем передает имя каждого компьютера функции search-registrykey. Функция search-registrykey выполняет рекурсивный поиск шаблона регулярного выражения в разделе реестра (то есть она начинает поиск в указанном разделе и захватывает все подразделы данного раздела).

Примеры команд

Рассмотрим несколько практических примеров, показывающих, как можно использовать сценарий Search-Registry.ps1. Не забывайте, что хотя на странице код может занимать несколько строк (это продиктовано размерами колонок текста), вам следует вводить его в окне PowerShell одной строкой.

Search-Registry -StartKey HKCR -Pattern "Word\.Document\.\d» -MatchKey

Эта команда сканирует реестр локального компьютера, начиная с ветви HKEY_ CLASSES_ROOT, в поисках шаблона -Word\.Document\.\d?. В регулярных выражениях точка (.) соответствует любому символу, обратная косая черта означает «следующий символ нужно воспринимать буквально». Таким образом, -\? означает -?. Обратная косая черта, стоящая перед буквой d (\d), означает «любое десятичное число»; таким образом, данная операция поиска выявит подразделы реестра с именем Word.Document.n (где n — некое число). Если данная команда возвратит совпадение, это, по-видимому, будет означать, что в локальной системе имеется приложение, способное открывать документы Microsoft Word.

Search-Registry -StartKey HKLM -Pattern $ENV: USERNAME -MatchData

Эта команда осуществляет в ветви HKEY_ LOCAL_MACHINE реестра локального компьютера поиск любых данных реестра, содержащих имя текущего пользователя.

Search-Registry -StartKey HKLM\
SOFTWARE\Microsoft\Windows\
CurrentVersion\Policies\System
-Pattern EnableLinkedConnections
-MatchValue

Данная команда определяет, задано ли на локальном компьютере значение реестра EnableLinkedConnections. Узнать, для каких целей может понадобиться это значение, вы можете, ознакомившись с подготовленной специалистами Microsoft статьей -Programs may be unable to access some network locations after you turn on User Account Control in Windows Vista or in Windows 7, по адресу support.microsoft.com/kb/937624.

Get-Content Computers.txt|
Search-Registry HKLM\SOFTWARE\
Microsoft\Windows\CurrentVersion\
Policies\System -Pattern
EnableLinkedConnections -MatchValue | Export-CSV C:\Reports\
EnableLinkedConnections.csv
-NoTypeInformation

Эта команда аналогична предыдущей и отличается от нее только тем, что сценарий Search-Registry.ps1 ищет упомянутое значение реестра на компьютерах, перечисленных в файле Computers.txt, и создает отчет в формате CSV.

Поиск в реестре без лишних хлопот

.

Листинг 1. Сценарий Search-Registry.ps1

# Search-Registry.ps1
# Written by Bill Stewart (bstewart@iname.com)
#requires -version 2
<#
. SYNOPSIS
Searches the registry on one or more computers for a specified text pattern.
. DESCRIPTION
Searches the registry on one or more computers for a specified text pattern. Supports searching for any combination of key names, value names, and/or value data. The text pattern is a case-insensitive regular expression.
. PARAMETER StartKey
Starts searching at the specified key. The key name uses the following format:
subtree[:][\[keyname[\keyname...]]]
subtree can be any of the following:
HKCR or HKEY_CLASSES_ROOT
HKCU or HKEY_CURRENT_USER
HKLM or HKEY_LOCAL_MACHINE
HKU or HKEY_USERS
This parameter's format is compatible with PowerShell registry drive (e.g., HKLM:\SOFTWARE), reg.exe (e.g., HKLM\SOFTWARE), and regedit.exe (e.g., HKEY_LOCAL_MACHINE\SOFTWARE).
. PARAMETER Pattern
Searches for the specified regular expression pattern. The pattern is not case-sensitive. See help topic about_Regular_Expressions for more information.
. PARAMETER MatchKey
Matches registry key names. You must specify at least one of -MatchKey, -MatchValue, or -MatchData.
. PARAMETER MatchValue
Matches registry value names. You must specify at least one of -MatchKey, -MatchValue, or -MatchData.
. PARAMETER MatchData
Matches registry value data. You must specify at least one of -MatchKey, -MatchValue, or -MatchData.
. PARAMETER MaximumMatches
Specifies the maximum number of results per computer searched. 0 means «return the maximum number of possible matches.» The default is 0. This parameter is useful when searching the registry on remote computers in order to minimize unnecessary network traffic.
. PARAMETER ComputerName
Searches the registry on the specified computer. This parameter supports piped input.
. OUTPUTS
PSObjects with the following properties:
ComputerName The computer name on which the match occurred
Key The key name (e.g., HKLM:\SOFTWARE)
Value The registry value (empty for the default value)
Data The registry value's data
. EXAMPLE
PS C:\> Search-Registry -StartKey HKLM -Pattern $ENV:USERNAME -MatchData
Searches HKEY_LOCAL_MACHINE (i.e., HKLM) on the current computer for registry values whose data contains the current user's name.
. EXAMPLE
PS C:\> Search-Registry -StartKey HKLM:\SOFTWARE\Classes\Installer -Pattern LastUsedSource -MatchValue | Select-Object Key,Value,Data | Format-List
Outputs the LastUsedSource registry entries on the current computer.
. EXAMPLE
PS C:\> Search-Registry -StartKey HKCR\.odt -Pattern. * -MatchKey -MaximumMatches 1
Outputs at least one match if the specified reistry key exists. This command returns a result if the current computer has a program registered to open files with the. odt extension. The pattern. * means 0 or more of any character (i.e., match everything).
. EXAMPLE
PS C:\> Get-Content Computers.txt | Search-Registry -StartKey «HKLM:\SOFTWARE\My Application\Installed» -Pattern «Installation Complete» -MatchValue -MaximumMatches 1 | Export-CSV C:\Reports\MyReport.csv -NoTypeInformation
Searches for the specified value name pattern in the registry on each computer listed in the file Computers.txt starting at the specified subkey. Output is sent to the specifed CSV file.
#>
[CmdletBinding()]
param(
[parameter(Position=0,Mandatory=$TRUE)]
[String] $StartKey,
[parameter(Position=1,Mandatory=$TRUE)]
[String] $Pattern,
[Switch] $MatchKey,
[Switch] $MatchValue,
[Switch] $MatchData,
[UInt32] $MaximumMatches=0,
[parameter(ValueFromPipeline=$TRUE)]
[String[]] $ComputerName=$ENV:COMPUTERNAME
)
begin {
$PIPELINEINPUT = (-not $PSBOUNDPARAMETERS.ContainsKey(«ComputerName»)) -and
(-not $ComputerName)
# Throw an error if -Pattern is not valid
try {
«" -match $Pattern | out-null
}
catch [System.Management.Automation.RuntimeException] {
throw "-Pattern parameter not valid — $($_.Exception.Message)»
}
# You must specify at least one matching criteria
if (-not ($MatchKey -or $MatchValue -or $MatchData)) {
throw «You must specify at least one of: -MatchKey -MatchValue -MatchData»
}
# Interpret zero as «maximum possible number of matches»
if ($MaximumMatches -eq 0) { $MaximumMatches = [UInt32]::MaxValue }
# These two hash tables speed up lookup of key names and hive types
$HiveNameToHive = @{
«HKCR» = [Microsoft.Win32.RegistryHive] «ClassesRoot»;
«HKEY_CLASSES_ROOT» = [Microsoft.Win32.RegistryHive] «ClassesRoot»;
«HKCU» = [Microsoft.Win32.RegistryHive] «CurrentUser»;
«HKEY_CURRENT_USER» = [Microsoft.Win32.RegistryHive] «CurrentUser»;
«HKLM» = [Microsoft.Win32.RegistryHive] «LocalMachine»;
«HKEY_LOCAL_MACHINE» = [Microsoft.Win32.RegistryHive] «LocalMachine»;
«HKU» = [Microsoft.Win32.RegistryHive] «Users»;
«HKEY_USERS» = [Microsoft.Win32.RegistryHive] «Users»;
}
$HiveToHiveName = @{
[Microsoft.Win32.RegistryHive] «ClassesRoot» = «HKCR»;
[Microsoft.Win32.RegistryHive] «CurrentUser» = «HKCU»;
[Microsoft.Win32.RegistryHive] «LocalMachine» = «HKLM»;
[Microsoft.Win32.RegistryHive] «Users» = «HKU»;
}
# Search for 'hive:\startkey'; ':' and starting key optional
$StartKey | select-string «([^:\\]+):?\\?(.+)?» | foreach-object {
$HiveName = $_.Matches[0].Groups[1].Value
$StartPath = $_.Matches[0].Groups[2].Value
}
if (-not $HiveNameToHive.ContainsKey($HiveName)) {
throw «Invalid registry path»
} else {
$Hive = $HiveNameToHive[$HiveName]
$HiveName = $HiveToHiveName[$Hive]
}
# Recursive function that searches the registry
function search-registrykey($computerName, $rootKey, $keyPath, [Ref] $matchCount) {
# Write error and return if unable to open the key path as read-only
try {
$subKey = $rootKey.OpenSubKey($keyPath, $FALSE)
}
catch [System.Management.Automation.MethodInvocationException] {
$message = $_.Exception.Message
write-error «$message — $HiveName\$keyPath»
return
}
# Write error and return if the key doesn't exist
if (-not $subKey) {
write-error «Key does not exist: $HiveName\$keyPath» -category ObjectNotFound
return
}
# Search for value and/or data; -MatchValue also returns the data
if ($MatchValue -or $MatchData) {
if ($matchCount.Value -lt $MaximumMatches) {
foreach ($valueName in $subKey.GetValueNames()) {
$valueData = $subKey.GetValue($valueName)
if (($MatchValue -and ($valueName -match $Pattern)) -or ($MatchData -and ($valueData -match $Pattern))) {
«" | select-object `
@{N="ComputerName»; E={$computerName}},
@{N="Key"; E={"$HiveName\$keyPath"}},
@{N="Value"; E={$valueName}},
@{N="Data"; E={$valueData}}
$matchCount.Value++
}
if ($matchCount.Value -eq $MaximumMatches) { break }
}
}
}
# Iterate and recurse through subkeys; if -MatchKey requested, output
# objects only report computer and key (keys do not have values or data)
if ($matchCount.Value -lt $MaximumMatches) {
foreach ($keyName in $subKey.GetSubKeyNames()) {
if ($keyPath -eq «") {
$subkeyPath = $keyName
} else {
$subkeyPath = $keyPath + "\» + $keyName
}
if ($MatchKey -and ($keyName -match $Pattern)) {
«" | select-object `
@{N="ComputerName»; E={$computerName}},
@{N="Key"; E={"$HiveName\$subkeyPath"}},
@{N="Value"; E={}},
@{N="Data"; E={}}
$matchCount.Value++
}
# $matchCount is a reference
search-registrykey $computerName $rootKey $subkeyPath $matchCount
if ($matchCount.Value -eq $MaximumMatches) { break }
}
}
# Close opened subkey
$subKey.Close()
}
# Core function opens the registry on a computer and initiates searching
function search-registry2($computerName) {
# Write error and return if unable to open the key on the computer
try {
$rootKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive,
$computerName)
}
catch [System.Management.Automation.MethodInvocationException] {
$message = $_.Exception.Message
write-error «$message — $computerName»
return
}
# $matchCount is per computer; pass to recursive function as reference
$matchCount = 0
search-registrykey $computerName $rootKey $StartPath ([Ref] $matchCount)
$rootKey.Close()
}
}
process {
if ($PIPELINEINPUT) {
search-registry2 $_
}
else {
$ComputerName | foreach-object {
search-registry2 $_
}
}
}