### Get CPU Vendor and Family/Model/Stepping Information Source: https://context7.com/namazso/pawnio/llms.txt Retrieves CPU vendor information and decodes the family, model, and stepping fields from the CPUID instruction. Requires `pawnio.inc` and `extra.inc`. ```pawn #include DEFINE_IOCTL_SIZED(ioctl_cpu_vendor, 0, 3) { new CpuVendor:vendor = get_cpu_vendor(); new fms = get_cpu_fms(); out[0] = _:vendor; out[1] = cpu_fms_family(fms); // e.g. 25 for Zen 3 out[2] = cpu_fms_model(fms); // e.g. 33 for 5950X return STATUS_SUCCESS; // CpuVendor_AMD = 0xc0e946f161868306 // CpuVendor_Intel = 0x0d4770f6cacd93a6 } ``` -------------------------------- ### Dynamically Invoke Kernel Function KeQueryTickCount Source: https://context7.com/namazso/pawnio/llms.txt Resolves the kernel function 'KeQueryTickCount' by name and invokes it to get the system tick count. Handles procedure not found and memory allocation errors. Requires `pawnio.inc`. ```pawn #include static VAProc:pKeQueryTickCount = VAProc:NULL; DEFINE_IOCTL_SIZED(ioctl_get_tick, 0, 1) { if (pKeQueryTickCount == VAProc:NULL) { pKeQueryTickCount = get_proc_address("KeQueryTickCount"); if (pKeQueryTickCount == VAProc:NULL) return STATUS_PROCEDURE_NOT_FOUND; } new VA:tick_buf = virtual_alloc(8); if (tick_buf == NULL) return STATUS_NO_MEMORY; new retval; invoke(pKeQueryTickCount, retval, _:tick_buf); virtual_read_qword(tick_buf, out[0]); virtual_free(tick_buf); return STATUS_SUCCESS; } ``` -------------------------------- ### Query PawnIOLib DLL version Source: https://context7.com/namazso/pawnio/llms.txt Call pawnio_version to get the version of the PawnIOLib DLL. The version is returned as a packed ULONG, where major, minor, and patch are encoded in separate byte segments. Check for SUCCEEDED(hr) before using the version. ```c #include #include ULONG ver = 0; HRESULT hr = pawnio_version(&ver); if (SUCCEEDED(hr)) { printf("PawnIOLib version: %u.%u.%u\n", (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, (ver ) & 0xFF); // Output: PawnIOLib version: 2.0.0 } ``` -------------------------------- ### Declare Callable IOCTL Entry Points (Variable Size) Source: https://context7.com/namazso/pawnio/llms.txt Declares a public Pawn function callable via `pawnio_execute` that accepts variable buffer dimensions for input and output. ```pawn #include // Variable-size: caller may pass any buffer dimensions DEFINE_IOCTL(ioctl_echo) { new count = min(in_size, out_size); for (new i = 0; i < count; i++) out[i] = in[i]; return STATUS_SUCCESS; } ``` -------------------------------- ### Set Link Options for PawnIOLib Source: https://github.com/namazso/pawnio/blob/master/CMakeLists.txt Applies linker options to the PawnIOLib target, specifically setting the entry point to DllEntry. This is common for DLLs on Windows. ```cmake target_link_options(PawnIOLib PRIVATE /ENTRY:DllEntry ) ``` -------------------------------- ### Register VM creation callback Source: https://context7.com/namazso/pawnio/llms.txt Use pawnio_register_vm_callback_created to hook VM creation events. The callback is invoked when a module is loaded and can block creation by returning a non-success NTSTATUS. Unregister using pawnio_unregister_vm_callback_created with the returned cookie. ```c #include static PVOID g_cookie = NULL; static NTSTATUS my_vm_created(PVOID ctx) { DbgPrint("[ext] VM created: ctx=%p\n", ctx); // Return STATUS_ACCESS_DENIED to block the load return STATUS_SUCCESS; } // In DriverEntry or equivalent: g_cookie = pawnio_register_vm_callback_created(my_vm_created); // In DriverUnload: pawnio_unregister_vm_callback_created(g_cookie); ``` -------------------------------- ### Read PawnIO driver version (kernel export) Source: https://context7.com/namazso/pawnio/llms.txt Exported from the driver binary, pawnio_version (kernel export) returns the packed driver version integer. Use DbgPrint to display the version components. ```c #include ULONG ver = pawnio_version(); // ver == 0x00020200 for version 2.2.0 DbgPrint("PawnIO driver version: %u.%u.%u\n", (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, (ver ) & 0xFF); ``` -------------------------------- ### Execute NT-native async I/O with APC support Source: https://context7.com/namazso/pawnio/llms.txt Use pawnio_execute_async_nt for low-level async I/O operations. It supports optional event handles, APC routines, and contexts for integration with alertable wait I/O patterns or IOCP. Ensure proper handling of STATUS_PENDING by waiting on the event and checking the IO_STATUS_BLOCK. ```c #include #include IO_STATUS_BLOCK iosb = { 0 }; HANDLE event = NULL; NtCreateEvent(&event, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); ULONG64 in_buf[2] = { 0, 0 }; // bus=0, device=0 ULONG64 out_buf[1] = { 0 }; NTSTATUS status = pawnio_execute_async_nt( h, "ioctl_pci_vendor_id", event, // signal when done NULL, // no APC NULL, // no APC context &iosb, in_buf, 2, out_buf, 1 ); if (status == STATUS_PENDING) { NtWaitForSingleObject(event, FALSE, NULL); status = iosb.Status; } if (NT_SUCCESS(status)) printf("Vendor ID = 0x%04llX\n", out_buf[0]); NtClose(event); ``` -------------------------------- ### Configure PawnIOUtil Executable Source: https://github.com/namazso/pawnio/blob/master/CMakeLists.txt Defines an executable target named PawnIOUtil. It lists the source files and resource compiler file for the utility. ```cmake add_executable(PawnIOUtil PawnIOUtil/PawnIOUtil.cpp PawnIOUtil/signature.cpp PawnIOUtil/resource.rc ) ``` -------------------------------- ### pawnio_version (user-mode) Source: https://context7.com/namazso/pawnio/llms.txt Queries the version of the PawnIOLib DLL itself. The version is returned as a packed integer. ```APIDOC ## pawnio_version — Query PawnIOLib version ### Description Returns the version of the PawnIOLib DLL itself (not the driver) as `(major << 16) | (minor << 8) | patch`. ### Method HRESULT ### Parameters - **ver** (ULONG*) - Pointer to a ULONG where the packed version will be stored. ### Return Value - **HRESULT** - Returns SUCCEEDED on success. ### Example ```c #include #include ULONG ver = 0; HRESULT hr = pawnio_version(&ver); if (SUCCEEDED(hr)) { printf("PawnIOLib version: %u.%u.%u\n", (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, (ver ) & 0xFF); // Output: PawnIOLib version: 2.0.0 } ``` ``` -------------------------------- ### Declare Callable IOCTL Entry Points with Size Enforcement Source: https://context7.com/namazso/pawnio/llms.txt Declares a public Pawn function callable via `pawnio_execute` that enforces exact buffer sizes for input and output. Returns `STATUS_INVALID_PARAMETER` on mismatch. ```pawn #include // Fixed-size: expects exactly 1 input cell, returns 1 output cell DEFINE_IOCTL_SIZED(ioctl_read_msr, 1, 1) { new NTSTATUS:status = msr_read(in[0], out[0]); return status; // Call from C: pawnio_execute(h, "ioctl_read_msr", &msr_index, 1, &result, 1, &n) } ``` -------------------------------- ### Link Libraries for PawnIOUtil Source: https://github.com/namazso/pawnio/blob/master/CMakeLists.txt Links the PawnIOUtil executable against the PawnIOLib library, making the functionality of PawnIOLib available to the utility. ```cmake target_link_libraries(PawnIOUtil PRIVATE PawnIOLib) ``` -------------------------------- ### pawnio_open Source: https://context7.com/namazso/pawnio/llms.txt Opens a file handle to the PawnIO kernel device. This handle is required for all subsequent operations. It is available in HRESULT, Win32 BOOL, and NTSTATUS flavors. ```APIDOC ## pawnio_open ### Description Opens a file handle to the PawnIO kernel device. The returned handle is used for all subsequent calls. Available in HRESULT (`pawnio_open`), Win32 BOOL (`pawnio_open_win32`), and NTSTATUS (`pawnio_open_nt`) flavors. ### Method (Implicitly C function call) ### Parameters - **h** (HANDLE*) - Output parameter to receive the device handle. ### Request Example ```c #include HANDLE h = NULL; HRESULT hr = pawnio_open(&h); if (FAILED(hr)) { fprintf(stderr, "Failed to open PawnIO device: 0x%lx\n", hr); return 1; } // h is now a valid handle to \Device\PawnIO ``` ### Response #### Success Response - **hr** (HRESULT) - S_OK on success. #### Response Example (See Request Example for success indication) ``` -------------------------------- ### Execute CPUID Instruction Source: https://context7.com/namazso/pawnio/llms.txt Executes the CPUID instruction with specified leaf and subleaf values to retrieve CPU information. Results are stored in the output array. Requires `pawnio.inc`. ```pawn #include DEFINE_IOCTL_SIZED(ioctl_cpu_info, 2, 4) { // in[0]=leaf, in[1]=subleaf → out[0..3]=eax,ebx,ecx,edx cpuid(in[0], in[1], out); return STATUS_SUCCESS; // pawnio_execute(h, "ioctl_cpu_info", {1,0}, 2, out, 4, &n) // out[0] bits[11:8] = CPU family, out[0] bits[7:4] = model } ``` -------------------------------- ### pawnio_load Source: https://context7.com/namazso/pawnio/llms.txt Loads a signed AMX binary blob into the driver using the IOCTL_PIO_LOAD_BINARY control code. The blob must be correctly formatted with signature and image. A handle can only load one blob. ```APIDOC ## pawnio_load ### Description Sends a compiled and signed AMX binary blob to the driver via `IOCTL_PIO_LOAD_BINARY`. The blob must be prefixed with a 4-byte little-endian signature length followed by the raw ECDSA signature and then the AMX image. A handle may only be loaded once; attempting a second load returns `STATUS_ALREADY_INITIALIZED`. ### Method (Implicitly C function call) ### Parameters - **h** (HANDLE) - Handle to the PawnIO device obtained from `pawnio_open`. - **blob** (const unsigned char*) - Pointer to the signed AMX binary blob. - **size** (size_t) - The size of the blob in bytes. ### Request Example ```c #include #include // Read a signed blob produced by PawnIOUtil sign FILE* f = fopen("module.blob", "rb"); fseek(f, 0, SEEK_END); size_t size = (size_t)ftell(f); rewind(f); unsigned char* blob = (unsigned char*)malloc(size); fread(blob, 1, size, f); fclose(f); HANDLE h = NULL; HRESULT hr = pawnio_open(&h); if (FAILED(hr)) { /* handle error */ } hr = pawnio_load(h, blob, size); if (FAILED(hr)) { fprintf(stderr, "Failed to load module: 0x%lx\n", hr); pawnio_close(h); free(blob); return 1; } free(blob); // module is now active; ioctl functions can be called ``` ### Response #### Success Response - **hr** (HRESULT) - S_OK on success. #### Response Example (See Request Example for success indication) ``` -------------------------------- ### pawnio_register_vm_callback_created Source: https://context7.com/namazso/pawnio/llms.txt Registers a callback function that is invoked when a new Pawn VM is created. This allows for hooking into the VM creation lifecycle. ```APIDOC ## pawnio_register_vm_callback_created — VM creation lifecycle hook ### Description Registers a callback invoked every time a new Pawn VM is created (i.e., when a module is successfully loaded). Returning a non-success NTSTATUS from the callback blocks the creation. Returns an opaque cookie used for unregistration. ### Method PVOID ### Parameters - **callback** (PVOID) - Pointer to the callback function to register. The callback should have the signature `NTSTATUS (*)(PVOID ctx)`. ### Return Value - **PVOID** - An opaque cookie that must be used to unregister the callback. ### Example ```c #include static PVOID g_cookie = NULL; static NTSTATUS my_vm_created(PVOID ctx) { DbgPrint("[ext] VM created: ctx=%p\n", ctx); // Return STATUS_ACCESS_DENIED to block the load return STATUS_SUCCESS; } // In DriverEntry or equivalent: g_cookie = pawnio_register_vm_callback_created(my_vm_created); // In DriverUnload: pawnio_unregister_vm_callback_created(g_cookie); ``` ``` -------------------------------- ### Register VM pre-execution callback Source: https://context7.com/namazso/pawnio/llms.txt Register a callback with pawnio_register_vm_callback_precall to intercept calls just before a VM executes a public function. The callback receives the VM context and instruction pointer, and can block execution by returning a non-success NTSTATUS. Unregister with pawnio_unregister_vm_callback_precall. ```c #include static PVOID g_pre_cookie = NULL; static NTSTATUS my_precall(PVOID ctx, UINT_PTR cip) { DbgPrint("[ext] precall ctx=%p cip=0x%IX\n", ctx, cip); // Optionally block certain function addresses: // if (cip == forbidden_cip) return STATUS_ACCESS_DENIED; return STATUS_SUCCESS; } g_pre_cookie = pawnio_register_vm_callback_precall(my_precall); // ... pawnio_unregister_vm_callback_precall(g_pre_cookie); ``` -------------------------------- ### Link Libraries for PawnIOLib Source: https://github.com/namazso/pawnio/blob/master/CMakeLists.txt Links the PawnIOLib target against ntdll.lib and PawnIO. This is typically done for Windows-specific libraries. ```cmake target_link_libraries(PawnIOLib PRIVATE ntdll.lib PawnIO) ``` -------------------------------- ### Set Include Directories for PawnIOLib Source: https://github.com/namazso/pawnio/blob/master/CMakeLists.txt Configures the public include directories for the PawnIOLib target, making its headers available to other targets that link against it. ```cmake target_include_directories(PawnIOLib PUBLIC PawnIOLib/include) ``` -------------------------------- ### pawnio_version (kernel export) Source: https://context7.com/namazso/pawnio/llms.txt Reads the driver version from the kernel-mode export. Returns the version as a packed integer. ```APIDOC ## pawnio_version (kernel export) — Read driver version ### Description Exported from the driver binary. Returns the packed version integer. ### Method ULONG ### Return Value - **ULONG** - The packed version integer (e.g., `0x00020200` for version 2.2.0). ### Example ```c #include ULONG ver = pawnio_version(); // ver == 0x00020200 for version 2.2.0 DbgPrint("PawnIO driver version: %u.%u.%u\n", (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, (ver ) & 0xFF); ``` ``` -------------------------------- ### pawnio_register_vm_callback_precall Source: https://context7.com/namazso/pawnio/llms.txt Registers a callback function that is invoked immediately before the VM executes a public function. It provides the VM context and the instruction pointer. ```APIDOC ## pawnio_register_vm_callback_precall — Pre-execution hook ### Description Called immediately before the VM executes a public function, supplying the VM context and the AMX code instruction pointer (`cip`). Returning non-success blocks the execution. ### Method PVOID ### Parameters - **callback** (PVOID) - Pointer to the callback function to register. The callback should have the signature `NTSTATUS (*)(PVOID ctx, UINT_PTR cip)`. ### Return Value - **PVOID** - An opaque cookie that must be used to unregister the callback. ### Example ```c #include static PVOID g_pre_cookie = NULL; static NTSTATUS my_precall(PVOID ctx, UINT_PTR cip) { DbgPrint("[ext] precall ctx=%p cip=0x%IX\n", ctx, cip); // Optionally block certain function addresses: // if (cip == forbidden_cip) return STATUS_ACCESS_DENIED; return STATUS_SUCCESS; } g_pre_cookie = pawnio_register_vm_callback_precall(my_precall); // ... pawnio_unregister_vm_callback_precall(g_pre_cookie); ``` ``` -------------------------------- ### main and unload Source: https://context7.com/namazso/pawnio/llms.txt Module lifecycle hooks for the Pawn module. `main()` is executed upon successful module loading, and `unload()` is called when the module handle is closed. Both functions return an NTSTATUS code. ```APIDOC ## `main` and `unload` — Module lifecycle hooks ### Description `main()` is executed when the blob is successfully loaded (after signature verification). `unload()` is called when the handle is closed. Both return `NTSTATUS`. ### Usage ```pawn #include static VAProc:g_cb; NTSTATUS:main() { // One-time initialization new AmxCip:addr = get_public("my_callback"); g_cb = callback_alloc(addr); // allocate a native function pointer if (g_cb == VAProc:0) return STATUS_NO_MEMORY; debug_print("Module loaded OK\n"); return STATUS_SUCCESS; } public NTSTATUS:unload() { callback_free(g_cb); debug_print("Module unloaded\n"); return STATUS_SUCCESS; } ``` ``` -------------------------------- ### pawnio_execute Source: https://context7.com/namazso/pawnio/llms.txt Synchronously invokes a named public function within the loaded AMX module. It takes input parameters and returns output parameters. The function name must be 31 characters or less. ```APIDOC ## pawnio_execute ### Description Calls a `public` function declared with `DEFINE_IOCTL` or `DEFINE_IOCTL_SIZED` in the loaded module. The function name must be ≤ 31 characters. Input and output are arrays of `ULONG64`. Returns the number of output cells written through `return_size`. ### Method (Implicitly C function call) ### Parameters - **h** (HANDLE) - Handle to the PawnIO device. - **function_name** (const char*) - The name of the public function to execute (max 31 characters). - **input_buf** (const ULONG64*) - Pointer to an array of input cells. - **input_size** (SIZE_T) - The number of input cells. - **output_buf** (ULONG64*) - Pointer to a buffer to receive output cells. - **output_capacity** (SIZE_T) - The maximum number of output cells the buffer can hold. - **return_size** (SIZE_T*) - Pointer to a variable that will receive the number of output cells actually written. ### Request Example ```c #include // Call ioctl_read_msr with input: MSR index 0xC0000080 (EFER) ULONG64 in_buf[1] = { 0xC0000080 }; ULONG64 out_buf[1] = { 0 }; SIZE_T ret_size = 0; HRESULT hr = pawnio_execute( h, "ioctl_read_msr", // public function name in the module in_buf, // input cells 1, // number of input cells out_buf, // output buffer 1, // capacity in cells &ret_size // cells actually written ); if (SUCCEEDED(hr) && ret_size >= 1) { printf("EFER MSR = 0x%016llX\n", out_buf[0]); // Expected output: EFER MSR = 0x0000000000000D01 (typical x64) } ``` ### Response #### Success Response - **hr** (HRESULT) - S_OK on success. - **ret_size** (SIZE_T) - The number of output cells written to `output_buf`. #### Response Example (See Request Example for success indication and output format) ``` -------------------------------- ### Read PCI Configuration Space Vendor and Device IDs Source: https://context7.com/namazso/pawnio/llms.txt Reads the vendor and device ID from PCI configuration space using bus, device, and function numbers. Requires `pawnio.inc`. ```pawn #include DEFINE_IOCTL_SIZED(ioctl_pci_vendor_device, 3, 2) { // in[0]=bus, in[1]=dev, in[2]=func new vendor, device_id; new NTSTATUS:status; status = pci_config_read_word(in[0], in[1], in[2], 0x00, vendor); if (!NT_SUCCESS(status)) return status; status = pci_config_read_word(in[0], in[1], in[2], 0x02, device_id); if (!NT_SUCCESS(status)) return status; out[0] = vendor; out[1] = device_id; return STATUS_SUCCESS; // Example: bus=0,dev=0,func=0 → out[0]=0x8086 (Intel), out[1]=device } ``` -------------------------------- ### pawnio_execute_async_nt Source: https://context7.com/namazso/pawnio/llms.txt Performs an asynchronous I/O operation using NT-native methods with APC support. It's useful for integrating with alertable wait I/O patterns or IOCP. ```APIDOC ## pawnio_execute_async_nt ### Description Low-level variant of the async execute, mirroring `NtDeviceIoControlFile`. Accepts an optional event handle, APC routine, APC context, and `IO_STATUS_BLOCK`. Useful when integrating with alertable-wait I/O patterns or IOCP. ### Method NTSTATUS ### Parameters - **h** (HANDLE) - Handle to the PawnIO device. - **ioctl_code** (const char*) - String identifier for the IOCTL operation (e.g., "ioctl_pci_vendor_id"). - **event** (HANDLE) - Optional event handle to signal completion. - **apc_routine** (PVOID) - Optional APC routine to call on completion. - **apc_context** (PVOID) - Optional context for the APC routine. - **iosb** (IO_STATUS_BLOCK*) - Pointer to an IO_STATUS_BLOCK structure to receive I/O status. - **in_buf** (PVOID) - Pointer to the input buffer. - **in_buf_size** (ULONG) - Size of the input buffer. - **out_buf** (PVOID) - Pointer to the output buffer. - **out_buf_size** (ULONG) - Size of the output buffer. ### Return Value - **NTSTATUS** - Returns STATUS_PENDING if the operation is asynchronous, or the final status code upon completion. ### Example ```c #include #include IO_STATUS_BLOCK iosb = { 0 }; HANDLE event = NULL; NtCreateEvent(&event, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); ULONG64 in_buf[2] = { 0, 0 }; // bus=0, device=0 ULONG64 out_buf[1] = { 0 }; NTSTATUS status = pawnio_execute_async_nt( h, "ioctl_pci_vendor_id", event, // signal when done NULL, // no APC NULL, // no APC context &iosb, in_buf, 2, out_buf, 1 ); if (status == STATUS_PENDING) { NtWaitForSingleObject(event, FALSE, NULL); status = iosb.Status; } if (NT_SUCCESS(status)) printf("Vendor ID = 0x%04llX\n", out_buf[0]); NtClose(event); ``` ``` -------------------------------- ### DEFINE_IOCTL / DEFINE_IOCTL_SIZED Source: https://context7.com/namazso/pawnio/llms.txt Macros to declare callable IOCTL entry points for Pawn scripts. These allow user-mode scripts to invoke specific kernel functions via `pawnio_execute`. `DEFINE_IOCTL_SIZED` enforces exact buffer sizes. ```APIDOC ## `DEFINE_IOCTL` / `DEFINE_IOCTL_SIZED` — Declare callable IOCTL entry points ### Description Macros that declare a `public` Pawn function callable from user mode via `pawnio_execute`. `DEFINE_IOCTL_SIZED` additionally enforces exact buffer sizes and returns `STATUS_INVALID_PARAMETER` automatically on mismatch. ### Usage (Fixed-size IOCTL) ```pawn #include // Fixed-size: expects exactly 1 input cell, returns 1 output cell DEFINE_IOCTL_SIZED(ioctl_read_msr, 1, 1) { new NTSTATUS:status = msr_read(in[0], out[0]); return status; // Call from C: pawnio_execute(h, "ioctl_read_msr", &msr_index, 1, &result, 1, &n) } ``` ### Usage (Variable-size IOCTL) ```pawn #include // Variable-size: caller may pass any buffer dimensions DEFINE_IOCTL(ioctl_echo) { new count = min(in_size, out_size); for (new i = 0; i < count; i++) out[i] = in[i]; return STATUS_SUCCESS; } ``` ``` -------------------------------- ### Allocate and Free Kernel Callback Trampoline Source: https://context7.com/namazso/pawnio/llms.txt Allocates a native kernel function pointer that invokes a specified Pawn function, enabling Pawn scripts to register kernel callbacks like APCs. Requires `pawnio.inc`. ```pawn #include static VAProc:g_apc_va; public NTSTATUS:my_apc(VA:args) { debug_print("APC fired!\n"); return STATUS_SUCCESS; } NTSTATUS:main() { new AmxCip:cip = get_public("my_apc"); g_apc_va = callback_alloc(cip); if (g_apc_va == VAProc:0) return STATUS_NO_MEMORY; debug_print("Callback VA: %x\n", _:g_apc_va); return STATUS_SUCCESS; } public NTSTATUS:unload() { callback_free(g_apc_va); return STATUS_SUCCESS; } ``` -------------------------------- ### Kernel Sleep using microsleep or microsleep2 Source: https://context7.com/namazso/pawnio/llms.txt Pauses execution for a specified number of microseconds. It first attempts a passive IRQL wait (`microsleep`) and falls back to a busy-wait (`microsleep2`) if the former fails. Requires `pawnio.inc`. ```pawn #include DEFINE_IOCTL_SIZED(ioctl_delay, 1, 0) { // in[0] = microseconds new NTSTATUS:s = microsleep(in[0]); // passive-IRQL wait if (!NT_SUCCESS(s)) s = microsleep2(in[0]); // fallback: busy wait return s; } ``` -------------------------------- ### Read CPU Timestamps and Performance Counters Source: https://context7.com/namazso/pawnio/llms.txt Reads the CPU timestamp counter (TSC), the high-precision performance counter (QPC), and the TSC with process ID using `rdtsc`, `rdtscp`, and `qpc`. Requires `pawnio.inc`. ```pawn #include DEFINE_IOCTL_SIZED(ioctl_timing, 0, 3) { out[0] = rdtsc(); new pid; out[1] = rdtscp(pid); new freq; out[2] = qpc(freq); // out[3] would be freq if we had space; returned via debug_print instead debug_print("TSC=%d RDTSCP=%d QPC=%d freq=%d\n", out[0], out[1], out[2], freq); return STATUS_SUCCESS; } ``` -------------------------------- ### pawnio_register_vm_callback_postcall Source: https://context7.com/namazso/pawnio/llms.txt Registers a callback function that is invoked after the VM returns from executing a public function. ```APIDOC ## pawnio_register_vm_callback_postcall — Post-execution hook ### Description Called after the VM returns from executing a public function. ### Method PVOID ### Parameters - **callback** (PVOID) - Pointer to the callback function to register. The callback should have the signature `void (*)(PVOID ctx)`. ### Return Value - **PVOID** - An opaque cookie that must be used to unregister the callback. ### Example ```c #include static PVOID g_post_cookie = NULL; static void my_postcall(PVOID ctx) { DbgPrint("[ext] postcall ctx=%p\n", ctx); } g_post_cookie = pawnio_register_vm_callback_postcall(my_postcall); // ... pawnio_unregister_vm_callback_postcall(g_post_cookie); ``` ``` -------------------------------- ### Register VM post-execution callback Source: https://context7.com/namazso/pawnio/llms.txt Use pawnio_register_vm_callback_postcall to register a callback that is invoked after a VM finishes executing a public function. The callback receives the VM context. Unregister using pawnio_unregister_vm_callback_postcall. ```c #include static PVOID g_post_cookie = NULL; static void my_postcall(PVOID ctx) { DbgPrint("[ext] postcall ctx=%p\n", ctx); } g_post_cookie = pawnio_register_vm_callback_postcall(my_postcall); // ... pawnio_unregister_vm_callback_postcall(g_post_cookie); ``` -------------------------------- ### Sign AMX Binary with ECDSA Key Source: https://context7.com/namazso/pawnio/llms.txt Prepends an ECDSA signature to a compiled .amx file to produce a .blob file ready for pawnio_load. Requires a private key file for signing. ```bash PawnIOUtil.exe sign module.amx module.blob my_private_key.pem ``` -------------------------------- ### Test Module Load and Unload Source: https://context7.com/namazso/pawnio/llms.txt Loads a signed .blob module, executes its main function, and then immediately unloads it, running the unload function. The exit code indicates success (0) or failure (non-zero) with an HRESULT. ```bash PawnIOUtil.exe test module.blob my_private_key.pem ``` -------------------------------- ### Interactive Function Call REPL Source: https://context7.com/namazso/pawnio/llms.txt Loads a module and enters an interactive Read-Eval-Print Loop (REPL) for calling exported IOCTL functions. Input format is ' [hex_in_val ...]', and output is displayed in multiple formats. ```bash PawnIOUtil.exe interactive module.blob ``` ```text > ioctl_read_msr 1 C0000080 received 1 cells: 0000000000000D01 ........ 3329 0.000000 (0.000000 0.000000) > ioctl_cpu_info 4 1 0 received 4 cells: 00000000000A0671 q....... 656497 0.000000 ... ``` ```text > quit ``` -------------------------------- ### Open PawnIO Device Handle Source: https://context7.com/namazso/pawnio/llms.txt Opens a handle to the PawnIO kernel device. The returned handle is used for all subsequent API calls. Available in HRESULT, Win32 BOOL, and NTSTATUS flavors. ```c #include HANDLE h = NULL; HRESULT hr = pawnio_open(&h); if (FAILED(hr)) { fprintf(stderr, "Failed to open PawnIO device: 0x%lx\n", hr); return 1; } // h is now a valid handle to \Device\PawnIO ``` -------------------------------- ### Configure PawnIOLib Shared Library Source: https://github.com/namazso/pawnio/blob/master/CMakeLists.txt Defines a shared library target named PawnIOLib. It specifies the source files and resource compiler file for the library. ```cmake add_library(PawnIOLib SHARED PawnIOLib/PawnIOLib.cpp PawnIOLib/resource.rc ) ``` -------------------------------- ### Load Signed AMX Blob into Driver Source: https://context7.com/namazso/pawnio/llms.txt Sends a compiled and signed AMX binary blob to the driver via IOCTL_PIO_LOAD_BINARY. The blob must be prefixed with a 4-byte little-endian signature length, followed by the raw ECDSA signature and then the AMX image. A handle may only be loaded once; attempting a second load returns STATUS_ALREADY_INITIALIZED. ```c #include #include // Read a signed blob produced by PawnIOUtil sign FILE* f = fopen("module.blob", "rb"); fseek(f, 0, SEEK_END); size_t size = (size_t)ftell(f); rewind(f); unsigned char* blob = (unsigned char*)malloc(size); fread(blob, 1, size, f); fclose(f); HANDLE h = NULL; HRESULT hr = pawnio_open(&h); if (FAILED(hr)) { /* handle error */ } hr = pawnio_load(h, blob, size); if (FAILED(hr)) { fprintf(stderr, "Failed to load module: 0x%lx\n", hr); pawnio_close(h); free(blob); return 1; } free(blob); // module is now active; ioctl functions can be called ``` -------------------------------- ### Pawn Module Lifecycle Hooks Source: https://context7.com/namazso/pawnio/llms.txt Defines the `main` function executed on module load and the `unload` function called on handle close. Both functions return an NTSTATUS code. ```pawn #include static VAProc:g_cb; NTSTATUS:main() { // One-time initialization new AmxCip:addr = get_public("my_callback"); g_cb = callback_alloc(addr); // allocate a native function pointer if (g_cb == VAProc:0) return STATUS_NO_MEMORY; debug_print("Module loaded OK\n"); return STATUS_SUCCESS; } public NTSTATUS:unload() { callback_free(g_cb); debug_print("Module unloaded\n"); return STATUS_SUCCESS; } ``` -------------------------------- ### Write Byte to x86 I/O Port Source: https://context7.com/namazso/pawnio/llms.txt Writes a single byte to a specified x86 hardware I/O port. Requires `pawnio.inc`. ```pawn #include DEFINE_IOCTL_SIZED(ioctl_port_write, 2, 0) { // in[0] = port, in[1] = value io_out_byte(in[0], in[1]); return STATUS_SUCCESS; } ``` -------------------------------- ### Register VM destruction callback Source: https://context7.com/namazso/pawnio/llms.txt Hook VM destruction events with pawnio_register_vm_callback_destroyed. The callback is executed when a VM's handle is closed, allowing for cleanup of VM-specific resources. Unregister using pawnio_unregister_vm_callback_destroyed. ```c #include static PVOID g_destroy_cookie = NULL; static void my_vm_destroyed(PVOID ctx) { DbgPrint("[ext] VM destroyed: ctx=%p\n", ctx); // Free any per-VM resources here } g_destroy_cookie = pawnio_register_vm_callback_destroyed(my_vm_destroyed); // ... pawnio_unregister_vm_callback_destroyed(g_destroy_cookie); ``` -------------------------------- ### Atomic Compare-and-Exchange for Virtual Memory Source: https://context7.com/namazso/pawnio/llms.txt Atomically compares a value at a kernel virtual address with a given comparand and, if they match, replaces the value with a new value. Returns `STATUS_SUCCESS` on successful exchange or `STATUS_UNSUCCESSFUL` if the comparand did not match. ```pawn #include DEFINE_IOCTL_SIZED(ioctl_atomic_set, 2, 1) { // in[0] = virtual address, in[1] = new value new VA:addr = VA:in[0]; new NTSTATUS:status = virtual_cmpxchg_qword2(addr, in[1], 0); out[0] = _:status; return STATUS_SUCCESS; } ``` -------------------------------- ### Test Physical Memory Read/Write Source: https://context7.com/namazso/pawnio/llms.txt Performs a test of physical memory access by writing a 64-bit value and then reading it back. This function operates on physical addresses that are already mapped by the kernel. ```pawn #include DEFINE_IOCTL_SIZED(ioctl_phys_rw_test, 1, 1) { new pa = in[0]; new NTSTATUS:status; status = physical_write_qword(pa, 0xDEADBEEFCAFEBABE); if (!NT_SUCCESS(status)) return status; new readback; status = physical_read_qword(pa, readback); if (!NT_SUCCESS(status)) return status; out[0] = (readback == 0xDEADBEEFCAFEBABE) ? 1 : 0; return STATUS_SUCCESS; } ``` -------------------------------- ### `rdtsc` / `rdtscp` / `qpc` — Timing primitives Source: https://context7.com/namazso/pawnio/llms.txt Read the CPU timestamp counter or the high-precision performance counter. ```APIDOC ## `rdtsc` ### Description Reads the CPU timestamp counter. ### Method `rdtsc()` ### Response #### Success Response (integer) Returns the value of the timestamp counter. ## `rdtscp` ### Description Reads the CPU timestamp counter and processor ID. ### Method `rdtscp(&pid)` ### Parameters #### Path Parameters - **pid** (integer) - Output - Variable to store the processor ID. ### Response #### Success Response (integer) Returns the value of the timestamp counter. ## `qpc` ### Description Reads the high-precision performance counter. ### Method `qpc(&frequency)` ### Parameters #### Path Parameters - **frequency** (integer) - Output - Variable to store the counter's frequency. ### Response #### Success Response (integer) Returns the value of the performance counter. ### Request Example ```pawn #include DEFINE_IOCTL_SIZED(ioctl_timing, 0, 3) { out[0] = rdtsc(); new pid; out[1] = rdtscp(pid); new freq; out[2] = qpc(freq); // out[3] would be freq if we had space; returned via debug_print instead debug_print("TSC=%d RDTSCP=%d QPC=%d freq=%d\n", out[0], out[1], out[2], freq); return STATUS_SUCCESS; } ``` ``` -------------------------------- ### Read Byte from x86 I/O Port Source: https://context7.com/namazso/pawnio/llms.txt Reads a single byte from a specified x86 hardware I/O port. Requires `pawnio.inc`. ```pawn #include DEFINE_IOCTL_SIZED(ioctl_port_read, 1, 1) { // in[0] = I/O port number out[0] = io_in_byte(in[0]); return STATUS_SUCCESS; // Example: port 0x61 (PC Speaker control) } ``` -------------------------------- ### Synchronously Invoke Named Pawn IOCTL Function Source: https://context7.com/namazso/pawnio/llms.txt Calls a public function declared with DEFINE_IOCTL or DEFINE_IOCTL_SIZED in the loaded module. The function name must be ≤ 31 characters. Input and output are arrays of ULONG64. Returns the number of output cells written through return_size. ```c #include // Call ioctl_read_msr with input: MSR index 0xC0000080 (EFER) ULONG64 in_buf[1] = { 0xC0000080 }; ULONG64 out_buf[1] = { 0 }; SIZE_T ret_size = 0; HRESULT hr = pawnio_execute( h, "ioctl_read_msr", // public function name in the module in_buf, // input cells 1, // number of input cells out_buf, // output buffer 1, // capacity in cells &ret_size // cells actually written ); if (SUCCEEDED(hr) && ret_size >= 1) { printf("EFER MSR = 0x%016llX\n", out_buf[0]); // Expected output: EFER MSR = 0x0000000000000D01 (typical x64) } ``` -------------------------------- ### Asynchronously Invoke Pawn IOCTL Function Source: https://context7.com/namazso/pawnio/llms.txt Non-blocking version of pawnio_execute. Follows the same semantics as DeviceIoControl with an OVERLAPPED structure. The overlapped parameter is mandatory. Use GetOverlappedResult to retrieve the output count after the event fires. ```c #include #include ULONG64 out_buf[4] = { 0 }; OVERLAPPED ov = { 0 }; ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); ULONG64 in_buf[1] = { 1 /* cpu index */ }; HRESULT hr = pawnio_execute_async( h, "ioctl_read_cpuid", in_buf, 1, out_buf, 4, &ov ); // hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING) means pending if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING)) { WaitForSingleObject(ov.hEvent, INFINITE); DWORD written = 0; GetOverlappedResult(h, &ov, &written, FALSE); SIZE_T cells = written / sizeof(ULONG64); for (SIZE_T i = 0; i < cells; i++) printf("out[%zu] = 0x%016llX\n", i, out_buf[i]); } CloseHandle(ov.hEvent); ``` -------------------------------- ### pawnio_register_vm_callback_destroyed Source: https://context7.com/namazso/pawnio/llms.txt Registers a callback function that is invoked when a VM is torn down (handle closed). This is used for cleaning up VM-specific resources. ```APIDOC ## pawnio_register_vm_callback_destroyed — VM destruction hook ### Description Called when a VM is torn down (handle closed). Use to clean up any state associated with the VM context pointer. ### Method PVOID ### Parameters - **callback** (PVOID) - Pointer to the callback function to register. The callback should have the signature `void (*)(PVOID ctx)`. ### Return Value - **PVOID** - An opaque cookie that must be used to unregister the callback. ### Example ```c #include static PVOID g_destroy_cookie = NULL; static void my_vm_destroyed(PVOID ctx) { DbgPrint("[ext] VM destroyed: ctx=%p\n", ctx); // Free any per-VM resources here } g_destroy_cookie = pawnio_register_vm_callback_destroyed(my_vm_destroyed); // ... pawnio_unregister_vm_callback_destroyed(g_destroy_cookie); ``` ``` -------------------------------- ### pawnio_execute_async Source: https://context7.com/namazso/pawnio/llms.txt Asynchronously invokes a named public function within the loaded AMX module. This is a non-blocking operation that requires an OVERLAPPED structure. ```APIDOC ## pawnio_execute_async ### Description Non-blocking version of `pawnio_execute`. Follows the same semantics as `DeviceIoControl` with an `OVERLAPPED` structure. The overlapped parameter is mandatory. Use `GetOverlappedResult` to retrieve the output count after the event fires. ### Method (Implicitly C function call) ### Parameters - **h** (HANDLE) - Handle to the PawnIO device. - **function_name** (const char*) - The name of the public function to execute (max 31 characters). - **input_buf** (const ULONG64*) - Pointer to an array of input cells. - **input_size** (SIZE_T) - The number of input cells. - **output_buf** (ULONG64*) - Pointer to a buffer to receive output cells. - **output_capacity** (SIZE_T) - The maximum number of output cells the buffer can hold. - **overlapped** (OVERLAPPED*) - Pointer to an OVERLAPPED structure, which must contain a valid event handle. ### Request Example ```c #include #include ULONG64 out_buf[4] = { 0 }; OVERLAPPED ov = { 0 }; ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); ULONG64 in_buf[1] = { 1 // cpu index }; HRESULT hr = pawnio_execute_async( h, "ioctl_read_cpuid", in_buf, 1, out_buf, 4, &ov ); // hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING) means pending if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING)) { WaitForSingleObject(ov.hEvent, INFINITE); DWORD written = 0; GetOverlappedResult(h, &ov, &written, FALSE); SIZE_T cells = written / sizeof(ULONG64); for (SIZE_T i = 0; i < cells; i++) printf("out[%%zu] = 0x%016llX\n", i, out_buf[i]); } CloseHandle(ov.hEvent); ``` ### Response #### Success Response - **hr** (HRESULT) - HRESULT_FROM_WIN32(ERROR_IO_PENDING) if the operation is pending. Other HRESULT values indicate immediate success or failure. - **written** (DWORD, via GetOverlappedResult) - The number of bytes written to the output buffer. #### Response Example (See Request Example for success indication and output format) ``` -------------------------------- ### `callback_alloc` / `callback_free` — Native trampoline allocation Source: https://context7.com/namazso/pawnio/llms.txt Allocates a real kernel function pointer (native VA) that, when called by other kernel code, will invoke the specified Pawn function. Enables Pawn scripts to register callbacks (e.g., APCs, timer routines) directly with the kernel. ```APIDOC ## `callback_alloc` ### Description Allocates a native trampoline that invokes a specified Pawn function when called. ### Method `callback_alloc(pawn_function_pointer)` ### Parameters #### Path Parameters - **pawn_function_pointer** (AmxCip) - Required - A pointer to the Pawn function to be called. ### Response #### Success Response (VAProc) Returns the virtual address of the allocated native trampoline. #### Error Response (VAProc:0) Returns 0 if memory allocation fails. ## `callback_free` ### Description Frees a previously allocated native trampoline. ### Method `callback_free(trampoline_va)` ### Parameters #### Path Parameters - **trampoline_va** (VAProc) - Required - The virtual address of the trampoline to free. ### Request Example ```pawn #include static VAProc:g_apc_va; public NTSTATUS:my_apc(VA:args) { debug_print("APC fired!\n"); return STATUS_SUCCESS; } NTSTATUS:main() { new AmxCip:cip = get_public("my_apc"); g_apc_va = callback_alloc(cip); if (g_apc_va == VAProc:0) return STATUS_NO_MEMORY; debug_print("Callback VA: %x\n", _:g_apc_va); return STATUS_SUCCESS; } public NTSTATUS:unload() { callback_free(g_apc_va); return STATUS_SUCCESS; } ``` ### Response #### Success Response (STATUS_SUCCESS) Returns STATUS_SUCCESS upon successful deallocation. ```