-------------------------------------------------------------------- Solución al reto 1 de Panda (I parte, fracaso absoluto) Solución al reto 1 de Panda (II parte, FPqué!?) Solución al reto 1 de Panda (III parte, las dos comprobaciones) -------------------------------------------------------------------- En la anterior entrada hemos llegado a la conclusión de que las operaciones realizadas para verificar un password introducido no son triviales. Al menos sabemos el formato que debe tener: %d-%f-%f-%c. Y parece que se realizan operaciones con la FPU, algo no muy habitual.
Vamos a ojear que operaciones se hacen a continuación del scanf para intentar encontrar donde verifica si nuestro password es correcto o no.
Como vemos, se copian a los registros ecx y eax, los valores %d y %f1 introducidos y se llama a una función. Así que sabemos que posiblemente esa función haga algún tipo de comparaciones con esos dos valores.
Entramos a la función con F7 y observamos horrorizados el paisaje, el desensamblado.
Es una función muy larga y usa un montón de operaciones de la FPU. Yo, ciego por las prisas, no se me ocurre otra cosa que pensar que lo mejor sería ponerse manos a la obra y empezar a analizar todo lo que hace la función.
Ahí se puede observar como comenté gran parte de las instrucciones, bieeeen. Lo bueno es que aprendí algo sobre el funcionamiento de la FPU, lo malo es que tras analizar algunas instrucciones mas llegué a la conclusión de que eso no estaba haciendo nada con sentido. Reescribía valores obtenidos de anteriores operaciones, realizaba operaciones sin sentido o que provocaban algún tipo de error en la FPU, etc. Estaba perdiendo el tiempo.
Lo peor de todo es que no estaba seguro de si el programa hacia un uso “avanzado” de la FPU y no era capaz de comprender qué operaciones se estaban realizando.
Así que momentáneamente me desesperé y lo di por imposible. Y es en estos momentos en los que uno piensa, recapacita, pero no mucho.
Pensé en que lo mejor sería, como se dice en los primeros tutoriales de cracking, encontrar “el chico bueno” y “el chico malo”. El mensaje que nos diga que hemos superado la protección y el mensaje que nos diga que no lo hemos hecho. El segundo ya lo tenemos es el mensaje “bad!”. ¿Pero y el chico bueno? ¿Qué imprime el programa cuando introducimos un serial válido?, y lo mas importante, ¿cuándo y porqué lo hace?.
Umm, vamos a ver que APIs usa, quizás eso nos aclare un poco las ideas. Botón derecho sobre la zona central “Search for > Name (label) in current module”
Como ya sabíamos se usan las funciones printf y scanf. printf se usará para mostrar lo de “Password:”, a continuación se utiliza scanf para recoger lo que escribimos y de nuevo un printf para decirnos si es correcto o no.
Entonces busquemos todos los sitios donde se usa printf, en alguno de ellos se tendrá que imprimir un mensaje parecido a “Conseguido”. Clic derecho sobre printf, “Find references to import”.
Hay 3, ya que la última corresponde a la IAT. Pulsamos sobre cada referencia y vemos que parámetros se le pasan a printf en las 3 situaciones.
El primero estamos seguros que no es. Los otros dos pues depende de lo que se le valgan los parámetros LOCAL.3 y EAX en su debido momento. Si nos fijamos en el código y buscamos operaciones que modifiquen la variable LOCAL.3 vemos que solo se modifica al principio, asignándole el valor “bad!”.
Podemos verificar que en 403030 se encuentra la cadena “bad” y que casualidad, unos bytes mas adelante está la cadena “yes”, ¿será lo que buscamos?
Así que sabemos que el segundo printf imprime siempre “bad!”. Seguramente se llegue aquí después de alguna comprobación que determine que nuestro serial es incorrecto. De hecho un par de instrucciones antes del segundo printf hay una comparación apetitosa.
Si no se activa el flag zero en las operaciones anteriores al JNZ nos lleva al segundo printf que imprime siempre bad :( Mas adelante tendremos que revisar como evitar esto. pero antes vamos a echar un ojo al último printf.
Al tercer printf se le pasa el registro EAX, donde estará la dirección de lo que imprimirá. Como podemos ver en el código, EAX vale la suma de la dirección de la cadena “bad” + un desplazamiento sacado de LOCAL.4
Sabemos, porque lo vimos en el anterior, que la variable LOCAL.3 se mantiene siempre con la dirección de la cadena “bad”. Y que si la variable LOCAL.4 vale 0, se imprimirá “bad!”, pero si LOCAL.4 vale 4, se imprimiría “yes!”.
Resumiendo. Sabemos que hay una comprobación que si no se cumple nos llevará al segundo printf, mostrando “bad!”. Y que el tercer printf nos mostrará el mensaje “yes!” si conseguimos que LOCAL.4 valga 4. Ya tenemos mas o menos analizadas las condiciones necesarias para que un serial sea correcto, en la siguiente entrada veremos como conseguir que esto ocurra…
No hay comentarios:
Publicar un comentario