linux assembly elf

¿Cómo hacer un archivo ELF ejecutable en Linux usando un editor hexadecimal?



assembly (2)

Descompile un mundo hola NASM y comprenda cada byte en él

Versión de esta respuesta con un buen TOC y más contenido: http://www.cirosantilli.com/elf-hello-world (llegando al límite de 30k char aquí)

Normas

ELF es especificado por el LSB:

El LSB básicamente se vincula a otros estándares con extensiones menores, en particular:

Puede encontrar un resumen útil en:

man elf

Su estructura puede ser examinada de forma legible por humanos a través de utilidades como readelf y objdump .

Genera el ejemplo

Analicemos un ejemplo de Linux x86-64 ejecutable mínimo:

section .data hello_world db "Hello world!", 10 hello_world_len equ $ - hello_world section .text global _start _start: mov rax, 1 mov rdi, 1 mov rsi, hello_world mov rdx, hello_world_len syscall mov rax, 60 mov rdi, 0 syscall

Compilado con:

nasm -w+all -f elf64 -o ''hello_world.o'' ''hello_world.asm'' ld -o ''hello_world.out'' ''hello_world.o''

Versiones

  • NASM 2.10.09
  • Binutils versión 2.24 (contiene ld )
  • Ubuntu 14.04

No usamos un programa en C ya que eso complicaría el análisis, será el nivel 2 :-)

Hexdumps

hd hello_world.o hd hello_world.out

Salida en: https://gist.github.com/cirosantilli/7b03f6df2d404c0862c6

Estructura global de archivos

Un archivo ELF contiene las siguientes partes:

  • Encabezado ELF. Señala la posición de la tabla de encabezado de sección y la tabla de encabezado del programa.

  • Tabla de encabezado de sección (opcional en ejecutable). Cada uno tiene e_shnum sección e_shnum , cada uno apuntando a la posición de una sección.

  • N secciones, con N <= e_shnum (opcional en ejecutable)

  • Tabla de encabezado del programa (solo en ejecutable). Cada uno tiene e_phnum programa e_phnum , cada uno apuntando a la posición de un segmento.

  • N segmentos, con N <= e_phnum (opcional en ejecutable)

El orden de esas partes no es fijo: lo único fijo es el encabezado ELF que debe ser lo primero en el archivo: los documentos genéricos dicen:

Encabezado ELF

La forma más fácil de observar el encabezado es:

readelf -h hello_world.o readelf -h hello_world.out

Salida en: https://gist.github.com/cirosantilli/7b03f6df2d404c0862c6

Bytes en el archivo objeto:

00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 00000010 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 |..>.............| 00000020 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 |........@.......| 00000030 00 00 00 00 40 00 00 00 00 00 40 00 07 00 03 00 |....@.....@.....|

Ejecutable:

00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 00000010 02 00 3e 00 01 00 00 00 b0 00 40 00 00 00 00 00 |..>.......@.....| 00000020 40 00 00 00 00 00 00 00 10 01 00 00 00 00 00 00 |@...............| 00000030 00 00 00 00 40 00 38 00 02 00 40 00 06 00 03 00 |[email protected]...@.....|

Estructura representada:

typedef struct { unsigned char e_ident[EI_NIDENT]; Elf64_Half e_type; Elf64_Half e_machine; Elf64_Word e_version; Elf64_Addr e_entry; Elf64_Off e_phoff; Elf64_Off e_shoff; Elf64_Word e_flags; Elf64_Half e_ehsize; Elf64_Half e_phentsize; Elf64_Half e_phnum; Elf64_Half e_shentsize; Elf64_Half e_shnum; Elf64_Half e_shstrndx; } Elf64_Ehdr;

