Overview
The generator::direct policy generates syscall stubs that execute the syscall instruction (x64) or sysenter (x86) directly within the stub. This is the simplest and most performant approach.
Constants
Indicates whether this policy requires syscall gadgets from ntdll.dll static constexpr bool bRequiresGadget = false ;
Direct stubs do not require external gadgets
Method Signatures
Returns the size of the generated stub in bytes static constexpr size_t getStubSize ()
18 bytes on x64, 15 bytes on x86
Generates the syscall stub at the specified buffer static void generate (
uint8_t* pBuffer ,
uint32_t uSyscallNumber ,
void* /*pGadgetAddress*/
)
Pointer to the buffer where the stub will be written
The syscall number to embed in the stub
Unused (direct stubs don’t use gadgets)
Generated Shellcode
x64 Stub (18 bytes)
From syscall.hpp:314-322:
inline static constinit std ::array < uint8_t , 18 > arrShellcode =
{
0x 51 , // push rcx
0x 41 , 0x 5A , // pop r10
0x B8 , 0x 00 , 0x 00 , 0x 00 , 0x 00 , // mov eax, 0x00000000 (syscall number)
0x 0F , 0x 05 , // syscall
0x 48 , 0x 83 , 0x C4 , 0x 08 , // add rsp, 8
0x FF , 0x 64 , 0x 24 , 0x F8 // jmp qword ptr [rsp-8]
};
Assembly breakdown:
push rcx; pop r10 - Move the first parameter from RCX to R10 (Windows x64 calling convention for syscalls)
mov eax, <syscall_number> - Load the syscall number into EAX
syscall - Execute the system call
add rsp, 8 - Clean up the stack (remove the pushed RCX)
jmp qword ptr [rsp-8] - Return to the caller
x86 Stub (15 bytes)
From syscall.hpp:324-330:
inline static constinit std ::array < uint8_t , 15 > arrShellcode =
{
0x B8 , 0x 00 , 0x 00 , 0x 00 , 0x 00 , // mov eax, 0x00000000 (syscall number)
0x 89 , 0x E2 , // mov edx, esp
0x 64 , 0x FF , 0x 15 , 0x C0 , 0x 00 , 0x 00 , 0x 00 , // call dword ptr fs:[0xC0]
0x C3 // ret
};
Assembly breakdown:
mov eax, <syscall_number> - Load the syscall number
mov edx, esp - Save the stack pointer (required for sysenter)
call dword ptr fs:[0xC0] - Call the system service dispatcher (KiFastSystemCall)
ret - Return to caller
Source Code
From syscall.hpp:309-341:
struct direct
{
static constexpr bool bRequiresGadget = false ;
#if SYSCALL_PLATFORM_WINDOWS_64
inline static constinit std ::array < uint8_t , 18 > arrShellcode =
{
0x 51 , // push rcx
0x 41 , 0x 5A , // pop r10
0x B8 , 0x 00 , 0x 00 , 0x 00 , 0x 00 , // mov eax, syscall number
0x 0F , 0x 05 , // syscall
0x 48 , 0x 83 , 0x C4 , 0x 08 , // add rsp, 8
0x FF , 0x 64 , 0x 24 , 0x F8 // jmp qword ptr [rsp-8]
};
#elif SYSCALL_PLATFORM_WINDOWS_32
inline static constinit std ::array < uint8_t , 15 > arrShellcode =
{
0x B8 , 0x 00 , 0x 00 , 0x 00 , 0x 00 , // mov eax, syscall number
0x 89 , 0x E2 , // mov edx, esp
0x 64 , 0x FF , 0x 15 , 0x C0 , 0x 00 , 0x 00 , 0x 00 , // call fs:[0xC0]
0x C3 // ret
};
#endif
static void generate ( uint8_t* pBuffer , uint32_t uSyscallNumber , void* /*pGadgetAddress*/ )
{
std :: copy_n ( arrShellcode . data (), arrShellcode . size (), pBuffer);
if constexpr ( platform ::isWindows64)
* reinterpret_cast < uint32_t *> (pBuffer + 4 ) = uSyscallNumber;
else
* reinterpret_cast < uint32_t *> (pBuffer + 1 ) = uSyscallNumber;
}
static constexpr size_t getStubSize () { return arrShellcode . size (); }
};
Usage Example
#include <syscalls-cpp/syscall.hpp>
// Use the pre-defined type alias
SyscallSectionDirect syscallManager;
if ( ! syscallManager . initialize ()) {
std ::cerr << "Failed to initialize \n " ;
return 1 ;
}
// Or compose manually
using DirectManager = syscall ::Manager <
syscall :: policies :: allocator ::section,
syscall :: policies :: generator ::direct
> ;
DirectManager manager;
manager . initialize ();
// Invoke syscalls
PVOID pAddress = nullptr ;
SIZE_T uSize = 0x 1000 ;
manager . invoke < NTSTATUS > (
SYSCALL_ID ( "NtAllocateVirtualMemory" ),
NtCurrentProcess (),
& pAddress, 0 , & uSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
);
Characteristics
Performance Fastest approach—no indirection, gadget lookups, or exception handling overhead.
Simplicity Straightforward implementation with minimal complexity.
Portability Works on both x86 and x64 Windows platforms.
Detection Most easily detected by EDR solutions scanning for direct syscall instructions.
Trade-offs
Aspect Description Performance Excellent (no overhead) Stealth Low (contains syscall instruction) Complexity Minimal Platform Support x86 and x64 Use Case General purpose, performance-critical
While this is the fastest approach, it’s also the most detectable. EDR solutions can easily identify direct syscall instructions in user-mode code.
See Also
generator::gadget Gadget-based stub generation (x64 only)
generator::exception Exception-based stub generation
Direct Syscalls Example Complete working example