Skip to content

Instantly share code, notes, and snippets.

@x42en
Created May 22, 2026 09:56
Show Gist options
  • Select an option

  • Save x42en/77cada8691080418dadef3030935c307 to your computer and use it in GitHub Desktop.

Select an option

Save x42en/77cada8691080418dadef3030935c307 to your computer and use it in GitHub Desktop.
Simple VM detection mitigation

vm_hide

Reconfigure a VirtualBox VM so that its guest operating system perceives it as a physical server rather than a virtual machine. The script rewrites the DMI/SMBIOS strings, the storage device identity, the NIC vendor OUI, and the CPUID brand string, and disables the paravirtualization provider that would otherwise leak VBox/KVM to the guest.

Hardware profiles are stored in profiles.json next to the script. At startup, an interactive menu lists every profile defined in that file and lets the operator choose which one to apply.

Requirements

Tool Purpose
VBoxManage Provided by VirtualBox; performs all VM edits.
jq Reads profiles.json.
bash ≥ 4 Required for mapfile and associative arrays.

The target VM must be powered off (poweroff, aborted, or saved). The script aborts otherwise.

Usage

./vm_hide.sh <vm_name>

The script will:

  1. Validate that the VM exists and is powered off.
  2. Enumerate the profiles defined in profiles.json and prompt for a selection.
  3. Apply the chosen profile by writing the corresponding extradata keys and calling VBoxManage modifyvm.

