mirror of
https://dev.lirent.ru/Vatrog/vm-introspection-engine.git
synced 2026-06-18 02:06:36 +03:00
Split the library into CORE / ENGINE / HANDLERS layers
CORE (src/core): vmie_mem — guest-physical substrate with a data-driven segment map (replaces the hardcoded 4 GiB PCI-hole topology). ENGINE (src/engine): x86-64 paging + Windows bring-up; produces the generic memory model. HANDLERS (src/handlers): the signature/value/pointer scanners, which now consume an OS-agnostic contract. Keystone: gva_ctx is split into vmie_mem (core) + vmie (engine); the generic access functions take vmie_mem* + cr3 and no longer compile in the Windows offset table. New public contract include/memmodel.h (vmie_mem, mem_view_t, vregion, task, range, the gva_* access); win32 surface in include/vmie.h. Leak relocations: the PE parser, UTF-16 decode and CR3-recovery heuristics move engine-side; the matcher stays a pure, source-agnostic handler, and the pointer scanner takes a generic range[] instead of reaching into the process enumerator.
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
/* memmodel.h - the OS-agnostic memory-model contract (the middle layer).
|
||||
*
|
||||
* This is the shared vocabulary between the ENGINE (which turns guest-physical
|
||||
* RAM into a usable virtual memory model via x86-64 paging + Windows bring-up)
|
||||
* and the HANDLERS (scanners that consume that model). It names no Windows
|
||||
* concept: a handler compiled against this header literally cannot mention an
|
||||
* _EPROCESS, a PEB, or an LDR entry.
|
||||
*
|
||||
* Everything here is keyed by a `vmie_mem*` (the opaque physical/paging
|
||||
* substrate) plus a `cr3` (the address space). The engine handle `vmie` is
|
||||
* never handed to a handler - only `vmie_mem*` + `cr3`.
|
||||
*
|
||||
* 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.
|
||||
* - A "VA" is a 64-bit canonical guest virtual address. Reads/writes that
|
||||
* cross a page boundary are handled internally (per-page translation).
|
||||
* - Integer returns: 0 on success, negative on failure, unless stated.
|
||||
*/
|
||||
#ifndef VMIE_MEMMODEL_H
|
||||
#define VMIE_MEMMODEL_H
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* Opaque guest-physical memory handle (the mmap'd RAM backing file + segment
|
||||
* map). Defined in src/core/include/core.h; handlers hold only a pointer and
|
||||
* pass it, with a cr3, to the address-space primitives below. */
|
||||
typedef struct vmie_mem vmie_mem;
|
||||
|
||||
/* ---- flat memory view (single owner) ------------------------------------- *
|
||||
* A contiguous view of memory.
|
||||
* data - host pointer to the bytes (borrowed; not owned by the view)
|
||||
* size - number of valid bytes at `data`
|
||||
* base_va - address that data[0] corresponds to (guest VA, or GPA for a
|
||||
* physical view). All matches are reported as base_va + offset. */
|
||||
typedef struct {
|
||||
const uint8_t* data;
|
||||
size_t size;
|
||||
uint64_t base_va;
|
||||
} mem_view_t;
|
||||
|
||||
/* ---- region map ---------------------------------------------------------- *
|
||||
* A vregion is one run of VA-contiguous, present guest pages sharing the same
|
||||
* effective protection. It is the unit of "what is mapped, and how" and the
|
||||
* scoping primitive for the scanners (see scan.h).
|
||||
*
|
||||
* x86-64 has no read bit: a present page is readable, so VR_R is always set on a
|
||||
* returned region. Write/execute/user are the EFFECTIVE rights along the whole
|
||||
* page-table path (RW & US are AND-ed across levels, NX is OR-ed), not just the
|
||||
* leaf entry, so they reflect what the guest CPU actually enforces. */
|
||||
#ifndef VMIE_VREGION_DEFINED
|
||||
#define VMIE_VREGION_DEFINED
|
||||
#define VR_R 0x1u /* readable (present => always set) */
|
||||
#define VR_W 0x2u /* writable (RW bit set at every level) */
|
||||
#define VR_X 0x4u /* executable(NX clear at every level) */
|
||||
#define VR_U 0x8u /* user-accessible (US bit set at every level) */
|
||||
|
||||
typedef struct {
|
||||
uint64_t va; /* run start VA (clamped into the requested [lo,hi] window) */
|
||||
uint64_t len; /* run length in bytes */
|
||||
uint32_t prot; /* OR of VR_* flags */
|
||||
} vregion;
|
||||
#endif
|
||||
|
||||
/* Canonical VA-window bounds of the memory model, shared by every scanning TU.
|
||||
* These describe the address space the contract operates over (the [lo,hi]
|
||||
* windows of gva_regions/gva_sweep), so they are handler-visible.
|
||||
* USER_MIN is 0x10000: the low 64 KiB is reserved, so no live user pointer
|
||||
* targets below it - starting there drops a class of false positives. */
|
||||
#define USER_MIN 0x0000000000010000ull
|
||||
#define USER_MAX 0x00007FFFFFFFFFFFull
|
||||
#define KERN_MIN 0xFFFF800000000000ull
|
||||
|
||||
/* ---- generic boundary types (replace the Windows-typed process/pmodule) --- *
|
||||
* A schedulable address space, decoded by the engine from whatever the guest
|
||||
* OS calls one. `cr3` is all a handler needs to read/write its memory.
|
||||
* cr3 - DirectoryTableBase (PFN-masked); key to this address space
|
||||
* pid, ppid - process / parent ids (ppid == (uint64_t)-1 if unavailable)
|
||||
* name - short image name, NUL-terminated UTF-8 (engine-decoded) */
|
||||
typedef struct {
|
||||
uint64_t cr3;
|
||||
uint64_t pid;
|
||||
uint64_t ppid;
|
||||
char name[16];
|
||||
} task;
|
||||
|
||||
/* A named, contiguous VA range (e.g. a loaded module image), the anchor a
|
||||
* pointer scan walks back to. The engine decodes the name; no LDR entry VA.
|
||||
* base - range base VA (page-aligned)
|
||||
* size - range length in bytes
|
||||
* name - decoded UTF-8 name (e.g. "ntdll.dll"), NUL-terminated */
|
||||
typedef struct {
|
||||
uint64_t base;
|
||||
uint64_t size;
|
||||
char name[64];
|
||||
} range;
|
||||
|
||||
/* ---- guest memory access (hot path) -------------------------------------- */
|
||||
|
||||
/* Read `nmemb` bytes from guest VA `va` (translated under `cr3`) into `dst`.
|
||||
* Crosses page boundaries internally. Returns 0 on success, -1 if any page in
|
||||
* the range is not present/translatable (in which case `dst` is partially
|
||||
* written and must be treated as invalid). */
|
||||
int gva_read(vmie_mem* m, uintptr_t cr3, uintptr_t va, void* dst, size_t nmemb);
|
||||
|
||||
/* Write `nmemb` bytes from `src` to guest VA `va` (translated under `cr3`).
|
||||
* The mapping is RW and coherent, so the guest observes the change. Returns 0
|
||||
* on success, -1 if any page in the range is not present/translatable. */
|
||||
int gva_write(vmie_mem* m, uintptr_t cr3, uintptr_t va, const void* src, size_t nmemb);
|
||||
|
||||
/* Zero-copy borrowed read: host pointer to the guest byte at `va` (under `cr3`),
|
||||
* valid for *avail contiguous bytes (to the end of the containing leaf). NULL if
|
||||
* `va` is not mapped or the leaf is not fully covered by the image (caller falls
|
||||
* back to gva_read). Borrowed: valid until the mapping is closed, do NOT retain. */
|
||||
const void* gva_ptr(vmie_mem* m, uintptr_t cr3, uintptr_t va, size_t* avail);
|
||||
|
||||
/* Enumerate mapped memory under `cr3`, clamped to the VA window [lo,hi]
|
||||
* (inclusive), as runs of equal effective protection.
|
||||
* lo, hi - inclusive VA window; MUST lie within a single canonical half
|
||||
* (entirely user or entirely kernel). Use (0, ~0ull) loosely; the
|
||||
* walk prunes whole subtrees outside the window.
|
||||
* prot_any - protection filter: 0 keeps every run; otherwise a run is kept
|
||||
* only if (run.prot & prot_any) != 0 (e.g. VR_W for writable-only)
|
||||
* out - caller array receiving up to `nmax` `vregion` records
|
||||
* nmax - capacity of `out`
|
||||
* Returns the TOTAL number of matching runs found. If the return value exceeds
|
||||
* `nmax` the output was truncated; enlarge the buffer and retry. */
|
||||
int gva_regions(vmie_mem* m, uintptr_t cr3, uint64_t lo, uint64_t hi,
|
||||
uint32_t prot_any, vregion* out, int nmax);
|
||||
|
||||
/* ---- shared windowed sweep engine ---------------------------------------- *
|
||||
* gva_sweep() streams every mapped byte under `cr3` within [lo,hi] that passes
|
||||
* the protection filter to `cb`, one contiguous window at a time. Physical
|
||||
* fragmentation is hidden: each window is a flat buffer (gva_read-filled), and
|
||||
* adjacent windows of one run share `overlap` leading bytes so an object or
|
||||
* pattern straddling a window boundary is still seen whole. */
|
||||
typedef int (*gva_sweep_cb)(void* user, const uint8_t* data, size_t len,
|
||||
uint64_t base_va, size_t overlap, int last);
|
||||
/* user - passed through verbatim
|
||||
* data - host buffer with `len` valid bytes (do not retain past the call)
|
||||
* len - valid bytes at data
|
||||
* base_va - guest VA of data[0]
|
||||
* overlap - bytes at the front of `data` shared with the previous window of
|
||||
* this run (0 on a run's first window or right after a gap)
|
||||
* last - nonzero if this window ends a contiguous segment (run end / gap):
|
||||
* accept hits up to `len`; otherwise drop hits starting in the
|
||||
* trailing `overlap` zone, the next window re-presents them
|
||||
* cb returns nonzero to abort the sweep early (e.g. result buffer full).
|
||||
*
|
||||
* gva_sweep() returns 0 normally, 1 if a callback aborted it, -1 on allocation
|
||||
* failure. `overlap` must be < the internal window (1 MiB); patterns longer
|
||||
* than that are not supported by the windowed path. */
|
||||
int gva_sweep(vmie_mem* m, uintptr_t cr3, uint64_t lo, uint64_t hi,
|
||||
uint32_t prot_any, size_t overlap, gva_sweep_cb cb, void* user);
|
||||
|
||||
#endif /* VMIE_MEMMODEL_H */
|
||||
+20
-16
@@ -1,16 +1,21 @@
|
||||
/* scan.h - typed value scanner, pointer scanner, and gva<->signature bridges.
|
||||
*
|
||||
* Layered above the pure matcher (sigscan.h) and the gva core (include.h): this
|
||||
* is the gva-bound scanning surface. The value scanner narrows a candidate set
|
||||
* across successive snapshots; the pointer scanner discovers module-anchored
|
||||
* Layered above the pure matcher (sigscan.h) and the generic memory-model
|
||||
* contract (memmodel.h): this is the OS-agnostic scanning surface. Everything
|
||||
* here is keyed by a `vmie_mem*` + `cr3` (and, for the pointer scan, a decoded
|
||||
* `range[]`); it names no Windows object. The value scanner narrows a candidate
|
||||
* set across successive snapshots; the pointer scanner discovers range-anchored
|
||||
* pointer chains; the gva_sig_* bridges build mem_view_t windows out of guest
|
||||
* 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).
|
||||
*/
|
||||
#ifndef VMIE_SCAN_H
|
||||
#define VMIE_SCAN_H
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "include.h" /* gva_ctx, process (vregion - internal) */
|
||||
#include "memmodel.h" /* vmie_mem, range, vregion */
|
||||
#include "sigscan.h" /* mem_view_t, sig_pattern_t */
|
||||
|
||||
/* typed value scanner. ENUMERATOR ORDER IS LOAD-BEARING: scan.c indexes the
|
||||
@@ -32,32 +37,31 @@ typedef struct { uint64_t addr; uint64_t value; } scan_hit;
|
||||
|
||||
#define SCAN_PTR_MAXDEPTH 8 /* DFS depth and size of off[] */
|
||||
typedef struct {
|
||||
uint64_t base; /* module-anchored base address */
|
||||
uint64_t base; /* range-anchored base address */
|
||||
int depth; /* number of offsets in off[] */
|
||||
int32_t off[SCAN_PTR_MAXDEPTH]; /* dereference chain */
|
||||
} scan_ptr_path;
|
||||
|
||||
scan* scan_new(gva_ctx* ctx, const process* pr, scan_type t, const void* value,
|
||||
int be, int aligned, uint64_t lo, uint64_t hi);
|
||||
scan* scan_new_cr3(gva_ctx* ctx, uintptr_t cr3, scan_type t, const void* value,
|
||||
scan* scan_new_cr3(vmie_mem* m, uintptr_t cr3, scan_type t, const void* value,
|
||||
int be, int aligned, uint64_t lo, uint64_t hi);
|
||||
int64_t scan_next(scan* s, scan_op op, const void* value);
|
||||
int64_t scan_count(scan* s);
|
||||
int scan_results(scan* s, uint64_t offset, int max, scan_hit* out);
|
||||
void scan_free(scan* s);
|
||||
|
||||
int scan_pointer(gva_ctx* ctx, const process* pr, uint64_t target,
|
||||
int max_depth, uint32_t max_off, scan_ptr_path* out, int max);
|
||||
int scan_pointer(vmie_mem* m, uintptr_t cr3, const range* mods, int nmods,
|
||||
uint64_t target, int max_depth, uint32_t max_off,
|
||||
scan_ptr_path* out, int max);
|
||||
|
||||
/* gva bridges to the signature matcher: build mem_view from guest memory and feed sigscan.h */
|
||||
int gva_sig_scan (gva_ctx* ctx, uintptr_t cr3, uint64_t lo, uint64_t hi,
|
||||
int gva_sig_scan (vmie_mem* m, uintptr_t cr3, uint64_t lo, uint64_t hi,
|
||||
uint32_t prot_any, const sig_pattern_t* p, uint64_t* out, int max);
|
||||
int gva_sig_first(gva_ctx* ctx, uintptr_t cr3, uint64_t lo, uint64_t hi,
|
||||
int gva_sig_first(vmie_mem* m, uintptr_t cr3, uint64_t lo, uint64_t hi,
|
||||
uint32_t prot_any, const sig_pattern_t* p, uint64_t* va);
|
||||
int gva_sig_rip (gva_ctx* ctx, uintptr_t cr3, uint64_t hit_va,
|
||||
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);
|
||||
int gva_pe_section(gva_ctx* ctx, uintptr_t cr3, uint64_t module_base,
|
||||
const char* name, uint8_t* buf, size_t bufcap, mem_view_t* out);
|
||||
int gva_sig_phys (gva_ctx* ctx, const sig_pattern_t* p, uint64_t* out, int max);
|
||||
|
||||
/* 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. */
|
||||
|
||||
#endif /* VMIE_SCAN_H */
|
||||
|
||||
+4
-33
@@ -6,26 +6,16 @@
|
||||
* results are reported as addresses in the view's own coordinate space
|
||||
* (base_va + offset): a guest VA for a virtual view, a GPA for a physical view.
|
||||
*
|
||||
* This module is pure: it never touches a gva_ctx and performs no I/O. To scan
|
||||
* guest memory, build views from the gva layer (see scan.h: gva_sig_scan,
|
||||
* gva_pe_section, gva_sig_phys) and feed them here.
|
||||
* This module is pure: it never touches a vmie_mem and performs no I/O. To scan
|
||||
* guest memory, build views from the gva layer (see scan.h: gva_sig_scan) and
|
||||
* feed them here.
|
||||
*/
|
||||
#ifndef VMIE_SIGSCAN_H
|
||||
#define VMIE_SIGSCAN_H
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* A contiguous view of memory.
|
||||
* data - host pointer to the bytes (borrowed; not owned by the view)
|
||||
* size - number of valid bytes at `data`
|
||||
* base_va - address that data[0] corresponds to (guest VA, or GPA for a
|
||||
* physical view). All matches are reported as base_va + offset. */
|
||||
typedef struct {
|
||||
const uint8_t* data;
|
||||
size_t size;
|
||||
uint64_t base_va;
|
||||
} mem_view_t;
|
||||
#include "memmodel.h" /* mem_view_t (the single owner of the view type) */
|
||||
|
||||
/* A parsed byte pattern. mask[i] == 1 means bytes[i] must match; 0 = wildcard.
|
||||
* Owns two heap allocations of `len` bytes each; release with sig_free(). */
|
||||
@@ -83,23 +73,4 @@ uint64_t sig_rip(mem_view_t v, uint64_t hit_va, size_t disp_off, size_t instr_le
|
||||
* is actually available. Useful for narrowing a scan to a [start,end] window. */
|
||||
mem_view_t mem_sub(mem_view_t v, uint64_t start_va, size_t size);
|
||||
|
||||
/* Locate a PE section by name within a view that contains at least the image
|
||||
* headers at `module_base` (the first page is enough).
|
||||
* module_base - image base VA, must be >= v.base_va and inside `v`
|
||||
* name - section name, e.g. ".text" (compared up to 8 bytes)
|
||||
* rva_out - receives the section RVA (relative to module_base); may be NULL
|
||||
* vsize_out - receives the section virtual size; may be NULL
|
||||
* Returns true if found. Only the headers need to be present in `v`; the section
|
||||
* body does not. */
|
||||
bool pe_find_section(mem_view_t v, uint64_t module_base, const char* name,
|
||||
uint64_t* rva_out, uint32_t* vsize_out);
|
||||
|
||||
/* Locate a PE section AND return a sub-view spanning it. Requires the whole
|
||||
* section body to be present in `v` (true for an in-memory image dump). Prefer
|
||||
* scanning ".text" over a whole image: faster, and avoids false hits in data.
|
||||
* Returns true and fills *out on success. For guest memory, where the body is
|
||||
* usually not co-resident with the headers, use gva_pe_section (scan.h). */
|
||||
bool pe_section(mem_view_t v, uint64_t module_base, const char* name,
|
||||
mem_view_t* out);
|
||||
|
||||
#endif /* VMIE_SIGSCAN_H */
|
||||
@@ -1,30 +1,33 @@
|
||||
/* include.h - public interface of the Windows VMI core.
|
||||
/* vmie.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.
|
||||
*
|
||||
* Conventions used throughout this header:
|
||||
* 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.
|
||||
* - A "VA" is a 64-bit canonical guest virtual address. A "GPA" is a guest
|
||||
* physical address. Reads/writes that cross a page boundary are handled
|
||||
* internally (per-page translation), so callers pass plain ranges.
|
||||
* - 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_INCLUDE_H
|
||||
#define VMIE_INCLUDE_H
|
||||
#ifndef VMIE_VMIE_H
|
||||
#define VMIE_VMIE_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/include/memory.h; callers only
|
||||
* ever hold a pointer. Created by gva_ctx_alloc(), populated by host_bootstrap(),
|
||||
* released by gva_ctx_free(). */
|
||||
typedef struct gva_ctx gva_ctx;
|
||||
/* 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;
|
||||
|
||||
/* 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.
|
||||
@@ -69,26 +72,6 @@ typedef struct {
|
||||
gtext path;
|
||||
} pmodule;
|
||||
|
||||
/* ---- region map ---------------------------------------------------------- *
|
||||
* A vregion is one run of VA-contiguous, present guest pages sharing the same
|
||||
* effective protection. It is the unit of "what is mapped, and how" and the
|
||||
* scoping primitive for the scanners (see scan.h).
|
||||
*
|
||||
* x86-64 has no read bit: a present page is readable, so VR_R is always set on a
|
||||
* returned region. Write/execute/user are the EFFECTIVE rights along the whole
|
||||
* page-table path (RW & US are AND-ed across levels, NX is OR-ed), not just the
|
||||
* leaf entry, so they reflect what the guest CPU actually enforces. */
|
||||
#define VR_R 0x1u /* readable (present => always set) */
|
||||
#define VR_W 0x2u /* writable (RW bit set at every level) */
|
||||
#define VR_X 0x4u /* executable(NX clear at every level) */
|
||||
#define VR_U 0x8u /* user-accessible (US bit set at every level) */
|
||||
|
||||
typedef struct {
|
||||
uint64_t va; /* run start VA (clamped into the requested [lo,hi] window) */
|
||||
uint64_t len; /* run length in bytes */
|
||||
uint32_t prot; /* OR of VR_* flags */
|
||||
} vregion;
|
||||
|
||||
/* ---- lifecycle ----------------------------------------------------------- */
|
||||
|
||||
/* Open `ram_path` (the guest RAM backing file) and build a context over it.
|
||||
@@ -97,33 +80,28 @@ 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 gva_ctx_free(). */
|
||||
gva_ctx* gva_ctx_alloc(const char* ram_path, uint64_t low);
|
||||
* failure. Free with vmie_close(). */
|
||||
vmie* vmie_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 gva_ctx_free(gva_ctx* ctx);
|
||||
void vmie_close(vmie* 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
|
||||
* NULL `v`. */
|
||||
vmie_mem* vmie_memory(vmie* 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 gva_ctx_alloc(). */
|
||||
int host_bootstrap(gva_ctx* ctx);
|
||||
* that failed. Cold path: call once after vmie_open(). */
|
||||
int host_bootstrap(vmie* v);
|
||||
|
||||
/* ---- guest memory access (hot path) -------------------------------------- */
|
||||
|
||||
/* Read `nmemb` bytes from guest VA `va` (translated under `cr3`) into `dst`.
|
||||
* Crosses page boundaries internally. Returns 0 on success, -1 if any page in
|
||||
* the range is not present/translatable (in which case `dst` is partially
|
||||
* written and must be treated as invalid). */
|
||||
int gva_read(gva_ctx* ctx, uintptr_t cr3, uintptr_t va, void* dst, size_t nmemb);
|
||||
|
||||
/* Write `nmemb` bytes from `src` to guest VA `va` (translated under `cr3`).
|
||||
* The mapping is RW and coherent, so the guest observes the change. Returns 0
|
||||
* on success, -1 if any page in the range is not present/translatable. */
|
||||
int gva_write(gva_ctx* ctx, uintptr_t cr3, uintptr_t va, const void* src, size_t nmemb);
|
||||
/* ---- 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
|
||||
@@ -133,7 +111,7 @@ int gva_write(gva_ctx* ctx, uintptr_t cr3, uintptr_t va, const void* src, size_t
|
||||
* 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(gva_ctx* ctx, uintptr_t cr3, uintptr_t va, size_t nmemb, char* dst, size_t size);
|
||||
size_t gva_read_text(vmie* v, uintptr_t cr3, uintptr_t va, size_t nmemb, char* dst, size_t size);
|
||||
|
||||
/* ---- enumeration --------------------------------------------------------- */
|
||||
|
||||
@@ -143,30 +121,29 @@ size_t gva_read_text(gva_ctx* ctx, uintptr_t cr3, uintptr_t va, size_t nmemb, ch
|
||||
* 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(gva_ctx* ctx, int skip_system, process* dst, size_t nmax);
|
||||
int proc_list(vmie* 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(gva_ctx* ctx, const process* pr, pmodule* dst, size_t nmax);
|
||||
int proc_modules(vmie* v, const process* pr, pmodule* dst, size_t nmax);
|
||||
|
||||
/* Enumerate mapped memory under `cr3`, clamped to the VA window [lo,hi]
|
||||
* (inclusive), as runs of equal effective protection.
|
||||
* cr3 - address space to walk (a process cr3, or the kernel cr3)
|
||||
* lo, hi - inclusive VA window; MUST lie within a single canonical half
|
||||
* (entirely user or entirely kernel). Use (0, ~0ull) loosely; the
|
||||
* walk prunes whole subtrees outside the window.
|
||||
* prot_any - protection filter: 0 keeps every run; otherwise a run is kept
|
||||
* only if (run.prot & prot_any) != 0 (e.g. VR_W for writable-only,
|
||||
* VR_X for executable-only)
|
||||
* out - caller array receiving up to `nmax` `vregion` records
|
||||
* nmax - capacity of `out`
|
||||
* Returns the TOTAL number of matching runs found. If the return value exceeds
|
||||
* `nmax` the output was truncated (only `nmax` runs were written); enlarge the
|
||||
* buffer and retry for the full map. */
|
||||
int gva_regions(gva_ctx* ctx, uintptr_t cr3, uint64_t lo, uint64_t hi,
|
||||
uint32_t prot_any, vregion* out, int 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. */
|
||||
|
||||
#endif /* VMIE_INCLUDE_H */
|
||||
/* 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,
|
||||
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 max_depth, uint32_t max_off, scan_ptr_path* out, int max);
|
||||
|
||||
#endif /* VMIE_VMIE_H */
|
||||
Reference in New Issue
Block a user