Hay cosas en el mundo que damos por sentado, aunque son verdaderas obras maestras. Una de esas cosas son las utilidades de Linux como ls y ps. Aunque generalmente se perciben como simples, esto está lejos de ser el caso si miramos hacia adentro. Y también lo hace ELF, formato ejecutable y enlazable. Un formato de archivo que se usa universalmente, pero pocos lo entienden. Esta guía rápida lo ayudará a comprender.

Después de leer esta guía, aprenderá:
- ¿Por qué se necesita el formato ELF y para qué tipo de archivos se utiliza?
- Estructura de archivos ELF y detalles de formato
- Cómo leer y analizar contenido binario de un archivo ELF
- ¿Qué herramientas se utilizan para analizar archivos binarios?
¿Qué es un archivo ELF?
ELF significa formato ejecutable y enlazable, y define la estructura de archivos binarios, bibliotecas y archivos principales. La especificación de formato permite que el sistema operativo interprete correctamente las instrucciones de la máquina contenidas en el archivo. Un archivo ELF suele ser el archivo de salida de un compilador o enlazador y tiene un formato binario. Usando herramientas adecuadas, puede ser analizado y estudiado.
¿Por qué estudiar ELF en detalle?
Antes de sumergirse en los detalles técnicos, no estará mal explicar por qué es útil comprender el formato ELF. En primer lugar, le permite estudiar el funcionamiento interno del sistema operativo. Cuando algo salió mal, este conocimiento lo ayudará a comprender mejor qué sucedió exactamente y por qué motivo. Además, la capacidad de examinar archivos ELF puede ser valiosa para encontrar agujeros de seguridad y detectar archivos sospechosos. Y finalmente, para una mejor comprensión del proceso de desarrollo. Incluso si programa en un lenguaje de alto nivel como Go, sabrá mejor lo que sucede detrás de escena.
Entonces, ¿por qué estudiar ELF?
- Para una comprensión general del sistema operativo
- Para el desarrollo de software
- Análisis forense digital y respuesta a incidentes (DFIR)
- Investigación de malware (análisis binario)
De la fuente al proceso
Independientemente del sistema operativo que utilicemos, es necesario traducir de alguna manera las funciones del código fuente al lenguaje de la CPU: código de máquina. Las funciones pueden ser las más básicas, por ejemplo, abrir un archivo en el disco o mostrar algo en la pantalla. En lugar de usar el lenguaje de la CPU directamente, usamos un lenguaje de programación que tiene características estándar. El compilador luego traduce estas funciones en código objeto. Este código de objeto se vincula al programa completo, utilizando el vinculador. El resultado es un archivo binario que se puede ejecutar en una plataforma específica y un tipo específico de CPU.
Antes de empezar
Esta publicación contiene muchos equipos. Es mejor ejecutarlos en una máquina de prueba. Copie los archivos binarios existentes antes de ejecutar estos comandos en ellos. También escribiremos un pequeño programa en C que puede compilar. En definitiva, la práctica es la mejor manera de aprender algo.
Anatomía de un archivo ELF
Un error común es que los archivos ELF son solo para archivos binarios o ejecutables. Ya hemos dicho que pueden usarse para partes de archivos ejecutables (código objeto). Otro ejemplo son los archivos de biblioteca y los volcados de núcleo (archivos de núcleo y archivos a.out). La especificación ELF también se usa en Linux para kernel y módulos de kernel.

Estructura
Debido a la extensibilidad de los archivos ELF, la estructura puede variar para diferentes archivos. El archivo ELF consta de:
- Encabezado ELF
- datos
Con el comando readelf, podemos ver la estructura del archivo, y se verá más o menos así:

