Agencia web » Noticias digitales » Cómo ver binarios desde la línea de comandos de Linux

Cómo ver binarios desde la línea de comandos de Linux

¿Tiene un archivo misterioso? Linux file El comando le dirá rápidamente qué tipo de archivo es. Si es un archivo binario, puede leer más sobre él. file tiene un montón de compañeros estables que te ayudarán a analizarlo. Le mostraremos cómo utilizar algunas de estas herramientas.

Identificación de tipos de archivos

Los archivos generalmente tienen características que permiten a los paquetes de software identificar qué tipo de archivo es, así como qué datos contiene. No tendría sentido intentar abrir un archivo PNG en un reproductor de música MP3, por lo que es útil y práctico que un archivo lleve algún tipo de identificación.

Pueden ser unos pocos bytes de firma al principio del archivo. Esto permite que un archivo sea explícito sobre su formato y contenido. A veces, el tipo de archivo se infiere de un aspecto distintivo de la organización interna de los datos en sí, conocido como arquitectura de archivo.

Algunos sistemas operativos, como Windows, están completamente guiados por la extensión de un archivo. Puede llamarlo crédulo o confiable, pero Windows asume que cualquier archivo con la extensión DOCX es realmente un archivo de procesador de texto DOCX. Linux no es así, como pronto verá. Quiere una prueba y busca dentro del archivo para encontrarla.

Las herramientas descritas aquí ya estaban instaladas en las distribuciones Manjaro 20, Fedora 21 y Ubuntu 20.04 que usamos para investigar este artículo. Comencemos nuestra investigación usando el file comandante.

Usando el comando de archivo

Tenemos una colección de diferentes tipos de archivos en nuestro directorio actual. Son una mezcla de documentos, código fuente, ejecutables y archivos de texto.

le ls El comando nos mostrará lo que hay en el directorio, y el -hl La opción (tamaños legibles por humanos, lista larga) nos mostrará el tamaño de cada archivo:

ls-hl

intentar file en algunos de ellos y ver lo que obtenemos:

archivo build_instructions.odt
archivo build_instructions.pdf
archivo COBOL_Report_Apr60.djvu

Los tres formatos de archivo están correctamente identificados. Cuando sea posible, file nos da un poco más de información. El archivo PDF estaría en formato de la versión 1.5.

Incluso si cambiamos el nombre del archivo ODT para que tenga una extensión con el valor arbitrario de XYZ, el archivo aún se identifica correctamente, tanto en el Files explorador de archivos y en la línea de comando usando file.

En el Files explorador de archivos, se le asigna el icono correcto. En la línea de comando, file ignore la extensión y mire dentro del archivo para determinar su tipo:

archivo build_instructions.xyz

Utilizando file en los medios, como archivos de imagen y música, generalmente proporciona información sobre su formato, codificación, resolución, etc.:

captura de pantalla del archivo.png
captura de pantalla del archivo.jpg
archivo Pachelbel_Canon_In_D.mp3

Curiosamente, incluso con archivos de texto sin formato, file no juzga el archivo por su extensión. Por ejemplo, si tiene un archivo con la extensión ".c", que contiene texto sin formato estándar pero no código fuente, file no lo confunda con un archivo de código fuente C real:

función de archivo + encabezados.h
hacer archivoefile
archivo hello.c

file identifica correctamente el archivo de encabezado (".h") como parte de una colección de archivos de código fuente C, y sabe que el makefiel es un guión.

Usar archivos con archivos binarios

Los binarios son más una "caja negra" que otros. Los archivos de imagen se pueden ver, los archivos de sonido se pueden reproducir y los archivos de documentos se pueden abrir con el software apropiado. Sin embargo, los binarios son más difíciles.

Por ejemplo, los archivos "hello" y "wd" son ejecutables binarios. Estos son programas. El archivo llamado "wd.o" es un archivo de objeto. Cuando un compilador compila el código fuente, se crean uno o más archivos objeto. Estos contienen código de máquina que la computadora eventualmente ejecutará cuando se ejecute el programa completo, así como información para el enlazador. El vinculador comprueba cada archivo de objeto en busca de llamadas a funciones a las bibliotecas. Los vincula a todas las bibliotecas utilizadas por el programa. El resultado de este proceso es un archivo ejecutable.

