> Windows Syscalls
ntoskrnl.exeT1559T1106

NtReplyWaitReceivePort

Primitive LPC côté serveur : répond atomiquement à la requête précédente et bloque pour la suivante.

Prototype

NTSTATUS NtReplyWaitReceivePort(
  HANDLE         PortHandle,
  PVOID         *PortContext,
  PPORT_MESSAGE  ReplyMessage,
  PPORT_MESSAGE  ReceiveMessage
);

Arguments

NameTypeDirDescription
PortHandleHANDLEinHandle vers le port serveur retourné par NtCreatePort ; le thread bloque sur la file de ce port.
PortContextPVOID*outOptionnel ; reçoit le pointeur de contexte par client stocké lors de NtAcceptConnectPort.
ReplyMessagePPORT_MESSAGEinRéponse optionnelle à envoyer pour la requête précédemment reçue (NULL au premier appel / pas de réponse en attente).
ReceiveMessagePPORT_MESSAGEoutTampon alloué par l'appelant recevant le prochain PORT_MESSAGE entrant ; doit être ≥ MaxMessageLength.

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070xBwin10-1507
Win10 16070xBwin10-1607
Win10 17030xBwin10-1703
Win10 17090xBwin10-1709
Win10 18030xBwin10-1803
Win10 18090xBwin10-1809
Win10 19030xBwin10-1903
Win10 19090xBwin10-1909
Win10 20040xBwin10-2004
Win10 20H20xBwin10-20h2
Win10 21H10xBwin10-21h1
Win10 21H20xBwin10-21h2
Win10 22H20xBwin10-22h2
Win11 21H20xBwin11-21h2
Win11 22H20xBwin11-22h2
Win11 23H20xBwin11-23h2
Win11 24H20xBwin11-24h2
Server 20160xBwinserver-2016
Server 20190xBwinserver-2019
Server 20220xBwinserver-2022
Server 20250xBwinserver-2025

Module noyau

ntoskrnl.exeNtReplyWaitReceivePort

APIs liées

NtReplyPortNtRequestWaitReplyPortNtCreatePortNtAcceptConnectPortNtAlpcSendWaitReceivePort

Stub du syscall

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

NtReplyWaitReceivePort est la *boucle principale serveur* canonique de LPC historique : elle livre atomiquement la réponse à la requête traitée à l'itération précédente et bloque sur la file entrante jusqu'à la requête suivante. Le paramètre out `PortContext` fournit le pointeur de contexte par client enregistré à NtAcceptConnectPort, permettant à un thread serveur de multiplexer un nombre quelconque de clients. Le SSN `0x0B` est gelé depuis Windows 10 1507 — encore plus bas que NtRequestWaitReplyPort car les routines de dispatch LPC originales étaient placées tôt dans la table.

Usage courant par les malwares

Un attaquant n'appellera NtReplyWaitReceivePort qu'après avoir monté un serveur LPC custom via NtCreatePort + NtAcceptConnectPort + NtCompleteConnectPort. Ce pattern est *extrêmement rare* en malware moderne — l'IPC pratique pour les implants passe par named pipes, ALPC ou sections partagées. Intérêt historique / académique uniquement : les rootkits user-mode pré-Vista utilisaient la boucle serveur LPC comme étage C2 intra-host à faible bruit. Tout processus non OS bloqué dans NtReplyWaitReceivePort sur Windows 10/11 mérite enquête.

Opportunités de détection

Pas de provider ETW dédié au LPC historique ; la détection requiert des callbacks kernel ou un hook syscall en driver. L'approche blue-team la plus solide est d'énumérer les objets `Port` vivants sous `\RPC Control` et `\Sessions\<n>\Windows` (SystemInformer / WinObj) et de marquer les propriétaires qui ne sont pas csrss, lsass, smss ou le sous-système RPC. Un thread user-mode bloqué en permanence dans `ntdll!NtReplyWaitReceivePort` sans binaire Microsoft correspondant sur sa pile est à fort signal.

Exemples de syscalls directs

asmx64 direct stub

; Direct syscall stub for NtReplyWaitReceivePort (SSN 0x0B, stable Win10 1507+)
NtReplyWaitReceivePort PROC
    mov  r10, rcx          ; PortHandle
    mov  eax, 0Bh          ; SSN
    syscall
    ret
NtReplyWaitReceivePort ENDP

cMinimal LPC server loop

// Skeleton of a custom LPC server. Error handling elided for brevity.
#include <windows.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI *pNtReplyWaitReceivePort)(
    HANDLE, PVOID*, PVOID /*PPORT_MESSAGE*/, PVOID /*PPORT_MESSAGE*/);

void ServerLoop(HANDLE hServerPort) {
    BYTE reqBuf[0x100], replyBuf[0x100];
    PVOID ctx = NULL;
    BOOL  havePendingReply = FALSE;

    pNtReplyWaitReceivePort fn = (pNtReplyWaitReceivePort)GetProcAddress(
        GetModuleHandleA("ntdll.dll"), "NtReplyWaitReceivePort");

    for (;;) {
        NTSTATUS s = fn(hServerPort, &ctx,
                        havePendingReply ? replyBuf : NULL,
                        reqBuf);
        if (s < 0) break;
        // ... dispatch on reqBuf, populate replyBuf, set havePendingReply = TRUE ...
        havePendingReply = TRUE;
    }
}

rustFFI declaration via ntapi

// Cargo: ntapi = "0.4"
use ntapi::ntlpcapi::NtReplyWaitReceivePort;
use winapi::shared::ntdef::{HANDLE, NTSTATUS, PVOID};

unsafe fn recv(port: HANDLE, ctx_out: *mut PVOID, reply: *mut u8, recv: *mut u8) -> NTSTATUS {
    NtReplyWaitReceivePort(port, ctx_out, reply as *mut _, recv as *mut _)
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20