Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
metasm — This is the main repository for metasm, a free assembler / disassembler / compiler written in ruby | Kitploit
Herramientas/GitHubGitHub/jjyg/metasm
Code AnalysisReverse EngineeringShellcodeDebuggersBinary Analysis
GitHubjjyg/metasm

metasm

This is the main repository for metasm, a free assembler / disassembler / compiler written in ruby

Ver Repositorio
475821hace 5 mesesRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Sitio web

Metasm, la suite de manipulación de ensamblador de Ruby

  • scripts de ejemplo en samples/ -- lee los comentarios al principio de los archivos
  • todos los archivos están licenciados bajo los términos de la LGPL

Autor: Yoann Guillot

Visión general básica:

Metasm te permite interactuar con formatos de ejecutables (ExeFormat): PE, ELF, Mach-O, Shellcode, etc. Hay tres enfoques para un ExeFormat:

  • compilar uno desde cero
  • descompilar un formato existente
  • manipular la estructura del archivo

Puedes encontrar scripts listos para usar en el subdirectorio samples/; revisa los comentarios en las cabeceras de los scripts. También puedes probar el argumento --help si te sientes con suerte.

Para más información, consulta el subdirectorio doc/. Los archivos de texto se pueden compilar a html usando el script misc/txt2html.rb.

Aquí tienes una breve visión general del funcionamiento interno de Metasm.

Ensamblado:

Al compilar, se parte de un texto fuente (String de Ruby, que consiste principalmente en una secuencia de instrucciones/datos/directivas de relleno), que se analiza.

