Qué es el ensamblador
El ensamblador son las instrucciones del procesador escritas con nombres que una persona puede leer. Saber leer unas pocas líneas de ensamblador x86-64 es la base de la ingeniería inversa, porque un binario solo trae eso.
Una instrucción como cmp compara dos valores. La siguiente, como jne, decide a dónde ir según el resultado.
Al final de esta lección
Nota
Puedo leer unas pocas líneas de ensamblador x86-64 y decir qué hacen.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Registro | una casilla muy pequeña dentro del procesador | rax |
| Instrucción | una orden sencilla que ejecuta el procesador | mov |
| Operando | el valor o registro con el que trabaja una instrucción | |
| Bandera | un bit que recuerda el resultado de la última comparación | |
| Salto condicional | una instrucción que cambia de camino según una bandera | jne |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un registro es una casilla del procesador
Un registro es una casilla muy pequeña dentro del procesador. En x86-64 hay unos pocos con nombre, como rax o rdi. Las instrucciones los leen y los escriben mucho más deprisa que la memoria.
rax guarda 64 bits. Su mitad baja, eax, guarda 32 bits y es la que usa un int de C.
Los registros que más verás
| Registro | Para qué suele servir |
|---|---|
| rax o eax | el resultado de una función |
| rdi | el primer argumento de una llamada |
| rsi | el segundo argumento de una llamada |
| rsp | la cima de la pila |
| rbp | la base de la función en curso |
| rip | la instrucción que toca ejecutar |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
mov copia, add suma y sub resta
Usaremos la sintaxis Intel, que también usa radare2. Aquí el destino va primero. mov eax, 5 copia 5 en eax. add eax, 3 suma 3 a eax. sub eax, 1 resta 1.
Entre corchetes, como [rbp-0x4], hay una dirección de memoria: la casilla de una variable local, en la pila. Los números con 0x van en hexadecimal. El primer Run crea stock, un programa de ejemplo nuestro.
Crea el programa de ejemplo stock
printf '#include <stdio.h>\nint main(void) {\n int stock = 12;\n if (stock < 20) { puts("Hay que reponer"); return 1; }\n puts("Stock suficiente");\n return 0;\n}\n' | gcc -O0 -no-pie -x c -o /tmp/stock -Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Lee sus mov, cmp y jg en Intel
objdump -d /tmp/stock --disassemble=main -M intel | grep -E "mov|cmp|jg" | cut -f3Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Lee dos de ellas en sintaxis AT&T
objdump -d /tmp/stock --disassemble=main | grep -E "movl|cmpl" | cut -f3Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error frecuente
Advertencia
Mucha gente lee mov rax, rbx como «copia rax en rbx». En la sintaxis Intel el destino va primero, así que copia rbx en rax. En AT&T, la que usa objdump si no le pides otra, el orden es el contrario. Antes de leer, comprueba qué sintaxis tienes delante.
cmp compara y un salto decide
cmp a, b compara dos valores y anota el resultado en las banderas, sin guardar nada más. La instrucción siguiente decide. jne salta si no eran iguales, jg si el primero era mayor y jle si era menor o igual.
El compilador a veces invierte la condición. if (stock < 20) sale como cmp con 0x13 y jg: salta de largo cuando stock es mayor que 19.
Pasa 0x13 y 0xff a decimal
printf '%d %d\n' 0x13 0xffSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Para ir más lejos
Crea otra versión de stock en /tmp/stock2, con stock < 12 en lugar de stock < 20, y lee su cmp con el mismo Run.
Antes de ejecutarlo, escribe qué número hexadecimal esperas ver. Después explica con tus palabras por qué el compilador compara con un número menos que el del código.