Skip to content

Repository files navigation

License: GPL v2

x64ID — x86 and x86_64 Instruction Decoder

x64ID is a C library for decoding x86 and x86_64 machine code. It reports instruction length and encoding fields, and provides partial mnemonic translation. The repository also includes a command-line disassembler for raw binary files.

You can use x64ID in a disassembler, binary inspector, or instruction rewriter. It does not currently read executable files such as ELF or PE.

Contents

Links to sections in older versions of this README are kept where possible.

Features and scope

  • x86 and x86_64 decoding, including instruction prefixes, ModRM, SIB, displacement, immediates, REX, and VEX fields.
  • Mnemonic and operand formatting for the opcode definitions in src/tools/definitions.txt.
  • A CLI that displays instruction bytes alongside assembly, marks in-file branch targets, and can export a plain-text listing.
  • Unsupported or unmapped encodings are displayed as db bytes; decoding continues with the following instruction when its length can be determined.

Mnemonic coverage is incomplete. VEX/AVX support is partial; EVEX/AVX-512, XOP, and 3DNow! are not supported. The test suite covers declared definitions, not the entire x86 instruction set.

ELF and PE support and wider instruction coverage are planned. For now, provide the decoder with the instruction bytes to read.

Build

You need CMake 3.16 or newer, a C11 compiler, Python 3, and clang-format. CMake finds clang-format automatically, including versioned commands such as clang-format-22. The build formats the C files before compiling them.

From the repository root:

make                 # Configure in build/ and compile the project
make test            # Build the disassembler and run the automated suite
make help            # List targets, executables, and sample files
make clean           # Remove build output and generated test binaries

Set BUILD_DIR to use a different build directory:

make test BUILD_DIR=out

Command-line disassembler

The CLI reads a raw byte stream and starts decoding at file offset zero. It does not locate code sections in ELF or PE files. If you pass an ELF or PE file, the program will treat its headers and other contents as instructions. Extract the code bytes first.

Build and disassemble a sample:

make
./build/x64id_disassembler --arch x86_64 test/bubble_sort.bin

The screenshot shows the actual output, including the machine-code column, colored assembly, and branch arrows:

Disassembler output for the bubble sort sample

--arch accepts x86, x86_64, 1, or 2. If omitted, the program asks interactively. The input path may also be omitted to enter it interactively. Use --help for the full option list.

Export the listing to a text file while also printing it to the terminal:

./build/x64id_disassembler --arch x86_64 \
  --output disassembly.txt test/bubble_sort.bin

The text export contains the byte and assembly columns without terminal color codes. Branch arrows are shown in the left gutter for targets inside the input buffer.

Color configuration

By default, settings are read from ./disassembler.ini. Select another file with --config FILE or the X64ID_DISASSEMBLER_CONFIG environment variable. The enabled setting accepts auto (color only on a terminal), always, or never. The remaining keys select colors for instruction and operand parts:

[colors]
enabled = auto
mnemonic = cyan
jump = yellow
call = magenta
return = green
data = bright_black
register = bright_cyan
memory_size = bright_blue
brackets = bright_yellow

Color names are black, red, green, yellow, blue, magenta, cyan, white, their bright_ variants, default, and none.

Library API

Include src/x64id.h. Set the architecture before decoding; the decoder keeps this setting as global state until it is changed again.

Decode an instruction

#include <stdio.h>

#include "x64id.h"

char code[] = { (char)0x48, (char)0x89, (char)0xD8 }; /* mov rax, rbx */
x64id_set_arch(X64);

for (int offset = 0; offset < (int)sizeof(code);) {
    struct instruction instr;
    int length = x64id_decode(&instr, code, offset);
    if (length <= 0)
        break;

    for (int i = 0; i < length; i++)
        printf("%02X ", (unsigned)instr.instr[i]);
    printf(" %s\n", instr.disasm);
    offset += length;
}

This prints the instruction bytes followed by mov rax, rbx. After make, compile the example with:

cc -Isrc example.c build/libx64id_decoder.a -o example

x64id_decode returns the decoded instruction length. The instruction structure also exposes fields such as prefixes, ModRM, SIB, displacement, immediate, branch target, and decoded operands. Raw bytes and VEX details are enabled by default in the public header; see that header for the complete structure and constants.

Optional instruction fields

_ENABLE_RAW_BYTES stores the original bytes in instr.instr, while _ENABLE_VEX_INFO enables the expanded VEX fields. Both are defined by default in src/x64id.h. Changing them changes the public structure, so rebuild all code that uses the library.

Function length helper

src/utils/function_length.h provides the experimental getFunctionLength(char *buffer) helper. Call x64id_set_arch() first. It follows direct branches and stops at ret or int3 (the C3 and CC bytes). It does not handle jump tables and has no buffer-size argument, so the memory it reads must remain valid for every path it follows. The reported length is the span between the lowest and highest visited addresses and may include gaps.

The x64id executable is a small decoder and function-length demonstration. Its usage is listed by make help.

Tests and opcode coverage tools

Run the automated suite with:

make test

The suite builds byte sequences from definitions.txt, exercises supported ModRM and addressing forms, and checks the CLI output in both architectures. Its generated corpus is temporary. See test/README.md for the test model and its limits.

Generate compact binaries with one representative encoding per declared opcode, then inspect one with the CLI:

make opcode-sample ARCH=x86_64
./build/x64id_disassembler --arch x86_64 test/defined_opcodes_x86_64.bin

To scan opcode maps for encodings that currently fall back to db and print a deduplicated report:

make inspect-opcode-sweep ARCH=x86_64

Use ARCH=x86 for the 32-bit sweep. The full disassembly and missing-opcode summary are saved under /tmp by default; see test/README.md for paths and the SWEEP_OUTPUT and SWEEP_MISSING overrides. Sweep results identify unrecognized encoding forms; they do not establish full ISA coverage.

Contributing opcode definitions

Add or update entries in src/tools/definitions.txt, then regenerate the dispatch header:

python3 src/tools/defs2header.py

The generated src/tools/generated_opcodes.h is included by the disassembler. Definitions select both the mnemonic and an operand handler; consult test/README.md when adding a new handler so the test generator can construct its encoding forms.

References

License

Copyright (C) 2020–2026 Marco 'DispatchCode' Crivellari.

This project is licensed under the GNU General Public License, version 2 only (GPL-2.0-only). Copyright (C) 2020–2026 Marco 'DispatchCode' Crivellari. See LICENSE for the full license text.

Releases

Contributors

Languages