Wax/Trace: classify restore .reg by content root, not filename
Install-FormtracepakSettings decided per-user vs HKLM by matching the filename against 'HKEY_USERS'. The backup names files after their source path, so an operator pref captured from HKCU:\ lands in a file named "HKCU_..." with content root HKEY_CURRENT_USER - which the filename match missed entirely, dropping it from the restore. Read each .reg once and classify by its content root(s): [HKEY_CURRENT_USER... -> per-user, remap root to HKEY_USERS\<targetSid> [HKEY_USERS\<srcSid>... -> per-user, remap srcSid -> targetSid [HKEY_LOCAL_MACHINE... -> HKLM The temp rewrite (UTF-16-LE for reg.exe import) is only written when the content actually changes; otherwise the file imports as-is. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -444,41 +444,64 @@ if ($RestoreRegistry) {
|
|||||||
# If not resolved, skip HKEY_USERS .reg files.
|
# If not resolved, skip HKEY_USERS .reg files.
|
||||||
$regFiles = Get-ChildItem -Path $regDir -Filter '*.reg' -ErrorAction SilentlyContinue
|
$regFiles = Get-ChildItem -Path $regDir -Filter '*.reg' -ErrorAction SilentlyContinue
|
||||||
foreach ($rf in $regFiles) {
|
foreach ($rf in $regFiles) {
|
||||||
$isUserReg = ($rf.Name -match 'HKEY_USERS')
|
# Decide per-user vs HKLM by the .reg CONTENT root, NOT the filename.
|
||||||
|
# The backup names files after the source path, so an operator pref
|
||||||
|
# captured via HKCU:\ lands in a file named "HKCU_..." with content
|
||||||
|
# root HKEY_CURRENT_USER - which a filename match on 'HKEY_USERS'
|
||||||
|
# misses entirely. Read the content once and classify by its root(s):
|
||||||
|
# [HKEY_CURRENT_USER... -> per-user, remap root to HKEY_USERS\<targetSid>
|
||||||
|
# [HKEY_USERS\<srcSid>... -> per-user, remap srcSid -> targetSid
|
||||||
|
# [HKEY_LOCAL_MACHINE... -> HKLM
|
||||||
|
$content = $null
|
||||||
|
try { $content = Get-Content -LiteralPath $rf.FullName -Raw } catch {
|
||||||
|
Write-Warning " [ERR] Could not read $($rf.Name): $_ - skipping"
|
||||||
|
$counters.Errors++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
$hasCU = $content -match '\[HKEY_CURRENT_USER'
|
||||||
|
$hasHKU = $content -match '\[HKEY_USERS\\S-1-5-21'
|
||||||
|
$isUserReg = ($hasCU -or $hasHKU)
|
||||||
|
|
||||||
if ($HKEYUsersOnly -and -not $isUserReg) {
|
if ($HKEYUsersOnly -and -not $isUserReg) {
|
||||||
Write-Host " [SKIP] HKEY_USERS-only mode, skipping HKLM .reg: $($rf.Name)" -ForegroundColor DarkGray
|
Write-Host " [SKIP] HKEY_USERS-only mode, skipping HKLM .reg: $($rf.Name)" -ForegroundColor DarkGray
|
||||||
$counters.Skipped++
|
$counters.Skipped++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if ($isUserReg -and -not $targetSid) {
|
if ($isUserReg -and -not $targetSid) {
|
||||||
Write-Host " [SKIP] HKEY_USERS .reg file - target SID not resolved: $($rf.Name)" -ForegroundColor DarkGray
|
Write-Host " [SKIP] per-user .reg - target SID not resolved: $($rf.Name)" -ForegroundColor DarkGray
|
||||||
$counters.Skipped++
|
$counters.Skipped++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
$importPath = $rf.FullName
|
$importPath = $rf.FullName
|
||||||
if ($isUserReg) {
|
if ($isUserReg) {
|
||||||
# Read .reg, detect src SID (first S-1-5-21-* match), substitute
|
# Rewrite the root(s) so the import lands in the TARGET user's hive,
|
||||||
# target SID, write to a temp file. reg.exe import the temp.
|
# then write a temp UTF-16-LE file (reg.exe import requires Unicode).
|
||||||
try {
|
try {
|
||||||
$content = Get-Content -LiteralPath $rf.FullName -Raw
|
$newContent = $content
|
||||||
|
if ($hasHKU) {
|
||||||
|
# HKEY_USERS\<srcSid> -> HKEY_USERS\<targetSid>
|
||||||
$m = [regex]::Match($content, $srcSidRegex)
|
$m = [regex]::Match($content, $srcSidRegex)
|
||||||
if ($m.Success) {
|
if ($m.Success -and $m.Value -ne $targetSid) {
|
||||||
$srcSid = $m.Value
|
$newContent = $newContent -replace [regex]::Escape($m.Value), $targetSid
|
||||||
if ($srcSid -ne $targetSid) {
|
Write-Host " [REMAP] $($rf.Name): SID $($m.Value) -> $targetSid"
|
||||||
$newContent = $content -replace [regex]::Escape($srcSid), $targetSid
|
}
|
||||||
|
}
|
||||||
|
if ($hasCU) {
|
||||||
|
# HKEY_CURRENT_USER -> HKEY_USERS\<targetSid> (this is the
|
||||||
|
# case the old filename-based logic dropped on the floor).
|
||||||
|
$newContent = $newContent -replace '\[HKEY_CURRENT_USER', "[HKEY_USERS\$targetSid"
|
||||||
|
Write-Host " [REMAP] $($rf.Name): HKEY_CURRENT_USER -> HKEY_USERS\$targetSid"
|
||||||
|
}
|
||||||
|
if ($newContent -ne $content) {
|
||||||
$tmp = Join-Path $env:TEMP ("rewrite-" + $rf.Name)
|
$tmp = Join-Path $env:TEMP ("rewrite-" + $rf.Name)
|
||||||
# .reg files MUST be UTF-16-LE (Unicode) for reg.exe import.
|
|
||||||
# Set-Content -Encoding Unicode does that.
|
|
||||||
Set-Content -LiteralPath $tmp -Value $newContent -Encoding Unicode -Force
|
Set-Content -LiteralPath $tmp -Value $newContent -Encoding Unicode -Force
|
||||||
$importPath = $tmp
|
$importPath = $tmp
|
||||||
Write-Host " [REMAP] $($rf.Name): SID $srcSid -> $targetSid"
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Write-Host " [INFO] No SID found in $($rf.Name) - importing as-is" -ForegroundColor DarkGray
|
Write-Host " [INFO] $($rf.Name): per-user root already matches target - importing as-is" -ForegroundColor DarkGray
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Write-Warning " [ERR] SID rewrite failed for $($rf.Name): $_ - skipping"
|
Write-Warning " [ERR] root rewrite failed for $($rf.Name): $_ - skipping"
|
||||||
$counters.Errors++
|
$counters.Errors++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user