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:
2026-06-15 08:20:50 +03:00
parent b3441dd6f6
commit 93966c3df2
21 changed files with 383 additions and 211 deletions
+63
View File
@@ -0,0 +1,63 @@
#include "pe.h"
#include <string.h>
#include "memmodel.h" /* gva_read */
#include "sigscan.h" /* mem_sub (pure matcher; engine may use it) */
bool pe_find_section(mem_view_t v, uint64_t module_base, const char* name,
uint64_t* rva_out, uint32_t* vsize_out) {
if (!v.data || !name || module_base < v.base_va) return false;
const size_t mo = (size_t)(module_base - v.base_va);
if (mo + 0x40 > v.size) return false;
if (v.data[mo] != 'M' || v.data[mo + 1] != 'Z') return false;
int32_t e_lfanew;
memcpy(&e_lfanew, v.data + mo + 0x3C, 4);
const size_t nt = mo + (size_t)(uint32_t)e_lfanew;
if (nt + 0x18 > v.size) return false;
if (memcmp(v.data + nt, "PE\0\0", 4) != 0) return false;
uint16_t nsec, opt_size;
memcpy(&nsec, v.data + nt + 6, 2); /* NumberOfSections */
memcpy(&opt_size, v.data + nt + 20, 2); /* SizeOfOptionalHeader */
const size_t sec = nt + 24 + opt_size; /* first section header */
size_t want = strlen(name);
if (want > 8) want = 8;
for (uint16_t i = 0; i < nsec; i++) {
const size_t sh = sec + (size_t)i * 40;
if (sh + 40 > v.size) break;
char nm[9] = {0};
memcpy(nm, v.data + sh, 8);
if (strncmp(nm, name, want) == 0 && (want == 8 || nm[want] == '\0')) {
uint32_t vsize, vaddr;
memcpy(&vsize, v.data + sh + 8, 4); /* Misc.VirtualSize */
memcpy(&vaddr, v.data + sh + 12, 4); /* VirtualAddress */
if (rva_out) *rva_out = vaddr;
if (vsize_out) *vsize_out = vsize;
return true;
}
}
return false;
}
bool pe_section(mem_view_t v, uint64_t module_base, const char* name, mem_view_t* out) {
uint64_t rva; uint32_t vsize;
if (!out || !pe_find_section(v, module_base, name, &rva, &vsize)) return false;
*out = mem_sub(v, module_base + rva, vsize);
return out->data != NULL;
}
int vmie_pe_section(vmie_mem* m, uintptr_t cr3, uint64_t module_base,
const char* name, uint8_t* buf, size_t bufcap, mem_view_t* out) {
uint8_t hdr[0x1000];
if (!out || !buf || gva_read(m, cr3, module_base, hdr, sizeof hdr)) return -1;
const mem_view_t hv = { hdr, sizeof hdr, module_base };
uint64_t rva; uint32_t vsize;
if (!pe_find_section(hv, module_base, name, &rva, &vsize)) return -1;
const size_t n = vsize < bufcap ? vsize : bufcap;
if (gva_read(m, cr3, module_base + rva, buf, n)) return -1;
out->data = buf; out->size = n; out->base_va = module_base + rva;
return 0;
}