> Windows Syscalls
ntoskrnl.exeT1620T1574T1106

NtCreateEnclave

Allocates a new enclave (SGX or VBS/VTL1) inside a target process's address space.

Prototype

NTSTATUS NtCreateEnclave(
  HANDLE    ProcessHandle,
  PVOID    *BaseAddress,
  ULONG_PTR ZeroBits,
  SIZE_T    Size,
  SIZE_T    InitialCommitment,
  ULONG     EnclaveType,
  PVOID     EnclaveInformation,
  ULONG     EnclaveInformationLength,
  PSIZE_T   EnclaveError
);

Arguments

NameTypeDirDescription
ProcessHandleHANDLEinHandle to the host process. NtCurrentProcess() ((HANDLE)-1) is the only practical value.
BaseAddressPVOID*in/outPointer to the requested base. NULL lets the kernel pick a slot. Receives the chosen base on success.
ZeroBitsULONG_PTRinNumber of high-order zero bits in BaseAddress. Typically 0.
SizeSIZE_TinTotal enclave address-range size in bytes. Must be a power of two on SGX; page-aligned on VBS.
InitialCommitmentSIZE_TinBytes initially committed; remainder is reserved. Use 0 to reserve only.
EnclaveTypeULONGinENCLAVE_TYPE_SGX (1), ENCLAVE_TYPE_SGX2 (2) or ENCLAVE_TYPE_VBS (0x10). SGX is deprecated post-2024.
EnclaveInformationPVOIDinType-specific creation struct: ENCLAVE_CREATE_INFO_SGX or ENCLAVE_CREATE_INFO_VBS (owner ID + flags).
EnclaveInformationLengthULONGinSize in bytes of EnclaveInformation.
EnclaveErrorPSIZE_ToutReceives the hardware/securekernel-specific error code when NTSTATUS is STATUS_ENCLAVE_FAILURE.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 16070x9Ewin10-1607
Win10 17030xA1win10-1703
Win10 17090xA2win10-1709
Win10 18030xA3win10-1803
Win10 18090xA3win10-1809
Win10 19030xA4win10-1903
Win10 19090xA4win10-1909
Win10 20040xA8win10-2004
Win10 20H20xA8win10-20h2
Win10 21H10xA8win10-21h1
Win10 21H20xA9win10-21h2
Win10 22H20xA9win10-22h2
Win11 21H20xABwin11-21h2
Win11 22H20xACwin11-22h2
Win11 23H20xACwin11-23h2
Win11 24H20xAEwin11-24h2
Server 20160x9Ewinserver-2016
Server 20190xA3winserver-2019
Server 20220xABwinserver-2022
Server 20250xAEwinserver-2025

Kernel module

ntoskrnl.exeNtCreateEnclave

Related APIs

CreateEnclaveNtInitializeEnclaveNtLoadEnclaveDataNtCallEnclaveNtTerminateEnclaveIsEnclaveTypeSupportedDeleteEnclave

Syscall stub

4C 8B D1            mov r10, rcx
B8 AE 00 00 00      mov eax, 0xAE
F6 04 25 08 03 FE 7F 01   test byte ptr [0x7FFE0308], 1
75 03               jne short +3
0F 05               syscall
C3                  ret
CD 2E               int 2Eh
C3                  ret

Undocumented notes

`NtCreateEnclave` is the entry point into Windows' two enclave technologies: **Intel SGX** (`ENCLAVE_TYPE_SGX`/`SGX2`) and **Virtualization-Based Security enclaves** (`ENCLAVE_TYPE_VBS` = 0x10). SGX is effectively dead — Intel removed client SGX in 12th-gen Core, and Microsoft has been deprecating the API surface since 2024. VBS enclaves are the strategic primitive: a chunk of address space in a user-mode process whose code and data execute in **VTL1** (Virtual Trust Level 1), a more-privileged trust level managed by the **Secure Kernel** (securekernel.exe) on top of Hyper-V. From the host process' perspective the enclave is a normal range of memory; from the *kernel's* perspective it is opaque — VTL0 (where ntoskrnl runs) cannot read or write VTL1 pages. Creation requires the `SeEnableVirtualizationPrivilege` (held only by signed processes that opted in via `IMAGE_ENCLAVE_CONFIG`) and the eventual enclave image must be Authenticode-signed with a Microsoft-issued enclave-signing cert (or be a debug enclave on a test-signed machine). The host links against `vertdll.dll` which marshals the create/init/call/load sequence into the LSASS-adjacent `LsaIso.exe` trustlet model. Production users today: **Credential Guard**, **vTPM**, **Win32k mitigation policy attestation**, and some Microsoft research deployments.

Common malware usage

