Compiler
Hola bandita, ¿cómo están? El día de hoy les traigo la guía para la máquina easy de TryHackMe Compiled, que debo decir, fue un poco una pesadilla, pero eso lo verán más adelante. Así que sin más, ¿por qué no empezamos? Advertencia, no hay capturas de pantalla (sadly) porque solo a mi se me ocurre hacer máquinas a las 9pm y terminé casi a las 12 am.
Primero tenemos un simple y pequeño binario que solo mostraba un prompt pidiendo una contraseña. Probaba “123”, “password”, “admin”… y siempre la misma respuesta: “Try again!”. Como ya he hecho la lección de malware analysis, lo primero que hice fue mandar un string, usualmente encuentras cosas básicas, pero quizá algo útil.
1
2
3
4
bash
strings Compiled
Entre el output aparecía “StringsForNoobs”, y obviamente me sentí ofendida, pero con esto deduje que los trings no nos iban a ayudar mucho (o eso era lo que pensaba, oh, boy, how wrong I was). Así que cambié completamente de enfoque, si strings no me daba, entonces debía ir al mero core del asunto. Había que desamblarlo y aquí, señores, señoritas, señorites, es cuando las cosas se ponen serias.
1
2
3
bash
cutter Compiled
Cuando analicé el desensamblado, encontré algo interesante. El programa no estaba cargando ningún string desde la sección de datos. En lugar de eso, movía valores hexadecimales directamente a los registros:
1
2
3
4
5
6
7
assembly
movabs rax, 0x4973676e69727453
movabs rdx, 0x626f6f4e726f4673
mov qword [rbp-0x48], rax
mov qword [rbp-0x40], rdx
mov word [rbp-0x38], 0x73
Aquí es donde recordé algo crucial sobre cómo las computadoras almacenan la información. Estos valores estaban en little-endian. Tenía que convertir esos hexas a texto, pero leyendo los bytes al revés. Usé Python para descifrarlo:
1
2
3
4
5
6
hex_strings = ["4973676e69727453", "626f6f4e726f4673", "73"]
for h in hex_strings:
bytes_obj = bytes.fromhex(h)
print(bytes_obj[::-1].decode('ascii'))
Y apareció ante mí: “StringsIsForNoobs”. ¡Eureka! Bueno, medio eureka, porque todavía no funcionaba cuando lo probaba directamente.
Resulta que había otra capa de misterio. Siguiendo el análisis del desensamblado, encontré el scanf:
1
2
3
assembly
lea rdi, [format_string] ; "DoYouEven%sCTF"
¡Ajá! Esas palabras no eran solo texto decorativo. El formato completo era “DoYouEven%sCTF”, donde “%s” era mi input. Era como si me dieran un formulario con partes ya escritas y un espacio en blanco en medio.
Pero la trampa más elegante venía después. El programa hacía no una, sino tres comparaciones diferentes con strcmp. Usé gdb para poner breakpoints y seguirlas:
1
2
3
4
5
6
bash
gdb ./Compiled
break *strcmp
run
Las dos primeras comparaciones eran puro teatro: comparaban mi entrada con “__dso_handle” pero usaban js y jle - saltos condicionales que no buscan igualdad, solo relaciones de orden. Era como un guardia que te pide decir un número, no para ver si es correcto, sino para ver si sabes contar.
La tercera comparación era la que importaba. Ahí sí usaba jne - jump if not equal - y comparaba exactamente con la cadena que había construido con esos valores hexadecimales: “StringsIsForNoobs”.
Fue como armar un rompecabezas donde todas las piezas finalmente encajaron. La contraseña completa tenía que respetar el formato del scanf y pasar todas las validaciones:
1
2
3
4
5
6
bash
./Compiled
Password: DoYouEvenStringsIsForNoobsCTF
Correct!
¡Funcionó! Este reto me enseñó que el reverse engineering no es solo sobre herramientas, sino sobre pensamiento crítico. No se trata de qué comandos ejecutar, sino de entender el flujo del programa, cómo manipula los datos en memoria, y qué trucos usa para esconder información.
Cada comando que usé - strings, objdump, gdb, Python para decodificar hex - era solo un pincel en el cuadro completo. La verdadera habilidad estaba en saber cuándo usarlo y, más importante, cómo interpretar lo que me mostraba.
¿Y ustedes? ¿Han tenido esa experiencia de descubrir que lo obvio era una trampa? ¿O de encontrar que una herramienta en la que confiaban les estaba mostrando solo parte de la verdad? Cuéntenme en los comentarios sus historias de debugging y reverse engineering. Y si quieren ver más análisis donde desarmamos binarios capa por capa, suscríbanse - porque cada reto es una nueva historia por descifrar juntos.
Glosario de Comandos para Análisis de Binarios
| Comando | Uso/Parámetros | Descripción | Ejemplo Práctico |
|---|---|---|---|
strings | strings [binario] | Extrae strings legibles de un binario | strings Compiled para buscar contraseñas hardcodeadas |
gdb | gdb ./[binario] | Debugger interactivo para análisis dinámico | gdb ./Compiled para ejecutar paso a paso |
break (gdb) | break *[función] o break *[dirección] | Coloca breakpoint en función/dirección | break *main o break *0x400500 |
run (gdb) | run [args] | Ejecuta el programa dentro del debugger | run o con argumentos run "test" |
info registers | info registers en gdb | Muestra estado de registros del CPU | info registers para ver RAX, RBP, RSP, etc. |
x (examine) | x/[n][formato] [dirección] | Examina memoria en gdb | x/20x $rsp muestra 20 valores hex desde stack |
disas | disas [función] en gdb | Desensambla función específica | disas main para ver código de main |
python hex decode | python3 -c "bytes.fromhex('...')[::-1].decode()" | Convierte hex a texto (little-endian) | Para decodificar 4973676e69727453 → "StringsI" |
xxd | xxd [archivo] \| head -n | Visualizador hexadecimal | xxd Compiled \| head -20 para ver header |
hexdump | hexdump -C [archivo] \| less | Hexdump con ASCII | hexdump -C Compiled \| less para navegar |
file | file [binario] | Identifica tipo de archivo/arquitectura | file Compiled para ver si es ELF 64-bit |
checksec | checksec [binario] | Analiza protecciones de seguridad | checksec Compiled para ASLR, NX, Canary, etc. |
readelf | readelf -a [binario] | Información detallada de ELF | readelf -s Compiled para tabla de símbolos |
ltrace | ltrace ./[binario] | Traza llamadas a librerías | ltrace ./Compiled para ver strcmp/scanf |
strace | strace ./[binario] | Traza llamadas al sistema | strace ./Compiled para syscalls de I/O |
gcc | gcc -o output archivo.c | Compila código C | gcc -g -o test test.c con símbolos debug |
radare2 | r2 [binario] | Framework de análisis de binarios | r2 -AAA Compiled para análisis automático |
ghidra | (GUI) | Herramienta gráfica de descompilación | Descompila assembly a pseudocódigo C |
nm | nm [binario] | Lista símbolos del binario | nm Compiled para funciones/variables |
ldd | ldd [binario] | Muestra dependencias de librerías | ldd Compiled para ver libc, libm, etc. |
size | size [binario] | Muestra tamaño de secciones | size Compiled para .text, .data, .bss |
strip | strip [binario] | Elimina símbolos de debugging | strip Compiled hace más difícil el reversing |
objcopy | objcopy --dump-section .text=out.text [binario] | Extrae secciones específicas | Para extraer solo el código ejecutable |
grep | grep -r "palabra" [dir] | Búsqueda en archivos | grep -r "strcmp" desensamblado.txt |
awk | awk '/pattern/ {print $1}' | Procesamiento de texto | Para filtrar output de objdump |
sed | sed 's/buscar/reemplazar/g' | Editor de stream | Para limpiar output de análisis |
diff | diff archivo1 archivo2 | Compara archivos | diff original.txt modificado.txt |
md5sum | md5sum [archivo] | Checksum MD5 | md5sum Compiled para integridad |
sha256sum | sha256sum [archivo] | Checksum SHA256 | Para verificar binarios descargados |
objdump (headers) | objdump -f [binario] | Muestra header del archivo | objdump -f Compiled para entry point |
readelf (headers) | readelf -h [binario] | Header ELF | readelf -h Compiled información básica |
ps | ps aux \| grep [proceso] | Lista procesos | Para encontrar PID de proceso en ejecución |
kill | kill -9 [PID] | Termina proceso | Para matar proceso colgado |
time | time ./[binario] | Mide tiempo de ejecución | time ./Compiled para profiling básico |
Flujos de Comando Típicos
Análisis Inicial Rápido:
1
2
3
file target_binary
strings target_binary | head -50
checksec target_binary
Desensamblado para Buscar Funciones:
1
2
objdump -d target_binary | grep -A 20 "<main>:"
objdump -d target_binary | grep -n "strcmp"
Sesión de Debugging Básica:
1
2
3
4
5
gdb ./target_binary
(gdb) break *main
(gdb) run
(gdb) disas
(gdb) continue
Extraer y Decodificar Strings Ocultas:
1
2
3
4
# Buscar valores hex en objdump
objdump -d target_binary | grep -B2 -A2 "mov.*0x"
# Decodificar con Python
python3 -c "print(bytes.fromhex('41424344')[::-1].decode('ascii'))"
