vmspect 0.5.1

Blazing-fast static inspection, forensic analysis and information extraction library for virtual machine disk images (VMDK, RAW, QCOW2, VHD, VHDX, VDI).
# vmspect


[![Crates.io](https://img.shields.io/crates/v/vmspect.svg)](https://crates.io/crates/vmspect)
[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE)
[![Rust](https://img.shields.io/badge/rust-2021%20edition-orange.svg)]()

`vmspect` is a Rust library and CLI tool for **ultra-fast static inspection, forensic analysis and information extraction of virtual machine disk images** (VMDK, RAW, QCOW2, VHD, VHDX, VDI, etc.).

It can examine partition-table structures (MBR/GPT), identify the guest operating system (Windows/Linux), extract complete lists of installed software and detect integration tools (Guest Tools) in a **non-invasive** way (without booting the virtual machine or requiring mount privileges on the host) in **less than 70 ms** even for virtual disks larger than 80 GiB.

---

## πŸš€ Key Features


- **Extreme performance and lightweight streaming (<70 ms for 80 GiB images):**
  - **Native Rust parser:** Direct, ultra-low-latency reading for `RAW` and `VMDK` images (`monolithicSparse`, `monolithicFlat`, `twoGbMaxExtentFlat/Sparse`, etc.) without external dependencies or child processes.
  - **Integrated `qemu-nbd` server:** For complex formats (`QCOW2`, `VHDX`, `VDI`, compressed/streamOptimized VMDK), connects over a local TCP socket (`127.0.0.1`) or UNIX sockets using the standard NBD protocol, with direct block streaming and no temporary files on disk.
- **Resilience against dirty registries and NTFS fallback (Graceful Degradation):**
  - **Permissive Windows Registry reading:** Tolerance for dirty or damaged registry hives (`SequenceNumberMismatch` caused by abrupt shutdowns or hot snapshots) using `Hive::without_validation` and isolating internal panics from third-party libraries via `catch_unwind`.
  - **NTFS fallback inspection:** If the Registry hives are totally inaccessible, `vmspect` gracefully degrades by inspecting the PE header of `\Windows\System32\ntoskrnl.exe` directly to extract the OS build and version, and scans `\Program Files`, tagging applications as `source: Some("FallbackFS")`.
  - **Non-fatal warnings list:** Reports issues in the `warnings` field of the report without aborting the inspection pipeline.
- **Multi-hypervisor, hypervisor-agnostic Guest Tools detection:**
  - Full and typed support in the `GuestTools` struct to identify and extract the version of:
    - **VMware Tools / open-vm-tools**
    - **VirtualBox Guest Additions**
    - **QEMU Guest Agent**
    - **Hyper-V Integration Services**
- **Supported guest operating systems:**
  - **Windows (NTFS):** Extracts Registry hives (`SOFTWARE` and `SYSTEM`) by parsing uninstall keys (32 and 64-bit), operating system version, build number, Service Pack and Guest Tools.
  - **Linux (ext2 / ext3 / ext4):** Reads `/etc/os-release`, `/etc/hostname` and analyzes the `/var/lib/dpkg/status` package database along with virtualization agents.
- **Scheme and file-system detection:**
  - Partition schemes: **MBR**, **GPT** and **Volumes without a partition table**.
  - Signature recognition: **NTFS**, **FAT12/16/32**, **ext2/3/4**, **XFS**, **Btrfs**, **LVM2 PV**, **Linux Swap**.
- **Agnostic and complete extraction:**
  - Default, complete collection of all applications and system information without noise filters or proprietary categorizations.
  - Support for `--no-apps` (disables application collection) and `--no-system` (disables OS metadata collection) flags.
- **Designed for UI and CLI:**
  - Emits progress events in structured percentages (`0%` to `100%`) ideal for **Tauri**, **egui** or **Electron**.
  - Cancellation support via atomic tokens (`Arc<AtomicBool>` / `CancellationToken`) while preserving partial results.

---

## DiagnΓ³stico de discos VMDK y `qemu-nbd`


Un descriptor VMDK no siempre contiene los datos del disco. Puede declarar varios extents
(`FLAT`, `VMFS`, `VMFSRAW`, `SPARSE` o `VMFSSPARSE`) y tambiΓ©n puede apuntar a un disco padre
mediante `parentFileNameHint`. Todos esos archivos forman parte de la entrada que debe estar
disponible para la inspecciΓ³n.

Cuando falta un componente, `vmspect` devuelve `VmSpectError::MissingDiskComponent` en lugar de
`VmSpectError::QemuNotFound`. El error conserva el nombre declarado, el descriptor principal,
la ruta resuelta y el error original del sistema operativo. Por ejemplo:

```text
Missing VMDK extent 'drive-0-cl2-s001.vmdk' referenced by 'D:\PLC NΒ°4\MΓ‘quinas Virtuales\Windows UE 6.0 ROckWell Revs 9\drive-0-cl2.vmdk'. Resolved path: 'D:\PLC NΒ°4\MΓ‘quinas Virtuales\Windows UE 6.0 ROckWell Revs 9\drive-0-cl2-s001.vmdk'. OS error: The system cannot find the file specified.
```

`qemu-nbd` no puede reparar una cadena VMDK incompleta: solo proporciona acceso a una imagen
que ya es coherente. Si falta un extent o un disco padre, hay que recuperar el archivo correcto
desde el almacenamiento original o desde una copia consistente. No se debe renombrar otro
extent para sustituir al faltante, porque eso puede mezclar segmentos distintos y producir una
imagen silenciosamente corrupta.

Los errores de resoluciΓ³n del ejecutable (`qemu_nbd`, `QEMU_NBD` o `PATH`) se reportan como
`QemuNotFound`. Los fallos del proceso ya iniciado β€”cΓ³digo de salida distinto de cero,
`stderr`, handshake o timeout NBDβ€” se reportan como `Nbd` y conservan el contexto de ejecuciΓ³n.

---

## πŸ“‚ Crate Structure


The project follows the standard Rust library package convention:

```text
vmspect/
β”œβ”€β”€ Cargo.toml               # Crate configuration, metadata and dependencies
β”œβ”€β”€ readme.md                # Main documentation
β”œβ”€β”€ LICENSE                  # MIT / Apache-2.0 license
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ lib.rs               # Library entry point (public API and re-exports)
β”‚   β”œβ”€β”€ models/              # Domain types (InspectionReport, GuestInfo, Partition, etc.)
β”‚   β”‚   β”œβ”€β”€ image.rs
β”‚   β”‚   β”œβ”€β”€ options.rs       # Inspection options, progress and cancellation
β”‚   β”‚   β”œβ”€β”€ partition.rs
β”‚   β”‚   β”œβ”€β”€ software.rs      # Program and GuestInfo models
β”‚   β”‚   └── traits.rs        # Abstract traits (OsInspector, VmDriver, MemoryMapper)
β”‚   β”œβ”€β”€ parsers/             # Per-OS analyzers
β”‚   β”‚   β”œβ”€β”€ mod.rs           # OsInspector trait and polymorphic factory
β”‚   β”‚   β”œβ”€β”€ windows.rs       # NTFS extraction and Registry parsing (nt-hive)
β”‚   β”‚   β”œβ”€β”€ linux.rs         # ext4 superblock reading and DPKG database
β”‚   β”‚   └── desconocido.rs   # Handling of unrecognized operating systems
β”‚   └── vms/                 # Disk access and virtualization layer
β”‚       β”œβ”€β”€ mod.rs           # Disk access module
β”‚       β”œβ”€β”€ detector.rs       # MBR/GPT detection and FS signatures
β”‚       β”œβ”€β”€ nbd.rs           # Native NBD client and qemu-nbd connector
β”‚       β”œβ”€β”€ stream.rs        # DiskReader facade and VirtualDisk view (Read + Seek)
β”‚       └── vmdk.rs          # Native VMDK parser (sparse and descriptors)
β”œβ”€β”€ tests/                   # Integration tests
β”‚   └── integration_test.rs
└── examples/                # Ready-to-run usage examples
    └── basic_inspection.rs
```

---

## πŸ“¦ Installation


Add `vmspect` to your `Cargo.toml`:

```toml
[dependencies]
vmspect = "0.5.1"
```

---

## πŸ’‘ Examples as a Library


### 1. Full Inspection with Guest Tools, Warnings and Progress


```rust
use std::path::Path;
use vmspect::{inspect_with_progress, Options, InspectionProgressEvent};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let path = Path::new("virtual_disk.vmdk");
    
    // Options configuration (e.g. full OS and application analysis)
    let options = Options::default();

    let report = inspect_with_progress(path, &options, |p: InspectionProgressEvent| {
        println!("[{:>3}%] {} - {}", p.percentage, p.stage, p.detail.unwrap_or_default());
    })?;

    println!("Disk format: {}", report.image.format);
    println!("Operating system: {:?}", report.operating_system);
    println!("OS details: {}", report.guest_info.formatted_os_string());
    
    // Hypervisor-agnostic Guest Tools detection (VMware, VirtualBox, QEMU, Hyper-V)
    if let Some(ref tools) = report.guest_info.guest_tools {
        if tools.present {
            println!("Guest tools: {} (version: {})", tools.kind, tools.version.as_deref().unwrap_or("N/A"));
        }
    }

    // Non-fatal warnings (graceful Registry/FS degradation)
    if !report.warnings.is_empty() {
        println!("Inspection warnings:");
        for w in &report.warnings {
            println!("  [!] {}", w);
        }
    }

    println!("Partitions detected: {}", report.partitions.len());
    println!("Installed software found: {}", report.installed_programs.len());

    for prog in report.installed_programs.iter().take(10) {
        let source = prog.source.as_deref().map(|s| format!(" [{}]", s)).unwrap_or_default();
        println!(
            " - {} (v{}) [Publisher: {}]{}",
            prog.name,
            prog.version.as_deref().unwrap_or("N/A"),
            prog.publisher.as_deref().unwrap_or("N/A"),
            source
        );
    }

    Ok(())
}
```

### 2. Advanced Extraction Options


```rust
use vmspect::Options;

// Disable application or system extraction depending on performance needs:
let light_options = Options {
    no_apps: true,           // Skip installed-software scan
    no_system: false,        // Keep OS detection and Guest Tools
    force_nbd: false,        // Use the ultra-fast native parser when available
    ..Options::default()
};

assert!(!light_options.should_analyze_apps());
assert!(light_options.should_analyze_system());
```

### 3. Concurrent Processing and Result Preservation on Cancellation


`ConcurrentProcessor` and `InspectionEngine` provide clean shutdown (Graceful Shutdown) with **partial-result preservation**. When the cancellation token is triggered, worker threads do not accept new images, safely finish the in-progress analysis and return all successfully processed reports:

```rust
use std::path::PathBuf;
use vmspect::prelude::*;

fn main() -> Result<()> {
    let paths = vec![
        PathBuf::from("srv1.vmdk"),
        PathBuf::from("srv2.raw"),
        PathBuf::from("srv3.qcow2"),
        PathBuf::from("srv4.vhdx"),
    ];

    let cancel = CancellationToken::new();
    let options = Options::default()
        .with_cancellation_token(&cancel);

    let engine = InspectionEngine::new(options);

    // Cancel at any time from another thread or callback:
    // cancel.cancel();

    // Returns all reports completed before and during cancellation:
    let completed_reports = engine.inspect_batch(paths, 4)?;

    println!("Total reports recovered: {}", completed_reports.len());
    for r in &completed_reports {
        println!(" - {} (OS: {:?})", r.image.path.display(), r.operating_system);
    }

    Ok(())
}
```

### 4. Tauri / Async Runtime Integration


```rust,ignore
use tauri::Emitter;
use vmspect::{inspect_with_progress, InspectionReport, InspectionProgressEvent, Options};

#[tauri::command]

async fn inspect_vm(app_handle: tauri::AppHandle, path: String) -> Result<InspectionReport, String> {
    let path = std::path::PathBuf::from(path);
    let options = Options::default();

    tauri::async_runtime::spawn_blocking(move || {
        inspect_with_progress(&path, &options, |p: InspectionProgressEvent| {
            let _ = app_handle.emit("inspection_progress", p);
        })
        .map_err(|e| e.to_string())
    })
    .await
    .map_err(|e| e.to_string())?
}
```

---

## πŸ–₯️ Command-Line Interface (CLI) Usage


`vmspect` ships a high-performance terminal binary:

```bash
# Standard inspection with human-formatted output:

vmspect /path/to/disk.vmdk

# Structured JSON output (ideal for scripts, CI/CD pipelines and forensic analysis):

vmspect /path/to/disk.qcow2 --json

# Concurrent recursive scan of a full VM directory:

vmspect /var/lib/libvirt/images/ --concurrent --recursive --workers 8

# Fast inspection skipping application extraction:

vmspect /path/to/disk.vhdx --no-apps

# Inspection skipping OS metadata:

vmspect /path/to/disk.raw --no-system
```

---

## πŸ› οΈ Running Tests and Examples


### Run Unit and Integration Tests:

```bash
cargo test
```

### Run Example with a Real Disk:

```bash
cargo run --example basic_inspection -- /path/to/your/disk.vmdk
```

---

## πŸ“„ License


This project is licensed under the **MIT** or **Apache-2.0** license at your option.