mirror of
https://dev.lirent.ru/Vatrog/vm-introspection-engine.git
synced 2026-06-18 02:06:36 +03:00
93966c3df2
Name and isolate the Windows engine as one of potentially several. The public surface moves to include/win32.h with an opaque vmie_win32 handle (vmie_win32_open/close/mem); the engine's Windows internals — host bring-up, the struct-offset profile, process/module/PE/text decode — live under src/engine/win32. The generic address-space layer stays in src/engine (gva.c + engine-arch.h, carrying no offset table): gva.c is de-profiled, and CR3 bring-up reaches the hot translator through a cold gva_translate bridge so the zero-copy hot path stays private and inlinable. A memory source is now first-class and public: vmie_mem_open/_open_segs/ _close open a flat dump (or an explicit segment map) as a vmie_mem, with gpa_seg promoted to the public contract. The physical signature scan is exposed source-agnostically: sig_scan_mem returns GPAs for any vmie_mem, sig_scan_sources scans several sources with per-source attribution, and sig_from_bytes builds an exact needle from a byte span. The pure matcher is unchanged; dumps and the live engine image are scanned uniformly, neither needing the other.
150 lines
7.6 KiB
C
150 lines
7.6 KiB
C
/* 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 <stdint.h>
|
|
#include <stddef.h>
|
|
#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 */
|