Self-hosted service
GrammaTech/ddisasm avatar
GrammaTech/ddisasm

ddisasm turns ELF and PE binaries into GTIRB through datalog rules

A fast and accurate disassembler

884 stars72 forksC++AGPL-3.0

At a glance

What is it?
GrammaTech's ddisasm is a C++ disassembler whose disassembly rules are written in the souffle datalog language, so code location, symbolization and function boundaries come out of a fact analysis rather than a single decode pass. The output is GTIRB, and the toolchain around it, not ddisasm alone, is what turns a binary back into something you can modify.
Who is it for?
ddisasm fits reverse engineers and researchers who need a decoded binary they can rewrite programmatically, and who can work inside a Docker image built on Ubuntu 20.04 with Souffle and LIEF. It is the wrong tool for a quick single function dump, for a binary format outside ELF and PE, or for an instruction set outside x86, ARM and MIPS as listed.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • GrammaTech/ddisasm884 ★ · +6
Star history
Repository2026-10-212026-11-07
GrammaTech/ddisasm878884

6 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Disassembly written as datalog rules, not as a decode loop

The README describes ddisasm as a "fast disassembler which is accurate enough for the resulting assembly code to be reassembled", and then gives away the unusual part in the next sentence. The disassembly rules and heuristics are not C++ code. They are compiled from the datalog declarative logic programming language, souffle, and the project itself is written in C++.

That choice changes what the tool is. A conventional decoder walks bytes and asks what instruction could start here, producing a linear listing in which data bytes and code bytes are hard to tell apart until you look closely. A datalog approach separates the two layers. The program first produces a superset of plausible facts, then a set of rules reasons over those facts to decide which addresses hold code, which hold data, where functions begin and end, and which bytes can be tied to a symbol.

The repository's own publications point the same way. The cited USENIX Security paper is titled "Datalog Disassembly", and a 2025 IEEE Symposium on Security and Privacy paper is titled "Disassembly as Weighted Interval Scheduling with Learned Weights", which describes the current approach as a scheduling problem with learned weights rather than a fixed heuristic. Two papers, one mechanism.

The practical upshot is that ddisasm's output is designed to be trusted as input to a reassembly step, which is why the accuracy claim is framed in terms of reassembly rather than listing quality.

ELF and PE in, code and function facts, GTIRB out

The pipeline is described in three stages in the README. First the program parses ELF or PE file information and decodes a superset of possible instructions to create an initial set of datalog facts. Second, those facts are analyzed to identify code location, symbolization and function boundaries, and the result is a refined set of facts. Third, the refined facts are translated into the GTIRB intermediate representation for binary analysis and reverse engineering.

So the unit of output is not a listing. It is GTIRB, an intermediate representation designed to hold a binary in a form that tools can rewrite. The README makes the intent explicit by saying that once you have the representation you can make programmatic changes using GTIRB or gtirb-rewriting, and that the GTIRB pretty printer may then be used to produce reassemblable assembly code.

The GTIRB pretty printer is a separate project, gtirb-pprinter, and the README notes it is included in the Docker image. Its internal behaviour is described as generating an assembly file and invoking the compiler or assembler, for example gcc, to produce a new binary, handling compilation options, library dependencies and version linker scripts along the way.

This split has a consequence for evaluation. ddisasm produces the analysis, and gtirb-pprinter performs the write-back. If your question is whether ddisasm can edit a binary, the accurate answer is that ddisasm produces the editable representation, and the edit and print steps belong to the surrounding toolchain.

Running the example in Docker, from pull to GTIRB file

The getting started path uses a prebuilt image, so no local build is required to try the tool. Pull the image first.

bash
docker pull grammatech/ddisasm:latest

Then start a container with the repository's examples directory mounted at `/examples`, so the bundled sample programs are visible from inside.

bash
docker run -v $PWD/examples:/examples -it grammatech/ddisasm:latest

Inside the container, the README compiles one of the included examples. The image ships without a compiler, so gcc is installed first, then the sample is built from its C source.

