NtAlpcSendWaitReceivePort
Sendet eine ALPC-Nachricht auf einem Port und wartet optional auf eine Antwort oder die nächste eingehende Nachricht.
Prototyp
NTSTATUS NtAlpcSendWaitReceivePort( HANDLE PortHandle, ULONG Flags, PPORT_MESSAGE SendMessage, PALPC_MESSAGE_ATTRIBUTES SendMessageAttributes, PPORT_MESSAGE ReceiveMessage, PSIZE_T BufferLength, PALPC_MESSAGE_ATTRIBUTES ReceiveMessageAttributes, PLARGE_INTEGER Timeout );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| PortHandle | HANDLE | in | Handle auf einen ALPC-Port (Server- oder Client-Seite, Communication- oder Connection-Port). |
| Flags | ULONG | in | ALPC_MSGFLG_* (RELEASE_MESSAGE, SYNC_REQUEST, WAIT_USER_MODE, …) steuern Send/Receive-Semantik. |
| SendMessage | PPORT_MESSAGE | in | Optionale ausgehende Nachricht; NULL für reinen Receive. |
| SendMessageAttributes | PALPC_MESSAGE_ATTRIBUTES | in | Attribute der ausgehenden Nachricht (Handle / Context / View / Security). |
| ReceiveMessage | PPORT_MESSAGE | out | Empfängt die eingehende Antwort oder die nächste eingereihte Nachricht. |
| BufferLength | PSIZE_T | in/out | Eingangs Größe des ReceiveMessage-Puffers; ausgangs Größe der empfangenen Nachricht. |
| ReceiveMessageAttributes | PALPC_MESSAGE_ATTRIBUTES | in/out | Empfängt die an die eingehende Nachricht angehängten Attribut-Metadaten. |
| Timeout | PLARGE_INTEGER | in | Optionales Warte-Timeout (100-ns-Einheiten, negativ = relativ). NULL = unbegrenzt warten. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x88 | win10-1507 |
| Win10 1607 | 0x88 | win10-1607 |
| Win10 1703 | 0x89 | win10-1703 |
| Win10 1709 | 0x89 | win10-1709 |
| Win10 1803 | 0x8A | win10-1803 |
| Win10 1809 | 0x8A | win10-1809 |
| Win10 1903 | 0x8A | win10-1903 |
| Win10 1909 | 0x8A | win10-1909 |
| Win10 2004 | 0x8C | win10-2004 |
| Win10 20H2 | 0x8C | win10-20h2 |
| Win10 21H1 | 0x8C | win10-21h1 |
| Win10 21H2 | 0x8C | win10-21h2 |
| Win10 22H2 | 0x8C | win10-22h2 |
| Win11 21H2 | 0x8C | win11-21h2 |
| Win11 22H2 | 0x8C | win11-22h2 |
| Win11 23H2 | 0x8C | win11-23h2 |
| Win11 24H2 | 0x8E | win11-24h2 |
| Server 2016 | 0x88 | winserver-2016 |
| Server 2019 | 0x8A | winserver-2019 |
| Server 2022 | 0x8C | winserver-2022 |
| Server 2025 | 0x8E | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 8E 00 00 00 mov eax, 0x8E 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
Undokumentierte Hinweise
`NtAlpcSendWaitReceivePort` ist das Arbeitstier von ALPC: jeder RPC-Aufruf über `ncalrpc` mündet in einen oder mehrere Aufrufe dieses Syscalls. Er implementiert vier High-Level-Patterns — nur senden, nur empfangen, Request/Reply (synchron), Reply/Wait-Next (Server-Loop) — gewählt über Flags und welche von SendMessage/ReceiveMessage non-NULL sind. Auf den `PORT_MESSAGE`-Header folgt ein optionaler `ALPC_MESSAGE_ATTRIBUTES`-Block, der duplizierte Handles, einen Security-Context-Blob oder eine Shared-Section-View tragen kann — genau diese Attribute machen ALPC zu einem so lukrativen LPE-Ziel.
Häufige Malware-Nutzung
Zwei klar getrennte Missbrauchsmuster: 1. **LPE-Exploitation**: jede ALPC-LPE-Kette endet in einem gefälschten `SendMessage`, der einen verwundbaren Code-Pfad auf Server-Seite triggert. CVE-2018-8440 schickte ein gefälschtes `ALPC_MESSAGE_ATTRIBUTE_HANDLE` an den Task-Scheduler-ALPC-Port; CVE-2019-1322 (SVCMOVER) missbrauchte services.exe über ALPC; mehrere PrintNightmare-PoCs erreichen den Print-Spooler-ALPC-Endpoint über diesen Syscall. 2. **Verdeckter IPC-C2**: Red-Team-Frameworks bauen gelegentlich Implant↔Implant-Kanäle auf einem unbenannten ALPC-Port (mit `NtAlpcCreatePort` erzeugt, per `NtAlpcConnectPort` verbunden), weil ALPC für Sysmon-Netzwerk-Telemetrie unsichtbar ist. Geringer Durchsatz, aber für Command-and-Control genug.
Erkennungsmöglichkeiten
Detektion pro Aufruf unpraktikabel — der Syscall feuert millionenfach pro Stunde auf einem aktiven Host. Hilfreiche Blickwinkel: ETW `Microsoft-Windows-Kernel-ALPC` (sofern aktiv) ordnet Nachrichten Port-Namen und PIDs zu; große `ALPC_MESSAGE_ATTRIBUTE_HANDLE`-Payloads aus nicht-privilegierten Prozessen an System-Dienste sind verdächtig; Korrelation mit dem ETW `Microsoft-Windows-RPC` (das *über* ALPC liegt) liefert die invokierte RPC-Interface-UUID — ein unerwartetes UUID aus einem Nicht-Microsoft-Binary ist das stärkste Signal. Die historischen ALPC-LPE-Bugs wurden allesamt post-disclosure durch Suche nach Handle-Attributen, die aus User-Context-Sendern auf SYSTEM-Objekte zeigten, gefunden.
Direkte Syscall-Beispiele
asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtAlpcSendWaitReceivePort (SSN 0x8E on Win11 24H2)
NtAlpcSendWaitReceivePort PROC
mov r10, rcx ; PortHandle
mov eax, 8Eh ; SSN — version-sensitive
syscall
ret
NtAlpcSendWaitReceivePort ENDPcServer receive-reply loop
// Minimal ALPC server pump — receive, dispatch, reply.
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS (NTAPI *pNtAlpcSendWaitReceivePort)(
HANDLE, ULONG, PVOID, PVOID, PVOID, PSIZE_T, PVOID, PLARGE_INTEGER);
void ServerPump(HANDLE hPort) {
pNtAlpcSendWaitReceivePort fn = (pNtAlpcSendWaitReceivePort)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtAlpcSendWaitReceivePort");
BYTE recv[0x1000]; SIZE_T cb;
BYTE reply[0x1000];
for (;;) {
cb = sizeof(recv);
// Pure receive: SendMessage = NULL.
if (fn(hPort, 0, NULL, NULL, recv, &cb, NULL, NULL) != 0) break;
// ... build reply in 'reply' ...
// Send reply, wait for next request: ALPC_MSGFLG_RELEASE_MESSAGE = 0x10000.
cb = sizeof(recv);
fn(hPort, 0x10000, reply, NULL, recv, &cb, NULL, NULL);
}
}cClient SYNC_REQUEST (sync RPC)
// Synchronous request/reply on an already-connected ALPC port.
#include <windows.h>
#include <winternl.h>
#define ALPC_MSGFLG_SYNC_REQUEST 0x20000
typedef NTSTATUS (NTAPI *pNtAlpcSendWaitReceivePort)(
HANDLE, ULONG, PVOID, PVOID, PVOID, PSIZE_T, PVOID, PLARGE_INTEGER);
NTSTATUS Call(HANDLE hPort, PVOID req, SIZE_T reqLen, PVOID rsp, PSIZE_T rspLen) {
pNtAlpcSendWaitReceivePort fn = (pNtAlpcSendWaitReceivePort)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtAlpcSendWaitReceivePort");
LARGE_INTEGER to; to.QuadPart = -5LL * 10000000LL; // 5 s
return fn(hPort, ALPC_MSGFLG_SYNC_REQUEST, req, NULL, rsp, rspLen, NULL, &to);
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20