Core Isolation Toggle Leaves Hyper-V Active in BCD
I toggled Core Isolation OFF in Windows Security. The UI showed it was off. My test-signed hypervisor driver still wouldn't load. sc start returned ERROR_INVALID_IMAGE_HASH. I spent hours checking my signing setup before realizing: toggling Core Isolation off doesn't remove Hyper-V from BCD. The hypervisor stays active, and active Hyper-V enforces HVCI even when the UI says it's disabled.
This is the kind of bug that makes you question your own sanity. Everything about the signing was correct. The certificate was in the right store. Test-signing was enabled in BCD. The driver binary was signed with the right certificate. But it wouldn't load. And the error message gave no useful information about why.
The symptom
I had a freshly built hypervisor driver, signed with my self-signed test certificate. Test-signing mode was enabled. I'd been loading this driver for weeks. Then one day, after I'd been experimenting with VBS spoofing, it stopped loading.
// trying to load my driver
sc create ow2hv binPath= C:\ow2-driver\target\ow2hv.sys type= kernel
sc start ow2hv
// result:
// [SC] StartService FAILED 577:
// Windows cannot verify the digital signature for this file.
// (ERROR_INVALID_IMAGE_HASH)
//
// but test-signing IS enabled:
bcdedit /enum
// testsigning Yes
//
// and Core Isolation shows OFF in Windows Security:
// Settings > Device Security > Core Isolation
// Memory Integrity: Off
Test-signing was on. Core Isolation said off. The certificate was valid. The driver was signed. But it wouldn't load. I spent two hours re-signing the driver with different options, checking certificate trust chains, verifying the signature with signtool verify. Everything checked out. The driver should have loaded.
The root cause: hypervisorlaunchtype
I finally ran bcdedit and actually read all the output instead of just checking testsigning:
// the full bcdedit output that revealed the problem
bcdedit /enum {current}
identifier {current}
device partition=C:
path \Windows\system32\winload.efi
hypervisorlaunchtype Auto
testsigning Yes
// ...
// hypervisorlaunchtype = Auto means:
// - Hyper-V loads on every boot
// - Hyper-V enforces HVCI (code integrity)
// - HVCI rejects test-signed drivers
// - regardless of what the Core Isolation UI shows
hypervisorlaunchtype = Auto. Hyper-V was active. When Hyper-V is active, it enforces HVCI at the hypervisor level. HVCI checks driver signatures against Microsoft's code integrity policy, and test-signed drivers aren't in that policy. It doesn't matter that testsigning = Yes is set -- HVCI overrides it because HVCI operates at a higher privilege level (the hypervisor) than the boot configuration (the OS).
The Core Isolation toggle in Windows Security only changes a registry key. It tells the Windows Security app to show "off" and tells the kernel not to use HVCI for its own purposes. But it doesn't remove Hyper-V from the boot configuration. Hyper-V is still there, still loaded, still enforcing its own code integrity checks.
The fix: manual BCD edit
The fix is one command:
// disable Hyper-V in BCD
bcdedit /set hypervisorlaunchtype off
// reboot required for the change to take effect
shutdown /r /t 0
// after reboot:
// bcdedit /enum {current}
// hypervisorlaunchtype Off
//
// sc start ow2hv
// [SC] StartService SUCCESS
//
// driver loads immediately
After the reboot with hypervisorlaunchtype = off, my driver loaded on the first try. No changes to the signing, no changes to the certificate, no changes to the driver code. Just removing Hyper-V from the boot path.
How I got into this state
I traced the timeline. While working on VBS spoofing (post 03), I had to enable Hyper-V temporarily to test my CPUID leaf responses against real Hyper-V output. I ran bcdedit /set hypervisorlaunchtype auto and rebooted. After testing, I went to Windows Security and toggled Core Isolation back off, thinking that would undo the change. It didn't.
The toggle only changes HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\EnableVirtualizationBasedSecurity. It does NOT touch the BCD store. The BCD hypervisorlaunchtype value is completely separate from the registry key. They're controlled by different UI elements and different command-line tools.
// the disconnect between UI and BCD
//
// Windows Security "Core Isolation" toggle:
// changes: HKLM\...\DeviceGuard\EnableVirtualizationBasedSecurity
// does NOT change: BCD hypervisorlaunchtype
//
// bcdedit /set hypervisorlaunchtype:
// changes: BCD hypervisorlaunchtype
// does NOT change: any registry key
//
// to actually disable all of it:
bcdedit /set hypervisorlaunchtype off // BCD
// AND toggle Core Isolation off in UI // registry
// AND reboot // apply BCD change
systeminfo doesn't help either. It shows "A hypervisor has been detected" when Hyper-V is active, but it shows that even when my blue-pill is running. And it shows "Hyper-V Requirements: A hypervisor has been detected" which doesn't distinguish between Hyper-V and my hypervisor. The only reliable way to check is bcdedit /enum and look at the hypervisorlaunchtype value directly.The bigger lesson
Windows has at least three separate mechanisms that control hypervisor-related behavior, and they don't coordinate with each other:
1. BCD hypervisorlaunchtype -- controls whether the bootloader loads Hyper-V. Binary: either Hyper-V loads or it doesn't.
2. DeviceGuard registry keys -- controls VBS/HVCI policy. Tells the kernel whether to use Hyper-V for code integrity enforcement. Doesn't affect whether Hyper-V itself is present.
3. Windows Features (optionalfeatures.exe) -- installs/uninstalls the Hyper-V platform. Can remove the binaries entirely, but BCD might still reference them.
Toggling any one of these without the others creates an inconsistent state. The UI says one thing, BCD says another, and the actual boot behavior follows BCD, not the UI. For anyone doing driver development or hypervisor research on Windows: always check bcdedit /enum. The UI lies.