dyncvoke-core 0.1.1

PEB walking, dynamic invoke, and Tartarus Gate indirect syscalls for Dyncvoke
Documentation
// Regression for INVALID_HANDLE_VALUE.
//
// Before the fix, `dyncvoke_core::INVALID_HANDLE_VALUE` was NULL, which the
// kernel rejects with STATUS_INVALID_HANDLE (0xC0000008). The Windows
// constant is (HANDLE)(LONG_PTR)-1 — that's the NtCurrentProcess pseudo-
// handle and is what the NT memory syscalls actually expect.
//
// This test now asserts both directions: (a) the OS-level invariant that
// NULL is rejected and -1 succeeds, and (b) the lib-level invariant that
// dyncvoke_core's constant points at -1 so any caller threading it through
// is using a valid pseudo-handle.

use dyncvoke_core::{nt_protect_virtual_memory, INVALID_HANDLE_VALUE};
use std::ffi::c_void;

#[test]
fn invalid_handle_value_const_is_minus_one() {
    assert_eq!(
        INVALID_HANDLE_VALUE as isize, -1,
        "INVALID_HANDLE_VALUE must be the NtCurrentProcess pseudo-handle, not NULL"
    );
}

#[test]
fn nt_protect_with_null_vs_neg_one_handle() {
    let mut buf = vec![0u8; 4096];
    let mut addr: *mut c_void = buf.as_mut_ptr() as *mut c_void;
    let mut sz: usize = 4096;
    let mut old: u32 = 0;

    let null_handle: *mut c_void = std::ptr::null_mut();
    let status_null = nt_protect_virtual_memory(
        null_handle,
        &mut addr as *mut *mut c_void,
        &mut sz as *mut usize,
        0x04, // PAGE_READWRITE
        &mut old as *mut u32,
    );
    assert_ne!(
        status_null, 0,
        "NULL process handle must be rejected by NtProtectVirtualMemory"
    );

    addr = buf.as_mut_ptr() as *mut c_void;
    sz = 4096;
    let status_neg1 = nt_protect_virtual_memory(
        INVALID_HANDLE_VALUE,
        &mut addr as *mut *mut c_void,
        &mut sz as *mut usize,
        0x04,
        &mut old as *mut u32,
    );
    assert_eq!(
        status_neg1, 0,
        "INVALID_HANDLE_VALUE must succeed as the current-process pseudo-handle (NTSTATUS=0x{:08X})",
        status_neg1 as u32
    );
}