Fast Registry Searcher in Powershell

Cole

I'm trying to incorporate this search-registry script that I found on Github into one of my scripts.

https://github.com/KurtDeGreeff/PlayPowershell/blob/master/Search-Registry.ps1

To test it, I used one of the examples that it provided:

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

After executing, I was still prompted to enter a StartKey and a Pattern into the console. After providing that information once again, the command fails.

cmdlet Search-Registry.ps1 at command pipeline position 1
Supply values for the following parameters:
StartKey: HKLM
Pattern: $ENV:USERNAME -MatchData
You must specify at least one of: -MatchKey -MatchValue -MatchData
At C:\Users\Cole\Desktop\Powershell\Search-Registry.ps1:93 char:5
+     throw "You must specify at least one of: -MatchKey -MatchValue -M ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (You must specif...alue -MatchData:String) [], RuntimeException
    + FullyQualifiedErrorId : You must specify at least one of: -MatchKey -MatchValue -MatchData

Is there something that I'm doing incorrectly?

Theo

I have tested that same function on GitHub too and also came across errors with it, so I decided to do a complete rewrite myself.

The function below can use either Regular Expression matches or Wildcard comparisons using the -like operator.
To search for nameless default properties, either don't specify the Pattern or RegexPattern parameters at all, or use one of them with an empty string.

The function also has a Recurse switch, so it is up to you if you want to recursively search through all subkeys or not.

Note that although the function performs fast, searching the registry can take a long time to finish..

# If you want to run this on PowerShell < 3.0 use
# New-Object -TypeName PSObject -Property @{ ... } wherever it says [PSCustomObject]@{ ... }
# and change the -version value for 'requires' to 2

#requires -version 3

