Skip to main content

Overview

SysWhispers4 includes syscall tables for Windows 7 through Windows 11 24H2. When Microsoft releases new Windows builds, syscall numbers can change. This guide shows how to update the tables using the included update_syscall_table.py script.
This is only necessary if you’re using --resolve static (embedded syscall numbers). Dynamic methods like FreshyCalls, Halo’s Gate, etc. automatically adapt to any Windows version.

Why Update Tables?

When Syscall Numbers Change

Microsoft occasionally changes syscall numbers between builds:
// Windows 11 22H2 (build 22621)
NtCreateThreadEx = 0xC6

// Windows 11 23H2 (build 22631)
NtCreateThreadEx = 0xC7  // ← Changed!
If your embedded table has the wrong number, the syscall will:
  • Call the wrong kernel function (undefined behavior)
  • Trigger an invalid syscall exception
  • Return STATUS_INVALID_SYSTEM_SERVICE

New Windows Builds

Recent builds not in the default table:
  • Windows 11 25H2 (build 26200) - Released Q2 2026
  • Windows Server 2025 (build 26100) - Released late 2024
  • Future builds (use the update script to stay current)

Quick Start

Update x64 Table (Default)

cd SysWhispers4
python scripts/update_syscall_table.py
Output:
  [~] Fetching: https://raw.githubusercontent.com/j00ru/windows-syscalls/master/x64/csv/nt.csv
  [+] Written 183 functions (x64) → data/syscalls_nt_x64.json
  [+] Syscall table update complete.

Update Both x64 and x86

python scripts/update_syscall_table.py --arch x64,x86
Output:
  [~] Fetching: https://raw.githubusercontent.com/j00ru/windows-syscalls/master/x64/csv/nt.csv
  [+] Written 183 functions (x64) → data/syscalls_nt_x64.json
  [~] Fetching: https://raw.githubusercontent.com/j00ru/windows-syscalls/master/x86/csv/nt.csv
  [+] Written 231 functions (x86) → data/syscalls_nt_x86.json
  [+] Syscall table update complete.

Command-Line Options

Architecture Selection

# x64 only (default)
python scripts/update_syscall_table.py --arch x64

# x86 only
python scripts/update_syscall_table.py --arch x86

# Both
python scripts/update_syscall_table.py --arch x64,x86

Custom Output Path

# Write to custom location instead of data/
python scripts/update_syscall_table.py --out custom_syscalls.json

Filter Specific Functions

# Only update specific functions (useful for testing)
python scripts/update_syscall_table.py \
    --functions NtAllocateVirtualMemory,NtCreateThreadEx,NtWriteVirtualMemory

Script Internals

Data Source

The script fetches CSV files from j00ru/windows-syscalls:
CSV_URLS = {
    "x64": "https://raw.githubusercontent.com/j00ru/windows-syscalls/master/x64/csv/nt.csv",
    "x86": "https://raw.githubusercontent.com/j00ru/windows-syscalls/master/x86/csv/nt.csv",
}
j00ru’s repository is the authoritative source for Windows syscall research, maintained since Windows NT 3.1.

CSV Format (j00ru)

Example x64 CSV header:
Function Name,Windows 7 (SP1),Windows 10 (1507),Windows 10 (1903),Windows 11 and Server (11 24H2),...
NtAllocateVirtualMemory,24,24,24,24,...
NtCreateThreadEx,166,189,199,198,...
Each row = NT function, each column = Windows build.

Parsing Logic

