/* cli.c - thin demonstrator over the public vmi-engine API. * * Opens a guest RAM backing file, brings up the VMI context, lists processes, * and for the first user process dumps its loaded modules and mapped regions. * Public surface only (include/vmie.h); the region walk takes a vmie_mem*, * borrowed from the engine via vmie_memory(). * * argv[1] path to the guest RAM backing file * argv[2] `low` - size in bytes of below-4G guest RAM (strtoull, base 0) * argv[3] optional cap on the process count (default 512) */ #include #include #include #include #include #include "vmie.h" #define DEFAULT_NMAX 512 #define MOD_CAP 256 #define RGN_CAP 4096 #define TEXT_CAP 512 static const char* bootstrap_stage(int rc) { switch (rc) { case -1: return "beacon not found in guest RAM"; case -2: return "could not recover a bootstrap CR3"; case -3: return "ntoskrnl not located"; case -4: return "PsInitialSystemProcess unresolved"; case -5: return "struct-offset profile build failed"; case -6: return "kernel DirectoryTableBase read failed"; default: return "unknown bootstrap failure"; } } static void decode_prot(uint32_t prot, char out[5]) { out[0] = (prot & VR_R) ? 'R' : '-'; out[1] = (prot & VR_W) ? 'W' : '-'; out[2] = (prot & VR_X) ? 'X' : '-'; out[3] = (prot & VR_U) ? 'U' : '-'; out[4] = 0; } static void dump_modules(vmie* ctx, const process* pr) { pmodule mods[MOD_CAP]; const int nm = proc_modules(ctx, pr, mods, MOD_CAP); if (nm <= 0) { printf(" (no modules)\n"); return; } for (int i = 0; i < nm; i++) { char name[TEXT_CAP], path[TEXT_CAP]; /* module strings are user-space: read under the process cr3. */ if (mods[i].name.va) { gva_read_text(ctx, pr->cr3, mods[i].name.va, mods[i].name.len, name, sizeof name); } else { name[0] = 0; } if (mods[i].path.va) { gva_read_text(ctx, pr->cr3, mods[i].path.va, mods[i].path.len, path, sizeof path); } else { path[0] = 0; } printf(" %016" PRIx64 " %8" PRIu32 " %-24s %s\n", mods[i].base, mods[i].size, name[0] ? name : "", path[0] ? path : ""); } } static void dump_regions(vmie* ctx, const process* pr) { vregion* rg = malloc((size_t)RGN_CAP * sizeof *rg); if (!rg) { return; } const int total = gva_regions(vmie_memory(ctx), pr->cr3, 0, ~0ull, 0, rg, RGN_CAP); const int shown = total < 0 ? 0 : (total < RGN_CAP ? total : RGN_CAP); for (int i = 0; i < shown; i++) { char prot[5]; decode_prot(rg[i].prot, prot); printf(" %016" PRIx64 " %12" PRIu64 " %s\n", rg[i].va, rg[i].len, prot); } if (total > shown) { printf(" ... (%d more regions truncated)\n", total - shown); } free(rg); } int main(int argc, char** argv) { if (argc < 3) { fprintf(stderr, "usage: %s [nmax]\n", argc > 0 ? argv[0] : "vmie_cli"); return 2; } const char* ram_path = argv[1]; const uint64_t low = strtoull(argv[2], NULL, 0); size_t nmax = DEFAULT_NMAX; if (argc > 3) { const unsigned long long v = strtoull(argv[3], NULL, 0); if (v > 0) { nmax = (size_t)v; } } vmie* ctx = vmie_open(ram_path, low); if (!ctx) { fprintf(stderr, "error: cannot open RAM backing file '%s'\n", ram_path); return 1; } const int rc = host_bootstrap(ctx); if (rc != 0) { fprintf(stderr, "error: bootstrap failed (%d): %s\n", rc, bootstrap_stage(rc)); vmie_close(ctx); return 1; } process* procs = malloc(nmax * sizeof *procs); if (!procs) { fprintf(stderr, "error: out of memory\n"); vmie_close(ctx); return 1; } const int np = proc_list(ctx, 1, procs, nmax); if (np < 0) { fprintf(stderr, "error: proc_list failed (%d)\n", np); free(procs); vmie_close(ctx); return 1; } printf("%-6s %-6s %-16s %-18s %-18s %-18s\n", "PID", "PPID", "NAME", "CR3", "EPROCESS", "PEB"); for (int i = 0; i < np; i++) { const process* p = &procs[i]; char ppid[12]; if (p->ppid == (uint32_t)-1) { snprintf(ppid, sizeof ppid, "%s", "?"); } else { snprintf(ppid, sizeof ppid, "%" PRIu32, p->ppid); } printf("%-6" PRIu32 " %-6s %-16s %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n", p->pid, ppid, p->name[0] ? p->name : "", p->cr3, p->eprocess, p->peb); } if (np > 0) { const process* first = &procs[0]; printf("\nmodules of PID %" PRIu32 " (%s):\n", first->pid, first->name[0] ? first->name : ""); dump_modules(ctx, first); printf("\nregions of PID %" PRIu32 " (%s):\n", first->pid, first->name[0] ? first->name : ""); dump_regions(ctx, first); } free(procs); vmie_close(ctx); return 0; }