bash
apt update && apt install gcc -y
cd /examples/ex1
gcc ex.c -o ex

With a binary in hand, the disassembly step is a single command that writes the GTIRB representation to a file.

bash
ddisasm ex --ir ex.gtirb

What you should see after this is the file `ex.gtirb` in the current directory. The examples directory in the repository is extensive, and the entries visible in the tree include `ex1/`, `ex_adder/`, `ex_call_array/`, `ex_call_once/`, `ex_confusing_data/`, `ex_copy_relo/`, `ex_data_limit/`, `ex_dyn_library/`, `ex_dyn_library2/`, `ex_dynamic_initfini/`, `ex_dynamic_tls/`, `ex_dynamic_tls2/`, `ex_emit_relocs/`, `ex_exceptions1/` through `ex_exceptions4/`, `ex_false_pointer_array/` and `ex_float/`, along with `arm64/`, `arm64_asm_examples/`, `arm_asm_examples/`, `asm_examples/` and a top-level `Makefile`.

Those names are a map of the hard cases. Data mixed into code, relocation handling, dynamic libraries, thread local storage, exceptions and floating point each get their own sample, which tells you the test surface is aimed at exactly the situations where a naive decoder goes wrong.

Two ways back to a binary, one automated and one by hand

From the GTIRB file there are two documented routes, and they suit different working styles.

The first is fully automated. The pretty printer emits a new binary directly, and it takes care of the compiler options, library dependencies and linker script details that a hand written reassembly would miss.

bash
gtirb-pprinter ex.gtirb -b ex_rewritten

The second produces an assembly listing instead, which the README says can be modified manually and then recompiled.

bash
gtirb-pprinter ex.gtirb --asm ex.s
bash
gcc -nostartfiles ex.s -o ex_rewritten

The `-nostartfiles` flag in that last command is worth noticing, since it tells you the emitted assembly does not carry the startup files and therefore needs the flag to link into a working program. A manual listing gives you the point of the toolchain: you can read and edit the assembly before it goes back to a binary, and the automated path removes the need to understand the linkage details at all.

The README does not document a command that takes a binary and prints a human readable listing, which is the usual first request from someone new to a disassembler. The output format here is deliberately intermediate, and viewing it means either running the pretty printer or working with the GTIRB representation directly.

The supported binary formats and ISAs form a hard ceiling

The README states the coverage plainly. Binary formats are ELF (Linux) and PE (Windows). Instruction set architectures are x86_32, x86_64, ARM32, ARM64 and MIPS32.

Read those lists as exclusion rules as much as inclusion rules. A Mach-O binary is out, because the format list names only ELF and PE, and no loader or translator sits between the tool and the file. An AArch64 binary is in on the strength of ARM64 being listed, and there is separate credit in the README to the Programming Language Group at The University of Sydney for initial support for ARM64. A MIPS64 target is not covered, since only MIPS32 appears. A Java class file or a .NET assembly is not a binary format this tool reads.

There is a related constraint on where the tool can run, and it comes from the container rather than the source. The Dockerfile is built on `ubuntu:20.04`, installs Souffle from the `2.4` tag, pins LIEF to version 0.16.6, and requires CMake 3.24.0, which the file installs by hand with the comment that it is needed by LIEF 0.16.6.

So even the supported cases come with a floor. You need a Linux container environment, and a self-hosted build means reproducing a dependency stack that has been pinned rather than kept current. ARM64 support exists but was contributed rather than built in from the start.

Source activity continues while tagged releases stopped in 2023

The repository is not archived, and the last push was on 2026-10-13. The recorded releases tell a different story about how changes ship. The three most recent tags are v1.8.0 on 2023-12-14, v1.7.0 on 2023-09-14 and v1.6.0 on 2023-08-13.

So the development branch moved in 2026 while the newest published tag is from December 2023. The commit date is a fact about the source, and the tag date is a fact about packaged releases, and the two are not the same thing. Anyone who pins to a release gets 2023 code, while anyone who tracks the main branch gets whatever the untagged work produces. The README points at the `:latest` Docker tag rather than a versioned one, which follows the branch rather than the release history.

