Running x86-64 Linux on Apple Silicon
- Running x86-64 Linux on Apple Silicon
Why this is a problem at all
Every Mac sold since 2020 is ARM64. Most of the software this course depends on is not.
REMnux states it plainly: it is “currently based on an x86/amd64 version of Ubuntu, and won’t run on ARM processors such as Apple’s M-series chips.” IDA’s Linux builds are x86-64. So is essentially every malware sample you will ever be handed, because essentially every victim machine is x86-64. If your laptop is an M-series Mac, the architecture you need to analyze is not the architecture you own.
The general “VM setup on macOS” pages elsewhere in this repo solve a different problem. They install an architecture-matched guest: an arm64 Kali or Ubuntu on an arm64 Mac, which is fast and easy precisely because the CPU is running its own instruction set. This page is about the case where that is not an option, because the thing you need to run only exists as x86-64.
Three mechanisms can get you there, and they are not interchangeable.
| Virtualization | Emulation | Translation | |
|---|---|---|---|
| Guest ISA vs host | Same | Different | Different |
| What runs the code | The real CPU | A software CPU model | A recompiler, on the real CPU |
| Speed | Near native | 3–20x slower | Near native |
| Kernel architecture | Host’s | x86-64 | Host’s (ARM64) |
| Apple Silicon example | arm64 Linux under vz |
QEMU TCG x86-64 guest | Rosetta in an arm64 guest |
The row that decides everything is kernel architecture. Emulation gives you a genuine x86-64 kernel, so everything x86-64 works, slowly. Translation gives you x86-64 user-space only on an ARM64 kernel, quickly. Which one you need depends entirely on whether the thing you are running cares about the kernel.
Measured on an M1 Max
Abstract claims about emulation being “slow” are not useful for planning. Here are real numbers, taken on an Apple M1 Max (8 performance + 2 efficiency cores, 64 GB, macOS 27.0) under Lima 2.1.2 with QEMU 11.0.1. Both guests were given 4 CPUs and 4 GiB.
The same statically linked x86-64 binary was run in both, so the comparison is like-for-like:
| Workload | Rosetta (arm64 guest) | QEMU TCG (x86-64 guest) | Ratio |
|---|---|---|---|
| Boot to usable shell | 48 s | 89 s | 1.9x |
| Static C integer loop | 0.48 s | 1.12 s | 2.3x |
| CPython 3.12 integer loop | 1.75 s | 17.16 s | 9.8x |
The spread between those last two rows is the important part, and it is why a single “emulation is Nx slower” number is misleading.
QEMU’s TCG translates blocks of guest instructions and caches them. A tight arithmetic loop is translated once and then re-run from cache, so it costs only about 2x. CPython is the opposite: a branchy bytecode interpreter with an unpredictable indirect jump at the top of every opcode, which defeats block caching and pays the translation penalty continuously. Ten times slower is the realistic figure for the interpreted, I/O- and syscall-heavy tooling that makes up most of a malware analysis workflow.
Plan accordingly: expect roughly an order of magnitude on anything that feels like real work, and treat the tight-loop number as the best case you will rarely see.
Option 1: A real x86-64 guest, emulated
This is the option that always works, because the guest is genuinely x86-64 all the way down to the kernel.
Lima is the least painful way to get one. It is a
command-line VM manager built for exactly this: cloud images, automatic file
sharing, and an --arch flag that switches it to emulation without further
ceremony.
$ brew install lima
$ limactl create --name=remnux-x86 --arch=x86_64 --cpus=4 --memory=8 \
--disk=100 template://ubuntu-24.04
$ limactl start remnux-x86
$ limactl shell remnux-x86
Because --arch does not match the host, Lima automatically selects QEMU with
TCG rather than Apple’s vz backend — Apple’s Virtualization.framework can only
run guests of the host architecture, so it is simply not a candidate here.
Confirm what you got:
$ limactl shell remnux-x86 -- bash -c 'uname -m; grep -m1 "model name" /proc/cpuinfo'
x86_64
model name : QEMU TCG CPU version 2.5+
That model name is worth remembering for two reasons. It confirms you are on
software emulation rather than hardware virtualization — and, as covered in
Anti-Analysis Techniques, it is an enormous tell. Any
sample that reads /proc/cpuinfo or issues CPUID sees a CPU model that has
never existed in a physical machine. You cannot hide this. Emulation is fine for
running x86-64 tools; it is a poor place to detonate an evasive x86-64
sample.
Budget disk generously. On the machine above, a 100 GiB-provisioned Ubuntu instance with a few images pulled occupied 32 GiB of real disk. Lima images are sparse, so they grow as you use them and do not shrink when you delete files.
When you actually need this option
- You need a genuine x86-64 kernel: loading x86 kernel modules, kernel exploitation, driver work, or anything in Advanced Dynamic Analysis touching eBPF against x86 structures.
- You need to run 32-bit x86 binaries, including most Windows malware under Wine. See “The 32-bit wall” below — this is the common case, not an edge case.
- You want the real REMnux virtual appliance rather than its container.
- A tool refuses to run under translation.
Option 2: An ARM64 guest with Rosetta
Apple ships Rosetta not just for Intel Mac apps but for Linux guests: since
macOS 13, Virtualization.framework can expose Rosetta inside an ARM64 Linux VM,
where it registers as a binfmt_misc handler and transparently translates
x86-64 ELF binaries. The kernel stays ARM64; only user-space is translated.
Lima exposes this with two flags:
$ limactl create --name=rosetta --arch=aarch64 --vm-type=vz --rosetta \
--cpus=4 --memory=4 template://ubuntu-24.04
$ limactl start rosetta
Verify the handler is registered:
$ limactl shell rosetta -- cat /proc/sys/fs/binfmt_misc/rosetta
enabled
interpreter /mnt/lima-rosetta/rosetta
flags: OCF
offset 0
magic 7f454c4602010100000000000000000002003e00
mask fffffffffffefe00fffffffffffffffffeffffff
The magic matches the ELF header for a 64-bit little-endian EM_X86_64
executable. From here, an x86-64 binary just runs:
$ limactl shell rosetta -- bash -c 'uname -m; file ./bench.x86_64 | cut -c1-58; ./bench.x86_64'
aarch64
./bench.x86_64: ELF 64-bit LSB executable, x86-64, statically
0.48 s (checksum 16370625841040242842)
Note that uname -m says aarch64. The kernel is ARM. Only the binary was
translated.
Running REMnux this way
This is the payoff, and it works. REMnux publishes a container image, and
every published tag is amd64-only
— there is no arm64 build — so on Apple Silicon it needs translation. Lima
instances ship containerd and nerdctl:
$ limactl shell rosetta -- sudo systemctl start containerd
$ limactl shell rosetta -- sudo nerdctl run --rm --platform=amd64 \
remnux/remnux-distro:noble bash -c 'uname -m; head -1 /etc/os-release; which floss capa'
x86_64
PRETTY_NAME="Ubuntu 24.04.4 LTS"
/usr/bin/floss
/usr/local/bin/capa
The image is about 4.8 GiB and took roughly three minutes to pull. Once it is there, the tools work normally. Running it against the benign specimen built on the Windows malware page:
$ sudo nerdctl run --rm --platform=amd64 -v $PWD/specimen.exe:/s/specimen.exe \
remnux/remnux-distro:noble bash -c 'floss -q /s/specimen.exe | head -4; capa /s/specimen.exe'
SecurityUpdate
C:\Users\Public\dropped.bin
updates.example-c2.test
...
┌───────────┬──────────────────────────────────────────────────────────────────┐
│ sha256 │ beb984b4336000f385e267701bbb632b01ad9598fa7c21e97e6d01707fc93141 │
│ format │ pe │
│ arch │ amd64 │
└───────────┴──────────────────────────────────────────────────────────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ATT&CK Tactic ┃ ATT&CK Technique ┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ PERSISTENCE │ Boot or Logon Autostart Execution::Registry Run Keys │
│ │ / Startup Folder [T1547.001] │
└──────────────────────┴───────────────────────────────────────────────────────┘
x86-64 FLOSS and x86-64 capa, analyzing a Windows PE, on an ARM laptop, at roughly native speed. Note in passing that capa found the Run-key persistence statically — the same behavior the emulator missed dynamically on the Windows malware page. Static and dynamic analysis cover for each other; neither is sufficient.
The 32-bit wall
This is the limit that matters most for this course, and it is easy to miss until it bites.
Rosetta translates x86-64. It does not translate 32-bit x86. A 64-bit PE under Wine works fine; a 32-bit one does not, because the 32-bit Wine loader is itself an i386 ELF binary and nothing in the guest can execute it:
# In an amd64 container, under Rosetta, after apt-get install wine wine32:i386
$ wine specimen32.exe
/usr/bin/wine: 40: exec: /usr/lib/wine/wine: Exec format error
Note that wine32:i386 installs perfectly happily — apt has no idea
anything is wrong. The failure only appears at execution.
The same 32-bit binary in the emulated x86-64 guest from Option 1 runs normally, because there the kernel is genuinely x86-64 and handles i386 user-space the way any x86-64 Linux does:
# Same container, same commands, inside the QEMU TCG x86-64 guest
$ wine specimen32.exe
wine: created the configuration directory '/tmp/wp32'
$ ls -l /tmp/wp32/drive_c/users/Public/dropped.bin
-rw-r--r-- 1 root root 24 Sep 15 21:33 /tmp/wp32/drive_c/users/Public/dropped.bin
⚠️ Most Windows malware is 32-bit. As Working with Wine notes, the
i386architecture is required precisely because that is what samples target. If your work involves running 32-bit PEs — which is most of the Wine workflow on the Windows malware page — Rosetta will not do it, and you need Option 1. Use Rosetta for 64-bit binaries and for analysis tools; use the emulated guest when you actually need to execute 32-bit code.
What else Rosetta cannot do
The kernel is still ARM64, and that has consequences you must not forget:
- No x86-64 kernel modules or drivers. There is no x86 kernel to load them into.
- Syscalls are ARM64 syscalls. Rosetta maps them, but anything inspecting the syscall interface directly, or using unusual/obsolete syscalls, can diverge.
uname -mlies by omission. Inside an amd64 container it reportsx86_64because of the container’s personality setting, while/proc/cpuinfoand the running kernel remain ARM. Tools that check one and assume the other get confused.- Instruction set coverage is not total. Rosetta targets the common x86-64 baseline; exotic or very new vector extensions may fault.
- It is a terrible detonation chamber. For the same reason emulation is: an evasive sample inspecting its environment finds an incoherent machine. Use it to run analysis tools, not to run malware.
Option 3: UTM, when you want a desktop
UTM is the GUI option the other course pages already use, and it wraps QEMU, so it can do full-system x86-64 emulation as well as native ARM virtualization. Choose “Emulate” rather than “Virtualize” and select x86-64.
Use UTM when you want the actual REMnux virtual appliance with its desktop, rather than a headless shell — the graphical tools, the file manager, the familiar environment from lab instructions. Expect it to be slow: everything in the benchmark table applies, plus a software-rendered desktop on top.
⚠️
utmctldoes not work over SSH. UTM’s command-line tool requires a logged-in GUI session and fails withOSStatus error -1743otherwise. If you plan to drive VMs on a Mac you reach remotely, use Lima, which is command-line-native and has no such restriction.
Option 4: Do not emulate at all
The fastest x86-64 machine available to you is one that is actually x86-64. The course already provides this, and it is covered in Malware Analysis Lab Setup:
- CAT RemoteLab — MCECS-provided remote x86 Windows and Linux machines, free, and the officially supported route when you need Windows or IDA.
A cheap cloud instance works too. For anything that is CPU-bound, or that needs a real x86-64 kernel with hardware virtualization underneath it, a remote machine will beat local emulation by a wide margin and cost you less time than troubleshooting will.
⚠️ Do not detonate malware on RemoteLab or on a cloud VM you do not own the network for. Those machines are shared university or provider infrastructure. Running samples on them is an acceptable-use violation and potentially an incident. Use them for tools, and detonate only inside your own isolated lab.
Choosing
- Is the binary 32-bit? Option 1, emulated guest. Rosetta cannot run i386 code at all, and most Windows malware is i386.
- Do you need an x86-64 kernel — kernel modules, drivers,
vmlinuxstructures, or a faithful x86 system? Option 1 again. Accept the slowness. - Do you just need to run 64-bit x86-64 analysis tools — capa, FLOSS, IDA, the REMnux toolchain? Option 2, Rosetta. Roughly ten times faster, and this is most of what you do day to day.
- Do you want the REMnux desktop as the lab instructions depict it? Option 3, UTM.
- Are you going to actually execute a sample? Option 4, or your own isolated x86-64 hardware. Neither emulation nor translation is a credible place to detonate anything that looks at its environment.
A reasonable default for a student on an M-series Mac: Rosetta for 64-bit tooling, an emulated guest for 32-bit work, RemoteLab for detonation.
Traps specific to Apple Silicon
Nested virtualization needs M3 or later. Running a hypervisor inside your VM — KVM in the guest, or the libvirt workflow from the Windows malware page — requires macOS 15 or later and an M3 or newer chip. On M1 and M2 it is simply unavailable. Check before planning a lab around it.
Memory is shared, and VMs are greedy. Apple Silicon uses unified memory. A VM’s RAM comes out of the same pool as the GPU and the rest of macOS, so over-provisioning degrades the whole machine rather than just the VM. On a 16 GB Mac, 4 GiB to a guest is sensible and 8 GiB is aggressive.
Disk fills quietly. Sparse images grow and never shrink. Between an emulated
guest, a Rosetta guest, and a 4.8 GiB REMnux image, the measurements on this
page consumed over 50 GiB. Check du -sh ~/.lima/*/ occasionally, and
limactl delete instances you are done with.
Rosetta must be installed. It is not present on a fresh machine until
something needs it. softwareupdate --install-rosetta installs it explicitly.
The expiry date on Option 2
⚠️ Rosetta 2 is being phased out. Apple announced at WWDC 2025 that Rosetta is fully supported through macOS 27 and will be largely discontinued beginning with macOS 28, expected in late 2027, retaining only a subset aimed at older unmaintained games. macOS 26.4 already warns users when launching an app that requires it.
That announcement is about translating Intel macOS applications. Rosetta for Linux VMs is a distinct feature of Virtualization.framework, and Apple has made no separate statement about ending it — but it is the same underlying translator, and the strategic direction is unambiguous.
The practical consequence for you: Option 2 is excellent today and should not be the only path your work depends on in two years. Keep the emulated-guest route (Option 1) working, since QEMU is not going anywhere, and prefer arm64-native tooling where it exists. If you are choosing what to learn rather than what to install this week, learn the Lima and QEMU workflow — it will outlive the shortcut.
Key takeaways
- Apple Silicon can virtualize only ARM64. Running x86-64 means either emulating a whole machine or translating user-space binaries, and those two differ in whether you get an x86-64 kernel.
- Measured on an M1 Max: Rosetta beat QEMU TCG by 2.3x on a tight loop and 9.8x on CPython. Expect roughly an order of magnitude on realistic tooling and plan schedules around it.
- REMnux is amd64-only and publishes no arm64 image, but its amd64 container runs under Rosetta at usable speed — verified with FLOSS and capa against a real PE.
- Rosetta does x86-64 only. 32-bit PEs fail under it with
Exec format erroreven afterwine32:i386installs cleanly, and they run normally in the emulated guest. Since most Windows malware is 32-bit, this decides the option for a lot of the course’s work. - Use translation and emulation to run analysis tools, never to detonate
samples.
QEMU TCG CPU version 2.5+in/proc/cpuinfois an unhideable tell, and a Rosetta guest is an ARM kernel wearing an x86-64 costume. - Nested virtualization requires macOS 15+ and M3 or later. On M1/M2 there is no KVM-in-a-VM.
- Rosetta is scheduled to be largely withdrawn in macOS 28. Do not make it your only route.
References
- REMnux — get the virtual appliance, including the x86/amd64-only statement. https://docs.remnux.org/install-distro/get-virtual-appliance
- REMnux distro container images on Docker Hub (amd64 only). https://hub.docker.com/r/remnux/remnux-distro
- Lima — Linux virtual machines on macOS, including cross-architecture guests. https://lima-vm.io/docs/
- Lima — Rosetta support for x86-64 binaries in ARM64 guests. https://lima-vm.io/docs/config/multi-arch/
- Apple — Virtualization framework, running Intel binaries in Linux VMs with Rosetta. https://developer.apple.com/documentation/virtualization/running_intel_binaries_in_linux_vms_with_rosetta
- Apple — Hypervisor framework. https://developer.apple.com/documentation/hypervisor
- UTM documentation — emulation versus virtualization on Apple Silicon. https://docs.getutm.app/
- UTM — Rosetta support for Linux guests. https://docs.getutm.app/advanced/rosetta/
- QEMU — TCG, the Tiny Code Generator used for cross-architecture emulation. https://www.qemu.org/docs/master/devel/tcg.html
- MacRumors — “Apple to Phase Out Rosetta 2 Starting With macOS 28” (WWDC 2025 announcement). https://www.macrumors.com/2025/06/10/apple-to-phase-out-rosetta-2/
- 9to5Mac — macOS 26.4 begins warning users about Rosetta 2 discontinuation. https://9to5mac.com/2026/02/16/macos-26-4-will-notify-users-of-rosetta-2-discontinuation/
- Parallels KB — using Rosetta for x86-64 containers and binaries in Linux VMs. https://kb.parallels.com/en/129871
- CAT RemoteLab — MCECS remote x86 lab machines. https://cat.pdx.edu/users/facilities/remote-labs/
Related course pages: Malware Analysis Lab Setup · Environment Configuration · REMnux Installation · Dynamic Analysis of Windows Malware from Linux · Anti-Analysis Techniques · Tools
🛠️ Maintenance note: the benchmark table was measured on one specific machine (M1 Max, macOS 27.0, Lima 2.1.2, QEMU 11.0.1) and the absolute numbers will not reproduce elsewhere — the 2x-versus-10x spread between tight-loop and interpreter workloads is the durable finding, not the seconds. Re-measure on whatever hardware the class actually has. The Rosetta deprecation timeline is the fastest-moving item here: verify each term whether macOS 28 shipped, whether Rosetta-for-Linux-VMs survived it, and rewrite Option 2 accordingly — if it is gone, this page becomes a straight Lima/QEMU page plus RemoteLab. REMnux may eventually publish an arm64 image, which would change the recommendation substantially; check the Docker Hub tag architectures rather than assuming. Nested-virtualization support may extend to more chips, and Lima’s CLI flags (
--vm-type,--rosetta) have changed spelling between major versions.