Nyxstone es una poderosa biblioteca de ensamblaje y desarmado basada en LLVM. No requiere parches para el árbol de origen LLVM y los enlaces contra las bibliotecas LLVM estándar disponibles en la mayoría de las distribuciones de Linux. Implementado como una biblioteca C ++, Nyxstone también ofrece enlaces de óxido y pitón. Admite todas las arquitecturas LLVM oficiales y permite configurar la configuración de destino específica de la arquitectura.
Ensambla y desmonta el código para todas las arquitecturas compatibles con el LLVM vinculado, incluidos X86, ARM, MIPS, RISC-V y otros.
Biblioteca C ++ basada en LLVM con enlaces de óxido y pitón.
Soporte de plataforma nativa para Linux y MacOS.
Admite etiquetas en el ensamblador, incluida la especificación de las asignaciones de etiqueta a dirección
Ensambla y desmonta a bytes y texto crudos, pero también proporciona objetos de instrucción detallados que contienen la dirección, los bytes sin procesar y la representación de ensamblaje.
El desmontaje se puede limitar a un número de instrucciones especificados por el usuario de las secuencias de bytes.
Permite configurar características de destino específicas de la arquitectura, como las extensiones ISA y las características de hardware.
Para una lista completa de arquitecturas compatibles, puede usar clang -print-targets . Para obtener una lista completa de características para cada arquitectura, consulte llc -march=ARCH -mattr=help .
Nota
Descargo de responsabilidad: Nyxstone se ha desarrollado y probado principalmente para arquitecturas X86_64, Aarch64 y ARM32. Tenemos un alto grado de confianza en su capacidad para generar con precisión el ensamblaje e identificar errores para estas plataformas. Para otras arquitecturas, la efectividad de Nyxstone depende de la confiabilidad y el rendimiento de sus respectivos backends LLVM.
Esta sección proporciona instrucciones sobre cómo comenzar con Nyxstone, cubriendo los requisitos previos necesarios, cómo usar la herramienta CLI y las pautas paso a paso para usar la biblioteca con C ++, Rust y Python.
Antes de construir Nyxstone, asegúrese de que Clang y LLVM estén presentes en su sistema. Nyxstone apoya las versiones principales de LLVM 15-18. Al construir, busca llvm-config en $PATH de su sistema o en la variable de entorno especificada $NYXSTONE_LLVM_PREFIX/bin .
Opciones de instalación para versiones LLVM 15-18:
sudo apt install llvm- ${version} llvm- ${version} -devDebian LLVM versión 15 y 16 están disponibles a través de repositorios de Debian. La instalación es la misma que para Ubuntu. Para las versiones 17 o 18, consulte https://apt.llvm.org/ para obtener instrucciones de instalación.
Arco
sudo pacman -S llvm llvm-libsbrew install llvm@18
export NYXSTONE_LLVM_PREFIX=/opt/homebrew/opt/llvm@18 Nota : En Windows debe ejecutar estos comandos desde un símbolo del sistema Visual Studio 2022 X64. Además, reemplace ~lib/my-llvm-18 con una ruta diferente.
# 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 También asegúrese de instalar las bibliotecas dependientes del sistema que necesiten su versión LLVM para un enlace estático. Se pueden ver con el comando llvm-config --system-libs ; La lista puede estar vacía. En Ubuntu/Debian, necesitará los paquetes zlib1g-dev y zlibstd-dev .
Nyxstone viene con una práctica herramienta CLI para tareas rápidas de ensamblaje y desmontaje. Consulte el repositorio de Nyxstone y cree la herramienta con CMake:
# clone directory
git clone https://github.com/emproof-com/nyxstone
cd nyxstone
# build nyxstone
mkdir build && cd build && cmake .. && make Luego, nyxstone se puede usar desde la línea de comando. Aquí hay una salida de su menú de ayuda:
$ ./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'.
Ahora, podemos ensamblar una instrucción para la arquitectura x86_64:
$ ./nyxstone -t x86_64 "mov rax, rbx"
0x00000000: mov rax, rbx ; 48 89 d8
También podemos ensamblar una secuencia de instrucciones. A continuación, utilizamos el direccionamiento basado en etiquetas y asumimos que la primera instrucción se asigna para abordar 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
Además, podemos desmontar instrucciones para diferentes conjuntos de instrucciones, aquí el conjunto de instrucciones de pulgar ARM32:
$ ./nyxstone -t thumbv8 -d "13 37"
0x00000000: adds r7, #19 ; 13 37
Usando el soporte para etiquetas definidas por el usuario, podemos ensamblar este fragmento que no contiene la etiqueta .label especificando su ubicación de memoria nosotros mismos.
$ ./nyxstone -p "0x1000" -l ".label=0x1238" "jmp .label"
0x00001000: jmp .label ; e9 33 02 00 00
Para usar Nyxstone como una biblioteca C ++, su código C ++ debe estar vinculado contra NYXSTONE y LLVM.
El siguiente ejemplo de CMake asume Nyxstone en un subdirectorio nyxstone en su proyecto:
add_subdirectory (nyxstone)
add_executable (my_executable main.cpp)
target_link_libraries (my_executable nyxstone::nyxstone)El ejemplo de uso de C ++ correspondiente:
# 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 ;
}Para un ejemplo completo de C ++, eche un vistazo a Ejemplo.cpp.
Para usar nyxstone como una biblioteca de óxido, agréguela a su Cargo.toml y úsela como se muestra en el siguiente ejemplo:
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 ( ( ) )
}Para obtener más instrucciones sobre el enlace de óxido, eche un vistazo al readme correspondiente.
Para usar Nyxstone de Python, instálelo con PIP:
pip install nyxstoneEntonces, puedes usarlo desde Python:
$ python -q
>>> from nyxstone import Nyxstone
>>> nyxstone = Nyxstone("x86_64")
>>> nyxstone.assemble("jne .loop", 0x1100, {".loop": 0x1000})
Las instrucciones detalladas están disponibles en el readMe correspondiente.
Nyxstone aprovecha las funciones de API pública de C ++ de LLVM, como Target::createMCAsmParser y Target::createMCDisassembler para realizar tareas de ensamblaje y desmontaje. Nyxstone también extiende dos clases LLVM, MCELFStreamer y MCObjectWriter , para inyectar lógica personalizada y extraer información adicional. Específicamente, Nyxstone aumenta el proceso de ensamblaje con los siguientes pasos:
ELFStreamerWrapper::emitInstruction : captura la representación de ensamblaje y los bytes crudos iniciales de instrucciones si el usuario solicita objetos de instrucción detallados.
ObjectWriterWrapper::writeObject : escribe los bytes crudos finales de instrucciones (con ajustes de reubicación) a objetos de instrucciones detallados. Además, cambia la salida de bytes sin procesar del archivo ELF completo a la sección .Text.
ObjectWriterWrapper::validate_fixups : realiza verificaciones adicionales, como verificar el rango y la alineación de las reubicaciones.
ObjectWriterWrapper::recordRelocation : aplica reubicaciones adicionales. MCObjectWriter omite algunas reubicaciones que solo se aplican durante el enlace. En este momento, esto solo es relevante para la reubicación fixup_aarch64_pcrel_adrp_imm21 de la instrucción AARCH64 adrp .
Si bien la extensión de las clases de LLVM introduce algunos inconvenientes, como una fuerte dependencia de una versión específica de LLVM, creemos que este enfoque todavía es preferible sobre alternativas que requieren parches difíciles de mantener en el árbol de origen LLVM.
Estamos comprometidos a reducir aún más la complejidad del proyecto y estar abiertos a sugerencias de mejora. Mirando hacia el futuro, podemos eliminar la necesidad de extender las clases de LLVM aprovechando la infraestructura LLVM existente de manera más inteligente o incorporando lógica adicional en un paso posterior al procesamiento.
A continuación hay algunas ideas y mejoras que creemos que avanzarían significativamente en Nyxstone. Los elementos no figuran en ningún orden en particular:
Verifique la seguridad del hilo
Agregar soporte para más versiones LLVM (selección automática dependiendo de la versión de la biblioteca LLVM encontrada)
Explore la opción para hacer que LLVM aplique todas las reubicaciones (incluido adrp ) configurando MCObjectWriter de manera diferente o usando un escritor diferente
Explore la opción para generar objetos de instrucciones detallados al desmontar la salida de bytes sin procesar del proceso de ensamblaje en lugar de depender de la extensión de las clases LLVM
Explore la opción para implementar el rango adicional y la alineación de las reubicaciones en un paso posterior al procesamiento en lugar de confiar en la extensión de las clases de LLVM
Nyxstone está disponible bajo la licencia MIT.
¡Agradecemos las contribuciones de la comunidad! Si encuentra algún problema con NYXSTONE, no dude en abrir un problema de GitHub.
Si está interesado en contribuir directamente al proyecto, puede, por ejemplo,:
Una vez que esté listo, envíe una solicitud de extracción con sus cambios. ¡Esperamos su contribución!
Los contribuyentes actuales son:
Centro :
Menor :
Para garantizar que vinculemos LLVM correctamente con los versiones adecuadas en óxido, adaptamos Build.rs de LLVM-Sys.