Ghidra's C++ decompiler running in your browser.
Drag in an ELF, Mach-O, PE, or .wasm. Pyre parses the binary, lazy-loads
the SLEIGH spec for its architecture, and decompiles functions on demand.
Navigate via a function list, Cmd-click on call sites in Monaco to follow
calls into new tabs, and read cross-references in the side panel.
Everything runs client-side. Binaries never leave the browser; there is no server, no upload, no telemetry.
Alpha. The core pipeline (binary parse → wasm decompile → Monaco render → navigation) works on x86 (32/64), AArch64, and WebAssembly modules. Other architectures Ghidra supports compile fine but are not bundled in the default spec set yet — see Roadmap.
You need:
- Node 20+
- emsdk
on your
PATH(or sourced viaemsdk_env.sh) - A staged set of SLEIGH specs — pre-built
.slafiles for one or more architectures (see step 2)
# 1. Build the wasm module (~30 s clean, ~5 s incremental)
./decompiler-wasm/build.sh
# → decompiler-wasm/dist/pyre_decompiler.{js,wasm}
# 2. Stage SLEIGH specs from a directory laid out as
# Ghidra/Processors/<arch>/data/languages/...
./specs/stage-specs.sh /path/to/your/Ghidra/source/tree
# → specs/dist/<arch>/data/languages/*.{sla,ldefs,cspec,pspec}
# → specs/dist/manifest.json
# 3. Run the web frontend
cd web
npm install
npm run dev # http://localhost:5173The web/public/decompiler and web/public/specs symlinks point at
the build outputs from steps 1 and 2.
If you'd rather not install emsdk + Node locally:
docker build -t pyre-dev -f docker/Dockerfile .
docker run --rm -it -v "$PWD":/work -w /work -p 5173:5173 pyre-dev
# inside the container:
./decompiler-wasm/build.sh
./specs/stage-specs.sh /path/to/Ghidra
cd web && npm install && npm run dev -- --host 0.0.0.0decompiler-wasm/ C++ → wasm. Vendored Ghidra decompiler tree, multi-region
LoadImage, SleighArchitecture subclass, extern "C" bridge,
emscripten unity build.
specs/ SLEIGH .sla / .ldefs / .cspec staging + manifest pipeline.
web/ React + Vite + TypeScript + Tailwind frontend, Monaco editor.
docker/ Dev image: emsdk + Node + JDK + gradle.
┌──────────────────── browser ────────────────────┐
│ │
│ React UI ──► Zustand store ──► DecompilerClient │
│ │ │
│ postMessage ▼ │
│ ┌─────────────┐ │
│ │ Web Worker │ │
│ │ ┌────────┐ │ │
│ │ │ wasm │ │ │
│ │ │ Ghidra │ │ │
│ │ │ decomp │ │ │
│ │ └────────┘ │ │
│ │ FS lazy- │ │
│ │ mount │ │
│ │ /spec/... │ │
│ └─────────────┘ │
└──────────────────────────────────────────────────┘
Three design choices worth flagging:
- Why a Web Worker (hard requirement). Emscripten's
FS.createLazyFileuses synchronous XHR on first byte access. That API is illegal on the main thread but fine inside a worker. Spec files (~30 MB across all architectures Ghidra supports) are mounted as lazy entries; only the arch the user actually opens is paid for. - Multi-region
LoadImage. Mach-O segments live at non-contiguous virtual memory addresses, and even ELF binaries can have entry points far from the first PT_LOAD page. A flat single-buffer projection silently aliases the wrong bytes. Pyre'sWebLoadImageholds an arbitrary set of regions keyed by VMA and zero-fills any gaps the decompiler asks about. - Lazy libc prototype catalogue. Ghidra's
parse_Cbinds prototypes to symbols by name, soprintf/puts/malloconly render as their named forms in decompiled output if the catalogue is imported after the JS caller has registered the binary's symbols. The bridge defers this import to the firstdecompile()call.
-
sleighcbuild step inspecs/— compile every.slaspecunderGhidra/Processors/so we ship every architecture Ghidra supports out of the box (RISC-V, MIPS, PowerPC, SPARC, ...) - In-browser variable rename — round-trip a name edit through the worker and re-decompile
- Persistent workspace (IndexedDB) so refreshing the page doesn't drop open tabs
- Disassembly side panel (Capstone-wasm next to Monaco)
- WASM module decompilation polish — string / data section parsing, better entry-point detection
MIT — see LICENSE. Vendored Ghidra decompiler sources under
decompiler-wasm/third_party/ retain their original Apache 2.0 license;
see THIRD_PARTY_NOTICES.md for full attribution.