Skip to content

How Antinomie works

Antinomie runs a Linux program in a QEMU virtual machine on your own computer and observes it from inside the guest kernel. The host app keeps what the kernel reports, captures the VM’s network traffic from outside, and analyses the evidence in sandboxed workers.

Guest VM QEMU, one per VM

Your program and its descendants
Patched Linux 6.12.94 kernelrecords each call, its arguments and the data it passes
BusyBox · Debian 13 Minimal · Debian 13 desktop

Host Linux x86-64, your user, no root

Antinomie desktop appVMs, live and saved recordings, Triage
Recordingscalls, buffers, terminal, file bytes
Packet captureper-run route: drop, InetSim, Internet, Tor, WireGuard, SOCKS5
bubblewrap workersstatic analysis, YARA / Sigma / Suricata, TShark, disk and memory readers
The guest kernel observes the program; the host keeps the evidence and analyses it in sandboxed workers. Nothing runs inside the guest to watch the program.

Every base boots a patched Debian Linux 6.12.94 kernel. The patches add a recorder to the kernel itself. For each program you run, its descendants, a process you attach to, or all of userspace with Capture all, the recorder writes:

  • each system call, at entry and exit, with its arguments, its return value and the bytes it passed: paths, buffers read and written, socket addresses, structures. Inputs that were not resident at entry are read again at exit;
  • records beside the calls: process creation and exit, the credentials a task ran as, the SHA-256 of files the program ran, wrote or deleted, the interpreter of an executed script, the file requests of io_uring and AIO, and the guest’s own RAM and disk use for the Summary;
  • from a revision-18 base New in 0.8.0, the position a read or write used in its file, the operands of in-kernel copies (sendfile, splice, copy_file_range, clones, fallocate) and the pages written back through shared file mappings.

Memory can change while it is read, so a captured value carries a capture status, and bytes the recorder could not read stay marked as missing instead of guessed. What the recorder does not see is listed in Known gaps.

The same kernel carries a debug engine for the Debugger tab: breakpoints, stepping, threads and memory without ptrace, so TracerPid stays 0 and the sample’s own PTRACE_TRACEME succeeds. gdb and the GEF or pwndbg console run on the host and talk to it through a local server.

Each base records a guest contract revision, the version of what its kernel offers the host: 0.7.1 uses revision 17, 0.8.0 revision 18. The app asks the guest what it supports before using an addition, so an older base keeps working with fewer records. See Compatibility.

Records leave the guest through one ring buffer per virtual CPU in guest RAM. QEMU keeps the VM’s RAM in a file in the VM’s private runtime folder (mode 0700); the host app maps the data rings read-only and a small control region read-write, and parses every record in its own process with bounded, checked parsing. Commands (upload, run, terminal input, signals, shutdown) and their replies travel through the control region. No network, no shared folder and no device file in the guest is involved.

When a ring is full, When buffers fill in the VM’s telemetry transport settings decides:

Mode What happens Cost
Never drop (default) The system call waits until the host has room, up to Longest wait per event (5 s by default) Heavy tracing can slow the guest; a wait that runs out still drops, and is counted
Drop new telemetry The guest never waits The guest keeps its timing; what did not fit is counted as lost

The ring size (Guest buffer per vCPU, 256 KiB to 4 MiB, 1 MiB by default) decides how long a burst the guest can ride out.

Nothing is dropped silently. The status bar counts loss at each stage, and the Summary’s health card breaks it down:

Counter Where the loss happened
TRL In the transport: records the guest could not hand over
UIL In the window: preview rows the interface could not display (the recording is unaffected)
STL On disk: data that could not be saved
Guest hash / credential drops Records the guest tracer could not send

From 0.8.0 New in 0.8.0 the host also times each pause between its reads of the rings and records its cause, so a guest drop can be tied to what held the host up. A missing event is never proof that something did not happen; see Loss counters and Recording incomplete.

The host app is a native Rust desktop application (egui) that runs as your user, without root. It starts and stops QEMU, attaches to the transport, assembles records into calls, writes recordings, runs the per-run network gateway, and draws every view: Run History, Syscall Trace, Triage, Network, Memory, Disks, Core dumps, Static Analysis and the Debugger. Only one VM runs at a time.

Everything a run produced is saved as it arrives, so a run can be reopened from Run History long after the VM is stopped or deleted. A run folder holds the raw telemetry in order, the run’s summary and health, its terminal output, and an object store (objects/): captured buffers packed into segment files with an index, which Triage, File Reconstruction and the Syscall Trace read back byte for byte. Network captures, memory dumps, process cores and disk dumps sit beside the runs, and a recording quota (4 GiB per boot by default) bounds what one boot may save.

From 0.8.0 New in 0.8.0 a boot’s recording also keeps the earlier bytes of the files its runs changed, copied from the disk the boot started from, and the sample library keeps an encrypted archive of every sample. See Data folders and run layout and Live versus saved recordings.

Host tools that read hostile data run in a bubblewrap sandbox:

Component Where it runs Handles
Static analysis: built-in ELF, PE and Mach-O parsing and strings, capa, FLOSS, UPX, Detect It Easy, Ghidra bubblewrap The specimen
Properties (hashes, ssdeep, TLSH, Magika) and detection (YARA, Sigma, Suricata) bubblewrap Specimens, recordings, captures
TShark: packet list, display filters, network triage bubblewrap and prlimit, as user 65534 Packet captures
Disk browser, and qemu-img for imported qcow2 images bubblewrap and prlimit Disk images
MemNixFS bubblewrap Memory dumps
Volatility 3 and its community plugins Your user, no sandbox Memory dumps
gdb console (GEF or pwndbg) Your user, no bubblewrap; cleared environment, private home The debugged guest process
Open in Wireshark Your user, no sandbox The capture

The sandbox unshares every namespace (network included), drops all capabilities and clears the environment. Inside are only /usr, a few read-only /etc entries, a private /etc/passwd, a minimal /dev, /proc and a size-limited /tmp; your home, the data directory, /run, X11, D-Bus and /dev/shm are not visible. Each tool gets its input read-only and one folder to write to, with address-space, open-file and time limits. If bubblewrap cannot start, the analysis fails with that reason instead of running unsandboxed. See the security model.

Packets are captured outside the guest, on the VM’s network interface, so the sample cannot hide them from the capture. Each VM has one network mode:

Mode What the guest gets
Disconnected (default) No network card, nothing to capture
Isolated A card and an address; QEMU forwards nothing
Routed A card wired to Antinomie’s own gateway in the host app; each run picks a route
NAT (Internet) QEMU user-mode networking without restriction; the least isolated mode

On a Routed VM the run’s route is None (drop), InetSim, Internet, Tor, VPN (WireGuard) or SOCKS5 proxy, all without root, firewall rules or a VPN client on the host. A route that cannot start stops the run; Antinomie never falls back to another route. With a Cloud VPS profile New in 0.8.0 the gateway also answers the provider’s metadata address itself. See Network routes and Network isolation.

  • No agent or monitor process. The recorder is part of the kernel; nothing is started in the guest to watch the program. On bases built before 0.7.0 only, a temporary collector is uploaded to measure guest RAM and disk.
  • No ptrace, for tracing or for the debugger.
  • No shared host folder. Files reach the guest only through Upload file or the Upload source of Run program, and an upload never runs the file.
  • No analysis tool. Static analysis, rule scans, packet parsing and disk reading all happen on the host, on the saved evidence.