Reputation: 2178
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. Finding the keys is not hard:
Get-ChildItem -path hkcu:\ -recurse -ErrorAction SilentlyContinue | Where-Object {$_.Name -like "*foo*"}
The problem is that I don't know the best way to find the values, given that I don't know the names of the properties ahead of time. I tried this:
Get-ChildItem -path hkcu:\ -recurse -erroraction silentlycontinue | get-itemproperty | where {$_.'(default)' -like "*foo*"}
But I got this error:
get-itemproperty : Specified cast is not valid.
At line:1 char:69
+ ... u:\ -recurse -erroraction silentlycontinue | get-itemproperty | where ...
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-ItemProperty], InvalidCastException
+ FullyQualifiedErrorId : System.InvalidCastException,Microsoft.PowerShell.Commands.GetItemPropertyCommand
even when I added -ErrorAction SilentlyContinue
to the get-itemproperty
.
Furthermore, that only finds the values of the (default)
keys.
Also, is it possible to search all hives within a single command?
Upvotes: 14
Views: 110122
Reputation: 81
This might work
# Define the word to search for
$searchTerm = "YourSearchWord"
# Function to search registry keys
function Search-RegistryKey {
param(
[string]$keyPath
)
try {
# Get all subkeys
$subKeys = Get-ChildItem -Path $keyPath -ErrorAction Stop
foreach ($subKey in $subKeys) {
# Recursively search in subkeys
Search-RegistryKey -keyPath $subKey.PSPath
}
} catch {
# Ignore inaccessible keys
}
try {
# Get all values in the current key
$values = Get-ItemProperty -Path $keyPath -ErrorAction Stop
foreach ($value in $values.PSObject.Properties) {
if ($value.Value -like "*$searchTerm*") {
Write-Host "Found match in $keyPath: $($value.Name) = $($value.Value)"
}
}
} catch {
# Ignore inaccessible values
}
}
# Define the root registry paths to search in
$rootKeys = @("HKLM:\", "HKCU:\")
foreach ($rootKey in $rootKeys) {
Search-RegistryKey -keyPath $rootKey
}
Write-Host "Search completed."
OR
for a more simpler version try this
Get-ChildItem -Path HKLM:\,HKCU:\ -Recurse | ForEach-Object { Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue } | Where-Object { $_.PSObject.Properties.Value -like "*<searchterm>*" } | ForEach-Object { Write-Host "$($_.PSPath): $($_.PSObject.Properties.Name) = $($_.PSObject.Properties.Value)" }
This command searches the HKEY_LOCAL_MACHINE (HKLM) and HKEY_CURRENT_USER (HKCU) registry hives for any values that contain the specified search term.
Upvotes: 1
Reputation: 11
(Get-ItemProperty "HKLM:\Software\*\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue | Where-Object {$_ -like "*Office*"}).PSChildName
I've used something similar to this to do the trick. Never really had an issue finding an unknown GUID.
Upvotes: 1
Reputation: 143
I created a code script for registry issues. Use RegRootReplace -Key $Key -ResultType 'registry'
in order to not having any issue for key-not-found problems.
<#
# "REGISTRY::HKEY_CLASSES_ROOT" = "HKEY_CLASSES_ROOT" = "HKCR:"
# "REGISTRY::HKEY_CURRENT_CONFIG" = "HKEY_CURRENT_CONFIG" = "HKCC:"
# "REGISTRY::HKEY_USERS" = "HKEY_USERS" = "HKU:"
# "REGISTRY::HKEY_CURRENT_USER" = "HKEY_CURRENT_USER" = "HKCU:"
# "REGISTRY::HKEY_LOCAL_MACHINE" = "HKEY_LOCAL_MACHINE" = "HKLM:"
#>
function RegRootReplace {
<#
.EXAMPLE
RegRootReplace -Key $Key -ResultType $ResultType # $ResultType can be 'small', 'long' or 'registry'.
#>
param (
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
$Key,
[Parameter(Mandatory = $false)]
[string[]]
$ResultType = 'small'
)
$Key = $Key -replace 'REGISTRY::', ''
switch ($ResultType) {
'small' {
$Key = $Key -replace 'HKEY_CLASSES_ROOT', 'HKCR:'
$Key = $Key -replace 'HKEY_CURRENT_CONFIG', 'HKCC:'
$Key = $Key -replace 'HKEY_USERS', 'HKU:'
$Key = $Key -replace 'HKEY_CURRENT_USER', 'HKCU:'
$Key = $Key -replace 'HKEY_LOCAL_MACHINE', 'HKLM:'
return $Key
}
'long' {
$Key = $Key -replace 'HKCR:', 'HKEY_CLASSES_ROOT'
$Key = $Key -replace 'HKCC:', 'HKEY_CURRENT_CONFIG'
$Key = $Key -replace 'HKU:', 'HKEY_USERS'
$Key = $Key -replace 'HKCU:', 'HKEY_CURRENT_USER'
$Key = $Key -replace 'HKLM:', 'HKEY_LOCAL_MACHINE'
return $Key
}
'registry' {
$Key = 'REGISTRY::' + $Key
$Key = $Key -replace 'HKCR:', 'HKEY_CLASSES_ROOT'
$Key = $Key -replace 'HKCC:', 'HKEY_CURRENT_CONFIG'
$Key = $Key -replace 'HKU:', 'HKEY_USERS'
$Key = $Key -replace 'HKCU:', 'HKEY_CURRENT_USER'
$Key = $Key -replace 'HKLM:', 'HKEY_LOCAL_MACHINE'
return $Key
}
Default {
$Key = $Key -replace 'HKEY_CLASSES_ROOT', 'HKCR:'
$Key = $Key -replace 'HKEY_CURRENT_CONFIG', 'HKCC:'
$Key = $Key -replace 'HKEY_USERS', 'HKU:'
$Key = $Key -replace 'HKEY_CURRENT_USER', 'HKCU:'
$Key = $Key -replace 'HKEY_LOCAL_MACHINE', 'HKLM:'
return $Key
}
}
}
Update: I found get-itemproperty2
code at here with a name Get-RegistryItemProperty
, and I just modified it with my RegRootReplace
for possible powershell path issues. The result is this:
<#
Get-ItemProperty2 -> https://stackoverflow.com/questions/42963661
Get-RegistryItemProperty -> https://carlbarrett.uk/use-powershell-to-search-for-and-delete-registry-values
#>
function Get-RegistryItemProperty {
param(
[Parameter(ValueFromPipeline)]
$Key
)
process {
$Key.GetValueNames() | ForEach-Object {
$Value = $_
[pscustomobject] @{
Path = $(RegRootReplace -Key $Key -ResultType 'registry')
Name = $Value
Value = $Key.GetValue($Value)
Type = $Key.GetValueKind($Value)
}
}
}
}
I also created a function to search registry for a search key.
function Search-Registry-KeyValueData {
<#
.EXAMPLE
Search-Registry-KeyValueData $SearchKey
#>
param(
[Parameter(ValueFromPipeline)]
[string[]]
$SearchKey
)
$SearchKey = $SearchKey.Replace(' ', '(([\s,\.])?)+')
$RegHivesStr = @('HKCR:', 'HKCC:', 'HKU:', 'HKCU:', 'HKLM:')
$RegHives = $($RegHivesStr | ForEach-Object { Get-ChildItem -Path $(RegRootReplace -Key $_ -ResultType 'registry') -Recurse -ErrorAction 'SilentlyContinue' })
$RegHives | ForEach-Object {
$_ | Get-RegistryItemProperty | Where-Object { $_.'Path' -match $SearchKey -or $_.'Name' -match $SearchKey -or $_.'Value' -match $SearchKey }
}
}
Update: However, I still have an issue when I run my Search-Registry-KeyValueData
code:
Get-RegistryItemProperty : Exception calling "GetValueNames" with "0" argument(s): "Silmek üzere imlenmiş bir kayıt defteri anahtarına geçersiz işlem yapılmak istendi."
My system is in Turkish language. It says that "An illegal operation was attempted on a registry key marked for deletion.". I need help for this issue.
Upvotes: 1
Reputation: 27418
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' }
Upvotes: 4
Reputation: 2659
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.
Upvotes: 17