DEV Community

Cover image for ARET translates legacy Windows x86 binaries to native Linux executables
Aciiderixx
Aciiderixx

Posted on

ARET translates legacy Windows x86 binaries to native Linux executables

I've been working on something that still sounds impossible to me when I describe it:

You give it a Windows .exe. It hands you back a native Linux ELF that runs directly on your CPU — no Wine, no QEMU, no virtualization at runtime.

It's called ARET — Automatic Reverse Engineering Toolkit, and it's an open-source project written in Rust.

What it does

Most tools that "run a Windows program on Linux" either emulate the CPU (QEMU, Box86) or keep a compatibility runtime in the loop (Wine). ARET takes a radically different approach:

  1. Translates the machine code to C via a typed SSA IR.
  2. Re-implements the OS calls in a native shim layer.
  3. Recompiles the result into an ordinary native binary.

The output is a normal ELF — or a .wasm module, derived from the exact same lift.

Architecture overview

Here is how the pipeline flows from a compiled binary to a native executable:

       Windows PE (.exe)
              │
              ▼
     x86 decoder + lifter
              │
              ▼
     Typed SSA machine IR
              │
    ┌─────────┴─────────┐
    ▼                   ▼
 C backend         WASM backend
    │                   │
    ▼                   ▼
 ELF binary        WebAssembly

              +
              │
 Native Win32/CRT HLE layer
Enter fullscreen mode Exit fullscreen mode

Clarifying the High-Level Emulation (HLE)

When I say ARET uses an HLE layer, it does not emulate the Windows kernel or the CPU. Instead, it provides native C implementations of the subset of Win32/CRT APIs required by the lifted programs (like kernel32, msvcrt, user32). These shims are statically linked into the final executable.

  • Files: Windows to POSIX path translation.
  • Threads: CreateThread is implemented using cooperative fibers (ucontext), ensuring deterministic round-robin scheduling.
  • GUI (WIP): Uses SDL2 and FreeType under the hood to render native controls.

The Shared-Stack Model

This is probably the most crucial design decision in ARET.

Most binary translators try to recover high-level function signatures before translation. ARET deliberately avoids making this assumption. The lifted machine state remains explicit.

Lifted functions receive the machine stack pointer (esp) by value. The machine stack is a single shared region, and ebp is threaded as an extra callee-saved parameter. This allows arguments to safely cross function calls whether they were passed on the stack (cdecl/stdcall) or in registers (regparm/fastcall), without ever needing to reconstruct or guess the high-level C signatures.

This design trades some traditional compiler assumptions for a closer representation of the original machine semantics. It also keeps the model thread-safe, making the cooperative-fiber threading model consistent with the lifted execution model.

Design principle: Correct or Loud Abort

This is the rule I am most proud of.

ARET never emits a result it cannot justify. Every mechanism is either verified against an independent reference (Unicorn for CPU instructions, Wine for OS APIs, Z3 for SMT rewrites), or it aborts with a named message (aret_unmodelled()) at the point where it would otherwise have to guess.

If an x87 floating-point operation is too complex, or a specific API isn't mapped, the program halts loudly with a diagnostic. The goal is to avoid silent corruption by failing explicitly when behavior cannot be modeled. You always know exactly where the boundary is.

One IR, Multiple Uses

ARET is not only a binary translator. Because it builds a clean, unified SSA IR, the same recovered representation powers:

  • Transpilation to C/LLVM/WASM
  • Decompilation to readable pseudo-C (if/while, with goto fallback)
  • CFG analysis
  • Wall detection (statically finding unmodelled instructions and missing imports before running)

Function and CFG recovery works on stripped binaries using prologue scanning, address-taken analysis, jump/pointer tables, and FLIRT signatures — scaling to large binaries (a 27 MB game → ~43k functions recovered).

# Transpile to ELF
aret program.exe --mode transpile --out-dir out/ --run

# Retarget to WebAssembly
aret program.exe --mode transpile --target wasm --out-dir out/ --run

# Decompile to pseudo-C
aret program.exe                 # structured
aret program.exe --flat          # goto form

# Static analysis (wall detection)
aret program.exe --mode walls
Enter fullscreen mode Exit fullscreen mode

Show, don't tell

To continuously validate correctness, ARET relies on differential testing. The repository contains a 21-binary regression gauntlet. Each fixture is checked against Wine, used as an independent behavioral reference.

Here are some real, third-party compiled binaries transpiled to native Linux ELFs and verified:

Binary Toolchain Verified Features
Lua 5.4.7 MinGW Interpreter, coroutines, closures, metatables, GC stress tests.
sqlite3.exe MSVC Full SQL engine (CRUD, JOIN, CTE, window functions, JSON, triggers).
NASM 2.16.01 MSVC The generated output files (-f elf/win32/bin/obj) match the Wine execution output byte-for-byte.
busybox-w32 MinGW grep, sed, awk, sort, cksum, etc.
strings.exe MSVC (Static C++) Sysinternals tool. Output text matches Wine byte-for-byte.

Try it, Break it, Contribute

If you work on binary analysis, compilers, reverse engineering, or if you just think systems programming is fun, I'd love to hear from you.

git clone https://github.com/aciderix/Automatic-reverse-engineering-toolkit.git
cd Automatic-reverse-engineering-toolkit
cargo build --release
./target/release/aret program.exe --mode transpile --run
Enter fullscreen mode Exit fullscreen mode

🔗 Repository: aciderix/Automatic-reverse-engineering-toolkit

Issues, PRs, and brutal feedback are all welcome.

Current Limitations

ARET is not a universal Windows replacement yet. Paradoxically, admitting this is the best way to explain what it actually does. Current gaps include:

  • x86 32-bit only: 64-bit support is on the roadmap but not implemented yet.
  • Incomplete GUI stack: GDI, dialogs, and controls are heavily being worked on (native controls actually paint on screen via SDL2), but complex UI applications will likely hit missing API stubs.
  • No DirectX support: Games are currently out of reach (the plan is to eventually route D3D to DXVK/Vulkan).
  • No kernel drivers: Ring 0 emulation is completely out of scope.
  • Undocumented internals: Programs relying heavily on undocumented Windows internals or aggressive obfuscation/packers will hit the "loud abort" wall.

Note: This is a solo open-source project. I used AI assistants as development tools, but all architecture, implementation choices, and verification strategy are part of the project itself. If you find it interesting, a ⭐ on the repo means a lot.

Top comments (0)