Preparación del laboratorio

Contenido descargable del reto:

El archivo descargable del reto contiene el comprimido a12c7365-d876-403c-9f84-7b447eadc52e.zip.

Extrae el contenido en una carpeta para ver los siguientes archivos:

HackTheBox CubeMadness1_Data
baselib.dll
GameAssembly.dll
HackTheBox CubeMadness1.exe
UnityCrashHandler64.exe
UnityPlayer.dll

La presencia de UnityPlayer.dll, GameAssembly.dll y el directorio “HackTheBox CubeMadness1_Data” indica que estamos ante un juego desarrollado con el motor Unity.

En particular, GameAssembly.dll suele aparecer en aplicaciones de Unity compiladas mediante IL2CPP. En este tipo de compilación, parte de la lógica originalmente escrita en C# se transforma en código nativo, por lo que durante el análisis encontraremos instrucciones ensamblador ejecutadas directamente por el proceso.

El objetivo del laboratorio consiste en alcanzar una puntuación determinada dentro del juego. En lugar de completar el reto únicamente mediante interacción manual, analizaremos cómo se almacena y actualiza dicho valor en memoria.

Ejecución del juego

Ejecutamos el archivo:

HackTheBox CubeMadness1.exe

Al iniciar el juego podemos observar un contador que aumenta al alcanzar un cubo.

Este valor será nuestro punto de partida para el análisis dinámico. Tenemos que localizar qué dirección de memoria contiene el contador y, posteriormente, determinar cuál de las direcciones encontradas participa realmente en la lógica del programa.

Búsqueda inicial del valor

Abrimos Cheat Engine y seleccionamos el proceso correspondiente al juego.

Cheat Engine, Abrir proceso

Como conocemos que el valor actual del contador es 0, realizamos una primera búsqueda utilizando un tipo de dato de 4 bytes, en “value” ponemos el valor 0 y pulsamos el botón First Scan.

Tras modificar el contador en el juego alcanzando un cubo, repetimos la búsqueda con el nuevo valor (1) haciendo clic en Next Scan. Repetimos este proceso iterativamente hasta filtrar la lista a un número reducido de memoria candidata. En este caso, tras el quinto escaneo, logramos filtrar los resultados a solo 3 direcciones.

Cheat Engine, Procesos Filtrados

En este punto, bastaría con hacer doble clic sobre las direcciones para añadirlas a la tabla inferior y modificar su valor a 20 para conseguir la flag. Aun así, te recomiendo seguir adelante para explorar en detalle el comportamiento interno de la memoria

Análisis de instrucciones de memoria

Hacemos clic derecho en cada una de las tres direcciones y ejecutamos Find out what writes to this address. Esto nos permitirá detectar la instrucción en ensamblador que escribe en la memoria.

Cheat Engine, Escritura en memoria

Descartamos por el momento la dirección 2186E536830 debido a que presenta escritura continua en segundo plano, incluso cuando el personaje permanece estático.

mov [rdi], ecx

Escribe en la dirección base apuntada por rdi, sin desplazamiento. También puede ser la variable correcta, pero podría tratarse del primer campo de una estructura, un búfer o un destino genérico.


A continuación, recolectamos el último cubo dentro del juego para provocar la escritura en la memoria y capturar la instrucción correspondiente en las direcciones que nos quedan.

Cheat Engine, Captura de la escritura en memoria

En la primera ventana 2186E522A40 vemos la instrucción inc [rax], mientras que en la segunda 2186E536834 tenemos mov [rbx+24],ecx.

De ambas, la que muestra un patrón claro de variable dentro de una estructura de objeto es: mov [rbx+24], ecx

Aquí, el registro rbx probablemente almacena la dirección base del objeto, y +24 corresponde al desplazamiento (offset) del atributo, es un patrón habitual en variables como vida, munición o coleccionables.

Por otro lado, la instrucción inc [rax] parece limitarse a un contador secundario e incremental, propio de eventos de sincronización o gestión de caché interna del juego, en lugar de gestionar el estado real del jugador.


En la ventana correspondiente a la dirección 2186E536834, pulsamos sobre el botón “More Information” para analizar el contexto de la instrucción.

Cheat Engine, 2186E536834 Más Información

La secuencia de instrucciones que observamos es:

cmp [rbx+24], ecx       ; Compara el valor actual en [rbx+24] con ECX
je  7FFB08277F3C        ; Si son iguales, salta (no vuelve a escribir)
mov [rbx+24], ecx       ; Si son distintos, copia el valor de ECX en [rbx+24]

En el panel inferior observamos los valores de los registros tras la ejecución:

RAX = 2186E522A40
RBX = 2186E536810
RCX = 00000006

Haciendo la aritmética de punteros, confirmamos la dirección:

RBX + 0x24
2186E536810 + 0x24 = 2186E536834.

Por lo tanto, la instrucción mov [rbx+24], ecx se encarga de escribir el valor del registro ECX directamente en la dirección de memoria 2186E536834.

¿De dónde proviene el valor de ECX?

Para entender el origen de este dato, pulsamos en el botón “Show Disassembler” y nos desplazamos un poco hacia arriba:

Cheat Engine, 2186E536834 Mostrar Desensamblador

Analizando el bloque de código completo:

mov rax,[GameAssembly.dll+CF6C68]   ; Carga el puntero base
mov rax,[rax+000000B8]              ; Resuelve la dirección 2186E522A40
lea rdi,[rbx+20]                    ; Calcula la dirección RBX+0x20 (2186E536830)
mov ecx,[rax]                       ; Lee el contador y lo guarda en ECX
mov [rdi],ecx                       ; Copia el valor en [RBX+0x20]
cmp [rbx+24],ecx                    ; Comprueba si [RBX+0x24] está actualizado
je  GameAssembly.dll+A67F3C         ; Salta si ambos valores coinciden
mov [rbx+24],ecx                    ; Actualiza [RBX+0x24]

La lógica del juego es clara: lee el contador desde [RAX], lo almacena temporalmente en el registro ECX y, si el valor almacenado en [RBX+24] (2186E536834) no coincide, lo sobrescribe para actualizar el estado.

Ahora volvemos hacer la aritmética de punteros, para conocer el valor de ECX:

RBX + 0x20
2186E536810 + 0x20 = 2186E536830.

La dirección 2186E522A40, resuelta dinámicamente y almacenada en RAX, funciona como la fuente del valor en las rutinas analizadas. Desde esta ubicación se lee el contador y sobre ella se realiza directamente el incremento.

Por otro lado, las direcciones 2186E536830 (RBX+0x20) y 2186E536834 (RBX+0x24) son variables espejo o secundarias que el juego utiliza para validar los cambios de estado localmente.

La ejecución lee constantemente el valor de la fuente original 2186E522A40, lo carga temporalmente en el registro de trabajo ECX y lo compara con el valor almacenado en la copia secundaria 2186E536834. Si intentamos alterar manualmente 2186E536834 mediante Cheat Engine, la instrucción cmp [rbx+24], ecx detecta la discrepancia y ejecuta la instrucción mov [rbx+24], ecx, sobrescribiendo nuestro cambio en el siguiente ciclo con el dato original que aún reside en 2186E522A40.

Para lograr una modificación permanente, debemos alterar directamente la memoria en 2186E522A40.

CubeMadness1 Pawned

La flag ha sido obtenida con éxito y ha sido censurada para respetar las reglas de HTB.