Introducción
Decompilado
Abrimos la víctima con nuestro decompilador favorito y nos fijamos en su contenido.

Abrimos la víctima con nuestro decompilador favorito y nos fijamos en su contenido.

Alerta de Spoiler: El reto está en activo a fecha de publicación.
Spoiler alert: The challenge is still alive.
Este tipo de retos son de lo más variopinto pero una de las primeras cosas que se suele hacer es ver el código fuente y fijarse en los enlaces para hacernos una idea del tipo de vulnerabilidades a explotar. Empezamos por explorar el código fuente.
<html>
<head>
<link href="../../assets/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<script src="../../assets/js/jquery.min.js" type="text/javascript"></script>
<meta charset="UTF-8">
<style>
body{
background-color: #111;
}
.main{
background-color: #fff;
}
</style>
</head>
<body>
<div class="container main">
<div class="jumbotron">
<div class="container">
<h1>ジェフのショップ</h1>
<p>ジェフのショップへようこそ、私たちは...私たちは、新しいセキュリティシステムを幸せ持っています <img height="100" width="100" src="source/cat.gif"></p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">もっと詳しく知る »</a></p>
</div>
</div>
<div class="row">
<!-- Example row of columns -->
<div class="row text-center">
<div class="col-md-4">
<h2>車</h2>
<p class="text-center" style="margin: 20px;">我々 50,000円の価格で車を販売しています. </p>
<p><a class="btn btn-danger " href="#" role="button">今すぐ購入<span class="glyphicon glyphicon-shopping-cart"></span></a></p>
</div>
<div class="col-md-4">
<h2>剣</h2>
<p class="text-justify" style="margin: 20px;">我々は、 25円の価格のために地球全体の最も鋭い剣を販売しています </p>
<p><a class="btn btn-danger " href="#" role="button">今すぐ購入<span class="glyphicon glyphicon-shopping-cart"></span></a></p>
</div>
<div class="col-md-4">
<h2>赤ジェフの毒</h2>
<p class="text-justify" style="margin: 20px;">我々は毒、世界に存在するほとんどの不治の毒を販売しています。プライスレス90000.</p>
<p><a class="btn btn-danger " href="#" role="button">今すぐ購入<span class="glyphicon glyphicon-shopping-cart"></span></a></p>
</div>
</div>
<hr>
</div>
<div class="row">
<!-- Example row of columns -->
<div class="row text-center">
<div class="col-md-4">
<h2>魔法の脳</h2>
<p class="text-justify" style="margin: 20px;">彼らは私たちの脳販売しているいくつかの場面で非常に便利です。その価格は約10円です.</p>
<p><a class="btn btn-danger " href="#" role="button">今すぐ購入<span class="glyphicon glyphicon-shopping-cart"></span></a></p>
</div>
<div class="col-md-4">
<h2>侵入テスト</h2>
<p class="text-justify" style="margin: 20px;">私たちはあなたのウェブページやサーバーで完全なセキュリティを提供します。これはジェフの店の最高の製品です。</p>
<p><a class="btn btn-danger " href="#" role="button">今すぐ購入<span class="glyphicon glyphicon-shopping-cart"></span></a></p>
</div>
</div>
<hr>
</div>
<footer>
<p>© ジェフは2015フンメルス</p>
</footer>
</div>
</body>
</html>
A simple vista no hay nada sospechoso pero me llama la atención el enlace de la imagen del gatito «source/cat.gif«. Si fisgamos dentro de la carpeta «source» podemos ver que nos muestra el contenido de la carpeta como se puede apreciar en la imagen a continuación.
La carpeta «app» suena interesante. Hacemos clic y vemos lo siguiente.
Notice: Undefined index: commit in C:/xampp/htdocs/challenge-land/Realistic/shop/source/app/index.php on line 2
Vemos que el error mostrado muestra más información de la debida y la aprovecharemos en beneficio propio. Aquí la clave está en el fichero index.php y en el parámetro commit. Haremos una prueba para ver que vamos por el buen camino.
http://challengeland.co/Realistic/e4633b53f9/source/app/index.php?commit
y recibimos el error «The commit number not found.»
Probamos entonces añadiendo números al parámetro commit y nos fijamos en las respuestas.
http://challengeland.co/Realistic/e4633b53f9/source/app/index.php?commit=1 すべて更新 http://challengeland.co/Realistic/e4633b53f9/source/app/index.php?commit=2 Jeff@shop.com 2013年5月5日 - >ちょっと私たちは.htpasswdの使用する必要がジェームズと.htaccess保護...本当に安全です。すべてのファイルを更新します http://challengeland.co/Realistic/e4633b53f9/source/app/index.php?commit=3 すべて更新 http://challengeland.co/Realistic/e4633b53f9/source/app/index.php?commit=4 すべて更新 http://challengeland.co/Realistic/e4633b53f9/source/app/index.php?commit=5 すべて更新 http://challengeland.co/Realistic/e4633b53f9/source/app/index.php?commit=6 James@shop.com 2013年5月7日に - >ジェフ、大丈夫、私はそれに仕事に行きます。すべてのファイルを更新し、覚えています - jeffshop:adm1n$2015*+ http://challengeland.co/Realistic/e4633b53f9/source/app/index.php?commit=7 すべて更新 http://challengeland.co/Realistic/e4633b53f9/source/app/index.php?commit=8 jeff@shop.com 2013年5月9日 - >ジェームズ、良い仕事...新しいユーザーとパスがあります jeffcrazyshop:h4rDtoF1nD
Hay varias respuestas sugerentes pero quizá la más relevante es la 8. Ahora bien, solo falta encontrar donde introducir el usuario y la clave.
Si volvemos a la página principal vemos en el enlace algo interesante, me refiero a index.php?page=index. Tras probar varias cosas la que funciona es la típica, admin.
http://challengeland.co/Realistic/e4633b53f9/index.php?page=admin
Al entrar vemos que nos redirige al index de nuevo tras pocos segundos. Aquí hay dos opciones, desactivar javascript para evitar la redirección o entrar directamente a la página admin.php. Optamos por el camino fácil entrando directamente en admin.php:
http://challengeland.co/Realistic/e4633b53f9/admin.php
Introduciendo los datos obtenidos anteriormente nos muestra la querida flag.

Este un crackme muy interesante para principiantes ya que la rutina no es muy compleja. Está hecho en ensamblador.
Arrancamos el crackme en Olly damos al play y se cierra. Buscamos en las «Intermodular Calls» y vemos «IsDebuggerPresent«, clickamos sobre ella y vemos el típico call, lo NOPeamos.
Aquí vemos el call.
Call Nopeado.
Encontrar en serial válido en esta ocasión es muy sencillo, basta con buscar en las «String References» el mensaje de «Bad boy» y fijarse en la comparación.
Si nos fijamos en el serial generado nos da muchas pistas pero vamos a destriparlo ya que tampoco tiene mucha complicación. De nuevo miramos en las «String references» y clickamos sobre el mensaje de «bad boy«. Encima de los mensajes vemos claramente la rutina de creación del serial.
004010EB |. 83F8 04 CMP EAX,4 ;Longitud del nombre >4 004010EE |. 72 05 JB SHORT Ice9.004010F5 004010F0 |. 83F8 0A CMP EAX,0A ;Longitud del nombre <=10 004010F3 |. 76 15 JBE SHORT Ice9.0040110A 004010F5 |> 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL 004010F7 |. 68 04304000 PUSH Ice9.00403004 ; |Title = "Error, Bad Boy" 004010FC |. 68 1C304000 PUSH Ice9.0040301C ; |Text = "name must be at least 4 chars" 00401101 |. 6A 00 PUSH 0 ; |hOwner = NULL 00401103 |. E8 70010000 CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA ........ 00401183 . 3BD3 CMP EDX,EBX 00401185 . 74 15 JE SHORT Ice9.0040119C 00401187 . 8A07 MOV AL,BYTE PTR DS:[EDI] 00401189 . 3C 5A CMP AL,5A ;Compara que el dígito < 5A 0040118B . 7E 05 JLE SHORT Ice9.00401192 0040118D > 03C8 ADD ECX,EAX ;ECX + Ascii(dígito) 0040118F . 47 INC EDI 00401190 .^ EB EE JMP SHORT Ice9.00401180 00401192 > 3C 41 CMP AL,41 ;Compara que el dígito > 41 00401194 . 7D 02 JGE SHORT Ice9.00401198 00401196 . EB 02 JMP SHORT Ice9.0040119A 00401198 > 04 2C ADD AL,2C ;Si cumple lo anterior dígito +2C 0040119A >^ EB F1 JMP SHORT Ice9.0040118D 0040119C > 81C1 9A020000 ADD ECX,29A ;ECX + 29A 004011A2 . 69C9 39300000 IMUL ECX,ECX,3039 ;ECX * 3039 004011A8 . 83E9 17 SUB ECX,17 ;ECX - 17 004011AB . 6BC9 09 IMUL ECX,ECX,9 ;ECX * 9 004011AE . 33DB XOR EBX,EBX 004011B0 . 8BC1 MOV EAX,ECX ;Mueve nuestro SUM en EAX 004011B2 . B9 0A000000 MOV ECX,0A ;ECX = A 004011B7 > 33D2 XOR EDX,EDX 004011B9 . F7F1 DIV ECX ;SUM / ECX (Resultado a EAX) 004011BB . 80C2 30 ADD DL,30 004011BE . 881433 MOV BYTE PTR DS:[EBX+ESI],DL 004011C1 . 83C3 01 ADD EBX,1 004011C4 . 83F8 00 CMP EAX,0 004011C7 . 74 02 JE SHORT Ice9.004011CB 004011C9 .^ EB EC JMP SHORT Ice9.004011B7 004011CB > BF C8304000 MOV EDI,Ice9.004030C8 004011D0 > 8A4433 FF MOV AL,BYTE PTR DS:[EBX+ESI-1] 004011D4 . 8807 MOV BYTE PTR DS:[EDI],AL 004011D6 . 47 INC EDI 004011D7 . 4B DEC EBX 004011D8 . 83FB 00 CMP EBX,0 004011DB .^ 75 F3 JNZ SHORT Ice9.004011D0 004011DD . C607 00 MOV BYTE PTR DS:[EDI],0 ;Coje letras del nombre en función 004011E0 . 8D3D B4304000 LEA EDI,DWORD PTR DS:[4030B4] ;del resultado anterior 004011E6 . 68 B7304000 PUSH Ice9.004030B7 ; ASCII "rus" 004011EB . 68 C8304000 PUSH Ice9.004030C8 ; ASCII "134992368rus" 004011F0 . E8 BB000000 CALL Ice9.004012B0 ; Concatena 004011F5 . 68 C8304000 PUSH Ice9.004030C8 ; /String2 = "136325628rus" 004011FA . 68 98314000 PUSH Ice9.00403198 ; |String1 = "12345" 004011FF . E8 98000000 CALL <JMP.&kernel32.lstrcmpA> ; \lstrcmpA
Resumen (valores hexadecimales):
Finalmente concatena letras siguiendo este criterio:
Ejemplo para deurus
d e u r u (s) 64+65+75+72+75 = 225 225 + 29A = 4BF 4BF * 3039 = E4DE87 E4DE87 - 17 = E4DE70 E4DE70 * 9 = 80BD1F0 ;Pasamos a decimal y concatenamos 134992368rus
Ejemplo para Deurus
D e u r u (s) 44(+2C)+65+75+72+75 = 25D 25D + 29A = 4F7 4BF * 3039 = EF6AFF EF6AFF - 17 = EF6AE8 EF6AE8 * 9 = 86AC228 ;Pasamos a decimal y concatenamos 141214248rus
Como curiosidad decirtos que con el tiempo valores del estilo 29A y 3039 os pegarán rápido al ojo ya que equivalen a 666 y 12345 en decimal. Por cierto 29A fue un grupo de hackers creadores de virus muy conocido en la escena Nacional e Internacional.

Los retos de criptografía pueden ser muy variados como he dicho anteriormente. El secreto suele estar en saber a que te enfrentas y posteriormente construir una herramienta para descifrarlo o usar una ya existente (la mayoría de los casos).
Una web con la que suelo resolver la mayoría de retos es dcode.fr. Si os fijáis en el enlace, la lista de categorías asciende a 48 y disponéis de unos 800 algoritmos para rebanaros los sesos.
A continuación veamos unos cuantos retos que podéis encontrar por la red. Cabe destacar que normalmente el título del reto dice mucho del algoritmo.
Con John The Ripper tenemos que preparar un archivo de texto del estilo: deurus.info:1011:4C240DDAB17D1796AAD3B435B51404EE:4C240DDAB17D1796AAD3B435B51404EE:::
y ejecutar el comando: john –format=lm LM.txt
Conversiones, cifra clásica, hash, simétricos, asimétricos, combinaciones de varios algoritmos y un largo etcetera. Como veis los hay para todos los gustos, ten en cuenta que aquí os muestro una pequeñísima parte de lo que os encontrareis en las webs de retos, pero para despertar la curiosidad es suficiente.
¡Hala, a decodificar!