function Search-Registry {
    <#
        .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 search pattern is either a regular expression or a wildcard pattern using the 'like' operator.
            (both are case-insensitive)
        .PARAMETER ComputerName
            (Required) Searches the registry on the specified computer(s). This parameter supports piped input.
        .PARAMETER Pattern
            (Optional) Searches using a wildcard pattern and the -like operator.
            Mutually exclusive with parameter 'RegexPattern'
        .PARAMETER RegexPattern
            (Optional) Searches using a regular expression pattern.
            Mutually exclusive with parameter 'Pattern'
        .PARAMETER Hive
            (Optional) The registry hive rootname.
            Can be any of 'HKEY_CLASSES_ROOT','HKEY_CURRENT_CONFIG','HKEY_CURRENT_USER','HKEY_DYN_DATA','HKEY_LOCAL_MACHINE',
                          'HKEY_PERFORMANCE_DATA','HKEY_USERS','HKCR','HKCC','HKCU','HKDD','HKLM','HKPD','HKU'
            If not specified, the hive must be part of the 'KeyPath' parameter.
        .PARAMETER KeyPath
            (Optional) Starts the search at the specified registry key. The key name contains only the subkey.
            This parameter can be prefixed with the hive name. 
            In that case, parameter 'Hive' is ignored as it is then taken from the given path.
            Examples: 
              HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall
              HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall
              Software\Microsoft\Windows\CurrentVersion\Uninstall
        .PARAMETER MaximumResults
            (Optional) Specifies the maximum number of results per computer searched. 
            A value <= 0 means will return the maximum number of possible matches (2147483647).
        .PARAMETER SearchKeyName
            (Optional) Searches for registry key names. You must specify at least one of -SearchKeyName, -SearchPropertyName, or -SearchPropertyValue.
        .PARAMETER SearchPropertyName
            (Optional) Searches for registry value names. You must specify at least one of -SearchKeyName, -SearchPropertyName, or -SearchPropertyValue.
        .PARAMETER SearchPropertyValue
            (Optional) Searches for registry value data. You must specify at least one of -SearchKeyName, -SearchPropertyName, or -SearchPropertyValue.
        .PARAMETER Recurse
            (Optional) If set, the function will recurse the search through all subkeys found.
        .OUTPUTS
            PSCustomObjects with the following properties:

              ComputerName     The computer name where the search was executed
              Hive             The hive name used in Win32 format ("CurrentUser", "LocalMachine" etc)
              HiveName         The hive name used ("HKEY_CURRENT_USER", "HKEY_LOCAL_MACHINE" etc.)
              HiveShortName    The abbreviated hive name used ("HKCU", "HKLM" etc.)
              Path             The full registry path ("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall")
              SubKey           The subkey without the hive ("Software\Microsoft\Windows\CurrentVersion\Uninstall")
              ItemType         Informational: describes the type 'RegistryKey' or 'RegistryProperty'
              DataType         The .REG formatted datatype ("REG_SZ", "REG_EXPAND_SZ", "REG_DWORD" etc.). $null for ItemType 'RegistryKey'
              ValueKind        The Win32 datatype ("String", "ExpandString", "DWord" etc.). $null for ItemType 'RegistryKey'
              PropertyName     The name of the property. $null for ItemType 'RegistryKey'
              PropertyValue    The value of the registry property. $null for ItemType 'RegistryKey'
              PropertyValueRaw The raw, unexpanded value of the registry property. $null for ItemType 'RegistryKey'

              The difference between 'PropertyValue' and 'PropertyValueRaw' is that in 'PropertyValue' Environment names are expanded
              ('%SystemRoot%' in the data gets expanded to 'C:\Windows'), whereas in 'PropertyValueRaw' the data is returned as-is.
              (Environment names return as '%SystemRoot%')

        .EXAMPLE
            Search-Registry -Hive HKLM -KeyPath SOFTWARE -Pattern $env:USERNAME -SearchPropertyValue -Recurse -Verbose

            Searches HKEY_LOCAL_MACHINE on the local computer for registry values whose data contains the current user's name.
            Searches like this can take a long time and you may see warning messages on registry keys you are not allowed to enter.
        .EXAMPLE
            Search-Registry -KeyPath 'HKEY_CURRENT_USER\Printers\Settings' -Pattern * -SearchPropertyName | Export-Csv -Path 'D:\printers.csv' -NoTypeInformation

            or

            Search-Registry -Hive HKEY_CURRENT_USER -KeyPath 'Printers\Settings' -Pattern * -SearchPropertyName | Export-Csv -Path 'D:\printers.csv' -NoTypeInformation

            Searches HKEY_CURRENT_USER (HKCU) on the local computer for printer names and outputs it as a CSV file.
        .EXAMPLE
            Search-Registry -KeyPath 'HKLM:\SOFTWARE\Classes\Installer' -Pattern LastUsedSource -SearchPropertyName -Recurse

            or

            Search-Registry -Hive HKLM -KeyPath 'SOFTWARE\Classes\Installer' -Pattern LastUsedSource -SearchPropertyName -Recurse

            Outputs the LastUsedSource registry entries on the current computer.
        .EXAMPLE
            Search-Registry -KeyPath 'HKCR\.odt' -RegexPattern '.*' -SearchKeyName -MaximumResults 10 -Verbose

            or 

            Search-Registry -Hive HKCR -KeyPath '.odt' -RegexPattern '.*' -SearchKeyName -MaximumResults 10 -Verbose

            Outputs at most ten matches if the specified 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 match everything.
        .EXAMPLE
            Get-Content Computers.txt | Search-Registry -KeyPath "HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine" -Pattern '*' -SearchPropertyName | Export-Csv -Path 'D:\powershell.csv' -NoTypeInformation

            Searches for any property name in the registry on each computer listed in the file Computers.txt starting at the specified subkey. 
            Output is sent to the specified CSV file.
        .EXAMPLE
            Search-Registry -KeyPath 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace' -SearchPropertyName -Recurse -Verbose

            Searches for the default (nameless) properties in the specified registry key.
    #>
    [CmdletBinding(DefaultParameterSetName = 'ByWildCard')]
    Param(
        [Parameter(ValueFromPipeline = $true, Mandatory = $false, Position = 0)]
        [string[]]$ComputerName = $env:COMPUTERNAME,

        [Parameter(Mandatory = $false, ParameterSetName = 'ByRegex')]
        [string]$RegexPattern,

        [Parameter(Mandatory = $false, ParameterSetName = 'ByWildCard')]
        [string]$Pattern,

        [Parameter(Mandatory = $false)]
        [ValidateSet('HKEY_CLASSES_ROOT','HKEY_CURRENT_CONFIG','HKEY_CURRENT_USER','HKEY_DYN_DATA','HKEY_LOCAL_MACHINE',
                     'HKEY_PERFORMANCE_DATA','HKEY_USERS','HKCR','HKCC','HKCU','HKDD','HKLM','HKPD','HKU')]
        [string]$Hive,

        [string]$KeyPath,
        [int32] $MaximumResults = [int32]::MaxValue,
        [switch]$SearchKeyName,
        [switch]$SearchPropertyName,
        [switch]$SearchPropertyValue,
        [switch]$Recurse
    )
    Begin {
        # detect if the function is called using the pipeline or not
        # see: https://communary.net/2015/01/12/quick-tip-determine-if-input-comes-from-the-pipeline-or-not/
        # and: https://www.petri.com/unraveling-mystery-myinvocation
        [bool]$isPipeLine = $MyInvocation.ExpectingInput

        # sanitize given parameters
        if ([string]::IsNullOrWhiteSpace($ComputerName) -or $ComputerName -eq '.') { $ComputerName = $env:COMPUTERNAME }

        # parse the give KeyPath
        if ($KeyPath -match '^(HK(?:CR|CU|LM|U|PD|CC|DD)|HKEY_[A-Z_]+)[:\\]?') {
            $Hive = $matches[1]
            # remove HKLM, HKEY_CURRENT_USER etc. from the path
            $KeyPath = $KeyPath.Split("\", 2)[1]
        }
        switch($Hive) {
            { @('HKCC', 'HKEY_CURRENT_CONFIG') -contains $_ }   { $objHive = [Microsoft.Win32.RegistryHive]::CurrentConfig;   break }
            { @('HKCR', 'HKEY_CLASSES_ROOT') -contains $_ }     { $objHive = [Microsoft.Win32.RegistryHive]::ClassesRoot;     break }
            { @('HKCU', 'HKEY_CURRENT_USER') -contains $_ }     { $objHive = [Microsoft.Win32.RegistryHive]::CurrentUser;     break }
            { @('HKDD', 'HKEY_DYN_DATA') -contains $_ }         { $objHive = [Microsoft.Win32.RegistryHive]::DynData;         break }
            { @('HKLM', 'HKEY_LOCAL_MACHINE') -contains $_ }    { $objHive = [Microsoft.Win32.RegistryHive]::LocalMachine;    break }
            { @('HKPD', 'HKEY_PERFORMANCE_DATA') -contains $_ } { $objHive = [Microsoft.Win32.RegistryHive]::PerformanceData; break }
            { @('HKU',  'HKEY_USERS') -contains $_ }            { $objHive = [Microsoft.Win32.RegistryHive]::Users;           break }
        }

        # critical: Hive could not be determined
        if (!$objHive) {
            Throw "Parameter 'Hive' not specified or could not be parsed from the 'KeyPath' parameter."
        }

        # critical: no search criteria given
        if (-not ($SearchKeyName -or $SearchPropertyName -or $SearchPropertyValue)) {
            Throw "You must specify at least one of these parameters: 'SearchKeyName', 'SearchPropertyName' or 'SearchPropertyValue'"
        }

        # no patterns given will only work for SearchPropertyName and SearchPropertyValue
        if ([string]::IsNullOrEmpty($RegexPattern) -and [string]::IsNullOrEmpty($Pattern)) {
            if ($SearchKeyName) {
                Write-Warning "Both parameters 'RegexPattern' and 'Pattern' are emtpy strings. Searching for KeyNames will not yield results."
            }
        }

        # create two variables for output purposes
        switch ($objHive.ToString()) {
            'CurrentConfig'   { $hiveShort = 'HKCC'; $hiveName = 'HKEY_CURRENT_CONFIG' }
            'ClassesRoot'     { $hiveShort = 'HKCR'; $hiveName = 'HKEY_CLASSES_ROOT' }
            'CurrentUser'     { $hiveShort = 'HKCU'; $hiveName = 'HKEY_CURRENT_USER' }
            'DynData'         { $hiveShort = 'HKDD'; $hiveName = 'HKEY_DYN_DATA' }
            'LocalMachine'    { $hiveShort = 'HKLM'; $hiveName = 'HKEY_LOCAL_MACHINE' }
            'PerformanceData' { $hiveShort = 'HKPD'; $hiveName = 'HKEY_PERFORMANCE_DATA' }
            'Users'           { $hiveShort = 'HKU' ; $hiveName = 'HKEY_USERS' }
        }

        if ($MaximumResults -le 0) { $MaximumResults = [int32]::MaxValue }
        $script:resultCount = 0
        [bool]$useRegEx = ($PSCmdlet.ParameterSetName -eq 'ByRegex')

        # -------------------------------------------------------------------------------------
        # Nested helper function to (recursively) search the registry
        # -------------------------------------------------------------------------------------
        function _RegSearch([Microsoft.Win32.RegistryKey]$objRootKey, [string]$regPath, [string]$computer) {
            try {
                if ([string]::IsNullOrWhiteSpace($regPath)) {
                    $objSubKey = $objRootKey
                }
                else {
                    $regPath = $regPath.TrimStart("\")
                    $objSubKey = $objRootKey.OpenSubKey($regPath, $false)    # $false --> ReadOnly
                }
            }
            catch {
              Write-Warning ("Error opening $($objRootKey.Name)\$regPath" + "`r`n         " + $_.Exception.Message)
              return
            }
            $subKeys = $objSubKey.GetSubKeyNames()

            # Search for Keyname
            if ($SearchKeyName) {
                foreach ($keyName in $subKeys) {
                    if ($script:resultCount -lt $MaximumResults) {
                        if ($useRegEx) { $isMatch = ($keyName -match $RegexPattern) } 
                        else { $isMatch = ($keyName -like $Pattern) }
                        if ($isMatch) {
                            # for PowerShell < 3.0 use: New-Object -TypeName PSObject -Property @{ ... }
                            [PSCustomObject]@{
                                'ComputerName'     = $computer
                                'Hive'             = $objHive.ToString()
                                'HiveName'         = $hiveName
                                'HiveShortName'    = $hiveShort
                                'Path'             = $objSubKey.Name
                                'SubKey'           = "$regPath\$keyName".TrimStart("\")
                                'ItemType'         = 'RegistryKey'
                                'DataType'         = $null
                                'ValueKind'        = $null
                                'PropertyName'     = $null
                                'PropertyValue'    = $null
                                'PropertyValueRaw' = $null
                            }
                            $script:resultCount++
                        }
                    }
                }
            }

            # search for PropertyName and/or PropertyValue
            if ($SearchPropertyName -or $SearchPropertyValue) {
                foreach ($name in $objSubKey.GetValueNames()) {
                    if ($script:resultCount -lt $MaximumResults) {
                        $data = $objSubKey.GetValue($name)
                        $raw  = $objSubKey.GetValue($name, '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)

                        if ($SearchPropertyName) {
                            if ($useRegEx) { $isMatch = ($name -match $RegexPattern) }
                            else { $isMatch = ($name -like $Pattern) }

                        }
                        else {
                            if ($useRegEx) { $isMatch = ($data -match $RegexPattern -or $raw -match $RegexPattern) } 
                            else { $isMatch = ($data -like $Pattern -or $raw -like $Pattern) }
                        }

                        if ($isMatch) {
                            $kind = $objSubKey.GetValueKind($name).ToString()
                            switch ($kind) {
                                'Binary'       { $dataType = 'REG_BINARY';    break }
                                'DWord'        { $dataType = 'REG_DWORD';     break }
                                'ExpandString' { $dataType = 'REG_EXPAND_SZ'; break }
                                'MultiString'  { $dataType = 'REG_MULTI_SZ';  break }
                                'QWord'        { $dataType = 'REG_QWORD';     break }
                                'String'       { $dataType = 'REG_SZ';        break }
                                default        { $dataType = 'REG_NONE';      break }
                            }
                            # for PowerShell < 3.0 use: New-Object -TypeName PSObject -Property @{ ... }
                            [PSCustomObject]@{
                                'ComputerName'     = $computer
                                'Hive'             = $objHive.ToString()
                                'HiveName'         = $hiveName
                                'HiveShortName'    = $hiveShort
                                'Path'             = $objSubKey.Name
                                'SubKey'           = $regPath.TrimStart("\")
                                'ItemType'         = 'RegistryProperty'
                                'DataType'         = $dataType
                                'ValueKind'        = $kind
                                'PropertyName'     = if ([string]::IsNullOrEmpty($name)) { '(Default)' } else { $name }
                                'PropertyValue'    = $data
                                'PropertyValueRaw' = $raw
                            }
                            $script:resultCount++
                        }
                    }
                }
            }

            # recurse through all subkeys
            if ($Recurse) {
                foreach ($keyName in $subKeys) {
                    if ($script:resultCount -lt $MaximumResults) {
                        $newPath = "$regPath\$keyName"
                        _RegSearch $objRootKey $newPath $computer
                    }
                }
            }

            # close opened subkey
            if (($objSubKey) -and $objSubKey.Name -ne $objRootKey.Name) { $objSubKey.Close() }
        }
    }
    Process{
       if ($isPipeLine) { $ComputerName = @($_) }
       $ComputerName | ForEach-Object {
            Write-Verbose "Searching the registry on computer '$ComputerName'.."
            try {
                $rootKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($objHive, $_)
                _RegSearch $rootKey $KeyPath $_
            }
            catch {
                Write-Error "$($_.Exception.Message)"
            }
            finally {
                if ($rootKey) { $rootKey.Close() }
            }
        }
        Write-Verbose "All Done searching the registry. Found $($script:resultCount) results."
    }
}

It returns a collection of objects with the following properties:

ComputerName     : MYMACHINE
Hive             : LocalMachine
HiveName         : HKEY_LOCAL_MACHINE
HiveShortName    : HKLM
Path             : HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{F3F5824C-AD58-4728-AF59-A1EBE3392799}
SubKey           : SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{F3F5824C-AD58-4728-AF59-A1EBE3392799}
ItemType         : RegistryProperty
DataType         : REG_SZ
ValueKind        : String
PropertyName     : (Default)
PropertyValue    : Sticky Notes Namespace Extension for Windows Desktop Search
PropertyValueRaw : Sticky Notes Namespace Extension for Windows Desktop Search

The difference between PropertyValue and PropertyValueRaw is that in PropertyValue Environment names are expanded ('%SystemRoot%' in the data gets expanded to 'C:\Windows'), whereas in PropertyValueRaw the data is returned as-is. (Environment names return as '%SystemRoot%')

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

TOP Ranking

HotTag

Archive