El archivo "watch.exe" es un ejecutable binario que ha sido compilado de forma cruzada para ejecutarse en Windows:

archivo wd
archivo wd.o
archivo hola
archivo watch.exe

Tomando el último primero file nos dice que el archivo "watch.exe" es un ejecutable PE32 +, un programa de consola, para la familia de procesadores x86 bajo Microsoft Windows. PE son las siglas de Portable Executable Format, que tiene versiones de 32 y 64 bits. PE32 es la versión de 32 bits y PE32 + es la versión de 64 bits.

Los otros tres archivos están todos identificados como archivos ELF (formato ejecutable y enlazable). Este es un estándar para archivos ejecutables y archivos de objetos compartidos, como bibliotecas. Veremos el formato de encabezado ELF en breve.

Lo que podría llamar su atención es que los dos ejecutables ("wd" y "hello") se identifican como objetos compartidos Linux Standard Base (LSB), y el archivo de objeto "wd.o" se identifica como un LSB móvil. La palabra ejecutable es evidente en su ausencia.

Los archivos de objetos son móviles, lo que significa que el código que contienen se puede cargar en la memoria en cualquier ubicación. Los ejecutables se enumeran como objetos compartidos porque fueron creados por el vinculador a partir de archivos de objeto de tal manera que heredan esta capacidad.

Esto permite que el sistema ASMR (Address Space Layout Randomization) cargue los ejecutables en la memoria en las direcciones que elija. Los ejecutables estándar tienen una dirección de carga codificada en sus encabezados, que dicta dónde se cargan en la memoria.

ASMR es una técnica de seguridad. La carga de ejecutables en la memoria en direcciones predecibles los hace vulnerables a los ataques. De hecho, los atacantes siempre conocerán sus puntos de entrada y la ubicación de sus funciones. Los ejecutables independientes de posición (PIE) colocados en una dirección aleatoria superan esta vulnerabilidad.

si hacemos un compilonuestro programa con el gcc compilador y proporcionar el -no-pie opción, generaremos un ejecutable clásico.

le -o (archivo de salida) nos permite darle un nombre a nuestro ejecutable:

gcc -o hola -no-pie hola.c

Usaremos file en el nuevo ejecutable y vea qué ha cambiado:

archivo hola

El tamaño del ejecutable es el mismo que antes (17 KB):

ls -hl hola

El binario ahora se identifica como un ejecutable estándar. Hacemos esto solo con fines de demostración. Si compila aplicaciones de esta manera, perderá todos los beneficios de ASMR.

¿Por qué un ejecutable es tan grande?

Nuestro ejemplo hello El programa es de 17 KB, por lo que difícilmente podría llamarse grande, pero entonces todo es relativo. El código fuente es de 120 bytes:

gato hola c

¿Qué hace estallar el binario si solo imprime una cadena en la ventana de la terminal? Sabemos que hay un encabezado ELF, pero solo tiene 64 bytes para un binario de 64 bits. Claramente, debe ser otra cosa:

ls -hl hola

Analicemos el binario con el strings como un simple primer paso para descubrir qué contiene. Lo canalizaremos en less:

hola cadenas | menos

Hay muchas cadenas dentro del binario además de "¡Hola, mundo friki!" De nuestro código fuente. La mayoría de ellos son etiquetas para regiones dentro del binario y los nombres y la información de enlace de los objetos compartidos. Estos incluyen las bibliotecas y funciones dentro de esas bibliotecas, de las que depende el binario.

le ldd El comando nos muestra las dependencias de los objetos compartidos de un binario:

ldd hola