Esta vez se trata de un crackme realizado en VC++ 5.0/6.0 y en sus entrañas utiliza RSA-24. En este caso la peculiaridad es que el nombre no interviene en la generación del serial siendo un resultado único.
p = Primer número primo
q = Segundo número primo
e = Exponente público que cumpla MCD(e,(p-1)*(q-1))==1
n = Módulo público siendo n=p*q
d = Exponente privado que cumpla d=e^(-1) mod ((p-1)*(q-1))
De este modo e y n son la parte pública de la clave y d y n la parte privada. Los número primos p y q se utilizan solo para generar los parámetros y de ahí en adelante se pueden desechar.
cifrado = descifrado ^ e mod n
descifrado = cifrado ^ d mod n
Nuestro primer vistazo con OllyDbg nos muestra cuatro números de los que podemos hacernos una idea de que 9901 es un buen candidato a ser el exponente público (e) y 12790891 el módulo n ya que casualmente es un número de 24 bits. Los otros dos números de momento no nos dicen nada.

A continuación de los números tenemos la rutina de comprobación en la que comprueba que nuestro serial tenga 14 dígitos y lo divide en dos partes de 7 dígitos. Interesante ya que los otros dos números que aparecían en las referencias de texto tienen 7 dígitos cada uno.
004029CD |. 68 DC004200 PUSH RSA24.004200DC ; ASCII "9901"
004029D2 |. 8D8C24 E40000>LEA ECX,[DWORD SS:ESP+E4]
004029D9 |. E8 52E7FFFF CALL RSA24.00401130
004029DE |. 68 D0004200 PUSH RSA24.004200D0 ; ASCII "12790891"
004029E3 |. 8D4C24 1C LEA ECX,[DWORD SS:ESP+1C]
004029E7 |. C78424 640600>MOV [DWORD SS:ESP+664],0
004029F2 |. E8 39E7FFFF CALL RSA24.00401130
004029F7 |. 68 C8004200 PUSH RSA24.004200C8 ; ASCII "8483678"
004029FC |. 8D8C24 740200>LEA ECX,[DWORD SS:ESP+274]
00402A03 |. C68424 640600>MOV [BYTE SS:ESP+664],1
00402A0B |. E8 20E7FFFF CALL RSA24.00401130
00402A10 |. 68 C0004200 PUSH RSA24.004200C0 ; ASCII "5666933"
00402A15 |. 8D8C24 AC0100>LEA ECX,[DWORD SS:ESP+1AC]
00402A1C |. C68424 640600>MOV [BYTE SS:ESP+664],2
00402A24 |. E8 07E7FFFF CALL RSA24.00401130
00402A29 |. 8B9424 680600>MOV EDX,[DWORD SS:ESP+668]
00402A30 |. 83CE FF OR ESI,FFFFFFFF
00402A33 |. 8BFA MOV EDI,EDX
00402A35 |. 8BCE MOV ECX,ESI
00402A37 |. 33C0 XOR EAX,EAX
00402A39 |. C68424 600600>MOV [BYTE SS:ESP+660],3
00402A41 |. F2:AE REPNE SCAS [BYTE ES:EDI]
00402A43 |. F7D1 NOT ECX
00402A45 |. 49 DEC ECX
00402A46 |. 83F9 0E CMP ECX,0E ; serial 0xE chars -> 14 digitos
00402A49 |. 0F85 63010000 JNZ RSA24.00402BB2
00402A4F |. 33C9 XOR ECX,ECX
00402A51 |> 8A0411 /MOV AL,[BYTE DS:ECX+EDX] ; {
00402A54 |. 3C 30 |CMP AL,30
00402A56 |. 0F8C 56010000 |JL RSA24.00402BB2
00402A5C |. 3C 39 |CMP AL,39 ; comprueba que el serial sea numerico
00402A5E |. 0F8F 4E010000 |JG RSA24.00402BB2
00402A64 |. 41 |INC ECX
00402A65 |. 83F9 0E |CMP ECX,0E
00402A68 |.^ 7C E7 \JL SHORT RSA24.00402A51 ; }
00402A6A |. 8BC2 MOV EAX,EDX
00402A6C |. C64424 17 00 MOV [BYTE SS:ESP+17],0 ; {
00402A71 |. C64424 0F 00 MOV [BYTE SS:ESP+F],0
00402A76 |. 8B08 MOV ECX,[DWORD DS:EAX]
00402A78 |. 894C24 10 MOV [DWORD SS:ESP+10],ECX
00402A7C |. 66:8B48 04 MOV CX,[WORD DS:EAX+4]
00402A80 |. 66:894C24 14 MOV [WORD SS:ESP+14],CX
00402A85 |. 8B4A 07 MOV ECX,[DWORD DS:EDX+7]
00402A88 |. 8A40 06 MOV AL,[BYTE DS:EAX+6] ; divide el serial en dos partes de 7 digitos
00402A8B |. 894C24 08 MOV [DWORD SS:ESP+8],ECX
00402A8F |. 884424 16 MOV [BYTE SS:ESP+16],AL
00402A93 |. 8D42 07 LEA EAX,[DWORD DS:EDX+7]
00402A96 |. 8D4C24 10 LEA ECX,[DWORD SS:ESP+10]
00402A9A |. 66:8B50 04 MOV DX,[WORD DS:EAX+4]
00402A9E |. 8A40 06 MOV AL,[BYTE DS:EAX+6] ; }
A continuación hace una serie de operaciones matemáticas para finalmente comparar el resultado con 8483678 y con 5666933. Lo que está haciendo es cifrar con nuestro serial en dos partes para comprobar que tenemos el número descifrado. Veamos un ejemplo con el serial 12345678901234.
descifrado ^ e mod n = cifrado
x1 = 1234567 y x2 = 8901234
1º parte del serial
x1 ^ 9901 mod 12790891 != 8483678
2º parte del serial
x2 ^ 9901 mod 12790891 != 5666933
Obviamente el resultado de las operaciones anteriores no da ese resultado y el Crackme nos tira fuera de modo que no nos queda más que atacar a RSA para obtener los primos p y q y el módulo privado d. De este modo podremos obtener los números buenos.
Los primos p y q se obtienen factorizando (botón Factor N) y una vez que tenemos p y q hallamos d (botón Calc. D). Todo esto es coser y cantar con la ayuda de la herramienta RSA-Tool 2. El exponente público e se introduce en hexadecimal.
Una vez que tenemos d hallamos el serial de forma sencilla con la herramienta Big Integer Calculator.
cifrado ^ d mod n = descifrado
1º parte del serial
8483678 ^ 10961333 mod 12790891 = 7167622
2º parte del serial
5666933 ^ 10961333 mod 12790891 = 3196885
SERIAL = 71676223196885


