/* * spoof-core.c — seed/config acquisition + the deterministic derivation engine * + the policy knobs (hv mode / WAET / vmgenid). See spoof-core.h, spoof.h. */ #include "qemu/osdep.h" #include "qemu/cutils.h" /* pstrcpy */ #include "hw/misc/spoof.h" #include "hw/misc/spoof-core.h" #include "hw/core/boards.h" /* current_machine */ #include "qom/object.h" const char SPOOF_A36[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const char SPOOF_LET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const char SPOOF_DIG[] = "0123456789"; /* ---- config: machine property first, env var as a test fallback ----------- */ static char g_seed[256]; static bool g_ready; static char g_mode[16], g_hv[16], g_waet[16], g_vgid[16], g_pvp[16]; static void read_prop(const char *prop, const char *env, char *out, size_t n) { out[0] = '\0'; if (current_machine && object_property_find(OBJECT(current_machine), prop)) { char *v = object_property_get_str(OBJECT(current_machine), prop, NULL); if (v) { pstrcpy(out, n, v); g_free(v); } } if (!out[0]) { const char *e = getenv(env); if (e) { pstrcpy(out, n, e); } } } static void config_load(void) { if (g_ready) { return; } read_prop("spoof-seed", "QEMU_SPOOF_SEED", g_seed, sizeof(g_seed)); read_prop("spoof-mode", "QEMU_SPOOF_MODE", g_mode, sizeof(g_mode)); read_prop("spoof-hv", "QEMU_SPOOF_HV", g_hv, sizeof(g_hv)); read_prop("spoof-waet", "QEMU_SPOOF_WAET", g_waet, sizeof(g_waet)); read_prop("spoof-vmgenid", "QEMU_SPOOF_VMGENID", g_vgid, sizeof(g_vgid)); read_prop("spoof-pvpanic", "QEMU_SPOOF_PVPANIC", g_pvp, sizeof(g_pvp)); g_ready = true; } bool spoof_on(void) { return spoof_mode() != SPOOF_MODE_NONE; } bool spoof_enabled(void) { return spoof_on(); } SpoofMode spoof_mode(void) { config_load(); if (!g_seed[0]) return SPOOF_MODE_NONE; if (!strcmp(g_mode, "none")) return SPOOF_MODE_NONE; if (!strcmp(g_mode, "hyperv")) return SPOOF_MODE_HYPERV; if (!strcmp(g_mode, "vbs")) return SPOOF_MODE_VBS; if (!strcmp(g_mode, "physical")) return SPOOF_MODE_PHYSICAL; /* back-compat with the old spoof-hv knob (real-OEM persona either way) */ if (!strcmp(g_hv, "hidden")) return SPOOF_MODE_PHYSICAL; if (!strcmp(g_hv, "hyperv")) return SPOOF_MODE_VBS; if (!strcmp(g_hv, "off")) return SPOOF_MODE_NONE; return SPOOF_MODE_VBS; /* seeded default = VBS (real-OEM + Hyper-V) */ } bool spoof_persona_msvm(void) { return spoof_mode() == SPOOF_MODE_HYPERV; } bool spoof_presence_hyperv(void) { SpoofMode m = spoof_mode(); return m == SPOOF_MODE_HYPERV || m == SPOOF_MODE_VBS; } /* ---- deterministic derivation: fnv1a(seed|key) -> splitmix64 -------------- */ static uint64_t fnv1a(const char *s) { uint64_t h = 1469598103934665603ULL; for (; *s; s++) { h ^= (uint8_t)*s; h *= 1099511628211ULL; } return h; } static uint64_t mix(uint64_t x) { x += 0x9E3779B97F4A7C15ULL; x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL; x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL; return x ^ (x >> 31); } uint64_t spoof_field(const char *key) { config_load(); return mix(fnv1a(g_seed) ^ (fnv1a(key) * 0x100000001B3ULL)); } uint64_t spoof_field_n(const char *key, unsigned i) { char k[96]; snprintf(k, sizeof(k), "%s#%u", key, i); return spoof_field(k); } const char *spoof_pick(const char *key, const char *const *arr, size_t n) { return arr[spoof_field(key) % n]; } void spoof_gen(const char *key, const char *cs, int len, char *out) { size_t m = strlen(cs); for (int i = 0; i < len; i++) { out[i] = cs[spoof_field_n(key, (unsigned)i) % m]; } out[len] = '\0'; } /* one cpu-vendor draw anchors BOTH the CPU signature and the chipset/PCI vendor. */ int spoof_anchor_vendor(void) { return (int)(spoof_field("anchor.cpu") & 1); /* 0 Intel, 1 AMD */ } /* ---- policy knobs --------------------------------------------------------- */ SpoofHvMode spoof_hv_mode(void) { switch (spoof_mode()) { /* derived from the mode preset */ case SPOOF_MODE_HYPERV: case SPOOF_MODE_VBS: return SPOOF_HV_HYPERV; /* present Hyper-V */ case SPOOF_MODE_PHYSICAL: return SPOOF_HV_HIDDEN; /* bare metal */ default: return SPOOF_HV_OFF; } } bool spoof_waet_drop(void) { config_load(); if (!strcmp(g_waet, "on")) return true; if (!strcmp(g_waet, "off")) return false; return spoof_on(); /* any spoof mode drops WAET */ } bool spoof_pvpanic_hide(void) { config_load(); if (!strcmp(g_pvp, "on")) return true; if (!strcmp(g_pvp, "off")) return false; return spoof_on(); /* QEMU pvpanic isn't a Hyper-V/real device */ } SpoofVgidPolicy spoof_vmgenid_policy(void) { config_load(); if (!strcmp(g_vgid, "hide")) return SPOOF_VGID_HIDE; if (!strcmp(g_vgid, "mask")) return SPOOF_VGID_MASK; if (!strcmp(g_vgid, "keep")) return SPOOF_VGID_KEEP; switch (spoof_hv_mode()) { /* default tracks hv mode */ case SPOOF_HV_HIDDEN: return SPOOF_VGID_HIDE; case SPOOF_HV_HYPERV: return SPOOF_VGID_MASK; default: return SPOOF_VGID_KEEP; } }