> Windows Syscalls
ntoskrnl.exeT1068T1559T1106

NtAlpcCreatePort

Crée un port de connexion ALPC côté serveur que les clients peuvent atteindre via NtAlpcConnectPort.

Prototype

NTSTATUS NtAlpcCreatePort(
  PHANDLE               PortHandle,
  POBJECT_ATTRIBUTES    ObjectAttributes,
  PALPC_PORT_ATTRIBUTES PortAttributes
);

Arguments

NameTypeDirDescription
PortHandlePHANDLEoutReçoit le handle vers le nouveau port de connexion serveur.
ObjectAttributesPOBJECT_ATTRIBUTESinNomme le port dans le namespace objet (ex. \RPC Control\foo) et fournit un SECURITY_DESCRIPTOR.
PortAttributesPALPC_PORT_ATTRIBUTESinLimites et comportement du port (MaxMessageLength, MemoryBandwidth, MaxViewSize, SecurityQos, Flags).

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070x77win10-1507
Win10 16070x77win10-1607
Win10 17030x78win10-1703
Win10 17090x78win10-1709
Win10 18030x79win10-1803
Win10 18090x79win10-1809
Win10 19030x79win10-1903
Win10 19090x79win10-1909
Win10 20040x7Bwin10-2004
Win10 20H20x7Bwin10-20h2
Win10 21H10x7Bwin10-21h1
Win10 21H20x7Bwin10-21h2
Win10 22H20x7Bwin10-22h2
Win11 21H20x7Bwin11-21h2
Win11 22H20x7Bwin11-22h2
Win11 23H20x7Bwin11-23h2
Win11 24H20x7Dwin11-24h2
Server 20160x77winserver-2016
Server 20190x79winserver-2019
Server 20220x7Bwinserver-2022
Server 20250x7Dwinserver-2025

Module noyau

ntoskrnl.exeNtAlpcCreatePort

APIs liées

NtAlpcConnectPortNtAlpcAcceptConnectPortNtAlpcSendWaitReceivePortNtAlpcDisconnectPortRpcServerUseProtseqEpWRpcServerRegisterIfEx

Stub du syscall

4C 8B D1            mov r10, rcx
B8 7D 00 00 00      mov eax, 0x7D
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

`NtAlpcCreatePort` alloue le **port de connexion** côté serveur — l'objet d'écoute que les clients viseront ensuite avec `NtAlpcConnectPort`. Le handle retourné n'est *pas* encore un endpoint de communication ; c'est la socket bind. Chaque client accepté reçoit son propre port par-connexion via `NtAlpcAcceptConnectPort`. Les processus serveurs enregistrent typiquement le port sous `\RPC Control\<endpoint>` (pour RPC sur `ncalrpc`) ou `\Sessions\<n>\AppContainerNamedObjects\...` (pour IPC isolé AppContainer). L'implémentation noyau vit dans `AlpcpCreateConnectionPort` dans ntoskrnl.exe.

Usage courant par les malwares

Trois angles observés : 1. **Serveur RPC malveillant** : un implant expose un port `\RPC Control\<nom>` pour que des implants pairs émettent des commandes, souvent combiné avec un token de service ETW ou NDIS capturé pour faire croire à un contexte privilégié. 2. **Mise en place d'exploit** : chaque PoC ALPC LPE construit l'échafaudage déclencheur autour d'une paire d'appels `NtAlpcCreatePort` — un pour le serveur frauduleux, un pour le handshake mimé (ex. le truc *AlpcRpcGetUserName* de SandboxEscaper avant CVE-2018-8440). 3. **C2 peer-to-peer furtif** : un beacon en SYSTEM crée un port ALPC à DACL restreinte ; un second implant en contexte utilisateur s'y connecte. Le trafic reste intégralement noyau et n'atteint jamais la pile réseau.

Opportunités de détection

Énumérer les ports ALPC par processus (SystemInformer, `!alpc` dans WinDbg, ou `Get-WinEvent -ProviderName 'Microsoft-Windows-Kernel-ALPC'` si activé) révèle les processus non Microsoft hébergeant un port de connexion — population très restreinte sur un poste typique. Des DACL suspectes (port autorisant `Everyone`, ou port appartenant à un processus user-context nommé pour mimer un service RPC) sont de forts indicateurs. Combiner avec l'ETW `Microsoft-Windows-RPC` pour voir quelles interfaces sont enregistrées — un port ALPC sans interface mais qui accepte des connexions n'est par définition pas du RPC et mérite enquête.

Exemples de syscalls directs

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtAlpcCreatePort (SSN 0x7D on Win11 24H2)
NtAlpcCreatePort PROC
    mov  r10, rcx          ; PortHandle
    mov  eax, 7Dh          ; SSN — version-sensitive, resolve dynamically
    syscall
    ret
NtAlpcCreatePort ENDP

cMinimal LRPC server bind

// Create \RPC Control\demo_port as a server connection port.
#include <windows.h>
#include <winternl.h>

typedef struct _ALPC_PORT_ATTRIBUTES {
    ULONG Flags;
    SECURITY_QUALITY_OF_SERVICE SecurityQos;
    SIZE_T MaxMessageLength;
    SIZE_T MemoryBandwidth;
    SIZE_T MaxPoolUsage;
    SIZE_T MaxSectionSize;
    SIZE_T MaxViewSize;
    SIZE_T MaxTotalSectionSize;
    ULONG DupObjectTypes;
#ifdef _WIN64
    ULONG Reserved;
#endif
} ALPC_PORT_ATTRIBUTES, *PALPC_PORT_ATTRIBUTES;

typedef NTSTATUS (NTAPI *pNtAlpcCreatePort)(
    PHANDLE, POBJECT_ATTRIBUTES, PALPC_PORT_ATTRIBUTES);

HANDLE CreateServer(LPCWSTR name) {
    UNICODE_STRING us; RtlInitUnicodeString(&us, name);
    OBJECT_ATTRIBUTES oa;
    InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE, NULL, NULL);
    ALPC_PORT_ATTRIBUTES pa = {0};
    pa.MaxMessageLength = 0x1000;
    pa.SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
    pa.SecurityQos.ImpersonationLevel = SecurityImpersonation;
    pa.SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
    pa.SecurityQos.EffectiveOnly = FALSE;
    HANDLE hPort = NULL;
    pNtAlpcCreatePort fn = (pNtAlpcCreatePort)GetProcAddress(
        GetModuleHandleA("ntdll.dll"), "NtAlpcCreatePort");
    fn(&hPort, &oa, &pa);
    return hPort;
}

rustwindows-sys handle wrapper

// Cargo: windows-sys = "0.59", features = ["Wdk_Foundation", "Win32_Foundation"]
use std::ptr::null_mut;
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Wdk::Foundation::OBJECT_ATTRIBUTES;

// Phnt ALPC bindings are not in windows-sys today; resolve dynamically.
type FnCreate = unsafe extern "system" fn(
    *mut HANDLE, *const OBJECT_ATTRIBUTES, *const u8,
) -> i32;

pub unsafe fn create_alpc_port(
    name_obj_attrs: *const OBJECT_ATTRIBUTES,
    attrs: *const u8,
) -> HANDLE {
    let ntdll = libloading::Library::new("ntdll.dll").unwrap();
    let f: libloading::Symbol<FnCreate> = ntdll.get(b"NtAlpcCreatePort\0").unwrap();
    let mut h: HANDLE = 0;
    let _ = f(&mut h, name_obj_attrs, attrs);
    h
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20