Suppose you are writing a script that requires a 32 bit powershell window. How can you query 64bit registry keys from this script?
If you are connecting to a remote computer your most obvious and best option is Enter-PSSession or New-PSSession to open a native powershell session on the remote computer.
You also have other options for querying registry strings but we need to be careful
Option 1: Get-Item
Lets first consider using Get-Item directly. Here is a very simple function with no error control (or warranty):
Function Get-RegStringValueByGetItem{ Param([string]$KeyPath, [string]$StringName) $objRegKey = Get-ItemProperty $KeyPath -Name $StringName Return $objRegKey.($stringName) }
When running from a 64bit powershell session, this returns as expected:
Get-RegStringValueByGetItem -KeyPath 'HKLM:\software\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir'
C:\Program Files\Common Files
Get-RegStringValueByGetItem -KeyPath 'HKLM:\software\Wow6432Node\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir'
C:\Program Files (x86)\Common Files
Lets try this from a 32bit powershell session:
Get-RegStringValueByGetItem -KeyPath 'HKLM:\software\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir'
C:\Program Files (x86)\Common Files
Get-RegStringValueByGetItem -KeyPath 'HKLM:\software\Wow6432Node\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir'
C:\Program Files (x86)\Common Files
So, you can see that when running from a 32bit session we get redirected to the Wow6432Node without warning.
Option 2: Microsoft.Win32.RegistryKey
We know that Powershell has access to the DotNet Classes lets try it through the Microsoft.Win32.RegistryKey class.
You can read more about this class here: https://msdn.microsoft.com/en-us/library/Microsoft.Win32.RegistryKey(v=vs.110).aspx
Function Get-RegStringValueBYMicrosoft.Win32.RegistryKeyCLASS{ Param([string]$KeyPath, [string]$StringName, [string]$keyRoot ) Switch($keyRoot){ 'HKLM' {$strKeyRoot = [Microsoft.Win32.RegistryHive]'LocalMachine'} 'HKCR' {$strKeyRoot = [Microsoft.Win32.RegistryHive]'ClassesRoot'} 'HKCU' {$strKeyRoot = [Microsoft.Win32.RegistryHive]'CurrentUser'} 'HKU' {$strKeyRoot = [Microsoft.Win32.RegistryHive]'Users'} } $objReg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($strKeyRoot, 'localhost') $strKeyPath = $keyPath -replace '\\','\\' $objSubKey = $objReg.OpenSubKey($strKeyPath) $strValue = $objSubKey.GetValue($StringName) Return ($strValue) }
When running from a 64bit powershell session, this returns as expected:
Get-RegStringValueBYMicrosoft.Win32.RegistryKeyCLASS -KeyPath 'software\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir' -keyRoot 'HKLM'
C:\Program Files\Common Files
Get-RegStringValueBYMicrosoft.Win32.RegistryKeyCLASS -KeyPath 'software\Wow6432Node\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir' -keyRoot 'HKLM'
C:\Program Files (x86)\Common Files
Lets try this from a 32bit powershell session:
Get-RegStringValueBYMicrosoft.Win32.RegistryKeyCLASS -KeyPath 'software\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir' -keyRoot 'HKLM'
C:\Program Files (x86)\Common Files
Get-RegStringValueBYMicrosoft.Win32.RegistryKeyCLASS -KeyPath 'software\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir' -keyRoot 'HKLM'
C:\Program Files (x86)\Common Files
So, you can see that once again when running from a 32bit session we get redirected to the Wow6432Node without warning.
Option 3: WbemScripting.SWbemNamedValueSet
This last option may take a little to wrap your head around.
Function Get-RegStringValueBYWbemScripting.SWbemNamedValueSetCOM{ Param([string]$KeyPath, [string]$StringName, [string]$keyRoot) Switch($keyRoot){ 'HKLM' {$strKeyRoot = '&h80000002'} 'HKCR' {$strKeyRoot = '&h80000000'} 'HKCU' {$strKeyRoot = '&h80000001'} 'HKU' {$strKeyRoot = '&h80000003'} } #Use the wbem scripting com object to enumerate the 64 bit standard registry provider $objNamedValueSet = New-Object -COM 'WbemScripting.SWbemNamedValueSet' $objNamedValueSet.Add('__ProviderArchitecture', 64) | Out-Null $objLocator = New-Object -COM 'Wbemscripting.SWbemLocator' $objServices = $objLocator.ConnectServer('localhost', 'root\default', '', '', '', '', '', $objNamedValueSet) $objStdRegProv = $objServices.Get('StdRegProv') $Inparams = ($objStdRegProv.Methods_ | where { $_.name -eq 'GetStringValue' }).InParameters.SpawnInstance_() # Add the input parameters $regkey = $keyPath -replace '\\','\\' ($Inparams.Properties_ | where { $_.name -eq 'Hdefkey' }).Value = $strkeyroot ($Inparams.Properties_ | where { $_.name -eq 'Ssubkeyname' }).Value = $regkey ($Inparams.Properties_ | where { $_.name -eq 'Svaluename' }).Value = $StringName #Execute the method $Outparams = $objStdRegProv.ExecMethod_('GetStringValue', $Inparams, '', $objNamedValueSet) Return ($Outparams.Properties_ | where { $_.name -eq 'sValue' }).Value }
You can read more about this COM object here: https://msdn.microsoft.com/en-us/library/aa390788(v=vs.85).aspx and the underlying StdRegProv here: https://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
Essentially what we are doing is using the WMI scripting Com object to reference the 64bit Registry provider.
Lets see how this works in a 64 bit session:
Get-RegStringValueBYWbemScripting.SWbemNamedValueSetCOM -KeyPath 'software\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir' -keyRoot 'HKLM'
C:\Program Files\Common Files
Get-RegStringValueBYWbemScripting.SWbemNamedValueSetCOM -KeyPath 'software\Wow6432Node\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir' -keyRoot 'HKLM'
C:\Program Files (x86)\Common Files
And from a 32bit session:
Get-RegStringValueBYWbemScripting.SWbemNamedValueSetCOM -KeyPath 'software\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir' -keyRoot 'HKLM'
C:\Program Files\Common Files
Get-RegStringValueBYWbemScripting.SWbemNamedValueSetCOM -KeyPath 'software\Wow6432Node\microsoft\Windows\CurrentVersion' -StringName 'CommonFilesDir' -keyRoot 'HKLM'
C:\Program Files (x86)\Common Files
Finally!
You can see that we needed to use an intermediate session (the WBEM provider) to query the 64 bit registry from a 32 bit shell. This is also the method I used to get the execution history in the SysJam Powershell Rightclick tool from an SCCM client which you can download at https://github.com/mdedeboer/SysJamRightClickToolForConfigMgr and is usable with any of the methods defined by the StdRegProv class.