Encabezado ELF
Como puede ver en la captura de pantalla, el encabezado ELF comienza con un "número mágico". Este "número mágico" proporciona información sobre el archivo. Los primeros 4 bytes determinan que este es un archivo ELF (45 = E, 4c = L, 46 = F, están precedidos por 7f).
Se requiere el encabezado ELF. Es necesario para que los datos se interpreten correctamente durante el enlace y la ejecución. Para una mejor comprensión del funcionamiento interno de un archivo ELF, es útil saber para qué se utiliza esta información.
Clase
Después de declarar un tipo ELF, sigue un campo de clase. Este valor significa la arquitectura a la que está destinado el archivo. Puede ser 01 (arquitectura de 32 bits) o 02 (64 bits). Aquí vemos 02, que es traducido por el comando readelf como un archivo ELF64, es decir, este archivo usa una arquitectura de 64 bits. Esto no es sorprendente, hay un procesador moderno instalado en mi automóvil.
Datos
Luego viene el campo "datos", que tiene dos opciones: 01 - LSB (bit menos significativo), también conocido como little-endian, o 02 - MSB (bit más significativo, big-endian). Estos valores ayudan a interpretar el resto de los objetos en el archivo. Esto es importante porque los diferentes tipos de procesadores manejan las estructuras de datos de manera diferente. En nuestro caso, se utiliza LSB, ya que el procesador tiene una arquitectura AMD64.
El efecto LSB se hace visible cuando se usa la utilidad hexdump en un archivo binario. Veamos el encabezado ELF para / bin / ps.
$ hexdump -n 16 /bin/ps 0000000 457f 464c 0102 0001 0000 0000 0000 0000 0000010
Vemos que los pares de valores son diferentes, debido a la interpretación del orden de los datos.
Versión
Luego sigue otro valor mágico "01", que es el número de versión. Actualmente solo está disponible la versión 01, por lo que este número no significa nada interesante.
OS / ABI
Cada sistema operativo tiene su propia forma de llamar a las funciones, tienen mucho en común, pero, además, cada sistema tiene pequeñas diferencias. El orden de la llamada de función está determinado por la interfaz binaria de aplicación (ABI). Los campos OS / ABI describen qué ABI se usa y su versión. En nuestro caso, el valor es 00, lo que significa que no se utilizan extensiones específicas. En la salida, esto se muestra como Sistema V.
Versión ABI
Si es necesario, se puede indicar una versión ABI.
Coche
El título también indica el tipo esperado de máquina (AMD64).
Tipo
El campo de tipo indica para qué es el archivo. Aquí hay algunos tipos de archivos comunes.
NÚCLEO (valor 4)
DYN (archivo de objeto compartido), biblioteca (valor 3)
EXEC (archivo ejecutable), archivo ejecutable (valor 2)
REL (archivo reubicable), archivo antes de vincular (valor 1)
Ver el título completo
Aunque algunos campos se pueden ver a través de readelf, en realidad hay más. Por ejemplo, puede averiguar para qué procesador es el archivo. Use hexdump para ver el encabezado ELF completo y todos los valores.
7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 02 00 3e 00 01 00 00 00 a8 2b 40 00 00 00 00 00 |..>......+@.....| 40 00 00 00 00 00 00 00 30 65 01 00 00 00 00 00 |@.......0e......| 00 00 00 00 40 00 38 00 09 00 40 00 1c 00 1b 00 |....@.8...@.....|
(salida hexdump -C -n 64 / bin / ps)
El campo resaltado determina el tipo de máquina. El valor 3e es el decimal 62, que corresponde a AMD64. Para tener una idea de todos los tipos de archivos, vea
este archivo de encabezado.
Aunque puede hacer todo esto en un volcado hexadecimal, tiene sentido usar una herramienta que haga el trabajo por usted. La utilidad dumpelf puede ser útil. Muestra resultados formateados que coinciden con el encabezado ELF. Será bueno estudiar qué campos se utilizan y cuáles son sus valores típicos.
Ahora, donde hemos explicado el significado de estos campos, ¡es hora de ver qué magia real hay detrás de ellos y pasar a los siguientes encabezados!
Datos de archivo
Además del encabezado, los archivos ELF constan de tres partes.
- Encabezados o segmentos del programa
- Sección o encabezados de sección
- Datos
Antes de sumergirnos en estos encabezados, sería útil saber que el archivo ELF tiene dos "tipos" diferentes. Uno de ellos está diseñado para el enlazador y permite la ejecución de código (segmentos). El otro es para comandos y datos (secciones). Dependiendo del propósito, se usa el tipo de encabezado apropiado. Comencemos con el encabezado del programa, que se encuentra en los archivos ejecutables ELF.
Títulos de programa
Un archivo ELF consta de cero o más segmentos, y describe cómo crear un proceso, una imagen de memoria para la ejecución en tiempo de ejecución. Cuando el núcleo ve estos segmentos, los coloca en el espacio de direcciones virtuales utilizando la llamada al sistema mmap (2). En otras palabras, convierte las instrucciones preparadas previamente en una imagen en la memoria. Si el archivo ELF es un binario regular, requiere estos encabezados de programa, de lo contrario simplemente no funcionará. Estos encabezados se utilizan, junto con las estructuras de datos correspondientes, para formar el proceso. Para las bibliotecas compartidas, el proceso es similar.