Este Crackme está basado en la protección de DVD Audio Extractor 4.3. Afrontaremos dos partes, una primera donde desempacaremos PECompact 2.x y otra donde analizaremos la rutina de comprobación del número de serie. Os adelante que la única dificultad reside en desempacar ya que la rutina del serial es bastante floja.
El motivo que me ha llevado a realizar un apartado para Ollydbg 1 y otro para Ollydbg 2 es principalmente por que con Ollydbg 2 lo haremos desde Windows 7 x64 y con Ollydbg 1 desde Windos 7 x32.
Videotutorial disponible: http://youtu.be/-63yEUTqP-c.
Resumen de pasos:
1. Cargamos el Crackme en Olly y aparecemos aquí.
2. Pulsamos una vez F9 y veremos esto:
3. Ponemos un breakpoint de la siguiente manera «bp VirtualFree» con la ayuda del plugin CmdBar.
4. Pulsamos F9 dos veces y aparecemos aquí.
5. A continuación pulsamos Ctrl+F9 y veremos esto.
6. Pulsamos F8 hasta salir del RETN anterior y veremos esto.
7. Al final vemos lo que estábamos buscando. El JMP EAX es el salto que nos lleva al punto de entrada original (OEP). Ponemos un breakpoint en JMP EAX y pulsamos F9, cuando se detenga Ollydbg, pulsamos F8 y aparecemos aquí.
8. Ya tenemos a PECompact contra las cuerdas, ahora mismo tenemos el Crackme desempacado en memoria.
Hacemos click en Plugins > OllyDumpEx > Dump process y veremos esto.
Pulsamos en Dump y esto nos generará un archivo que se llama DAE430_CrackMe_dump.
9. A continuación con Import Reconstructor seleccionamos el crackme y pulsamos IAT AutoSearch y Get Imports.
Veremos unas importaciones NO válidas, pulsamos en Show Invalid y las clickamos con el botón derecho > Delete thunks.
Finalmente pulsamos Fix Dump y elegimos el crackme dumpeado anteriormente. Con esto ya hemos finalizado el desempacado.
Videotutorial disponible: http://youtu.be/mm42HRlPXOE
Resumen de pasos:
1. Cargamos el crackme en Ollydbg y vemos esto.
2. Pulsamos F8 hasta que veamos dos Calls. Pulsamos F8 hasta el segundo Call y cuando estemos situados encima de él pulsamos F7 para entrar en el.
Dentro del segundo call veremos esto.
3. Seguimos con F8 y llegamos aquí.
4. Sin tracear, nos desplazamos por el código hasta encontrar un poco más abajo JMP EAX. Le ponemos un breakpoint y pulsamos F9.
5. Cuando estemos situados en JMP EAX pulsamos F8 y llegamos al punto de entrada original (OEP).
6. Ahora con el plugin OllyDump vamos a dumpear el ejecutable que tenemos desempacado en memoria.
Dumpeamos.
7. Finalmente con Import reconstructor arreglamos las importaciones.
Cargamos en Ollydbg el crackme desempacado y en las referencias de texto encontramos el mensaje «Gracias por registrarte». Pulsamos en él y llegamos a la rutina de comprobación del serial que desgranamos a continuación.
00401B89 . 83F9 03 CMP ECX,3 ; Len(nombre) >=3 00401B8C . 0F8E 46010000 JLE DAE430_C.00401CD8 00401B92 . B2 44 MOV DL,44 ; Dl=44(Letra D) 00401B94 . 31C0 XOR EAX,EAX 00401B96 . 31FF XOR EDI,EDI 00401B98 . 381403 CMP BYTE PTR DS:[EBX+EAX],DL ; Compara 1er digito con la letra D 00401B9B . 74 05 JE SHORT DAE430_C.00401BA2 00401B9D > BF 01000000 MOV EDI,1 00401BA2 > 40 INC EAX 00401BA3 . 83F8 04 CMP EAX,4 00401BA6 . 74 0C JE SHORT DAE430_C.00401BB4 00401BA8 . 8A5404 45 MOV DL,BYTE PTR SS:[ESP+EAX+45] ; Memoria 22FAF5 a 22FAF | Lee los digitos A1X 00401BAC . 381403 CMP BYTE PTR DS:[EBX+EAX],DL ; Los compara 00401BAF .^ 75 EC JNZ SHORT DAE430_C.00401B9D 00401BB1 .^ EB EF JMP SHORT DAE430_C.00401BA2 00401BB3 90 NOP 00401BB4 > 66:0FBE4424 53 MOVSX AX,BYTE PTR SS:[ESP+53] ; EAX = 5ºdígito 00401BBA . 8D1480 LEA EDX,DWORD PTR DS:[EAX+EAX*4] ; EAX*4+EAX = A 00401BBD . 8D04D0 LEA EAX,DWORD PTR DS:[EAX+EDX*8] ; A*8 + 5ºdigito=B 00401BC0 . 66:C1E8 08 SHR AX,8 ; B/100=C 00401BC4 . C0F8 02 SAR AL,2 ; C/4=D 00401BC7 . 8A5424 53 MOV DL,BYTE PTR SS:[ESP+53] ; DL = 5ºdígito 00401BCB . C0FA 07 SAR DL,7 ; 5ºdígito/80=E 00401BCE . 29D0 SUB EAX,EDX ; E-D=F 00401BD0 . 8D0480 LEA EAX,DWORD PTR DS:[EAX+EAX*4] ; F*4*F=G 00401BD3 . 8D0480 LEA EAX,DWORD PTR DS:[EAX+EAX*4] ; G*4+G=H 00401BD6 . 8A5424 53 MOV DL,BYTE PTR SS:[ESP+53] ; DL = 5ºdígito 00401BDA . 29C2 SUB EDX,EAX ; 5ºdigito - H = I 00401BDC . 83C2 41 ADD EDX,41 ; I+41 = J 00401BDF . 885424 4A MOV BYTE PTR SS:[ESP+4A],DL ; GUARDA J EN LA MEMORIA 22FAFA 00401BE3 . 66:0FBE4424 54 MOVSX AX,BYTE PTR SS:[ESP+54] 00401BE9 . 8D3480 LEA ESI,DWORD PTR DS:[EAX+EAX*4] 00401BEC . 8D04F0 LEA EAX,DWORD PTR DS:[EAX+ESI*8] 00401BEF . 66:C1E8 08 SHR AX,8 00401BF3 . C0F8 02 SAR AL,2 00401BF6 . 8A4C24 54 MOV CL,BYTE PTR SS:[ESP+54] 00401BFA . C0F9 07 SAR CL,7 00401BFD . 29C8 SUB EAX,ECX 00401BFF . 89C6 MOV ESI,EAX 00401C01 . 8D34B6 LEA ESI,DWORD PTR DS:[ESI+ESI*4] 00401C04 . 8D34B6 LEA ESI,DWORD PTR DS:[ESI+ESI*4] 00401C07 . 8A4424 54 MOV AL,BYTE PTR SS:[ESP+54] 00401C0B . 89F1 MOV ECX,ESI 00401C0D . 29C8 SUB EAX,ECX 00401C0F . 89C6 MOV ESI,EAX 00401C11 . 8D46 41 LEA EAX,DWORD PTR DS:[ESI+41] 00401C14 . 884424 4B MOV BYTE PTR SS:[ESP+4B],AL ; GUARDA J2 EN LA MEMORIA 22FAFB para el 6ºdígito 00401C18 . 66:0FBE4424 55 MOVSX AX,BYTE PTR SS:[ESP+55] 00401C1E . 8D3480 LEA ESI,DWORD PTR DS:[EAX+EAX*4] 00401C21 . 8D04F0 LEA EAX,DWORD PTR DS:[EAX+ESI*8] 00401C24 . 66:C1E8 08 SHR AX,8 00401C28 . C0F8 02 SAR AL,2 00401C2B . 8A4C24 55 MOV CL,BYTE PTR SS:[ESP+55] 00401C2F . C0F9 07 SAR CL,7 00401C32 . 29C8 SUB EAX,ECX 00401C34 . 89C6 MOV ESI,EAX 00401C36 . 8D34B6 LEA ESI,DWORD PTR DS:[ESI+ESI*4] 00401C39 . 8D34B6 LEA ESI,DWORD PTR DS:[ESI+ESI*4] 00401C3C . 8A4424 55 MOV AL,BYTE PTR SS:[ESP+55] 00401C40 . 89F1 MOV ECX,ESI 00401C42 . 29C8 SUB EAX,ECX 00401C44 . 89C6 MOV ESI,EAX 00401C46 . 8D46 41 LEA EAX,DWORD PTR DS:[ESI+41] 00401C49 . 884424 4C MOV BYTE PTR SS:[ESP+4C],AL ; GUARDA J3 EN LA MEMORIA 22FAFC para el 7ºdígito 00401C4D . 66:0FBE4424 56 MOVSX AX,BYTE PTR SS:[ESP+56] 00401C53 . 8D3480 LEA ESI,DWORD PTR DS:[EAX+EAX*4] 00401C56 . 8D04F0 LEA EAX,DWORD PTR DS:[EAX+ESI*8] 00401C59 . 66:C1E8 08 SHR AX,8 00401C5D . C0F8 02 SAR AL,2 00401C60 . 8A4C24 56 MOV CL,BYTE PTR SS:[ESP+56] 00401C64 . C0F9 07 SAR CL,7 00401C67 . 29C8 SUB EAX,ECX 00401C69 . 89C6 MOV ESI,EAX 00401C6B . 8D34B6 LEA ESI,DWORD PTR DS:[ESI+ESI*4] 00401C6E . 8D34B6 LEA ESI,DWORD PTR DS:[ESI+ESI*4] 00401C71 . 8A4424 56 MOV AL,BYTE PTR SS:[ESP+56] 00401C75 . 89F1 MOV ECX,ESI 00401C77 . 29C8 SUB EAX,ECX 00401C79 . 89C6 MOV ESI,EAX 00401C7B . 8D46 41 LEA EAX,DWORD PTR DS:[ESI+41] 00401C7E . 884424 4D MOV BYTE PTR SS:[ESP+4D],AL ; GUARDA J4 EN LA MEMORIA 22FAFD para el 8ºdígito 00401C82 . B8 08000000 MOV EAX,8 00401C87 . 381403 CMP BYTE PTR DS:[EBX+EAX],DL 00401C8A . 74 05 JE SHORT DAE430_C.00401C91 00401C8C > BF 01000000 MOV EDI,1 00401C91 > 40 INC EAX 00401C92 . 83F8 0C CMP EAX,0C 00401C95 . 74 0D JE SHORT DAE430_C.00401CA4 00401C97 . 8A5404 42 MOV DL,BYTE PTR SS:[ESP+EAX+42] 00401C9B . 381403 CMP BYTE PTR DS:[EBX+EAX],DL ; Compara 22FAFA y siguientes con 9, 10, 11 y 12avo digito 00401C9E .^ 75 EC JNZ SHORT DAE430_C.00401C8C 00401CA0 .^ EB EF JMP SHORT DAE430_C.00401C91 00401CA2 . 66:90 NOP 00401CA4 > 89F8 MOV EAX,EDI ; | 00401CA6 . FEC8 DEC AL ; | 00401CA8 . 74 5C JE SHORT DAE430_C.00401D06 ; | 00401CAA . C74424 0C 40000000 MOV DWORD PTR SS:[ESP+C],40 ; | 00401CB2 . C74424 08 4C004100 MOV DWORD PTR SS:[ESP+8],DAE430_C.0041004>; |ASCII "Info" 00401CBA . C74424 04 51004100 MOV DWORD PTR SS:[ESP+4],DAE430_C.0041005>; |ASCII "Gracias por registrarte." 00401CC2 . C70424 00000000 MOV DWORD PTR SS:[ESP],0 ; | 00401CC9 . E8 EE000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA 00401CCE . 83EC 10 SUB ESP,10 00401CD1 . 31C0 XOR EAX,EAX 00401CD3 .^ E9 F2FBFFFF JMP DAE430_C.004018CA 00401CD8 > C74424 0C 40000000 MOV DWORD PTR SS:[ESP+C],40 ; | 00401CE0 . C74424 08 4C004100 MOV DWORD PTR SS:[ESP+8],DAE430_C.0041004>; |ASCII "Info" 00401CE8 . C74424 04 6A004100 MOV DWORD PTR SS:[ESP+4],DAE430_C.0041006>; |ASCII "Nombre mínimo 4 caracteres." 00401CF0 . C70424 00000000 MOV DWORD PTR SS:[ESP],0 ; | 00401CF7 . E8 C0000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA 00401CFC . 83EC 10 SUB ESP,10 00401CFF . 31C0 XOR EAX,EAX 00401D01 .^ E9 C4FBFFFF JMP DAE430_C.004018CA 00401D06 > C74424 0C 10000000 MOV DWORD PTR SS:[ESP+C],10 ; | 00401D0E . C74424 08 34004100 MOV DWORD PTR SS:[ESP+8],DAE430_C.0041003>; |ASCII "Error" 00401D16 . C74424 04 3A004100 MOV DWORD PTR SS:[ESP+4],DAE430_C.0041003>; |ASCII "Registro fallido." 00401D1E . C70424 00000000 MOV DWORD PTR SS:[ESP],0 ; | 00401D25 . E8 92000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
Resumen
- El nombre debe tener más de 3 dígitos aunque no lo usa para el número de serie. - El serial tiene 12 dígitos dividiendose en tres partes, 111122223333. - La primera parte 1111 es comparada directamente con DA1X. - Segunda parte (2222), para los dígitos 5º, 6º, 7º y 8º hace lo siguiente: dígito *4 + dígito = A A*8 + dígito=B B/100 = C C/4 = D dígito/80 = E E-D = F F*4*F = G G*4+G = H digito - H = I I+41 = J GUARDA J EN LA MEMORIA 22FAFA **Todo esto se puede resumir en dígito mod 19 + 41 - Tercera parte (3333). Finalmente compara el resultado del 5º, 6º, 7º y 8º dígitos con el 9º, 10º, 11º y 12º dígitos.
Ejemplo:
Serial = DA1X12345678 1 - (31h mod 19h) + 41h = 48h(Y) 2 - (32h mod 19h) + 41h = 41h(A) 3 - (33h mod 19h) + 41h = 42h(B) 4 - (34h mod 19h) + 41h = 43h(C) Compara Y con 5 Compara A con 6 Compara B con 7 Compara C con 8 Luego el serial correcto sería DA1X1234YABC

Un error que habitualmente cometo cuando me enfrento a todo tipo de retos (especialmente en CTFs) es empezar a procesar el fichero proporcionado con todo tipo de herramientas como pollo sin cabeza. En el caso que nos ocupa se proporcionaba un fichero de audio WAV que procesé hasta con 4 herramientas diferentes antes de tomar aire y decidir simplemente escuchar el audio. Al escucharlo me di cuenta de que se trataba de una marcación por tonos comúnmente conocido como DTMF (Dual-Tone Multi-Frequency).
Con una rápida búsqueda por la web encontré una sencilla herramienta realizada en python llamada dtmf-decoder con la que enseguida obtenemos resultados. La herramienta es bastante sencilla, simplemente parte la señal en trozos, calcula la FFT (Fast Fourier Transform) para obtener las amplitudes y las compara con las de los tonos DTMF. Hay que tener en cuenta que el audio entregado es muy limpio y eso facilita mucho las cosas.
El siguiente comando nos devuelve los números marcados.

Como era de esperar, los números obtenidos no son la solución final aunque en este caso enseguida damos con que el tipo de codificación es simple y llanamente ASCII.
DTMF = 837283123119104521169510048951214811795119521101166363125
HEX = 53 48 53 7B 77 68 34 74 5F 64 30 5F 79 30 75 5F 77 34 6E 74 3F 3F 7D
Solución: SHS{wh4t_d0_y0u_w4nt??}

