NtCreatePort
Creates a named server-side LPC port object — the legacy pre-ALPC IPC listener primitive.
Prototype
NTSTATUS NtCreatePort( PHANDLE PortHandle, POBJECT_ATTRIBUTES ObjectAttributes, ULONG MaxConnectionInfoLength, ULONG MaxMessageLength, ULONG MaxPoolUsage );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| PortHandle | PHANDLE | out | Receives the handle to the newly created server port object. |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | Object attributes; typically supplies an NT path like \RPC Control\<Name> as the port name. |
| MaxConnectionInfoLength | ULONG | in | Maximum size in bytes of connection-info payload exchanged at NtAcceptConnectPort time. |
| MaxMessageLength | ULONG | in | Maximum size of an inline LPC message; capped at PORT_MAXIMUM_MESSAGE_LENGTH (256 bytes). |
| MaxPoolUsage | ULONG | in | Upper bound on paged-pool usage for queued messages on this port. |
Syscall IDs by Windows version
| Windows version | Syscall ID | Build |
|---|---|---|
| Win10 1507 | 0xAB | win10-1507 |
| Win10 1607 | 0xAD | win10-1607 |
| Win10 1703 | 0xB0 | win10-1703 |
| Win10 1709 | 0xB1 | win10-1709 |
| Win10 1803 | 0xB2 | win10-1803 |
| Win10 1809 | 0xB2 | win10-1809 |
| Win10 1903 | 0xB3 | win10-1903 |
| Win10 1909 | 0xB3 | win10-1909 |
| Win10 2004 | 0xB7 | win10-2004 |
| Win10 20H2 | 0xB7 | win10-20h2 |
| Win10 21H1 | 0xB7 | win10-21h1 |
| Win10 21H2 | 0xB8 | win10-21h2 |
| Win10 22H2 | 0xB8 | win10-22h2 |
| Win11 21H2 | 0xBB | win11-21h2 |
| Win11 22H2 | 0xBC | win11-22h2 |
| Win11 23H2 | 0xBC | win11-23h2 |
| Win11 24H2 | 0xBE | win11-24h2 |
| Server 2016 | 0xAD | winserver-2016 |
| Server 2019 | 0xB2 | winserver-2019 |
| Server 2022 | 0xBA | winserver-2022 |
| Server 2025 | 0xBE | winserver-2025 |
Kernel module
Related APIs
Syscall stub
4C 8B D1 mov r10, rcx B8 BE 00 00 00 mov eax, 0xBE 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
NtCreatePort is the *server* side of the legacy **LPC** IPC mechanism (pre-Vista). It registers a named port object — typically under `\RPC Control\` — that clients reach via NtConnectPort. After accepting a connection with NtAcceptConnectPort and completing the handshake with NtCompleteConnectPort, the server enters its receive loop using NtReplyWaitReceivePort. Since Vista, the kernel exposes the richer **ALPC** API (NtAlpcCreatePort etc.) and Microsoft has migrated almost all in-box RPC traffic to ALPC. LPC remains in the Service Descriptor Table for backwards compatibility but is rarely used by anything shipping today.
Common malware usage
Modern offensive use of NtCreatePort is *uncommon*. The relevant historical pattern is a kernel- or user-mode implant that registers an LPC port name mimicking a legitimate kernel component (e.g. something resembling `\SeLsaCommandPort`) and uses it as a covert intra-host C2 channel that never touches Winsock or named-pipe APIs. Today an attacker who wants in-host IPC will almost always reach for ALPC, named pipes, mailslots or shared sections instead. Treat any non-Microsoft-signed process invoking NtCreatePort as anomalous.
Detection opportunities
There is no dedicated ETW provider for legacy LPC equivalent to `Microsoft-Windows-Kernel-ALPC`. Detection therefore relies on kernel callbacks (object-manager ObCreateObject hooks via ObRegisterCallbacks for type `Port`) or driver-level syscall hooking. SystemInformer and WinObj enumerate live port objects under `\RPC Control` and `\Sessions\<n>\Windows`. Any new port name that doesn't match a known Microsoft component (csrss, lsass, smss, RPC subsystem) is worth investigating.
Direct syscall examples
asmx64 direct stub
; Direct syscall stub for NtCreatePort (SSN 0xBE on Win11 24H2)
NtCreatePort PROC
mov r10, rcx ; PortHandle
mov eax, 0BEh ; SSN — verify per-build before deploying
syscall
ret
NtCreatePort ENDPcRegister a named LPC server port
// Create an LPC server port under \RPC Control.
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS (NTAPI *pNtCreatePort)(
PHANDLE, POBJECT_ATTRIBUTES, ULONG, ULONG, ULONG);
NTSTATUS CreateLpcPort(HANDLE *outHandle, PCWSTR ntName) {
UNICODE_STRING name;
OBJECT_ATTRIBUTES oa;
RtlInitUnicodeString(&name, ntName);
InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE, NULL, NULL);
pNtCreatePort fn = (pNtCreatePort)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtCreatePort");
return fn(outHandle, &oa, 0x100, 0x100, 0x2000);
}MITRE ATT&CK mappings
Last verified: 2026-05-20