Nyxstone est une bibliothèque d'assemblage et de démontage puissante basée sur LLVM. Il ne nécessite pas de correctifs dans l'arborescence source LLVM et des liens avec les bibliothèques LLVM standard disponibles dans la plupart des distributions Linux. Implémenté en tant que bibliothèque C ++, Nyxstone propose également des liaisons rouille et python. Il prend en charge toutes les architectures LLVM officielles et permet de configurer les paramètres cibles spécifiques à l'architecture.
Assemble et démonte le code pour toutes les architectures soutenues par le LLVM lié, y compris x86, ARM, MIPS, RISC-V et autres.
Bibliothèque C ++ basée sur LLVM avec des liaisons rouille et python.
Prise en charge de la plate-forme native pour Linux et MacOS.
Prend en charge les étiquettes dans l'assembleur, y compris la spécification des mappages d'étiquette à adresse
Assemble et démêle des octets et du texte bruts, mais fournit également des objets d'instruction détaillés contenant l'adresse, les octets bruts et la représentation de l'assemblage.
Le démontage peut être limité à un nombre d'instructions spécifié par l'utilisateur des séquences d'octets.
Permet de configurer des fonctionnalités cibles spécifiques à l'architecture, telles que les extensions ISA et les fonctionnalités matérielles.
Pour une liste complète des architectures prises en charge, vous pouvez utiliser clang -print-targets . Pour une liste complète des fonctionnalités pour chaque architecture, reportez-vous à llc -march=ARCH -mattr=help .
Note
Avertissement: Nyxstone a été principalement développé et testé pour les architectures x86_64, AARCH64 et ARM32. Nous avons un degré élevé de confiance dans sa capacité à générer avec précision l'assemblage et à identifier les erreurs pour ces plateformes. Pour d'autres architectures, l'efficacité de Nyxstone dépend de la fiabilité et des performances de leurs backends LLVM respectifs.
Cette section fournit des instructions sur la façon de démarrer avec Nyxstone, couvrant les conditions préalables nécessaires, comment utiliser l'outil CLI et les directives étape par étape pour l'utilisation de la bibliothèque avec C ++, Rust et Python.
Avant de construire Nyxstone, assurez-vous que Clang et LLVM sont présents sur votre système. Nyxstone prend en charge les versions majeures LLVM 15-18. Lors de la construction, il recherche llvm-config dans $PATH de votre système ou la variable d'environnement spécifiée $NYXSTONE_LLVM_PREFIX/bin .
Options d'installation pour les versions LLVM 15-18:
sudo apt install llvm- ${version} llvm- ${version} -devDebian LLVM version 15 et 16 sont disponibles par le biais de Debian Repositaires. L'installation est la même que pour Ubuntu. Pour les versions 17 ou 18, reportez-vous à https://apt.llvm.org/ pour les instructions d'installation.
Cambre
sudo pacman -S llvm llvm-libsbrew install llvm@18
export NYXSTONE_LLVM_PREFIX=/opt/homebrew/opt/llvm@18 Remarque : Sur Windows, vous devez exécuter ces commandes à partir d'une invite de commande Visual Studio 2022 x64. Remplacez également ~lib/my-llvm-18 par un chemin différent.
# checkout llvm
git clone -b release/18.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-18
# export path
export NYXSTONE_LLVM_PREFIX= ~ /lib/my-llvm-18 Assurez-vous également d'installer toutes les bibliothèques dépendantes du système nécessaires à votre version LLVM pour la liaison statique. Ils peuvent être visualisés avec la commande llvm-config --system-libs ; La liste peut être vide. Sur Ubuntu / Debian, vous aurez besoin des packages zlib1g-dev et zlibstd-dev .
Nyxstone est livré avec un outil CLI pratique pour les tâches d'assemblage et de démontage rapides. Découvrez le référentiel Nyxstone et créez l'outil avec CMake:
# clone directory
git clone https://github.com/emproof-com/nyxstone
cd nyxstone
# build nyxstone
mkdir build && cd build && cmake .. && make Ensuite, nyxstone peut être utilisé à partir de la ligne de commande. Voici une sortie de son menu d'aide:
$ ./nyxstone -h
Usage: nyxstone [-t=<triple>] [-p=<pc>] [-d] <input>
Examples:
# Assemble an instruction with the default architecture ('x86_64').
nyxstone 'push eax'
# Disassemble the bytes 'ffc300d1' as AArch64 code.
nyxstone -t aarch64 -d ffc300d1
Options:
-t, --triple=<triple> LLVM target triple or alias, e.g. 'aarch64'
-c, --cpu=<cpu> LLVM CPU specifier, e.g. 'cortex-a53'
-f, --features=<list> LLVM architecture/CPU feature list, e.g. '+mte,-neon'
-p, --address=<pc> Initial address to assemble/disassemble relative to
-l, --labels=<list> Label-to-address mappings (used when assembling only)
-d, --disassemble Treat <input> as bytes to disassemble instead of assembly
-h, --help Show this help and usage message
Notes:
The '--triple' parameter also supports aliases for common target triples:
'x86_32' -> 'i686-linux-gnu'
'x86_64' -> 'x86_64-linux-gnu'
'armv6m' -> 'armv6m-none-eabi'
'armv7m' -> 'armv7m-none-eabi'
'armv8m' -> 'armv8m.main-none-eabi'
'aarch64' -> 'aarch64-linux-gnueabihf'
The CPUs for a target can be found with 'llc -mtriple=<triple> -mcpu=help'.
The features for a target can be found with 'llc -mtriple=<triple> -mattr=help'.
Maintenant, nous pouvons assembler une instruction pour l'architecture x86_64:
$ ./nyxstone -t x86_64 "mov rax, rbx"
0x00000000: mov rax, rbx ; 48 89 d8
Nous pouvons également assembler une séquence d'instructions. Dans ce qui suit, nous utilisons l'adressage basé sur les étiquettes et supposons que la première instruction est mappée pour aborder 0xdeadbeef :
$ ./nyxstone -t x86_64 -p 0xdeadbeef "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
De plus, nous pouvons démonter des instructions pour différents ensembles d'instructions, ici l'ensemble d'instructions ARM32:
$ ./nyxstone -t thumbv8 -d "13 37"
0x00000000: adds r7, #19 ; 13 37
En utilisant la prise en charge des étiquettes définies par l'utilisateur, nous pouvons assembler cet extrait qui ne contient pas l'étiquette .label en spécifiant notre emplacement de mémoire nous-mêmes.
$ ./nyxstone -p "0x1000" -l ".label=0x1238" "jmp .label"
0x00001000: jmp .label ; e9 33 02 00 00
Pour utiliser Nyxstone comme bibliothèque C ++, votre code C ++ doit être lié à Nyxstone et LLVM.
L'exemple CMake suivant suppose Nyxstone dans un sous-répertoire nyxstone dans votre projet:
add_subdirectory (nyxstone)
add_executable (my_executable main.cpp)
target_link_libraries (my_executable nyxstone::nyxstone)L'exemple d'utilisation C ++ correspondant:
# 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 ;
}Pour un exemple complet C ++, jetez un œil à l'exemple.cpp.
Pour utiliser Nyxstone comme bibliothèque de rouille, ajoutez-le à votre Cargo.toml et utilisez-le comme indiqué dans l'exemple suivant:
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 ( ( ) )
}Pour plus d'instructions concernant la liaison de la rouille, jetez un œil à la lecture correspondante.
Pour utiliser Nyxstone de Python, installez-le à l'aide de PIP:
pip install nyxstoneEnsuite, vous pouvez l'utiliser à partir de Python:
$ python -q
>>> from nyxstone import Nyxstone
>>> nyxstone = Nyxstone("x86_64")
>>> nyxstone.assemble("jne .loop", 0x1100, {".loop": 0x1000})
Des instructions détaillées sont disponibles dans la lecture correspondante.
Nyxstone exploite les fonctions de l'API C ++ publique de LLVM telles que Target::createMCAsmParser et Target::createMCDisassembler pour effectuer des tâches d'assemblage et de démontage. Nyxstone étend également deux classes LLVM, MCELFStreamer et MCObjectWriter , pour injecter une logique personnalisée et extraire des informations supplémentaires. Plus précisément, Nyxstone augmente le processus d'assemblage avec les étapes suivantes:
ELFStreamerWrapper::emitInstruction : capture la représentation de l'assemblage et les octets d'instructions brutes initiaux si des objets d'instruction détaillés sont demandés par l'utilisateur.
ObjectWriterWrapper::writeObject : écrit les octets bruts finaux des instructions (avec ajustements de relocalisation) à des objets d'instruction détaillés. De plus, il change les octets bruts de sortie du fichier ELF complet à la section .Text.
ObjectWriterWrapper::validate_fixups : effectue des vérifications supplémentaires, telles que la vérification de la plage et l'alignement des relocations.
ObjectWriterWrapper::recordRelocation : applique des délocalisations supplémentaires. MCObjectWriter saute certaines relocations qui ne sont appliquées que lors de la liaison. À l'heure actuelle, cela n'est pertinent que pour la relocalisation fixup_aarch64_pcrel_adrp_imm21 de l'instruction AARCH64 adrp .
Bien que l'extension des classes LLVM présente certains inconvénients, comme une forte dépendance à une version LLVM spécifique, nous pensons que cette approche est toujours préférable aux alternatives qui nécessitent durement pour maintenir les correctifs dans l'arbre source LLVM.
Nous nous engageons à réduire davantage la complexité du projet et ouverts aux suggestions d'amélioration. Pour l'avenir, nous pouvons éliminer la nécessité d'étendre les classes LLVM en tirant parti de l'infrastructure LLVM existante de manière plus intelligente ou en incorporant une logique supplémentaire dans une étape post-traitement.
Vous trouverez ci-dessous quelques idées et améliorations qui, selon nous, feraient progresser considérablement le nyxstone. Les éléments ne sont pas répertoriés dans un ordre particulier:
Vérifiez la sécurité du fil
Ajouter la prise en charge de plus de versions LLVM (sélection automatique en fonction de la version de la bibliothèque LLVM trouvé)
Explorez l'option pour que LLVM applique toutes les délocalisations (y compris adrp ) en configurant MCObjectWriter différemment ou en utilisant un écrivain différent
Explorez l'option pour générer des objets d'instructions détaillées en démontant la sortie des octets bruts du processus d'assemblage au lieu de s'appuyer sur l'extension des classes LLVM
Explorez l'option pour implémenter une plage supplémentaire et l'alignement des délocalisations dans une étape de post-traitement au lieu de s'appuyer sur l'extension des classes LLVM
Nyxstone est disponible sous la licence du MIT.
Nous accueillons les contributions de la communauté! Si vous rencontrez des problèmes avec Nyxstone, n'hésitez pas à ouvrir un problème GitHub.
Si vous souhaitez contribuer directement au projet, vous pouvez par exemple:
Une fois que vous êtes prêt, soumettez une demande de traction avec vos modifications. Nous attendons avec impatience votre contribution!
Les contributeurs actuels sont:
Cœur :
Mineure :
Pour nous assurer que nous lions correctement LLVM avec un versioning approprié dans Rust, nous avons adapté la build.rs de LLVM-SYS.