Aquí tenemos un Crackme clásico creado por Scarebyte hallá por el año 2000 y que cuenta con varias fases siendo un crackme muy interesante para iniciarse o simplemente para divertirse. Al estar realizado en Delphi, los apartados de las checkboxes y de las trackbars se simplifican y mucho, pero aún así hay que currarselo un poco para dejar todo bien atado. Si os fijáis en las soluciones que aparecen en crackmes.de, en aquellos años se usaba DEDE y aunque yo usaré otra herramienta, DEDE sigue siendo igual de útil.
PEiD nos dice que nos enfrentamos a ASPack 1.08.03 -> Alexey Solodovnikov, así que vamos al lío.
Tan sencillo como poner un Breakpoint a User32.MessageBoxA. La llamada a NOPear está en la dirección 441CF2.
Desde las string references localizamos los mensajes de chico bueno y chico malo que nos llevan al código a analizar.
0044C3CD |. E8 5294FDFF CALL CrackMe_.00425824 0044C3D2 |. 8B45 FC MOV EAX,[LOCAL.1] 0044C3D5 |. E8 9A76FBFF CALL CrackMe_.00403A74 0044C3DA |. 83F8 0C CMP EAX,0C ; Lengh C = 12 0044C3DD |. 0F85 53010000 JNZ CrackMe_.0044C536 ; Salto a chico malo 0044C3E3 |. 8D55 FC LEA EDX,[LOCAL.1] 0044C3E6 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C3EC |. E8 3394FDFF CALL CrackMe_.00425824 0044C3F1 |. 8B45 FC MOV EAX,[LOCAL.1] 0044C3F4 |. 8038 43 CMP BYTE PTR DS:[EAX],43 ; 1º dígito serial = C 0044C3F7 |. 0F85 27010000 JNZ CrackMe_.0044C524 ; Salto a chico malo 0044C3FD |. 8D55 F8 LEA EDX,[LOCAL.2] 0044C400 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C406 |. E8 1994FDFF CALL CrackMe_.00425824 0044C40B |. 8B45 F8 MOV EAX,[LOCAL.2] 0044C40E |. 8078 03 6F CMP BYTE PTR DS:[EAX+3],6F ; 4º dígito serial = o 0044C412 |. 0F85 0C010000 JNZ CrackMe_.0044C524 ; Salto a chico malo 0044C418 |. 8D55 F4 LEA EDX,[LOCAL.3] 0044C41B |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C421 |. E8 FE93FDFF CALL CrackMe_.00425824 0044C426 |. 8B45 F4 MOV EAX,[LOCAL.3] 0044C429 |. 8078 08 6F CMP BYTE PTR DS:[EAX+8],6F ; 9º dígito serial = o 0044C42D |. 0F85 F1000000 JNZ CrackMe_.0044C524 ; Salto a chico malo 0044C433 |. 8D55 F0 LEA EDX,[LOCAL.4] 0044C436 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C43C |. E8 E393FDFF CALL CrackMe_.00425824 0044C441 |. 8B45 F0 MOV EAX,[LOCAL.4] 0044C444 |. 8078 01 6C CMP BYTE PTR DS:[EAX+1],6C ; 2º dígito serial = l 0044C448 |. 0F85 D6000000 JNZ CrackMe_.0044C524 ; Salto a chico malo 0044C44E |. 8D55 EC LEA EDX,[LOCAL.5] 0044C451 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C457 |. E8 C893FDFF CALL CrackMe_.00425824 0044C45C |. 8B45 EC MOV EAX,[LOCAL.5] 0044C45F |. 8078 04 20 CMP BYTE PTR DS:[EAX+4],20 ; 5º dígito serial = espacio 0044C463 |. 0F85 BB000000 JNZ CrackMe_.0044C524 ; Salto a chico malo 0044C469 |. 8D55 E8 LEA EDX,[LOCAL.6] 0044C46C |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C472 |. E8 AD93FDFF CALL CrackMe_.00425824 0044C477 |. 8B45 E8 MOV EAX,[LOCAL.6] 0044C47A |. 8078 0A 52 CMP BYTE PTR DS:[EAX+A],52 ; 11º dígito serial = R 0044C47E |. 0F85 A0000000 JNZ CrackMe_.0044C524 ; Salto a chico malo 0044C484 |. 8D55 E4 LEA EDX,[LOCAL.7] 0044C487 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C48D |. E8 9293FDFF CALL CrackMe_.00425824 0044C492 |. 8B45 E4 MOV EAX,[LOCAL.7] 0044C495 |. 8078 07 75 CMP BYTE PTR DS:[EAX+7],75 ; 8º dígito serial = u 0044C499 |. 0F85 85000000 JNZ CrackMe_.0044C524 ; Salto a chico malo 0044C49F |. 8D55 E0 LEA EDX,[LOCAL.8] 0044C4A2 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C4A8 |. E8 7793FDFF CALL CrackMe_.00425824 0044C4AD |. 8B45 E0 MOV EAX,[LOCAL.8] 0044C4B0 |. 8078 09 6E CMP BYTE PTR DS:[EAX+9],6E ; 10º dígito serial = n 0044C4B4 |. 75 6E JNZ SHORT CrackMe_.0044C524 ; Salto a chico malo 0044C4B6 |. 8D55 DC LEA EDX,[LOCAL.9] 0044C4B9 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C4BF |. E8 6093FDFF CALL CrackMe_.00425824 0044C4C4 |. 8B45 DC MOV EAX,[LOCAL.9] 0044C4C7 |. 8078 02 6E CMP BYTE PTR DS:[EAX+2],6E ; 3º dígito serial = n 0044C4CB |. 75 57 JNZ SHORT CrackMe_.0044C524 ; Salto a chico malo 0044C4CD |. 8D55 D8 LEA EDX,[LOCAL.10] 0044C4D0 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C4D6 |. E8 4993FDFF CALL CrackMe_.00425824 0044C4DB |. 8B45 D8 MOV EAX,[LOCAL.10] 0044C4DE |. 8078 05 69 CMP BYTE PTR DS:[EAX+5],69 ; 6º dígito serial = i 0044C4E2 |. 75 40 JNZ SHORT CrackMe_.0044C524 ; Salto a chico malo 0044C4E4 |. 8D55 D4 LEA EDX,[LOCAL.11] 0044C4E7 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C4ED |. E8 3293FDFF CALL CrackMe_.00425824 0044C4F2 |. 8B45 D4 MOV EAX,[LOCAL.11] 0044C4F5 |. 8078 0B 6E CMP BYTE PTR DS:[EAX+B],6E ; 12º dígito serial = n 0044C4F9 |. 75 29 JNZ SHORT CrackMe_.0044C524 ; Salto a chico malo 0044C4FB |. 8D55 D0 LEA EDX,[LOCAL.12] 0044C4FE |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C504 |. E8 1B93FDFF CALL CrackMe_.00425824 0044C509 |. 8B45 D0 MOV EAX,[LOCAL.12] 0044C50C |. 8078 06 67 CMP BYTE PTR DS:[EAX+6],67 ; 7º dígito serial = g 0044C510 |. 75 12 JNZ SHORT CrackMe_.0044C524 ; Salto a chico malo 0044C512 |. BA 78C54400 MOV EDX,CrackMe_.0044C578 ; ASCII "Right Password" 0044C517 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C51D |. E8 3293FDFF CALL CrackMe_.00425854 0044C522 |. EB 22 JMP SHORT CrackMe_.0044C546 0044C524 |> BA 90C54400 MOV EDX,CrackMe_.0044C590 ; ASCII "Wrong Password" 0044C529 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8] 0044C52F |. E8 2093FDFF CALL CrackMe_.00425854 0044C534 |. EB 10 JMP SHORT CrackMe_.0044C546 0044C536 |> BA 90C54400 MOV EDX,CrackMe_.0044C590 ; ASCII "Wrong Password" Chequeo rápido ABCD EFGHIJK Clno iguonRn ; 1º dígito serial = C ; 4º dígito serial = o ; 9º dígito serial = o ; 2º dígito serial = l ; 5º dígito serial = espacio ; 11º dígito serial = R ; 8º dígito serial = u ; 10º dígito serial = n ; 3º dígito serial = n ; 6º dígito serial = i ; 12º dígito serial = n ; 7º dígito serial = g
Básicamente chequea la frase «Cool Running» de forma desordenada como se ve justo encima, siendo el password correcto «Clno iguonRn«. Os dejo el código para que lo analicéis.
De nuevo con las string references localizamos el código.
0044C648 /. 55 PUSH EBP 0044C649 |. 8BEC MOV EBP,ESP 0044C64B |. 83C4 F8 ADD ESP,-8 0044C64E |. 53 PUSH EBX 0044C64F |. 56 PUSH ESI 0044C650 |. 33C9 XOR ECX,ECX 0044C652 |. 894D F8 MOV [LOCAL.2],ECX 0044C655 |. 8BF0 MOV ESI,EAX 0044C657 |. 33C0 XOR EAX,EAX 0044C659 |. 55 PUSH EBP 0044C65A |. 68 83C74400 PUSH CrackMe_.0044C783 0044C65F |. 64:FF30 PUSH DWORD PTR FS:[EAX] 0044C662 |. 64:8920 MOV DWORD PTR FS:[EAX],ESP 0044C665 |. 33C0 XOR EAX,EAX 0044C667 |. 8945 FC MOV [LOCAL.1],EAX 0044C66A |. A1 80F84400 MOV EAX,DWORD PTR DS:[44F880] ; Eax = Nombre 0044C66F |. E8 0074FBFF CALL CrackMe_.00403A74 0044C674 |. 83F8 06 CMP EAX,6 ; Cmp lengh nombre con 6 0044C677 |. 0F8E F0000000 JLE CrackMe_.0044C76D ; Salta si <= 6 0044C67D |. A1 80F84400 MOV EAX,DWORD PTR DS:[44F880] ; Eax = Nombre 0044C682 |. E8 ED73FBFF CALL CrackMe_.00403A74 0044C687 |. 83F8 14 CMP EAX,14 ; Cmp lengh nombre con 20 (14h) 0044C68A |. 0F8D DD000000 JGE CrackMe_.0044C76D ; salta si >= 20 0044C690 |. A1 80F84400 MOV EAX,DWORD PTR DS:[44F880] 0044C695 |. E8 DA73FBFF CALL CrackMe_.00403A74 0044C69A |. 85C0 TEST EAX,EAX 0044C69C |. 7E 17 JLE SHORT CrackMe_.0044C6B5 0044C69E |. BA 01000000 MOV EDX,1 0044C6A3 |> 8B0D 80F84400 /MOV ECX,DWORD PTR DS:[44F880] ; Bucle in 0044C6A9 |. 0FB64C11 FF |MOVZX ECX,BYTE PTR DS:[ECX+EDX-1] 0044C6AE |. 014D FC |ADD [LOCAL.1],ECX ; Suma dig nombre y guarda en 12FBC4 0044C6B1 |. 42 |INC EDX 0044C6B2 |. 48 |DEC EAX 0044C6B3 |.^ 75 EE \JNZ SHORT CrackMe_.0044C6A3 ; Bucle out 0044C6B5 |> A1 84F84400 MOV EAX,DWORD PTR DS:[44F884] ; Eax = Compañia 0044C6BA |. E8 B573FBFF CALL CrackMe_.00403A74 0044C6BF |. 83F8 02 CMP EAX,2 ; Cmp lengh compañia con 2 0044C6C2 |. 7E 18 JLE SHORT CrackMe_.0044C6DC ; Salta si <= 2 0044C6C4 |. A1 84F84400 MOV EAX,DWORD PTR DS:[44F884] ; Eax = Compañia 0044C6C9 |. E8 A673FBFF CALL CrackMe_.00403A74 0044C6CE |. 83F8 08 CMP EAX,8 ; Cmp lengh compañia con 8 0044C6D1 |. 7D 09 JGE SHORT CrackMe_.0044C6DC ; Salta si >= 8 0044C6D3 |. 8B45 FC MOV EAX,[LOCAL.1] ; Eax = sum nombre 0044C6D6 |. 6BC0 02 IMUL EAX,EAX,2 ; Sum nombre * 2 0044C6D9 |. 8945 FC MOV [LOCAL.1],EAX 0044C6DC |> 68 98C74400 PUSH CrackMe_.0044C798 ; ASCII "I Love Cracking and " 0044C6E1 |. 8D55 F8 LEA EDX,[LOCAL.2] 0044C6E4 |. 8B45 FC MOV EAX,[LOCAL.1] 0044C6E7 |. E8 68B0FBFF CALL CrackMe_.00407754 0044C6EC |. FF75 F8 PUSH [LOCAL.2] ; sum del nombre 0044C6EF |. 68 B8C74400 PUSH CrackMe_.0044C7B8 ; ASCII " Girls ;)" 0044C6F4 |. B8 8CF84400 MOV EAX,CrackMe_.0044F88C 0044C6F9 |. BA 03000000 MOV EDX,3 0044C6FE |. E8 3174FBFF CALL CrackMe_.00403B34 ; Concatena 1º frase + sum nombre + 2ºfrase 0044C703 |. 33C0 XOR EAX,EAX 0044C705 |. 8945 FC MOV [LOCAL.1],EAX 0044C708 |. A1 88F84400 MOV EAX,DWORD PTR DS:[44F888] ; Eax = Serial 0044C70D |. E8 6273FBFF CALL CrackMe_.00403A74 0044C712 |. 8BD8 MOV EBX,EAX 0044C714 |. A1 8CF84400 MOV EAX,DWORD PTR DS:[44F88C] 0044C719 |. E8 5673FBFF CALL CrackMe_.00403A74 0044C71E |. 3BD8 CMP EBX,EAX ; Compara tamaño frase con tamaño serial 0044C720 |. 75 4B JNZ SHORT CrackMe_.0044C76D 0044C722 |. A1 88F84400 MOV EAX,DWORD PTR DS:[44F888] 0044C727 |. E8 4873FBFF CALL CrackMe_.00403A74 0044C72C |. 85C0 TEST EAX,EAX 0044C72E |. 7E 27 JLE SHORT CrackMe_.0044C757 0044C730 |. BA 01000000 MOV EDX,1 0044C735 |> 8B0D 88F84400 /MOV ECX,DWORD PTR DS:[44F888] ; Bucle in --> 0044C73B |. 0FB64C11 FF |MOVZX ECX,BYTE PTR DS:[ECX+EDX-1] 0044C740 |. 034D FC |ADD ECX,[LOCAL.1] 0044C743 |. 8B1D 8CF84400 |MOV EBX,DWORD PTR DS:[44F88C] 0044C749 |. 0FB65C13 FF |MOVZX EBX,BYTE PTR DS:[EBX+EDX-1] ; Compara dígito a dígito nuestro serial 0044C74E |. 2BCB |SUB ECX,EBX ; con la concatenación anterior 0044C750 |. 894D FC |MOV [LOCAL.1],ECX 0044C753 |. 42 |INC EDX 0044C754 |. 48 |DEC EAX 0044C755 |.^ 75 DE \JNZ SHORT CrackMe_.0044C735 ; <-- Bucle out 0044C757 |> 837D FC 00 CMP [LOCAL.1],0 0044C75B |. 75 10 JNZ SHORT CrackMe_.0044C76D ; Salta si algo ha ido mal 0044C75D |. 8B86 14030000 MOV EAX,DWORD PTR DS:[ESI+314] 0044C763 |. BA CCC74400 MOV EDX,CrackMe_.0044C7CC ; "You have found the correct Serial :)"
En resumen
Para afrontar esta parte del reto vamos a usar una herramienta llamada Interactive Delphi Reconstructor o IDR. En su día la mejor herramienta era DEDE, pero IDR a mi parecer es algo más potente.
Básicamente IDR nos permite sin quebraderos de cabeza localizar el código del botón que comprueba la secuencia de checkboxes correcta. Cargamos el crackme en IDR y dentro de la pestaña «Units (F2)«, abajo del todo hacemos doble click sobre «F Crack» y vemos que nos muestra todos los controles del formulario. El botón que nos interesa se llama «SpeedButton3«.
Si hacemos doble click sobre el nos muestra el código que se muestra a continuación.
crack::TForm1.SpeedButton3Click 0044C7F4 push ebp 0044C7F5 mov ebp,esp 0044C7F7 push 0 0044C7F9 push ebx 0044C7FA mov ebx,eax 0044C7FC xor eax,eax 0044C7FE push ebp 0044C7FF push 44C920 0044C804 push dword ptr fs:[eax] 0044C807 mov dword ptr fs:[eax],esp 0044C80A mov eax,dword ptr [ebx+324]; TForm1.cb3:TCheckBox 0044C810 mov edx,dword ptr [eax] 0044C812 call dword ptr [edx+0B8]; TCustomCheckBox.GetChecked 0044C818 test al,al >0044C81A je 0044C8ED 0044C820 mov eax,dword ptr [ebx+328]; TForm1.cb5:TCheckBox 0044C826 mov edx,dword ptr [eax] 0044C828 call dword ptr [edx+0B8]; TCustomCheckBox.GetChecked 0044C82E test al,al >0044C830 je 0044C8ED 0044C836 mov eax,dword ptr [ebx+32C]; TForm1.cb6:TCheckBox 0044C83C mov edx,dword ptr [eax] 0044C83E call dword ptr [edx+0B8]; TCustomCheckBox.GetChecked 0044C844 test al,al >0044C846 je 0044C8ED 0044C84C mov eax,dword ptr [ebx+358]; TForm1.cb12:TCheckBox 0044C852 mov edx,dword ptr [eax] 0044C854 call dword ptr [edx+0B8]; TCustomCheckBox.GetChecked 0044C85A test al,al >0044C85C je 0044C8ED 0044C862 mov eax,dword ptr [ebx+364]; TForm1.cb15:TCheckBox 0044C868 mov edx,dword ptr [eax] 0044C86A call dword ptr [edx+0B8]; TCustomCheckBox.GetChecked 0044C870 test al,al >0044C872 je 0044C8ED 0044C874 mov eax,dword ptr [ebx+330]; TForm1.cb20:TCheckBox 0044C87A mov edx,dword ptr [eax] 0044C87C call dword ptr [edx+0B8]; TCustomCheckBox.GetChecked 0044C882 test al,al >0044C884 je 0044C8ED 0044C886 mov eax,dword ptr [ebx+34C]; TForm1.cb9:TCheckBox 0044C88C mov edx,dword ptr [eax] 0044C88E call dword ptr [edx+0B8]; TCustomCheckBox.GetChecked 0044C894 test al,al >0044C896 je 0044C8ED 0044C898 mov eax,dword ptr [ebx+354]; TForm1.cb11:TCheckBox 0044C89E mov edx,dword ptr [eax] 0044C8A0 call dword ptr [edx+0B8]; TCustomCheckBox.GetChecked 0044C8A6 test al,al >0044C8A8 je 0044C8ED 0044C8AA mov eax,dword ptr [ebx+35C]; TForm1.cb13:TCheckBox 0044C8B0 mov edx,dword ptr [eax] 0044C8B2 call dword ptr [edx+0B8]; TCustomCheckBox.GetChecked 0044C8B8 test al,al >0044C8BA je 0044C8ED 0044C8BC mov eax,dword ptr [ebx+33C]; TForm1.cb19:TCheckBox 0044C8C2 mov edx,dword ptr [eax] 0044C8C4 call dword ptr [edx+0B8]; TCustomCheckBox.GetChecked 0044C8CA test al,al >0044C8CC je 0044C8ED 0044C8CE lea eax,[ebp-4] 0044C8D1 mov edx,44C934; 'Ìõô¸ö÷ê¥ó¤ÉÚÀÆ²Äæââîàä¶¶' 0044C8D6 call @LStrLAsg 0044C8DB lea eax,[ebp-4] 0044C8DE call 0044BF00 0044C8E3 mov eax,dword ptr [ebp-4] 0044C8E6 call ShowMessage >0044C8EB jmp 0044C90A 0044C8ED lea eax,[ebp-4] 0044C8F0 mov edx,44C958; 'Åÿæò衦óàù¨ïêçð®øé¤íüàî诹' 0044C8F5 call @LStrLAsg 0044C8FA lea eax,[ebp-4] 0044C8FD call 0044BF00 0044C902 mov eax,dword ptr [ebp-4] 0044C905 call ShowMessage 0044C90A xor eax,eax 0044C90C pop edx 0044C90D pop ecx 0044C90E pop ecx 0044C90F mov dword ptr fs:[eax],edx 0044C912 push 44C927 0044C917 lea eax,[ebp-4] 0044C91A call @LStrClr 0044C91F ret <0044C920 jmp @HandleFinally <0044C925 jmp 0044C917 0044C927 pop ebx 0044C928 pop ecx 0044C929 pop ebp 0044C92A ret
Como podéis apreciar, las checkboxes involucradas son la 3, 5, 6, 9, 11, 12, 13, 15, 19 y 20. Solo nos falta saber cuales se corresponden con esa numeración y aquí ya depende de cada uno, yo en su día saqué los números a mano mediante el orden de tabulación, pero ya que tenemos IDR, el nos va a dar la solución de una forma sencilla y rápida.
Vamos a la pestaña «Forms (F5)«, seleccionamos la opción Form y hacemos doble click sobre el formulario.
Veréis que aparece el formulario con todos los recursos, incluso los puedes modificar. Localizar los checkboxes ahora es un juego de niños.
Os dejo un vídeo.
De nuevo, con la ayuda de IDR, localizamos la parte del código y analizamos su funcionamiento. Esta parte es la más divertida ya que requiere de un keygen pero en vez de coger el número de serie de una caja de texto lo obtiene de 5 trackbars como muestra la siguiente imagen.
El código de comprobación es el siguiente.
CPU Disasm Address Hex dump Command Comments 0044C1FF |. E8 ECF6FCFF CALL 0041B8F0 ; b^3 0044C204 |. D805 50C34400 FADD DWORD PTR DS:[44C350] ; b^3+5 0044C20A |. D9FA FSQRT ; sqrt(b^3+5) 0044C20C |. E8 F365FBFF CALL 00402804 ; Cos(sqrt(b^3+5)) = U 0044C211 |. DB7D B8 FSTP TBYTE PTR SS:[EBP-48] 0044C214 |. 9B WAIT 0044C215 |. D905 54C34400 FLD DWORD PTR DS:[44C354] ; Coje a 0044C21B |. DC45 E8 FADD QWORD PTR SS:[EBP-18] ; a+1 0044C21E |. D9FA FSQRT ; sqrt(a+1) 0044C220 |. D9E0 FCHS ; -sqrt(a+1) = V 0044C222 |. DB6D B8 FLD TBYTE PTR SS:[EBP-48] 0044C225 |. DEC1 FADDP ST(1),ST 0044C227 |. DB7D AC FSTP TBYTE PTR SS:[EBP-54] 0044C22A |. 9B WAIT 0044C22B |. D905 58C34400 FLD DWORD PTR DS:[44C358] ; coje c 0044C231 |. DC4D D8 FMUL QWORD PTR SS:[EBP-28] ; c*3 0044C234 |. D805 54C34400 FADD DWORD PTR DS:[44C354] ; c*3+1 0044C23A |. D9ED FLDLN2 ; Ln(c*3+1) = X 0044C23C |. D9C9 FXCH ST(1) 0044C23E |. D9F1 FYL2X 0044C240 |. DB6D AC FLD TBYTE PTR SS:[EBP-54] 0044C243 |. DEC1 FADDP ST(1),ST ; U+V+X 0044C245 |. DB7D A0 FSTP TBYTE PTR SS:[EBP-60] 0044C248 |. 9B WAIT 0044C249 |. D905 5CC34400 FLD DWORD PTR DS:[44C35C] ; coje d 0044C24F |. DC45 D0 FADD QWORD PTR SS:[EBP-30] ; d+2 0044C252 |. D9FA FSQRT ; sqrt(d+2) = Y 0044C254 |. DB6D A0 FLD TBYTE PTR SS:[EBP-60] 0044C257 |. DEE1 FSUBRP ST(1),ST ; U+V+X+(-Y) 0044C259 |. D905 58C34400 FLD DWORD PTR DS:[44C358] ; coje e 0044C25F |. DC4D C8 FMUL QWORD PTR SS:[EBP-38] ; e*3 0044C262 |. D835 5CC34400 FDIV DWORD PTR DS:[44C35C] ; (e*3)/2 = Z 0044C268 |. DEC1 FADDP ST(1),ST ; U+V+X+Y+Z 0044C26A |. DB2D 60C34400 FLD TBYTE PTR DS:[44C360] 0044C270 |. DEC1 FADDP ST(1),ST ; U+V+X+Y+Z+0.37 0044C272 |. D80D 6CC34400 FMUL DWORD PTR DS:[44C36C] ; (U+V+X+Y+Z+0.37)*1000 0044C278 |. DD5D F0 FSTP QWORD PTR SS:[EBP-10] 0044C27B |. 9B WAIT 0044C27C |. DD45 F0 FLD QWORD PTR SS:[EBP-10] 0044C27F |. E8 9065FBFF CALL 00402814 ; Redondea((U+V+X+Y+Z+0,37)*1000) 0044C284 |. 8945 98 MOV DWORD PTR SS:[EBP-68],EAX 0044C287 |. 8955 9C MOV DWORD PTR SS:[EBP-64],EDX 0044C28A |. DF6D 98 FILD QWORD PTR SS:[EBP-68] 0044C28D |. 83C4 F4 ADD ESP,-0C 0044C290 |. DB3C24 FSTP TBYTE PTR SS:[LOCAL.33] 0044C293 |. 9B WAIT 0044C294 |. 8D45 FC LEA EAX,[EBP-4] 0044C297 |. E8 68BFFBFF CALL 00408204 0044C29C |. 8D45 FC LEA EAX,[EBP-4] 0044C29F |. E8 5CFCFFFF CALL 0044BF00 ; Llamada de generación de hash ........ 0044BF04 |. 8BF0 MOV ESI,EAX 0044BF06 |. 8B06 MOV EAX,DWORD PTR DS:[ESI] ; EAX = 5415 0044BF08 |. E8 677BFBFF CALL 00403A74 0044BF0D |. 8B15 98EE4400 MOV EDX,DWORD PTR DS:[44EE98] 0044BF13 |. 8902 MOV DWORD PTR DS:[EDX],EAX 0044BF15 |. 8B06 MOV EAX,DWORD PTR DS:[ESI] 0044BF17 |. E8 587BFBFF CALL 00403A74 0044BF1C |. 84C0 TEST AL,AL 0044BF1E |. 76 38 JBE SHORT 0044BF58 0044BF20 |. 880424 MOV BYTE PTR SS:[LOCAL.3],AL 0044BF23 |. B3 01 MOV BL,1 0044BF25 |> B8 1C000000 /MOV EAX,1C 0044BF2A |. E8 516AFBFF |CALL 00402980 0044BF2F |. 0D 80000000 |OR EAX,00000080 0044BF34 |. 8BFB |MOV EDI,EBX 0044BF36 |. 81E7 FF000000 |AND EDI,000000FF 0044BF3C |. 8B16 |MOV EDX,DWORD PTR DS:[ESI] 0044BF3E |. 0FB6543A FF |MOVZX EDX,BYTE PTR DS:[EDI+EDX-1] ; Coje dig a dig el hash, en este caso 5415 0044BF43 |. 33C2 |XOR EAX,EDX ; 1 dig XOR 83; 2 dig XOR 89; 3 dig XOR 86; 4 dig XOR 8D 0044BF45 |. 50 |PUSH EAX 0044BF46 |. 8BC6 |MOV EAX,ESI 0044BF48 |. E8 F77CFBFF |CALL 00403C44 0044BF4D |. 5A |POP EDX 0044BF4E |. 885438 FF |MOV BYTE PTR DS:[EDI+EAX-1],DL 0044BF52 |. 43 |INC EBX 0044BF53 |. FE0C24 |DEC BYTE PTR SS:[LOCAL.3] 0044BF56 |.^ 75 CD \JNZ SHORT 0044BF25 ........ 0044C2AC |. E8 D378FBFF CALL 00403B84 ; Llamada a comparación ........ 00403BAD |> /8B0E /MOV ECX,DWORD PTR DS:[ESI] ; ECX = nuestro Serial XOReado 00403BAF |. |8B1F |MOV EBX,DWORD PTR DS:[EDI] ; EBX = Serial bueno 00403BB1 |. |39D9 |CMP ECX,EBX ; Compara 00403BB3 |. |75 58 |JNE SHORT 00403C0D ; Chico malo 00403BB5 |. |4A |DEC EDX 00403BB6 |. |74 15 |JZ SHORT 00403BCD 00403BB8 |. |8B4E 04 |MOV ECX,DWORD PTR DS:[ESI+4] 00403BBB |. |8B5F 04 |MOV EBX,DWORD PTR DS:[EDI+4] 00403BBE |. |39D9 |CMP ECX,EBX 00403BC0 |. |75 4B |JNE SHORT 00403C0D 00403BC2 |. |83C6 08 |ADD ESI,8 00403BC5 |. |83C7 08 |ADD EDI,8 00403BC8 |. |4A |DEC EDX 00403BC9 |.^\75 E2 \JNZ SHORT 00403BAD
En resumen
1) Siendo nuestro serial : 1 2 3 4 5
a b c d e
2) Realiza las operaciones matemáticas:
Round(((Cos(sqrt(b^3+5)) + (-sqrt(a+1)) + Ln(c*3+1) + (-sqrt(d+2)) + ((e*3)/2))+0.37)*1000))
3) Obtenemos un hash resultante de 5415
4) XORea los dígitos de la siguiente manera:
(5)35 xor 86 = B6
(4)34 xor 83 = BD
(1)31 xor 86 = B7
(5)35 xor 8D = B8
De modo que tenemos B6BDB7B8
5) Compara B6BDB7B8 con B5BAB2BA
6) Revertimos el XOR para obtener el hash bueno
B5 xor 86 = 36(6)
BA xor 83 = 33(3)
B2 xor 86 = 34(4)
BA xor 8D = 37(7)
Luego el hash bueno es 6347
7) Debemos hacer fuerza bruta buscando:
Round(((Cos(sqrt(b^3+5)) + (-sqrt(a+1)) + Ln(c*3+1) + (-sqrt(d+2)) + ((e*3)/2))+0.37)*1000)) = 6347
Para obtener los seriales válidos podemos hacer bucles recursivos hasta recorrer las 10^5 opciones posibles. Una forma de hacerlo en VBNet es la siguiente.
Dim tmp As Double
Dim an, bn, cn, dn, en As Integer
For an = 0 To 9
For bn = 0 To 9
For cn = 0 To 9
For dn = 0 To 9
For en = 0 To 9
tmp = Round(((Cos(Sqrt((Pow(bn, 3)) + 5)) + (-Sqrt(an + 1)) + Log(cn * 3 + 1) + (-Sqrt(dn + 2)) + ((en * 3) / 2) + 0.37) * 1000))
txtdebug.Text = "a-b-c-d-e = Hash || " & an & "-" & bn & "-" & cn & "-" & dn & "-" & en & " = " & tmp
If tmp = 6347 Then
ListBox1.Items.Add("Serial: " & an & bn & cn & dn & en)
End If
Application.DoEvents()
Next
Next
Next
Next
Next
Os dejo como siempre el crackme y el keygen en los enlaces.

