Diseccionando binarios - 0x04 Crackmes Hardcoded

Publicada en Publicada en Cracking, Reversing

Buenas a todos, continuamos con los Hardcoded ahora siendo este un poco más complejo que los del tutorial anterior.

Crackme Hardcoded 3

Pueden descargarse el Crackme aquí para que puedan ir practicando mientras siguen el tutorial:

crakmeeasy.exe

Primero antes de nada realizaremos un análisis estático con r2 viendo algunas instrucciones y así ir manejando poco a poco esta CLI aunque profundizaremos un poco más en la siguiente entrada. Realizamos una búsqueda de todas las Strings y no solo en .data con izz:

Localizamos cuatro Strings bastante interesantes, "Enter password...", "10445678951", "Correct!" y "Invalid!". La segunda de ellas es interesante a tener en cuenta y las dos últimas si es correcto la password o Serial que introduzcamos saltará un mensaje de correcto y sino, inválido. Con afl vemos si hay algúna api conocida que hayamos visto con anterioridad en el pasado tutorial:

sub.USER32.dll_GetDlgItemTextA_650

Este módulo guardará nuestro Serial que introduzcamos por teclado para realizar una serie de operaciones y comprobar si es correcto o no. En este caso Debuggeando es más sencillo ya que conocemos los valores que tomará los registros, pero primero antes de nada echemos un vistazo al código en assembly con r2.

Si sabemos y tenemos conocimiento de la api GetDlgItemTextA es interesante ir hacía la dirección de memoria después del call <api/módulo>. Esto es básicamente porque una vez ha recogido nuestros carácteres el programa tendrá que realizar una serie de operaciones para la comparación. Hacemos v y seguidamente p, y vamos a la dirección de memoria 401303.

Podemos apreciar como en la dirección 401303 mueve el valor 0 al contenido de la dirección [ebp-0x10], cuya dirección se encuentra en el Stack y seguidamente mueve al registro EAX una dirección de memoria que si vemos en la búsqueda que hicimos de referencias a cadenas corresponde con el comienzo de la String "10445678951". En la segunda columna vemos el contenido en hexadecimal.

Si queremos conocer el Volcado en hexadecimal y ASCII desde el modo de visualización v, podemos ver el contenido de las diferentes direcciones de memoria con sus offset correspondientes. Si nos vamos a la dirección de memoria 401222 y con la flecha de la izquierda para colocarnos exactamente en esa dirección, podemos apreciar el String en el Volcado:

Por lo tanto deducimos que en esa dirección de memoria almacena el String hasta el offset A. Radare2 nos especifica las referencias con DATA XREF a la dirección 40130F que corresponde a la dirección siguiente a 40130A cuya instrucción es: mov eax, 401222. Para asegurarnos usamos axt para ver si la dirección de memoria del String esta referenciado en algún sitio:

Podemos verificar la información con los valores en hexadecimal en la segunda columna acorde al valor en ASCII del String y como realiza operaciones lógicas XOR en la tercerca columna de instrucciones.

Con todas estas deducciones nos ha quedado claro el contenido o valor que tiene una dirección de memoria dada apoyándonos en la búsqueda de referencias a cadenas, al volcado hexadecimal y las referencias de datos. Estáticamente no podemos saber el valor de los registros, pero es interesante hacer un análisis para comprender mejor el assembly y también porque no ejecutamos el programa. Por supuesto también conocer cuando mueve direcciones de memoria o cuando su contenido, y en r2 es mediante el uso de corchetes ya que no conocemos el valor o contenido que tiene un registro. Hemos visto ya algún ejemplo, pero en la anterior instrucción mov eax, 0x401222 al estar solo la dirección de memoria lógicamente mueve al registro EAX dicha dirección y no el contenido.

En la dirección de memoria 40130F mueve el contenido del registro EAX al registro EDX, mov edx, dword [eax]. Radare2 nos especifica que es un tipo DWORD el contenido de dicho registro (4 Bytes).

Vimos ya en tutoriales pasados como la instrucción lea mueve la dirección de memoria y no el contenido, a pesar de usar corchetes mueve la dirección sin acceder al contenido de la misma porque en el fondo solo resuelve las operaciones dentro del corchete sin acceder al contenido y como EBP normalmente se usa como base de las variables y argumentos del Stack en cada función, lo que hace realmente es sumarle o restarle una constante al valor de EBP que apunta a una dirección del Stack tomada como base para dicha función.

