emproof-com / nyxstone

Nyxstone: assembly / disassembly library based on LLVM, implemented in C++ with Rust and Python bindings, maintained by emproof.com
https://www.emproof.com
MIT License
230 stars 6 forks source link
aarch64 arm assembly disassembly infosec mips powerpc reverse-engineering risc-v security thumb x86 x86-64

Nyxstone

Github Cpp CI Badge crates.io PyPI

Nyxstone is a powerful assembly and disassembly library based on LLVM. It doesn’t require patches to the LLVM source tree and links against standard LLVM libraries available in most Linux distributions. Implemented as a C++ library, Nyxstone also offers Rust and Python bindings. It supports all official LLVM architectures and allows to configure architecture-specific target settings.

Nyxstone Python binding demo

Index

  1. Core Features
  2. Using Nyxstone
    1. Prerequisites
    2. CLI Tool
    3. C++ Library
    4. Rust Bindings
    5. Python Bindings
  3. How it works
  4. Roadmap
  5. License
  6. Contributing
  7. Contributors

Core Features

For a comprehensive list of supported architectures, you can use clang -print-targets. For a comprehensive list of features for each architecture, refer to llc -march=ARCH -mattr=help.

[!NOTE] Disclaimer: Nyxstone has been primarily developed and tested for x86_64, AArch64, and ARM32 architectures. We have a high degree of confidence in its ability to accurately generate assembly and identify errors for these platforms. For other architectures, Nyxstone's effectiveness depends on the reliability and performance of their respective LLVM backends.

Using Nyxstone

This section provides instructions on how to get started with Nyxstone, covering the necessary prerequisites, how to use the CLI tool, and step-by-step guidelines for using the library with C++, Rust, and Python.

Prerequisites

Before building Nyxstone, ensure clang and LLVM 15 are present as statically linked libraries. Nyxstone looks for llvm-config in your system's $PATH or the specified environment variable $NYXSTONE_LLVM_PREFIX/bin.

Installation Options for LLVM 15:

Note: On Windows you need to run these commands from a Visual Studio 2022 x64 command prompt. Additionally replace ~lib/my-llvm-15 with a different path.

# checkout llvm
git clone -b release/15.x --single-branch https://github.com/llvm/llvm-project.git
cd llvm-project

# build LLVM with custom installation directory
cmake -S llvm -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_PARALLEL_LINK_JOBS=1
cmake --build build
cmake --install build --prefix ~/lib/my-llvm-15

# export path
export NYXSTONE_LLVM_PREFIX=~/lib/my-llvm-15

Also make sure to install any system dependent libraries needed by your LLVM version for static linking. They can be viewed with the command llvm-config --system-libs; the list can be empty. On Ubuntu/Debian, you will need the packages zlib1g-dev and zlibstd-dev.

CLI Tool

Nyxstone comes with a handy CLI tool for quick assembly and disassembly tasks. Install boost with your distribution's package manager, checkout the Nyxstone repository, and build the tool with cmake:

# install boost on Ubuntu/Debian
apt install boost

# clone directory
git clone https://github.com/emproof-com/nyxstone
cd nyxstone

# build nyxstone
mkdir build && cd build && cmake .. && make 

Then, nyxstone can be used from the command line. Here's an output of its help menu:

$ ./nyxstone --help
Allowed options:
  --help                    Show this message
  --arch arg (=x86_64)      LLVM triple or architecture identifier of triple,
                            for example "x86_64", "x86_64-linux-gnu", "armv8",
                            "armv8eb", "thumbv8", "aarch64"
  --cpu arg                 LLVM cpu specifier, refer to `llc -march=ARCH
                            -mcpu=help` for a comprehensive list
  --features arg            LLVM features to enable/disable, comma seperated
                            feature strings prepended by '+' or '-' toenable or
                            disable respectively. Refer to `llc -march=ARCH
                            -mattr=help` for a comprehensive list
  --address arg (=0)        Address

Assembling:
  --labels arg              Labels, for example "label0=0x10,label1=0x20"
  -A [ --assemble ] arg     Assembly

Disassembling:
  -D [ --disassemble ] arg  Byte code in hex, for example: "0203"

Now, we can assemble an instruction for the x86_64 architecture:

