¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Deshacer una comprobación aritmética

Después de esta lección: Puedo deshacer una comprobación de suma, xor y resta para hallar el número que la cumple.

Lección de 55 min7 comandosLaboratorio de ingeniería inversa: leer programas con gdb, radare2, strace y ltrace en una VM

Qué es una comprobación aritmética

Una comprobación aritmética es un tramo de programa que toma un número, le aplica unas operaciones y mira si el resultado es el esperado. Para hallar el número que la cumple basta con deshacer las operaciones en orden inverso.

Un programa de bonos de una tienda hace justo eso con el número de un vale.

Al final de esta lección

Nota

Puedo deshacer una comprobación de suma, xor y resta para hallar el número que la cumple.

Palabras clave

PalabraQué significaEjemplo
Comprobación aritméticaunas operaciones sobre un número y una comparación final
Operación inversala que deshace otra: la resta deshace la sumasub 8
XORuna operación bit a bit que se deshace aplicándola otra vezxor 85
Hexadecimaluna forma de escribir números en base 16, con 0x delante0x55
Valor inmediatoun número escrito dentro de la propia instrucción

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Cada paso es una instrucción con un número

En el ensamblador, la comprobación se lee paso a paso: sub, xor, add y al final cmp. Cada instrucción lleva su número escrito dentro, en hexadecimal.

El primer Run crea bono, un programa de ejemplo nuestro que comprueba el número de un vale. No es uno de los de la práctica del laboratorio, que tienen otras operaciones y otro número en cada copia.

Crea el programa de ejemplo bono

Comando
printf '#include <stdio.h>\n#include <stdlib.h>\nstatic int comprobar(unsigned n) {\n    unsigned x = n;\n    x = x - 30;\n    x = x ^ 85;\n    x = x + 8;\n    return x == 200;\n}\nint main(int argc, char **argv) {\n    if (argc == 2 && comprobar((unsigned)strtoul(argv[1], 0, 10))) { puts("Bono valido"); return 0; }\n    puts("Bono no valido");\n    return 1;\n}\n' | gcc -O0 -no-pie -x c -o /tmp/bono -

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Lee los cuatro pasos de la comprobación

Comando
objdump -d /tmp/bono --disassemble=comprobar -M intel | grep -E "sub|xor|add|cmp" | cut -f3

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Los pasos, con sus números en decimal

InstrucciónNúmero en hexadecimalNúmero en decimal
sub0x1e30
xor0x5585
add0x88
cmp0xc8200

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Comprueba los cuatro números

Comando
printf '%d %d %d %d\n' 0x1e 0x55 0x8 0xc8

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Se deshace desde la última operación

Para hallar el número, empieza por el valor final y aplica la operación contraria, de abajo arriba. add 8 se deshace con una resta de 8. sub 30 se deshace con una suma de 30.

xor 85 se deshace con xor 85: aplicarlo dos veces devuelve el valor de partida. La tabla lo hace con el programa de ejemplo.

Ejemplo resuelto: de 200 al número del vale

Paso que se deshaceCálculoResultado
Valor finalel cmp compara con 200200
add 8200 - 8192
xor 85192 xor 85149
sub 30149 + 30179

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Haz la cuenta al revés

Comando
echo $(( ((200 - 8) ^ 85) + 30 ))

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Prueba el número en el programa

Comando
/tmp/bono 179

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Prueba el número siguiente

Comando
/tmp/bono 180

Se 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 deshace las operaciones en el mismo orden en que aparecen. La última operación del programa es la primera que hay que deshacer. Si empiezas por la primera, el número que obtienes no pasa la comprobación.

Con 32 bits, los números dan la vuelta

El número del programa es de 32 bits sin signo. No cabe nada mayor que 4294967295. Si una resta baja de cero, el resultado vuelve a empezar por arriba.

Con 5 - 8 no sale -3, sino 4294967293. Al deshacer, hay que usar el mismo ajuste: un resto de 4294967296. El Run lo comprueba con & 4294967295.

Mira cómo da la vuelta 5 - 8

Comando
echo $(( (5 - 8) & 4294967295 ))

Se 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

Imagina que bono restara 40 en lugar de 30, con el mismo xor, la misma suma y el mismo 200 final. Calcula a mano qué número del vale sería válido.

Después explica por qué una comprobación así no protege nada. Quien lee las instrucciones recupera el número igual que tú.

Sobre este laboratorio y su código: Laboratorio de ingeniería inversa: leer programas con gdb, radare2, strace y ltrace en una VM

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.