-
Notifications
You must be signed in to change notification settings - Fork 7
/
Search-Registry.ps1
243 lines (213 loc) · 9.46 KB
/
Search-Registry.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# Search-Registry.ps1
# Written by Bill Stewart ([email protected])
#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(Mandatory=$FALSE)] [Switch] $ExactMatch,
[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 ($ExactMatch) {
if (($MatchValue -and ($valueName -contains $Pattern)) -or ($MatchData -and ($valueData -contains $Pattern))) {
"" | select-object `
@{N="ComputerName"; E={$computerName}},
@{N="Key"; E={"$HiveName\$keyPath"}},
@{N="Value"; E={$valueName}},
@{N="Data"; E={$valueData}}
$matchCount.Value++
}
} else {
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 ($ExactMatch) {
if ($MatchKey -and ($keyName -contains $Pattern)) {
"" | select-object `
@{N="ComputerName"; E={$computerName}},
@{N="Key"; E={"$HiveName\$subkeyPath"}},
@{N="Value"; E={}},
@{N="Data"; E={}}
$matchCount.Value++
}
} else {
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 $_
}
}
}