Hemos interceptado un mensaje secreto, pero ninguno de nuestros traductores lo sabe interpretar, ¿sabrías interpretarlo tú? Lo único que hemos encontrado es esto en un foro: шжзклмнпфъ = 1234567890
Mensaje secreto: нж, фн, фф, шън, нф, шшъ, шжз, мф, шъп, фл, пк, шъш, шшм, шшк, шъл, шшл, фл, шъш, шшл, фл, шшн, шшъ, фл, шъм, шшн, шъш, шъз, шшш, фл, пж, шшн, шшл, шшш, шжл
Parece que el mensaje secreto está encriptado utilizando un alfabeto cifrado que corresponde a números. Según la clave proporcionada (шжзклмнпфъ = 1234567890), cada letra del alfabeto cirílico se sustituye por un número.
Primero, descompondremos la clave dada:
ш = 1
ж = 2
з = 3
к = 4
л = 5
м = 6
н = 7
п = 8
ф = 9
ъ = 0
Ahora, aplicamos esta clave al mensaje secreto:
нж, фн, фф, шън, нф, шшъ, шжз, мф, шъп, фл, пк, шъш, шшм, шшк, шъл, шшл, фл, шъш, шшл, фл, шшн, шшъ, фл, шъм, шшн, шъш, шъз, шшш, фл, пж, шшн, шшл, шшш, шжл
Sustituyendo cada letra según la clave:
нж = 72
фн = 97
фф = 99
шън = 107
нф = 79
шшъ = 110
шжз = 123
мф = 69
шъп = 108
фл = 95
пк = 84
шъш = 101
шшм = 116
шшк = 114
шъл = 105
шшл = 115
фл = 95
шъш = 101
шшл = 115
фл = 95
шшн = 117
шшъ = 110
фл = 95
шъм = 106
шшн = 117
шъш = 101
шъз = 130
шшш = 111
фл = 95
пж = 82
шшн = 117
шшл = 115
шшш = 111
шжл = 125
El mensaje traducido a números es:
72, 97, 99, 107, 79, 110, 123, 69, 108, 95, 84, 101, 116, 114, 105, 115, 95, 101, 115, 95, 117, 110, 95, 106, 117, 101, 130, 111, 95, 82, 117, 115, 111, 125
Este parece ser un mensaje cifrado en números. La secuencia de números se puede interpretar de varias maneras (como ASCII, coordenadas, etc.). Si asumimos que es un texto codificado en ASCII:
Convertimos cada número a su correspondiente carácter ASCII:
72 = H
97 = a
99 = c
107 = k
79 = O
110 = n
123 = {
69 = E
108 = l
95 = _
84 = T
101 = e
116 = t
114 = r
105 = i
115 = s
95 = _
101 = e
115 = s
95 = _
117 = u
110 = n
95 = _
106 = j
117 = u
101 = e
130 = ?
111 = o
95 = _
82 = R
117 = u
115 = s
111 = o
125 = }
Juntando todo:
HackOn{El_Tetris_e_s_u_n_j_u_e?o_Ruso}
La parte «{El_Tetris_e_s_u_n_j_u_e?o_Ruso}» parece un mensaje en español. Probablemente deba ser leído como: HackOn{El_Tetris_es_un_juego_Ruso}
Así, el mensaje secreto es: HackOn{El_Tetris_es_un_juego_Ruso}.
La imagen de portada de la entrada ha sido generada con ChatGPT.

Este BTM va otra vez sobre IPs. Si amigos del séptimo arte, viendo un capítulo de mi querida «The Sinner» me han vuelto a chirriar los dientes. La verdad que viendo el capítulo no te da tiempo a apreciarlo, únicamente me quedo con que aparece una URL y lo reviso a posteriori (esto lo digo para los curiosos que me preguntáis).
En esta ocasión me tiene un poco inquieto ya que es una serie que cuida enormemente los detalles y el fallo que os voy a mostrar parece intencionado. La imagen en cuestión es esta:

Aparece un buscador con una URL más o menos creíble si no fuera porque la IP que aparece es IMPOSIBLE. La máxima IPv4 es 255.255.255.255, es decir, no han dado ni una, y eso es lo que me tiene mosca. Si hubieran utilizado 82.47.25.29 hubiera quedado bien y estaríamos hablando de un problema de geolocalización de IPs, ya que el rango 82.47.xx.xx le pertenece a UK y deberíamos discernir si el servidor está en EEUU o no…
En definitiva, puede ser un fallo a propósito, un guiño o tener un significado. No se que deciros, bueno si, ¡lo investigaré!

Hoy tenemos un crackme hecho en ensamblador y que cuenta con tres niveles. En el primero de todos nos enfrentamos a una «Splash screen» o nag. El segundo en un serial Hardcodeado y el tercero un número de serie asociado a un nombre.
Abrimos el crackme con Olly y vamos a las «Intermodular Calls«, enseguida vemos la función que crea las ventanas «CreateWindowExA«. Se puede ver lo que parece ser la creación de la pantalla del crackme y al final hay algo que salta a la vista y es la propiedad «WS_TOPMOST», es decir, que se mantenga delante del resto de ventanas.
Pinchamos sobre la función y vamos a parar aquí.
Vemos la llamada a CreateWindowExA que podríamos parchear pero vamos a pensar un poco. Vemos la función GetTickCount y que carga el valor 7D0. 7D0 es 2000 en decimal, que perfectamente pueden ser milisegundos, por lo tanto el parcheo más elegante sería poner la función GetTickCount a 0. En la imagen inferior se puede ver como queda parcheado el valor 7D0.
Probamos y funciona, pasamos a lo siguiente.
El mensaje de error del serial hardcodeado dice «Sorry, please try again». Lo buscamos en las string references y vamos a parar aquí.
Vemos un bucle de comparación que carga unos bytes de la memoria, los bytes dicen «HardCoded«, probamos y prueba superada.
Con el mismo método de las string references localizamos el código que nos interesa. Metemos deurus como nombre y 12345 como serial y empezamos a tracear. Lo primero que hace es una serie de operaciones con nuestro nombre a las que podemos llamar aritmética modular. Aunque en la imagen viene bastante detallado se vé mejor con un ejemplo.
Ejemplo para Nombre: deurus
d e u r u s 64 65 75 72 75 73 -hex 100 101 117 114 117 115 -dec 1ºByte = ((Nombre[0] % 10)^0)+2 2ºByte = ((Nombre[1] % 10)^1)+2 3ºByte = ((Nombre[2] % 10)^2)+2 4ºByte = ((Nombre[3] % 10)^3)+2 5ºByte = ((Nombre[4] % 10)^4)+2 6ºByte = ((Nombre[5] % 10)^5)+2 1ºByte = ((100 Mod 10) Xor 0) + 2 2ºByte = ((101 Mod 10) Xor 1) + 2 3ºByte = ((117 Mod 10) Xor 2) + 2 4ºByte = ((114 Mod 10) Xor 3) + 2 5ºByte = ((117 Mod 10) Xor 4) + 2 6ºByte = ((115 Mod 10) Xor 5) + 2 Si el byte > 10 --> Byte = byte - 10 1ºByte = 2 2ºByte = 2 3ºByte = 7 4ºByte = 9 5ºByte = 5 6ºByte = 2
Lo que nos deja que los Bytes mágicos para deurus son: 227952.
Debido a la naturaleza de la operación IDIV y el bucle en general, llegamos a la conclusión de que para cada letra es un solo byte mágico y que este está comprendido entre 0 y 9.
A continuación realiza las siguientes operaciones con el serial introducido.
Ejemplo para serial: 12345
1 2 3 4 5 31 32 33 34 35 -hex 49 50 51 52 53 -dec 49 mod 10 = 9 50 mod 10 = 0 51 mod 10 = 1 52 mod 10 = 2 53 mod 10 = 3
Los bytes mágicos del serial son: 90123, que difieren bastante de los conseguidos con el nombre.
A continuación compara byte a byte 227952 con 90123.
En resumen, para cada nombre genera un código por cada letra y luego la comprobación del serial la realiza usando el módulo 10 del dígito ascii. Lo primero que se me ocurre es que necesitamos cotejar algún dígito del 0 al 9 para tener cubiertas todas las posibilidades. Realizamos manualmente mod 10 a los números del 0 al 9 y obtenemos sus valores.
(0) 48 mod 10 = 8 (1) 49 mod 10 = 9 (2) 50 mod 10 = 0 (3) 51 mod 10 = 1 (4) 52 mod 10 = 2 (5) 53 mod 10 = 3 (6) 54 mod 10 = 4 (7) 55 mod 10 = 5 (8) 56 mod 10 = 6 (9) 57 mod 10 = 7
Con esto ya podríamos generar un serial válido.
0123456789 - Nuestro alfabeto numérico 8901234567 - Su valor Mod 10
Por lo que para deurus un serial válido sería: 449174. Recordemos que los bytes mágicos para deurus eran «227952», solo hay que sustituir.
Para realizar un KeyGen más interesante, he sacado los valores de un alfabeto mayor y le he añadido una rutina aleatoria para que genere seriales diferentes para un mismo nombre.
'abcdefghijklmnñppqrstuvwxyz0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ - Alfabeto
'7890123456778901234567789018901234567567890123455678901234556880 - Valor
Dim suma As Integer = 0
'Para hacer el serial más divertido
Dim brute() As String = {"2", "3", "4", "5", "6", "7", "8", "9", "0", "1"}
Dim brute2() As String = {"d", "e", "f", "g", "h", "i", "j", "a", "b", "c"}
Dim brute3() As String = {"P", "Q", "R", "S", "T", "U", "j", "a", "D", "E"}
Dim alea As New Random()
txtserial.Text = ""
'Evito nombres mayores de 11 para evitar el BUG comentado en le manual
If Len(txtnombre.Text) > 0 And Len(txtnombre.Text) < 12 Then
For i = 1 To Len(txtnombre.Text)
Dim aleatorio As Integer = alea.Next(0, 9)
suma = (((Asc(Mid(txtnombre.Text, i, 1))) Mod 10) Xor i - 1) + 2
If suma > 9 Then
suma = suma - 10
End If
If (aleatorio) >= 0 And (aleatorio) <= 4 Then
txtserial.Text = txtserial.Text & brute(suma)
ElseIf (aleatorio) > 4 And (aleatorio) <= 7 Then
txtserial.Text = txtserial.Text & brute2(suma)
ElseIf (aleatorio) > 7 And (aleatorio) <= 10 Then
txtserial.Text = txtserial.Text & brute3(suma)
End If
suma = 0
Next
Else
txtserial.Text = "El Nombre..."
End If
Hay un pequeño bug en el almacenaje del nombre y serial y en el guardado de bytes mágicos del serial. Si nos fijamos en los bucles del nombre y el serial, vemos que los bytes mágicos del nombre los guarda a partir de la dirección de memoria 403258 y los bytes mágicos del serial a partir de 40324D. En la siguiente imagen podemos ver seleccionados los 11 primeros bytes donde se almacenan los bytes mágicos del serial. Vemos que hay seleccionados 11 bytes y que el siguiente sería ya 403258, precisamente donde están los bytes mágicos del nombre. Como puedes imaginar si escribes un serial >11 dígitos se solapan bytes y es una chapuza, de modo que el keygen lo he limitado a nombres de 11 dígitos.