For build hygiene this is the detail to plan around. The repository includes a `CHANGELOG.md`, a `conanfile.py`, `CMakeLists.txt`, `cpack-config.cmake` and `version.txt`, so the packaging machinery for a release exists in the tree, and a `Dockerfile` that pins third-party versions explicitly. A build from source is a cmake and conan exercise on top of Souffle and LIEF, which is heavier than the Docker path for anyone who only wants to try the tool.

Where ddisasm differs from a plain listing tool

The realistic alternative for most people is a conventional disassembler that emits a listing from a chosen entry point. The difference in approach is not cosmetic. A listing tool answers what bytes could be an instruction, and it leaves the separation of code from data, the placement of function boundaries and the attribution of bytes to symbols as your problem. ddisasm answers a different set of questions, because the datalog analysis is designed to produce code locations, symbolization and function boundaries, and because the accuracy criterion in the README is that the result can be reassembled.

That reordering also changes the cost profile. A listing is cheap and immediate. ddisasm needs Souffle as a build dependency, it needs the analysis to finish, and it hands you an intermediate representation that only becomes a listing once another tool runs. For a quick look at one function, the traditional route stays ahead.

The second alternative is a different way of using the same output. Rather than letting gtirb-pprinter regenerate the binary, you can ask it for the assembly listing, edit that text yourself, and recompile it, as the README shows with the `--asm` output and the `gcc -nostartfiles` step. When the change you want is small and legible at the assembly level, that route keeps a human in the loop where the automated path does not.

Editorial conclusion

ddisasm fits reverse engineers and researchers who need a decoded binary they can rewrite programmatically, and who can work inside a Docker image built on Ubuntu 20.04 with Souffle and LIEF. It is the wrong tool for a quick single function dump, for a binary format outside ELF and PE, or for an instruction set outside x86, ARM and MIPS as listed. Before you commit, check that the ISA and container format you need are supported, and confirm that the analysis results meet your accuracy bar on your own sample, since the repository publishes no accuracy figures.

Frequently asked questions

How do I run ddisasm on a binary?

Use the Docker image and pass the input binary with the `--ir` option, for example `ddisasm ex --ir ex.gtirb`, which writes the GTIRB representation of the binary to a file. The README also documents the option as part of the usage of `ddisasm` in the getting started example.

What binary formats and architectures does ddisasm support?

The README lists ELF for Linux and PE for Windows as the binary formats, and x86_32, x86_64, ARM32, ARM64 and MIPS32 as the supported instruction set architectures. No other formats or ISAs are named.

Why does ddisasm need Souffle?

The README states that ddisasm is implemented using the datalog declarative logic programming language souffle to compile disassembly rules and heuristics. The Dockerfile builds Souffle from the `2.4` tag, so it is a build dependency rather than an optional extra.

How do I turn a ddisasm result back into a working binary?

Use the GTIRB pretty printer, which is included in the Docker image. The README shows `gtirb-pprinter ex.gtirb -b ex_rewritten` for a rebuilt binary, and `gtirb-pprinter ex.gtirb --asm ex.s` to get an assembly listing that can be edited and recompiled with `gcc -nostartfiles ex.s -o ex_rewritten`.

Is ddisasm still maintained?

The repository is not archived and the last push was on 2026-10-13. The most recent tagged release is v1.8.0 from 2023-12-14, so source changes and tagged releases are not moving at the same rate.

What licence does ddisasm use, and does that affect use in a commercial tool?

The repository is licensed under AGPL-3.0, with the licence text in `LICENSE.txt`. The README does not discuss licensing implications, so any use in a closed or distributed product needs its own review of the AGPL-3.0 terms.

Official sources

  1. GrammaTech/ddisasm on GitHub
  2. License: AGPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/grammatech-ddisasm.svg)](https://hysenlabs.com/projects/grammatech-ddisasm)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/grammatech-ddisasm.svg)](https://hysenlabs.com/projects/grammatech-ddisasm)

Was this useful?