4a. Direct Syscalls cpp
1. Direct Syscall for NtAllocateVirtualMemory
in C++ (x64 Windows)
#include <windows.h>
#include <iostream>
typedef LONG NTSTATUS;
typedef SIZE_T* PSIZE_T;
// Declare the syscall function
extern "C" NTSTATUS DirectSyscall(
HANDLE ProcessHandle,
PVOID* BaseAddress,
ULONG_PTR ZeroBits,
PSIZE_T RegionSize,
ULONG AllocationType,
ULONG Protect);
// Define the syscall function with proper 64-bit assembly
extern "C" NTSTATUS DirectSyscall(
HANDLE ProcessHandle,
PVOID* BaseAddress,
ULONG_PTR ZeroBits,
PSIZE_T RegionSize,
ULONG AllocationType,
ULONG Protect)
{
NTSTATUS status;
// Pass the value of RegionSize (dereferenced) directly to r8
SIZE_T regionSizeValue = *RegionSize;
// Ensure stack alignment (Windows x64 requires 16-byte alignment)
// We push one value (8 bytes), so we need to adjust rsp to align
__asm__ __volatile__ (
"subq $8, %%rsp \n\t" // Align stack to 16 bytes (rsp % 16 == 0)
"movq %1, %%r10 \n\t" // Move ProcessHandle to r10 (64-bit)
"movq %2, %%rcx \n\t" // BaseAddress to rcx
"movq %3, %%rdx \n\t" // ZeroBits to rdx
"movq %4, %%r8 \n\t" // RegionSize value to r8 (64-bit)
"movl %5, %%r9d \n\t" // AllocationType to r9d (32-bit, zero-extends to r9)
"movl %6, %%eax \n\t" // Protect to eax
"pushq %%rax \n\t" // Push Protect onto the stack (6th arg)
"movl $0x18, %%eax \n\t" // Syscall number for NtAllocateVirtualMemory
"syscall \n\t" // Execute syscall
"movl %%eax, %0 \n\t" // Store NTSTATUS return value
"addq $16, %%rsp \n\t" // Clean up the stack (8 for push, 8 for alignment)
: "=r" (status) // Output
: "r" (ProcessHandle), "r" (BaseAddress), "r" (ZeroBits), "r" (regionSizeValue),
"r" (AllocationType), "r" (Protect) // Inputs
: "rax", "rcx", "rdx", "r8", "r9", "r10", "r11", "memory" // Clobbered
);
return status;
}
int main() {
PVOID baseAddress = NULL;
SIZE_T regionSize = 0x1000; // 4KB page
NTSTATUS status;
std::cout << "[*] Calling NtAllocateVirtualMemory via direct syscall..." << std::endl;
status = DirectSyscall(
GetCurrentProcess(), // Current process handle
&baseAddress, // Pointer to base address
0, // ZeroBits
®ionSize, // Pointer to region size
MEM_COMMIT | MEM_RESERVE, // Allocation type
PAGE_READWRITE // Protection
);
if (status == 0) {
std::cout << "[+] Memory allocated at: 0x" << std::hex << baseAddress << std::endl;
} else {
std::cout << "[-] NtAllocateVirtualMemory failed with status: 0x" << std::hex << status << std::endl;
}
return 0;
}
.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "Build for Windows (64-bit)",
"type": "shell",
"command": "x86_64-w64-mingw32-g++",
"args": [
"-g",
"-Wall",
"-static",
"/home/tester/tools/cobaltstrike/payloads/cpp/a.cpp",
"-o",
"/home/tester/tools/cobaltstrike/payloads/cpp/a.exe"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": ["$gcc"],
"options": {
"cwd": "${workspaceFolder}"
}
}
]
}
.vscode/c_cpp_properties.json
{
"configurations": [
{
"name": "Linux",
"includePath": [
"/usr/include",
"/usr/local/include",
"/usr/share/mingw-w64/include"
],
"defines": [],
"compilerPath": "/usr/bin/x86_64-w64-mingw32-g++",
"cStandard": "c17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}
.vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Windows Executable with GDB (Wine)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/${input:executableName}.exe",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "Build for Windows (64-bit)",
"winePath": "/usr/bin/wine",
"windows": {
"MIMode": "gdb"
}
}
],
"inputs": [
{
"type": "promptString",
"id": "executableName",
"description": "Name of your executable",
"default": "alloc_memory"
}
]
}
2. How This Works
Step | Explanation |
---|---|
1. Prepare Parameters | Sets up parameters for NtAllocateVirtualMemory . |
2. Move ProcessHandle to R10 |
x64 syscalls require RCX → R10 . |
3. Set EAX = 0x18 (Syscall Number) |
Loads the syscall number for NtAllocateVirtualMemory . |
4. Execute syscall Instruction |
Directly transitions to kernel mode (Ring 0). |
5. Return Result | The syscall result (NTSTATUS) is returned to the program. |
6. Check Success or Failure | If NTSTATUS == 0 , memory was allocated. Otherwise, error code is printed. |
3. Compile and Run
Compile with MinGW-w64 (for x64)
x86_64-w64-mingw32-g++ -o alloc_memory.exe alloc_memory.cpp -Wall -static
Run the Executable in Windows
alloc_memory.exe
✔ Success Output:
[*] Calling NtAllocateVirtualMemory via direct syscall...
[+] Memory allocated at: 0x00000012345678
❌ Failure Output:
[*] Calling NtAllocateVirtualMemory via direct syscall...
[-] NtAllocateVirtualMemory failed with status: 0xC000000D