I\'d like to use PowerShell to find all registry keys and values within a particular hive that contain a string foo
, possibly embedded within a longer string. F
Each key has a GetValueNames()
, GetValueKind()
, and GetValue()
method that let you enumerate child values. You can also use the GetSubKeyNames()
instead of depending on Get-ChildItem -Recurse
to enumerate keys.
To answer your question about searching multiple hives: if you start with Get-ChildItem Registry::\
, you can see all hives and start your search there. You'll probably want to stick with HKLM and HKCU (maybe HKU if there are other user hives loaded).
Here's a sample implementation that I created a while back on the TechNet gallery:
function Search-Registry {
<#
.SYNOPSIS
Searches registry key names, value names, and value data (limited).
.DESCRIPTION
This function can search registry key names, value names, and value data (in a limited fashion). It outputs custom objects that contain the key and the first match type (KeyName, ValueName, or ValueData).
.EXAMPLE
Search-Registry -Path HKLM:\SYSTEM\CurrentControlSet\Services\* -SearchRegex "svchost" -ValueData
.EXAMPLE
Search-Registry -Path HKLM:\SOFTWARE\Microsoft -Recurse -ValueNameRegex "ValueName1|ValueName2" -ValueDataRegex "ValueData" -KeyNameRegex "KeyNameToFind1|KeyNameToFind2"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory, Position=0, ValueFromPipelineByPropertyName)]
[Alias("PsPath")]
# Registry path to search
[string[]] $Path,
# Specifies whether or not all subkeys should also be searched
[switch] $Recurse,
[Parameter(ParameterSetName="SingleSearchString", Mandatory)]
# A regular expression that will be checked against key names, value names, and value data (depending on the specified switches)
[string] $SearchRegex,
[Parameter(ParameterSetName="SingleSearchString")]
# When the -SearchRegex parameter is used, this switch means that key names will be tested (if none of the three switches are used, keys will be tested)
[switch] $KeyName,
[Parameter(ParameterSetName="SingleSearchString")]
# When the -SearchRegex parameter is used, this switch means that the value names will be tested (if none of the three switches are used, value names will be tested)
[switch] $ValueName,
[Parameter(ParameterSetName="SingleSearchString")]
# When the -SearchRegex parameter is used, this switch means that the value data will be tested (if none of the three switches are used, value data will be tested)
[switch] $ValueData,
[Parameter(ParameterSetName="MultipleSearchStrings")]
# Specifies a regex that will be checked against key names only
[string] $KeyNameRegex,
[Parameter(ParameterSetName="MultipleSearchStrings")]
# Specifies a regex that will be checked against value names only
[string] $ValueNameRegex,
[Parameter(ParameterSetName="MultipleSearchStrings")]
# Specifies a regex that will be checked against value data only
[string] $ValueDataRegex
)
begin {
switch ($PSCmdlet.ParameterSetName) {
SingleSearchString {
$NoSwitchesSpecified = -not ($PSBoundParameters.ContainsKey("KeyName") -or $PSBoundParameters.ContainsKey("ValueName") -or $PSBoundParameters.ContainsKey("ValueData"))
if ($KeyName -or $NoSwitchesSpecified) { $KeyNameRegex = $SearchRegex }
if ($ValueName -or $NoSwitchesSpecified) { $ValueNameRegex = $SearchRegex }
if ($ValueData -or $NoSwitchesSpecified) { $ValueDataRegex = $SearchRegex }
}
MultipleSearchStrings {
# No extra work needed
}
}
}
process {
foreach ($CurrentPath in $Path) {
Get-ChildItem $CurrentPath -Recurse:$Recurse |
ForEach-Object {
$Key = $_
if ($KeyNameRegex) {
Write-Verbose ("{0}: Checking KeyNamesRegex" -f $Key.Name)
if ($Key.PSChildName -match $KeyNameRegex) {
Write-Verbose " -> Match found!"
return [PSCustomObject] @{
Key = $Key
Reason = "KeyName"
}
}
}
if ($ValueNameRegex) {
Write-Verbose ("{0}: Checking ValueNamesRegex" -f $Key.Name)
if ($Key.GetValueNames() -match $ValueNameRegex) {
Write-Verbose " -> Match found!"
return [PSCustomObject] @{
Key = $Key
Reason = "ValueName"
}
}
}
if ($ValueDataRegex) {
Write-Verbose ("{0}: Checking ValueDataRegex" -f $Key.Name)
if (($Key.GetValueNames() | % { $Key.GetValue($_) }) -match $ValueDataRegex) {
Write-Verbose " -> Match!"
return [PSCustomObject] @{
Key = $Key
Reason = "ValueData"
}
}
}
}
}
}
}
I haven't looked at it in a while, and I can definitely see some parts of it that should be changed to make it better, but it should work as a starting point for you.
This is a replacement for get-itemproperty that dumps out the registry in a simple manner. It's easy to use with where-object. You can also pipe it to set-itemproperty.
function get-itemproperty2 {
# get-childitem skips top level key, use get-item for that
# set-alias gp2 get-itemproperty2
param([parameter(ValueFromPipeline)]$key)
process {
$key.getvaluenames() | foreach-object {
$value = $_
[pscustomobject] @{
Path = $Key -replace 'HKEY_CURRENT_USER',
'HKCU:' -replace 'HKEY_LOCAL_MACHINE','HKLM:'
Name = $Value
Value = $Key.GetValue($Value)
Type = $Key.GetValueKind($Value)
}
}
}
}
ls -r hkcu:\key1 | get-itemproperty2 | where name -eq name
Path Name Value Type
---- ---- ----- ----
HKCU:\key1\key2 name 1 DWord
ls -r hkcu:\key1 | get-itemproperty2 | where name -eq name | set-itemproperty -value 0
ls -r hkcu:\key1 | get-itemproperty2 | where name -eq name
Path Name Value Type
---- ---- ----- ----
HKCU:\key1\key2 name 0 DWord
# pipe 2 commands to one
$(get-item hkcu:\key1; ls -r hkcu:\key1 ) | get-itemproperty2
Path Name Value Type
---- ---- ----- ----
HKCU:\key1 multi {hi, there} MultiString
HKCU:\key1\key2 name 0 DWord
HKCU:\key1\key2 name2 0 String
HKCU:\key1\key2\key3 name3 {18, 52, 80} Binary
EDIT:
This where construction isn't bad for searching both property names and values (and the key name is a value). (Watch out for Netbeans. It creates an invalid registry dword key that causes an exception in get-itemproperty.)
get-childitem -recurse HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
get-itemproperty | where { $_ -match 'Office16.PROPLUS' }