Categories

Registry

When did Windows update last run

A question came up on the forum regarding when Windows Update last run and when an update was last installed.  Get-Hotfix shows the date of installation for most BUT not all patches.

The registry holds values showing last successful detection and install:

$props = [ordered]@{
LastDetect = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Detect' -Name LastSuccessTime |
select -ExpandProperty LastSuccessTime

LastInstall = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install' -Name LastSuccessTime |
select -ExpandProperty LastSuccessTime
}

New-Object -TypeName psobject -Property $props

Registry oddity

Looking at modifying the registry on a virtual machine while its offline. I mount the VHDX file and can run this  to load the registry

PS> reg load HKLM\VHDSYS h:\windows\system32\config\system
The operation completed successfully.

If I perform a reg unload at this point everything works but if I access the registry – for instance

PS> ls hklm:

and then try and unload I get an error!

PS> reg unload HKLM\VHDSYS
ERROR: Access is denied.

One thing I found is that there is as reference to the remote hive in the variable collection

PS> ls variable:

Name                           Value
----                           -----
$                              HKLM\VHDSYS

Empirically I’ve found that running these commands

ls env:
ls variable:

changes the value of the $ variable

You can then unload the hive

PS> reg unload HKLM\VHDSYS
The operation completed successfully.

Decoding the mounted device information

In the previous post we looked at how to read the mounted device information. The data is in binary though – if you want it readable and not all of it is readable – try this

$data = @()            
Get-Item -Path HKLM:\SYSTEM\MountedDevices |            
select -ExpandProperty Property |            
where {$_ -like "\Dos*"} |             
foreach {            
 $name = $_            
 $bin = (Get-ItemProperty -Path HKLM:\SYSTEM\MountedDevices -Name $name)."$name"            
             
 $decoded = @()            
 $bin | foreach {            
  $decoded += [char]$_            
 }            
            
            
 $data += New-Object -TypeName psobject -Property @{            
  Device =  $name            
  BinaryValue  = $bin            
  DecodedValue = $($decoded -join "")            
 }            
             
}            
$data | Format-Table  Device, DecodedValue  -AutoSize


Same as last time except for the loop through the binary data using [char] to decode the ASCII values.  use –join to make a string rather than an array. The apparent gaps in the resultant string are because we’re dealing with Unicode

Reading mounted device information from the registry

Interesting question about reading the registry.  How do you read HKLM:\SYSTEM\MountedDevices and pull out the name of the device and the associated data.

Get-Item -Path HKLM:\SYSTEM\MountedDevices

returns data of this form

Name                           Property
----                           --------
MountedDevices                 \DosDevices\C: : {218, 187, 32, 142...}
                               \DosDevices\G: : {92, 0, 63, 0...}
                               \DosDevices\E: : {95, 0, 63, 0...}
                               \DosDevices\F: : {92, 0, 63, 0...}
                               \DosDevices\D: : {218, 187, 32, 142...}
                               \DosDevices\I: : {95, 0, 63, 0...}

We need to drill into the property but if we expand the property we will only get the name of the device. So we need to loop through those names

$data = @()            
Get-Item -Path HKLM:\SYSTEM\MountedDevices |            
select -ExpandProperty Property |            
where {$_ -like "\Dos*"} |             
foreach {            
 $name = $_            
$data += New-Object -TypeName psobject -Property @{            
  Device =  $name             
  Value  = (Get-ItemProperty -Path HKLM:\SYSTEM\MountedDevices -Name $name)."$name"            
 }            
            
}            
$data


To simplify the output is limited to Dos devices



Get-itemproperty returns the data with the name of the device as the property name so we need to drill in to get the value

Set registry key owner

In chapter 7 of PowerShell and WMI I stated that I would post a .NET version of a script to set ownership of a registry key. The WMI method needs Vista or above so we need the .NET version for pre-Vista operating systems.