$ ./nyxstone --arch "x86_64" -A "mov rax, rbx"
Assembled:
    0x00000000: mov rax, rbx - [ 48 89 d8 ]

We can also assemble a sequence of instructions. In the following, we make use of label-based addressing and assume the first instruction is mapped to address 0xdeadbeef:

$ ./nyxstone --arch "x86_64" --address 0xdeadbeef -A "cmp rax, rbx; jz .exit ; inc rax ; .exit: ret"
    0xdeadbeef: cmp rax, rbx - [ 48 39 d8 ]
    0xdeadbef2: je .exit - [ 74 03 ]
    0xdeadbef4: inc rax - [ 48 ff c0 ]
    0xdeadbef7: ret - [ c3 ]

We can also disassemble an instruction for the ARM32 thumb instruction set:

$ ./nyxstone --arch "thumbv8" -D "13 37"
Disassembled:
    0x00000000: adds r7, #19 - [ 13 37 ]

C++ Library

To use Nyxstone as a C++ library, your C++ code has to be linked against Nyxstone and LLVM 15.

The following cmake example assumes Nyxstone in a subdirectory nyxstone in your project:

add_subdirectory(nyxstone)

add_executable(my_executable main.cpp)
target_link_libraries(my_executable nyxstone::nyxstone)

The corresponding C++ usage example:

#include <cassert>
#include <iostream>

#include "nyxstone.h"

int main(int, char**) {
    // Create the nyxstone instance:
    auto nyxstone {
        NyxstoneBuilder("x86_64")
            .build()
            .value()
    };

     // Assemble to bytes
    std::vector<uint8_t> bytes = 
        nyxstone->assemble(/*assembly=*/"mov rax, rbx", /*address=*/0x1000, /* labels= */ {}).value();

    std::vector<uint8_t> expected {0x48, 0x89, 0xd8};
    assert(bytes == expected);

    return 0;
}

For a comprehensive C++ example, take a look at example.cpp.

Rust Bindings

To use Nyxstone as a Rust library, add it to your Cargo.tomland use it as shown in the following example:

use anyhow::Result;
use nyxstone::{Nyxstone, NyxstoneConfig};

use std::collections::HashMap;

fn main() -> Result<()> {
    let nyxstone = Nyxstone::new("x86_64", NyxstoneConfig::default())?;

    let bytes = nyxstone.assemble_with(
        "mov rax, rbx; cmp rax, rdx; jne .label",
        0x1000,
        &HashMap::from([(".label", 0x1200)]),
    )?;

    println!("Bytes: {:x?}", bytes);

    Ok(())
}

For more instructions regarding the Rust binding, take a look at the corresponding README.

Python Bindings

To use Nyxstone from Python, install it using pip:

pip install nyxstone

Then, you can use it from Python:

$ python -q
>>> from nyxstone import Nyxstone
>>> nyxstone = Nyxstone("x86_64")
>>> nyxstone.assemble("jne .loop", 0x1100, {".loop": 0x1000})

Detailed instructions are available in the corresponding README.

How it works

Nyxstone leverages public C++ API functions from LLVM such as Target::createMCAsmParser and Target::createMCDisassembler to perform assembly and disassembly tasks. Nyxstone also extends two LLVM classes, MCELFStreamer and MCObjectWriter, to inject custom logic and extract additional information. Specifically, Nyxstone augments the assembly process with the following steps:

While extending LLVM classes introduces some drawbacks, like a strong dependency on a specific LLVM version, we believe this approach is still preferable over alternatives that require hard to maintain patches in the LLVM source tree.

We are committed to further reduce the project's complexity and open to suggestions for improvement. Looking ahead, we may eliminate the need to extend LLVM classes by leveraging the existing LLVM infrastructure in a smarter way or incorporating additional logic in a post-processing step.

Roadmap

Below are some ideas and improvements we believe would significantly advance Nyxstone. The items are not listed in any particular order:

License

Nyxstone is available under the MIT license.

Contributing

We welcome contributions from the community! If you encounter any issues with Nyxstone, please feel free to open a GitHub issue.

If you are interested in contributing directly to the project, you can for example:

Once you're ready, submit a pull request with your changes. We are looking forward to your contribution!

Contributors

The current contributors are:

Core:

Minor: