dyncvoke-core 0.1.0

PEB walking, dynamic invoke, and Tartarus Gate indirect syscalls for Dyncvoke
// Sanity check for the sys module.
//
// Resolves a handful of known syscalls via the byte-derived API and
// asserts (a) the SSNs match expected values for Win11 24H2, and (b) the
// syscall instruction address found is exactly 18 bytes into the stub
// (standard Hell's Gate stub layout) for clean stubs.

use dyncvoke_core::{
    get_function_address, get_module_base_address, get_syscall_address, resolve_syscall,
};

#[test]
fn resolve_known_syscalls() {
    // Expected SSNs from audit_hellsgate.rs on Win11 24H2 build 26200.
    // If this test starts failing on a different build, that's the kernel
    // SSDT shifting — not a regression in our resolver, but a useful signal.
    for (name, expected_ssn) in &[
        ("NtClose", 15u16),
        ("NtAllocateVirtualMemory", 24),
        ("NtOpenProcess", 38),
        ("NtCreateFile", 85),
        ("NtProtectVirtualMemory", 80),
        ("NtCreateThreadEx", 201),
    ] {
        let (ssn, addr) = resolve_syscall(name).unwrap_or_else(|e| panic!("{}: {}", name, e));
        assert_eq!(ssn, *expected_ssn, "{}: ssn mismatch", name);
        assert!(addr != 0, "{}: syscall addr is 0", name);

        // The clean Hell's Gate stub puts `0F 05` at offset 18.
        let stub = get_function_address(get_module_base_address("ntdll.dll"), name);
        assert_eq!(addr - stub, 18, "{}: syscall addr is at offset {}, expected 18", name, addr - stub);
    }
}

#[test]
fn resolve_unknown_returns_error() {
    let result = resolve_syscall("NtThisFunctionDoesNotExistAnywhere");
    assert!(result.is_err());
}

#[test]
fn get_syscall_address_skips_ssdt_check_branch() {
    // The stricter `0F 05 C3` matcher must find the syscall+ret path, not
    // some random `0F 05` byte pair earlier in the stub.
    let ntdll = get_module_base_address("ntdll.dll");
    let nt_close = get_function_address(ntdll, "NtClose");
    let addr = get_syscall_address(nt_close as *mut _).unwrap();
    // The byte after the syscall must be a ret (0xC3).
    let byte_after = unsafe { *((addr + 2) as *const u8) };
    assert_eq!(byte_after, 0xC3, "byte after syscall must be 0xC3 (ret)");
}