/* win32.h - public Windows-guest surface of the vmi-engine. * * The host opens a guest's RAM backing file (a flat, writable, coherent mmap), * recovers the kernel address space, and reads/writes guest memory by CR3 and * virtual address. Everything is CR3-keyed, never PID-keyed: a `process` already * carries its own cr3, which is the key to that address space. * * This header is the Windows-typed surface (process/pmodule/gtext, bring-up, * enumeration, the win32 scan wrappers). The OS-agnostic memory-model contract * lives in memmodel.h (pulled in below); the scanners in scan.h/sigscan.h. * * Conventions: * - `cr3` is a raw CR3 / DirectoryTableBase value; low flag bits are masked * internally, so either the masked PML4 GPA or the raw register works. * - Integer returns: 0 on success, negative on failure, unless stated. * - The library never takes ownership of caller buffers and never retains a * pointer past the call that received it, unless explicitly stated. */ #ifndef VMIE_WIN32_H #define VMIE_WIN32_H #include #include #include "memmodel.h" /* vmie_mem, vregion/VR_*, task/range, gva_read/write/ptr/regions/sweep */ #include "sigscan.h" /* mem_view_t, sig_pattern_t */ #include "scan.h" /* scan_type, scan_ptr_path, generic scan surface */ /* Opaque introspection context. Completed in src/engine/win32/engine-win32.h; * callers only ever hold a pointer. Created by vmie_win32_open(), populated by * host_bootstrap(), released by vmie_win32_close(). */ typedef struct vmie_win32 vmie_win32; /* A guest counted string still resident in guest memory (e.g. a UNICODE_STRING * buffer). Not a copy: `va` points into the guest, decode it with gva_read_text. * va - guest VA of the first UTF-16LE code unit (0 if absent) * len - length in BYTES (not characters); always even for UTF-16 */ typedef struct { uint64_t va; uint32_t len; } gtext; /* A live process, as produced by proc_list(). Self-contained: `cr3` is all you * need to read/write its user address space, `eprocess`/`peb` re-anchor it in * kernel/user space without another lookup. * cr3 - DirectoryTableBase (PFN-masked); key to this address space * peb - PEB VA (0 for system/kernel-only processes) * eprocess - _EPROCESS VA (kernel object, read under the kernel cr3) * pid, ppid - process / parent ids (ppid == (uint32_t)-1 if unavailable) * create_time - raw KSYSTEM_TIME / FILETIME (100 ns ticks; 0 if unavailable) * name - ImageFileName, NUL-terminated ASCII (up to 15 chars) * path - full image path as a guest UTF-16 string (gtext; may be empty) */ typedef struct { uint64_t cr3; uint64_t peb; uint64_t eprocess; uint32_t pid; uint32_t ppid; uint64_t create_time; char name[16]; gtext path; } process; /* A loaded module (image) inside a process, as produced by proc_modules(). * pr - owning process (its cr3 is the address space these VAs live in) * entry - _LDR_DATA_TABLE_ENTRY VA * base - image base VA (page-aligned); pair with `size` for a MODULE scope * size - image size in bytes (SizeOfImage) * name - module file name (gtext UTF-16, e.g. "ntdll.dll") * path - full module path (gtext UTF-16) */ typedef struct { const process* pr; uint64_t entry; uint64_t base; uint32_t size; gtext name; gtext path; } pmodule; /* ---- lifecycle ----------------------------------------------------------- */ /* Open `ram_path` (the guest RAM backing file) and build a context over it. * ram_path - path to a writable, share=on RAM backing file * low - size in bytes of below-4G guest RAM (the PCI-hole split point); * pass the value from the VM's memory layout. If total RAM <= low, * the split is inert. * Returns a new context (call host_bootstrap() next), or NULL on open/mmap * failure. Free with vmie_win32_close(). */ vmie_win32* vmie_win32_open(const char* ram_path, uint64_t low); /* Unmap, close, and free a context. Safe on NULL. After this, every pointer * into guest memory obtained through this context is invalid. */ void vmie_win32_close(vmie_win32* v); /* Borrow the engine's guest-memory handle for the generic address-space * primitives (gva_read/gva_regions/...). The returned pointer is owned by `v` * and valid until vmie_win32_close(v); do NOT free or retain it past that. NULL on * NULL `v`. */ vmie_mem* vmie_win32_mem(vmie_win32* v); /* One-shot bring-up: locate the guest agent beacon in physical RAM, recover a * bootstrap CR3, find ntoskrnl, build the struct-offset profile, derive the * permanent System DirectoryTableBase (kernel cr3) and System _EPROCESS, then * ACK the agent. On success the context is ready for proc_list()/gva_read()/etc. * Returns 0 on success, or a negative stage code (-1..-6) identifying the step * that failed. Cold path: call once after vmie_win32_open(). */ int host_bootstrap(vmie_win32* v); /* ---- guest string decode ------------------------------------------------- */ /* Read a UTF-16LE guest string and transcode it to UTF-8. * va - guest VA of the first UTF-16 code unit * nmemb - number of BYTES to read from the guest (rounded down to even) * dst - output buffer for NUL-terminated UTF-8 (may be NULL to size only) * size - capacity of `dst` in bytes * Returns the number of UTF-8 bytes the full conversion needs, EXCLUDING the * terminator (like snprintf): if it is >= `size`, output was truncated. When * `dst` is non-NULL and `size` > 0 the result is always NUL-terminated. */ size_t gva_read_text(vmie_win32* v, uintptr_t cr3, uintptr_t va, size_t nmemb, char* dst, size_t size); /* ---- enumeration --------------------------------------------------------- */ /* Enumerate processes by walking ActiveProcessLinks from System. * skip_system - if nonzero, omit processes with no PEB (System/kernel-only) * dst - caller array receiving up to `nmax` `process` records * nmax - capacity of `dst` * Returns the number written (<= nmax), or negative on failure (e.g. bootstrap * not completed). Enumeration stops at `nmax`; raise it to see more. */ int proc_list(vmie_win32* v, int skip_system, process* dst, size_t nmax); /* Enumerate a process's loaded modules via the PEB loader InLoadOrder list. * pr - process to inspect (uses pr->cr3 and pr->peb) * dst - caller array receiving up to `nmax` `pmodule` records * nmax - capacity of `dst` * Returns the number written (<= nmax), 0 if the process has no PEB/loader. */ int proc_modules(vmie_win32* v, const process* pr, pmodule* dst, size_t nmax); /* ---- win32 scan wrappers ------------------------------------------------- * * Convenience entry points over the generic cr3/range scan surface (scan.h). * They project a Windows `process` to its cr3, and its `pmodule[]` to a decoded * `range[]` (UTF-8 names), then delegate to scan_new_cr3 / scan_pointer. */ /* Open a value-scan session over the user address space of `pr`. Equivalent to * scan_new_cr3(&v->mem, pr->cr3, ...). Returns NULL on NULL pr or OOM. */ scan* scan_new(vmie_win32* v, const process* pr, scan_type t, const void* value, int be, int aligned, uint64_t lo, uint64_t hi); /* Pointer scan over `pr`'s user space, anchored on its loaded modules. Resolves * `pr`'s module list to range[] (names engine-decoded) and delegates to * scan_pointer. Returns the number of paths found, or negative on failure. */ int vmie_scan_pointer(vmie_win32* v, const process* pr, uint64_t target, int max_depth, uint32_t max_off, scan_ptr_path* out, int max); #endif /* VMIE_WIN32_H */