NtLoadEnclaveData
Copie un buffer aligné page (code ou données) depuis la mémoire hôte VTL0 vers la plage VTL1 d'une enclave avant initialisation.
Prototype
NTSTATUS NtLoadEnclaveData( HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, SIZE_T BufferSize, ULONG Protect, PVOID PageInformation, ULONG PageInformationLength, PSIZE_T NumberOfBytesWritten, PULONG EnclaveError );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle vers le processus hôte propriétaire de l'enclave. Typiquement NtCurrentProcess(). |
| BaseAddress | PVOID | in | Adresse VTL1 cible dans l'enclave où Buffer est copié. Doit être alignée page et dans la plage réservée de l'enclave. |
| Buffer | PVOID | in | Buffer source VTL0 (image d'enclave mappée ou page de données). Aligné page. |
| BufferSize | SIZE_T | in | Nombre d'octets à copier. Doit être un multiple de la taille de page. |
| Protect | ULONG | in | Protection finale des pages dans l'enclave (PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ — jamais WRITE+EXECUTE en VBS). |
| PageInformation | PVOID | in | Descripteur spécifique au type : ENCLAVE_LOAD_DATA_SGX (SECINFO + flags EADD) ou ENCLAVE_LOAD_DATA_VBS (type de page). |
| PageInformationLength | ULONG | in | Taille en octets du descripteur PageInformation. |
| NumberOfBytesWritten | PSIZE_T | out | Reçoit le nombre d'octets réellement écrits dans l'enclave (normalement égal à BufferSize en succès). |
| EnclaveError | PULONG | out | Reçoit le code d'erreur SGX EADD ou securekernel sur STATUS_ENCLAVE_FAILURE. |
IDs de syscalls par version de Windows
| Version de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1607 | 0xFA | win10-1607 |
| Win10 1703 | 0xFD | win10-1703 |
| Win10 1709 | 0xFE | win10-1709 |
| Win10 1803 | 0xFF | win10-1803 |
| Win10 1809 | 0x100 | win10-1809 |
| Win10 1903 | 0x101 | win10-1903 |
| Win10 1909 | 0x101 | win10-1909 |
| Win10 2004 | 0x106 | win10-2004 |
| Win10 20H2 | 0x106 | win10-20h2 |
| Win10 21H1 | 0x106 | win10-21h1 |
| Win10 21H2 | 0x107 | win10-21h2 |
| Win10 22H2 | 0x107 | win10-22h2 |
| Win11 21H2 | 0x10C | win11-21h2 |
| Win11 22H2 | 0x10D | win11-22h2 |
| Win11 23H2 | 0x10D | win11-23h2 |
| Win11 24H2 | 0x10F | win11-24h2 |
| Server 2016 | 0xFA | winserver-2016 |
| Server 2019 | 0x100 | winserver-2019 |
| Server 2022 | 0x10B | winserver-2022 |
| Server 2025 | 0x10F | winserver-2025 |
Module noyau
APIs liées
Stub du syscall
4C 8B D1 mov r10, rcx B8 0F 01 00 00 mov eax, 0x10F 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
Notes non documentées
`NtLoadEnclaveData` est l'appel d'**ingestion page-par-page** qui tourne *entre* `NtCreateEnclave` et `NtInitializeEnclave`. Sur SGX il se transforme en `EADD` pour chaque page source ; sur VBS le Secure Kernel `IumLoadEnclaveData` copie le buffer source à travers la frontière VTL et estampille la mesure de la page destination dans le hash courant de l'enclave. La protection passée ici est la protection *finale* — l'enclave ne peut plus la remapper plus tard (aucun `mprotect` par page n'existe en VTL1). Pour une DLL d'enclave, `vertdll.dll` fait un appel par section `IMAGE_SCN_*` : un PAGE_READONLY pour `.rdata`, un PAGE_EXECUTE_READ pour `.text`, un PAGE_READWRITE pour `.data`. Tous les loads doivent précéder `NtInitializeEnclave` ; après, l'appel retourne `STATUS_ENCLAVE_NOT_TERMINATED`.
Usage courant par les malwares
Côté offensif, c'est l'appel qui *place physiquement du code en VTL1*. La barrière à l'abus est que les octets chargés seront mesurés dans le hash d'enclave et ce hash doit correspondre à ce que `IMAGE_ENCLAVE_CONFIG` permet à l'init. Deux leviers réels : (1) **enclave signée Microsoft avec données contrôlées par l'attaquant** : charger des pages de données bénignes dont le contenu est interprété comme des commandes par le code signé — équivalent à du ROP dans l'enclave ; (2) **loader de recherche** : les travaux Yuste / Soriano-Salvador chargent un *stub* d'enclave signé dont le 'code' est en fait un mini-dispatcher acceptant un pointeur shellcode au moment de `NtCallEnclave` — le payload malveillant n'est jamais mesuré dans MRENCLAVE, seul le dispatcher l'est. Les défenseurs ne peuvent pas inspecter les octets chargés a posteriori — la mémoire VTL1 est opaque à VTL0.
Opportunités de détection
Le burst de loads pré-init est très caractéristique : dizaines à centaines d'appels `NtLoadEnclaveData` rapprochés sur la même base d'enclave, suivis d'`NtInitializeEnclave`. Le provider ETW `Microsoft-Windows-Kernel-Memory` émet un event par load. Les EDR qui hookent le pattern syscall côté hôte peuvent flaguer la séquence trivialement ; le problème dur est l'attribution — quelle DLL est chargée dans l'enclave. Le meilleur levier défenseur est la *source fichier* : le module hôte qui effectue les loads aura un répertoire `IMAGE_ENCLAVE_CONFIG` nommant la DLL d'enclave par hash d'image. Sysmon Event ID 7 (ImageLoad) côté hôte montrera le mapping PE de la DLL d'enclave avant les syscalls de load — coupler les deux pour identification rétroactive.
Exemples de syscalls directs
cVBS enclave bring-up (step 2/3 — load image)
// Walk the PE sections of a signed enclave DLL and copy them into the enclave.
// (Helper for the create -> load -> initialize -> call sequence.)
#include <windows.h>
BOOL LoadEnclaveImage(PVOID enclaveBase, PVOID peImage, SIZE_T peSize) {
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)peImage;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((BYTE*)peImage + dos->e_lfanew);
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++) {
ULONG prot = PAGE_READONLY;
if (sec->Characteristics & IMAGE_SCN_MEM_EXECUTE) prot = PAGE_EXECUTE_READ;
else if (sec->Characteristics & IMAGE_SCN_MEM_WRITE) prot = PAGE_READWRITE;
PVOID dst = (BYTE*)enclaveBase + sec->VirtualAddress;
PVOID src = (BYTE*)peImage + sec->PointerToRawData;
SIZE_T size = ((sec->SizeOfRawData + 0xFFF) & ~0xFFFu);
if (!LoadEnclaveData(
GetCurrentProcess(),
dst, src, size, prot,
NULL, 0, // page info (VBS-specific, may be NULL)
NULL, // bytes written
NULL)) { // enclave error
return FALSE;
}
}
return TRUE;
}asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtLoadEnclaveData (SSN 0x10F on Win11 24H2 / Server 2025)
NtLoadEnclaveData PROC
mov r10, rcx ; ProcessHandle
mov eax, 10Fh ; SSN
syscall
ret
NtLoadEnclaveData ENDPMappings MITRE ATT&CK
Last verified: 2026-05-20