Hoy tenemos aquí un bonito crackme matemático realizado por Spider. El crackme está realizado en ensamblador y precisamente por eso, vamos a tener que lidiar con ciertas peculiaridades al realizar el keygen con un lenguaje de bajo nivel.
Al inicio comprueba la longitud del nombre y de el número de serie. El nombre debe tener al menos 6 caracteres y el número de serie debe tener 10. Os adelanto ya que la asignación de memoria del nombre es de 9 caracteres, es decir, da igual la longitud del nombre que solo va a usar 9.
004014AD | E8 1A 02 00 00 | call <pythagoras.GetWindowTextA> | ;Lee el nombre 004014B2 | 83 F8 06 | cmp eax,6 | ;Nombre >=6 caracteres 004014B5 | 0F 82 03 01 00 00 | jb pythagoras.4015BE | 004014BB | 6A 14 | push 14 | 004014BD | 68 D9 31 40 00 | push pythagoras.4031D9 | ;004031D9:"1234567890" 004014C2 | FF 35 10 32 40 00 | push dword ptr ds:[403210] | 004014C8 | E8 FF 01 00 00 | call <pythagoras.GetWindowTextA> | ;Lee el serial 004014CD | 83 F8 0A | cmp eax,A | ;Serial debe tener 10 (A) caracteres 004014D0 | 0F 85 E8 00 00 00 | jne pythagoras.4015BE |
Sabiendo esto introducimos Nombre: deurus y Serial: 1234567890
A continuación chequea que nuestro serial tenga caracteres hexadecimales.
004014DA | 8A 81 D9 31 40 00 | mov al,byte ptr ds:[ecx+4031D9] | ; ecx+004031D9:"1234567890" 004014E0 | 3C 00 | cmp al,0 | ; contador del bucle 004014E2 | 74 1F | je pythagoras.401503 | ; fin del bucle 004014E4 | 3C 30 | cmp al,30 | ; 0x30 = número 1 004014E6 | 0F 82 D2 00 00 00 | jb pythagoras.4015BE | ; < 30 bad boy 004014EC | 3C 46 | cmp al,46 | ; 0x46 = letra F 004014EE | 0F 87 CA 00 00 00 | ja pythagoras.4015BE | ; > 46 bad boy 004014F4 | 3C 39 | cmp al,39 | ; 0x39 = número 9 004014F6 | 76 08 | jbe pythagoras.401500 | ; <=39 ok continua el bucle 004014F8 | 3C 41 | cmp al,41 | ; 0x41 = letra A 004014FA | 0F 82 BE 00 00 00 | jb pythagoras.4015BE | ; <41 bad boy 00401500 | 41 | inc ecx | ; contador += 1 00401501 | EB D7 | jmp pythagoras.4014DA | ; bucle
Continua realizando un sumatorio con nuestro nombre, pero tenemos que tener especial cuidado al tratamiento de los datos, ya que el crackme al estar hecho en ensamblador puede jugar con los registros como quiere y eso nos puede inducir a error.
0040150B | 3C 00 | cmp al,0 | ; ¿Fin bucle? 0040150D | 74 05 | je pythagoras.401514 | ; Salta fuera del bucle si procede 0040150F | 02 D8 | add bl,al | ; bl = bl + al 00401511 | 41 | inc ecx | ; contador +=1 00401512 | EB F1 | jmp pythagoras.401505 | ; bucle
Si os fijáis utiliza registros de 8 bits como son AL y BL. Debajo os dejo una explicación de EAX pero para EBX es lo mismo.
EAX
-----------------------------------
AX
-----------------
AH AL
-------- --------
00000000 00000000 00000000 00000000
(8bit) (8bit) (8bit) (8bit)
EAX (32 bit)
--------
AX (16 bit)
----
AHAL (AH y AL 8 bit)
--------
00000000
El uso de registros de 8 bits nos implica tomar precauciones al realizar el Keygen debido a que por ejemplo, en .Net no tenemos la capacidad de decirle que haga una suma y que nos devuelva solamente 8 bits del resultado. Veamos como ejemplo para el nombre «deurus». La suma de los caracteres hexadecimales quedaría:
64+65+75+72+75+73 = 298, es decir, EAX = 00000298
Pero recordad que el crackme solo cogerá el 98 que es lo correspondiente al registro AL. De momento nos quedamos con nuestro SUMNOMBRE = 98.
A continuación coge los dos primeros caracteres del serial y les resta nuestro SUMNOMBRE y comprueba que el resultado esté entre 4 (0x4) y -4 (0xFC).
0040154B | 0F B6 05 F3 31 40 00 | movzx eax,byte ptr ds:[4031F3] | 00401552 | 8A C8 | mov cl,al | 00401554 | 2A CB | sub cl,bl | ; CL = CL - BL | CL = 12 - 98 = 7A 00401556 | 80 F9 04 | cmp cl,4 | ; Compara CL con 4 00401559 | 7F 63 | jg pythagoras.4015BE | ; Salta si es mayor 0040155B | 80 F9 FC | cmp cl,FC | ; Compara CL con FC (-4) 0040155E | 7C 5E | jl pythagoras.4015BE | ; Salta si es menor
Como veis, el resultado de la resta da 7A (122) que al ser mayor que 4 nos echa vilmente. Aquí de nuevo utiliza registros de 8 bits por lo que debemos tener cuidado con las operaciones matemáticas para no cometer errores, veamos un ejemplo para clarificar de aquí en adelante.
Utilizando 8 bits ----------------- 12 - 98 = 7A que en decimal es 122 Utilizando 16 bits ------------------ 0012 - 0098 = FF7A que en decimal es -134
Ahora ya veis la diferencia entre FC (252) y FFFC (-4). Estrictamente, el crackme comprueba el rango entre 4 (4) y FC (122) al trabajar con registros de 8 bits pero nosotros, como veremos más adelante tomaremos el rango entre 4 y -4. De momento, para poder continuar depurando cambiamos los dos primeros caracteres del serial de 12 a 98, ya que 98 – 98 = 0 y cumple la condición anterior.
Introducimos Nombre: deurus y Serial: 9834567890
Analicemos el siguiente código.
00401560 | F7 E0 | mul eax | ; EAX = EAX * EAX 00401562 | 8B D8 | mov ebx,eax | ; EBX = EAX 00401564 | 0F B7 05 F4 31 40 00 | movzx eax,word ptr ds:[4031F4] | ; EAX = 3456 (4 dígitos siguientes del serial) 0040156B | F7 E0 | mul eax | ; EAX = EAX * EAX 0040156D | 03 D8 | add ebx,eax | ; EBX = EBX + EAX 0040156F | 0F B7 05 F6 31 40 00 | movzx eax,word ptr ds:[4031F6] | ; EAX = 7890 (4 últimos dígitos del serial) 00401576 | F7 E0 | mul eax | ; EAX = EAX * EAX 00401578 | 33 C3 | xor eax,ebx | ; EAX 0040157A | 75 42 | jne pythagoras.4015BE | ; Salta si el flag ZF no se activa
En resumen:
Es decir, Pitágoras entra en escena -> 7890² = 98² + 3456²
Serial = aabbbbcccc
Finalmente comprueba lo siguiente:
0040157C | 66 A1 F6 31 40 00 | mov ax,word ptr ds:[4031F6] | ; AX = 7890 00401582 | 66 2B 05 F4 31 40 00 | sub ax,word ptr ds:[4031F4] | ; AX = 7890 - 3456 = 443A 00401589 | 2C 08 | sub al,8 | ; AL = 3A - 8 = 32 0040158B | 75 31 | jne pythagoras.4015BE | ; Si el resultado de la resta no ha sido cero, serial no válido 0040158D | 6A 30 | push 30 | 0040158F | 68 B0 31 40 00 | push pythagoras.4031B0 | ;004031B0:":-) Well done!!!" 00401594 | 68 7F 31 40 00 | push pythagoras.40317F | ;0040317F:"Bravo, hai trovato il seriale di questo CrackMe!" 00401599 | FF 75 08 | push dword ptr ds:[ebp+8] |
En resumen:
Nuestro serial tiene que cumplir tres condiciones para ser válido.
Como hemos dicho anteriormente, tomaremos el SUMNOMBRE y le sumaremos y restaremos valores siempre y cuando el resultado esté entre 4 y -4. Para deurus hemos dicho que el SUMNOMBRE es 98 por lo que los posibles valores de «a» se pueden ver debajo. Además debemos tener en cuenta que el crackme solo lee los 9 primeros dígitos del nombre.
98-4 = 94 98-3 = 95 98-2 = 96 98-1 = 97 98-0 = 98 98+1 = 99 98+2 = 9A 98+3 = 9B 98+4 = 9C
Es evidente que para encontrar el valor de «c» vamos a tener que utilizar fuerza bruta chequeando todos los valores de «b» comprendidos entre 0 y FFFF (65535). Además, como trabajaremos en un lenguaje de alto nivel, debemos descartar los resultados decimales. Esto nos limitará los seriales válidos asociados a un determinado nombre. Si realizáramos el keygen en ensamblador obtendríamos bastantes más seriales válidos.
Una vez encontrados los valores enteros de la operación «c² = a² + b²», se debe cumplir que «c – b – 8 = 0», lo que nos limitará bastante los resultados.
Private Sub btn_generar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_generar.Click
Try
If txt_nombre.TextLength > 5 Then
lst_serials.Items.Clear()
Dim tmp, c, cx As String
Dim sumanombre, tmp2 As Integer
If txt_nombre.TextLength > 9 Then tmp2 = 8 Else tmp2 = txt_nombre.TextLength - 1
'Calculo el SUMNOMBRE
For i = 0 To tmp2
sumanombre += Asc(Mid(txt_nombre.Text, i + 1, 1)) 'Acumulo suma
tmp = Strings.Right(Hex(sumanombre).ToString, 2) 'Solo 8 bits (Registro AL)
sumanombre = Val("&H" & tmp) 'Paso a decimal
Next
tmp = Strings.Right(Hex(sumanombre).ToString, 2)
sumanombre = CInt("&H" & tmp)
txtdebug.Text = "- SumNombre = " & Hex(sumanombre) & vbCrLf
txtdebug.Text &= "----------------------------------------------" & vbCrLf
Dim a(8) As Integer
'
'a - sumanombre >=4 y <=4
'
a(0) = sumanombre - 4
a(1) = sumanombre - 3
a(2) = sumanombre - 2
a(3) = sumanombre - 1
a(4) = sumanombre
a(5) = sumanombre + 1
a(6) = sumanombre + 2
a(7) = sumanombre + 3
a(8) = sumanombre + 4
txtdebug.Text &= "- Posibles valores de 'a'" & vbCrLf
For i = 0 To a.Length - 1
txtdebug.Text &= Hex(a(i)) & " "
Next
txtdebug.Text &= "----------------------------------------------" & vbCrLf
txtdebug.Text &= "- Buscando valores de b y c" & vbCrLf
txtdebug.Text &= "Serial = aabbbbcccc" & vbCrLf
'
'c = sqr(a^2 + b^2)
'
txtdebug.Text &= "(1) c = raiz(a^2 + b^2)" & vbCrLf
txtdebug.Text &= "(2) c - b - 8 = 0" & vbCrLf
For i = 0 To a.Length - 1 ' todas las posibilidades de a
For b = 0 To 65535 'b -> 0000 - FFFF
c = Math.Sqrt(a(i) ^ 2 + b ^ 2)
If c.Contains(".") Then 'busco enteros
Else
cx = c - b - 8
cx = Hex(cx).PadLeft(4, "0"c)
lbl_info.Text = cx
If cx = "0000" Then
txtdebug.Text &= " (1) " & Hex(c).PadLeft(4, "0"c) & " = raiz(" & Hex(a(i)).PadLeft(2, "0"c) & "^2 + " & Hex(b).PadLeft(4, "0"c) & "^2)" & vbCrLf
lst_serials.Items.Add(Hex(a(i)).PadLeft(2, "0"c) & Hex(b).PadLeft(4, "0"c) & Hex(c).PadLeft(4, "0"c))
txtdebug.Text &= " (2) " & Hex(c).PadLeft(4, "0"c) & " - " & Hex(b).PadLeft(4, "0"c) & " - 8 = 0" & vbCrLf
End If
End If
Application.DoEvents()
Next
Next
lbl_info.Text = "Búsqueda finalizada"
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try


