NtCreateEnclave
Asigna una nueva enclave (SGX o VBS/VTL1) en el espacio de direcciones de un proceso objetivo.
Prototipo
NTSTATUS NtCreateEnclave( HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits, SIZE_T Size, SIZE_T InitialCommitment, ULONG EnclaveType, PVOID EnclaveInformation, ULONG EnclaveInformationLength, PSIZE_T EnclaveError );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle al proceso anfitrión. NtCurrentProcess() ((HANDLE)-1) es el único valor práctico. |
| BaseAddress | PVOID* | in/out | Puntero a la base solicitada. NULL deja al kernel elegir. Recibe la base elegida en éxito. |
| ZeroBits | ULONG_PTR | in | Número de bits altos en cero en BaseAddress. Típicamente 0. |
| Size | SIZE_T | in | Tamaño total del rango de direcciones de la enclave en bytes. Potencia de dos en SGX; alineado a página en VBS. |
| InitialCommitment | SIZE_T | in | Bytes inicialmente confirmados; el resto se reserva. 0 para sólo reservar. |
| EnclaveType | ULONG | in | ENCLAVE_TYPE_SGX (1), ENCLAVE_TYPE_SGX2 (2) o ENCLAVE_TYPE_VBS (0x10). SGX está deprecado tras 2024. |
| EnclaveInformation | PVOID | in | Estructura de creación específica del tipo: ENCLAVE_CREATE_INFO_SGX o ENCLAVE_CREATE_INFO_VBS (owner ID + flags). |
| EnclaveInformationLength | ULONG | in | Tamaño en bytes de EnclaveInformation. |
| EnclaveError | PSIZE_T | out | Recibe el código de error hardware/securekernel cuando NTSTATUS es STATUS_ENCLAVE_FAILURE. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1607 | 0x9E | win10-1607 |
| Win10 1703 | 0xA1 | win10-1703 |
| Win10 1709 | 0xA2 | win10-1709 |
| Win10 1803 | 0xA3 | win10-1803 |
| Win10 1809 | 0xA3 | win10-1809 |
| Win10 1903 | 0xA4 | win10-1903 |
| Win10 1909 | 0xA4 | win10-1909 |
| Win10 2004 | 0xA8 | win10-2004 |
| Win10 20H2 | 0xA8 | win10-20h2 |
| Win10 21H1 | 0xA8 | win10-21h1 |
| Win10 21H2 | 0xA9 | win10-21h2 |
| Win10 22H2 | 0xA9 | win10-22h2 |
| Win11 21H2 | 0xAB | win11-21h2 |
| Win11 22H2 | 0xAC | win11-22h2 |
| Win11 23H2 | 0xAC | win11-23h2 |
| Win11 24H2 | 0xAE | win11-24h2 |
| Server 2016 | 0x9E | winserver-2016 |
| Server 2019 | 0xA3 | winserver-2019 |
| Server 2022 | 0xAB | winserver-2022 |
| Server 2025 | 0xAE | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
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
Notas no documentadas
`NtCreateEnclave` es el punto de entrada a las dos tecnologías de enclaves de Windows: **Intel SGX** (`ENCLAVE_TYPE_SGX`/`SGX2`) y las **enclaves de Virtualization-Based Security** (`ENCLAVE_TYPE_VBS` = 0x10). SGX está prácticamente muerta — Intel retiró SGX del Core de 12ª gen y Microsoft viene depreciando la API desde 2024. Las enclaves VBS son la primitiva estratégica: un trozo de espacio de direcciones en un proceso de modo usuario cuyo código y datos se ejecutan en **VTL1** (Virtual Trust Level 1), un nivel de confianza más privilegiado gestionado por el **Secure Kernel** (securekernel.exe) sobre Hyper-V. Desde la perspectiva del proceso anfitrión la enclave es un rango normal de memoria; desde la del *kernel* es opaca — VTL0 (donde corre ntoskrnl) no puede leer ni escribir páginas de VTL1. La creación requiere `SeEnableVirtualizationPrivilege` (sólo lo poseen procesos firmados que se afiliaron vía `IMAGE_ENCLAVE_CONFIG`) y la imagen final debe estar firmada Authenticode con un certificado de firma de enclave emitido por Microsoft (o ser una enclave de depuración en máquina test-signed). El anfitrión enlaza con `vertdll.dll`, que marshala la secuencia create/init/call/load al modelo trustlet vecino de LSASS (`LsaIso.exe`). Usuarios en producción hoy: **Credential Guard**, **vTPM**, **atestación de Win32k** y algunas implementaciones de investigación de Microsoft.
Uso común por malware
Las enclaves son una **primitiva ofensiva en fase de investigación**, todavía no un elemento fijo del malware comoditizado. El atractivo es estructural: los drivers EDR en VTL0 (Microsoft, CrowdStrike, SentinelOne, Defender) **no pueden leer memoria VTL1** — Hyper-V rechaza el mapeo. Por eso el shellcode alojado en una enclave es invisible a los escaneos AV en modo kernel; los callbacks del kernel se disparan sólo a posteriori, sobre los *efectos colaterales*. Trabajos públicos notables: **Schenk & Park (Black Hat USA 2022)** sobre aislamiento de enclave; **Yuste / Soriano-Salvador (Recon 2023)** demostraron un cargador de enclave VBS para shellcode no firmado abusando de un stub cargador firmado; **TrustedSec** y **MDSec** publicaron PoC. La barrera práctica es el *requisito de firma* — el abuso requiere hoy (a) una clave de enclave Microsoft robada, (b) una vulnerabilidad de trustlet con clave privada de code-signing, o (c) un cargador de enclave firmado por Microsoft con un bug de corrupción de memoria (la vía más realista). Ninguna familia ampliamente atribuida in-the-wild usa enclaves todavía.
Oportunidades de detección
La creación de enclave es **casi nula en un endpoint normal** — ésa es toda la estrategia de detección. El proveedor ETW `Microsoft-Windows-Kernel-Memory` emite eventos al crearse una enclave VBS; `Microsoft-Windows-Hyper-V-Worker` muestra actividad VTL1. En Win11 24H2, `Microsoft-Windows-Security-Mitigations/KernelMode` incluye el ciclo de vida de la enclave. Hunt práctico: listar procesos con el derecho `SeEnableVirtualizationPrivilege`, contrastar con la presencia de directorio `IMAGE_ENCLAVE_CONFIG` en sus módulos cargados y alertar sobre todo lo que **no** sea `lsass.exe`, `LsaIso.exe`, pares de `securekernel.exe` o trustlets Microsoft conocidos. Los EDR *no ven* la ejecución dentro de la enclave, pero sí ven la secuencia syscall `NtCreateEnclave` / `NtLoadEnclaveData` / `NtInitializeEnclave` del proceso anfitrión — hookéelos (o coseche ETW kernel-syscall) y trate la cadena como una señal de alta fidelidad fuera de `lsass`. Defender usa la capa HVCI para rechazar imágenes de enclave no firmadas; nunca deshabilite HVCI en un entorno gestionado.
Ejemplos de syscalls directos
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 ENDPrustwindows-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) }
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20