Tuesday, December 24, 2013

Solution: Using StdRegProv


I recently came across an issue and a solution for it regarding the use of the StdRegProv which enables us to access the registry via all other languages except Powershell.

Lets take a look at what I'm talking about:

The MOF looks like this:

uint32 EnumKey(
  [in, optional]  uint32 hDefKey = HKEY_LOCAL_MACHINE,
  [in]            string sSubKeyName,
  [out]           string sNames[]
);
 
We call it by creating the HKEY_ value in hex or dec:
 
Const HKEY_CLASSES_ROOT = &H80000000 
or
Const HKEY_CLASSES_ROOT = 2147483648 

Dim iret
Dim Names()
Dim sKey
sKey = "clsid"

Set oReg = GetObject("winmgmts:\\.\root\cimv2").Get("StdRegProv")
oReg.EnumKey HKEY_CLASSES_ROOT, sKey, Names
For each Name in Names
   wscript.echo Name
Exit For
Next

Works flawlessly.

Now, let's turn this into a powershell script:

$HKEY_CLASSES_ROOT = 2147483648

[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey

$sKey = "clsid"
$oReg = Get-WmiObject -namespace root\cimv2 -class StdRegProv
$iret = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey, $Names)
Foreach($Name in $Names)
{
  write-host $Name
  break
}

As it turns out, you can't call this as an instance of a class:


You cannot call a method on a null-valued expression.
At C:\Users\Administrator\Desktop\testregcode.ps1:9 char:1
+ $iret = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey, $Names)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

You need to call it as a class:

$HKEY_CLASSES_ROOT = 2147483648

[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey

$sKey = "clsid"
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$iret = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey, $Names)
Foreach($Name in $Names)
{
  write-host $Name
  break
}

But you will still get an error:

Cannot find an overload for "EnumKey" and the argument count: "3".
At C:\Users\Administrator\Desktop\testregcode.ps1:9 char:1
+ $iret = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey, $Names)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest


Okay, so what is going on here?  Well, it turns out that you pass in the correct  IN parameters as you normally would and get passed back out a System.Management.ManagementBaseObject.


 $HKEY_CLASSES_ROOT = 2147483648

[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
[System.Object]$retValues

$sKey = "clsid"
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$retValues = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey)
foreach($prop in $retValues.Properties)
{
    $prop.Name
}

This will return:

ReturnValue
sNames

You will see this in examples on the web:

$HKEY_CLASSES_ROOT = 2147483648

[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
[System.String[]]$strValues

$sKey = "clsid"
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$strValues = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey).snames
foreach($val in $strValues)
{
    write-host $val
}

This will return every subkey in HKEY_CLASSES_ROOT\clsid.  With that said, you really need to parse for the return Value as it will tell you whether or not the call was successful or not.


$HKEY_CLASSES_ROOT = 2147483648

[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
$OutVal

$sKey = "clsid"
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$OutVal = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey)
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
    $sNames =  $OutVal.Properties.Item("sNames").Value
    foreach($sName in $sNames)
    {
         write-host $sName
    }
}

Here's an example of getting a DWORD value: 

$HKEY_LOCAL_MACHINE = 2147483650

[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
$OutVal

$sKey = "Software\Microsoft\Windows NT\CurrentVersion"
$oReg = [WMICLASS]"root\cimv2:StdRegProv"

$OutVal = $oReg.GetDWordValue($HKEY_LOCAL_MACHINE, $sKey, "InstallDate")
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
    $Value =  $OutVal.Properties.Item("uValue").Value
    $vs = '{0:x}' -f $Value
    $vs = "0x" + $vs + "(" + $Value + ")"
    write-host $vs
}

Now, let's do the Reg_Binary:


$HKEY_LOCAL_MACHINE = 2147483650