Por eso el lea lo que hace realmente es resolver esa operación ebp-0x24 ya que EBP tiene una dirección del Stack que será la base en esta función, restándole 0x24 obtengo la dirección de dicha variable. En conclusión la instrucción lea la mayor parte de las veces se usa para obtener direcciones de variables o argumentos del Stack.

Hemos visto por encima algunas instrucciones básicas y búsqueda de referencias a cadenas con r2, aunque en la próxima entrada me centraré exclusivamente al análisis estático de un Crackme y con esta tool. Ahora vamos a realizar Debugging con x64dbg.

Abrimos con el Debugger y le damos a Ejecutar para situarse en el EntryPoint. Realizamos una búsqueda a referencias de cadenas, para ver los Strings:

Vemos también en las apis, e identificamos una conocida que vimos antes con radare2 y procedemos a colocar un Breakpoint y Ejecutamos.

Al ejecutar nos aparece la ventana del Serial e introducimos "naivenom" y le damos a Check. Ahora nuestro EIP apunta a la instrucción de la llamada a la api.

Presionamos F7 para entrar en la función y luego hasta Ret para salir de ella. Ahora el EIP apunta a la siguiente instrucción después del call, que corresponde a la dirección de retorno dónde tiene que continuar el programa una vez ejecute la api.

x64dbg nos muestra en la columna de la derecha del Desensamblador el contenido de una dirección de memoria correspondiente al pequeño análisis que hicimos anteriormente con r2.  Antes de llegar a llamar a otra api y sus correspondientes push a la pila, realiza una serie de instrucciones en la cual vamos a analizar detenidamente usando el Debugger y apuntando el valor que van tomando los registros para no perdernos y así entender mejor el assembly. Nose si habrán intentado a introducir ese número como Serial pero ya les digo de que no es.

Como podéis observar en la imagen, va a mover la dirección de memoria a EAX. En el cuadrado del x64dbg entre el Desensamblador y el Volcado, nos aclara realmente que es lo que va a hacer la siguiente instrucción a ejecutar, es decir, lo que apunta EIP. El registro EAX vale 8, pero cuando se ejecute esta instrucción el valor o contenido que tendrá este registro será la dirección de memoria y no su contenido, ya que, si fuese su contenido tanto en radare2 como en x64dbg nos lo indica con corchetes. Dicha dirección de memoria apunta a la String "10445678951" podemos verlo en los Registros una vez Ejecutemos con F7:

Si presionamos boton derecho al registro EAX y Mostrar en el Volcado:

En la siguiente instrucción,

Como EAX es una dirección de memoria y en este caso lo que quiere es mover a EDX el contenido de este registro y como sabemos lo que contiene "10445678951", EDX tendrá este valor en hexadecimal y siendo una DWORD moverá los primeros 4 Bytes. Sabemos que estos registros son de 32 Bits (4 Bytesx8). Presionamos F7 y observamos en el registro EDX como los números en hexadecimal estan colocados al revés.

En la instrucción,

Mueve el valor de EDX que son los 4 Bytes al contenido de [ebp-30] del Stack o también como viene en la aclaración [0240F7C0] en mi caso, en el vuestro puede ser otra dirección de memoria de la pila. Localizamos dicha memoria en el Stack y apreciamos como su contenido es 4, lo vemos también en el Volcado.

Y al ejecutar con F7 vemos dicho valor en la dirección de memoria del Stack.

Ahora en esta instrucción mueve los siguientes 4 Bytes de la String "10445678951" cuya expresión es [eax+4] a EDX,

"5678" = 35363738 hex. (Volcado). Presionamos F7 para verlo,

Y en la ventana de Registros nos aparece EDX  con su valor correspondiente al revés,

Y ahora los copia en el Stack justo a continuación (en el offset 4) de donde se copio lo anterior, siendo más exactos en la dirección [ebp-2C] o [0240F7C4],

Apreciamos lo dicho ahora mismo en el Volcado una vez hayamos ejecutado con F7,

Ahora copia los últimos 4 Bytes correspondientes a la String "10445678951" cuya expresión es [eax+8] a EAX,

Presionamos con F7 y allí esta el valor de EAX correspondiente a los 4 Bytes,

Y por último almacena el contenido seguidamente en el Stack siguiendo mismo procedimiento y a continuación del último offset usado. Antes de pushear, con la instrucción lea va a mover la dirección de memoria correspondiente a [ebp-24] a EAX. Insisto de que con lea mueve direcciones de memorias y no contenido, aunque veamos corchetes.

