qemu-spoof: seed-driven per-VM hardware-identity anti-detection for pve-qemu
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
* text=auto eol=lf
|
||||
*.patch text eol=lf
|
||||
*.deb binary
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "pve-qemu"]
|
||||
path = pve-qemu
|
||||
url = https://git.proxmox.com/git/pve-qemu.git
|
||||
@@ -0,0 +1,77 @@
|
||||
# qemu-spoof — inject the seed-driven spoof module + anti-detect patches into
|
||||
# pve-qemu and build the pve-qemu-kvm .deb.
|
||||
#
|
||||
# make prepare # init submodules, drop in the module, queue patches, bump changelog
|
||||
# make deb # build the .deb (runs pve-qemu's own dpkg-buildpackage)
|
||||
# make clean
|
||||
#
|
||||
# Build on Debian trixie (matches pve-qemu 11.0). Never on a production node.
|
||||
|
||||
# Override PVE to build against a pve-qemu checkout kept outside this tree:
|
||||
# make PVE=/path/to/pve-qemu deb
|
||||
PVE ?= pve-qemu
|
||||
QSRC := $(PVE)/qemu
|
||||
PATCHDIR := $(PVE)/debian/patches
|
||||
SERIES := $(PATCHDIR)/series
|
||||
# package revision = qemu-spoof commit count: unique + monotonic + orderable per
|
||||
# content change, so a rebuilt/improved package gets a new version (no 409 on the
|
||||
# immutable registry) and apt always picks the latest.
|
||||
SPOOF_REV ?= $(shell git rev-list --count HEAD 2>/dev/null || echo 1)
|
||||
TAG := qemu-spoof
|
||||
# Epoch: makes our package version permanently OUTRANK any stock pve-qemu-kvm
|
||||
# (which has no epoch), so a Proxmox repo update never reverts the spoof. We pull
|
||||
# upstream fixes deliberately by rebuilding on a newer pve-qemu and re-publishing
|
||||
# (the epoch carries forward). Set EPOCH= to disable.
|
||||
EPOCH ?= 1
|
||||
|
||||
.PHONY: all prepare deb clean distclean submodule
|
||||
|
||||
all: deb
|
||||
|
||||
submodule:
|
||||
@if [ ! -f "$(QSRC)/configure" ]; then \
|
||||
git submodule update --init --recursive; \
|
||||
fi
|
||||
cd $(QSRC) && meson subprojects download
|
||||
|
||||
# Idempotent: safe to re-run. pve-qemu copies the qemu/ working tree (cp -a) into
|
||||
# its build dir, so the module files we drop in here are compiled; the call-site
|
||||
# wiring is applied on top via debian/patches/series (quilt).
|
||||
prepare: submodule
|
||||
@echo ">> install spoof module headers + sources into the qemu tree"
|
||||
@for h in src/spoof*.h; do install -D -m644 "$$h" "$(QSRC)/include/hw/misc/$$(basename $$h)"; done
|
||||
@for c in src/spoof*.c; do install -D -m644 "$$c" "$(QSRC)/hw/misc/$$(basename $$c)"; done
|
||||
@echo ">> register sources in hw/misc/meson.build"
|
||||
@for c in src/spoof*.c; do b=$$(basename "$$c"); \
|
||||
grep -q "$$b" $(QSRC)/hw/misc/meson.build || \
|
||||
echo "system_ss.add(files('$$b'))" >> $(QSRC)/hw/misc/meson.build; \
|
||||
echo " + $$b"; done
|
||||
@echo ">> queue anti-detect patches into the series"
|
||||
@for p in patches/0*.patch; do \
|
||||
[ -e "$$p" ] || continue; \
|
||||
b=$$(basename $$p); \
|
||||
cp -f $$p $(PATCHDIR)/$$b; \
|
||||
grep -qxF "$$b" $(SERIES) || echo "$$b" >> $(SERIES); \
|
||||
echo " + $$b"; \
|
||||
done
|
||||
@echo ">> bump changelog with epoch $(EPOCH) so it permanently outranks stock pve-qemu-kvm"
|
||||
@cd $(PVE) && { head -1 debian/changelog | grep -q "$(TAG)" || { \
|
||||
cur=$$(dpkg-parsechangelog -S Version); \
|
||||
dch -v "$(EPOCH):$${cur}+$(TAG)$(SPOOF_REV)" "qemu-spoof: seed-driven per-VM hardware identity"; }; }
|
||||
@cd $(PVE) && head -1 debian/changelog
|
||||
@echo ">> prepared. run 'make deb'."
|
||||
|
||||
deb: prepare
|
||||
$(MAKE) -C $(PVE) deb
|
||||
@echo ">> built:"; ls -1 $(PVE)/*.deb 2>/dev/null || ls -1 *.deb 2>/dev/null || true
|
||||
|
||||
clean:
|
||||
-$(MAKE) -C $(PVE) clean 2>/dev/null || true
|
||||
rm -f *.deb *.buildinfo *.changes
|
||||
|
||||
# Drop the injected files + series entries so the submodule is pristine again.
|
||||
distclean: clean
|
||||
-cd $(QSRC) && git checkout -- hw/misc/meson.build 2>/dev/null || true
|
||||
-rm -f $(QSRC)/hw/misc/spoof*.c $(QSRC)/include/hw/misc/spoof*.h
|
||||
-cd $(PVE) && git checkout -- debian/patches/series debian/changelog 2>/dev/null || true
|
||||
-rm -f $(PATCHDIR)/0*qemu-spoof* $(PATCHDIR)/9*antidetect*
|
||||
@@ -0,0 +1,98 @@
|
||||
# qemu-spoof
|
||||
|
||||
Seed-driven, **per-VM** hardware-identity anti-detection for QEMU / Proxmox VE —
|
||||
a drop-in `pve-qemu-kvm` build. Each VM derives a *coherent, unique* hardware
|
||||
persona from a per-VM seed, so a fleet of VMs does not share one fingerprint, and
|
||||
with **no seed it falls back to stock QEMU** (zero behaviour change).
|
||||
|
||||
## Why seed-driven (not a static fork)
|
||||
|
||||
A hard-coded anti-detect fork gives every VM the *same* firmware/bus identity
|
||||
(same ACPI OEM, same fw_cfg signature, same PCI IDs, same monitor) — which is
|
||||
itself a **fleet fingerprint**: many VMs with identical "hardware" correlate
|
||||
trivially. qemu-spoof instead derives a **coherent persona from a per-VM seed**,
|
||||
so each VM looks like a different real machine while staying internally
|
||||
consistent (CPU vendor ↔ chipset ↔ ACPI OEM ↔ board ↔ socket all cohere).
|
||||
|
||||
## How it works
|
||||
|
||||
A small in-tree module (`hw/misc/spoof.c`, source kept in `src/`) holds pools of
|
||||
real identities and a pure derivation `seed -> splitmix64 -> pool pick`. Every
|
||||
anti-detect call site is patched from a hard-coded literal to a getter:
|
||||
|
||||
```c
|
||||
- memcpy(signature, "KVMKVMKVM\0\0\0", 12);
|
||||
+ memcpy(signature, spoof_kvm_signature("KVMKVMKVM\0\0\0"), 12);
|
||||
```
|
||||
|
||||
The getter returns the seed-derived value, or the **stock default** when no seed
|
||||
is set — so the patch is inert until a VM opts in.
|
||||
|
||||
### Seed input
|
||||
|
||||
```
|
||||
-machine <type>,...,spoof-seed=<string> # preferred (Proxmox: via args)
|
||||
QEMU_SPOOF_SEED=<string> # env fallback (testing)
|
||||
```
|
||||
|
||||
Same seed → same persona. Mix in a host secret so personas are not guessable from
|
||||
the vmid. Proxmox: add `spoof-seed=` through the VM `args:` line.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
pve-qemu/ git submodule -> https://git.proxmox.com/git/pve-qemu.git (pinned)
|
||||
src/spoof*.{c,h} the seed-driven identity module (decomposed by aspect)
|
||||
patches/ quilt patches injected into pve-qemu/debian/patches/series:
|
||||
0002 register the spoof-seed machine property
|
||||
0010+ wire each anti-detect call site to a getter
|
||||
Makefile prepare -> build the .deb
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
git submodule update --init --recursive # pve-qemu + its qemu submodule
|
||||
make prepare # inject spoof module + patches + changelog
|
||||
make deb # dpkg-buildpackage -> pve-qemu/*.deb
|
||||
```
|
||||
|
||||
Builds on Debian trixie (matches pve-qemu 11.0). Use a clean builder / WSL, never
|
||||
a production node. `make deb` produces `pve-qemu-kvm_*_amd64.deb` (+ `-dbgsym`).
|
||||
|
||||
## Packaging & delivery
|
||||
|
||||
Install the built `.deb` on a Proxmox VE node in place of the stock package, or
|
||||
serve it from any apt repository (e.g. a Debian package registry).
|
||||
|
||||
### Surviving Proxmox updates (epoch + per-commit revision)
|
||||
|
||||
The Makefile stamps an **epoch** — `1:<upstream>+qemu-spoofN`, where `N` is the
|
||||
commit count — so the package permanently outranks stock `pve-qemu-kvm` (no
|
||||
epoch). An `apt upgrade` from the Proxmox repo therefore never reverts the spoof;
|
||||
you are the source of truth for this package on your nodes. Pull upstream QEMU
|
||||
fixes deliberately by rebuilding on a newer `pve-qemu` (the epoch carries
|
||||
forward). Optional belt-and-suspenders apt pin on each node:
|
||||
|
||||
```
|
||||
Package: pve-qemu-kvm
|
||||
Pin: origin <your-apt-registry-host>
|
||||
Pin-Priority: 1001
|
||||
```
|
||||
|
||||
## Companion config (machine options, not code)
|
||||
|
||||
A few tells are configuration, not code — set them when provisioning a spoofed VM
|
||||
(alongside the `spoof-seed`):
|
||||
|
||||
- `-machine ...,vmport=off` — disable the VMware backdoor port (0x5658)
|
||||
- prefer real-id emulated devices over virtio where stealth matters: `e1000e` NIC,
|
||||
SATA/AHCI disk (their PCI vendor:device are real Intel; virtio's `1af4` is a tell
|
||||
and its id cannot be spoofed without breaking the guest driver)
|
||||
- GPU: vfio passthrough with `x-pci-vendor-id/...` overrides
|
||||
- CPU: `host,hidden=1` (+ hv enlightenments for Windows) to back the `spoof-hv` policy
|
||||
|
||||
## License
|
||||
|
||||
Patches and module are derivative of QEMU and licensed under **GPL v2** (the QEMU
|
||||
license). See the COPYING file in the QEMU tree.
|
||||
@@ -0,0 +1,79 @@
|
||||
qemu-spoof: register per-VM anti-detect machine properties
|
||||
|
||||
Adds string machine properties spoof-seed / spoof-hv / spoof-waet /
|
||||
spoof-vmgenid / spoof-pvpanic on the x86 machine, backed by X86MachineState
|
||||
fields. The qemu-spoof module (hw/misc/spoof*) reads them via
|
||||
object_property_get_str(current_machine, "spoof-*"). Inert unless set.
|
||||
diff --git a/hw/i386/x86.c b/hw/i386/x86.c
|
||||
index 01872cb..66400fe 100644
|
||||
--- a/hw/i386/x86.c
|
||||
+++ b/hw/i386/x86.c
|
||||
@@ -372,6 +372,26 @@ static void x86_machine_initfn(Object *obj)
|
||||
x86ms->above_4g_mem_start = 4 * GiB;
|
||||
}
|
||||
|
||||
+/* qemu-spoof: plain string machine properties, read back by the spoof module via
|
||||
+ * object_property_get_str(current_machine, "spoof-*"). */
|
||||
+#define X86_SPOOF_PROP(field) \
|
||||
+ static char *x86_machine_get_##field(Object *obj, Error **errp) \
|
||||
+ { \
|
||||
+ return g_strdup(X86_MACHINE(obj)->field); \
|
||||
+ } \
|
||||
+ static void x86_machine_set_##field(Object *obj, const char *value, \
|
||||
+ Error **errp) \
|
||||
+ { \
|
||||
+ X86MachineState *x86ms = X86_MACHINE(obj); \
|
||||
+ g_free(x86ms->field); \
|
||||
+ x86ms->field = g_strdup(value); \
|
||||
+ }
|
||||
+X86_SPOOF_PROP(spoof_seed)
|
||||
+X86_SPOOF_PROP(spoof_hv)
|
||||
+X86_SPOOF_PROP(spoof_waet)
|
||||
+X86_SPOOF_PROP(spoof_vmgenid)
|
||||
+X86_SPOOF_PROP(spoof_pvpanic)
|
||||
+
|
||||
static void x86_machine_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
@@ -426,6 +446,27 @@ static void x86_machine_class_init(ObjectClass *oc, const void *data)
|
||||
"in ACPI table header."
|
||||
"The string may be up to 8 bytes in size");
|
||||
|
||||
+ object_class_property_add_str(oc, "spoof-seed",
|
||||
+ x86_machine_get_spoof_seed, x86_machine_set_spoof_seed);
|
||||
+ object_class_property_set_description(oc, "spoof-seed",
|
||||
+ "qemu-spoof: per-VM persona seed (empty = stock QEMU)");
|
||||
+ object_class_property_add_str(oc, "spoof-hv",
|
||||
+ x86_machine_get_spoof_hv, x86_machine_set_spoof_hv);
|
||||
+ object_class_property_set_description(oc, "spoof-hv",
|
||||
+ "qemu-spoof: hypervisor mode (off|hyperv|hidden)");
|
||||
+ object_class_property_add_str(oc, "spoof-waet",
|
||||
+ x86_machine_get_spoof_waet, x86_machine_set_spoof_waet);
|
||||
+ object_class_property_set_description(oc, "spoof-waet",
|
||||
+ "qemu-spoof: drop the WAET ACPI table (on|off)");
|
||||
+ object_class_property_add_str(oc, "spoof-vmgenid",
|
||||
+ x86_machine_get_spoof_vmgenid, x86_machine_set_spoof_vmgenid);
|
||||
+ object_class_property_set_description(oc, "spoof-vmgenid",
|
||||
+ "qemu-spoof: vmgenid policy (keep|mask|hide)");
|
||||
+ object_class_property_add_str(oc, "spoof-pvpanic",
|
||||
+ x86_machine_get_spoof_pvpanic, x86_machine_set_spoof_pvpanic);
|
||||
+ object_class_property_set_description(oc, "spoof-pvpanic",
|
||||
+ "qemu-spoof: drop the pvpanic device (on|off)");
|
||||
+
|
||||
object_class_property_add(oc, X86_MACHINE_BUS_LOCK_RATELIMIT, "uint64_t",
|
||||
x86_machine_get_bus_lock_ratelimit,
|
||||
x86_machine_set_bus_lock_ratelimit, NULL, NULL);
|
||||
diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h
|
||||
index 71fe6b5..a80700f 100644
|
||||
--- a/include/hw/i386/x86.h
|
||||
+++ b/include/hw/i386/x86.h
|
||||
@@ -79,6 +79,9 @@ struct X86MachineState {
|
||||
|
||||
char *oem_id;
|
||||
char *oem_table_id;
|
||||
+
|
||||
+ /* qemu-spoof: per-VM anti-detect config, read by hw/misc/spoof*.c */
|
||||
+ char *spoof_seed, *spoof_hv, *spoof_waet, *spoof_vmgenid, *spoof_pvpanic;
|
||||
/*
|
||||
* Address space used by IOAPIC device. All IOAPIC interrupts
|
||||
* will be translated to MSI messages in the address space.
|
||||
@@ -0,0 +1,41 @@
|
||||
qemu-spoof: route ACPI table header identity through the spoof module
|
||||
|
||||
acpi_table_begin(): OEM id / OEM table id / creator id now come from
|
||||
spoof_acpi_*(); build_fadt(): blank the "QEMU" hypervisor-vendor when spoofing.
|
||||
All inert (return the stock default) unless a spoof-seed is set.
|
||||
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
|
||||
index 4b37405..8bc3f4d 100644
|
||||
--- a/hw/acpi/aml-build.c
|
||||
+++ b/hw/acpi/aml-build.c
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include <glib/gprintf.h>
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
@@ -1725,11 +1726,11 @@ void acpi_table_begin(AcpiTable *desc, GArray *array)
|
||||
build_append_int_noprefix(array, 0, 4); /* Length */
|
||||
build_append_int_noprefix(array, desc->rev, 1); /* Revision */
|
||||
build_append_int_noprefix(array, 0, 1); /* Checksum */
|
||||
- build_append_padded_str(array, desc->oem_id, 6, '\0'); /* OEMID */
|
||||
+ build_append_padded_str(array, spoof_acpi_oem_id(desc->oem_id), 6, '\0'); /* OEMID */
|
||||
/* OEM Table ID */
|
||||
- build_append_padded_str(array, desc->oem_table_id, 8, '\0');
|
||||
+ build_append_padded_str(array, spoof_acpi_oem_table_id(desc->oem_table_id), 8, '\0');
|
||||
build_append_int_noprefix(array, 1, 4); /* OEM Revision */
|
||||
- g_array_append_vals(array, ACPI_BUILD_APPNAME8, 4); /* Creator ID */
|
||||
+ g_array_append_vals(array, spoof_acpi_creator_id(ACPI_BUILD_APPNAME8), 4); /* Creator ID */
|
||||
build_append_int_noprefix(array, 1, 4); /* Creator Revision */
|
||||
}
|
||||
|
||||
@@ -2370,7 +2371,7 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
|
||||
}
|
||||
|
||||
/* Hypervisor Vendor Identity */
|
||||
- build_append_padded_str(tbl, "QEMU", 8, '\0');
|
||||
+ build_append_padded_str(tbl, spoof_enabled() ? "" : "QEMU", 8, '\0');
|
||||
|
||||
/* TODO: extra fields need to be added to support revisions above rev6 */
|
||||
assert(f->rev == 6);
|
||||
@@ -0,0 +1,39 @@
|
||||
qemu-spoof: ACPI policy gates (WAET drop, vmgenid hide)
|
||||
|
||||
x86_build_acpi_tables(): skip the WAET (Windows ACPI Emulated Devices) table when
|
||||
spoof_waet_drop(); skip the vmgenid SSDT when spoof_vmgenid_policy()==HIDE. Both
|
||||
default to off unless a spoof-seed is set.
|
||||
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
|
||||
index f622b91..fece5ce 100644
|
||||
--- a/hw/i386/acpi-build.c
|
||||
+++ b/hw/i386/acpi-build.c
|
||||
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qobject/qnum.h"
|
||||
#include "acpi-build.h"
|
||||
@@ -2000,7 +2001,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
||||
#endif
|
||||
|
||||
vmgenid_dev = find_vmgenid_dev();
|
||||
- if (vmgenid_dev) {
|
||||
+ if (vmgenid_dev && spoof_vmgenid_policy() != SPOOF_VGID_HIDE) {
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
vmgenid_build_acpi(VMGENID(vmgenid_dev), tables_blob,
|
||||
tables->vmgenid, tables->linker, x86ms->oem_id);
|
||||
@@ -2075,8 +2076,10 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
||||
x86ms->oem_id, x86ms->oem_table_id, &pcms->cxl_devices_state);
|
||||
}
|
||||
|
||||
- acpi_add_table(table_offsets, tables_blob);
|
||||
- build_waet(tables_blob, tables->linker, x86ms->oem_id, x86ms->oem_table_id);
|
||||
+ if (!spoof_waet_drop()) { /* qemu-spoof: WAET = emulated-devices tell */
|
||||
+ acpi_add_table(table_offsets, tables_blob);
|
||||
+ build_waet(tables_blob, tables->linker, x86ms->oem_id, x86ms->oem_table_id);
|
||||
+ }
|
||||
|
||||
/* Add tables supplied by user (if any) */
|
||||
for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
|
||||
@@ -0,0 +1,70 @@
|
||||
qemu-spoof: scrub device ACPI _HIDs (vmgenid / pvpanic / fw_cfg)
|
||||
|
||||
vmgenid: _HID QEMUVGID -> MSFT0002 when vmgenid policy = mask. pvpanic: omit the
|
||||
QEMU0001 _HID node when spoof_pvpanic_hide(). fw_cfg: _HID QEMU0002 ->
|
||||
spoof_fwcfg_acpi_devid(). Inert unless a spoof-seed is set.
|
||||
diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
|
||||
index 70ad029..7f95d79 100644
|
||||
--- a/hw/acpi/vmgenid.c
|
||||
+++ b/hw/acpi/vmgenid.c
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
@@ -53,7 +54,8 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid,
|
||||
build_append_named_dword(ssdt->buf, "VGIA");
|
||||
scope = aml_scope("\\_SB");
|
||||
dev = aml_device("VGEN");
|
||||
- aml_append(dev, aml_name_decl("_HID", aml_string("QEMUVGID")));
|
||||
+ aml_append(dev, aml_name_decl("_HID", aml_string("%s",
|
||||
+ spoof_vmgenid_policy() == SPOOF_VGID_MASK ? "MSFT0002" : "QEMUVGID")));
|
||||
aml_append(dev, aml_name_decl("_CID", aml_string("VM_Gen_Counter")));
|
||||
aml_append(dev, aml_name_decl("_DDN", aml_string("VM_Gen_Counter")));
|
||||
|
||||
diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c
|
||||
index 85fb7da..4b6854e 100644
|
||||
--- a/hw/misc/pvpanic-isa.c
|
||||
+++ b/hw/misc/pvpanic-isa.c
|
||||
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qemu/module.h"
|
||||
#include "system/runstate.h"
|
||||
|
||||
@@ -69,6 +70,10 @@ static void build_pvpanic_isa_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
PVPanicISAState *s = PVPANIC_ISA_DEVICE(adev);
|
||||
Aml *dev = aml_device("PEVT");
|
||||
|
||||
+ if (spoof_pvpanic_hide()) { /* qemu-spoof: omit the QEMU0001 _HID node */
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0001")));
|
||||
|
||||
crs = aml_resource_template();
|
||||
diff --git a/hw/nvram/fw_cfg-acpi.c b/hw/nvram/fw_cfg-acpi.c
|
||||
index 2e6ef89..8bb7be7 100644
|
||||
--- a/hw/nvram/fw_cfg-acpi.c
|
||||
+++ b/hw/nvram/fw_cfg-acpi.c
|
||||
@@ -5,13 +5,14 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "hw/nvram/fw_cfg_acpi.h"
|
||||
#include "hw/acpi/aml-build.h"
|
||||
|
||||
void fw_cfg_acpi_dsdt_add(Aml *scope, const MemMapEntry *fw_cfg_memmap)
|
||||
{
|
||||
Aml *dev = aml_device("FWCF");
|
||||
- aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
|
||||
+ aml_append(dev, aml_name_decl("_HID", aml_string("%s", spoof_fwcfg_acpi_devid("QEMU0002"))));
|
||||
/* device present, functioning, decoding, not shown in UI */
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
||||
aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
|
||||
@@ -0,0 +1,61 @@
|
||||
qemu-spoof: CPUID — KVM signature + leaf 0x16 frequency
|
||||
|
||||
CPUID 0x40000000 KVM signature "KVMKVMKVM" -> spoof_kvm_signature() (vendor-
|
||||
anchored GenuineIntel/AuthenticAMD). CPUID leaf 0x16 (Processor Frequency Info),
|
||||
which stock QEMU returns as zeros, is filled from spoof_cpu_{base,max,bus}_mhz().
|
||||
Inert unless a spoof-seed is set. (hv-mode kvm=off / hypervisor-bit handling is
|
||||
done via the Proxmox cpu flags, see README.)
|
||||
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
|
||||
index c6fd1dc..1093326 100644
|
||||
--- a/target/i386/cpu.c
|
||||
+++ b/target/i386/cpu.c
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
@@ -9166,6 +9167,20 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||
*ecx = 0;
|
||||
*edx = 0;
|
||||
break;
|
||||
+ case 0x16:
|
||||
+ /* qemu-spoof: Processor Frequency Information (stock QEMU returns 0). */
|
||||
+ if (spoof_enabled()) {
|
||||
+ *eax = spoof_cpu_base_mhz(0) & 0xffff;
|
||||
+ *ebx = spoof_cpu_max_mhz(0) & 0xffff;
|
||||
+ *ecx = spoof_cpu_bus_mhz(0) & 0xffff;
|
||||
+ *edx = 0;
|
||||
+ } else {
|
||||
+ *eax = 0;
|
||||
+ *ebx = 0;
|
||||
+ *ecx = 0;
|
||||
+ *edx = 0;
|
||||
+ }
|
||||
+ break;
|
||||
default:
|
||||
/* reserved values: zero */
|
||||
*eax = 0;
|
||||
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
|
||||
index 9e35288..5b112b5 100644
|
||||
--- a/target/i386/kvm/kvm.c
|
||||
+++ b/target/i386/kvm/kvm.c
|
||||
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qapi/qapi-events-run-state.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
@@ -2380,7 +2381,7 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
||||
abort();
|
||||
#endif
|
||||
} else if (cpu->expose_kvm) {
|
||||
- memcpy(signature, "KVMKVMKVM\0\0\0", 12);
|
||||
+ memcpy(signature, spoof_kvm_signature("KVMKVMKVM\0\0\0"), 12);
|
||||
c = &cpuid_data.entries[cpuid_i++];
|
||||
c->function = KVM_CPUID_SIGNATURE | kvm_base;
|
||||
c->eax = KVM_CPUID_FEATURES | kvm_base;
|
||||
@@ -0,0 +1,34 @@
|
||||
qemu-spoof: fw_cfg selector + DMA signatures
|
||||
|
||||
FW_CFG_SIGNATURE "QEMU" -> spoof_fwcfg_sig(); the DMA signature ("QEMU CFG") ->
|
||||
spoof_fwcfg_dma_sig(). Inert unless a spoof-seed is set.
|
||||
diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
|
||||
index 1d7d835..3b29e96 100644
|
||||
--- a/hw/nvram/fw_cfg.c
|
||||
+++ b/hw/nvram/fw_cfg.c
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qemu/datadir.h"
|
||||
#include "system/system.h"
|
||||
#include "system/dma.h"
|
||||
@@ -442,7 +443,7 @@ static uint64_t fw_cfg_dma_mem_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
/* Return a signature value (and handle various read sizes) */
|
||||
- return extract64(FW_CFG_DMA_SIGNATURE, (8 - addr - size) * 8, size * 8);
|
||||
+ return extract64(spoof_fwcfg_dma_sig(FW_CFG_DMA_SIGNATURE), (8 - addr - size) * 8, size * 8);
|
||||
}
|
||||
|
||||
static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr,
|
||||
@@ -1002,7 +1003,7 @@ static void fw_cfg_common_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
- fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4);
|
||||
+ fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)spoof_fwcfg_sig("QEMU"), 4);
|
||||
fw_cfg_add_bytes(s, FW_CFG_UUID, &qemu_uuid, 16);
|
||||
fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)!machine->enable_graphics);
|
||||
fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)(machine->boot_config.has_menu && machine->boot_config.menu));
|
||||
@@ -0,0 +1,27 @@
|
||||
qemu-spoof: clear the SMBIOS type0 "VM" characteristic bit
|
||||
|
||||
type0 bios-characteristics-extension byte sets bit 0x10 ("VM") for QEMU; suppress
|
||||
it when spoof_smbios_hide_vm(). (type1/2/3 manufacturer/product/serial are the
|
||||
config layer; type4 socket / type11 OEM-strings / type17 memory wiring is a
|
||||
follow-up.) Inert unless a spoof-seed is set.
|
||||
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
|
||||
index 7d71418..4c39b5a 100644
|
||||
--- a/hw/smbios/smbios.c
|
||||
+++ b/hw/smbios/smbios.c
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qapi/error.h"
|
||||
@@ -583,7 +584,7 @@ static void smbios_build_type_0_table(void)
|
||||
if (smbios_type0.uefi) {
|
||||
t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */
|
||||
}
|
||||
- if (smbios_type0.vm) {
|
||||
+ if (smbios_type0.vm && !spoof_smbios_hide_vm()) {
|
||||
t->bios_characteristics_extension_bytes[1] |= 0x10; /* |= VM */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
qemu-spoof: EDID monitor identity (with weak fallbacks for the qemu-edid tool)
|
||||
|
||||
vendor/name/model/serial/manufacture-date now come from spoof_edid_*(). Since
|
||||
edid-generate.c is also compiled into the standalone qemu-edid tool (which does
|
||||
not link the spoof module), weak default definitions of spoof_edid_* are provided
|
||||
here so the tool links; the strong spoof-display.c definitions override them in
|
||||
the system emulator. Inert unless a spoof-seed is set.
|
||||
diff --git a/hw/display/edid-generate.c b/hw/display/edid-generate.c
|
||||
index 2cb8196..170f2b8 100644
|
||||
--- a/hw/display/edid-generate.c
|
||||
+++ b/hw/display/edid-generate.c
|
||||
@@ -7,6 +7,20 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "hw/display/edid.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
+
|
||||
+/*
|
||||
+ * Weak fallbacks: the standalone qemu-edid tool compiles this file but does NOT
|
||||
+ * link the spoof module, so it would have undefined spoof_edid_* references.
|
||||
+ * These defaults let it link; in the system emulator the strong spoof-display.c
|
||||
+ * definitions override them.
|
||||
+ */
|
||||
+__attribute__((weak)) const char *spoof_edid_vendor(const char *def) { return def; }
|
||||
+__attribute__((weak)) const char *spoof_edid_name(const char *def) { return def; }
|
||||
+__attribute__((weak)) uint16_t spoof_edid_model(uint16_t def) { return def; }
|
||||
+__attribute__((weak)) uint32_t spoof_edid_serial(uint32_t def) { return def; }
|
||||
+__attribute__((weak)) int spoof_edid_week(int def) { return def; }
|
||||
+__attribute__((weak)) int spoof_edid_year(int def) { return def; }
|
||||
|
||||
static const struct edid_mode {
|
||||
uint32_t xres;
|
||||
@@ -394,10 +408,10 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
|
||||
/* =============== set defaults =============== */
|
||||
|
||||
if (!info->vendor || strlen(info->vendor) != 3) {
|
||||
- info->vendor = "RHT";
|
||||
+ info->vendor = spoof_edid_vendor("RHT");
|
||||
}
|
||||
if (!info->name) {
|
||||
- info->name = "QEMU Monitor";
|
||||
+ info->name = spoof_edid_name("QEMU Monitor");
|
||||
}
|
||||
if (!info->prefx) {
|
||||
info->prefx = 1280;
|
||||
@@ -449,15 +463,15 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
|
||||
uint16_t vendor_id = ((((info->vendor[0] - '@') & 0x1f) << 10) |
|
||||
(((info->vendor[1] - '@') & 0x1f) << 5) |
|
||||
(((info->vendor[2] - '@') & 0x1f) << 0));
|
||||
- uint16_t model_nr = 0x1234;
|
||||
- uint32_t serial_nr = info->serial ? atoi(info->serial) : 0;
|
||||
+ uint16_t model_nr = spoof_edid_model(0x1234);
|
||||
+ uint32_t serial_nr = spoof_edid_serial(info->serial ? atoi(info->serial) : 0);
|
||||
stw_be_p(edid + 8, vendor_id);
|
||||
stw_le_p(edid + 10, model_nr);
|
||||
stl_le_p(edid + 12, serial_nr);
|
||||
|
||||
/* manufacture week and year */
|
||||
- edid[16] = 42;
|
||||
- edid[17] = 2014 - 1990;
|
||||
+ edid[16] = spoof_edid_week(42);
|
||||
+ edid[17] = spoof_edid_year(2014) - 1990;
|
||||
|
||||
/* edid version */
|
||||
edid[18] = 1;
|
||||
@@ -0,0 +1,78 @@
|
||||
qemu-spoof: storage identity (IDE + NVMe)
|
||||
|
||||
IDE: model "QEMU HARDDISK"/"QEMU DVD-ROM", firmware rev, and the auto serial now
|
||||
route through spoof_disk_*/spoof_cdrom_model. NVMe: model "QEMU NVMe Ctrl",
|
||||
serial, and the firmware revision (was QEMU_VERSION -- an information leak) now
|
||||
route through spoof_nvme_*. Inert unless a spoof-seed is set.
|
||||
diff --git a/hw/ide/core.c b/hw/ide/core.c
|
||||
index 7a15d6c..ad5251f 100644
|
||||
--- a/hw/ide/core.c
|
||||
+++ b/hw/ide/core.c
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "hw/core/irq.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "migration/vmstate.h"
|
||||
@@ -2641,19 +2642,23 @@ int ide_init_drive(IDEState *s, IDEDevice *dev, IDEDriveKind kind, Error **errp)
|
||||
} else {
|
||||
snprintf(s->drive_serial_str, sizeof(s->drive_serial_str),
|
||||
"QM%05d", s->drive_serial);
|
||||
+ if (spoof_enabled()) { /* qemu-spoof: brand-correct serial */
|
||||
+ pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str),
|
||||
+ spoof_disk_serial(s->drive_serial_str));
|
||||
+ }
|
||||
}
|
||||
if (dev->model) {
|
||||
pstrcpy(s->drive_model_str, sizeof(s->drive_model_str), dev->model);
|
||||
} else {
|
||||
switch (kind) {
|
||||
case IDE_CD:
|
||||
- strcpy(s->drive_model_str, "QEMU DVD-ROM");
|
||||
+ strcpy(s->drive_model_str, spoof_cdrom_model("QEMU DVD-ROM"));
|
||||
break;
|
||||
case IDE_CFATA:
|
||||
strcpy(s->drive_model_str, "QEMU MICRODRIVE");
|
||||
break;
|
||||
default:
|
||||
- strcpy(s->drive_model_str, "QEMU HARDDISK");
|
||||
+ strcpy(s->drive_model_str, spoof_disk_model("QEMU HARDDISK"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2661,7 +2666,7 @@ int ide_init_drive(IDEState *s, IDEDevice *dev, IDEDriveKind kind, Error **errp)
|
||||
if (dev->version) {
|
||||
pstrcpy(s->version, sizeof(s->version), dev->version);
|
||||
} else {
|
||||
- pstrcpy(s->version, sizeof(s->version), QEMU_HW_VERSION);
|
||||
+ pstrcpy(s->version, sizeof(s->version), spoof_disk_fw(QEMU_HW_VERSION));
|
||||
}
|
||||
|
||||
ide_reset(s);
|
||||
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
|
||||
index be6c702..575085a 100644
|
||||
--- a/hw/nvme/ctrl.c
|
||||
+++ b/hw/nvme/ctrl.c
|
||||
@@ -194,6 +194,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/log.h"
|
||||
@@ -9096,9 +9097,9 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev)
|
||||
|
||||
id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID));
|
||||
id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID));
|
||||
- strpadcpy((char *)id->mn, sizeof(id->mn), "QEMU NVMe Ctrl", ' ');
|
||||
- strpadcpy((char *)id->fr, sizeof(id->fr), QEMU_VERSION, ' ');
|
||||
- strpadcpy((char *)id->sn, sizeof(id->sn), n->params.serial, ' ');
|
||||
+ strpadcpy((char *)id->mn, sizeof(id->mn), spoof_nvme_model("QEMU NVMe Ctrl"), ' ');
|
||||
+ strpadcpy((char *)id->fr, sizeof(id->fr), spoof_nvme_fw(QEMU_VERSION), ' ');
|
||||
+ strpadcpy((char *)id->sn, sizeof(id->sn), spoof_nvme_serial(n->params.serial), ' ');
|
||||
|
||||
id->cntlid = cpu_to_le16(n->cntlid);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
qemu-spoof: OEM-brand emulated PCI subsystem ids (realize-time)
|
||||
|
||||
QEMU stamps a constant subsystem id (default 1b36:1100) on every emulated PCI
|
||||
device -- a fleet fingerprint. The subsystem id is not used for guest driver
|
||||
binding, so override it at realize time with an OEM-branded SVID/SSID (chipset
|
||||
vendor + per-role device id) for non-bridge devices. Red Hat/virtio (0x1af4) is
|
||||
skipped: legacy-virtio encodes the device type in the subsystem id, so changing
|
||||
it would break the guest driver (virtio is de-fingerprinted by device choice
|
||||
instead). vfio passthrough overwrites this with the real hardware SSID afterwards.
|
||||
This is the realize-time override path the deferred 0018 note called for; it does
|
||||
NOT touch vendor/device ids (those ARE driver-binding and would break drivers).
|
||||
Inert unless a spoof-seed is set.
|
||||
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
|
||||
index 2c3657d..e3da2ab 100644
|
||||
--- a/hw/pci/pci.c
|
||||
+++ b/hw/pci/pci.c
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qemu/datadir.h"
|
||||
#include "qemu/units.h"
|
||||
#include "hw/core/irq.h"
|
||||
@@ -1417,6 +1418,27 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev,
|
||||
assert(!pc->subsystem_vendor_id);
|
||||
assert(!pc->subsystem_id);
|
||||
}
|
||||
+
|
||||
+ /*
|
||||
+ * qemu-spoof: OEM-brand the subsystem id of emulated devices. Real boards
|
||||
+ * carry the vendor's SSID; QEMU stamps a constant 1b36:1100 default on every
|
||||
+ * device, which is itself a fleet fingerprint. The subsystem id does not take
|
||||
+ * part in driver binding, so this is safe -- EXCEPT for Red Hat/virtio
|
||||
+ * (0x1af4), where legacy-virtio encodes the device type in the subsystem id;
|
||||
+ * skip those (virtio is de-fingerprinted by device choice, not id spoofing).
|
||||
+ * vfio passthrough overwrites this later with the real hardware SSID. Inert
|
||||
+ * unless a spoof-seed is set.
|
||||
+ */
|
||||
+ if (spoof_enabled() && !is_bridge &&
|
||||
+ pci_get_word(pci_dev->config + PCI_VENDOR_ID) != 0x1af4) {
|
||||
+ const char *role = object_get_typename(OBJECT(pci_dev));
|
||||
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID,
|
||||
+ spoof_pci_subvendor(
|
||||
+ pci_get_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID)));
|
||||
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID,
|
||||
+ spoof_pci_subdevice(role,
|
||||
+ pci_get_word(pci_dev->config + PCI_SUBSYSTEM_ID)));
|
||||
+ }
|
||||
pci_init_cmask(pci_dev);
|
||||
pci_init_wmask(pci_dev);
|
||||
pci_init_w1cmask(pci_dev);
|
||||
@@ -0,0 +1,46 @@
|
||||
qemu-spoof: machine description string
|
||||
|
||||
m->desc "Standard PC (Q35...)" / "(i440FX...)" -> spoof_machine_desc() (the
|
||||
platform-anchored board model). Inert unless a spoof-seed is set.
|
||||
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
|
||||
index 4d71e0d..d5561dd 100644
|
||||
--- a/hw/i386/pc_piix.c
|
||||
+++ b/hw/i386/pc_piix.c
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include CONFIG_DEVICES
|
||||
|
||||
#include "qemu/units.h"
|
||||
@@ -408,7 +409,7 @@ static void pc_i440fx_machine_options(MachineClass *m)
|
||||
pcmc->default_cpu_version = 1;
|
||||
|
||||
m->family = "pc_piix";
|
||||
- m->desc = "Standard PC (i440FX + PIIX, 1996)";
|
||||
+ m->desc = spoof_machine_desc("Standard PC (i440FX + PIIX, 1996)");
|
||||
m->default_machine_opts = "firmware=bios-256k.bin";
|
||||
m->default_display = "std";
|
||||
m->default_nic = "e1000";
|
||||
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
|
||||
index cb23322..65864b0 100644
|
||||
--- a/hw/i386/pc_q35.c
|
||||
+++ b/hw/i386/pc_q35.c
|
||||
@@ -29,6 +29,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qemu/units.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/char/parallel-isa.h"
|
||||
@@ -346,7 +347,7 @@ static void pc_q35_machine_options(MachineClass *m)
|
||||
pcmc->default_cpu_version = 1;
|
||||
|
||||
m->family = "pc_q35";
|
||||
- m->desc = "Standard PC (Q35 + ICH9, 2009)";
|
||||
+ m->desc = spoof_machine_desc("Standard PC (Q35 + ICH9, 2009)");
|
||||
m->units_per_default_bus = 1;
|
||||
m->default_machine_opts = "firmware=bios-256k.bin";
|
||||
m->default_display = "std";
|
||||
@@ -0,0 +1,81 @@
|
||||
qemu-spoof: SMBIOS type3/4/11/17 identity
|
||||
|
||||
Wire the remaining SMBIOS getters (module already derives them):
|
||||
- type3 chassis type (Desktop/Notebook, from the persona machine class)
|
||||
- type4 processor socket designation + processor manufacturer (CPU-vendor anchored)
|
||||
- type11 OEM strings: QEMU emits no type 11 at all (a tell); emit the spoofed
|
||||
OEM string when config provided none
|
||||
- type17 memory manufacturer / serial / part (brand-correct, per-unit)
|
||||
All inert unless a spoof-seed is set (getters return the stock value when off).
|
||||
The type0 "VM" bit is handled by 0015; the spoof.h include is added there.
|
||||
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
|
||||
index 7d71418..0d8e569 100644
|
||||
--- a/hw/smbios/smbios.c
|
||||
+++ b/hw/smbios/smbios.c
|
||||
@@ -656,7 +656,7 @@ static void smbios_build_type_3_table(void)
|
||||
SMBIOS_BUILD_TABLE_PRE(3, T3_BASE, true); /* required */
|
||||
|
||||
SMBIOS_TABLE_SET_STR(3, manufacturer_str, type3.manufacturer);
|
||||
- t->type = 0x01; /* Other */
|
||||
+ t->type = spoof_smbios_chassis_type(0x01); /* Other (3=Desktop, 10=Notebook) */
|
||||
SMBIOS_TABLE_SET_STR(3, version_str, type3.version);
|
||||
SMBIOS_TABLE_SET_STR(3, serial_number_str, type3.serial);
|
||||
SMBIOS_TABLE_SET_STR(3, asset_tag_number_str, type3.asset);
|
||||
@@ -690,11 +690,13 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance,
|
||||
SMBIOS_BUILD_TABLE_PRE_SIZE(4, T4_BASE + instance,
|
||||
true, tbl_len); /* required */
|
||||
|
||||
- snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance);
|
||||
+ snprintf(sock_str, sizeof(sock_str), "%s%2x",
|
||||
+ spoof_smbios_cpu_socket(type4.sock_pfx), instance);
|
||||
SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str);
|
||||
t->processor_type = 0x03; /* CPU */
|
||||
t->processor_family = 0xfe; /* use Processor Family 2 field */
|
||||
- SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer);
|
||||
+ SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str,
|
||||
+ spoof_smbios_cpu_manufacturer(type4.manufacturer));
|
||||
if (type4.processor_id == 0) {
|
||||
t->processor_id[0] = cpu_to_le32(smbios_cpuid_version);
|
||||
t->processor_id[1] = cpu_to_le32(smbios_cpuid_features);
|
||||
@@ -825,13 +827,23 @@ static void smbios_build_type_11_table(void)
|
||||
{
|
||||
char count_str[128];
|
||||
size_t i;
|
||||
+ /* qemu-spoof: real machines expose OEM strings here; QEMU emits no type 11 at
|
||||
+ * all, which is itself a tell. If config gave none, emit the spoofed one. */
|
||||
+ const char *spoof_oem = spoof_smbios_oem_string(NULL);
|
||||
|
||||
- if (type11.nvalues == 0) {
|
||||
+ if (type11.nvalues == 0 && !spoof_oem) {
|
||||
return;
|
||||
}
|
||||
|
||||
SMBIOS_BUILD_TABLE_PRE(11, T11_BASE, true); /* required */
|
||||
|
||||
+ if (type11.nvalues == 0) {
|
||||
+ t->count = 1;
|
||||
+ SMBIOS_TABLE_SET_STR_LIST(11, spoof_oem);
|
||||
+ SMBIOS_BUILD_TABLE_POST;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
snprintf(count_str, sizeof(count_str), "%zu", type11.nvalues);
|
||||
t->count = type11.nvalues;
|
||||
|
||||
@@ -900,10 +912,13 @@ static void smbios_build_type_17_table(unsigned instance, uint64_t size)
|
||||
t->memory_type = 0x07; /* RAM */
|
||||
t->type_detail = cpu_to_le16(0x02); /* Other */
|
||||
t->speed = cpu_to_le16(type17.speed);
|
||||
- SMBIOS_TABLE_SET_STR(17, manufacturer_str, type17.manufacturer);
|
||||
- SMBIOS_TABLE_SET_STR(17, serial_number_str, type17.serial);
|
||||
+ SMBIOS_TABLE_SET_STR(17, manufacturer_str,
|
||||
+ spoof_smbios_mem_manufacturer(type17.manufacturer));
|
||||
+ SMBIOS_TABLE_SET_STR(17, serial_number_str,
|
||||
+ spoof_smbios_mem_serial(type17.serial));
|
||||
SMBIOS_TABLE_SET_STR(17, asset_tag_number_str, type17.asset);
|
||||
- SMBIOS_TABLE_SET_STR(17, part_number_str, type17.part);
|
||||
+ SMBIOS_TABLE_SET_STR(17, part_number_str,
|
||||
+ spoof_smbios_mem_part(type17.part));
|
||||
t->attributes = 0; /* Unknown */
|
||||
t->configured_clock_speed = t->speed; /* reuse value for max speed */
|
||||
t->minimum_voltage = cpu_to_le16(0); /* Unknown */
|
||||
@@ -0,0 +1,62 @@
|
||||
qemu-spoof: storage extra identity (IDE WWN/rotation + NVMe EUI64/NGUID)
|
||||
|
||||
Complements 0017 (model/serial/fw). When a spoof-seed is set and these are unset:
|
||||
- IDE: a NAA-5 World Wide Name (brand OUI) and the ATA nominal rotation rate
|
||||
(1 = SSD, else RPM matching the spoofed model) -- QEMU leaves both at 0/absent.
|
||||
- NVMe: a real-OUI EUI-64 and a 16-byte NGUID -- QEMU advertises none unless asked,
|
||||
itself a tell. Inert unless a spoof-seed is set. (ide/core.c spoof.h include is
|
||||
added by 0017; ns.c adds it here.)
|
||||
diff --git a/hw/ide/core.c b/hw/ide/core.c
|
||||
index 7a15d6c..6dcd4f9 100644
|
||||
--- a/hw/ide/core.c
|
||||
+++ b/hw/ide/core.c
|
||||
@@ -2617,6 +2617,14 @@ int ide_init_drive(IDEState *s, IDEDevice *dev, IDEDriveKind kind, Error **errp)
|
||||
s->chs_trans = dev->chs_trans;
|
||||
s->nb_sectors = nb_sectors;
|
||||
s->wwn = dev->wwn;
|
||||
+ if (spoof_enabled()) { /* qemu-spoof: NAA WWN + rotation rate */
|
||||
+ if (!s->wwn) {
|
||||
+ s->wwn = spoof_disk_wwn(0);
|
||||
+ }
|
||||
+ if (kind != IDE_CD && dev->rotation_rate == 0) {
|
||||
+ dev->rotation_rate = spoof_disk_rotation(0);
|
||||
+ }
|
||||
+ }
|
||||
/* The SMART values should be preserved across power cycles
|
||||
but they aren't. */
|
||||
s->smart_enabled = 1;
|
||||
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
|
||||
index b0106ea..723216b 100644
|
||||
--- a/hw/nvme/ns.c
|
||||
+++ b/hw/nvme/ns.c
|
||||
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
+#include "hw/misc/spoof.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -91,6 +92,22 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
|
||||
if (!ns->params.eui64 && ns->params.eui64_default) {
|
||||
ns->params.eui64 = ns_count + NVME_EUI64_DEFAULT;
|
||||
}
|
||||
+ if (spoof_enabled()) { /* qemu-spoof: real OUI EUI-64 + NGUID identifiers */
|
||||
+ int zi;
|
||||
+ bool nguid_zero = true;
|
||||
+ if (!ns->params.eui64) {
|
||||
+ ns->params.eui64 = spoof_nvme_eui64(0);
|
||||
+ }
|
||||
+ for (zi = 0; zi < 16; zi++) {
|
||||
+ if (ns->params.nguid.data[zi]) {
|
||||
+ nguid_zero = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (nguid_zero) {
|
||||
+ spoof_nvme_nguid(ns->params.nguid.data);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
/* simple copy */
|
||||
id_ns->mssrl = cpu_to_le16(ns->params.mssrl);
|
||||
@@ -0,0 +1,19 @@
|
||||
qemu-spoof: CPU microcode revision (IA32_UCODE_REV)
|
||||
|
||||
QEMU/KVM default ucode_rev (Intel 0x1_00000000, AMD 0x01000065) is constant and
|
||||
recognisable. Route it through spoof_cpu_microcode, which returns a plausible
|
||||
vendor-positioned revision (Intel in MSR bits 63:32, AMD in 31:0) anchored to the
|
||||
persona CPU vendor. Inert unless a spoof-seed is set. (target/i386 spoof.h include
|
||||
is added by 0013.)
|
||||
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
|
||||
index c6fd1dc..96cbedc 100644
|
||||
--- a/target/i386/cpu.c
|
||||
+++ b/target/i386/cpu.c
|
||||
@@ -10026,6 +10026,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
} else {
|
||||
cpu->ucode_rev = 0x100000000ULL;
|
||||
}
|
||||
+ cpu->ucode_rev = spoof_cpu_microcode(cpu->ucode_rev); /* qemu-spoof */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -0,0 +1,53 @@
|
||||
# Anti-detect patch series
|
||||
|
||||
Quilt patches injected (by `make prepare`) into `pve-qemu/debian/patches/series`,
|
||||
applied in order during the `.deb` build. Each wires a stock QEMU literal to a
|
||||
`spoof_*()` getter from the in-tree module (`hw/misc/spoof.{c,h}`, dropped in by
|
||||
the Makefile). The getter returns the seed-derived value, or the **stock default**
|
||||
when no `spoof-seed` is set — so the whole series is inert until a VM opts in.
|
||||
|
||||
Authored against the pinned `pve-qemu/qemu` tree (QEMU 11.0) so the context lines
|
||||
match exactly. Naming: `0002` = infra, `0010+` = one aspect each.
|
||||
|
||||
| patch | covers |
|
||||
|---|---|
|
||||
| 0002-x86-machine-spoof-properties | register `spoof-seed` / `spoof-hv` / `spoof-waet` / `spoof-vmgenid` / `spoof-pvpanic` machine props |
|
||||
| 0010-acpi-table-header | ACPI OEM id / table id / creator + FADT hypervisor-vendor |
|
||||
| 0011-acpi-policy-waet-vmgenid | drop the WAET table; skip the vmgenid SSDT when policy = hide |
|
||||
| 0012-acpi-device-hids | vmgenid `_HID` (mask), pvpanic `_HID` (hide), fw_cfg `_HID` |
|
||||
| 0013-cpuid-kvm-sig-freq | CPUID KVM signature + leaf 0x16 frequency |
|
||||
| 0014-fwcfg-signatures | fw_cfg selector + DMA signatures |
|
||||
| 0015-smbios-vm-bit | clear the SMBIOS type0 "VM" characteristic bit |
|
||||
| 0016-edid-monitor | EDID vendor / name / model / serial / manufacture-date |
|
||||
| 0017-storage-identity | IDE model / serial / fw / cdrom + NVMe model / serial / fw |
|
||||
| 0018-pci-subsystem-id | realize-time OEM-brand of emulated PCI subsystem ids (SVID/SSID) |
|
||||
| 0019-machine-desc | machine `desc` string → platform board model |
|
||||
| 0021-smbios-identity | SMBIOS type3 chassis / type4 socket + cpu-mfr / type11 OEM / type17 memory |
|
||||
| 0022-storage-extra | IDE WWN + rotation rate; NVMe EUI-64 + NGUID |
|
||||
| 0023-cpu-microcode | CPU microcode revision (IA32_UCODE_REV), vendor-positioned |
|
||||
|
||||
## Why 0018 is subsystem-only (the PCI-id problem)
|
||||
|
||||
The PCI **vendor:device** id IS the guest driver-binding contract: NetKVM binds
|
||||
`1af4:1000`, the ivshmem driver binds `1af4:1110`, the GPU driver binds its id.
|
||||
Spoofing those breaks the device (no driver matches). So `0018` only rewrites the
|
||||
**subsystem** id (not used for binding) — and even that skips `0x1af4`, because
|
||||
*legacy*-virtio encodes the device type in the subsystem id. Class-bound devices
|
||||
(NVMe / xHCI / AHCI bind by class code, not id) *could* take a real vendor:device,
|
||||
but that needs a per-role allowlist. virtio / ivshmem / GPU ids are instead
|
||||
de-fingerprinted by **device choice** (`e1000e` / SATA) and vfio `x-pci-*`, not by
|
||||
id spoofing.
|
||||
|
||||
## Out of scope (handled by config or upstream, not these patches)
|
||||
|
||||
- **PCI vendor:device id** for class-bound devices (qemu-nvme `1b36:0010` → a real
|
||||
NVMe vendor; `nec-usb-xhci`): safe in principle, would need a per-role allowlist in
|
||||
the `0018` realize hook.
|
||||
- **HDA codec id** (`QEMU_HDA_ID_VENDOR 0x1af4`): a compile-time constant baked into a
|
||||
`static const` codec descriptor; spoofing needs a runtime override of the
|
||||
GET_PARAMETER verb response.
|
||||
- **vmport/vmmouse** (VMware backdoor port 0x5658): disable via `-machine vmport=off`
|
||||
(a machine option).
|
||||
- **hv-mode / hypervisor bit**: via the CPU model flags (`cpu: host,hidden=1` + hv
|
||||
enlightenments). The module's `spoof_hv_mode` policy is the intent; enforcement is
|
||||
configuration.
|
||||
Submodule
+1
Submodule pve-qemu added at 817dab1cda
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
}
|
||||
@@ -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
@@ -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 */
|
||||
Reference in New Issue
Block a user