La cadena se pasa a una instancia de Preprocessor (que gestiona #if, #ifdef, #include, #define, /* */, etc., y debería ser 100% compatible con gcc -E), que está encapsulada en un AsmPreprocessor para fuentes de ensamblador (para manejar definiciones de macros asm, 'equ' y comentarios asm ';'). La interfaz para hacerlo es ExeFormat#parse(text[, filename, lineno]) o ExeFormat.assemble (que llama a .new, #parse y #assemble).

El (Asm)Preprocessor devuelve tokens al ExeFormat, que los analiza como Data, Padding, Labels o directivas del analizador. Las directivas del analizador siempre comienzan con un punto. Pueden ser genéricas (.pad, .offset...) o específicas de ExeFormat (.section, .import, .entrypoint...). Se gestionan mediante #parse_parser_instruction(). Si el ExeFormat no reconoce una palabra, se la pasa a su instancia de CPU, que es responsable de analizar Instructions (o lanzar una excepción). Todos esos tokens se almacenan en uno o más arrays en el atributo @source del ExeFormat (el @source de Shellcode es un Array; para PE/ELF es un hash [nombre de sección] => [Array de datos analizados]). Cada valor inmediato puede ser una Expression arbitraria (ver más adelante).

A continuación puedes ensamblar el fuente en secciones binarias usando ExeFormat#assemble.

Una vez que los binarios de sección están disponibles, el ejecutable binario completo se puede escribir en disco usando ExeFormat#encode_file(filename[, format]).

PE y ELF incluyen una función de importación automática (autoimport) que permite la creación automática de datos relacionados con la importación para funciones conocidas específicas del sistema operativo (p. ej., las llamadas no resueltas a 'strcpy' generarán datos para que el binario se enlace contra la biblioteca libc en tiempo de ejecución).

Los samples/{exe,pe,elf}encode.rb pueden tomar un archivo fuente asm como argumento y compilarlo a un ejecutable funcional.

Las clases CPU son responsables de analizar y codificar instrucciones individuales. El analizador Ia32 actual usa la sintaxis de Intel (p. ej. mov eax, 42). El analizador genérico reconoce las etiquetas como una cadena al comienzo de una línea seguida de dos puntos (p. ej. 'some_label:'). Se pueden usar etiquetas locales de estilo GCC (p. ej. '1:', a las que se hace referencia usando '1b' (hacia atrás) o '1f' (hacia adelante); pueden redefinirse tantas veces como sea necesario). Los datos se especifican mediante notación de estilo 'db' (p. ej. 'dd 42h', 'db "blabla", 0'). Ver samples/asmsyntax.rb

EncodedData:

En Metasm todos los datos binarios se almacenan como un EncodedData.

EncodedData tiene 3 atributos principales:

  • #data, que contiene los datos binarios brutos (generalmente un String de Ruby, pero ver VirtualString)
  • #export, que es un hash que asocia un nombre de exportación (nombre de etiqueta) con un desplazamiento dentro de #data
  • #reloc, que es un hash cuyas claves son desplazamientos dentro de #data y cuyos valores son objetos Relocation.

Un objeto Relocation tiene un endianness (:little/:big), un tipo (:u32 para 32 bits sin signo) y un target (el valor previsto almacenado aquí). El target es una Expression aritmética/lógica arbitraria.

EncodedData también tiene un #virtsize (p. ej., para secciones .bss) y un #ptr (desplazamiento interno usado al decodificar cosas).

Puedes hacer fixup de un EncodedData con un Hash nombre de variable => valor (el valor debe ser una Expression o un valor numérico). Al hacerlo, el target de cada reubicación se vincula usando el binding y, si el resultado es calculable (no se usa ningún nombre de variable externa en la Expression), el resultado se codifica usando la información de tamaño/signo/endianness de la reubicación. Si se desborda (intentar almacenar 128 en una reubicación con signo de 8 bits), se lanza una excepción EncodeError. Usa el tipo :a32 para permitir el truncamiento silencioso por desbordamiento. Si el target de la reubicación no es numérico, el target no cambia si usas EncodedData#fixup, o se reemplaza con el target vinculado usando #fixup!.

Desensamblado:

Este código se encuentra en el archivo fuente metasm/decode.rb, que define la clase Disassembler.

El desensamblador necesita un ExeFormat decodificado (para poder decir qué datos hay en cada dirección virtual) y un punto de entrada (una dirección virtual o nombre de exportación). A continuación puede empezar a desensamblar instrucciones. Cuando encuentra un Opcode marcado como :setip, le pide a la CPU el destino del salto (una Expression que puede involucrar valores de registros, por ejemplo jmp eax), y hace backtrace de las instrucciones hasta encontrar el valor numérico.

Al decodificar, el Disassembler mantiene un hash #decoded que asocia direcciones (expresiones/enteros #normalize()d) con DecodedInstructions.

El desensamblado genera un grafo InstructionBlock. Cada bloque contiene una lista de DecodedInstruction y punteros al bloque siguiente/anterior (por dirección).

El desensamblador también rastrea los accesos a datos de las instrucciones y almacena Xrefs para ellos. Los parámetros de backtrace se pueden ajustar, y la profundidad máxima a considerar puede cambiarse específicamente para los backtraces :r/:w (xrefs de memoria de instrucciones) usando #backtrace_maxblocks_data. Cuando se hace backtrace de una Expression, cada bloque recorrido se marca para que se detecten bucles, y para que si se encuentra una nueva ruta de código hacia un bloque existente, los backtraces puedan reanudarse usando esta nueva ruta.

El desensamblador hace muy pocas suposiciones y, en particular, no supone que las funciones vayan a retornar; solo lo harán si el backtrace de las instrucciones 'ret' es concluyente. Esto es bastante potente, pero también implica que cualquier error en el proceso de backtracking puede provocar una parada total; y también significa que el desensamblador es bastante lento.

El método especial #disassemble_fast se puede usar para solucionar esto cuando se sabe que el código está bien formado (es decir, asume que todas las llamadas retornan).

Cuando se encuentra una subfunción, se crea una DecodedFunction especial, que contiene un resumen de los efectos de la función (como una DecodedInstruction con esteroides). Esto permite al backtracker 'saltar' las subfunciones, lo que mejora enormemente la velocidad. Las DecodedFunctions pueden estar basadas en callbacks, para permitir un comportamiento muy dinámico. Las llamadas a funciones externas crean DecodedFunctions dedicadas, que contienen cierta información de la API (p. ej., información de fixup de la pila, accesos básicos a parámetros...). Esta información puede derivarse de una cabecera C analizada previamente. Si no hay un prototipo de función C disponible, se usa una entrada 'default' especial, que asume que la función tiene una ABI estándar.

Ia32 implementa una entrada :default específica, que gestiona la resolución automática del fixup de la pila asumiendo que la última instrucción 'call' retorna. Esto puede llevar a resultados inesperados; para una precisión máxima se recomienda una cabecera C que contenga información para todas las funciones externas (ver samples/factorize-headers-peimports para un script que genera dicha cabecera a partir de una instalación completa de Visual Studio y del binario objetivo).

Ia32 también implementa un callback específico GetProcAddress/dlsym, que devolverá el valor de retorno correcto si se puede hacer backtrace de los parámetros.

Los scripts que implementan un desensamblador completo son samples/disassemble{-gui}.rb. Ver los comentarios para los atajos de teclado de la GUI.

Manipulación de ExeFormat:

Puedes codificar/decodificar un ExeFormat (es decir, decodificar secciones, importaciones, cabeceras, etc.)

Constructor: ExeFormat.decode_file(str), ExeFormat.decode_file_header(str) Métodos: ExeFormat#encode_file(filename), ExeFormat#encode_string

Los archivos PE y ELF tienen una contraparte LoadedPE/LoadedELF, que son capaces de trabajar con versiones mapeadas en memoria de esos formatos (p. ej., para depurar procesos en ejecución).

VirtualString:

Una VirtualString es un objeto similar a String: puedes leer y posiblemente reescribir slices del mismo. Puede usarse como EncodedData#data y, por tanto, permite la virtualización de la mayoría de los algoritmos de Metasm. No puedes cambiar la longitud de una VirtualString. Tomar un slice de una VirtualString devuelve o bien un String (para tamaños pequeños) u otra VirtualString (una 'ventana' dentro de la otra). Puedes forzar la obtención de una VirtualString pequeña usando el método #dup(offset, length). Cualquier método no implementado que se llame sobre ella se reenvía a un String congelado (frozen) que es una copia completa de la VirtualString (debería evitarse si es posible; la cadena subyacente puede ser muy grande y lenta de acceder).

Actualmente hay 3 VirtualStrings implementadas:

  • VirtualFile, que carga un archivo en fragmentos del tamaño de una página bajo demanda,
  • WindowsRemoteString, que mapea la memoria virtual de otro proceso (usa la API de depuración de Windows a través de WinDbgAPI)
  • LinuxRemoteString, que mapea la memoria virtual de otro proceso (necesita derechos ptrace; la lectura de memoria se realiza usando /proc/pid/mem)

Las versiones Win/Lin son bastante potentes y permiten cosas como el desensamblado/parcheo de procesos en vivo fácilmente (usando LoadedPE/LoadedELF como ExeFormat).

Depuración:

Metasm incluye algunas interfaces para manejar la depuración.

Las clases WinOS y LinOS ofrecen acceso a los procesos del sistema operativo subyacente (p. ej., OS.current.find_process('foobar') recuperará un proceso en ejecución con foobar en su nombre de archivo; entonces se puede usar process.mem para acceder a su memoria).

Las API de depuración de bajo nivel de Windows y Linux tienen una interfaz básica de Ruby (PTrace y WinAPI), que son usadas por la clase Debugger unificada de alto nivel. La depuración remota es compatible mediante el protocolo de red del servidor GDB.

Se pueden crear depuradores de alto nivel con la siguiente línea de Ruby: Metasm::OS.current.create_debugger('foo')

Solo puede existir un tipo de clase de depurador host a la vez; para depurar múltiples procesos, conéctate a otros procesos usando la clase existente. Esto se debe a la forma en que funciona la API de depuración del sistema operativo en Windows y Linux.

Los backends de bajo nivel se definen en el subdirectorio os/; el front-end se define en debug.rb.

Una interfaz de depuración en consola de Linux está disponible en samples/lindebug.rb; utiliza una apariencia y comportamiento similares a SoftICE (simplificado). Puede comunicarse con un socket gdb-server; usa un target [udp:]host:port.

El ejemplo disassembler-gui permite la interacción con procesos en vivo cuando se usa como target 'live:'.

Analizador C:

Metasm incluye un analizador de C escrito a mano. Maneja todas las construcciones que conozco, excepto los flotantes hexadecimales:

  • static const L"bla"
  • argumentos variables
  • tipos incompletos
  • attributes(()), __declspec()
  • #pragma once
  • #pragma pack()
  • declaradores C99 - type bla = { [ 2 ... 14 ].toto = 28 };
  • funciones anidadas
  • tipos nativos __int8, etc.
  • direcciones de etiquetas (&&label)

Ten en cuenta también que todas esas cosas se analizan, pero la mayoría fallará al compilar en el backend Ia32/X64 (el único implementado hasta ahora).

El análisis de archivos C debería hacerse usando un ExeFormat existente, con el método parse_c_file. Esto asegura que las macros/ABI específicas del formato estén definidas correctamente (p. ej.: tamaño del tipo 'long', ABI para pasar parámetros a funciones, etc.)

Cuando analizas un String de C usando C::Parser.parse(text), recibes un objeto Parser. Contiene un campo #toplevel, que es un C::Block, que contiene #structs, #symbols y #statements. Las funciones de nivel superior se encuentran en el hash #symbol cuyas claves son los nombres de los símbolos, asociados a un objeto C::Variable que contiene las funciones. Los parámetros/atributos de la función son accesibles a través de func.type, y el código está en func.initializer, que es a su vez un C::Block. Debajo encontrarás una estructura en forma de árbol de C::Statements (If, While, Asm, CExpressions...).

Un C::Parser puede ser #precompilado para transformarlo en una versión simplificada que sea más fácil de compilar: se eliminan los typedefs, las secuencias de control se transforman en 'if (XX) goto YY;', etc.

Para compilar un programa C, usa PE/ELF.compile_c, que creará un C::Parser con macros específicas del ejecutable definidas (por ejemplo PE o ELF).

Las cabeceras específicas de proveedor pueden necesitar usar #pragma prepare_visualstudio (para analizar las cabeceras de Microsoft Visual Studio) o prepare_gcc (para gcc); esta última puede autodetectarse (o no). Las cabeceras de proveedor probadas son VS2003 (incl. DDK) y gcc4; los resultados pueden variar.

Actualmente, CPU#compilation de un código C generará un fuente asm (texto), que luego puede analizarse y ensamblarse a código binario.

Ver ExeFormat#compile_c y samples/exeencode.rb.

Descargar herramienta