Hay tres entradas en la salida y dos de ellas incluyen una ruta de directorio (la primera no):

  • linux-vdso.so: Virtual Dynamic Shared Object (VDSO) es un mecanismo de kernel que permite que un binario de espacio de usuario acceda a un conjunto de rutinas de espacio de kernel. Esto evita la sobrecarga del cambio de contexto del modo kernel de un usuario. Los objetos compartidos VDSO se adhieren al formato ejecutable y enlazable (ELF), lo que les permite vincularse dinámicamente al binario en tiempo de ejecución. VDSO se asigna dinámicamente y aprovecha ASMR. La biblioteca GNU C estándar proporciona la capacidad VDSO si el kernel admite el esquema ASMR.
  • libc.so.6: El objeto compartido de la biblioteca GNU C.
  • /lib64/ld-linux-x86-64.so.2: Este es el enlazador dinámico que quiere usar el binario. El enlazador dinámico consulta el binario para descubrir sus dependencias. Lanza estos objetos compartidos en la memoria. Prepara el binario para que se ejecute y pueda encontrar y acceder a las dependencias en la memoria. Luego lanza el programa.

Encabezado ELF

Podemos examinar y decodificar el encabezado ELF usando el readelf utilidad y -h Opción (encabezado de archivo):

readelf -h hola

El encabezado se interpreta para nosotros.

El primer byte de todos los binarios ELF se establece en el valor hexadecimal 0x7F. Los siguientes tres bytes se establecen en 0x45, 0x4C y 0x46. El primer byte es una bandera que identifica el archivo como un binario ELF. Para ser claros, los siguientes tres bytes indican "ELF" en ASCII:

  • Clase Indica si el binario es un ejecutable de 32 o 64 bits (1 = 32, 2 = 64).
  • Los datos: Indica el endianity en uso. La codificación endian define cómo se almacenan los números multibyte. En la codificación big-endian, primero se almacena un número con sus bits más significativos. En la codificación little-endian, el número se almacena primero con sus bits menos significativos.
  • Versión: La versión de ELF (actualmente es 1).
  • Sistema operativo/ABI: Representa el tipo de interfaz binaria de aplicación utilizada. Esto define la interfaz entre dos módulos binarios, como un programa y una biblioteca compartida.
  • Versión ABI: La versión ABI.
  • Tipo: Tipo de archivo binario ELF. Los valores comunes son ET_REL para un recurso móvil (como un archivo de objeto), ET_EXEC para un ejecutable compilado con el -no-pie bandera, y ET_DYN para un ejecutable que admita ASMR.
  • Equipo: La arquitectura del conjunto de instrucciones. Esto indica la plataforma de destino para la que se creó el binario.
  • Versión: Siempre configurado en 1, para esta versión de ELF.
  • Dirección del punto de entrada: Dirección de memoria en el binario en la que comienza la ejecución.

Las otras entradas son tamaños y números de regiones y secciones en el binario para que se puedan calcular sus ubicaciones.

Un vistazo rápido a los primeros ocho bytes del binario con hexdump mostrará el byte de firma y la cadena "ELF" en los primeros cuatro bytes del archivo. la -C (canónica) nos da la representación ASCII de los bytes junto a sus valores hexadecimales, y la -n La opción (número) nos permite especificar cuántos bytes queremos ver:

hexdump -C -n 8 hola

objdump y la vista granular

Si desea ver el detalle, puede utilizar el objdumporden con el -d opción (desmontar):

objdump -d hola | menos

Esto desensambla el código de máquina ejecutable y lo muestra en bytes hexadecimales junto con el equivalente en lenguaje ensamblador. La ubicación de la dirección del primer bye en cada línea se muestra en el extremo izquierdo.

Esto solo es útil si puede leer el lenguaje ensamblador o si tiene curiosidad sobre lo que sucede detrás de la cortina. Hay mucha salida, así que la canalizamos a less.

Recopilación y vinculación

Hay muchas formas de compilar un binario. Por ejemplo, el desarrollador elige si incluir o no información de depuración. La forma en que se vincula el binario también influye en su contenido y tamaño. Si las referencias binarias comparten objetos como dependencias externas, será más pequeño que aquel al que las dependencias están vinculadas estáticamente.

La mayoría de los desarrolladores ya están familiarizados con los comandos que hemos cubierto aquí. Para otros, sin embargo, ofrecen formas fáciles de hurgar y ver qué hay dentro de la caja negra binaria.

★ ★ ★ ★ ★