Desglose manual:

  • 0 0: EI_MAG = 7f 45 4c 46 = 0x7f ''E'', ''L'', ''F'' : número mágico ELF

  • 0 4: EI_CLASS = 02 = ELFCLASS64 : elfo de 64 bits

  • 0 5: EI_DATA = 01 = ELFDATA2LSB : datos big endian

  • 0 6: EI_VERSION = 01 : versión de formato

  • 0 7: EI_OSABI (solo en la actualización de 2003) = 00 = ELFOSABI_NONE : sin extensiones.

  • 0 8: EI_PAD = 8x 00 : bytes reservados. Debe establecerse en 0.

  • 1 0: e_type = 01 00 = 1 (big endian) = ET_REl : formato reubicable

    En el ejecutable es 02 00 para ET_EXEC .

  • 1 2: e_machine = 3e 00 = 62 = EM_X86_64 : arquitectura AMD64

  • 1 4: e_version = 01 00 00 00 : debe ser 1

  • 1 8: e_entry = 8x 00 : punto de entrada de la dirección de ejecución, o 0 si no corresponde, como para el archivo de objeto, ya que no hay un punto de entrada.

    En el ejecutable, es b0 00 40 00 00 00 00 00 . TODO: ¿a qué más podemos configurar esto? El núcleo parece poner la IP directamente en ese valor, no está codificado.

  • 2 0: e_phoff = 8x 00 : desplazamiento de la tabla de encabezado del programa, 0 si no está presente.

    40 00 00 00 en el ejecutable, es decir, comienza inmediatamente después del encabezado ELF.

  • 2 8: e_shoff = 40 7x 00 = 0x40 : desplazamiento de archivo de tabla de encabezado de sección, 0 si no está presente.

  • 3 0: e_flags = 00 00 00 00 TODO. Arco específico.

  • 3 4: e_ehsize = 40 00 : tamaño de este encabezado elfo. TODO por qué este campo? ¿Cómo puede variar?

  • 3 6: e_phentsize = 00 00 : tamaño de cada encabezado de programa, 0 si no está presente.

    38 00 en el ejecutable: tiene 56 bytes de longitud

  • 3 8: e_phnum = 00 00 : número de entradas de encabezado de programa, 0 si no está presente.

    02 00 en ejecutable: hay 2 entradas.

  • 3 A: e_shentsize y e_shnum = 40 00 07 00 : tamaño del encabezado de sección y número de entradas

  • 3 E: e_shstrndx ( Section Header STRing iNDeX ) = 03 00 : índice de la sección .shstrtab .

Tabla de encabezado de sección

Matriz de estructuras Elf64_Shdr .

Cada entrada contiene metadatos sobre una sección determinada.

e_shoff del encabezado ELF da la posición inicial, 0x40 aquí.

e_shentsize y e_shnum del encabezado ELF dicen que tenemos 7 entradas, cada una de 0x40 bytes de longitud.

Entonces la tabla toma bytes de 0x40 a 0x40 + 7 + 0x40 - 1 = 0x1FF.

Algunos nombres de sección están reservados para ciertos tipos de sección: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections por ejemplo .text requiere un tipo SHF_ALLOC y SHF_ALLOC + SHF_EXECINSTR

readelf -S hello_world.o :

There are 7 section headers, starting at offset 0x40: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .data PROGBITS 0000000000000000 00000200 000000000000000d 0000000000000000 WA 0 0 4 [ 2] .text PROGBITS 0000000000000000 00000210 0000000000000027 0000000000000000 AX 0 0 16 [ 3] .shstrtab STRTAB 0000000000000000 00000240 0000000000000032 0000000000000000 0 0 1 [ 4] .symtab SYMTAB 0000000000000000 00000280 00000000000000a8 0000000000000018 5 6 4 [ 5] .strtab STRTAB 0000000000000000 00000330 0000000000000034 0000000000000000 0 0 1 [ 6] .rela.text RELA 0000000000000000 00000370 0000000000000018 0000000000000018 4 2 4 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), l (large) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)

struct representada por cada entrada:

typedef struct { Elf64_Word sh_name; Elf64_Word sh_type; Elf64_Xword sh_flags; Elf64_Addr sh_addr; Elf64_Off sh_offset; Elf64_Xword sh_size; Elf64_Word sh_link; Elf64_Word sh_info; Elf64_Xword sh_addralign; Elf64_Xword sh_entsize; } Elf64_Shdr;

Secciones

Índice 0 sección

Contenido en bytes 0x40 a 0x7F.

La primera sección siempre es mágica: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html dice:

Si el número de secciones es mayor o igual que SHN_LORESERVE (0xff00), e_shnum tiene el valor SHN_UNDEF (0) y el número real de entradas de la tabla de encabezado de sección está contenido en el campo sh_size del encabezado de sección en el índice 0 (de lo contrario, el El miembro sh_size de la entrada inicial contiene 0).

También hay otras secciones mágicas detalladas en la Figure 4-7: Special Section Indexes .

SHT_NULL

En el índice 0, SHT_NULL es obligatorio. ¿Tiene otros usos? ¿Cuál es el uso de la sección SHT_NULL en ELF? ?

sección de datos

.data es la sección 1:

00000080 01 00 00 00 01 00 00 00 03 00 00 00 00 00 00 00 |................| 00000090 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 |................| 000000a0 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000b0 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

  • 80 0: sh_name = 01 00 00 00 : índice 1 en la tabla de cadenas .shstrtab

    Aquí, 1 dice que el nombre de esta sección comienza en el primer carácter de esa sección y termina en el primer carácter NUL, formando la cadena .data .

    .data es uno de los nombres de sección que tiene un significado predefinido http://www.sco.com/developers/gabi/2003-12-17/ch4.strtab.html

    Estas secciones contienen datos inicializados que contribuyen a la imagen de memoria del programa.

  • 80 4: sh_type = 01 00 00 00 : SHT_PROGBITS : el contenido de la sección no está especificado por ELF, solo por cómo lo interpreta el programa. Normal desde una sección .data .

  • 80 8: sh_flags = 03 7x 00 : SHF_ALLOC y SHF_EXECINSTR : http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#sh_flags , según sea necesario en una sección .data

  • 90 0: sh_addr = 8x 00 : en qué dirección virtual se colocará la sección durante la ejecución, 0 si no se coloca

  • 90 8: sh_offset = 00 02 00 00 00 00 00 00 = 0x200 : número de bytes desde el inicio del programa hasta el primer byte en esta sección

  • a0 0: sh_size = 0d 00 00 00 00 00 00 00

    Si tomamos 0xD bytes comenzando en sh_offset 200, vemos:

    00000200 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 |Hello world!.. |

    AHA! Entonces nuestro "Hello world!" la cadena está en la sección de datos como le dijimos que estuviera en el NASM.

    Una vez que nos graduemos de hd , buscaremos esto como:

    readelf -x .data hello_world.o

    que salidas:

    Hex dump of section ''.data'': 0x00000000 48656c6c 6f20776f 726c6421 0a Hello world!.

    NASM establece propiedades decentes para esa sección porque trata .data mágicamente: http://www.nasm.us/doc/nasmdoc7.html#section-7.9.2

    También tenga en cuenta que esta fue una mala elección de sección: un buen compilador de C pondría la cadena en .rodata en .rodata lugar, porque es de solo lectura y permitiría más optimizaciones del sistema operativo.

  • a0 8: sh_link y sh_info = 8x 0: no se aplican a este tipo de sección. http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections

  • b0 0: sh_addralign = 04 = TODO: ¿por qué es necesaria esta alineación? ¿Es solo para sh_addr , o también para símbolos dentro de sh_addr ?

  • b0 8: sh_entsize = 00 = la sección no contiene una tabla. Si! = 0, significa que la sección contiene una tabla de entradas de tamaño fijo. En este archivo, vemos en la salida de readelf que este es el caso de las secciones .symtab y .rela.text .

sección de texto

Ahora que hemos hecho una sección manualmente, readelf -S y usemos el readelf -S de las otras secciones.

[Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 2] .text PROGBITS 0000000000000000 00000210 0000000000000027 0000000000000000 AX 0 0 16

.text es ejecutable pero no editable: si intentamos escribir en él, Linux segfaults. Veamos si realmente tenemos algún código allí:

objdump -d hello_world.o

da:

hello_world.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <_start>: 0: b8 01 00 00 00 mov $0x1,%eax 5: bf 01 00 00 00 mov $0x1,%edi a: 48 be 00 00 00 00 00 movabs $0x0,%rsi 11: 00 00 00 14: ba 0d 00 00 00 mov $0xd,%edx 19: 0f 05 syscall 1b: b8 3c 00 00 00 mov $0x3c,%eax 20: bf 00 00 00 00 mov $0x0,%edi 25: 0f 05 syscall

Si grep b8 01 00 00 en el hd , vemos que esto solo ocurre en 00000210 , que es lo que dice la sección. Y el tamaño es 27, que también coincide. Entonces debemos estar hablando de la sección correcta.

Parece el código correcto: una write seguida de una exit .

La parte más interesante es la línea a que hace:

movabs $0x0,%rsi

para pasar la dirección de la cadena a la llamada del sistema. Actualmente, el 0x0 es solo un marcador de posición. Después de que ocurra la vinculación, se modificará para contener:

4000ba: 48 be d8 00 60 00 00 movabs $0x6000d8,%rsi

Esta modificación es posible debido a los datos de la sección .rela.text .

SHT_STRTAB

Las secciones con sh_type == SHT_STRTAB se llaman tablas de cadenas .

Contienen una serie de cadenas separadas por un valor nulo.

Dichas secciones son utilizadas por otras secciones cuando se van a utilizar nombres de cadenas. La sección de uso dice:

  • qué tabla de cadenas están usando
  • ¿Cuál es el índice en la tabla de cadenas de destino donde comienza la cadena?

Entonces, por ejemplo, podríamos tener una tabla de cadenas que contenga: TODO: ¿tiene que comenzar con /0 ?

Data: /0 a b c /0 d e f /0 Index: 0 1 2 3 4 5 6 7 8

Y si otra sección quiere usar la cadena def , tienen que apuntar al índice 5 de esta sección (letra d ).

Secciones notables de la tabla de cadenas:

  • .shstrtab
  • .strtab

.shstrtab

Tipo de sección: sh_type == SHT_STRTAB .

Nombre común: tabla de cadena de encabezado de sección .

El nombre de la sección .shstrtab está reservado. El estándar dice:

Esta sección contiene los nombres de las secciones.

El campo e_shstrnd del encabezado ELF mismo señala esta sección.

Los índices de cadena de esta sección se señalan mediante el campo sh_name de los encabezados de sección, que denota cadenas.

Esta sección no tiene SHF_ALLOC marcado, por lo que no aparecerá en el programa en ejecución.

readelf -x .shstrtab hello_world.o

Da:

Hex dump of section ''.shstrtab'': 0x00000000 002e6461 7461002e 74657874 002e7368 ..data..text..sh 0x00000010 73747274 6162002e 73796d74 6162002e strtab..symtab.. 0x00000020 73747274 6162002e 72656c61 2e746578 strtab..rela.tex 0x00000030 7400 t.

Los datos en esta sección tienen un formato fijo: http://www.sco.com/developers/gabi/2003-12-17/ch4.strtab.html

Si observamos los nombres de otras secciones, vemos que todas contienen números, por ejemplo, la sección .text es el número 7 .

Luego, cada cadena termina cuando se encuentra el primer carácter NUL, por ejemplo, el carácter 12 es /0 justo después de .text/0 .

.symtab

Tipo de sección: sh_type == SHT_SYMTAB .

Nombre común: tabla de símbolos .

Primero notamos que:

  • sh_link = 5
  • sh_info = 6

Para SHT_SYMTAB secciones SHT_SYMTAB , esos números significan que:

  • las cadenas que dan nombres de símbolos se encuentran en la sección 5, .strtab
  • los datos de reubicación se encuentran en la sección 6, .rela.text

Una buena herramienta de alto nivel para desmontar esa sección es:

nm hello_world.o

lo que da:

0000000000000000 T _start 0000000000000000 d hello_world 000000000000000d a hello_world_len

Sin embargo, esta es una vista de alto nivel que omite algunos tipos de símbolos y en la que los tipos de símbolos. Se puede obtener un desmontaje más detallado con:

readelf -s hello_world.o

lo que da:

Symbol table ''.symtab'' contains 7 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello_world.asm 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 3: 0000000000000000 0 SECTION LOCAL DEFAULT 2 4: 0000000000000000 0 NOTYPE LOCAL DEFAULT 1 hello_world 5: 000000000000000d 0 NOTYPE LOCAL DEFAULT ABS hello_world_len 6: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 2 _start

El formato binario de la tabla está documentado en http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html

Los datos son:

readelf -x .symtab hello_world.o

Lo que da:

Hex dump of section ''.symtab'': 0x00000000 00000000 00000000 00000000 00000000 ................ 0x00000010 00000000 00000000 01000000 0400f1ff ................ 0x00000020 00000000 00000000 00000000 00000000 ................ 0x00000030 00000000 03000100 00000000 00000000 ................ 0x00000040 00000000 00000000 00000000 03000200 ................ 0x00000050 00000000 00000000 00000000 00000000 ................ 0x00000060 11000000 00000100 00000000 00000000 ................ 0x00000070 00000000 00000000 1d000000 0000f1ff ................ 0x00000080 0d000000 00000000 00000000 00000000 ................ 0x00000090 2d000000 10000200 00000000 00000000 -............... 0x000000a0 00000000 00000000 ........

Las entradas son de tipo:

typedef struct { Elf64_Word st_name; unsigned char st_info; unsigned char st_other; Elf64_Half st_shndx; Elf64_Addr st_value; Elf64_Xword st_size; } Elf64_Sym;

Al igual que en la tabla de sección, la primera entrada es mágica y se establece en valores sin sentido fijos.

STT_FILE

La entrada 1 tiene ELF64_R_TYPE == STT_FILE . ELF64_R_TYPE continúa dentro de st_info .

Análisis de bytes:

  • 10 8: st_name = 01000000 = carácter 1 en el .strtab , que hasta el siguiente /0 hace hello_world.asm

    El enlazador puede utilizar este archivo de información para decidir qué secciones de segmento van.

  • 10 12: st_info = 04

    Bits 0-3 = ELF64_R_TYPE = Tipo = 4 = STT_FILE : el propósito principal de esta entrada es usar st_name para indicar el nombre del archivo que generó este archivo objeto.

    Bits 4-7 = ELF64_ST_BIND = ELF64_ST_BIND = 0 = STB_LOCAL . Valor requerido para STT_FILE .

  • 10 13: st_shndx = st_shndx sección de tabla de símbolos Índice = f1ff = SHN_ABS . Necesario para STT_FILE .

  • 20 0: st_value = 8x 00 : requerido para el valor de STT_FILE

  • 20 8: st_size = 8x 00 : sin tamaño asignado

Ahora desde la readelf , interpretamos a los demás rápidamente.

STT_SECTION

Hay dos entradas de este tipo, una apuntando a .data y la otra a .text (índices de sección 1 y 2 ).

Num: Value Size Type Bind Vis Ndx Name 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 3: 0000000000000000 0 SECTION LOCAL DEFAULT 2

TODO cuál es su propósito?

STT_NOTYPE

Luego vienen los símbolos más importantes:

Num: Value Size Type Bind Vis Ndx Name 4: 0000000000000000 0 NOTYPE LOCAL DEFAULT 1 hello_world 5: 000000000000000d 0 NOTYPE LOCAL DEFAULT ABS hello_world_len 6: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 2 _start

hello_world cadena hello_world está en la sección .data (índice 1). Su valor es 0: apunta al primer byte de esa sección.

_start está marcado con visibilidad GLOBAL desde que escribimos:

global _start

en NASM. Esto es necesario ya que debe verse como el punto de entrada. A diferencia de C, por defecto las etiquetas NASM son locales.

SHN_ABS

hello_world_len apunta al st_shndx == SHN_ABS == 0xF1FF especial st_shndx == SHN_ABS == 0xF1FF .

Se elige 0xF1FF para no entrar en conflicto con otras secciones.

st_value == 0xD == 13 que es el valor que hemos almacenado allí en el ensamblaje: la longitud de la cadena Hello World! .

Esto significa que la reubicación no afectará este valor: es una constante.

Esta es una pequeña optimización que nuestro ensamblador hace por nosotros y que tiene soporte ELF.

Si hubiéramos utilizado la dirección de hello_world_len cualquier lugar, el ensamblador no habría podido marcarla como SHN_ABS , y el enlazador tendría un trabajo de reubicación adicional más adelante.

SHT_SYMTAB en el ejecutable

Por defecto, NASM también coloca un .symtab en el ejecutable.

Esto solo se usa para la depuración. Sin los símbolos, estamos completamente ciegos y debemos aplicar ingeniería inversa a todo.

Puede objcopy con objcopy , y el ejecutable seguirá ejecutándose. Dichos ejecutables se denominan ejecutables eliminados .

.strtab

Contiene cadenas para la tabla de símbolos.

Esta sección tiene sh_type == SHT_STRTAB .

Se señala mediante sh_link == 5 de la sección .symtab .

readelf -x .strtab hello_world.o

Da:

Hex dump of section ''.strtab'': 0x00000000 0068656c 6c6f5f77 6f726c64 2e61736d .hello_world.asm 0x00000010 0068656c 6c6f5f77 6f726c64 0068656c .hello_world.hel 0x00000020 6c6f5f77 6f726c64 5f6c656e 005f7374 lo_world_len._st 0x00000030 61727400 art.

Esto implica que es una limitación de nivel ELF que las variables globales no pueden contener caracteres NUL.

.rela.text

Tipo de sección: sh_type == SHT_RELA .

Nombre común: sección de reubicación .

.rela.text contiene datos de reubicación que indican cómo se debe modificar la dirección cuando se vincula el ejecutable final. Esto apunta a bytes del área de texto que deben modificarse cuando sucede que la vinculación apunta a las ubicaciones de memoria correctas.

Básicamente, traduce el texto del objeto que contiene la dirección del marcador de posición 0x0:

a: 48 be 00 00 00 00 00 movabs $0x0,%rsi 11: 00 00 00

al código ejecutable real que contiene el 0x6000d8 final:

4000ba: 48 be d8 00 60 00 00 movabs $0x6000d8,%rsi 4000c1: 00 00 00

Fue señalado por sh_info = 6 de la sección .symtab .

readelf -r hello_world.o da:

Relocation section ''.rela.text'' at offset 0x3b0 contains 1 entries: Offset Info Type Sym. Value Sym. Name + Addend 00000000000c 000200000001 R_X86_64_64 0000000000000000 .data + 0

La sección no existe en el ejecutable.

Los bytes reales son:

00000370 0c 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 |................| 00000380 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

La struct representada es:

typedef struct { Elf64_Addr r_offset; Elf64_Xword r_info; Elf64_Sxword r_addend; } Elf64_Rela;

Entonces:

  • 370 0: r_offset = 0xC: dirección en el .text cuya dirección modificará esta reubicación

  • 370 8: r_info = 0x200000001. Contiene 2 campos:

    • ELF64_R_TYPE = 0x1: el significado depende de la arquitectura exacta.
    • ELF64_R_SYM = ELF64_R_SYM : índice de la sección a la que apunta la dirección, entonces .data que está en el índice 2.

    El R_X86_64_64 AMD64 dice que el tipo 1 se llama R_X86_64_64 y que representa la operación S + A donde:

    • S : el valor del símbolo en el archivo objeto, aquí 0 porque movabs $0x0,%rsi a 00 00 00 00 00 00 00 00 de movabs $0x0,%rsi
    • A : el agregado, presente en el campo r_added

    Esta dirección se agrega a la sección en la que opera la reubicación.

    Esta operación de reubicación actúa en un total de 8 bytes.

  • 380 0: r_addend = 0

Entonces, en nuestro ejemplo, concluimos que la nueva dirección será: S + A = .data + 0 y, por lo tanto, lo primero en la sección de datos.

Tabla de encabezado del programa

Solo aparece en el ejecutable.

Contiene información sobre cómo se debe colocar el ejecutable en la memoria virtual del proceso.

El ejecutable es generado a partir de archivos de objetos por el vinculador. Los principales trabajos que realiza el vinculador son:

  • determine qué secciones de los archivos de objeto irán a qué segmentos del ejecutable.

    En Binutils, esto se reduce a analizar un script vinculador y a lidiar con un montón de valores predeterminados.

    Puede obtener el script de enlazador utilizado con ld --verbose , y configurar uno personalizado con ld -T .

  • hacer reubicación en secciones de texto. Esto depende de cómo se guardan las múltiples secciones en la memoria.

readelf -l hello_world.out da:

Elf file type is EXEC (Executable file) Entry point 0x4000b0 There are 2 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000000d7 0x00000000000000d7 R E 200000 LOAD 0x00000000000000d8 0x00000000006000d8 0x00000000006000d8 0x000000000000000d 0x000000000000000d RW 200000 Section to Segment mapping: Segment Sections... 00 .text 01 .data

En el encabezado ELF, e_phoff , e_phnum y e_phentsize nos dijeron que hay 2 encabezados de programa, que comienzan en 0x40 y tienen 0x38 longitud de 0x38 bytes cada uno, por lo que son:

00000040 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 |................| 00000050 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00 |..@.......@.....| 00000060 d7 00 00 00 00 00 00 00 d7 00 00 00 00 00 00 00 |................| 00000070 00 00 20 00 00 00 00 00 |.. ..... |

y:

00000070 01 00 00 00 06 00 00 00 | ........| 00000080 d8 00 00 00 00 00 00 00 d8 00 60 00 00 00 00 00 |..........`.....| 00000090 d8 00 60 00 00 00 00 00 0d 00 00 00 00 00 00 00 |..`.............| 000000a0 0d 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 |.......... .....|

Estructura representada http://www.sco.com/developers/gabi/2003-12-17/ch5.pheader.html :

typedef struct { Elf64_Word p_type; Elf64_Word p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; Elf64_Xword p_filesz; Elf64_Xword p_memsz; Elf64_Xword p_align; } Elf64_Phdr;

Desglose del primero:

  • 40 0: p_type = 01 00 00 00 = PT_LOAD : TODO. Creo que significa que en realidad se cargará en la memoria. Otros tipos pueden no ser necesariamente.
  • 40 4: p_flags = 05 00 00 00 = ejecutar y leer permisos, no escribir TODO
  • 40 8: p_offset = 8x 00 TODO: ¿qué es esto? Parecen desplazamientos desde el comienzo de los segmentos. ¿Pero esto significaría que algunos segmentos están entrelazados? Es posible jugar un poco con él: gcc -Wl,-Ttext-segment=0x400030 hello_world.c
  • 50 0: p_vaddr = 00 00 40 00 00 00 00 00 : dirección de memoria virtual inicial para cargar este segmento
  • 50 8: p_paddr = 00 00 40 00 00 00 00 00 : dirección física inicial para cargar en la memoria. Solo es importante para los sistemas en los que el programa puede configurar su dirección física. De lo contrario, como en el Sistema V, como los sistemas, puede ser cualquier cosa. NASM parece simplemente copiar p_vaddrr
  • 60 0: p_filesz = d7 00 00 00 00 00 00 00 : TODO vs p_memsz
  • 60 8: p_memsz = d7 00 00 00 00 00 00 00 : TODO
  • 70 0: p_align = 00 00 20 00 00 00 00 00 : 0 o 1 significa que no se requiere alineación TODO ¿qué significa eso? de lo contrario redundante con otros campos

El segundo es análogo.

Entonces la:

Section to Segment mapping:

La sección de readelf nos dice que:

  • 0 es el segmento .text . Ajá, por eso es ejecutable y no escribible
  • 1 es el segmento .data .

Sólo curioso. Obviamente, esta no es una muy buena solución para la programación real, pero digamos que quería hacer un ejecutable en Bless (un editor hexadecimal).

Mi arquitectura es x86. ¿Qué es un programa muy simple que puedo hacer? Un hola mundo? ¿Un bucle infinito? Similar a this pregunta, pero en Linux.


Como mencioné en mi comentario, esencialmente escribirás tu propio encabezado elfo para el ejecutable eliminando las secciones innecesarias. Todavía hay varias secciones requeridas. La documentación en Muppetlabs-TinyPrograms hace un trabajo justo explicando este proceso. Por diversión, aquí hay un par de ejemplos:

El equivalente de / bin / true (45 bytes):

00000000 7F 45 4C 46 01 00 00 00 00 00 00 00 00 00 49 25 |.ELF..........I%| 00000010 02 00 03 00 1A 00 49 25 1A 00 49 25 04 00 00 00 |......I%..I%....| 00000020 5B 5F F2 AE 40 22 5F FB CD 80 20 00 01 |[_..@"_... ..| 0000002d

Tu clásico ''¡Hola Mundo!'' (160 bytes):

00000000 7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00 |.ELF............| 00000010 02 00 03 00 01 00 00 00 74 80 04 08 34 00 00 00 |........t...4...| 00000020 00 00 00 00 00 00 00 00 34 00 20 00 02 00 28 00 |........4. ...(.| 00000030 00 00 00 00 01 00 00 00 74 00 00 00 74 80 04 08 |........t...t...| 00000040 74 80 04 08 1f 00 00 00 1f 00 00 00 05 00 00 00 |t...............| 00000050 00 10 00 00 01 00 00 00 93 00 00 00 93 90 04 08 |................| 00000060 93 90 04 08 0d 00 00 00 0d 00 00 00 06 00 00 00 |................| 00000070 00 10 00 00 b8 04 00 00 00 bb 01 00 00 00 b9 93 |................| 00000080 90 04 08 ba 0d 00 00 00 cd 80 b8 01 00 00 00 31 |...............1| 00000090 db cd 80 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a |...Hello world!.| 000000a0

No olvides hacerlos ejecutables ...