qemu-spoof: seed-driven per-VM hardware-identity anti-detection for pve-qemu

This commit is contained in:
2026-06-11 17:34:09 +03:00
commit 06463ee65c
33 changed files with 1788 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
/* spoof-acpi.c — ACPI table header identity. OEM id / table id come from the
* PLATFORM anchor (spoof-platform.c) so they cohere with the machine / BIOS /
* baseboard. Only the creator id (the ASL compiler) is its own pool. */
#include "qemu/osdep.h"
#include "hw/misc/spoof.h"
#include "hw/misc/spoof-core.h"
/* 4-byte creator id: real machines are overwhelmingly the Intel ASL compiler. */
static const char *const CREATOR[] = { "INTL", "INTL", "INTL", "MSFT", "AMI ", "ACPI", "PTL " };
const char *spoof_acpi_oem_id(const char *def)
{
return spoof_on() ? spoof_plat_acpi_oem() : def;
}
const char *spoof_acpi_oem_table_id(const char *def)
{
return spoof_on() ? spoof_plat_acpi_table() : def;
}
const char *spoof_acpi_creator_id(const char *def)
{
return spoof_on() ? SPOOF_PICK("acpi.creator", CREATOR) : def;
}
+22
View File
@@ -0,0 +1,22 @@
/* spoof-audio.c — HDA codec identity (real machines = Realtek ALC, not a QEMU id). */
#include "qemu/osdep.h"
#include "hw/misc/spoof.h"
#include "hw/misc/spoof-core.h"
/* 32-bit HDA codec vendor/device id (Realtek vendor 0x10EC). */
static const uint32_t HDA_ID[] = {
0x10EC0892, 0x10EC1220, 0x10EC0887, 0x10EC0255, 0x10EC0256, 0x10EC0897, 0x10EC0233,
};
static const char *const HDA_NAME[] = {
"Realtek ALC892", "Realtek ALC1220", "Realtek ALC887", "Realtek ALC255",
"Realtek ALC256", "Realtek ALC897", "Realtek ALC233",
};
uint32_t spoof_hda_vendor_id(uint32_t def)
{
return spoof_on() ? HDA_ID[spoof_field("hda.id") % ARRAY_SIZE(HDA_ID)] : def;
}
const char *spoof_hda_name(const char *def)
{
return spoof_on() ? SPOOF_PICK("hda.name", HDA_NAME) : def;
}
+128
View File
@@ -0,0 +1,128 @@
/*
* 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, g_enabled;
static char 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-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_enabled = g_seed[0] != '\0';
g_ready = true;
}
bool spoof_on(void) { config_load(); return g_enabled; }
bool spoof_enabled(void) { return spoof_on(); }
/* ---- 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)
{
config_load();
if (!strcmp(g_hv, "hidden")) return SPOOF_HV_HIDDEN;
if (!strcmp(g_hv, "hyperv")) return SPOOF_HV_HYPERV;
if (!strcmp(g_hv, "off")) return SPOOF_HV_OFF;
return g_enabled ? SPOOF_HV_HYPERV : SPOOF_HV_OFF; /* seeded default = hyperv */
}
bool spoof_waet_drop(void)
{
config_load();
if (!strcmp(g_waet, "on")) return true;
if (!strcmp(g_waet, "off")) return false;
return g_enabled; /* seeded default = drop */
}
bool spoof_pvpanic_hide(void)
{
config_load();
if (!strcmp(g_pvp, "on")) return true;
if (!strcmp(g_pvp, "off")) return false;
return g_enabled; /* seeded default = hide pvpanic */
}
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;
}
}
+41
View File
@@ -0,0 +1,41 @@
/*
* spoof-core.h — the engine shared by every spoof-<aspect>.c (INTERNAL).
*
* Pure, seed-driven derivation: no entropy, no files, deterministic. Aspect
* modules use these to pick from pools or generate format-correct values, and
* cache their own result (lazily) for stable storage.
*/
#ifndef QEMU_SPOOF_CORE_H
#define QEMU_SPOOF_CORE_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif
bool spoof_on(void); /* seed present */
uint64_t spoof_field(const char *key); /* stable 64-bit draw for a key */
uint64_t spoof_field_n(const char *key, unsigned i); /* nth independent draw */
const char *spoof_pick(const char *key, const char *const *arr, size_t n);
void spoof_gen(const char *key, const char *cs, int len, char *out); /* len chars from cs */
int spoof_anchor_vendor(void); /* 0=Intel, 1=AMD (stable per seed) */
/* platform anchor (spoof-platform.c): one coherent machine identity per seed. */
const char *spoof_plat_acpi_oem(void); /* 6-byte ACPI OEM id */
const char *spoof_plat_acpi_table(void); /* 8-byte ACPI OEM table id */
const char *spoof_plat_machine_desc(void); /* board model (CPU-vendor matched) */
const char *spoof_plat_socket(void); /* that board's CPU socket */
const char *spoof_plat_bios_vendor(void);
const char *spoof_plat_baseboard(void);
const char *spoof_plat_oem_string(void); /* SMBIOS type11 */
extern const char SPOOF_A36[37]; /* "0-9A-Z" + NUL */
extern const char SPOOF_LET[27]; /* "A-Z" + NUL */
extern const char SPOOF_DIG[11]; /* "0-9" + NUL */
#define SPOOF_PICK(key, arr) spoof_pick((key), (arr), ARRAY_SIZE(arr))
#endif /* QEMU_SPOOF_CORE_H */
+44
View File
@@ -0,0 +1,44 @@
/* spoof-cpu.c — CPUID hypervisor signature, anchored to the CPU vendor. */
#include "qemu/osdep.h"
#include "hw/misc/spoof.h"
#include "hw/misc/spoof-core.h"
/* index 0 = Intel, 1 = AMD (the anchor). The chipset/PCI vendor tracks this. */
static const char *const KVM_SIG[] = { "GenuineIntel", "AuthenticAMD" };
const char *spoof_kvm_signature(const char *def)
{
return spoof_on() ? KVM_SIG[spoof_anchor_vendor()] : def;
}
/* CPUID leaf 0x16 (Processor Frequency Information) — QEMU returns zeros; real
* CPUs report base/max/bus MHz. Fill plausible, self-consistent values. */
static const int BASE_MHZ[] = { 2400, 2900, 3000, 3200, 3400, 3600, 3700, 3800 };
static const int TURBO_MHZ[] = { 400, 600, 800, 1000, 1200, 1400 };
int spoof_cpu_base_mhz(int def)
{
return spoof_on() ? BASE_MHZ[spoof_field("cpu.base") % ARRAY_SIZE(BASE_MHZ)] : def;
}
int spoof_cpu_max_mhz(int def)
{
if (!spoof_on()) return def;
return spoof_cpu_base_mhz(0) + TURBO_MHZ[spoof_field("cpu.turbo") % ARRAY_SIZE(TURBO_MHZ)];
}
int spoof_cpu_bus_mhz(int def)
{
return spoof_on() ? 100 : def;
}
/* Microcode revision, returned already positioned for the MSR (IA32_UCODE_REV
* 0x8B): Intel reports the revision in bits 63:32, AMD the patch level in 31:0.
* Stock QEMU/KVM defaults (Intel 0x1_00000000, AMD 0x01000065) follow the same
* layout, so the seeded value stays a drop-in. Anchored to the persona's CPU
* vendor (the operator drives -cpu to match, as with the KVM signature). */
uint64_t spoof_cpu_microcode(uint64_t def)
{
if (!spoof_on()) return def;
if (spoof_anchor_vendor()) { /* AMD: patch level, low dword */
return 0x0A201000ull | (uint64_t)(spoof_field("ucode") & 0xFFF);
}
return ((uint64_t)(0xC0u | (spoof_field("ucode") & 0x3F))) << 32; /* Intel: revision, hi dword */
}
+41
View File
@@ -0,0 +1,41 @@
/* spoof-display.c — EDID: vendor + name from pools; product code / serial /
* manufacture date GENERATED (real per-unit fields). */
#include "qemu/osdep.h"
#include "hw/misc/spoof.h"
#include "hw/misc/spoof-core.h"
static const char *const EDID_VEND[] = {
"DEL", "SAM", "AUS", "LEN", "HWP", "ACR", "BNQ", "GSM", "AOC", "MSI", "VSC", "PHL",
"GBT", "IVM", "NEC", "EIZ", "SHP", "SNY", "AUO", "CMN", "LGD", "BOE", "HSD", "VIZ",
};
static const char *const EDID_NAME[] = {
"DELL U2415", "DELL P2419H", "DELL S2721DGF", "SyncMaster", "S24F350", "Odyssey G5",
"ASUS VG248", "VG279Q", "ProArt PA248", "LG 27GL850", "27GP850", "UltraGear",
"BenQ GL2480", "HP 24mh", "HP X27q", "ThinkVision T24i", "Acer KG241", "Nitro VG240",
"AOC 24G2", "ViewSonic VX2458", "Philips 245V", "iiyama PL2480H", "EIZO EV2456", "MSI G241",
};
const char *spoof_edid_vendor(const char *def)
{
return spoof_on() ? SPOOF_PICK("edid.vend", EDID_VEND) : def;
}
const char *spoof_edid_name(const char *def)
{
return spoof_on() ? SPOOF_PICK("edid.name", EDID_NAME) : def;
}
uint16_t spoof_edid_model(uint16_t def)
{
return spoof_on() ? (uint16_t)(spoof_field("edid.model") % 0xFFFE) + 1 : def;
}
uint32_t spoof_edid_serial(uint32_t def)
{
return spoof_on() ? (uint32_t)spoof_field("edid.serial") : def;
}
int spoof_edid_year(int def)
{
return spoof_on() ? 2017 + (int)(spoof_field("edid.year") % 8) : def; /* 2017..2024 */
}
int spoof_edid_week(int def)
{
return spoof_on() ? 1 + (int)(spoof_field("edid.week") % 52) : def;
}
+32
View File
@@ -0,0 +1,32 @@
/* spoof-fwcfg.c — fw_cfg signatures + ACPI _HID (the "QEMU"/"QEMU CFG" tells). */
#include "qemu/osdep.h"
#include "hw/misc/spoof.h"
#include "hw/misc/spoof-core.h"
static const char *const FWCFG_SIG[] = {
"INTL", "ASUS", "DELL", "LENV", "HPQ ", "MSI ", "ACER", "GBT ", "ASRK", "SUPM", "BIOS", "SONY",
};
static const char *const FWCFG_DEVID[] = {
"INTC0002", "PNP0C01", "PNP0C02", "ASUS0002", "DELL0002", "LEN0078", "HPQ0002",
"MSFT0002", "ACPI0002", "INT33A0",
};
const char *spoof_fwcfg_sig(const char *def)
{
return spoof_on() ? SPOOF_PICK("fwcfg.sig", FWCFG_SIG) : def;
}
const char *spoof_fwcfg_acpi_devid(const char *def)
{
return spoof_on() ? SPOOF_PICK("fwcfg.devid", FWCFG_DEVID) : def;
}
uint64_t spoof_fwcfg_dma_sig(uint64_t def)
{
if (!spoof_on()) {
return def;
}
uint64_t v = 0;
for (int i = 0; i < 8; i++) {
v = (v << 8) | (uint8_t)SPOOF_A36[spoof_field_n("fwcfg.dma", (unsigned)i) % (sizeof(SPOOF_A36) - 1)];
}
return v;
}
+10
View File
@@ -0,0 +1,10 @@
/* spoof-machine.c — the QEMU machine `desc` string. Comes from the PLATFORM
* anchor (spoof-platform.c) so the model matches the ACPI OEM / BIOS / baseboard. */
#include "qemu/osdep.h"
#include "hw/misc/spoof.h"
#include "hw/misc/spoof-core.h"
const char *spoof_machine_desc(const char *def)
{
return spoof_on() ? spoof_plat_machine_desc() : def;
}
+38
View File
@@ -0,0 +1,38 @@
/* spoof-pci.c — PCI identity: hide the Red Hat/virtio fingerprint, anchored to
* the CPU vendor so the chipset matches the CPU (Intel CPU -> Intel chipset). */
#include "qemu/osdep.h"
#include "hw/misc/spoof.h"
#include "hw/misc/spoof-core.h"
static const uint16_t PCI_VENDOR[] = { 0x8086 /* Intel */, 0x1022 /* AMD */ };
uint16_t spoof_pci_vendor(uint16_t def)
{
return spoof_on() ? PCI_VENDOR[spoof_anchor_vendor()] : def;
}
uint16_t spoof_pci_subvendor(uint16_t def)
{
return spoof_on() ? PCI_VENDOR[spoof_anchor_vendor()] : def;
}
uint16_t spoof_pci_device(const char *role, uint16_t def)
{
if (!spoof_on()) {
return def;
}
/* TODO: curated real per-role device-id table. For now keep it stable per
* role and in a plausible range (never the bogus device==vendor case). */
char k[64];
snprintf(k, sizeof(k), "pci.dev.%s", role ? role : "");
return (uint16_t)(0x1500 + (spoof_field(k) % 0x0400));
}
/* Subsystem device id (the OEM/board sub-identity); subsystem vendor =
* spoof_pci_subvendor. Gives QEMU-origin controllers an OEM-branded SSID. */
uint16_t spoof_pci_subdevice(const char *role, uint16_t def)
{
if (!spoof_on()) {
return def;
}
char k[64];
snprintf(k, sizeof(k), "pci.ssid.%s", role ? role : "");
return (uint16_t)(0x8000 | (spoof_field(k) & 0x7FFF));
}
+95
View File
@@ -0,0 +1,95 @@
/*
* spoof-platform.c — the PLATFORM anchor: one seed draw selects a coherent
* machine identity (ACPI OEM id, ACPI table id, BIOS vendor, baseboard, the
* board model AND its socket). The board list is split by CPU vendor and indexed
* by the CPU anchor, so the board's chipset/socket always matches the CPU
* (no "Intel H510 board + AMD CPU"). The board's own socket feeds SMBIOS type4.
*
* Two classes: OEM prebuilt (ACPI OEM = vendor code) and DIY/retail (AMI Aptio
* firmware -> ACPI OEM "ALASKA"/"A M I"; the board brand is in the baseboard).
*/
#include "qemu/osdep.h"
#include "hw/misc/spoof.h"
#include "hw/misc/spoof-core.h"
typedef struct { const char *model, *socket; } Board;
/* --- OEM prebuilt: real Intel and (separate) AMD desktop SKUs --- */
static const Board DELL_I[] = { {"OptiPlex 7090","LGA1200"}, {"OptiPlex 5090","LGA1200"},
{"Precision 3650 Tower","LGA1200"}, {"OptiPlex 7000","LGA1700"} };
static const Board DELL_A[] = { {"OptiPlex 5055","AM4"}, {"OptiPlex 7010 AMD","AM4"} };
static const Board HP_I[] = { {"HP EliteDesk 800 G6","LGA1200"}, {"HP ProDesk 600 G6","LGA1200"},
{"HP Z2 Tower G5","LGA1200"}, {"HP EliteDesk 800 G9","LGA1700"} };
static const Board HP_A[] = { {"HP ProDesk 405 G6","AM4"}, {"HP ProDesk 485 G4","AM4"} };
static const Board LEN_I[] = { {"ThinkCentre M720q","LGA1151"}, {"ThinkCentre M920t","LGA1151"},
{"ThinkStation P340","LGA1200"}, {"ThinkCentre M70q Gen 3","LGA1700"} };
static const Board LEN_A[] = { {"ThinkCentre M75q Gen 2","AM4"}, {"ThinkCentre M75s Gen 2","AM4"} };
static const Board ACE_I[] = { {"Veriton M4660G","LGA1151"}, {"Aspire TC-895","LGA1200"},
{"Aspire TC-1760","LGA1700"} };
static const Board ACE_A[] = { {"Nitro N50-110","AM4"}, {"Aspire TC-380","AM4"} };
/* --- DIY/retail boards: the model unambiguously implies the socket --- */
static const Board ASUS_I[] = { {"ROG STRIX Z590-E GAMING WIFI","LGA1200"}, {"PRIME B560M-A","LGA1200"},
{"TUF GAMING Z690-PLUS WIFI","LGA1700"}, {"PRIME H610M-E D4","LGA1700"} };
static const Board ASUS_A[] = { {"TUF GAMING B550M-PLUS","AM4"}, {"ROG STRIX X570-E GAMING","AM4"},
{"PRIME B650M-A","AM5"}, {"ProArt X670E-CREATOR WIFI","AM5"} };
static const Board GBT_I[] = { {"Z390 AORUS PRO","LGA1151"}, {"B660M DS3H DDR4","LGA1700"},
{"Z690 AORUS ELITE AX","LGA1700"} };
static const Board GBT_A[] = { {"X570 AORUS ELITE","AM4"}, {"B450 AORUS M","AM4"}, {"B650 AORUS ELITE AX","AM5"} };
static const Board MSI_I[] = { {"MAG B560 TOMAHAWK","LGA1200"}, {"PRO B660M-A DDR4","LGA1700"},
{"MPG Z690 CARBON WIFI","LGA1700"} };
static const Board MSI_A[] = { {"MPG B550 GAMING PLUS","AM4"}, {"B450 TOMAHAWK MAX","AM4"},
{"MAG B650 TOMAHAWK WIFI","AM5"} };
static const Board ASR_I[] = { {"H510M-HVS","LGA1200"}, {"B660M Pro RS","LGA1700"},
{"Z690 Steel Legend","LGA1700"} };
static const Board ASR_A[] = { {"X570 Taichi","AM4"}, {"B450M Steel Legend","AM4"}, {"B650M PG Riptide","AM5"} };
typedef struct {
const char *oem, *table, *bios, *board, *oemstr;
const Board *intel; size_t ni;
const Board *amd; size_t na;
} Platform;
#define BL(a) (a), ARRAY_SIZE(a)
static const Platform PLAT[] = {
/* --- OEM prebuilt: ACPI OEM id = vendor code --- */
{ "DELL ", "QA09 ", "Dell Inc.", "Dell Inc.", "Dell System", BL(DELL_I), BL(DELL_A) },
{ "HPQOEM", "8054 ", "HP", "HP", "HP", BL(HP_I), BL(HP_A) },
{ "LENOVO", "TP-N1Q ", "LENOVO", "LENOVO", "LENOVO", BL(LEN_I), BL(LEN_A) },
{ "ACRSYS", "ACRSYS ", "Insyde Corp.", "Acer", "Acer System", BL(ACE_I), BL(ACE_A) },
/* --- DIY/retail (AMI firmware): ACPI OEM id "ALASKA"; brand in baseboard --- */
{ "ALASKA", "A M I ", "American Megatrends Inc.", "ASUSTeK COMPUTER INC.", "$ASUS$", BL(ASUS_I), BL(ASUS_A) },
{ "ALASKA", "A M I ", "American Megatrends Inc.", "Gigabyte Technology Co., Ltd.", "GIGABYTE", BL(GBT_I), BL(GBT_A) },
{ "ALASKA", "A M I ", "American Megatrends Inc.", "Micro-Star International Co., Ltd.", "MSI", BL(MSI_I), BL(MSI_A) },
{ "ALASKA", "A M I ", "American Megatrends Inc.", "ASRock", "ASRock", BL(ASR_I), BL(ASR_A) },
};
static const Platform *plat(void)
{
return &PLAT[spoof_field("anchor.platform") % ARRAY_SIZE(PLAT)];
}
/* the chosen board for THIS platform under the CPU-vendor anchor (Intel/AMD). */
static const Board *chosen_board(void)
{
const Platform *p = plat();
const Board *list = spoof_anchor_vendor() ? p->amd : p->intel;
size_t n = spoof_anchor_vendor() ? p->na : p->ni;
return &list[spoof_field("mach.board") % n];
}
const char *spoof_plat_acpi_oem(void) { return plat()->oem; }
const char *spoof_plat_acpi_table(void) { return plat()->table; }
const char *spoof_plat_bios_vendor(void) { return plat()->bios; }
const char *spoof_plat_baseboard(void) { return plat()->board; }
const char *spoof_plat_machine_desc(void) { return chosen_board()->model; }
const char *spoof_plat_socket(void) { return chosen_board()->socket; }
const char *spoof_plat_oem_string(void) { return plat()->oemstr; }
const char *spoof_bios_vendor(const char *def)
{
return spoof_on() ? spoof_plat_bios_vendor() : def;
}
const char *spoof_baseboard_manufacturer(const char *def)
{
return spoof_on() ? spoof_plat_baseboard() : def;
}
+82
View File
@@ -0,0 +1,82 @@
/* spoof-smbios.c — SMBIOS: clear the type0 "VM" bit, fill type17 (memory) and
* type4 (processor). CPU manufacturer + socket are anchored to the CPU vendor. */
#include "qemu/osdep.h"
#include "hw/misc/spoof.h"
#include "hw/misc/spoof-core.h"
static const char *const MEM_MFR[] = {
"Samsung", "SK Hynix", "Micron Technology", "Crucial", "Corsair",
"G.Skill", "Kingston", "ADATA", "Patriot", "Team Group",
};
static const char *const CPU_MFR[] = { "Intel(R) Corporation", "Advanced Micro Devices, Inc." };
static const char *mem_prefix(const char *mfr)
{
if (!strncmp(mfr, "Samsung", 7)) return "M";
if (!strncmp(mfr, "SK", 2)) return "HMA";
if (!strncmp(mfr, "Micron", 6)) return "MTA";
if (!strncmp(mfr, "Crucial", 7)) return "CT";
if (!strncmp(mfr, "Corsair", 7)) return "CM";
if (!strncmp(mfr, "G.Skill", 7)) return "F4-";
if (!strncmp(mfr, "Kingston", 8)) return "K";
return "AD";
}
static struct {
const char *mfr, *part_pre;
char part[24], serial[12];
bool done;
} m;
static void m_init(void)
{
if (m.done) {
return;
}
m.mfr = SPOOF_PICK("mem.mfr", MEM_MFR);
char tail[16];
spoof_gen("mem.part", SPOOF_A36, 12, tail);
snprintf(m.part, sizeof(m.part), "%s%s", mem_prefix(m.mfr), tail);
spoof_gen("mem.serial", SPOOF_A36, 8, m.serial);
m.done = true;
}
bool spoof_smbios_hide_vm(void) { return spoof_on(); }
const char *spoof_smbios_mem_manufacturer(const char *def)
{
if (!spoof_on()) return def;
m_init(); return m.mfr;
}
const char *spoof_smbios_mem_part(const char *def)
{
if (!spoof_on()) return def;
m_init(); return m.part;
}
const char *spoof_smbios_mem_serial(const char *def)
{
if (!spoof_on()) return def;
m_init(); return m.serial;
}
const char *spoof_smbios_cpu_manufacturer(const char *def)
{
return spoof_on() ? CPU_MFR[spoof_anchor_vendor()] : def;
}
const char *spoof_smbios_cpu_socket(const char *def)
{
/* the socket of the platform's chosen board -> matches the model + CPU vendor. */
return spoof_on() ? spoof_plat_socket() : def;
}
const char *spoof_smbios_oem_string(const char *def) /* type11 */
{
return spoof_on() ? spoof_plat_oem_string() : def;
}
int spoof_smbios_chassis_type(int def) /* type3: 3=Desktop, 10=Notebook */
{
if (!spoof_on()) return def;
const char *m = spoof_plat_machine_desc();
if (strstr(m, "ThinkPad") || strstr(m, "Latitude") || strstr(m, "EliteBook")) {
return 10;
}
return 3; /* all current models are desktops */
}
+141
View File
@@ -0,0 +1,141 @@
/* spoof-storage.c — disks/NVMe/CD-ROM. Model = pool; per-unit serial / WWN /
* firmware / EUI64 / NGUID = GENERATED to brand-correct format. */
#include "qemu/osdep.h"
#include "qemu/cutils.h" /* pstrcpy */
#include "hw/misc/spoof.h"
#include "hw/misc/spoof-core.h"
/* rpm: 1 = SSD (non-rotational), else the spinning rate — stored, not guessed. */
typedef struct { const char *model; int rpm; } DiskModel;
static const DiskModel DISK_MODEL[] = {
{ "Samsung SSD 870 EVO 500GB", 1 }, { "Samsung SSD 860 EVO 1TB", 1 }, { "Samsung SSD 870 QVO 2TB", 1 },
{ "WDC WD10EZEX-08WN4A0", 7200 }, { "WDC WD20EZBX-00AYRA0", 5400 }, { "WDC WDS500G2B0A-00SM50", 1 },
{ "ST1000DM010-2EP102", 7200 }, { "ST2000DM008-2FR102", 7200 }, { "ST500DM002-1BD142", 7200 },
{ "CT500MX500SSD1", 1 }, { "CT1000BX500SSD1", 1 }, { "KINGSTON SA400S37480G", 1 }, { "KINGSTON SKC600512G", 1 },
{ "Crucial_CT525MX300SSD1", 1 }, { "TOSHIBA DT01ACA100", 7200 }, { "SanDisk SDSSDA240G", 1 },
{ "INTEL SSDSC2KW256G8", 1 }, { "HGST HUS726T4TALA6L4", 7200 }, { "ADATA SU800", 1 }, { "PNY CS900 240GB", 1 },
};
static const char *const NVME_MODEL[] = {
"Samsung SSD 980 1TB", "Samsung SSD 970 EVO Plus 1TB", "Samsung SSD 990 PRO 2TB",
"WD_BLACK SN770 1TB", "WD Blue SN570 1TB", "KXG60ZNV512G TOSHIBA",
"CT1000P3SSD8", "CT500P5SSD8", "Sabrent Rocket 4.0", "KINGSTON SNV2S1000G",
"ADATA SX8200PNP", "Seagate FireCuda 520", "Corsair MP600", "PNY CS3030",
"INTEL SSDPEKNW010T8", "SKHynix_HFS512GD9TNG", "Micron 2210 MTFDHBA512QFD", "GIGABYTE GP-GSM2NE3",
};
static const char *const CDROM_MODEL[] = {
"HL-DT-ST DVD-RAM GH24NSD1", "ASUS DRW-24D5MT", "TSSTcorp CDDVDW SH-224",
"PLDS DVD+-RW DH-16AES", "HL-DT-ST DVDRWBD CH12NS30",
};
/* Disk serial in the format the model's brand actually uses. */
static void gen_disk_serial(const char *model, char *out, size_t n)
{
char b[24];
if (!strncmp(model, "Samsung", 7)) { /* S<3L>N<d>0<6 alnum> */
b[0] = 'S';
for (int i = 0; i < 3; i++) b[1 + i] = SPOOF_LET[spoof_field_n("dsk.sa.l", i) % 26];
b[4] = 'N'; b[5] = SPOOF_DIG[spoof_field_n("dsk.sa.d", 0) % 10]; b[6] = '0';
for (int i = 0; i < 6; i++) b[7 + i] = SPOOF_A36[spoof_field_n("dsk.sa.t", i) % 36];
b[13] = '\0';
} else if (!strncmp(model, "WDC", 3) || !strncmp(model, "WD", 2)) { /* WD-WCC<7> */
memcpy(b, "WD-WCC", 6);
for (int i = 0; i < 7; i++) b[6 + i] = SPOOF_A36[spoof_field_n("dsk.wd", i) % 36];
b[13] = '\0';
} else if (!strncmp(model, "ST", 2)) { /* Seagate: Z<7 base36> */
b[0] = 'Z';
for (int i = 0; i < 7; i++) b[1 + i] = SPOOF_A36[spoof_field_n("dsk.st", i) % 36];
b[8] = '\0';
} else if (!strncmp(model, "CT", 2) || !strncmp(model, "Crucial", 7)) { /* 4d+12 */
for (int i = 0; i < 4; i++) b[i] = SPOOF_DIG[spoof_field_n("dsk.ct.a", i) % 10];
for (int i = 0; i < 12; i++) b[4 + i] = SPOOF_A36[spoof_field_n("dsk.ct.b", i) % 36];
b[16] = '\0';
} else { /* generic 16 alnum */
for (int i = 0; i < 16; i++) b[i] = SPOOF_A36[spoof_field_n("dsk.g", i) % 36];
b[16] = '\0';
}
pstrcpy(out, n, b);
}
static void gen_nvme_serial(const char *model, char *out, size_t n)
{
char b[24];
if (!strncmp(model, "Samsung", 7)) {
b[0] = 'S';
for (int i = 0; i < 3; i++) b[1 + i] = SPOOF_LET[spoof_field_n("nvm.sa.l", i) % 26];
memcpy(b + 4, "NX0", 3);
for (int i = 0; i < 7; i++) b[7 + i] = SPOOF_A36[spoof_field_n("nvm.sa.t", i) % 36];
b[14] = '\0';
} else {
for (int i = 0; i < 20; i++) b[i] = SPOOF_A36[spoof_field_n("nvm.g", i) % 36];
b[20] = '\0';
}
pstrcpy(out, n, b);
}
/* WWN: NAA-5 + a brand-matched 24-bit OUI + 36-bit vendor-specific. */
static uint64_t brand_oui(const char *m)
{
if (!strncmp(m, "Samsung", 7)) return 0x002538;
if (!strncmp(m, "WD", 2)) return 0x0014EE;
if (!strncmp(m, "ST", 2)) return 0x000C50;
if (!strncmp(m, "CT", 2) || !strncmp(m, "Crucial", 7)) return 0x00A075; /* Micron */
return 0x0024E9;
}
static struct {
const char *model; char serial[24], fw[12]; uint64_t wwn; int rpm;
bool done;
} d;
static struct {
const char *model; char serial[24], fw[12]; uint64_t eui64;
bool done;
} v;
static void d_init(void)
{
if (d.done) return;
const DiskModel *dm = &DISK_MODEL[spoof_field("disk.model") % ARRAY_SIZE(DISK_MODEL)];
d.model = dm->model;
d.rpm = dm->rpm;
gen_disk_serial(d.model, d.serial, sizeof(d.serial));
spoof_gen("disk.fw", SPOOF_A36, 8, d.fw);
d.wwn = (0x5ULL << 60) | (brand_oui(d.model) << 36) | (spoof_field("disk.wwn") & 0xFFFFFFFFFULL);
d.done = true;
}
static void v_init(void)
{
if (v.done) return;
v.model = SPOOF_PICK("nvme.model", NVME_MODEL);
gen_nvme_serial(v.model, v.serial, sizeof(v.serial));
spoof_gen("nvme.fw", SPOOF_A36, 8, v.fw);
v.eui64 = (brand_oui(v.model) << 40) | (spoof_field("nvme.eui") & 0xFFFFFFFFFFULL);
v.done = true;
}
const char *spoof_disk_model(const char *def) { if (!spoof_on()) return def; d_init(); return d.model; }
const char *spoof_disk_serial(const char *def) { if (!spoof_on()) return def; d_init(); return d.serial; }
const char *spoof_disk_fw(const char *def) { if (!spoof_on()) return def; d_init(); return d.fw; }
uint64_t spoof_disk_wwn(uint64_t def) { if (!spoof_on()) return def; d_init(); return d.wwn; }
const char *spoof_nvme_model(const char *def) { if (!spoof_on()) return def; v_init(); return v.model; }
const char *spoof_nvme_serial(const char *def) { if (!spoof_on()) return def; v_init(); return v.serial; }
const char *spoof_nvme_fw(const char *def) { if (!spoof_on()) return def; v_init(); return v.fw; }
uint64_t spoof_nvme_eui64(uint64_t def) { if (!spoof_on()) return def; v_init(); return v.eui64; }
void spoof_nvme_nguid(uint8_t out[16])
{
if (!spoof_on()) return;
for (int i = 0; i < 16; i++) {
out[i] = (uint8_t)spoof_field_n("nvme.nguid", (unsigned)i);
}
}
const char *spoof_cdrom_model(const char *def)
{
return spoof_on() ? SPOOF_PICK("cdrom.model", CDROM_MODEL) : def;
}
/* ATA rotation rate: 1 = non-rotational (SSD), else RPM — matched to the model
* so an "SSD 870 EVO" reports SSD and an "ST1000DM" reports a spinning rate. */
int spoof_disk_rotation(int def)
{
if (!spoof_on()) return def;
d_init();
return d.rpm; /* 1 = SSD, else RPM (from the pool) */
}
+103
View File
@@ -0,0 +1,103 @@
/*
* qemu-spoof — seed-driven, per-VM hardware identity for anti-detection.
*
* PUBLIC header: this is what QEMU call sites include. Every getter takes the
* STOCK default and returns it verbatim when spoofing is disabled (no seed) — so
* an un-seeded VM behaves exactly like stock QEMU (safe fallback). With a seed,
* each value is derived deterministically: same seed -> same coherent persona,
* different seed -> different one (kills fleet correlation).
*
* Implementation is decomposed by aspect (spoof-core.c + spoof-<aspect>.c).
*
* Inputs (env var = test fallback for the machine property):
* spoof-seed / QEMU_SPOOF_SEED persona seed (empty => everything stock)
* spoof-hv / QEMU_SPOOF_HV hv mode: off|hyperv|hidden
* spoof-waet / QEMU_SPOOF_WAET drop the WAET ACPI table: on|off
* spoof-vmgenid/ QEMU_SPOOF_VMGENID vmgenid: keep|mask|hide
* Config (the machine property) ALWAYS overrides the seed-implied default.
*/
#ifndef QEMU_SPOOF_H
#define QEMU_SPOOF_H
#include <stdint.h>
#include <stdbool.h>
bool spoof_enabled(void); /* a seed was supplied */
/* ---- policy knobs (config-overridable; not part of the random persona) ----- */
typedef enum { SPOOF_HV_OFF = 0, SPOOF_HV_HYPERV, SPOOF_HV_HIDDEN } SpoofHvMode;
SpoofHvMode spoof_hv_mode(void); /* seeded default = HYPERV, else OFF */
typedef enum { SPOOF_VGID_KEEP = 0, SPOOF_VGID_MASK, SPOOF_VGID_HIDE } SpoofVgidPolicy;
SpoofVgidPolicy spoof_vmgenid_policy(void); /* HIDDEN->HIDE, HYPERV->MASK, OFF->KEEP */
bool spoof_waet_drop(void); /* drop the WAET (emulated-devices) table */
bool spoof_pvpanic_hide(void); /* drop the pvpanic device (_HID QEMU0001) */
/* ---- platform (spoof-platform.c) — one coherent firmware/board identity ----
* The ACPI OEM id, machine desc, BIOS vendor and baseboard all come from a single
* per-seed platform pick (OEM-prebuilt vendor code, or "ALASKA"/AMI for DIY). */
const char *spoof_bios_vendor(const char *def); /* SMBIOS type0 */
const char *spoof_baseboard_manufacturer(const char *def); /* SMBIOS type2 */
/* ---- ACPI (spoof-acpi.c; hw/acpi, hw/i386/acpi-build) --------------------- */
const char *spoof_acpi_oem_id(const char *def); /* 6 bytes (platform-anchored) */
const char *spoof_acpi_oem_table_id(const char *def); /* 8 bytes (platform-anchored) */
const char *spoof_acpi_creator_id(const char *def); /* 4 bytes (ASL compiler) */
/* ---- CPU (spoof-cpu.c; target/i386) --------------------------------------- */
const char *spoof_kvm_signature(const char *def); /* 12 bytes; vendor-anchored */
int spoof_cpu_base_mhz(int def); /* CPUID leaf 0x16 base freq */
int spoof_cpu_max_mhz(int def); /* CPUID leaf 0x16 max freq */
int spoof_cpu_bus_mhz(int def); /* CPUID leaf 0x16 bus freq */
uint64_t spoof_cpu_microcode(uint64_t def); /* IA32_UCODE_REV, vendor-positioned */
/* ---- PCI (spoof-pci.c) ---------------------------------------------------- */
uint16_t spoof_pci_vendor(uint16_t def); /* anchored to the CPU vendor */
uint16_t spoof_pci_subvendor(uint16_t def);
uint16_t spoof_pci_device(const char *role, uint16_t def);
uint16_t spoof_pci_subdevice(const char *role, uint16_t def); /* OEM-branded SSID */
/* ---- fw_cfg (spoof-fwcfg.c; hw/nvram/fw_cfg, standard-headers) ------------- */
const char *spoof_fwcfg_sig(const char *def); /* 4-byte selector signature */
uint64_t spoof_fwcfg_dma_sig(uint64_t def); /* 8-byte DMA signature */
const char *spoof_fwcfg_acpi_devid(const char *def); /* fw_cfg device _HID */
/* ---- SMBIOS (spoof-smbios.c; hw/smbios) ----------------------------------- */
bool spoof_smbios_hide_vm(void); /* clear type0 "VM" char. bit */
const char *spoof_smbios_mem_manufacturer(const char *def); /* type17 */
const char *spoof_smbios_mem_part(const char *def);
const char *spoof_smbios_mem_serial(const char *def);
const char *spoof_smbios_cpu_manufacturer(const char *def); /* type4 */
const char *spoof_smbios_cpu_socket(const char *def);
const char *spoof_smbios_oem_string(const char *def); /* type11 */
int spoof_smbios_chassis_type(int def); /* type3: 3=Desktop, 10=Notebook */
/* ---- display / EDID (spoof-display.c; hw/display/edid-generate) ------------ */
const char *spoof_edid_vendor(const char *def); /* 3-char PNP (pool) */
const char *spoof_edid_name(const char *def); /* model name (pool) */
uint16_t spoof_edid_model(uint16_t def); /* product code (generated) */
uint32_t spoof_edid_serial(uint32_t def); /* serial (generated) */
int spoof_edid_year(int def); /* year of manufacture */
int spoof_edid_week(int def); /* week of manufacture */
/* ---- storage (spoof-storage.c; hw/ide, hw/scsi, hw/nvme) ------------------ */
/* model = pool (finite real set); per-unit serial/WWN/EUI64/FW = GENERATED. */
const char *spoof_disk_model(const char *def);
const char *spoof_disk_serial(const char *def); /* brand-matched format */
uint64_t spoof_disk_wwn(uint64_t def); /* NAA-5 + brand OUI */
const char *spoof_disk_fw(const char *def); /* ATA firmware revision */
const char *spoof_nvme_model(const char *def);
const char *spoof_nvme_serial(const char *def);
const char *spoof_nvme_fw(const char *def); /* 8-char rev (fixes FR leak) */
uint64_t spoof_nvme_eui64(uint64_t def); /* OUI + vendor */
void spoof_nvme_nguid(uint8_t out[16]); /* filled iff enabled */
const char *spoof_cdrom_model(const char *def);
int spoof_disk_rotation(int def); /* 1=SSD, else RPM (matches model) */
/* ---- audio (spoof-audio.c; hw/audio/hda-codec) ---------------------------- */
uint32_t spoof_hda_vendor_id(uint32_t def); /* HDA codec vendor/device id */
const char *spoof_hda_name(const char *def);
/* ---- machine (spoof-machine.c; hw/i386/pc_*) ------------------------------ */
const char *spoof_machine_desc(const char *def);
#endif /* QEMU_SPOOF_H */