ntqueueapcthreadex-ntdll-gadget-injection
ntqueueapcthreadex-ntdll-gadget-injection copied to clipboard
This novel way of using NtQueueApcThreadEx by abusing the ApcRoutine and SystemArgument[0-3] parameters by passing a random pop r32; ret gadget can be used for stealthy code injection.
🧪 NtQueueApcThreadEx NTDLL Gadget Injection
Introduction
This novel way of using NtQueueApcThreadEx by abusing the ApcRoutine and SystemArgument[0-3] parameters by passing a random pop r32; ret gadget can be used for stealthy code injection. Within this PoC, the gadget in this case is picked randomly from ntdll.dll's memory region which matches a specific pattern. This means the gadget returns into the shellcode.
- Allocate shellcode in target process. For best results, this should be used with the shellcode-plain-sight project.
- Find a random gadget in PE sections matching
IMAGE_SCN_CNT_CODEandIMAGE_SCN_MEM_EXECUTEwithinntdll.dllin memory. - Setup the
NtQueueApcThreadExcall,ApcRoutine= gadget address chosen randomly,SystemArgument= pointer to shellcode - The
NtQueueApcThreadExwill force the IP to the gadgetntdll.dll!<random pop r32; ret>, and return intoSystemArgument1 - ✨tada✨
There are hundreds gadgets which can be used inside ntdll.dll (from my build, at least) - making this tricky to detect.
Upon inspecting the call, unlike traditional APC injection, ApcRoutine will simply point to a legitimate address inside of ntdll.dll.
Visualization

Possible Detection Vectors
This is likely a bit tricky to detect. Upon inspecting calls to NtQueueApcThreadEx, check if any of the arguments point towards executable memory. Another possible detection avenue is to check if ApcRoutine points towards an address inside ntdll - however this is likely to trigger a lot of false positives.
This technique was originally detailed in Avast's Raspberry Robin writeup here.