mirror of
https://dev.lirent.ru/Vatrog/vm-introspection-engine.git
synced 2026-06-18 02:06:36 +03:00
1ec70b7ede
Static library over a flat RW mmap of guest RAM: GPA/GVA paging walks, beacon-driven bootstrap, dynamic struct-offset profiling, process and module enumeration, a region map, and value/pointer/signature scanners on a shared windowed sweep. Public API in include/; internals under src/. Thin CLI demonstrator over the public API. Guest agent cross-compiled to Windows x86-64 via mingw-w64. CMake: static library + CLI + guest target, C17.
105 lines
5.4 KiB
C
105 lines
5.4 KiB
C
/* sigscan.h - source-agnostic x86-64 signature scanner.
|
|
*
|
|
* Everything operates on a mem_view_t: a flat byte span plus the virtual address
|
|
* that data[0] maps to. Live guest memory, a retained snapshot, and an on-disk
|
|
* dump are identical to the matcher - only how you build the view differs. All
|
|
* 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.
|
|
*/
|
|
#ifndef W32MS_SIGSCAN_H
|
|
#define W32MS_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;
|
|
|
|
/* 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(). */
|
|
typedef struct {
|
|
uint8_t* bytes;
|
|
uint8_t* mask;
|
|
size_t len;
|
|
} sig_pattern_t;
|
|
|
|
/* Parse an IDA-style string, e.g. "48 8B 05 ? ? ? ? 48 85 C0" ('?' or '??' =
|
|
* wildcard). On success fills *out and returns true; free it with sig_free().
|
|
* Returns false on NULL args, an empty string, or a malformed hex byte. */
|
|
bool sig_parse_ida(const char* ida, sig_pattern_t* out);
|
|
|
|
/* Parse code+mask form, e.g. bytes="\x48\x8B\x05\x00\x00\x00\x00", mask="xxx????"
|
|
* ('x'/'X' = must match, anything else = wildcard). `bytes` must have at least
|
|
* strlen(mask) readable bytes. Returns true on success (free with sig_free()),
|
|
* false on NULL args or an empty mask. */
|
|
bool sig_parse_mask(const uint8_t* bytes, const char* mask, 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);
|
|
|
|
/* Invoke cb(user, match_va) for every match of `p` in `v`, in ascending address
|
|
* order. The driver anchors on the pattern's first non-wildcard byte and uses
|
|
* memchr to skip, so it is fast even on sparse matches. `cb` returns nonzero to
|
|
* stop early. This is the building block under sig_first/sig_all and is what a
|
|
* windowed caller uses to de-duplicate across window seams (see scan.h). */
|
|
void sig_each(mem_view_t v, const sig_pattern_t* p,
|
|
int (*cb)(void* user, uint64_t va), void* user);
|
|
|
|
/* First match, or 0 if none. (0 is also a theoretically valid base_va of 0; in
|
|
* practice view base addresses are nonzero, so 0 reliably means "no match".) */
|
|
uint64_t sig_first(mem_view_t v, const sig_pattern_t* p);
|
|
|
|
/* All matches. If `out` is NULL, returns the total match count (use it to size a
|
|
* buffer). Otherwise writes up to `max` addresses to `out` and returns how many
|
|
* were written (capped at `max`). */
|
|
size_t sig_all(mem_view_t v, const sig_pattern_t* p, uint64_t* out, size_t max);
|
|
|
|
/* Resolve an x86-64 RIP-relative operand at a match site.
|
|
* hit_va - VA of the matched pattern start (== instruction start)
|
|
* disp_off - byte offset of the int32 displacement within the pattern
|
|
* instr_len - full instruction length (next RIP = hit_va + instr_len); for the
|
|
* common "<prefix> disp32" tail this is disp_off + 4
|
|
* Returns the absolute target VA, or 0 if the displacement bytes lie outside `v`.
|
|
* The result is an address in the same space as `v` (a guest VA for a guest
|
|
* view): dereference it with gva_read under the matching cr3. This is how an
|
|
* unexported global is located from a code signature. */
|
|
uint64_t sig_rip(mem_view_t v, uint64_t hit_va, size_t disp_off, size_t instr_len);
|
|
|
|
/* Clamp a sub-view [start_va, start_va+size) against `v`. Returns a zeroed view
|
|
* (data == NULL) if start_va is outside `v`; otherwise `size` is trimmed to what
|
|
* 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 /* W32MS_SIGSCAN_H */ |