def parse_joru_csv(csv_text: str) -> dict:
    reader = csv.reader(io.StringIO(csv_text))
    rows = list(reader)
    header = rows[0]  # Column headers = Windows versions
    
    result = {
        "_comment": "NT syscall numbers — generated by SysWhispers4",
        "_source": "https://github.com/j00ru/windows-syscalls",
        "_windows_builds": {},
    }
    
    # Map human-readable version strings to build keys
    for col in range(1, len(header)):
        version_str = header[col]  # e.g., "Windows 11 and Server (11 24H2)"
        build_key, display_label = parse_version(version_str)  # → ("26100", "Windows 11 24H2")
        result["_windows_builds"][build_key] = display_label
    
    # Parse function rows
    for row in rows[1:]:
        func_name = row[0]
        if not func_name.startswith("Nt"):
            continue  # Skip non-NT functions
        
        func_entry = {}
        for col, build_key in enumerate(build_keys, start=1):
            ssn_str = row[col].strip()
            if ssn_str and ssn_str not in ("", "n/a", "-"):
                ssn = int(ssn_str, 16) if ssn_str.startswith("0x") else int(ssn_str)
                func_entry[build_key] = ssn
        
        result[func_name] = func_entry
    
    return result

Version Mapping

The script maps j00ru’s human-readable column headers to short build keys:
VER_MAP = {
    "Windows 7 (SP1)": ("7_sp1", "Windows 7 SP1"),
    "Windows 10 (1507)": ("10240", "Windows 10 1507 (build 10240)"),
    "Windows 10 (1903)": ("18362", "Windows 10 1903 (build 18362)"),
    "Windows 11 and Server (11 24H2)": ("26100", "Windows 11 24H2 (build 26100)"),
    "Windows 11 and Server (Server 2025)": ("26100_srv", "Windows Server 2025 (build 26100)"),
    # ... full list in source
}
Why use build numbers instead of version names?
  • Consistent across x64/x86
  • Unambiguous (“22H2” exists for both Win10 and Win11)
  • Easier to match against RtlGetVersion() output

Output Format

JSON Structure

{
  "_comment": "NT syscall numbers — generated by SysWhispers4/scripts/update_syscall_table.py",
  "_source": "https://github.com/j00ru/windows-syscalls",
  "_format": "FunctionName -> { build_key -> decimal_ssn }",
  "_windows_builds": {
    "7_sp1": "Windows 7 SP1",
    "10240": "Windows 10 1507 (build 10240)",
    "18362": "Windows 10 1903 (build 18362)",
    "22000": "Windows 11 21H2 (build 22000)",
    "26100": "Windows 11 24H2 (build 26100)",
    "26100_srv": "Windows Server 2025 (build 26100)"
  },
  "NtAllocateVirtualMemory": {
    "7_sp1": 24,
    "10240": 24,
    "18362": 24,
    "22000": 24,
    "26100": 24
  },
  "NtCreateThreadEx": {
    "7_sp1": 166,
    "10240": 189,
    "18362": 199,
    "22000": 197,
    "26100": 198
  },
  ...
}

Metadata Keys

KeyPurpose
_commentDescribes file origin
_sourceLink to j00ru’s repository
_formatExplains data structure
_windows_buildsMaps build keys to human-readable labels
Metadata keys are ignored during generation (filtered by key.startswith("_") check).

How SysWhispers4 Uses the Table

Static Resolution (--resolve static)

# In generator.py
def gen_ssn_table(self):
    build_key = self.get_target_build_key()  # e.g., "22000" for Win11 21H2
    ssn_table = load_syscall_table(self.arch)  # Load data/syscalls_nt_x64.json
    
    code = "DWORD SW4_SsnTable[] = {\n"
    for func in self.functions:
        if func.name in ssn_table and build_key in ssn_table[func.name]:
            ssn = ssn_table[func.name][build_key]
            code += f"    {ssn},  // {func.name}\n"
        else:
            code += f"    0xFFFFFFFF,  // {func.name} (unavailable)\n"
    code += "};\n"
    return code
Generated code:
DWORD SW4_SsnTable[] = {
    24,   // NtAllocateVirtualMemory
    198,  // NtCreateThreadEx
    58,   // NtWriteVirtualMemory
    // ...
};

Runtime Build Detection

To support multiple Windows versions with one binary, use dynamic resolution instead:
# Instead of static:
python syswhispers.py --preset common --resolve static

# Use FreshyCalls (works on any version):
python syswhispers.py --preset common --resolve freshycalls
Why: FreshyCalls sorts exports by virtual address — the sorted index is the syscall number. Works on:
  • Windows 7 → 11 (any build)
  • Future versions (no table update needed)

