Add win-fix-registry-corruption - #159
Marcus Ferreira (mvaferreira) wants to merge 3 commits into
Conversation
Repairs corrupted or unloadable registry hives on an offline Windows disk.
cddb2e1 to
69601af
Compare
- Repair-HiveInPlace validated the installed hive while its stale transaction logs were still beside it, and only moved them aside afterwards. reg.exe replays those logs on load, so the state that was validated was not the state the VM boots into. The logs are now moved first and the file is validated in the state it will be loaded in, and a failed validation puts both the hive and its logs back. - Restore-HiveFromRegBack rolled the hive files back when a later hive failed but left the renamed transaction logs renamed, so a rollback did not return the disk to the state it was found in. Each move is now recorded per hive and undone with it. - Test-RegBackMaintained trusted the exit code of reg load and then unloaded with a bare reg unload whose result was never checked, which silently leaked a mounted hive and a locked scratch file. Mount-OfflineHive cannot be pointed at a scratch copy, so the load stays direct but is now confirmed by key state, and the unload goes through Invoke-OfflineRegUnload, which retries while handles release and verifies the key is gone. The scratch file is only deleted once the unload is confirmed. - Hive replacements now pass through Assert-OfflineTarget, the gate every other writer in the library calls, so a path outside the offline image is refused rather than written. - The post-repair verification scan honoured the hive parameter, but the identity pair rule can restore SAM alongside SECURITY. A hive written outside the filter was never re-validated and the run reported clean. Get-AllFinding now takes a set of hives and the scan covers everything that was actually written. With hive=all the scope is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b4446db8-dda7-4210-a934-5b9ae9e3fb15
Review findings addressed —
|
| ID | Finding | Change |
|---|---|---|
| W1 | Transaction logs were renamed but never rolled back, and the hive was validated before the logs were moved aside — so a failure left the installation worse than it started | Repair-HiveInPlace moves .LOG* aside before validating and restores both hive and logs if the repair fails. Restore-HiveFromRegBack records the moved logs per hive so each one can be rolled back individually |
| W2 | Test-RegBackMaintained used raw reg.exe and never verified the unload, so a scratch copy could be left mounted |
the load is confirmed through Get-OfflineHiveKeyState, the unload goes through Invoke-OfflineRegUnload, and the scratch file is deleted only after the unload is confirmed |
| W3 | finally did not release assigned drive letters |
finally now calls Clear-OfflineDriveLetter |
| W4 | Offline writes bypassed Assert-OfflineTarget |
asserted before both hive-replacement paths |
| W5 | A partner hive written outside the requested scope was excluded from the final verification | -HiveFilter widened from [string] to [string[]], and the post-repair scan now covers hiveFilter ∪ restored ∪ repairedInPlace |
Validation
Break → detect → repair → detect → az vm repair restore → confirm the guest boots, all through
the product path with --preview pointing at the candidate branch.
The break is deliberately harsher than typical real-world corruption: it damages three hives in
three different ways and deletes all six .LOG1/.LOG2 recovery logs, denying the transactional
recovery that normally resolves a torn write.
| Hive | Damage planted |
|---|---|
| SOFTWARE | 64 KB of 0xDE written into the hive bins, header left intact |
| DEFAULT | regf header signature zeroed |
| SECURITY | truncated to 0 bytes |
| Scenario | Image | Generation | Build | Result |
|---|---|---|---|---|
allowRegBack=true |
WindowsServer:2025-datacenter-g2 |
Gen2 / UEFI | 26100 | 11/11 passed |
default (allowRegBack=false) |
windows-11:win11-24h2-pro |
Gen2 / client | 26100 | correct refusal — see below |
The RegBack run exercised every fix above, including the two that are unreachable by default:
Repaired 3 of 3 damaged hive(s). Repaired in place: SOFTWARE.
Restored from RegBack: SECURITY, DEFAULT, SAM.
Every replaced hive was saved next to itself with a .bak-<timestamp> suffix.
W5's partner-hive rule fired exactly as intended — SAM was undamaged, but was pulled into scope and
into the final verification to stay consistent with SECURITY:
Restoring SAM from RegBack as well, because the account and security hives are sealed together
and only work as a matched pair.
…and the cost was disclosed rather than hidden:
The account hives are being replaced with older copies. Local account passwords revert to their
state at backup time, and on a domain joined VM the machine account password reverts too, so the
domain trust may need to be repaired after the VM boots.
Post-repair detect reported "No registry hive corruption was found", and the guest booted.
The Windows 11 run is a deliberate negative case and is reported as a pass. With the default
allowRegBack=false, a 0-byte SECURITY hive and a signature-less DEFAULT have nothing recoverable
left on the disk. The script repaired SOFTWARE in place, refused to overstate the rest, and exited
ERROR:
Repaired 1 of 3 damaged hive(s). Repaired in place: SOFTWARE. 2 hive(s) are still damaged and
need a source image or a disk level repair.
That VM then did not boot — which is the honest outcome for that damage, and confirms the script
does not claim more than it achieved. It also confirms the advisory path adapts to the image:
WS2025 shipped with RegBack populated and periodic backup enabled, so detect offered it with a real
timestamp ("RegBack holds a usable backup from 2026-09-05 18:19 UTC covering SECURITY, DEFAULT"),
while the Windows 11 image had no RegBack directory at all and correctly stayed silent.
Provenance
- Reviewed head:
69601af46f9524ee9c41d6bd80d9e09f278c5e9d - Final tested head:
5c967e1 - Review prompt:
PR-Review-Agent.md, SHA2565451BA5C325F61E34FE63B9AF99322A17D70E55210CAF23F5CE27E0F839D8F70 - Counts derived from the report tables: 0 Criticals, 5 Warnings (8 Info items not itemised here)
- The original agent report is tied to head
69601af4and is historical: the code has changed since. - Quality gates on the final head: PowerShell 7 and 5.1 parse clean, PSSAnalyzer at parity with the
reviewed head, repository pre-commit hook passing, CRLF line endings preserved.
Additional validation — the recoverable case, on Gen1 / MBRThe run reported above is deliberately unrecoverable: it deletes the Break: 64 KB of
Detect, with the default
Repair, then re-detect, then boot:
Two things are worth calling out. This is the run that actually exercises W1. With the logs present, the fixed Two hives were damaged and only one was reported, and that is the correct answer. DEFAULT is a Combined
|
Azure#149 landed win-fix-firewall-service at the same map.json slot this entry uses, so git saw two independent appends as one competing edit. Both entries are kept, upstream's first so it holds the position it already has on main. Catalog verified after the merge: 30 entries, no duplicate ids, the entry present. win-fix-registry-corruption.ps1 is byte-identical to the lab-validated head 5c967e1.
What this adds
win-fix-registry-corruption— a single repair scenario, 982 lines, plus its ownmap.jsonentry.Repairs corrupted or unloadable registry hives on an offline Windows disk.
The catalog entry a support engineer reads when choosing it:
How it works
Runs against the broken OS disk attached to a rescue VM by "az vm repair create". Every hive is validated, only the damaged ones are repaired, and the result is re-validated. A healthy disk produces no writes at all.
Detection, per hive: 1. The hive file is missing. 2. The hive file is 0 bytes. 3. The hive file does not start with the 'regf' signature. 4. Windows itself cannot parse the hive. This is the authoritative check: a scratch copy of the hive and its transaction logs is loaded with reg.exe, so a hive that is merely dirty is recovered by log replay and correctly reported as healthy, and the file on the offline disk is never modified by the check. 5. chkreg.exe reports repairable structural damage in a hive that still loads.
Parameters
detectOnlyallowRegBackhivewindowsDriveConventions followed
.\src\windows\common\setup\init.ps1and returns$STATUS_SUCCESSor$STATUS_ERROR.Write-Host.az vm run-commandkeeps only the last 4096 characters of the output stream, so a summary printed first is the first thing a long run loses.PSUseShouldProcessForStateChangingFunctionson an internal helper function. The script is invoked non-interactively byaz vm repair run, so-WhatIf/-Confirmon an internal function is unreachable; the reporting-only mode is thedetectOnlyparameter instead.Testing
Exercised through the
az vm repair run --previewproduct path against real Azure VMs: a lab VM is broken deliberately to create the fault, the repair is run against the attached disk from a rescue VM, and the VM is confirmed to boot and behave afterwards. Detection, restraint on a healthy image, and the repair itself are each checked.Series
Second wave of four independent scenario PRs, after #149, #150, #151 and #152. The shared helpers
these scripts dot-source are already merged in #143, #146 and #147. This PR adds no helper files and
changes no existing scenario; its only existing-file change is appending this run-id to
map.json,inserted into whatever upstream carries now so every existing entry is preserved byte for byte.