
close
close
In the first few articles of this series, I guided you through several different techniques for identifying installed applications. Remember, I’m only talking about applications that have been installed via an installation package. Stand alone or portable applications are much more difficult to identify. In the previous article, I demonstrated how to query the registry. I ended by showing you how to query a registry path under HKEY_CURRENT_USER.
The challenge is that when you search that registry hive, you are doing it for your current credentials. So if you try the following command, then the search will be for whatever credentials you connect with. This is probably not what you really want to accomplish.
Invoke-Command -scriptblock {
dir HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall -pv p |
get-ItemProperty |Select @{Name="Path";Expression={$p.name}},Displayname,DisplayVersion,
Publisher,InstallDate,InstallLocation,Comments,UninstallString
} -ComputerName DESK01
Today I have a few options using WMI. As a benefit, you can easily query remote computers, although you will need to make sure the RemoteRegistry service is running. Allow me to introduce you to something called StdRegProv.
This is WMI provider that coordinates access to the registry. It is a bit cumbersome and very much a developer-oriented topic, but we can still use it. First, I’m going to create a variable to represent the HKEY_USERS hive.
$HKEY_USERS=2147483651
Each registry hive is a separate numeric value, but let’s stick to this for now. Next is a variable for the computer to be queried.
$computername = $env:computername
I’m going to test with the local host. The tricky part with the registry provider is that it isn’t something with instances. Instead you need to use the WMI class directly, which you can do by using the [WMIClass] type accelerator.
[WMIClass]$Reg = "\\$computername\root\default:StdRegProv"
To make my code more flexible, I’m going to define a new hive variable.
$HIVE = $HKEY_USERS
Next, I need to enumerate the hive, which should show me the SIDS for all of the currently logged on user accounts.
$reg.EnumKey($HIVE,"")
Enumerating the hive. A return result of zero indicates a success. (Image Credit: Jeff Hicks)
$reg.EnumKey($HIVE,"").snames
Obtaining a list of names in the registry. (Image Credit: Jeff Hicks)
$reg.EnumKey($HIVE,"").snames | foreach {
$regpath = "$_\Software\Microsoft\Windows\CurrentVersion\Uninstall"
$reg.EnumKey($HIVE,$regpath) }
Enumerating each hive. (Image Credit: Jeff Hicks)
Querying for values for the sNames property. (Image Credit: Jeff Hicks)
$reg.EnumKey($HIVE,"").snames | foreach {
$sid = $_
write-host "Processing $sid" -ForegroundColor cyan
$regpath = "$sid\Software\Microsoft\Windows\CurrentVersion\Uninstall"
$r = $reg.EnumKey($HIVE,$regpath)
if ($r.snames ) {
foreach ($app in $r.snames) {
#resolve SID
[WMI]$Resolve = "\\$computername\root\cimv2:Win32_SID.SID='$SID'"
if ($resolve.accountname) {
$Username = "$($resolve.ReferencedDomainName)\$($resolve.AccountName)"
}
else {
$Username = $SID
}
$hash = [ordered]@{Username = $Username;Name = $app}
#add a list of known properties
"Displayname","DisplayVersion","Publisher",
"InstallDate","InstallLocation","Comments","UninstallString" | foreach {
$value = $reg.GetStringValue($hive,"$regpath\$app",$_).sValue
$hash.Add($_,$value)
}
#write a custom object to the pipeline
[pscustomobject]$hash
} #foreach app
} #if snames
} #foreach sname in HKEY_USERS
Whenever I write PowerShell, I’m always thinking about objects in the pipeline, and this is no different. I want to create an object with application information. If the initial enumeration finds a hive with data in the sNames property, then that will require additional processing. The potentially tricky part is that there are different methods to call depending on the type of registry data, such as String or DWord. Fortunately, everything we are after is a string. And I already know the names. All I need to do is get the string value for each.
As you can see, I use the EnumValues() method that requires the Hive value and the registry entry path.
"Displayname","DisplayVersion","Publisher",
"InstallDate","InstallLocation","Comments","UninstallString" | foreach {
$value = $reg.GetStringValue($hive,"$regpath\$app",$_).sValue
$hash.Add($_,$value)
}
The GetStringValue() method is retrieving the value from the appropriate hive for a specific registry key and name. The sValue property is the actual piece of data that I want. In my code, I’m adding it to a hash table that I created earlier. I did that so when I’m finished I can turn that hashtable into an object.
[pscustomobject]$hash
Note that the [pscustomobject] accelerator requires PowerShell 3.0 and later. The other piece of the puzzle is that I want to identify the user. Right now all I have is the SID string. Fortunately, I can use WMI and the Win32_SID class to resolve it.
[WMI]$Resolve = "\\$computername\root\cimv2:Win32_SID.SID='$SID'"
I’ve added some error handling so that if the account doesn’t get resolved to a user name, then I’ll have to use the original SID. The end result is something like this:
Error handling for when the account doesn’t get resolved to a user name. (Image Credit: Jeff Hicks)
$sb = {
$computername = $env:computername
[WMIClass]$Reg = "\\$computername\root\default:StdRegProv"
$HIVE = 2147483651
#enumerate subkeys
$reg.EnumKey($HIVE,"").snames | foreach {
$sid = $_
write-host "Processing $sid" -ForegroundColor cyan
$regpath = "$sid\Software\Microsoft\Windows\CurrentVersion\Uninstall"
$r = $reg.EnumKey($HIVE,$regpath)
if ($r.snames ) {
foreach ($app in $r.snames) {
#$data = $reg.EnumValues($Hive,"$regpath\$app")
#resolve SID
[WMI]$Resolve = "\\$computername\root\cimv2:Win32_SID.SID='$SID'"
if ($resolve.accountname) {
$Username = "$($resolve.ReferencedDomainName)\$($resolve.AccountName)"
}
else {
$Username = $SID
}
$hash = [ordered]@{Username = $Username;Name = $app}
#add a list of known properties
"Displayname","DisplayVersion","Publisher",
"InstallDate","InstallLocation","Comments","UninstallString" | foreach {
$value = $reg.GetStringValue($hive,"$regpath\$app",$_).sValue
$hash.Add($_,$value)
}
#write a custom object to the pipeline
[pscustomobject]$hash
} #foreach app
} #if snames
} #foreach sname in HKEY_USERS
} #close scriptblock
$d = invoke-command $sb -computername $computername -Credential globomantics\administrator
$d | out-gridview -Title "$computername User Apps"
Results of code sample. (Image Credit: Jeff Hicks)
More in PowerShell
Most popular on petri