PDQ.com mobilePDQ.com desktop
Support

Modifying the Registry for All Users

Kris PowellKris Powell
·

We’re going to look at modifying the registry for all users whether or not a user is logged into a machine. This is a continuation of my last blog post – Modifying the Registry of Another User.

As a quick refresher, we learned how to modify a user’s registry (HKEY_CURRENT USER or HKEY_USERS) without having that user logged onto a machine. We had to load and unload their NTUSER.DAT file separately in the HKEY_USERS registry hive.

It was pretty exciting.

Now, we’re going to add to that excitement by learning how to do it for all users instead of only specific users.

Modifying the Registry for All Users

Before we can modify the registry for all users, we need to be able to go out and grab all the ntuser.dat files so that we can load them as we did in the last blog post.

I know what you’re thinking. You’re thinking that’s easy! We know that the ntuser.dat file is in the C:\Users\<Username>\ directory, so that should be as simple as searching through C:\Users for any ntuser.dat file, right?!

This will only work if nobody is logged into a machine. We have to take into consideration any currently-logged on users. Any currently-logged on users will already have their ntuser.dat files loaded into the registry. This includes users who forget to log off. Even though their session is disconnected and somebody else has logged on, their registry is still loaded in the registry.

Here’s an example of this. I’m currently logged into my test machine. There is also a disconnected user Reg who forgot to log off:

PS-Blog-Registry-HKU-example

If I try loading Reg’s ntuser.dat, I encounter an error telling me that ntuser.dat is already being used by something else.

PS-Blog-Registry-Cannot-load-ntuser.dat

So, what do we do?

We need to find all users on a machine and compare it with all currently-logged on user security identifiers (SIDs).

Find all users and their SIDs

Fortunately for us, there is a convenient location in the registry that stores the users on a machine and their SIDs.

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*

This location will have a list of all the SIDs for a machine as well as some other properties. We’re interested in the SIDs that start with S-1-5-21. Notice that you see the two SIDs from an earlier screenshot:

PS-Blog-Registry-ProfileList-example

From this, we are able to use regular expressions and some calculated properties to select some great information with PowerShell. We’ll use the Get-ItemProperty cmdlet to get that information from the registry.

$PatternSID = 'S-1-5-21-\d+-\d+\-\d+\-\d+$'
Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' | Where-Object {$_.PSChildName -match $PatternSID} |
select @{name="SID";expression={$_.PSChildName}},
@{name="UserHive";expression={"$($_.ProfileImagePath)\ntuser.dat"}},
@{name="Username";expression={$_.ProfileImagePath -replace '^(.*[\\\/])', ''}}

Now we have a list of the usernames and their associated SIDs.

Getting SID of users in HKEY_USERS

Next, we’ll need to compare those SIDs with the SIDs of the users that are currently logged on and have their registry’s loaded to HKEY_USERS:

Get-ChildItem Registry::HKEY_USERS | Where-Object {$_.PSChildName -match $PatternSID} | select PSChildName

Easy peasy.

Putting it all together

Now, we just need to compare the two lists of SIDs and we’ll be able to modify the registry at will. I’ve compiled it all into a template that somebody could use to read or modify the registry of each user on a machine. In my example, I load each registry (if not loaded) and attempt to read the Uninstall key at HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*

This will show me which users have per-user installs of software as well as the software name:

# Regex pattern for SIDs
$PatternSID = 'S-1-5-21-\d+-\d+\-\d+\-\d+$'

# Get Username, SID, and location of ntuser.dat for all users
$ProfileList = gp 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' | Where-Object {$_.PSChildName -match $PatternSID} |
Select @{name="SID";expression={$_.PSChildName}},
@{name="UserHive";expression={"$($_.ProfileImagePath)\ntuser.dat"}},
@{name="Username";expression={$_.ProfileImagePath -replace '^(.*[\\\/])', ''}}

# Get all user SIDs found in HKEY_USERS (ntuder.dat files that are loaded)
$LoadedHives = gci Registry::HKEY_USERS | ? {$_.PSChildname -match $PatternSID} | Select @{name="SID";expression={$_.PSChildName}}

# Get all users that are not currently logged
$UnloadedHives = Compare-Object $ProfileList.SID $LoadedHives.SID | Select @{name="SID";expression={$_.InputObject}}, UserHive, Username

# Loop through each profile on the machine
Foreach ($item in $ProfileList) {
# Load User ntuser.dat if it's not already loaded
IF ($item.SID -in $UnloadedHives.SID) {
reg load HKU\$($Item.SID) $($Item.UserHive) | Out-Null
}

#####################################################################
# This is where you can read/modify a users portion of the registry

# This example lists the Uninstall keys for each user registry hive
"{0}" -f $($item.Username) | Write-Output
Get-ItemProperty registry::HKEY_USERS\$($Item.SID)\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |
Foreach {"{0} {1}" -f " Program:", $($_.DisplayName) | Write-Output}
Get-ItemProperty registry::HKEY_USERS\$($Item.SID)\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
Foreach {"{0} {1}" -f " Program:", $($_.DisplayName) | Write-Output}

#####################################################################

# Unload ntuser.dat
IF ($item.SID -in $UnloadedHives.SID) {
### Garbage collection and closing of ntuser.dat ###
[gc]::Collect()
reg unload HKU\$($Item.SID) | Out-Null
}
}

Final Notes

Use this information with a healthy dose of caution. It is never wise to modify the registry without a good reason, and even some good reasons aren’t always great justification. In other words, be responsible and test your scripts before using on production systems. We cannot be held responsible for any issues that you may encounter.

Happy PowerShelling!

Did you know that PDQ Deploy has a PowerShell step you can use to deploy your scripts? This blog post is part of a series on Powershell:

Don't miss the next post!

Quick Fix For Windows Crypto Bug (CVE-2020-0601)

The NSA has found a significant vulnerability in Windows known as CVE-2020-0601. This vulnerability is particularly nasty and impacts the Windows CryptoAPI.