Enclaves are a **research-stage offensive primitive**, not yet a fixture of commodity malware. The appeal is structural: VTL0 kernel-mode EDR drivers (every Microsoft, CrowdStrike, SentinelOne, Defender driver) **cannot read VTL1 memory** — Hyper-V refuses the mapping. So shellcode hosted in an enclave is invisible to kernel-mode AV scans, kernel callbacks fire after the fact only on the *side effects* of enclave execution. Notable public work: **Schenk & Park (Black Hat USA 2022) — "A Dirty Little History: Bypassing Spectre Hardware Mitigations"** touched enclave isolation; **Yuste / Soriano-Salvador (Recon 2023)** demonstrated a VBS-enclave loader for arbitrary unsigned shellcode by abusing a signed loader stub; **TrustedSec** and **MDSec** have public PoCs. The practical barrier is the *signing requirement* — abuse currently requires either (a) a stolen Microsoft enclave-signing key, (b) a code-signing-cert-private trustlet vulnerability, or (c) a Microsoft-signed enclave loader with a memory-corruption bug (the most realistic path). No widely-attributed in-the-wild family uses enclaves yet.

Detection opportunities

Enclave creation is **near-zero on a normal endpoint** — that is the entire detection strategy. ETW provider `Microsoft-Windows-Kernel-Memory` emits events on VBS enclave creation; `Microsoft-Windows-Hyper-V-Worker` shows VTL1 activity. On Win11 24H2, `Microsoft-Windows-Security-Mitigations/KernelMode` includes enclave lifecycle. Practical hunt: list processes that hold the `SeEnableVirtualizationPrivilege` access right, cross-reference against `IMAGE_ENCLAVE_CONFIG` directory presence in their loaded modules, and alert on anything that is **not** `lsass.exe`, `LsaIso.exe`, `securekernel.exe` peers, or known Microsoft trustlets. EDR vendors do *not* see enclave-internal code execution, but they do see the host process' `NtCreateEnclave` / `NtLoadEnclaveData` / `NtInitializeEnclave` syscall sequence — hook those (or scrape kernel-syscall ETW) and treat the chain as a high-fidelity signal outside of `lsass`. Defender uses the Hyper-V code-integrity layer (HVCI) to refuse unsigned enclave images; never disable HVCI in a managed environment.

Direct syscall examples

cVBS enclave bring-up (step 1/3 — create)

// Minimal VBS enclave creation skeleton. Requires:
//   * Host binary compiled with /ENCLAVE flag and an IMAGE_ENCLAVE_CONFIG directory
//   * Enclave DLL Authenticode-signed with a Microsoft-issued enclave cert
//   * SeEnableVirtualizationPrivilege available (granted to host by signing policy)
// See NtInitializeEnclave + NtCallEnclave for the rest of the lifecycle.
#include <windows.h>
#include <ntsecapi.h>

typedef struct _ENCLAVE_CREATE_INFO_VBS {
    ULONG Flags;           // 0 or ENCLAVE_VBS_FLAG_DEBUG
    UCHAR OwnerID[32];     // 32-byte owner identity
} ENCLAVE_CREATE_INFO_VBS;

PVOID CreateVbsEnclave(SIZE_T size) {
    ENCLAVE_CREATE_INFO_VBS info = {0};
    info.Flags = ENCLAVE_VBS_FLAG_DEBUG; // omit in production
    // OwnerID can be left zero for a single-owner test enclave
    PVOID base = CreateEnclave(
        GetCurrentProcess(),
        NULL,                  // let kernel choose base
        size,
        0,                     // initial commit = 0
        ENCLAVE_TYPE_VBS,
        &info, sizeof(info),
        NULL);                 // enclaveError
    if (!base) {
        DWORD ec = GetLastError(); // NTSTATUS-mapped
        return NULL;
    }
    return base;
}

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtCreateEnclave (SSN 0xAE on Win11 24H2 / Server 2025)
; Note: enclave SSNs drift across builds — resolve dynamically for portable code.
NtCreateEnclave PROC
    mov  r10, rcx          ; ProcessHandle
    mov  eax, 0AEh         ; SSN
    syscall
    ret
NtCreateEnclave ENDP

rustwindows-sys CreateEnclave wrapper

// Cargo: windows-sys = { version = "0.59", features = [
//     "Win32_System_Threading", "Win32_Foundation", "Win32_System_Memory"
// ] }
use std::ptr::{null, null_mut};
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::Threading::GetCurrentProcess;

#[repr(C)]
struct EnclaveCreateInfoVbs {
    flags: u32,
    owner_id: [u8; 32],
}

extern "system" {
    fn CreateEnclave(
        process: HANDLE, base: *const u8, size: usize, initial_commit: usize,
        ty: u32, info: *const core::ffi::c_void, info_len: u32, err: *mut u32,
    ) -> *mut core::ffi::c_void;
}

const ENCLAVE_TYPE_VBS: u32 = 0x10;
const ENCLAVE_VBS_FLAG_DEBUG: u32 = 1;

pub unsafe fn create_vbs_enclave(size: usize) -> Option<*mut core::ffi::c_void> {
    let info = EnclaveCreateInfoVbs { flags: ENCLAVE_VBS_FLAG_DEBUG, owner_id: [0u8; 32] };
    let mut enclave_err: u32 = 0;
    let base = CreateEnclave(
        GetCurrentProcess(), null(), size, 0,
        ENCLAVE_TYPE_VBS,
        &info as *const _ as *const _, core::mem::size_of::<EnclaveCreateInfoVbs>() as u32,
        &mut enclave_err,
    );
    if base.is_null() { None } else { Some(base) }
}

MITRE ATT&CK mappings

Last verified: 2026-05-20