HackIt!2013. Level 7. Old School Spectrum (y II)

Fuse_pokedAnalicemos el código. La línea 15 define la función módulo (no existente en el Basic del Spectrum). La línea 20 declara e inicializa ciertas variables que luego veremos. La línea 30 pide al usuario un password (Code). La línea 40 comprueba que la longitud del pass introducido sea al menos igual a 6 (las letras de «HackIt»). Si fuera menor, concatena el pass hasta que se cumpla la condición. En la línea 50 comienza la chicha. Iteraremos 6 veces. En cada iteración, la línea 55 del programa cambiará el contenido de la línea 60, sobre-escribiendo en memoria 3 posiciones (como si fuera el código de un virus polimórfico). ¿Cómo sabemos qué es lo que escriben esos POKE y dónde? Poniendo un STOP justo al comienzo de la línea 70 (ver figura de la izquierda).

10 REM PoliMorph
15 DEF FN m(a,b) = a- (INT (a/b)*b) : REM a Mod b
20 LET pos1=00053: LET pos2=00069: LET pos3=24000: LET x1=181: LET x2=42: LET x3=45
30 LET n$="HackIt": INPUT "Code:";c$: LET x$=""
40 IF LEN c$< LEN n$ THEN LET c$=c$+c$: GOTO 40 
50 FOR g=1 TO LEN n$ 
55 POKE pos1,x1: POKE pos2,x2: POKE pos3,x3 
60 LET cod = INT (98*ATN (g/ LEN n$)) + CODE c$(g)/0 + g 
70 LET x1=x1+1: IF x1>183 THEN LET x1=181
80 LET x2=x2+1: IF x2>47 THEN LET x2=42
90 LET x3=x3+2: IF x3>45 THEN LET x3=43
110 LET x$=x$ + CHR$ (32 + FN m(cod, 95))
120 NEXT g: PRINT "Decoded:";x$
130 IF n$ <> x$ THEN PRINT "Wrooonnggg!": FOR g=pos1-11 TO pos3+2: POKE g,48+ INT (RND*10): NEXT g: STOP
140 PRINT "Right!, here goes the rest..."

Vemos que tras la ejecución, la línea 60 ha cambiado:

Antes:
60 LET cod = INT (98*ATN (g/ LEN n$)) + CODE c$(g)/0 + g
Ahora, tras los POKE(pos_memoria, valor):
60 LET cod = INT (98*ASN (g/ LEN n$)) + CODE c$(g)*0 - g

¿Por qué? Las posiciones de memoria las calculó pacientemente el autor de la prueba (thEpOpE). ¿Y el valor? Podemos ayudarnos de esta tabla de mnemónicosPor ejemplo, el primer POKE(00053, 181) cambia la posición 00053 (donde teníamos un ATN -arcotangente-) por el operador ASN -arcoseno-, dado que al código 181 le corresponde el operador ASN según la tabla indicada. La división por 0 (ilegal), ha cambiado a multiplicación por 0 (legal) con POKE(00069, 42). El «+ g» ha cambiado a «- g» con el POKE(24000, 45). El 45 corresponde al operador «-«. Lo que no tengo claro es cómo demonios la dirección 24000 corresponde a esa sección del código (seguro que thEpOpE nos ilumina en los comentarios 🙂

Los valores 181, 42 y 45 corresponden a las variables x1, x2 y x3 definidas en la línea 20. En cada vuelta del bucle, x1, x2 y x3 cambiarán, por lo que los POKE modificarán la línea 60 en cada vuelta. La tabla enlazada nos ayudará a conocer qué hace en cada vuelta. Esa línea 60 va modificando cada letra de nuestro password (dejándola en la variable cod). En la línea 110 se calcula el carácter asociado al código ASCII de 32 + cod módulo 95. Esto nos dará un carácter imprimible entre ASCII(32) y ASCII (32+94). Ese carácter, en cada vuelta, debe coincidir con las letras del string «HackIt». ¡Un level muy trabajado!.

Algunos comentarios finales. Me ha gustado el efecto de bombardear la memoria y cambiar el código en caso de introducir un pass incorrecto (línea 130). También nos dejó ojipláticos al comienzo la división por cero de la línea 60. Cuando vimos los POKE de la 55 supusimos que corregiría ese bug intencionado. También nos costó encontrar un emulador que cargara el código en BASIC. No sé si fue un efecto buscado o no…

Finalmente, el level tenía un bug no intencionado. La clave final aparece en los strings del binario… nos dimos cuenta tras pasar horas y horas con la prueba, encontrar la solución de forma ortodoxa y recordar que ese string lo habíamos visto antes en algún sitio O:-)

Y con esto, llegamos al level 8, donde nos espera un interesante intérprete RPN del mismo autor…

