Patching boot.wim with chntpw to no effect
I was building a custom Windows 11 install ISO and trying to do a clever registry tweak inside boot.wim to make in-place upgrade scenarios work the way I wanted. The patch ran cleanly, the wimlib re-packaging completed, the ISO booted. And nothing I patched mattered.
What was happening
The flow was:
- Mount
boot.wimviawimlib-imagex. - Open the SYSTEM hive inside the mounted image with
chntpw. - Edit a registry value.
- Unmount, re-pack, rebuild the ISO with
oscdimg.
chntpw reported success. The unmount and re-pack worked. The ISO built. Boot worked. But the registry value I'd changed was clearly back to its default in the running installer environment.
What I found
boot.wim is the Windows PE image used to run Setup. It contains its own registry hives. For an in-place upgrade scenario, Setup doesn't actually use those hives the way I'd assumed — it loads the existing OS's hives from the upgrade target and treats the PE hives as scratch space. My registry edit was being applied to a hive that gets thrown away after Setup hands off.
So the chntpw modification was effectively a no-op. The wimlib re-packaging step that ran after it was still doing useful work in the sense that the modified hive was inside the WIM, but the WIM doesn't propagate those changes to the running upgrade.
The fix
For the in-place upgrade case, there's nothing to fix at the boot.wim layer — the modification needs to happen post-install via either a setupcomplete.cmd or an autounattend.xml FirstLogonCommands. Those run in the context of the target OS where the change actually persists.
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<Order>1</Order>
<CommandLine>reg add "HKLM\SOFTWARE\Path\To\Key" /v MyValue /t REG_DWORD /d 1 /f</CommandLine>
<RequiresUserInput>false</RequiresUserInput>
</SynchronousCommand>
</FirstLogonCommands>
For a clean-install scenario (not upgrade), the same patch would have worked, because clean install does use the PE hives during Setup. So this is specifically a fix-the-wrong-layer problem for the in-place upgrade path.
What I'd do differently
When something "should" work and doesn't, the diagnostic question to ask is: is my change actually being read at the point I expected? You can spend a long time tweaking the change itself when the real issue is that the read path doesn't reach what you're modifying.
In this case I'd have saved time by running the upgrade once, immediately checking whether the value was present in the running OS, and only then planning the patch. The empirical "is the value actually read after upgrade?" test takes ten minutes; the patch-and-rebuild loop takes a couple of hours per iteration.