Workflow: Updating for New Windows Release

Scenario: Windows 11 25H2 Just Released

Step 1: Wait for j00ru to update his repository j00ru typically updates within 1-2 weeks of a new build release. Check:
# Visit j00ru's repo
https://github.com/j00ru/windows-syscalls

# Look for new CSV columns
https://github.com/j00ru/windows-syscalls/blob/master/x64/csv/nt.csv
Step 2: Run the update script
cd SysWhispers4
python scripts/update_syscall_table.py --arch x64,x86
Step 3: Verify new build in output
# Check data/syscalls_nt_x64.json
cat data/syscalls_nt_x64.json | jq '._windows_builds'

# Should show new entry:
{
  ...
  "26200": "Windows 11 25H2 (build 26200)"
}
Step 4: Update version mapping (if needed) If the script doesn’t recognize the new build name, add it to VER_MAP in update_syscall_table.py:
VER_MAP = {
    # ... existing entries
    "Windows 11 and Server (11 25H2)": ("26200", "Windows 11 25H2 (build 26200)"),
}
Step 5: Test generation
python syswhispers.py --preset common --resolve static

# Verify generated code
grep -A 5 "SW4_SsnTable" SW4Syscalls.c
Step 6: Commit changes
git add data/syscalls_nt_x64.json data/syscalls_nt_x86.json
git commit -m "Update syscall tables for Windows 11 25H2 (build 26200)"

Troubleshooting

Script Fails to Fetch CSV

Error:
[!] Failed to fetch https://raw.githubusercontent.com/...: URLError
Solutions:
  1. Check internet connection
  2. Verify j00ru’s repo is accessible (not moved/deleted)
  3. Check firewall/proxy settings
  4. Use --out to write to accessible location

Unknown Build in CSV

Warning:
Unknown version string: "Windows 12 (Beta)"
Solution: Add to VER_MAP in update_syscall_table.py:
VER_MAP = {
    # ...
    "Windows 12 (Beta)": ("27000", "Windows 12 Beta (build 27000)"),
}

SSN Mismatch After Update

Symptom: Binary crashes with STATUS_INVALID_SYSTEM_SERVICE after regenerating stubs. Diagnosis:
# Check what build number the system reports
powershell "[System.Environment]::OSVersion.Version"

# Example output: 10.0.26100.0
# Build number = 26100

# Verify SSN table has entry for 26100
cat data/syscalls_nt_x64.json | jq '.NtAllocateVirtualMemory["26100"]'
Fix:
  • If missing, run update_syscall_table.py again
  • If present, verify you’re using the correct build key in generator logic

Alternative: Manual Table Creation

Use WinDbg to Extract SSNs