Se nos entrega un html con un juego que consiste en hacer clic en un botón tantas veces como podamos para ganar al juego. Acompañando al html tenemos un código javascript ofuscado aparentemente con Obfuscator.io
El código javascript ofuscado tiene el siguiente aspecto.
(function(_0x144932, _0xfc69c5) { var _0x187428 = _0x588c,
_0x3064c6 = _0x144932(); while (!![]) { try { var _0xb96d19 = -parseInt(_0x187428(0x1bd)) / 0x1 + parseInt(_0x187428(0x137)) / 0x2 + -parseInt(_0x187428(0x140)) / 0x3 * (-parseInt(_0x187428(0x13b)) / 0x4) + parseInt(_0x187428(0x15e)) / 0x5 * (parseInt(_0x187428(0x18c)) / 0x6) + -parseInt(_0x187428(0x159)) / 0x7 * (parseInt(_0x187428(0x1c3)) / 0x8) + parseInt(_0x187428(0x129)) / 0x9 * (-parseInt(_0x187428(0x149)) / 0xa) + parseInt(_0x187428(0x16d)) / 0xb; if (_0xb96d19 === _0xfc69c5) break;
else _0x3064c6['push'](_0x3064c6['shift']()); } catch (_0x377a04) { _0x3064c6['push'](_0x3064c6['shift']()); } } }(_0x5be3, 0x6fe59));;
function _0x5be3() { var _0x5a2048 = ['Utf8', 'push', 'createDecryptor', 'Malformed\x20UTF-8\x20data', '_ENC_XFORM_MODE', 'string', '_prevBlock', 'extend', '_doReset', 'AnsiX923', 'error', 'fromCharCode', 'object', '130340XnpiqM', '#res', 'HmacSHA256', 'DES', '4FuuDzS', 'finalize', 'byteLength', 'salt', '_keyPriorReset', '372669TnxSTf', '_xformMode', 'HMAC', 'stringify', 'Latin1', 'cfg', 'start', 'padding', 'show', '47650FNyFfQ', 'parse', 'TripleDES', 'MD5', 'ZeroPadding', 'length', 'Rabbit', 'console', 'Base', 'HmacSHA3', 'kdf', '_doFinalize', 'drop', 'BufferedBlockAlgorithm', 'Cipher', 'HmacSHA1', '7PKTjbP', 'CTR', '_reverseMap', 'clone', 'Encryptor', '43750GcrrDy', 'SHA384', 'byteOffset', 'indexOf', 'Word', '#loader', 'algo', 'apply', 'bind', 'HmacMD5', 'Base64', '_key', 'createEncryptor', 'min', '_counter', '4232173VijiOj', '_lBlock', 'You\x20Lose', 'ECB', 'BlockCipherMode', 'ciphertext', 'floor', 'constructor', 'log', 'search', 'flush', 'Iso10126', 'update', 'NoPadding', 'max', 'HmacSHA384', 'SHA512', 'decrypt', '_des2', '(((.+)+)+)+$', 'high', 'U2FsdGVkX189ylLOlgNSxq/TLeSxBr7cYDcRBgMXXS9VmwusKZx1XFDNdpkwg24v98iYGnUATwQVyWwFnlyoCg==', 'charAt', 'return\x20(function()\x20', 'pow', 'prototype', 'compute', 'reset', 'warn', '_rBlock', 'HmacSHA512', '498WcTnbi', 'join', 'HmacSHA224', 'display', '#trim', 'StreamCipher', 'enc', 'click', '#statistic', 'random', 'keySize', 'SHA1', '_process', 'sigBytes', '$super', '#mybut', 'clamp', 'RC4', '_invSubKeys', 'loader', '_cipher', '#backstart', 'BlockCipher', 'formatter', 'WordArray', 'slice', 'decryptBlock', '_minBufferSize', 'execute', '_iv', '_hash', 'mixIn', '__proto__', 'text', 'concat', 'RabbitLegacy', 'ceil', 'splice', 'Utf16LE', 'array[0]', 'function', 'SHA3', 'charCodeAt', 'EvpKDF', 'toString', 'css', '_state', 'Decryptor', 'Hex', '82275HVLogQ', 'ivSize', 'encrypt', '_des1', '_mode', 'words', '5353976ERfqhe', 'init', '_hasher', '_createHmacHelper', '_oKey', 'lib', 'CipherParams', 'x64', '_keySchedule', 'RC4Drop', 'PBKDF2', '__creator', '_subKeys', '_nDataBytes', '_DEC_XFORM_MODE', 'format', 'substr', '_createHelper', '_data', '_parse', '#cssfilter', '_map', 'create', 'OpenSSL', 'hasher', 'table', 'key', 'hide', 'iterations', '#timecount', 'undefined', 'pad', '_iKey', 'CBC', 'OFB', '#apply', 'SHA224', '_keystream', 'mode', 'exception', 'call', 'hasOwnProperty', 'Utf16', 'encryptBlock', '_invKeySchedule', '#buttons', '_doCryptBlock', 'RIPEMD160', 'blockSize', 'low', '_des3', '{}.constructor(\x22return\x20this\x22)(\x20)', '_nRounds', 'Hasher', 'addClass', 'AES', 'CFB', 'sqrt', '171bibExj'];
_0x5be3 = function() { return _0x5a2048; }; return _0x5be3(); }
...
Deofuscado el código con la web Obfuscator.io llegamos a interpretar la estructura aunque sigue siendo un galimatías en gran parte. Aún así, si nos fijamos al final encontramos una parte interesante con una clave encriptada cuya desencriptación depende de una comparación (línea 13 resaltada). Invirtiendo esa comparación engañamos al código y obtenemos la flag.
$("#mybut").click(function () {
_0x4171cc++;
$("#cssfilter").text(_0x4171cc);
});
$("#statistic").addClass("hide");
_0x35a8b9 = setInterval(function () {
_0x97292c--;
$("#timecount").text(_0x97292c);
if (_0x97292c == 0x0) {
clearInterval(_0x35a8b9);
$("#mybut").hide();
$("#buttons").show(0x64);
if (_0x4171cc > _0x43eef3) {
$('#trim').text(CryptoJS.AES.decrypt("U2FsdGVkX189ylLOlgNSxq/TLeSxBr7cYDcRBgMXXS9VmwusKZx1XFDNdpkwg24v98iYGnUATwQVyWwFnlyoCg==", "array[0]").toString(CryptoJS.enc.Utf8));
} else {
$("#trim").text("You Lose");
}
}
}, 0x3e8);
intervalcomp = setInterval(function () {
_0x43eef3++;
$('#apply').text(_0x43eef3);
if (_0x97292c == 0x0) {
clearInterval(intervalcomp);
}
}, 0x32);
}
});

AperiSolve es un conjunto de herramientas de análisis esteganográfico que nos ayuda a echar un primer vistazo cuando sospechamos que una imagen esconde algo.
Zsteg es una herramienta especializada en la detección y extracción de información oculta en imágenes, especialmente en formatos PNG y BMP. Está orientada a la esteganografía basada en bit-planes y es muy popular en entornos CTF y análisis forense, gracias a su capacidad para automatizar búsquedas exhaustivas de datos escondidos en los bits menos significativos (LSB) y en configuraciones de color poco habituales. Su principal fortaleza es que no se limita a examinar un único plano: prueba sistemáticamente combinaciones de canales (R, G, B, A), número de bits, orden de lectura y posicionamiento, detectando patrones que podrían pasar inadvertidos en una revisión manual.
Entre sus características más destacadas se encuentran la identificación automática de firmas de archivos (ZIP, PNG, texto ASCII, GZIP, etc.), la extracción directa de bitstreams reconstruidos y el soporte para diferentes rutas de exploración, como
b1,rgb,lsb,xy, que describen exactamente cómo se han recuperado los datos. Esta capacidad de correlacionar parámetros técnicos con resultados concretos convierte a zsteg en una herramienta muy eficiente tanto para localizar contenido oculto como para entender la técnica esteganográfica aplicada.
En AperiSolve se utiliza únicamente la parte de Zsteg encargada de ejecutar el análisis automático y devolver todas las detecciones posibles de esteganografía LSB en imágenes PNG y BMP. Concretamente, AperiSolve llama al comando zsteg <imagen> tal como está implementado en el módulo analyze_zsteg , y captura la salida completa línea por línea. Esta salida incluye todas las combinaciones probadas de bit-planes (b1, b2…), canales (r, g, b, a), orden de bits (lsb/msb) y métodos de recorrido (xy), junto con cualquier coincidencia que zsteg reconozca como firma de archivo o texto. Es decir, AperiSolve no aplica filtros ni interpretación adicional: muestra exactamente lo que zsteg detecta y lo organiza para que el usuario pueda identificar rápidamente si existe un archivo embebido, contenido ASCII, o algún patrón de interés.
En AperiSolve veremos algo como esto:
... b1,a,lsb,xy .. b1,a,msb,xy .. b1,rgb,lsb,xy .. file: Zip archive data, at least v1.0 to extract, compression method=store b1,rgb,msb,xy .. b1,bgr,lsb,xy .. b1,bgr,msb,xy .. b1,rgba,lsb,xy .. b1,rgba,msb,xy .. file: OpenPGP Public Key b1,abgr,lsb,xy .. b1,abgr,msb,xy .. file: OpenPGP Secret Key b2,r,lsb,xy .. b2,r,msb,xy .. text: "P@UPUUPAAUU@" b2,g,lsb,xy .. text: "(ahOFyIS!" ...
Para entender mejor a que se refiere todo esto vamos a repasar lo básico.
Cuando hablamos de esteganografía en imágenes PNG/BMP, nos referimos a manipular bits dentro de los canales de color (R, G, B, A). Cada canal tiene un valor de 0–255, es decir, 8 bits:
R = 11001010 G = 00110101 B = 11100001
LSB — Least Significant Bit (bit menos significativo). Es el bit más débil, el de la derecha:
1100101[0] ← LSB
Modificarlo cambia muy poco el color, por eso se usa en esteganografía.
Ejemplo: cambiar 11001010 ↦ 11001011 no cambia el color perceptible.
MSB — Most Significant Bit (bit más significativo). Es el bit más importante, el de la izquierda:
[1]1001010 ← MSB
Modificarlo sí altera mucho el color. A veces se usa pero suele ser detectable.
Cuando Zsteg muestra una línea del estilo b1,rgb,lsb,xy .. file: Zip archive data, está indicando que ha analizado la imagen extrayendo bits según la ruta especificada —en este caso, 1 bit por píxel (b1), combinando los canales rojo, verde y azul (rgb), utilizando el bit menos significativo (lsb) y recorriendo los píxeles en orden normal de lectura (xy)— y que, tras recomponer esos bits, el resultado coincide con la cabecera reconocible de un tipo de archivo real. Por eso aparece “file: Zip archive data”: significa que los bits ocultos forman un flujo válido cuya firma corresponde a un archivo ZIP. En otras ocasiones puede detectar texto ASCII, PNG, JPEG u otros formatos. En resumen, cuando Zsteg muestra esta línea no solo indica dónde se ocultan los datos, sino que confirma que lo recuperado es un archivo auténtico y probablemente extraíble, ya que la estructura binaria coincide con un formato conocido.
Si vemos que Zsteg nos ofrece algo interesante, podemos extraerlo mediante el comando:
zsteg -E b1,rgb,lsb,xy imagen.png > dump.bin
También es habitual usar herramientas como StegSolve. En este caso debemos dirigirnos a Analyse > Data extract para comprobar lo encontrado por zsteg y extraerlo mediante Save Bin.
| Zsteg | > Significado < | StegSolve |
|---|---|---|
| b1 | Extrae 1 bit por canal (bit plano 0, el menos significativo). | En Bit Planes, marca Red 0, Green 0, Blue 0. Solo esos. |
| rgb | Usa R + G + B en ese orden para reconstruir los bytes. | En Bit Plane Order, selecciona RGB. |
| lsb | Lee los bits empezando por el LSB (bit 0) antes que el MSB. | En Bit Order, selecciona LSB First. |
| xy | Recorre la imagen por filas (izquierda → derecha, arriba → abajo). | En Extract By, elige Row. |

Más allá de este caso concreto, conviene recordar que la esteganografía no se limita a los LSB: existen métodos basados en paletas, metadatos, manipulación de PNG chunks, secuencias alfa, audio incrustado o capas completas en formatos no comprimidos. Por ello, un análisis completo debería combinar la búsqueda clásica de LSB con herramientas complementarias como binwalk, foremost, exiftool, strings, o incluso análisis manual de cabeceras hexadecimales.