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
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Comprobación aritmética | unas operaciones sobre un número y una comparación final | |
| Operación inversa | la que deshace otra: la resta deshace la suma | sub 8 |
| XOR | una operación bit a bit que se deshace aplicándola otra vez | xor 85 |
| Hexadecimal | una forma de escribir números en base 16, con 0x delante | 0x55 |
| Valor inmediato | un 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
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
objdump -d /tmp/bono --disassemble=comprobar -M intel | grep -E "sub|xor|add|cmp" | cut -f3Se 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ón | Número en hexadecimal | Número en decimal |
|---|---|---|
| sub | 0x1e | 30 |
| xor | 0x55 | 85 |
| add | 0x8 | 8 |
| cmp | 0xc8 | 200 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Comprueba los cuatro números
printf '%d %d %d %d\n' 0x1e 0x55 0x8 0xc8Se 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 deshace | Cálculo | Resultado |
|---|---|---|
| Valor final | el cmp compara con 200 | 200 |
| add 8 | 200 - 8 | 192 |
| xor 85 | 192 xor 85 | 149 |
| sub 30 | 149 + 30 | 179 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Haz la cuenta al revés
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
/tmp/bono 179Se 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
/tmp/bono 180Se 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
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ú.