kd> u ntdll!NtAllocateVirtualMemory L5
ntdll!NtAllocateVirtualMemory:
00007ffe`12345678 4c8bd1          mov     r10,rcx
00007ffe`1234567b b818000000      mov     eax,18h     ← SSN = 0x18 (24 decimal)
00007ffe`12345680 f604250803fe7f01 test byte ptr [SharedUserData+0x308],1
00007ffe`12345688 7503            jne     ntdll!NtAllocateVirtualMemory+0x15
00007ffe`1234568a 0f05            syscall
Extract for all functions:
kd> .foreach (func {!for_each_function ntdll Nt*}) { u ntdll!${func} L3; .echo ${func} }

Manual JSON Entry

{
  "NtAllocateVirtualMemory": {
    "26100": 24,   // ← Manually add new build
    "22000": 24,
    "18362": 24
  }
}
When to use:
  • j00ru hasn’t updated yet
  • Testing on preview/insider builds
  • Custom Windows builds

Best Practices

1. Use Dynamic Resolution for Production

# Instead of brittle static tables:
python syswhispers.py --preset common --resolve static

# Use version-agnostic methods:
python syswhispers.py --preset common --resolve freshycalls  # or recycled, from_disk
Why:
  • Works on any Windows version (past, present, future)
  • No table updates needed
  • More resilient against hooks

2. Update Tables Before Engagements

# Before red team operation, ensure tables are current:
python scripts/update_syscall_table.py --arch x64,x86

3. Version Control Your Tables

# Track changes to syscall numbers over time
git log -p data/syscalls_nt_x64.json

# See when NtCreateThreadEx changed:
git log -p -S "NtCreateThreadEx" data/syscalls_nt_x64.json

4. Automate Updates (CI/CD)

# GitHub Actions example
name: Update Syscall Tables
on:
  schedule:
    - cron: '0 0 * * 0'  # Weekly

jobs:
  update:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
      - run: python scripts/update_syscall_table.py --arch x64,x86
      - uses: stefanzweifel/git-auto-commit-action@v4
        with:
          commit_message: "chore: update syscall tables"

Table Coverage

Current Coverage (Default Tables)

OSBuilds Included
Windows 7SP1 (7601)
Windows 88.0 (9200), 8.1 (9600)
Windows 101507 (10240) → 22H2 (19045) — 14 builds
Windows 1121H2 (22000) → 24H2 (26100) — 4 builds
Windows Server2022 (20348), 2025 (26100)

x86-Specific (Legacy Coverage)

OSBuilds Included
Windows NT3.1, 3.5, 3.51, 4.0 (SP0-SP6)
Windows 2000SP0-SP4
Windows XPSP0-SP3
Windows Server 2003RTM, SP1, SP2, R2, R2 SP2
Windows VistaRTM, SP1, SP2
All modern builds same as x64

Security Considerations

Trust in j00ru’s Data

The update script trusts data from j00ru’s GitHub repository. Risks:
  • GitHub account compromise
  • Man-in-the-middle attack (if not using HTTPS)
  • Malicious data injection
Mitigations:
  1. Verify SSL certificate:
    # In update_syscall_table.py
    import ssl
    context = ssl.create_default_context()
    context.check_hostname = True
    context.verify_mode = ssl.CERT_REQUIRED
    
  2. Manual verification:
    # After update, spot-check a few known SSNs
    # NtAllocateVirtualMemory on Win11 22H2 should be 24 (0x18)
    cat data/syscalls_nt_x64.json | jq '.NtAllocateVirtualMemory["22621"]'
    
  3. Use local copy:
    # Clone j00ru's repo locally
    git clone https://github.com/j00ru/windows-syscalls
    
    # Modify script to read from local CSV
    python scripts/update_syscall_table.py --local-csv windows-syscalls/x64/csv/nt.csv
    

FAQ

Do I need to update tables for dynamic resolution methods?

No. FreshyCalls, Halo’s Gate, Tartarus’ Gate, RecycledGate, etc. automatically determine SSNs at runtime. Tables are only needed for --resolve static.

How often does Microsoft change syscall numbers?

Rarely within a major version. Typically only between major releases (Win10 → Win11) or significant feature updates. Example stability:
  • NtAllocateVirtualMemory = 24 (0x18) on every Windows 10 build (1507-22H2)
  • NtCreateThreadEx varies: 189 (1507) → 199 (1903) → 197 (Win11 21H2)

Can I contribute updated tables back to SysWhispers4?

Yes! Submit a PR with updated JSON files:
git checkout -b update-syscall-tables
python scripts/update_syscall_table.py --arch x64,x86
git add data/syscalls_nt_*.json
git commit -m "Update syscall tables for Windows 11 25H2"
git push origin update-syscall-tables
# Open PR on GitHub

What if a function doesn’t exist on an older Windows version?

The JSON will omit that build:
{
  "NtAllocateVirtualMemoryEx": {
    "19041": 118,  // Added in Win10 2004
    "22000": 119,
    // No entries for Win7, Win8 (function didn't exist)
  }
}
Generated code handles this:
if (build_key in table) {
    ssn = table[func_name][build_key];
} else {
    ssn = 0xFFFFFFFF;  // Marker for unavailable
}

Next Steps