6 comentarios en «HackIt!2013. Level 7. Old School Spectrum (y II)»

  1. Estaba deseando que publicarais este nivel, porque a mí me tuvo dando vueltas a la cabeza unos días.

    Durante el reto, nosotros parcheamos el poke que machacaba el programa para poder probar repetidas veces sin mucha historia. El caso es que pese a todo, las cosas no nos acababan de cuadrar por esas divisiones entre 0 y otras cosas raras que veíamos.

    Tenemos que agradecer a thEpOpE que en sus dos retos dejó la posibilidad de pasarlos en plan lamer, y nosotros nunca dejamos pasar esas oportunidades, así que básicamente lo resolvimos por fuerza bruta carácter a carácter.

    Analizándolo posteriormente, hemos visto que los operadores inmediatos que vemos aparecen siempre en el programa seguidos de 0E0000 y un word con el valor real. Realmente el 00053 era un 24053, el 0 de la división por 0 es un 1… Con los valores reales el programa cobra sentido y se puede resolver de una forma algo más pro:

    http://codepad.org/tQclfLGO

    Rebuscando un poco sobre este tema, encontré el programa ZX-Editor, que básicamente resuelve todos los problemas él sólo para los que somos windows clickers:

    http://www.zxmodules.de/

    El programa no sólo nos permite ver el código y los valores reales (habla de caracteres de control, creo recordar), sino que también podemos ver el bloque de datos en el que se ve que al salvar el programa, ha guardado también las variables que en ese momento tenía en memoria, mostrando de forma obvia que esa cadena aparentemente sin sentido estaba asignada a c$. Dado este efecto no deseado, quizá la forma más fácil de resolverlo era:

    merge «hackme.bas»
    print c$

  2. Buen análisis ramandi. De hecho la explicación de por qué los valores que se ven en el listado basic no son los que realmente ejecuta el basic es un truco antiguo, muy documentado, y que está mucho más documentado en el manual del zx spectrum +2 y +3.

    Realmente es por la forma en que Spectrum guarda los programas Basic en memoria: guarda los valores numéricos en su representación ASCII, tal y como los teclea el usuario, y su valor en binario (asume que todos son valores en coma flotante); este último cálculo solo se hace 1 vez, en el editor, cuando se introduce la línea. Esto que parece un consumo de memoria, es realmente una optimización de velocidad, ya que se interpreta el número 1 sola vez.

    Cuando se ejecuta la instrucción se usa el valor binario ya calculado cuando se introduce la linea basic en el editor. Cuando se hace un «list» se muestra la representación ascii que tecleó el usuario originalmente. Si se edita cualquier linea donde se ha usado este truco el número se vuelve a evaluar, y se perdería la ejecución normal del programa; ahí por ejemplo empezaría a dar problemas la división entre 0.

    Lo de las variables… es cierto, y fue un error común por el uso de dsk en lugar de tap, además de que tenía que haber hecho un CLEAR antes de hacer el SAVE 😉 Estuve pensando en poner un truco antimerge, pero dejé incluso que entrara en un bucle infinito si no se teclea clave alguna, para poder detener el programa. El interés del nivel era más por el lado de que lo que se ejecuta no es lo que se ve, y la dificultad que entraña a veces incluso tener el código fuente. La variable c$ efectivamente se quedó con el valor, porque probé que funcionara correctamente antes de enviarselo a marcan :p

    Por cierto, un POKE en spectrum en los primeros 16kb de memoria, no produce nada (a menos que se modifique por hardware), ya que es zona de ROM, y los pokes en zonas tan bajas también eran una pista. El hecho de poder inyectar código fue otra puerta abierta que dejé adrede. Y la división por 0 consiguió lo que pretendía, que os pusiera ya con la mosca detrás de la oreja. Lo mismo los Rem…

    Para el año que viene…una intro en spectrum :p

  3. Capítulo del manual que explica y documenta cómo se almacena un programa basic en memoria.

    http://www.worldofspectrum.org/ZXBasicManual/zxmanchap24.html

    Las variables de sistema están en el siguiente capítulo, la variable PROG nos indica dónde empieza a almacenarse el programa Basic.

    http://www.worldofspectrum.org/ZXBasicManual/zxmanchap25.html

    Sobre códigos de control, números, y otras características…

    http://centrodeartigos.com/articulos-educativos/article_4696.html

    El manual de Taper, una utilidad muy usada en msdos para gestionar archivos TAP de Spectrum, hace referencia a esta técnica, usada en los años 80 por protecciones comerciales
    http://www.worldofspectrum.org/taperdoc.html#i110

    Una búsqueda a google con «dsk editor» nos ira directamente al paquete ZX, y con ZXPreview podemos visualizar Basic omitiendo todos los códigos de control.

    Finalmente un enlace a esas partes del manual, en pdf y en español
    http://manuales.speccy.org/manual_usuario_plus2/capitulo_8_parte_IV.pdf (a partir de la pagina numerada como 163)

  4. Muchísimas gracias por las referencias!!
    Me falta algo de base en el funcionamiento de la arquitectura, pero he aprendido varias cosas, y ya sé a dónde acudir para ir completando los huecos :).

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.