ec27a8f4e5
Phase 0. Replace the spoof-hv knob with spoof-mode, a first-class selector modeled as two axes (persona x presence) exposed as 4 presets: - none: stock (spoof_on now requires mode != none, killing half-spoof states) - hyperv: Microsoft Virtual Machine persona + Hyper-V presence (honest child VM) - vbs (seeded default): real-OEM persona + Hyper-V presence (mimic physical Win11+VBS) - physical: real-OEM persona + bare metal Engine: spoof_mode()/spoof_persona_msvm()/spoof_presence_hyperv() in spoof-core; the hv/waet/pvpanic/vmgenid policies now derive from the mode. microsoft-vm persona wired across platform (ACPI OEM VRTUAL/MICROSFT, Microsoft Corporation), system type1 (Virtual Machine), storage (Virtual HD / Msft Virtual DVD-ROM) and EDID (MSF/Hyper-V). New getters spoof_system_manufacturer/product (type1, real-OEM or Hyper-V). Patches: 0002 registers spoof-mode; 0024 now also forces type1 system identity. spoof-hv kept as a back-compat alias. Inert without a seed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
114 lines
4.7 KiB
Markdown
114 lines
4.7 KiB
Markdown
# 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.
|
||
|
||
### Modes
|
||
|
||
`-machine ...,spoof-mode=` selects the identity strategy (two axes — hardware
|
||
persona × hypervisor presence — as presets):
|
||
|
||
| mode | persona | presence | looks like |
|
||
|---|---|---|---|
|
||
| `none` | stock | KVM | a plain VM (no spoof) |
|
||
| `hyperv` | Microsoft "Virtual Machine" | Hyper-V | an honest Hyper-V guest |
|
||
| `vbs` (default when seeded) | real OEM | Hyper-V | a physical Win11 box with VBS on |
|
||
| `physical` | real OEM | bare metal | a physical machine (no hypervisor) |
|
||
|
||
The presence axis (clearing the hypervisor bit / Hyper-V enlightenments) is partly
|
||
the CPU model configuration (`cpu: host,hidden=1` + `hv-*`); set it to match the mode.
|
||
|
||
## 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.
|