When a user first logs on and their profile folder is created, their user hive is initialized as a copy of the default user hive, stored here:
C:\Users\Default\NTUSER.DAT
After the profile is created, the copies are independent, so there is no convenient way to edit all users' profiles in one shot. If, however, you load and edit that hive, the changes will be reflected in any subsequently created profiles.
The Classes
key is stored in a separate hive that you can load and edit separately:
%USERPROFILE%\AppData\Local\Microsoft\Windows\UsrClass.dat
Unfortunately, there is no default classes hive; it appears to be initialized to an empty hive when a profile is created.
To make dealing with all this a little less tedious for you, I wrote a PowerShell script to an audit rule on each of the given subtrees loaded under HKEY_USERS
. But first, we need to enable the auditing privilege, which requries a surprising amount of code (adapted from Lee Holmes):
param(
## The privilege to adjust.
$Privilege,
## The process on which to adjust the privilege. Defaults to the current process.
$ProcessId = $pid,
## Switch to disable the privilege, rather than enable it.
[Switch] $Disable
)
## Taken from P/Invoke.NET with minor adjustments.
$definition = @'
using System;
using System.Runtime.InteropServices;
public class AdjPriv
{
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
}
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
public static bool EnablePrivilege(long processHandle, string privilege, bool disable)
{
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = new IntPtr(processHandle);
IntPtr htok = IntPtr.Zero;
retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
if(disable)
{
tp.Attr = SE_PRIVILEGE_DISABLED;
}
else
{
tp.Attr = SE_PRIVILEGE_ENABLED;
}
retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
return retVal;
}
}
'@
$processHandle = (Get-Process -id $ProcessId).Handle
try {
Add-Type $definition
} catch {} # Silent failure on re-registration
[AdjPriv]::EnablePrivilege($processHandle, $Privilege, $Disable)
Save that as privs.ps1
. Then we can use it in our script, which should go in the same directory:
Param (
[string[]]$Subkeys
)
$rights = 'ChangePermissions,CreateSubKey,CreateLink,Delete,SetValue,TakeOwnership'
.\privs.ps1 -Privilege SeSecurityPrivilege
$users = [Microsoft.Win32.RegistryKey]::OpenBaseKey('Users', 'Default')
$Subkeys | % {
$key = $users.OpenSubKey($_, $true)
$acl = $key.GetAccessControl()
$rule = [System.Security.AccessControl.RegistryAuditRule]::new([System.Security.Principal.NTAccount]::new('Everyone'), $rights, 'ContainerInherit,ObjectInherit', 'None', 'Success')
$acl.AddAuditRule($rule)
$key.SetAccessControl($acl)
$key.Close()
}
For each of the provided subkeys, it creates an rule that audits successful changes (defined by the $rights
variable near the top, which you can change) in that key or subkeys. Save it as a PS1 file, e.g. regaudit.ps1
. If you haven't already, enable PowerShell script execution as directed in the PowerShell tag wiki. Then load all necessary hives under HKEY_USERS
. You can run the script from an elevated PowerShell prompt like so:
.\regaudit.ps1 -Subkeys 'Steven\Control Panel', 'Steven\Software\Microsoft'
Note that you should not include HKCU
or any such root. You can pass as many subkeys as you like. Alternatively, you can create a text file with one subkey per line and use that like so:
.\regaudit.ps1 -Subkeys (gc .\toaudit.txt)
N.B. Audit events will not appear in the Security log unless "Audit object access" is enabled in the Audit Policy section of the Local Security Policy.
Further reading: Access Control in .NET