Encabezado de programa en archivo binario ELF
Vemos 9 títulos de programas en este ejemplo. Al principio es difícil entender lo que significan. Vamos a sumergirnos en los detalles.
GNU_EH_FRAME
Esta es la cola ordenada utilizada por el compilador GCC. Almacena manejadores de excepciones. Si algo salió mal, se utilizan para manejar correctamente la situación.
GNU_STACK
Este encabezado se utiliza para guardar la información de la pila. Una característica interesante es que la pila no tiene que ser ejecutable, ya que esto podría implicar vulnerabilidades de seguridad.
Si falta el segmento GNU_STACK, se utiliza la pila ejecutable. Las utilidades scanelf y execstack muestran los detalles del dispositivo de pila.
Comandos para ver el encabezado del programa:
- dumpelf (pax-utils)
- elfos -S / bin / ps
- eu-readelf –program-headers / bin / ps
Secciones ELF
Encabezados de sección
Los encabezados de sección definen todas las secciones de un archivo. Como ya se mencionó, esta información se utiliza para vincular y reubicar.
Las secciones aparecen en un archivo ELF después de que el compilador GNU C convierte el código C en ensamblador, y el ensamblador GNU crea objetos.
Como se muestra en la figura anterior, un segmento puede tener 0 o más secciones. Hay cuatro secciones principales para archivos ejecutables: .text, .data, .rodata y .bss. Cada una de estas secciones arranca con diferentes permisos, que se pueden ver con readelf -S.
.text
Contiene código ejecutable. Se empaquetará en un segmento con derechos de lectura y ejecución. Se descarga una vez y su contenido no cambia. Esto se puede ver con la utilidad objdump.
12 .text 0000a3e9 0000000000402120 0000000000402120 00002120 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE
.data
Datos inicializados con permisos de lectura y escritura.
.rodata
Datos inicializados con permisos de solo lectura. (= A).
.bss
Datos no inicializados con permisos de lectura / escritura. (= WA)
[24] .data PROGBITS 00000000006172e0 000172e0 0000000000000100 0000000000000000 WA 0 0 8 [25] .bss NOBITS 00000000006173e0 000173e0 0000000000021110 0000000000000000 WA 0 0 32
Comandos para ver secciones y títulos.
- dumpelf
- elfls -p / bin / ps
- eu-readelf –section-headers / bin / ps
- readelf -S / bin / ps
- objdump -h / bin / ps
Grupos de secciones
Algunas secciones se pueden agrupar como si formaran un todo único. Los nuevos vinculadores admiten esta funcionalidad. Pero si bien esto no es común.
Aunque esto puede no parecer muy interesante, el conocimiento de las herramientas de análisis de archivos ELF proporciona grandes beneficios. Por esta razón, se proporciona una descripción general de estas herramientas y su propósito al final del artículo.
Binarios estáticos y dinámicos.
Cuando se trata de archivos binarios ELF, será útil saber cómo están vinculados estos dos tipos de archivos. Pueden ser estáticos y dinámicos, y esto se aplica a las bibliotecas que usan. Si el binario es "dinámico", significa que utiliza bibliotecas externas que contienen algunas funciones comunes, como abrir un archivo o crear un socket de red. Los binarios estáticos, por el contrario, incluyen todas las bibliotecas necesarias.
Si desea verificar si el archivo es estático o dinámico, use el comando de archivo. Ella mostrará algo como esto:
$ file /bin/ps /bin/ps: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), <b>dynamically linked (uses shared libs)</b>, for GNU/Linux 2.6.24, BuildID[sha1]=2053194ca4ee8754c695f5a7a7cff2fb8fdd297e, stripped
Para determinar qué bibliotecas externas se usan, simplemente use ldd en el mismo binario:
$ ldd /bin/ps linux-vdso.so.1 => (0x00007ffe5ef0d000) libprocps.so.3 => /lib/x86_64-linux-gnu/libprocps.so.3 (0x00007f8959711000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f895934c000) /lib64/ld-linux-x86-64.so.2 (0x00007f8959935000)
Consejo: Para ver más dependencias, es mejor usar la utilidad lddtree.
Herramientas de análisis binario
Si desea analizar archivos ELF, definitivamente será útil mirar primero las herramientas existentes. Hay kits de herramientas para el desarrollo inverso de binarios y código ejecutable. Si es nuevo en el análisis de archivos ELF, comience con el análisis estático. El análisis estático implica que examinamos los archivos sin iniciarlos. Cuando comience a comprender mejor su trabajo, pase al análisis dinámico. Ejecute los ejemplos y observe su comportamiento real.
Herramientas populares
Radare2
El kit de herramientas Radare2 fue creado por Sergi Alvarez. El número 2 implica que el código ha sido reescrito completamente en comparación con la primera versión. Ahora muchos investigadores lo utilizan para estudiar el funcionamiento del código.
Paquetes de software
La mayoría de los sistemas Linux tienen binutils instalados. Otros paquetes pueden ayudarlo a ver más información. El kit de herramientas correcto simplificará su trabajo, especialmente si está analizando archivos ELF. He compilado aquí una lista de paquetes y utilidades para analizar archivos ELF.
elfutils/ usr / bin / eu-addr2line
/ usr / bin / eu-ar: una alternativa a ar, para crear y procesar archivos de almacenamiento
/ usr / bin / eu-elfcmp
/ usr / bin / eu-elflint: compruebe el cumplimiento de las especificaciones gABI y psABI
/ usr / bin / eu-findtextrel - busca reubicaciones de texto
/ usr / bin / eu-ld - combina objetos y archivos de almacenamiento
/ usr / bin / eu-make-debug-archive
/ usr / bin / eu-nm: muestra los símbolos del objeto y los archivos ejecutables
/ usr / bin / eu-objdump - muestra información del archivo objeto
/ usr / bin / eu-ranlib: crea un índice de archivos
/ usr / bin / eu-readelf - muestra el archivo ELF en forma legible
/ usr / bin / eu-size: muestra el tamaño de cada sección (texto, datos, bss, etc.)
/ usr / bin / eu-stack: muestra la pila del proceso actual o el volcado del kernel
/ usr / bin / eu-strings: muestra cadenas de texto (como la utilidad de cadenas)
/ usr / bin / eu-strip: elimina la tabla de caracteres del archivo ELF
/ usr / bin / eu-unstrip: agrega símbolos e información de depuración al binario
Nota: el paquete elfutils será un buen comienzo, contiene la mayoría de las herramientas de análisis
elfkickers/ usr / bin / ebfc - compilador de lenguaje Brainfuck
/ usr / bin / elfls: muestra encabezados de programa y encabezados de sección con banderas
/ usr / bin / elftoc - convierte un programa binario en un programa en C
/ usr / bin / infect: una utilidad que inyecta un cuentagotas crea un archivo setuid en / tmp
/ usr / bin / objres: crea un objeto a partir de datos regulares o binarios
/ usr / bin / rebind: cambia el enlace y la visibilidad de los caracteres en los archivos ELF
/ usr / bin / sstrip: elimina componentes innecesarios de un archivo ELF
Nota: el autor del paquete ELFKickers se centra en manipular archivos ELF, lo que le permite obtener más información cuando trabaja con los archivos binarios ELF "incorrectos"
pax-utils/ usr / bin / dumpelf - volcado de la estructura ELF interna
/ usr / bin / lddtree - como ldd, con la configuración del nivel de dependencias que se muestra
/ usr / bin / pspax - muestra información ELF / PaX sobre procesos en ejecución
/ usr / bin / scanelf: una amplia gama de información, incluidos detalles de PaX
/ usr / bin / scanmacho: muestra detalles de los binarios de Mach-O (Mac OS X)
/ usr / bin / symtree - muestra los caracteres del árbol
Nota: algunas utilidades en este paquete pueden escanear directorios de forma recursiva y son ideales para analizar todo el contenido de un directorio. El foco está en las herramientas de investigación de PaX. Además de admitir ELF, puede extraer información de los binarios de Mach-O.
Ejemplo de salida
scanelf -a /bin/ps TYPE PAX PERM ENDIAN STK/REL/PTL TEXTREL RPATH BIND FILE ET_EXEC PeMRxS 0755 LE RW- R-- RW- - - LAZY /bin/ps
preenlace/ usr / bin / execstack: puede ver o cambiar información sobre si la pila es ejecutable
/ usr / bin / prelink: reubica llamadas en archivos ELF para acelerar el proceso
Preguntas frecuentes
¿Qué es un ABI?
ABI es la interfaz binaria de aplicación y define una interfaz de bajo nivel entre el sistema operativo y el código ejecutable.
¿Qué es el ELF?
ELF es un formato ejecutable y enlazable. Esta es una especificación de formato que define cómo se escriben las instrucciones en código ejecutable.
¿Cómo puedo ver el tipo de archivo?
Use el comando de archivo para la primera etapa de análisis. Este comando puede mostrar los detalles extraídos de los números y encabezados "mágicos".
Conclusión
Los archivos ELF son para ejecución y vinculación. Dependiendo del propósito, contienen los segmentos y secciones necesarios. El núcleo del sistema operativo escanea los segmentos y los asigna a la memoria (usando mmap). Las secciones son vistas por un enlazador que crea un archivo ejecutable u objeto compartido.
Los archivos ELF son muy flexibles y admiten varios tipos de CPU, arquitecturas de máquinas y sistemas operativos. También es extensible, cada archivo está diseñado de manera diferente, dependiendo de las partes requeridas. Mediante el uso de las herramientas adecuadas, puede averiguar el propósito del archivo y examinar el contenido de los archivos binarios. Puede ver las funciones y líneas contenidas en el archivo. Un buen comienzo para aquellos que investigan malware o para entender por qué el proceso se comporta (o no) de cierta manera.
Recursos para estudios posteriores
Si desea saber más sobre ELF y la ingeniería inversa, puede ver el trabajo que hacemos en Linux Security Expert. Como parte del plan de estudios, tenemos
un módulo de ingeniería inversa con trabajo práctico de laboratorio.
Para aquellos de ustedes que aman leer, un documento bueno y profundo:
Formato ELF y un
documento escrito por Brian Raiter, también conocido como ELFkickers. Para aquellos a quienes les gusta entender la fuente, miren el
encabezado ELF documentado de Apple.
Consejo:
Si desea mejorar en el análisis de archivos, comience a usar las
herramientas de análisis populares que están disponibles actualmente.