Take a snapshot of the VM before running the script. The configuration can be rolled back by restoring the snapshot, or by manually unsetting each extradata key (see the script's epilogue).

What gets reconfigured

Surface Mechanism
DMI / SMBIOS (BIOS, System, Board, Chassis) extradata VBoxInternal/Devices/pcbios/0/Config/Dmi*. Read by dmidecode inside the guest.
NIC controller type VBoxManage modifyvm --nictypeN 82545EM (Intel PRO/1000 MT Server).
NIC MAC OUI The first three octets of every active NIC are rewritten to the profile's OUI; the last three octets are preserved so VM-level uniqueness is kept.
Storage device identity extradata VBoxInternal/Devices/{ahci,piix3ide}/0/Config/.../{ModelNumber,SerialNumber,FirmwareRevision}. The controller and the attached disk are auto-detected.
CPUID brand string (leaves 0x80000002-4) VBoxManage modifyvm --cpuidset with the profile's brand string encoded as little-endian 32-bit words.
Hypervisor-present bit (CPUID 1.ECX[31]) Cleared by VBoxManage modifyvm --paravirtprovider none.
64-bit / long-mode capabilities VBoxManage modifyvm --longmode on --pae on --ioapic on.

Profiles

A profile is one object in the profiles[] array of profiles.json. Four reference profiles are shipped:

ID Label
dell-poweredge-r640 Dell PowerEdge R640 (Xeon Gold 6126)
hpe-proliant-dl380-gen10 HPE ProLiant DL380 Gen10 (Xeon Gold 5218)
lenovo-thinksystem-sr650 Lenovo ThinkSystem SR650 (Xeon Silver 4214)
supermicro-superserver-1029u Supermicro SuperServer 1029U-TR4 (Xeon Gold 6230)

Profile schema

{
  "id":    "unique-id",                  // selector key (string, kebab-case recommended)
  "label": "Human readable label",       // displayed in the interactive menu

  "bios":    { "vendor": "...", "version": "...", "release_date": "MM/DD/YYYY" },
  "system":  { "vendor": "...", "product": "...", "version": "...",
               "serial": "...", "sku": "...", "family": "...", "uuid": "..." },
  "board":   { "vendor": "...", "product": "...", "version": "...",
               "serial": "...", "asset_tag": "", "location": "..." },
  "chassis": { "vendor": "...", "version": "...", "serial": "...",
               "asset_tag": "", "type": 23 },            // 23 = Rack Mount Chassis
  "disk":    { "model": "...", "serial": "...", "fw": "..." },
  "cpu":     { "brand": "Intel(R) Xeon(R) ... CPU @ X.XXGHz" },
  "nic":     { "oui": "001B21" }                          // 6 hex digits, no separator
}

All 26 leaf fields are required. The loader bails out if a profile is incomplete.

Validation rules enforced by the script

The script will refuse to apply a profile (and abort the run) if any of the following are violated:

  • system.version must contain at least one non-digit character. VirtualBox stores extradata values that match ^[0-9]+$ as INTEGER, which causes the BIOS to fail at boot with VERR_CFGM_NOT_STRING. The same rule applies to every DMI string field — set_ed_str() rejects purely numeric values.
  • nic.oui must be exactly 6 hexadecimal digits with no separator (e.g. 001B21).
  • nic.oui must have bit 0 of its first octet cleared (unicast OUI). The script rejects multicast OUIs.
  • chassis.type is a SMBIOS chassis-type integer (see DMTF DSP0134 § 7.4.1). 23 = Rack Mount Chassis.

Adding a profile

  1. Append a new object to the profiles[] array.
  2. Validate the file: jq empty profiles.json.
  3. Re-run the script: the new entry appears in the selection menu.

Be conservative with serial-number conventions: a real-looking serial that follows the vendor's documented format is much less detectable than an obvious placeholder.

Known limitations

These limits are structural to VirtualBox and cannot be worked around from a configuration script:

  • PCI subsystem IDs are not overridable. VirtualBox exposes no CFGM key for the SubsystemVendorId / SubsystemId of e1000, ahci, or pcbios. lspci -nnv inside the guest will always show Subsystem: 8086:xxxx on the NIC and 80EE:xxxx on the chipset.
  • ACPI OEM identifiers cannot be set via extradata. Keys such as AcpiOemId, AcpiOemTableId, and AcpiCreatorId are not declared by the ACPI device and cause VERR_CFGM_CONFIG_UNKNOWN_VALUE. The only supported lever is CustomTable (path to a precompiled DSDT/SSDT blob).
  • Disk identity overrides only work on SATA (ahci) and IDE (piix3ide). SCSI/SAS/NVMe/VirtIO controllers do not expose ModelNumber / SerialNumber / FirmwareRevision keys. The script skips the disk step (with a warning) when the system disk sits on an unsupported controller.

Going beyond these limits requires patching the VirtualBox binaries themselves (e.g. via projects like VBoxHardenedLoader), which is out of scope for this script. A Python rewrite would not unlock additional surfaces, because no Python binding (neither pyvbox nor the upstream XPCOM SDK) bypasses the CFGM validation in VBoxDD.

File layout

.
├── vm_hide.sh        # main script
├── profiles.json     # hardware profile catalog (this is the only file to edit)
└── README.md

Rollback

# List every key the script wrote
VBoxManage getextradata <vm_name> enumerate

# Remove a single key
VBoxManage setextradata <vm_name> <key>

# Remove a CPUID override
VBoxManage modifyvm <vm_name> --cpuidremove 0x80000002
VBoxManage modifyvm <vm_name> --cpuidremove 0x80000003
VBoxManage modifyvm <vm_name> --cpuidremove 0x80000004

# Restore the default paravirt provider
VBoxManage modifyvm <vm_name> --paravirtprovider default

Restoring a snapshot taken before the run is the safest option.

{
"profiles": [
{
"id": "dell-poweredge-r640",
"label": "Dell PowerEdge R640 (Xeon Gold 6126)",
"bios": {
"vendor": "American Megatrends Inc.",
"version": "2.18.1263",
"release_date": "04/26/2018"
},
"system": {
"vendor": "Dell Inc.",
"product": "PowerEdge R640",
"version": "A01",
"serial": "JKD3F72",
"sku": "SKU=NotProvided",
"family": "PowerEdge",
"uuid": "4c4c4544-004a-4b10-8044-b3c04f463732"
},
"board": {
"vendor": "Dell Inc.",
"product": "0H28RR",
"version": "A07",
"serial": ".JKD3F72.CN701636A30123.",
"asset_tag": "",
"location": "Part Component"
},
"chassis": {
"vendor": "Dell Inc.",
"version": "Not Specified",
"serial": "JKD3F72",
"asset_tag": "",
"type": 23
},
"disk": {
"model": "SEAGATE ST600MM0006",
"serial": "S0M3X9YK0000E612KX9P",
"fw": "LS08"
},
"cpu": {
"brand": "Intel(R) Xeon(R) Gold 6126 CPU @ 2.60GHz"
},
"nic": {
"oui": "001B21"
}
},
{
"id": "hpe-proliant-dl380-gen10",
"label": "HPE ProLiant DL380 Gen10 (Xeon Gold 5218)",
"bios": {
"vendor": "HPE",
"version": "U30",
"release_date": "09/12/2021"
},
"system": {
"vendor": "HPE",
"product": "ProLiant DL380 Gen10",
"version": "U30",
"serial": "MXQ12504N7",
"sku": "868703-B21",
"family": "ProLiant",
"uuid": "37323335-3935-584d-5131-323530344e37"
},
"board": {
"vendor": "HPE",
"product": "ProLiant DL380 Gen10",
"version": "1",
"serial": "PWUFM0ARHB103J",
"asset_tag": "",
"location": "Part Component"
},
"chassis": {
"vendor": "HPE",
"version": "Not Specified",
"serial": "MXQ12504N7",
"asset_tag": "",
"type": 23
},
"disk": {
"model": "HP EH0300JEDHC",
"serial": "S7M0A1NK0000K641ABCD",
"fw": "HPD4"
},
"cpu": {
"brand": "Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz"
},
"nic": {
"oui": "9CDC71"
}
},
{
"id": "lenovo-thinksystem-sr650",
"label": "Lenovo ThinkSystem SR650 (Xeon Silver 4214)",
"bios": {
"vendor": "Lenovo",
"version": "IVE160N-2.71",
"release_date": "07/16/2021"
},
"system": {
"vendor": "Lenovo",
"product": "ThinkSystem SR650 -[7X06CTO1WW]-",
"version": "07A",
"serial": "J300X4VE",
"sku": "SKU=NotProvided",
"family": "ThinkSystem SR650",
"uuid": "30303031-3030-3030-3030-3030333030584a"
},
"board": {
"vendor": "Lenovo",
"product": "-[7X06CTO1WW]-",
"version": "SB27A11321",
"serial": "L1HF93A013Z",
"asset_tag": "",
"location": "Part Component"
},
"chassis": {
"vendor": "Lenovo",
"version": "Not Specified",
"serial": "J300X4VE",
"asset_tag": "",
"type": 23
},
"disk": {
"model": "ST1200MM0129",
"serial": "WBN1A7XQ0000E906LMNO",
"fw": "CN02"
},
"cpu": {
"brand": "Intel(R) Xeon(R) Silver 4214 CPU @ 2.20GHz"
},
"nic": {
"oui": "E454E8"
}
},
{
"id": "supermicro-superserver-1029u",
"label": "Supermicro SuperServer 1029U-TR4 (Xeon Gold 6230)",
"bios": {
"vendor": "American Megatrends Inc.",
"version": "3.4",
"release_date": "12/17/2020"
},
"system": {
"vendor": "Supermicro",
"product": "SYS-1029U-TR4",
"version": "REV1.02A",
"serial": "S358928X1816213",
"sku": "SKU=NotProvided",
"family": "Ultra",
"uuid": "00000000-0000-0000-0000-3cecef2f1ab4"
},
"board": {
"vendor": "Supermicro",
"product": "X11DPU",
"version": "1.02A",
"serial": "WM188S010138",
"asset_tag": "",
"location": "Part Component"
},
"chassis": {
"vendor": "Supermicro",
"version": "REV1.02A",
"serial": "C815UAH26B70069",
"asset_tag": "",
"type": 23
},
"disk": {
"model": "INTEL SSDSC2KB960G8",
"serial": "BTYF0489051C960CGN",
"fw": "XCV10132"
},
"cpu": {
"brand": "Intel(R) Xeon(R) Gold 6230 CPU @ 2.10GHz"
},
"nic": {
"oui": "0CC47A"
}
}
]
}
#!/usr/bin/env bash
#
# vm_hide.sh — Présente une VM VirtualBox au guest OS comme une machine physique.
#
# Modifie :
# - DMI/SMBIOS (BIOS, System, Board, Chassis) lus par dmidecode
# - Table ACPI (OEM ID/TableId/Creator)
# - Identité du disque (Model, Serial, Firmware) — auto-détection du contrôleur
# - Type de NIC (Intel PRO/1000 MT Server)
# - CPUID brand string + masquage du bit "hypervisor present"
# - Désactivation du provider paravirt (sinon le guest voit "KVM/VBox")
#
# Usage : ./vm_hide.sh <nom_de_la_VM>
#
# La VM doit être ÉTEINTE. Faites un snapshot avant exécution.
set -euo pipefail
# ---------------------------------------------------------------------------
# Profil matériel "physique" cible
#
# Les valeurs sont désormais chargées depuis 'profiles.json' (à côté du script)
# via un menu interactif. Voir la section "Sélection du profil" plus bas.
#
# Variables peuplées par load_profile() :
# BIOS_VENDOR BIOS_VERSION BIOS_RELEASE_DATE
# SYS_VENDOR SYS_PRODUCT SYS_VERSION SYS_SERIAL SYS_SKU SYS_FAMILY SYS_UUID
# BOARD_VENDOR BOARD_PRODUCT BOARD_VERSION BOARD_SERIAL BOARD_ASSET_TAG BOARD_LOCATION
# CHASSIS_VENDOR CHASSIS_VERSION CHASSIS_SERIAL CHASSIS_ASSET_TAG CHASSIS_TYPE
# DISK_MODEL DISK_SERIAL DISK_FW
# CPU_BRAND
# NIC_OUI
#
# ATTENTION (rappels conservés depuis l'ancienne version inline) :
# - SYS_VERSION ne doit JAMAIS être purement numérique (ex: "01"). VBox
# stockerait l'extradata comme INTEGER et le BIOS échouerait au démarrage
# avec VERR_CFGM_NOT_STRING. Toujours inclure au moins une lettre.
# set_ed_str() refuse de toute façon les valeurs purement numériques.
# - CHASSIS_TYPE : 23 = Rack Mount Chassis.
# - NIC_OUI : 6 hex sans séparateur (ex: 001B21 = Intel Corporate). Les 3
# derniers octets de chaque NIC sont conservés -> unicité par VM préservée.
# - VirtualBox n'expose AUCUNE clé CFGM pour overrider les PCI subsystem IDs
# (ni e1000, ni ahci, ni pcbios). Toute tentative -> VERR_CFGM_CONFIG_UNKNOWN_VALUE.
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# Vérifications préalables
# ---------------------------------------------------------------------------
if [[ $# -ne 1 ]]; then
echo "Usage : $0 <nom_de_la_VM>" >&2
exit 1
fi
VM_NAME="$1"
if ! command -v VBoxManage >/dev/null 2>&1; then
echo "Erreur : VBoxManage introuvable dans le PATH." >&2
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
echo "Erreur : jq introuvable dans le PATH (requis pour lire profiles.json)." >&2
echo " Installation : apt install jq (Debian/Ubuntu)" >&2
exit 1
fi
if ! VBoxManage showvminfo "$VM_NAME" >/dev/null 2>&1; then
echo "Erreur : la VM '$VM_NAME' n'existe pas." >&2
echo "Liste : VBoxManage list vms" >&2
exit 1
fi
VM_STATE=$(VBoxManage showvminfo "$VM_NAME" --machinereadable \
| awk -F= '/^VMState=/ {gsub(/"/,"",$2); print $2}')
if [[ "$VM_STATE" != "poweroff" && "$VM_STATE" != "aborted" && "$VM_STATE" != "saved" ]]; then
echo "Erreur : la VM doit être éteinte (état actuel : $VM_STATE)." >&2
exit 1
fi
# ---------------------------------------------------------------------------
# Sélection du profil matériel depuis profiles.json
#
# profiles.json doit se trouver dans le même dossier que ce script et avoir
# la structure : { "profiles": [ { "id": "...", "label": "...", "bios": {...},
# "system": {...}, "board": {...}, "chassis": {...}, "disk": {...},
# "cpu": {...}, "nic": {...} }, ... ] }
#
# Un unique appel jq émet toutes les valeurs en TSV (ordre figé) pour éviter
# 30 invocations successives et garantir la cohérence atomique du profil.
# ---------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROFILES_FILE="$SCRIPT_DIR/profiles.json"
if [[ ! -f "$PROFILES_FILE" ]]; then
echo "Erreur : fichier de profils introuvable : $PROFILES_FILE" >&2
exit 1
fi
if ! jq empty "$PROFILES_FILE" 2>/dev/null; then
echo "Erreur : $PROFILES_FILE n'est pas un JSON valide." >&2
exit 1
fi
# Liste (id, label) des profils disponibles
mapfile -t PROFILE_IDS < <(jq -r '.profiles[].id' "$PROFILES_FILE")
mapfile -t PROFILE_LABELS < <(jq -r '.profiles[].label' "$PROFILES_FILE")
if [[ ${#PROFILE_IDS[@]} -eq 0 ]]; then
echo "Erreur : aucun profil défini dans $PROFILES_FILE." >&2
exit 1
fi
echo
echo "Profils disponibles dans $PROFILES_FILE :"
PS3=$'\n'"Choisissez un profil pour la VM '$VM_NAME' (numéro) : "
select choice in "${PROFILE_LABELS[@]}"; do
if [[ -n "${choice:-}" ]]; then
PROFILE_INDEX=$((REPLY - 1))
PROFILE_ID="${PROFILE_IDS[$PROFILE_INDEX]}"
PROFILE_LABEL="${PROFILE_LABELS[$PROFILE_INDEX]}"
break
fi
echo "Choix invalide. Réessayez (ou Ctrl-C pour annuler)." >&2
done
echo "[*] Profil sélectionné : $PROFILE_LABEL (id=$PROFILE_ID)"
# Extraction atomique de toutes les valeurs (une par ligne), ordre figé.
# NB : on n'utilise PAS @tsv + 'read -d $\t' car le tab est traité comme
# whitespace par 'read' et les champs vides consécutifs seraient fusionnés
# (BOARD_ASSET_TAG="", CHASSIS_ASSET_TAG="" -> tout le reste décalé).
# Une ligne par valeur + mapfile préserve correctement les champs vides.
# Aucun champ de profil ne contient de saut de ligne.
mapfile -t PROFILE_VALS < <(jq -r --arg id "$PROFILE_ID" '
.profiles[] | select(.id == $id) |
.bios.vendor, .bios.version, .bios.release_date,
.system.vendor, .system.product, .system.version, .system.serial,
.system.sku, .system.family, .system.uuid,
.board.vendor, .board.product, .board.version, .board.serial,
.board.asset_tag, .board.location,
.chassis.vendor, .chassis.version, .chassis.serial,
.chassis.asset_tag, (.chassis.type | tostring),
.disk.model, .disk.serial, .disk.fw,
.cpu.brand,
.nic.oui
' "$PROFILES_FILE")
if [[ ${#PROFILE_VALS[@]} -ne 26 ]]; then
echo "Erreur : profil '$PROFILE_ID' introuvable ou incomplet dans $PROFILES_FILE" >&2
echo " (${#PROFILE_VALS[@]} valeurs lues, 26 attendues)." >&2
exit 1
fi
BIOS_VENDOR="${PROFILE_VALS[0]}"
BIOS_VERSION="${PROFILE_VALS[1]}"
BIOS_RELEASE_DATE="${PROFILE_VALS[2]}"
SYS_VENDOR="${PROFILE_VALS[3]}"
SYS_PRODUCT="${PROFILE_VALS[4]}"
SYS_VERSION="${PROFILE_VALS[5]}"
SYS_SERIAL="${PROFILE_VALS[6]}"
SYS_SKU="${PROFILE_VALS[7]}"
SYS_FAMILY="${PROFILE_VALS[8]}"
SYS_UUID="${PROFILE_VALS[9]}"
BOARD_VENDOR="${PROFILE_VALS[10]}"
BOARD_PRODUCT="${PROFILE_VALS[11]}"
BOARD_VERSION="${PROFILE_VALS[12]}"
BOARD_SERIAL="${PROFILE_VALS[13]}"
BOARD_ASSET_TAG="${PROFILE_VALS[14]}"
BOARD_LOCATION="${PROFILE_VALS[15]}"
CHASSIS_VENDOR="${PROFILE_VALS[16]}"
CHASSIS_VERSION="${PROFILE_VALS[17]}"
CHASSIS_SERIAL="${PROFILE_VALS[18]}"
CHASSIS_ASSET_TAG="${PROFILE_VALS[19]}"
CHASSIS_TYPE="${PROFILE_VALS[20]}"
DISK_MODEL="${PROFILE_VALS[21]}"
DISK_SERIAL="${PROFILE_VALS[22]}"
DISK_FW="${PROFILE_VALS[23]}"
CPU_BRAND="${PROFILE_VALS[24]}"
NIC_OUI="${PROFILE_VALS[25]}"
set_ed() {
# set_ed <key> <value> => définit
# set_ed <key> => supprime (valeur vide)
if [[ $# -eq 2 && -n "$2" ]]; then
VBoxManage setextradata "$VM_NAME" "$1" "$2"
else
VBoxManage setextradata "$VM_NAME" "$1"
fi
}
# set_ed_str : comme set_ed, mais REFUSE une valeur purement numérique.
# Indispensable pour les champs DMI typés "string" (sinon VBox stocke
# l'extradata comme INTEGER -> VERR_CFGM_NOT_STRING au démarrage).
set_ed_str() {
local key="$1" val="${2:-}"
if [[ -n "$val" && "$val" =~ ^[0-9]+$ ]]; then
echo "Erreur : la valeur '$val' pour '$key' est purement numérique." >&2
echo " VBox la stockerait comme INTEGER et le BIOS échouerait" >&2
echo " (VERR_CFGM_NOT_STRING). Ajoutez au moins une lettre." >&2
exit 1
fi
set_ed "$key" "$val"
}
# ---------------------------------------------------------------------------
# 1. NIC : type Intel 82545EM + OUI MAC
#
# Nettoyage préalable des clés PCI subsystem fautives écrites par d'anciennes
# versions du script (VBox n'accepte pas ces clés -> plantage au démarrage).
# ---------------------------------------------------------------------------
echo "[*] Nettoyage des clés PCI subsystem obsolètes éventuelles..."
while IFS= read -r oldkey; do
if [[ -n "$oldkey" ]]; then
echo " - suppression clé obsolète : $oldkey"
VBoxManage setextradata "$VM_NAME" "$oldkey" || true
fi
done < <(VBoxManage getextradata "$VM_NAME" enumerate 2>/dev/null \
| sed -nE 's@^Key: (VBoxInternal/Devices/[^/]+/[0-9]+/Config/(SubsystemVendorId|SubsystemId)),.*@\1@p')
echo "[*] Configuration des cartes réseau (type 82545EM + OUI MAC)..."
INFO=$(VBoxManage showvminfo "$VM_NAME" --machinereadable)
# Validation du format de l'OUI : 6 hex exactement, pas de séparateur.
if [[ ! "$NIC_OUI" =~ ^[0-9A-Fa-f]{6}$ ]]; then
echo "Erreur : NIC_OUI doit faire 6 caractères hex (ex: 001B21)." >&2
exit 1
fi
# Un OUI valide pour une carte unicast doit avoir le bit 0 du 1er octet à 0.
first_byte=$((16#${NIC_OUI:0:2}))
if (( (first_byte & 0x01) != 0 )); then
echo "Erreur : OUI '$NIC_OUI' a le bit multicast à 1." >&2
exit 1
fi
for i in 1 2 3 4 5 6 7 8; do
nic=$(echo "$INFO" | awk -F= "/^nic${i}=/ {gsub(/\"/,\"\",\$2); print \$2}")
if [[ -z "$nic" || "$nic" == "none" ]]; then
continue
fi
# a) Type de NIC
VBoxManage modifyvm "$VM_NAME" --nictype${i} 82545EM
# b) MAC : remplace l'OUI (3 premiers octets) en conservant le NIC ID
current_mac=$(echo "$INFO" \
| awk -F= "/^macaddress${i}=/ {gsub(/\"/,\"\",\$2); print \$2}")
if [[ -n "$current_mac" && "$current_mac" =~ ^[0-9A-Fa-f]{12}$ ]]; then
new_mac="${NIC_OUI^^}${current_mac:6:6}"
VBoxManage modifyvm "$VM_NAME" --macaddress${i} "$new_mac"
echo " - NIC${i} MAC : $current_mac -> $new_mac"
else
echo " - NIC${i} MAC : format inattendu ('$current_mac'), non modifié."
fi
done
# ---------------------------------------------------------------------------
# 2. DMI / SMBIOS (lus par dmidecode dans le guest)
# ---------------------------------------------------------------------------
echo "[*] Injection des chaînes DMI/SMBIOS..."
PCBIOS="VBoxInternal/Devices/pcbios/0/Config"
# --- Champs typés STRING (set_ed_str refuse les valeurs purement numériques) ---
set_ed_str "$PCBIOS/DmiBIOSVendor" "$BIOS_VENDOR"
set_ed_str "$PCBIOS/DmiBIOSVersion" "$BIOS_VERSION"
set_ed_str "$PCBIOS/DmiBIOSReleaseDate" "$BIOS_RELEASE_DATE"
set_ed_str "$PCBIOS/DmiSystemVendor" "$SYS_VENDOR"
set_ed_str "$PCBIOS/DmiSystemProduct" "$SYS_PRODUCT"
set_ed_str "$PCBIOS/DmiSystemVersion" "$SYS_VERSION"
set_ed_str "$PCBIOS/DmiSystemSerial" "$SYS_SERIAL"
set_ed_str "$PCBIOS/DmiSystemSKU" "$SYS_SKU"
set_ed_str "$PCBIOS/DmiSystemFamily" "$SYS_FAMILY"
set_ed_str "$PCBIOS/DmiSystemUuid" "$SYS_UUID"
set_ed_str "$PCBIOS/DmiBoardVendor" "$BOARD_VENDOR"
set_ed_str "$PCBIOS/DmiBoardProduct" "$BOARD_PRODUCT"
set_ed_str "$PCBIOS/DmiBoardVersion" "$BOARD_VERSION"
set_ed_str "$PCBIOS/DmiBoardSerial" "$BOARD_SERIAL"
set_ed_str "$PCBIOS/DmiBoardAssetTag" "$BOARD_ASSET_TAG"
set_ed_str "$PCBIOS/DmiBoardLocInChass" "$BOARD_LOCATION"
set_ed_str "$PCBIOS/DmiChassisVendor" "$CHASSIS_VENDOR"
set_ed_str "$PCBIOS/DmiChassisVersion" "$CHASSIS_VERSION"
set_ed_str "$PCBIOS/DmiChassisSerial" "$CHASSIS_SERIAL"
set_ed_str "$PCBIOS/DmiChassisAssetTag" "$CHASSIS_ASSET_TAG"
# --- Champs typés INTEGER (valeurs numériques attendues) ---
set_ed "$PCBIOS/DmiBIOSReleaseMajor" "2"
set_ed "$PCBIOS/DmiBIOSReleaseMinor" "18"
set_ed "$PCBIOS/DmiBIOSFirmwareMajor" "5"
set_ed "$PCBIOS/DmiBIOSFirmwareMinor" "13"
set_ed "$PCBIOS/DmiBoardBoardType" "10" # 10 = Motherboard
set_ed "$PCBIOS/DmiChassisType" "$CHASSIS_TYPE"
# ---------------------------------------------------------------------------
# 3. ACPI : (intentionnellement vide)
#
# Le device ACPI de VirtualBox valide strictement ses clés CFGM et n'expose
# AUCUNE clé du type AcpiOemId/AcpiOemTableId/AcpiCreatorId. Toute tentative
# de les définir via VBoxInternal/Devices/acpi/0/Config/... fait échouer le
# démarrage avec VERR_CFGM_CONFIG_UNKNOWN_VALUE.
#
# Le seul levier officiel est 'CustomTable' (chemin vers un blob ACPI DSDT/SSDT
# pré-compilé), ce qui sort du périmètre de ce script.
#
# Nettoyage des éventuelles clés ACPI fautives écrites par d'anciennes versions.
# ---------------------------------------------------------------------------
echo "[*] Nettoyage des clés ACPI obsolètes éventuelles..."
while IFS= read -r oldkey; do
if [[ -n "$oldkey" ]]; then
echo " - suppression clé obsolète : $oldkey"
VBoxManage setextradata "$VM_NAME" "$oldkey" || true
fi
done < <(VBoxManage getextradata "$VM_NAME" enumerate 2>/dev/null \
| sed -nE 's@^Key: (VBoxInternal/Devices/acpi/0/Config/(AcpiOemId|AcpiOemTableId|AcpiCreatorId|CustomTable)),.*@\1@p')
# ---------------------------------------------------------------------------
# 4. Disque : auto-détection du contrôleur, du device VBox, port et device
#
# IMPORTANT : ces clés vont au niveau DEVICE (ahci/piix3ide), PAS au niveau
# driver (LUN#x/Config). Sinon le driver DrvVD renvoie
# VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES et le disque ne s'attache pas.
# Seuls ahci (SATA) et piix3ide (IDE) supportent ces clés via extradata.
# Les contrôleurs SCSI/SAS/NVMe/VirtIO ne les exposent pas -> on ignore.
# ---------------------------------------------------------------------------
echo "[*] Détection du contrôleur de stockage hébergeant le disque système..."
# Nettoyage défensif d'anciennes clés erronées (LUN#x/Config/...) potentiellement
# écrites par une version précédente du script et qui font planter le démarrage.
# NB : le délimiteur sed est '@' car le motif contient '#' (LUN#).
while IFS= read -r oldkey; do
if [[ -n "$oldkey" ]]; then
echo " - suppression clé obsolète : $oldkey"
VBoxManage setextradata "$VM_NAME" "$oldkey" || true
fi
done < <(VBoxManage getextradata "$VM_NAME" enumerate 2>/dev/null \
| sed -nE 's@^Key: (VBoxInternal/Devices/[^/]+/0/LUN#[0-9]+/Config/(ModelNumber|SerialNumber|FirmwareRevision)),.*@\1@p')
declare -A CTRL_TYPE_MAP=(
[IntelAhci]="ahci"
[PIIX4]="piix3ide"
[PIIX3]="piix3ide"
[ICH6]="piix3ide"
)
DISK_CTRL_DEV=""
DISK_PORT=""
DISK_DEVNUM=""
DISK_FOUND=0
while IFS= read -r line; do
if [[ "$line" =~ ^storagecontrollername([0-9]+)=\"(.+)\"$ ]]; then
idx="${BASH_REMATCH[1]}"
name="${BASH_REMATCH[2]}"
type=$(echo "$INFO" | awk -F= "/^storagecontrollertype${idx}=/ {gsub(/\"/,\"\",\$2); print \$2}")
dev="${CTRL_TYPE_MAP[$type]:-}"
if [[ -z "$dev" ]]; then
echo " - Contrôleur '$name' (type=$type) : non géré pour Model/Serial, ignoré."
continue
fi
# Cherche le 1er disque attaché (exclut DVD/ISO et "none")
attach=$(echo "$INFO" | grep -E "^\"${name}-[0-9]+-[0-9]+\"=\"" \
| grep -viE '"none"|\.iso"' | head -n1 || true)
if [[ -n "$attach" ]]; then
port=$(echo "$attach" | sed -E 's/^"[^"]+-([0-9]+)-([0-9]+)".*/\1/')
devnum=$(echo "$attach" | sed -E 's/^"[^"]+-([0-9]+)-([0-9]+)".*/\2/')
DISK_CTRL_DEV="$dev"
DISK_PORT="$port"
DISK_DEVNUM="$devnum"
DISK_FOUND=1
echo " -> Disque sur '$name' (type=$type, device VBox=$dev, port=$port, dev=$devnum)"
break
fi
fi
done <<< "$INFO"
if [[ $DISK_FOUND -eq 1 ]]; then
case "$DISK_CTRL_DEV" in
ahci)
# SATA : sous-clé Port<n>/... (device toujours 0)
DISK_BASE="VBoxInternal/Devices/ahci/0/Config/Port${DISK_PORT}"
;;
piix3ide)
# IDE : port 0/1 = Primary/Secondary, device 0/1 = Master/Slave
case "${DISK_PORT}-${DISK_DEVNUM}" in
0-0) IDE_SLOT="PrimaryMaster" ;;
0-1) IDE_SLOT="PrimarySlave" ;;
1-0) IDE_SLOT="SecondaryMaster" ;;
1-1) IDE_SLOT="SecondarySlave" ;;
*) IDE_SLOT="" ;;
esac
if [[ -z "$IDE_SLOT" ]]; then
echo " !! Position IDE inattendue port=$DISK_PORT dev=$DISK_DEVNUM, étape ignorée."
DISK_FOUND=0
else
DISK_BASE="VBoxInternal/Devices/piix3ide/0/Config/${IDE_SLOT}"
fi
;;
esac
fi
if [[ $DISK_FOUND -eq 1 ]]; then
set_ed "$DISK_BASE/ModelNumber" "$DISK_MODEL"
set_ed "$DISK_BASE/SerialNumber" "$DISK_SERIAL"
set_ed "$DISK_BASE/FirmwareRevision" "$DISK_FW"
else
echo " !! Aucun disque détecté — étape disque ignorée."
fi
# ---------------------------------------------------------------------------
# 5. CPUID + capacités 64 bits
#
# IMPORTANT : on n'utilise PAS l'ancienne API VBoxInternal/CPUM/HostCPUID/...
# Sur VBox 7.x, ces clés sont obsolètes et leur écriture peut perturber le
# filtrage CPUID, jusqu'à faire perdre l'annonce du long mode (leaf
# 0x80000001 EDX bit 29) -> le guest démarre en i686.
# On utilise la vraie API : VBoxManage modifyvm --cpuidset / --cpuidremove.
#
# Le bit "hypervisor present" (leaf 1, ECX bit 31) est déjà géré par
# --paravirtprovider none : on n'y touche plus manuellement.
# ---------------------------------------------------------------------------
echo "[*] Nettoyage des éventuelles clés HostCPUID obsolètes..."
while IFS= read -r oldkey; do
if [[ -n "$oldkey" ]]; then
echo " - suppression clé obsolète : $oldkey"
VBoxManage setextradata "$VM_NAME" "$oldkey" || true
fi
done < <(VBoxManage getextradata "$VM_NAME" enumerate 2>/dev/null \
| sed -nE 's@^Key: (VBoxInternal/CPUM/HostCPUID/[^,]+),.*@\1@p')
echo "[*] Activation explicite des capacités 64 bits..."
VBoxManage modifyvm "$VM_NAME" --longmode on --pae on --ioapic on
echo "[*] Réécriture du brand string CPU via --cpuidset..."
# Pad/tronque à exactement 48 caractères
brand_padded=$(printf '%-48.48s' "$CPU_BRAND")
# Encode 4 caractères ASCII en mot 32 bits little-endian -> "DDCCBBAA" (8 hex)
encode_le4() {
local s="$1"
local b0 b1 b2 b3
printf -v b0 '%02x' "'${s:0:1}"
printf -v b1 '%02x' "'${s:1:1}"
printf -v b2 '%02x' "'${s:2:1}"
printf -v b3 '%02x' "'${s:3:1}"
echo "${b3}${b2}${b1}${b0}"
}
leaves=(0x80000002 0x80000003 0x80000004)
offset=0
for leaf in "${leaves[@]}"; do
eax_v=$(encode_le4 "${brand_padded:$offset:4}"); offset=$((offset+4))
ebx_v=$(encode_le4 "${brand_padded:$offset:4}"); offset=$((offset+4))
ecx_v=$(encode_le4 "${brand_padded:$offset:4}"); offset=$((offset+4))
edx_v=$(encode_le4 "${brand_padded:$offset:4}"); offset=$((offset+4))
# Supprime un éventuel override antérieur avant de poser le nouveau
VBoxManage modifyvm "$VM_NAME" --cpuidremove "$leaf" 2>/dev/null || true
VBoxManage modifyvm "$VM_NAME" --cpuidset "$leaf" "$eax_v" "$ebx_v" "$ecx_v" "$edx_v"
done
# Désactive le provider paravirt (masque aussi le bit hypervisor de leaf 1 ECX)
VBoxManage modifyvm "$VM_NAME" --paravirtprovider none
# ---------------------------------------------------------------------------
cat <<EOF
============================================================
VM '$VM_NAME' reconfigurée.
- DMI/SMBIOS : $SYS_VENDOR $SYS_PRODUCT (S/N $SYS_SERIAL)
- Disque : $DISK_MODEL (device=$DISK_CTRL_DEV port=$DISK_PORT)
- NIC : Intel 82545EM (PRO/1000 MT Server)
OUI MAC=$NIC_OUI (PCI subsys non modifiable côté VBox)
- CPU : $CPU_BRAND
long mode=on, PAE=on, IOAPIC=on
- Paravirt : désactivé (masque aussi le bit hypervisor de CPUID 1.ECX[31])
Démarrez la VM. En cas de souci, restaurez le snapshot.
Annuler une clé : VBoxManage setextradata "$VM_NAME" <clé>
Tout lister : VBoxManage getextradata "$VM_NAME" enumerate
============================================================
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment