Saltar a contenido

Assembling & Disassembling

Ahora que entendemos la estructura básica y los elementos de un archivo Assembly, podemos comenzar a ensamblarlo usando la herramienta nasm. Toda la estructura del archivo de ensamblado que aprendimos en la sección anterior se basa en la estructura del archivo nasm. Al ensamblar nuestro código con nasm, este comprende las distintas partes del archivo y luego las ensambla correctamente para que puedan ejecutarse durante el tiempo de ejecución.

Después de ensamblar nuestro código con nasm, podemos enlazarlo usando ld para utilizar varias características y bibliotecas del sistema operativo.


Assembling

Primero, copiaremos el siguiente código en un archivo llamado helloWorld.s.

Nota: los archivos de ensamblado suelen usar las extensiones .s o .asm. Usaremos .s en este módulo.

No es necesario seguir utilizando tabulaciones para separar las partes de un archivo de ensamblado, ya que esto fue solo para fines demostrativos. Podemos escribir el siguiente código en nuestro archivo helloWorld.s:

global _start

section .data
    message db "Hello HTB Academy!"
    length equ $-message

section .text
_start:
    mov rax, 1
    mov rdi, 1
    mov rsi, message
    mov rdx, length
    syscall

    mov rax, 60
    mov rdi, 0
    syscall

Observa cómo usamos equ para calcular dinámicamente la longitud de message, en lugar de usar un valor estático como 18. Esto será muy útil más adelante. Una vez hecho esto, ensamblaremos el archivo usando nasm con el siguiente comando:

nasm -f elf64 helloWorld.s

Nota: El flag -f elf64 se usa para indicar que queremos ensamblar un código en ensamblador de 64 bits. Si quisiéramos ensamblar un código de 32 bits, usaríamos -f elf.

Este comando debería generar un archivo objeto helloWorld.o, que se ensambla en código máquina junto con los detalles de todas las variables y secciones. Sin embargo, este archivo aún no es ejecutable.


Linking

El paso final es enlazar nuestro archivo usando ld. El archivo objeto helloWorld.o, aunque ensamblado, aún no puede ejecutarse. Esto se debe a que muchas referencias y etiquetas utilizadas por nasm deben resolverse en direcciones reales, además de enlazar el archivo con varias bibliotecas del sistema operativo que puedan ser necesarias.

Es por esto que un binario en Linux se llama ELF, que significa Executable and Linkable Format. Para enlazar un archivo usando ld, podemos utilizar el siguiente comando:

ld -o helloWorld helloWorld.o

Nota: si estuviéramos ensamblando un binario de 32 bits, necesitaríamos agregar el flag '-m elf_i386'.

Una vez que enlazamos el archivo con ld, deberíamos tener el archivo ejecutable final:

./helloWorld
Hello HTB Academy!

Hemos ensamblado y enlazado con éxito nuestro primer archivo de ensamblado. Estaremos ensamblando, enlazando y ejecutando nuestro código frecuentemente a lo largo de este módulo, así que construyamos un simple script en bash para hacerlo más fácil:

#!/bin/bash

fileName="${1%%.*}" # remove .s extension

nasm -f elf64 ${fileName}".s"
ld ${fileName}".o" -o ${fileName}
[ "$2" == "-g" ] && gdb -q ${fileName} || ./${fileName}

Ahora podemos escribir este script en assembler.sh, darle permisos ejecutables con chmod +x y luego ejecutarlo en nuestro archivo de ensamblado. Ensamblará, enlazará y ejecutará el archivo:

./assembler.sh helloWorld.s
Hello HTB Academy!

¡Genial! Antes de continuar, desensamblemos y examinemos nuestros archivos para aprender más sobre el proceso que acabamos de realizar.


Disassembling

Para desensamblar un archivo, usaremos la herramienta objdump, que vuelca el código de máquina de un archivo e interpreta las instrucciones en ensamblador de cada código hexadecimal. Podemos desensamblar un binario usando el flag -D.

Nota: también utilizaremos el flag -M intel, para que objdump escriba las instrucciones en la sintaxis Intel, que es la que estamos usando, como discutimos anteriormente.

Comencemos desensamblando nuestro archivo ejecutable final ELF:

objdump -M intel -d helloWorld

helloWorld:     file format elf64-x86-64

Disassembly of section .text:

0000000000401000 <_start>:
  401000:   b8 01 00 00 00        mov    eax,0x1
  401005:   bf 01 00 00 00        mov    edi,0x1
  40100a:   48 be 00 20 40 00 00  movabs rsi,0x402000
  401011:   00 00 00
  401014:   ba 12 00 00 00        mov    edx,0x12
  401019:   0f 05                 syscall
  40101b:   b8 3c 00 00 00        mov    eax,0x3c
  401020:   bf 00 00 00 00        mov    edi,0x0
  401025:   0f 05                 syscall

Podemos ver que nuestro código en ensamblador original se conserva en gran medida, con el único cambio siendo 0x402000 utilizado en lugar de la variable message y reemplazando la constante length con su valor de 0x12. También vemos que nasm cambió eficientemente nuestros registros de 64-bit a subregistros de 32-bit donde fue posible, para usar menos memoria, como cambiar mov rax, 1 a mov eax,0x1.

Si quisiéramos mostrar solo el código en ensamblador, sin el código de máquina ni las direcciones, podríamos agregar los flags --no-show-raw-insn --no-addresses, de la siguiente manera:

objdump -M intel --no-show-raw-insn --no-addresses -d helloWorld

helloWorld:     file format elf64-x86-64

Disassembly of section .text:

<_start>:
        mov    eax,0x1
        mov    edi,0x1
        movabs rsi,0x402000
        mov    edx,0x12
        syscall
        mov    eax,0x3c
        mov    edi,0x0
        syscall

Nota: Ten en cuenta que objdump ha cambiado la tercera instrucción a movabs. Esto es lo mismo que mov, por lo que en caso de necesitar reensamblar el código, puedes cambiarlo nuevamente a mov.

El flag -d solo desensamblará la sección .text de nuestro código. Para volcar cualquier cadena, podemos usar el flag -s y agregar -j .data para examinar solo la sección .data. Esto significa que tampoco necesitamos agregar -M intel. El comando final es el siguiente:

objdump -sj .data helloWorld

helloWorld:     file format elf64-x86-64

Contents of section .data:
 402000 48656c6c 6f204854 42204163 6164656d  Hello HTB Academ
 402010 7921                                 y!

Como podemos ver, la sección .data contiene efectivamente la variable message con la cadena Hello HTB Academy!. Esto debería darnos una mejor idea de cómo nuestro código fue ensamblado en código de máquina y cómo se ve después de ensamblarlo. A continuación, repasaremos los conceptos básicos de la depuración de código, que es una habilidad crítica que necesitamos aprender.