[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
[System.Management.ManagementBaseObject]$OutVal

$sKey = "Software\Microsoft\Windows NT\CurrentVersion"
[System.String]$vs = ""
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$Name = "DigitalProductId"
$OutVal = $oReg.GetBinaryValue($HKEY_LOCAL_MACHINE, $sKey, $Name)
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
   $Value =  $OutVal.Properties.Item("uValue").Value
   foreach($v in $Value)
   {
       $vv = '{0:x}' -f $v
       if($vv.ToString().Length -eq 1)
       {
          $vv = "0" + $vv
       }
       if($vs -ne "")
       {
            $vs = $vs + ","
       }
       $vs = $vs + $vv
       $vv = ""
   }
   write-host $vs
}

Now, let's enumerate through Values:

$HKEY_LOCAL_MACHINE = 2147483650

[System.Int32[]]$DataTypes
[System.String[]]$ValueNames
[System.String]$sKey
[System.Management.ManagementBaseObject]$OutVal

$sKey = "SYSTEM\CurrentControlSet\Control\Session Manager"
[System.String]$vs = ""
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$Name = "DigitalProductId"
$OutVal = $oReg.EnumValues($HKEY_LOCAL_MACHINE, $sKey)
[System.String]$Value
[System.String]$v
[System.String]$vv

$ValueNames = $outVal.Properties.Item("sNames").Value
$DataTypes = $outVal.Properties.Item("Types").Value

for($x=0;$x -lt $DataTypes.GetLength(0); $x++)
{

    switch ($DataTypes[$x])
        {
            1{
                $OutVal = $oReg.GetStringValue($HKEY_LOCAL_MACHINE, $sKey, $ValueNames[$x])
                if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
                {
                    $Value =  $OutVal.Properties.Item("sValue").Value
                }
                Write-Host $ValueNames[$x] REG_SZ $Value           
            }
            2{
                $OutVal = $oReg.GetExpandedStringValue($HKEY_LOCAL_MACHINE, $sKey, $ValueNames[$x])
                if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
                {
                    $Value =  $OutVal.Properties.Item("sValue").Value
                }
                Write-Host $ValueNames[$x] REG_EXPAND_SZ $Value           
            }
            3{

                $OutVal = $oReg.GetBinaryValue($HKEY_LOCAL_MACHINE, $sKey, $ValueNames[$x])
                if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
                {
                    $Value =  $OutVal.Properties.Item("uValue").Value
                    foreach($v in $Value)
                    {
                        $vv = '{0:x}' -f $v
                        if($vv.ToString().Length -eq 1)
                        {
                            $vv = "0" + $vv
                        }
                        if($vs -ne "")
                        {
                            $vs = $vs + ","
                        }
                        $vs = $vs + $vv
                        $vv = ""
                                         
                    }
                    Write-Host $ValueNames[$x] REG_BINARY $vs
                }
            }
            4{
                $vs = ""
                $OutVal = $oReg.GetDWordValue($HKEY_LOCAL_MACHINE, $sKey, $ValueNames[$x])
                if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
                {
                    $Value =  $OutVal.Properties.Item("uValue").Value
                    foreach($v in $Value)
                    {
                        $vv = '{0:x}' -f $v
                        if($vs -ne "")
                        {
                            $vs = $vs + ","
                        }
                        $vs = $vs + $vv
                        $vv = ""
                    }
                    Write-Host $ValueNames[$x] REG_DWORD $vs
                }
            }
            7{
                $OutVal = $oReg.GetMultiStringValue($HKEY_LOCAL_MACHINE, $sKey, $ValueNames[$x])
                if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
                {
                    $Value =  $OutVal.Properties.Item("sValue").Value
                }
                Write-Host $ValueNames[$x] REG_SZ $Value  
            }
        }
}






 


2 comments:

  1. Would you happen to know what would cause:
    Exception calling "EnumKey" : "Access denied " ?

    I get this error rarely when using similar code to yours and the error I catch doesn't say much else about it.

    ReplyDelete
  2. There are areas in the registry where Microsoft has flagged it for their use only.

    Nothing you can do about that and you will get an Access Denied.

    Are you calling the enumerator recursively? It is possible that the issue is due to that.

    I'd have to see your code in order to determine if there is a logical flow issue.

    ReplyDelete