function set-regkeyowner {            
[CmdletBinding()]            
param (            
 [parameter(Mandatory=$true)]            
 [string]            
 [Validateset(“HKCR”, “HKCU”, “HKLM”, "HKUS", "HKCC")]            
  $hive,            
            
 [parameter(Mandatory=$true)]            
 [string]$key            
)              
PROCESS {             
Write-Verbose "Set Hive"            
switch ($hive){            
 “HKCR” {$reg = [Microsoft.Win32.Registry]::ClassesRoot}            
 “HKCU” {$reg = [Microsoft.Win32.Registry]::CurrentUser}            
 “HKLM” {$reg = [Microsoft.Win32.Registry]::LocalMachine}            
 "HKUS" {$reg = [Microsoft.Win32.Registry]::Users}            
 "HKCC" {$reg = [Microsoft.Win32.Registry]::CurrentConfig}            
}            
            
$permchk = [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree            
$regrights = [System.Security.AccessControl.RegistryRights]::ChangePermissions            
            
Write-Verbose "Open Key and get access control"            
$regkey = $reg.OpenSubKey($key, $permchk, $regrights)            
$rs = $regkey.GetAccessControl()            
            
Write-Verbose "Create security principal"            
$user = New-Object -TypeName Security.Principal.NTaccount -ArgumentList "Administrators"            
            
$rs.SetGroup($user)             
$rs.SetOwner($user)             
$regkey.SetAccessControl($rs)            
}            
}


Take a hive and key as parameters. Use a switch to set the Registry enumeration and then set the permissions and rights we want. Open the key and get the access controls.



Create a security principal for the Administrators group and set the group and owner in the access control. Use SetAccessControl to change the permissions

Recording and slides for July 2010 UG meeting

This months meeting covered working with the registry.

The slides and the demo script are available from

http://cid-43cfa46a74cf3e96.office.live.com/browse.aspx/PowerShell%20User%20Group/2010%20July

 

The recording is available

Richard Siddaway has invited you to view a Microsoft Office Live Meeting recording.
View Recording
Recording Details
    Subject: PowerShell and the Registry
    Recording URL: https://www.livemeeting.com/cc/usergroups/view
    Recording ID: CB99JS
    Attendee Key: mm$2!",$G

Registry 7

One other aspect we need to cover is deleting individual values

001
002
003
$reg = [Microsoft.Win32.Registry]::LocalMachine
$key = $reg.OpenSubKey("Software\PSAM PSAdmins\Test", $true)
$key.DeleteValue("oops")

Open the key for writing and use the DeleteValue() method.

Registry 6

Having created some keys lets add some values

001
002
003
004
005
006
007
008
009
010
011
$reg = [Microsoft.Win32.Registry]::LocalMachine
$key = $reg.OpenSubKey("Software\ITKE PSAdmins", $true)
$key.SetValue("Dword Entry", 567 ,"Dword") 
$key.SetValue("String Entry", "My new string" ,"String") 
$key.SetValue("Expanded String Entry", "%COMPUTERNAME%" ,"ExpandString")

[string[]]$multi = "Z","Y","X"
$key.SetValue("Multi-string Entry", $multi ,"MultiString")

$key = $reg.OpenSubKey("Software\PSAM PSAdmins\Test", $true)
$key.SetValue("Oops", "Didn't mean to do this" ,"String")

This is very similar to the script we used to change the values – open the key for writing and use SetValue().  Just make sure we use the right data type.

Registry 5

Having seen how to read, change and delete keys – lets add some

 

001
002
003
$reg = [Microsoft.Win32.Registry]::LocalMachine
$key = $reg.CreateSubKey("Software\ITKE PSAdmins")
$key2 = $reg.CreateSubKey("Software\PSAM PSAdmins\Test")

Set the hive and call the CreateSubKey method.  Note the second example where we create a further depth of subkeys.  It isn’t necessary to create each step in the chain we can create the lot in one go

Registry 4

Now is the time to delete a registry key

001
002
$reg = [Microsoft.Win32.Registry]::LocalMachine
$key = $reg.DeleteSubKey("Software\ITKE PSAdmins")

If there is a tree of subkeys then we can use the DeleteSubKeyTree() method