Files
lirent ec27a8f4e5 spoof: spoof-mode master fork (none/hyperv/vbs/physical) + microsoft-vm persona
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>
2026-06-11 23:15:15 +03:00

114 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.