mirror of
https://dev.lirent.ru/Vatrog/vm-introspection-engine.git
synced 2026-06-18 02:06:36 +03:00
Define the win32 engine; add a dump source and physical sigscan
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.
This commit is contained in:
@@ -27,6 +27,40 @@
|
||||
* pass it, with a cr3, to the address-space primitives below. */
|
||||
typedef struct vmie_mem vmie_mem;
|
||||
|
||||
/* One contiguous GPA window backed by a file span: GPA [gpa, gpa+len) maps 1:1
|
||||
* onto file offset [file_off, file_off+len). A POD descriptor, promoted here so
|
||||
* an explicit-segment dump can be opened through the public surface below; the
|
||||
* full vmie_mem (which embeds an array of these) is defined in core.h. */
|
||||
#ifndef VMIE_GPA_SEG_DEFINED
|
||||
#define VMIE_GPA_SEG_DEFINED
|
||||
typedef struct gpa_seg {
|
||||
uint64_t gpa;
|
||||
uint64_t len;
|
||||
uint64_t file_off;
|
||||
} gpa_seg;
|
||||
#endif
|
||||
|
||||
/* ---- dump source lifecycle ----------------------------------------------- *
|
||||
* A vmie_mem is the universal memory source: a live win32 physical image and an
|
||||
* on-disk dump are both vmie_mem. These open a dump (or any flat/segmented RAM
|
||||
* image) as a heap-owned vmie_mem for the source-agnostic physical scanners
|
||||
* (scan.h: sig_scan_mem/sig_scan_sources). No paging/cr3: a dump supports the
|
||||
* physical signature scan only. The win32 engine produces its vmie_mem through
|
||||
* the win32 surface (win32.h) instead. */
|
||||
|
||||
/* Open `path` as a single-`low` image (the classic QEMU split; low >= file size
|
||||
* => one inert identity segment, i.e. a flat dump). Returns a heap-owned handle,
|
||||
* or NULL on open/mmap failure. Release with vmie_mem_close(). */
|
||||
vmie_mem* vmie_mem_open(const char* path, uint64_t low);
|
||||
|
||||
/* Open `path` with an explicit segment map (`nseg` entries; see gpa_seg). The
|
||||
* map must be well-formed against the file size (dense, sorted, in-file).
|
||||
* Returns a heap-owned handle, or NULL on failure. Release with vmie_mem_close(). */
|
||||
vmie_mem* vmie_mem_open_segs(const char* path, const gpa_seg* segs, int nseg);
|
||||
|
||||
/* Unmap, close, and free a handle from vmie_mem_open*. Safe on NULL. */
|
||||
void vmie_mem_close(vmie_mem* m);
|
||||
|
||||
/* ---- flat memory view (single owner) ------------------------------------- *
|
||||
* A contiguous view of memory.
|
||||
* data - host pointer to the bytes (borrowed; not owned by the view)
|
||||
|
||||
+19
-3
@@ -9,7 +9,7 @@
|
||||
* memory and feed them to the signature matcher.
|
||||
*
|
||||
* The Windows-typed convenience entry points (scan_new(process*),
|
||||
* vmie_scan_pointer(process*)) live in the win32 surface (vmie.h).
|
||||
* vmie_scan_pointer(process*)) live in the win32 surface (win32.h).
|
||||
*/
|
||||
#ifndef VMIE_SCAN_H
|
||||
#define VMIE_SCAN_H
|
||||
@@ -61,7 +61,23 @@ int gva_sig_first(vmie_mem* m, uintptr_t cr3, uint64_t lo, uint64_t hi,
|
||||
int gva_sig_rip (vmie_mem* m, uintptr_t cr3, uint64_t hit_va,
|
||||
size_t disp_off, size_t instr_len, uint64_t* target);
|
||||
|
||||
/* gva_sig_phys (scan the raw physical image) needs the core segment map, so it
|
||||
* is an engine bridge, declared in engine.h - not part of the handler surface. */
|
||||
/* ---- physical-image signature scan (OS-agnostic engine bridge) ----------- *
|
||||
* Scan the raw physical image (the core segment map) for a signature, without a
|
||||
* cr3 or page tables: each seg is one mem_view_t over its file span, fed to the
|
||||
* pure matcher. This is the dump path - a dump (vmie_mem_open*) supports the
|
||||
* physical scan only. Keyed by vmie_mem*, like the rest of this header. */
|
||||
|
||||
/* Attributed hit from a multi-source scan: which source matched, and where. */
|
||||
typedef struct { int source; uint64_t gpa; } sig_hit_src;
|
||||
|
||||
/* Scan one physical image for `p`. Writes up to `max` GPA hits to `out` (NULL to
|
||||
* count only) and returns the TOTAL number of hits, or -1 on a bad pattern. */
|
||||
int sig_scan_mem (vmie_mem* m, const sig_pattern_t* p, uint64_t* out, int max);
|
||||
|
||||
/* Scan `nsrc` physical images for `p`, tagging each hit with its source index.
|
||||
* Writes up to `max` attributed hits to `out` (NULL to count only) and returns
|
||||
* the TOTAL across all sources, or -1 on a bad pattern. */
|
||||
int sig_scan_sources(vmie_mem* const* srcs, int nsrc, const sig_pattern_t* p,
|
||||
sig_hit_src* out, int max);
|
||||
|
||||
#endif /* VMIE_SCAN_H */
|
||||
|
||||
@@ -36,6 +36,12 @@ bool sig_parse_ida(const char* ida, sig_pattern_t* out);
|
||||
* false on NULL args or an empty mask. */
|
||||
bool sig_parse_mask(const uint8_t* bytes, const char* mask, sig_pattern_t* out);
|
||||
|
||||
/* Build an exact (no-wildcard) pattern from `len` raw bytes: every byte must
|
||||
* match. A thin wrapper over sig_parse_mask with an all-'x' mask, so the result
|
||||
* is released with sig_free() like any other pattern. Returns true on success,
|
||||
* false on NULL args, a zero length, or OOM. Touches no vmie_mem (pure). */
|
||||
bool sig_from_bytes(const uint8_t* bytes, size_t len, sig_pattern_t* out);
|
||||
|
||||
/* Release a pattern produced by sig_parse_*. Safe on NULL and on an
|
||||
* already-freed pattern (it is zeroed). */
|
||||
void sig_free(sig_pattern_t* p);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* vmie.h - public Windows-guest surface of the vmi-engine.
|
||||
/* 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
|
||||
@@ -16,18 +16,18 @@
|
||||
* - The library never takes ownership of caller buffers and never retains a
|
||||
* pointer past the call that received it, unless explicitly stated.
|
||||
*/
|
||||
#ifndef VMIE_VMIE_H
|
||||
#define VMIE_VMIE_H
|
||||
#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/include/engine.h;
|
||||
* callers only ever hold a pointer. Created by vmie_open(), populated by
|
||||
* host_bootstrap(), released by vmie_close(). */
|
||||
typedef struct vmie vmie;
|
||||
/* 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.
|
||||
@@ -80,26 +80,26 @@ typedef struct {
|
||||
* 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_close(). */
|
||||
vmie* vmie_open(const char* ram_path, uint64_t low);
|
||||
* 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_close(vmie* v);
|
||||
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_close(v); do NOT free or retain it past that. NULL on
|
||||
* and valid until vmie_win32_close(v); do NOT free or retain it past that. NULL on
|
||||
* NULL `v`. */
|
||||
vmie_mem* vmie_memory(vmie* 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_open(). */
|
||||
int host_bootstrap(vmie* v);
|
||||
* that failed. Cold path: call once after vmie_win32_open(). */
|
||||
int host_bootstrap(vmie_win32* v);
|
||||
|
||||
/* ---- guest string decode ------------------------------------------------- */
|
||||
|
||||
@@ -111,7 +111,7 @@ int host_bootstrap(vmie* v);
|
||||
* 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* v, uintptr_t cr3, uintptr_t va, size_t nmemb, char* dst, size_t size);
|
||||
size_t gva_read_text(vmie_win32* v, uintptr_t cr3, uintptr_t va, size_t nmemb, char* dst, size_t size);
|
||||
|
||||
/* ---- enumeration --------------------------------------------------------- */
|
||||
|
||||
@@ -121,14 +121,14 @@ size_t gva_read_text(vmie* v, uintptr_t cr3, uintptr_t va, size_t nmemb, char* d
|
||||
* 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* v, int skip_system, process* dst, size_t nmax);
|
||||
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* v, const process* pr, pmodule* dst, size_t nmax);
|
||||
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).
|
||||
@@ -137,13 +137,13 @@ int proc_modules(vmie* v, const process* pr, pmodule* dst, size_t nmax);
|
||||
|
||||
/* 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* v, const process* pr, scan_type t, const void* value,
|
||||
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* v, const process* pr, uint64_t target,
|
||||
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_VMIE_H */
|
||||
#endif /* VMIE_WIN32_H */
|
||||
Reference in New Issue
Block a user