From 25b8ed8ca98ddc0d5fa081c997a6a8808f43bc3c Mon Sep 17 00:00:00 2001 From: Gregory Lirent Date: Tue, 16 Jun 2026 16:25:27 +0300 Subject: [PATCH] Add a dump-scan demonstrator (vmie_scan) A thin CLI proving the OS-agnostic dump path end to end: open one or more raw memory dumps as flat identity images (vmie_mem) and scan them all for an IDA-style pattern, printing each hit as source:gpa. Two-pass (count, then size the buffer exactly) so nothing is silently truncated. Kept separate from vmie_cli rather than folded in as a subcommand: vmie_cli demonstrates live win32 bring-up, this demonstrates the source-agnostic scan. Its source includes only the public memmodel/sigscan/scan headers and names no Windows symbol - it compiles against include/ alone. --- CMakeLists.txt | 5 +++ src/scan_cli.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/scan_cli.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5abb9c3..406250a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,11 @@ add_executable(vmie_cli src/cli.c) target_link_libraries(vmie_cli PRIVATE vmie) # public include/ comes via vmie (PUBLIC) target_compile_options(vmie_cli PRIVATE -Wall -Wextra) +# ---- host: dump-scan demonstrator (OS-agnostic, no win32) ---------------- +add_executable(vmie_scan src/scan_cli.c) +target_link_libraries(vmie_scan PRIVATE vmie) +target_compile_options(vmie_scan PRIVATE -Wall -Wextra) + # ---- guest: cross-compile to Windows x86-64 via mingw-w64 --------------- find_program(MINGW_CC NAMES x86_64-w64-mingw32-gcc REQUIRED) set(VMIE_STARTUP ${CMAKE_CURRENT_BINARY_DIR}/vmie-startup.exe) diff --git a/src/scan_cli.c b/src/scan_cli.c new file mode 100644 index 0000000..fe3fa28 --- /dev/null +++ b/src/scan_cli.c @@ -0,0 +1,91 @@ +/* scan_cli.c - thin demonstrator of the OS-agnostic dump -> signature path. + * + * Opens one or more raw memory dumps as flat physical images (vmie_mem) and + * scans them all for an IDA-style byte pattern, printing every hit as + * "source:gpa". No guest, no bootstrap, no win32: this links only the dump + * source (core), the physical signature bridge, and the pure matcher - proof + * that scanning a dump needs nothing from the Windows engine. + * + * argv[1] IDA pattern, e.g. "48 8B 05 ? ? ? ? 48 85 C0" + * argv[2..] one or more dump files, each mapped as a flat identity image + * (reported GPA == file offset) + */ +#include +#include +#include +#include +#include +#include "memmodel.h" /* vmie_mem_open / vmie_mem_close */ +#include "sigscan.h" /* sig_parse_ida / sig_pattern_t / sig_free */ +#include "scan.h" /* sig_scan_sources / sig_hit_src */ + +int main(int argc, char** argv) { + if (argc < 3) { + fprintf(stderr, "usage: %s [dump-file...]\n", + argc > 0 ? argv[0] : "vmie_scan"); + return 2; + } + + sig_pattern_t pat; + if (!sig_parse_ida(argv[1], &pat)) { + fprintf(stderr, "error: bad IDA pattern: '%s'\n", argv[1]); + return 2; + } + + const int nsrc = argc - 2; + vmie_mem** srcs = calloc((size_t)nsrc, sizeof *srcs); + if (!srcs) { + fprintf(stderr, "error: out of memory\n"); + sig_free(&pat); + return 1; + } + + int rc = 0, opened = 0; + for (int i = 0; i < nsrc; i++) { + const char* path = argv[2 + i]; + srcs[i] = vmie_mem_open(path, ~0ull); /* low >= fsize => flat identity dump */ + if (!srcs[i]) { + fprintf(stderr, "error: cannot open dump '%s'\n", path); + rc = 1; + goto cleanup; + } + opened++; + } + + { + /* count pass (out == NULL), then size the buffer exactly: no caps, no + * silent truncation. */ + const int total = sig_scan_sources(srcs, nsrc, &pat, NULL, 0); + if (total < 0) { + fprintf(stderr, "error: scan failed (%d)\n", total); + rc = 1; + goto cleanup; + } + if (total == 0) { + printf("no matches in %d source(s)\n", nsrc); + goto cleanup; + } + + sig_hit_src* hits = malloc((size_t)total * sizeof *hits); + if (!hits) { + fprintf(stderr, "error: out of memory\n"); + rc = 1; + goto cleanup; + } + const int got = sig_scan_sources(srcs, nsrc, &pat, hits, total); + for (int i = 0; i < got; i++) { + printf("%d:%016" PRIx64 " %s\n", + hits[i].source, hits[i].gpa, argv[2 + hits[i].source]); + } + printf("%d match(es) across %d source(s)\n", got, nsrc); + free(hits); + } + +cleanup: + for (int i = 0; i < opened; i++) { + vmie_mem_close(srcs[i]); + } + free(srcs); + sig_free(&pat); + return rc; +}