Files
CyberSecurityUP 300ce4b597 fix(update_syscall_table): handle j00ru's human-readable CSV header format
j00ru/windows-syscalls updated both x64 and x86 CSVs to use human-readable
column headers ("Windows 10 (1903)", "Windows NT 4.0 (SP3)") instead of
the dotted version strings ("10.0.19041.1") the script assumed.

Changes:
- Replace _version_to_build() with _parse_header_col() that handles both
  the human-readable format and legacy dotted strings as a fallback
- Add VER_MAP: unified label→(build_key, display_label) covering all 82
  columns across x64 (XP SP1 → Win11 25H2) and x86 (NT 3.1 → Win10 22H2)
- Win10/Win11 columns use actual build numbers (19041, 22000, 26100…) for
  cross-arch consistency; legacy columns use descriptive keys (xp_sp2, 7_sp1)
- Add data/syscalls_nt_x86.json (513 Nt* functions, NT 3.1 → Win10 22H2)
- Refresh data/syscalls_nt_x64.json (506 functions, XP SP1 → Win11 25H2)
  with correct Win11 / Server 2022/2025 build keys

Fixes: ValueError: invalid literal for int() with base 10: 'Windows NT 3'

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 22:18:01 -03:00

259 lines
11 KiB
Python

#!/usr/bin/env python3
"""
SysWhispers4 — Syscall Table Updater
Fetches the latest NT syscall numbers from j00ru/windows-syscalls and
updates data/syscalls_nt_x64.json and data/syscalls_nt_x86.json.
Usage:
python scripts/update_syscall_table.py
python scripts/update_syscall_table.py --arch x86
python scripts/update_syscall_table.py --arch x64,x86
python scripts/update_syscall_table.py --out custom_table.json
Requirements:
Standard library only (no pip dependencies).
"""
from __future__ import annotations
import argparse
import csv
import io
import json
import re
import sys
from pathlib import Path
from typing import Dict, Optional, Tuple
from urllib.request import urlopen, Request
from urllib.error import URLError
# ---------------------------------------------------------------------------
# j00ru table URLs (raw CSV from GitHub)
# ---------------------------------------------------------------------------
JORU_BASE = "https://raw.githubusercontent.com/j00ru/windows-syscalls/master"
CSV_URLS = {
"x64": f"{JORU_BASE}/x64/csv/nt.csv",
"x86": f"{JORU_BASE}/x86/csv/nt.csv",
}
# ---------------------------------------------------------------------------
# Unified human-readable label → (short build_key, display_label)
# Covers every column seen in both x64 and x86 j00ru CSVs.
# Win10/Win11 entries use actual build numbers for cross-arch consistency.
# ---------------------------------------------------------------------------
VER_MAP: Dict[str, Tuple[str, str]] = {
# ---- x86-only: legacy ----
"Windows NT 3.x (3.1)": ("nt3.1", "Windows NT 3.1"),
"Windows NT 3.x (3.5)": ("nt3.5", "Windows NT 3.5"),
"Windows NT 3.x (3.51)": ("nt3.51", "Windows NT 3.51"),
"Windows NT 4.0 (SP0)": ("nt4.0", "Windows NT 4.0 RTM"),
"Windows NT 4.0 (SP1)": ("nt4.0_sp1", "Windows NT 4.0 SP1"),
"Windows NT 4.0 (SP2)": ("nt4.0_sp2", "Windows NT 4.0 SP2"),
"Windows NT 4.0 (SP3)": ("nt4.0_sp3", "Windows NT 4.0 SP3"),
"Windows NT 4.0 (SP3 TSE)": ("nt4.0_sp3_tse", "Windows NT 4.0 SP3 TSE"),
"Windows NT 4.0 (SP4)": ("nt4.0_sp4", "Windows NT 4.0 SP4"),
"Windows NT 4.0 (SP5)": ("nt4.0_sp5", "Windows NT 4.0 SP5"),
"Windows NT 4.0 (SP6)": ("nt4.0_sp6", "Windows NT 4.0 SP6"),
"Windows 2000 (SP0)": ("2000_sp0", "Windows 2000 RTM"),
"Windows 2000 (SP1)": ("2000_sp1", "Windows 2000 SP1"),
"Windows 2000 (SP2)": ("2000_sp2", "Windows 2000 SP2"),
"Windows 2000 (SP3)": ("2000_sp3", "Windows 2000 SP3"),
"Windows 2000 (SP4)": ("2000_sp4", "Windows 2000 SP4"),
# ---- shared x64/x86 ----
"Windows XP (SP0)": ("xp_sp0", "Windows XP RTM"),
"Windows XP (SP1)": ("xp_sp1", "Windows XP SP1"),
"Windows XP (SP2)": ("xp_sp2", "Windows XP SP2"),
"Windows XP (SP3)": ("xp_sp3", "Windows XP SP3"),
"Windows Server 2003 (SP0)": ("2003_sp0", "Windows Server 2003 RTM"),
"Windows Server 2003 (SP1)": ("2003_sp1", "Windows Server 2003 SP1"),
"Windows Server 2003 (SP2)": ("2003_sp2", "Windows Server 2003 SP2"),
"Windows Server 2003 (R2)": ("2003_r2", "Windows Server 2003 R2"),
"Windows Server 2003 (R2 SP2)": ("2003_r2_sp2", "Windows Server 2003 R2 SP2"),
"Windows Vista (SP0)": ("vista_sp0", "Windows Vista RTM"),
"Windows Vista (SP1)": ("vista_sp1", "Windows Vista SP1"),
"Windows Vista (SP2)": ("vista_sp2", "Windows Vista SP2"),
"Windows 7 (SP0)": ("7_sp0", "Windows 7 RTM"),
"Windows 7 (SP1)": ("7_sp1", "Windows 7 SP1"),
"Windows 8 (8.0)": ("8.0", "Windows 8 RTM"),
"Windows 8 (8.1)": ("8.1", "Windows 8.1 RTM"),
# Win10 → actual build numbers (consistent across x64 and x86)
"Windows 10 (1507)": ("10240", "Windows 10 1507 (build 10240)"),
"Windows 10 (1511)": ("10586", "Windows 10 1511 (build 10586)"),
"Windows 10 (1607)": ("14393", "Windows 10 1607 (build 14393)"),
"Windows 10 (1703)": ("15063", "Windows 10 1703 (build 15063)"),
"Windows 10 (1709)": ("16299", "Windows 10 1709 (build 16299)"),
"Windows 10 (1803)": ("17134", "Windows 10 1803 (build 17134)"),
"Windows 10 (1809)": ("17763", "Windows 10 1809 (build 17763)"),
"Windows 10 (1903)": ("18362", "Windows 10 1903 (build 18362)"),
"Windows 10 (1909)": ("18363", "Windows 10 1909 (build 18363)"),
"Windows 10 (2004)": ("19041", "Windows 10 2004 (build 19041)"),
"Windows 10 (20H2)": ("19042", "Windows 10 20H2 (build 19042)"),
"Windows 10 (21H1)": ("19043", "Windows 10 21H1 (build 19043)"),
"Windows 10 (21H2)": ("19044", "Windows 10 21H2 (build 19044)"),
"Windows 10 (22H2)": ("19045", "Windows 10 22H2 (build 19045)"),
# Win11 / Server (x64-only in j00ru CSVs)
"Windows 11 and Server (Server 2022)": ("20348", "Windows Server 2022 (build 20348)"),
"Windows 11 and Server (11 21H2)": ("22000", "Windows 11 21H2 (build 22000)"),
"Windows 11 and Server (11 22H2)": ("22621", "Windows 11 22H2 (build 22621)"),
"Windows 11 and Server (11 23H2)": ("22631", "Windows 11 23H2 (build 22631)"),
"Windows 11 and Server (Server 23H2)": ("25398", "Windows Server 2022 23H2 (build 25398)"),
"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)"),
"Windows 11 and Server (11 25H2)": ("26200", "Windows 11 25H2 (build 26200)"),
}
def _parse_header_col(ver_str: str) -> Optional[Tuple[str, str]]:
"""
Return (build_key, display_label) for a CSV header column,
or None if it should be skipped.
Handles j00ru's human-readable format used in both x64 and x86 CSVs:
"Windows 10 (1903)", "Windows NT 4.0 (SP3)", "Windows 11 and Server (11 24H2)"
Also handles legacy dotted-version strings (kept for forward-compatibility):
"10.0.19041.1", "6.1.7601.17514"
"""
ver_str = ver_str.strip()
if not ver_str:
return None
# Fast path: known label
entry = VER_MAP.get(ver_str)
if entry:
return entry
# Legacy dotted format (kept in case j00ru ever reverts): "10.0.19041.1"
if re.match(r"^\d+\.\d+\.\d+", ver_str):
parts = ver_str.split(".")
try:
major = int(parts[0])
build = int(parts[2])
except (IndexError, ValueError):
return None
key = str(build) if major >= 10 else f"{parts[1]}.{parts[2]}"
return key, ver_str
# Unknown: sanitize string as fallback key so we never lose data
key = re.sub(r"[^a-zA-Z0-9._]", "_", ver_str).strip("_").lower()
return key, ver_str
def fetch_csv(url: str) -> str:
print(f" [~] Fetching: {url}")
req = Request(url, headers={"User-Agent": "SysWhispers4/1.0"})
try:
with urlopen(req, timeout=30) as resp:
return resp.read().decode("utf-8")
except URLError as e:
print(f" [!] Failed to fetch {url}: {e}")
sys.exit(1)
def parse_joru_csv(csv_text: str) -> dict:
"""
Parse j00ru's CSV into our JSON format:
{ "FunctionName": { "build_key": ssn_int, ... }, ... }
"""
reader = csv.reader(io.StringIO(csv_text))
rows = list(reader)
if not rows:
return {}
header = rows[0]
version_cols = range(1, len(header))
result: dict = {
"_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": {},
}
# Pre-parse all column headers once
col_meta: Dict[int, Tuple[str, str]] = {} # col_index → (key, label)
for col in version_cols:
parsed = _parse_header_col(header[col])
if parsed:
key, label = parsed
col_meta[col] = (key, label)
result["_windows_builds"][key] = label
for row in rows[1:]:
if not row or len(row) < 2:
continue
func_name = row[0].strip()
if not func_name:
continue
func_entry: dict = {}
for col, (build_key, _) in col_meta.items():
if col >= len(row):
break
cell = row[col].strip()
if not cell or cell.lower() in ("", "n/a", "-", "null"):
continue
try:
ssn = int(cell, 16) if cell.startswith("0x") else int(cell)
except ValueError:
continue
func_entry[build_key] = ssn
if func_entry:
result[func_name] = func_entry
return result
def main() -> None:
parser = argparse.ArgumentParser(
description="Update SysWhispers4 syscall tables from j00ru/windows-syscalls",
)
parser.add_argument(
"--arch",
default="x64",
help="Comma-separated architectures to fetch: x64, x86 (default: x64)",
)
parser.add_argument(
"--out",
default=None,
help="Custom output path (overrides the default data/ location)",
)
parser.add_argument(
"--functions",
default=None,
help="Comma-separated list of functions to keep (default: all Nt* functions)",
)
args = parser.parse_args()
data_dir = Path(__file__).parent.parent / "data"
archs = [a.strip() for a in args.arch.split(",")]
filter_funcs = set(f.strip() for f in args.functions.split(",")) if args.functions else None
for arch in archs:
if arch not in CSV_URLS:
print(f" [!] Unknown arch '{arch}'. Available: {list(CSV_URLS)}")
continue
csv_text = fetch_csv(CSV_URLS[arch])
table = parse_joru_csv(csv_text)
# Filter to Nt* functions only + keep metadata keys (_comment, _source, etc.)
filtered = {k: v for k, v in table.items()
if k.startswith("_") or k.startswith("Nt")}
if filter_funcs:
filtered = {k: v for k, v in filtered.items()
if k.startswith("_") or k in filter_funcs}
out_path = Path(args.out) if args.out else data_dir / f"syscalls_nt_{arch}.json"
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(json.dumps(filtered, indent=2, sort_keys=False), encoding="utf-8")
n_funcs = sum(1 for k in filtered if not k.startswith("_"))
print(f" [+] Written {n_funcs} functions ({arch}) → {out_path}")
print(" [+] Syscall table update complete.")
if __name__ == "__main__":
main()