Ahora apreciamos como pushea una serie de valores que serán los argumentos de la función o api que va a llamar con call en la siguiente instrucción a ejecutar (EIP). Miramos el Stack para apreciar como apila esos argumentos,

Ejecutamos con F8 para ejecutar la api memset pero sin entrar en ella y en el Volcado apreciamos como relleno con "00" 8 Bytes desde la dirección de memoria del registro EAX (0240f7cc),

Y nuestra String "10445678951" justo delante del buffer de 8 Bytes que se ha reservado rellenandolo con ceros en el Stack. Ahora seguimos adelante Debuggeando hasta llegar a la api strlen.

Pushea el registro EAX cuyo valor es una dirección de memoria del Stack correspondiente al inicio de la String, y nos situamos con EIP en la llamada a la api con call.

Y el argumento pusheado en el Stack,

Al ejecutar la api con F8, nos devolverá la longitud de la String en el registro EAX. "0B"= 11 en decimal, corresponde a la longitud del String "10445678951".

En esta instrucción le resta uno a EAX, es decir 0B-1 y mueve directamente el valor con lea a EDX. Vemos el valor de este registro en el Stack una vez ejecutamos con F7,

Ahora compara con cmp EDX que vale 0A con el contenido de [ebx-10] o [0240F7E0] que es cero,

Debido al resultado de la comparación salta si es más bajo como resultado de la comparación. Si es más bajo el primer operando con respecto al segundo saltaría y se activa la CF=1,

Si queréis vemos un vistazo al modo gráfico para que os hagáis un esquema mental,

Una vez tomado el salto mueve el contenido de la dirección de memoria en el Stack correspondiente a nuestro Serial introducido "naivenom" tal como nos aclara x64dbg.

Y presionamos F7 y localizamos en el Stack nuestro String que introdujimos por teclado (Serial),

En la siguiente instrucción mueve a EDX el valor 0 y luego le suma el valor a EAX. Como este registro contiene nuestra String "naivenom" y también apreciamos en el gráfico un bucle, posiblemente sea un contador para posicionarse en cada Byte de nuestro serial que introdujimos.

Como se ha sumado cero, en la siguiente instrucción mueve un Byte siendo la "n" (la primera) en hexadecimal "6E". Si se hubiese sumando uno anteriormente pues sería la "a". Esto se realiza con la instrucción movsx considerándose el signo y como es positivo se rellena con ceros.

Ahora con lea, mueve el contenido de [edx-14] al registro EAX. Como en EDX no es una dirección de memoria apuntando a un contenido, pues directamente mueve el contenido en sí. Por lo tanto lo que hizo es coger el primer valor en hexadecimal de lo introducido por teclado y restarle 14 dando como resultado "Z" o "5A", enviandolo a EAX.

Seguidamente mueve con lea la dirección de memoria (0240F7C0) que apunta al comienzo del contenido de la String "10445678951" a EDX,

Y ya en la ultima instrucción que aparece más arriba, mueve a ECX el valor de [ebp-10] que corresponde a 0, cuando vuelva a hacer otra pasada para el siguiente Byte de nuestro String que introdujimos será 1,...y así. Por tanto se pasa al registro ECX el valor del contador.

Ahora vemos como ECX vale 0 y EDX apunta al inicio de nuestro de la String "10445678951", y moverá el primer Byte del número a EDX en este primer paso siendo "1" o 31 en hexadecimal,

Llegamos a una comparación de dos registros que corresponde a EAX, siendo el primer Byte del String introducido y EDX, siendo el primer Byte de la String del programa.

Ahora en el salto condicional jne, salta si no es igual o no es cero (ZF=0) y como no es igual tomará el salto. Hasta aquí podemos deducir que como estamos usando un Serial incorrecto pues el resultado de la comparación no es igual. Por lo tanto como la comparación tiene que ser igual tenemos:

x= primer String introducido por nosotros "n"

x-14=EAX

31 = EDX

Y como EAX = EDX sustituimos,

x-14 = 31 => x = 31+14= 45 que en ASCII corresponde con la letra E.

El primer valor del Serial que tenemos que introducir en el Crackme válido es la letra E. Y así seguiríamos con los demás usando la misma lógica. Ejecutamos el programa, introducimos el Serial y nos sale la ventana de Correcto!.

Hasta la próxima entrada un saludo, Naivenom.

Un comentario en “Diseccionando binarios - 0x04 Crackmes Hardcoded

Deja un comentario

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