Blooper Tech Movie V – Algo huele a podrido en Hawkins

He de iniciar esta entrada diciendo que la segunda temporada de Stranger Things es sencillamente genial. Son 9 horas intensas que no dejan indiferente a nadie y además en el capítulo 8 nos han dejado una de esas perlas informáticas que tanto nos gustan.
La escena la protagoniza Bob Newby, un buen hombre amante de la electrónica de aquella época que trabaja en RadioShack y transcurre en el laboratorio secreto de Hawkins. En un momento dado, Bob propone «saltarse» la seguridad del laboratorio y para ello se traslada al sótano donde se encuentran los «servidores».
El PC de la época
Para comprender esta escena hay que situarse temporalmente. Estamos hablando de los años 80, en concreto la escena transcurre en 1984 y los equipos de los que dispone el laboratorio son unos maravillosos IBM. No se llega a apreciar bien el modelo de IBM utilizado pero teniendo en cuenta que el monitor que aparece es un terminal IBM 3180, la búsqueda se reduce a los sistemas compatibles S/36, S/38, AS/400, 5294 ó 5394.
Cracking BASIC or BASIC Cracking?
La escena plantea un ataque de fuerza bruta a un código de 4 dígitos como se puede observar en la imagen a continuación. Esto puede parecer una chorrada hoy día pero podía suponer un pequeño reto para un micro de 8 bits.
A simple vista se aprecian una serie de bucles recursivos, una llamada a una función y una sentencia condicional. Desconozco si la sintaxis del lenguaje es la correcta pero mucho me temo que es más bien una mezcla de BASIC y pseudocódigo. Pero lo que más me ha llamado la atención sin duda es que la palabra THEN parece que se sale del monitor como si estuviera realizado en post-producción. Os invito a que ampliéis la imagen y comentéis lo que os parece a vosotr@s.
Os dejo aquí el código para los más curiosos.
10 DIM FourDigitPassword INTEGER 20 FOR i = 0 TO 9 30 FOR j = 0 TO 9 40 FOR k = 0 TO 9 50 FOR l = 0 TO 9 60 FourDigitPassword = getFourDigits (i,j,k,l) 70 IF checkPasswordMatch(FourDigitPassword) = TRUE THEN 80 GOTO 140 90 END 100 NEXT l 110 NEXT k 120 NEXT j 130 NEXT i 140 PRINT FourDigitPassword
Aunque la entrada está dentro del contexto de los Blooper Tech Movies, digamos que en esta ocasión no voy a ir más allá. La escena es creíble y queda bien integrada en la época en la que se desarrolla el capítulo. Por esto mismo, solamente espero que las temporadas venideras sean tan buenas y cuiden tanto los detalles como sus predecesoras.
Referencias
[1] Ficha IMDB – Stranger Thing
[3] IBM 3180
[4] BASIC
Solución al Crackme 3 de Sotanez

- Introducción
- Activar un botón en memoria
- Activar el botón de forma permanente
- Serial Hardcodeado
- Links
Introducción
Este crackme pertenece a la página de Karpoff Spanish Tutor. Data del año 2000 y está realizado en «Borland Delphi 6.0 – 7.0», además, para resolverlo deberemos activar un botón y conseguir la clave de registro. La principal dificultad proviene a la hora de activar el botón ya que el serial es en realidad un serial hardcodeado muy sencillo.
Activar un botón en memoria
Existen numerosas herramientas para facilitarnos esta tarea, una de las más conocidas en el entorno del Cracking es «Veoveo» realizado por Crack el Destripador & Marmota hace ya unos añitos. Con el crackme ejecutado, ejecutamos VeoVeo y nos aparece el icono en la barra de tareas, hacemos click derecho y elegimos Activar Botones (manual) y ya tenemos el botón activado. Claro está que en cada ejecución del Crackme debemos de Re-activarlo.
Activar el botón de forma permanente
Lo que siempre nos interesa es que el botón esté activado de forma permanente y eso nos exige un poco más de atención. En este caso nos enfrentamos a Delphi y no nos sirve ni Resource Hacker ni Dede. Cuando nos encontramos en un punto muerto el último recurso siempre es realizar un programa en Delphi con un botón activado y otro desactivado y compararlos con un editor hexadecimal para saber que cambia. Si hacemos esto llegaremos a la conclusión de que en Delphi el bit que equivale a desactivado es 8 y ha activado es 9. Con este simple cambio ya tenemos el crackme parcheado. Comentar que en este caso el crackme no tiene ningún timer ni ninguna rutina que desactive el botón de forma periódica, este es el caso más simple.
Serial Hardcodeado
Abrimos Ollydbg y en las «String references» encontramos los mensajes de versión registrada, pinchamos sobre ellos y vemos a simple vista la zona de comprobación del serial. Como podéis observar, el serial se vé a simple vista.
0045811A |. B8 10824500 MOV EAX,CrackMe3.00458210 ; ASCII "ESCRIBE ALGO JOER" 0045811F |. E8 D889FDFF CALL CrackMe3.00430AFC 00458124 |. EB 5C JMP SHORT CrackMe3.00458182 00458126 |> 807D FF 4F CMP BYTE PTR SS:[EBP-1],4F - O 0045812A |. 75 56 JNZ SHORT CrackMe3.00458182 0045812C |. 807D FE 41 CMP BYTE PTR SS:[EBP-2],41 - A 00458130 |. 75 50 JNZ SHORT CrackMe3.00458182 00458132 |. 807D FD 45 CMP BYTE PTR SS:[EBP-3],45 - E 00458136 |. 75 4A JNZ SHORT CrackMe3.00458182 00458138 |. 807D FC 4B CMP BYTE PTR SS:[EBP-4],4B - K 0045813C |. 75 44 JNZ SHORT CrackMe3.00458182 0045813E |. 807D FB 43 CMP BYTE PTR SS:[EBP-5],43 - C 00458142 |. 75 3E JNZ SHORT CrackMe3.00458182 00458144 |. 807D FA 41 CMP BYTE PTR SS:[EBP-6],41 - A 00458148 |. 75 38 JNZ SHORT CrackMe3.00458182 0045814A |. 807D F9 52 CMP BYTE PTR SS:[EBP-7],52 - R 0045814E |. 75 32 JNZ SHORT CrackMe3.00458182 00458150 |. 807D F8 4B CMP BYTE PTR SS:[EBP-8],4B - K 00458154 |. 75 2C JNZ SHORT CrackMe3.00458182 00458156 |. 807D F7 20 CMP BYTE PTR SS:[EBP-9],20 - 0045815A |. 75 26 JNZ SHORT CrackMe3.00458182 0045815C |. 807D F6 49 CMP BYTE PTR SS:[EBP-A],49 - I 00458160 |. 75 20 JNZ SHORT CrackMe3.00458182 00458162 |. 807D F5 4F CMP BYTE PTR SS:[EBP-B],4F - O 00458166 |. 75 1A JNZ SHORT CrackMe3.00458182 00458168 |. 807D F4 54 CMP BYTE PTR SS:[EBP-C],54 - T 0045816C |. 75 14 JNZ SHORT CrackMe3.00458182 0045816E |. 807D F3 20 CMP BYTE PTR SS:[EBP-D],20 - 00458172 |. 75 0E JNZ SHORT CrackMe3.00458182 00458174 |. 807D F2 41 CMP BYTE PTR SS:[EBP-E],41 - A 00458178 |. 75 08 JNZ SHORT CrackMe3.00458182 0045817A |. 807D F1 59 CMP BYTE PTR SS:[EBP-F],59 - Y 0045817E |. 75 02 JNZ SHORT CrackMe3.00458182 00458180 |. B3 01 MOV BL,1 00458182 |> 80FB 01 CMP BL,1 00458185 |. 75 4C JNZ SHORT CrackMe3.004581D3 00458187 |. BA 2C824500 MOV EDX,CrackMe3.0045822C 0045818C |. 8B86 F4020000 MOV EAX,DWORD PTR DS:[ESI+2F4] 00458192 |. E8 B5EBFDFF CALL CrackMe3.00436D4C 00458197 |. BA 48824500 MOV EDX,CrackMe3.00458248 ; ASCII "VERSION REGISTRADA :)" Serial = YA TOI KRACKEAO
Links
Blooper Tech Movie XIV – Parque Jurásico

En Parque Jurásico (1993), la informática no es solo un elemento narrativo, es una pieza clave del suspense y del conflicto. A diferencia de otras películas donde las pantallas muestran interfaces ficticias o visualmente espectaculares pero irreales, Parque Jurásico opta por una aproximación sorprendentemente sobria y auténtica.

Durante bastantes escenas, se nos muestran terminales, ventanas de código y comandos que, lejos de ser decorativos, pertenecen a sistemas reales utilizados por programadores profesionales de principios de los años 90. Este detalle, que puede pasar desapercibido para el público general, resulta especialmente interesante desde un punto de vista técnico. En otras palabras, el trabajo de producción es excelente y destaca como una de las películas más respetuosas con la informática real de su época.
No es “código de película”: es software real
Uno de los puntos más interesantes es que el código que aparece en pantalla no fue escrito para la película. No hay pseudocódigo, ni pantallas diseñadas solo para quedar bonitas en cámara. Lo que se ve es software real, ejecutándose en el entorno Macintosh Programmer’s Workshop (MPW), el kit oficial de Apple para desarrolladores en aquellos años. El sistema operativo que se reconoce es un Macintosh clásico (System 7) corriendo sobre máquinas de la serie Quadra, auténticos pepinos para la época. Vamos, que cuando John Hammond decía aquello de «no hemos reparado en gastos», también iba en serio en lo informático.

«No hemos reparado en gastos»
En este punto no se le puede reprochar demasiado a la película. En líneas generales es bastante fiel a la novela, aunque la resolución del problema de seguridad se aborda de forma distinta. En el libro es el ingeniero Ray Arnold quien detecta el fallo y consigue reconducir la situación. En la película, sin embargo, el personaje desaparece cuando va a los barracones a restablecer la corriente del parque, con el resultado que todos conocemos.
Lo curioso es que muchos personajes sí cambian de forma notable con respecto al libro, el niño es mayor y más friki de los ordenadores, Ray Arnold no muere y acaba salvando la situación, o Gennaro es más atlético y bastante más valiente. Sin embargo, el gran disparate técnico permanece intacto.
En la novela se menciona de pasada a un equipo de informáticos de Cambridge que supuestamente colaboró en el diseño del software. Aun así, la puesta en marcha y la explotación del sistema recaen prácticamente en una sola persona, Dennis Nedry. Evidentemente, tanto al libro como al guion les viene de perlas que todo dependa de una única persona para que el desastre sea posible, pero cuesta aceptar que en un parque donde todo está duplicado, el control informático central dependa de una sola persona.
Curiosamente, en uno de los monitores de Nedry se puede ver una foto de Oppenheimer con la frase «Beginning of baby boom», de la que podemos sacar la conclusión de que Nedry es perfectamente consciente de que su trabajo puede tener consecuencias catastróficas e irreversibles. También es un maravilloso guiño del equipo de producción que nos está indicando exactamente donde se va originar el desastre.
Al final, Parque Jurásico no va de dinosaurios, ni siquiera de genética. Va de personas. Y, más concretamente, de personas con demasiado poder y muy pocos compañeros de equipo y poca supervisión.
Desde el punto de vista informático, la película es casi entrañable. Todo es serio, profesional y real… hasta que descubrimos que el sistema más complejo jamás construido depende, en la práctica, de un solo programador cabreado, mal pagado y con demasiadas líneas de código en la cabeza. Ningún comité de arquitectura, ninguna auditoría externa, ningún segundo par de ojos. Solo Dennis Nedry y su teclado. ¿Qué podía salir mal?
Lo curioso es que ni la película ni el libro se molestan en disimularlo demasiado. Te hablan de sistemas redundantes, de seguridad, de control absoluto… pero el corazón digital del parque es un castillo de naipes. Eso sí, un castillo de naipes programado en máquinas de primera, con software real y pantallas que hoy siguen pareciendo más creíbles que muchas producciones actuales.
Quizá por eso Parque Jurásico envejece tan bien. Porque, incluso cuando se equivoca, lo hace con honestidad. No intenta venderte magia disfrazada de tecnología. Te muestra ordenadores de verdad, código de verdad y errores muy humanos. Y al final, tanto en la novela como en la película, el mensaje es el mismo, puedes clonar dinosaurios, diseñar parques imposibles y rodearte de la mejor tecnología del mundo, que si todo depende de una sola persona, tarde o temprano, el sistema se vendrá abajo.
Y no, el problema no eran los dinosaurios, nunca lo fueron.









Solución al KeygenMe1 (RSA200) de Dihux

- Introducción
- Funcionamiento de RSA
- OllyDbg
- Calculando un serial válido
- Ejemplo operacional
- Keygen
- Links
Introducción
Empezamos con lo que espero que sea una serie de crackmes RSA. En este caso en particular y como el propio autor nos adelanta, se trata de RSA-200.
En criptografía, RSA (Rivest, Shamir y Adleman) es un sistema criptográfico de clave pública desarrollado en 1977. Es el primer y más utilizado algoritmo de este tipo y es válido tanto para cifrar como para firmar digitalmente.
Funcionamiento de RSA
- Inicialmente es necesario generar aleatoriamente dos números primos grandes, a los que llamaremos p y q.
- A continuación calcularemos n como producto de p y q:
n = p * q
- Se calcula fi:
fi(n)=(p-1)(q-1)
- Se calcula un número natural e de manera que MCD(e, fi(n))=1 , es decir e debe ser primo relativo de fi(n). Es lo mismo que buscar un numero impar por el que dividir fi(n) que de cero como resto.
- Mediante el algoritmo extendido de Euclides se calcula d que es el inverso modular de e.
Puede calcularse d=((Y*fi(n))+1)/e para Y=1,2,3,... hasta encontrar un d entero.
- El par de números (e,n) son la clave pública.
- El par de números (d,n) son la clave privada.
- Cifrado: La función de cifrado es.
c = m^e mod n
- Descifrado: La función de descifrado es.
m = c^d mod n
OllyDbg
Con OllyDbg analizamos la parte del código que nos interesa.
00401065 |>push 19 ; /Count = 19 (25.) 00401067 |>push 00404330 ; |Buffer = dihux_ke.00404330 0040106C |>push 2711 ; |ControlID = 2711 (10001.) 00401071 |>push dword ptr [ebp+8] ; |hWnd 00401074 |>call <GetDlgItemTextA> ; \GetDlgItemTextA 00401079 |>cmp eax, 5 ; Tamaño nombre >= 5 0040107C |>jb 00401214 00401082 |>cmp eax, 14 ; Tamaño nombre <= 0x14 00401085 |>ja 00401214 0040108B |>mov [404429], eax 00401090 |>push 96 ; /Count = 96 (150.) 00401095 |>push 00404349 ; |Buffer = dihux_ke.00404349 0040109A |>push 2712 ; |ControlID = 2712 (10002.) 0040109F |>push dword ptr [ebp+8] ; |hWnd 004010A2 |>call <GetDlgItemTextA> ; \GetDlgItemTextA 004010A7 |>test al, al ........ 004010D8 |>xor ecx, ecx ; Case 0 of switch 004010B6 004010DA |>/push 0 004010DC |>|call <__BigCreate@4> 004010E1 |>|mov [ecx*4+404411], eax 004010E8 |>|inc ecx 004010E9 |>|cmp ecx, 6 004010EC |>\jnz short 004010DA 004010EE |>push dword ptr [404411] ; /Arg3 = 00B60000 004010F4 |>push 10 ; |16?? 004010F6 |>push 0040401F ; |Arg1 = 0040401F ASCII "8ACFB4D27CBC8C2024A30C9417BBCA41AF3FC3BD9BDFF97F89" 004010FB |>call <__BigIn@12> ; \dihux_ke.004013F3 00401100 |>push dword ptr [404415] ; /Arg3 = 00C70000 00401106 |>push 10 ; |Arg2 = 00000010 00401108 |>push 00404019 ; |Arg1 = 00404019 ASCII "10001" 0040110D |>call <__BigIn@12> ; \dihux_ke.004013F3 00401112 |>push dword ptr [404425] ; /Arg3 = 00CB0000 00401118 |>push 10 ; |Arg2 = 00000010 0040111A |>push 00404349 ; |Arg1 = 00404349 ASCII "123456789123456789" 0040111F |>call <__BigIn@12> ; \dihux_ke.004013F3 00401124 |>push 00404330 ; /String = "deurus" 00401129 |>call <lstrlenA> ; \lstrlenA 0040112E |>push dword ptr [404419] 00401134 |>push eax 00401135 |>push 00404330 ; ASCII "deurus" 0040113A |>call <__BigInB256@12> 0040113F |>push dword ptr [404421] ; c 00401145 |>push dword ptr [404411] ; n = 8ACFB4D27CBC8C2024A30C9417BBCA41AF3FC3BD9BDFF97F89 0040114B |>push dword ptr [404415] ; e = 10001 00401151 |>push dword ptr [404425] ; serial 00401157 |>call <__BigPowMod@16> ; c = serial^e (mod n) 0040115C |>mov eax, 1337 00401161 |>push 0 ; /Arg4 = 00000000 00401163 |>push dword ptr [40441D] ; |x 00401169 |>push eax ; |0x1337 0040116A |>push dword ptr [404421] ; |c 00401170 |>call <__BigDiv32@16> ; \x = c/0x1337 00401175 |>push dword ptr [40441D] ; x 0040117B |>push dword ptr [404419] ; nombre 00401181 |>call <__BigCompare@8> ; ¿x = nombre? 00401186 |>jnz short 0040119C 00401188 |>push 0 ; /Style = MB_OK|MB_APPLMODAL 0040118A |>push 00404014 ; |Title = "iNFO" 0040118F |>push 00404004 ; |Text = "Serial is valid" 00401194 |>push dword ptr [ebp+8] ; |hOwner 00401197 |>call <MessageBoxA> ; \MessageBoxA 0040119C |>xor ecx, ecx 0040119E |>/push dword ptr [ecx*4+404411] 004011A5 |>|call <__BigDestroy@4> 004011AA |>|inc ecx 004011AB |>|cmp ecx, 6 004011AE |>\jnz short 0040119E
Lo primero que observamos es que el código nos proporciona el exponente público (e) y el módulo (n).
- e = 10001
- n = 8ACFB4D27CBC8C2024A30C9417BBCA41AF3FC3BD9BDFF97F89
A continuación halla c = serial^d mod n. Finalmente Divide c entre 0x1337 y lo compara con el nombre.
Como hemos visto en la teoría de RSA, necesitamos hallar el exponente privado (d) para poder desencriptar, según la fórmula vista anteriormente.
- Fórmula original: m=c^d mod n
- Nuestra fórmula: Serial = x^d mod n. Siendo x = c * 0x1337
Calculando un serial válido
Existen varios ataques a RSA, nosotros vamos a usar el de factorización. Para ello vamos a usar la herramienta RSA Tool. Copiamos el módulo (n), el exponente público (e) y factorizamos (Factor N).
Hallados los primos p y q, hallamos d (Calc. D).
Una vez obtenido d solo nos queda obtener x, que recordemos es nombre * 0x1337.
Cuando decimos nombre nos referimos a los bytes del nombre en hexadecimal, para deurus serían 646575727573.
Ejemplo operacional
Nombre: deurus
x = 646575727573 * 0x1337 = 7891983BA4EC4B5 Serial = x^d mod n Serial = 7891983BA4EC4B5^32593252229255151794D86C1A09C7AFCC2CCE42D440F55A2D mod 8ACFB4D27CBC8C2024A30C9417BBCA41AF3FC3BD9BDFF97F89 Serial = FD505CADDCC836FE32E34F5F202E34D11F385DEAD43D87FCD
Como la calculadora de Windows se queda un poco corta para trabajar con números tan grandes, vamos a usar la herramienta Big Integer Calculator. A continuación os dejo unas imágenes del proceso.
Keygen
En esta ocasión hemos elegido Java ya que permite trabajar con números grandes de forma sencilla, os dejo el código más importante.
JButton btnNewButton = new JButton("Generar");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
BigInteger serial = new BigInteger("0");
BigInteger n = new BigInteger("871332984042175151665553882265818310920539633758381377421193");//módulo
BigInteger d = new BigInteger("316042180198461106401603389463895139535543421270452849695277");//exponente privado
BigInteger x = new BigInteger("4919");//0x1337
String nombre = t1.getText();
BigInteger nombre2 = new BigInteger(nombre.getBytes());
nombre2 = nombre2.multiply(x);
serial = nombre2.modPow(d, n);
t2.setText(serial.toString(16).toUpperCase());
}
});
Links
- RSA (wikipedia)
- El algoritmo RSA y la factorización de números grandes (recomendable)
- Exponenciación modular (wikipedia)
- RSA Tool
- Big Integer Calculator v1.14
- Crackme
- Keygen
Migas de pan

Se nos entrega el siguiente ELF:
/* This file was generated by the Hex-Rays decompiler version 8.4.0.240320.
Copyright (c) 2007-2021 Hex-Rays <info@hex-rays.com>
Detected compiler: GNU C++
*/
#include <defs.h>
//-------------------------------------------------------------------------
// Function declarations
__int64 (**init_proc())(void);
void sub_1020();
// int printf(const char *format, ...);
// int getchar(void);
// int fflush(FILE *stream);
// __int64 __isoc99_scanf(const char *, ...); weak
// void __noreturn exit(int status);
// int __fastcall _cxa_finalize(void *);
void __fastcall __noreturn start(__int64 a1, __int64 a2, void (*a3)(void));
FILE **deregister_tm_clones();
__int64 register_tm_clones(); // weak
FILE **_do_global_dtors_aux();
__int64 __fastcall frame_dummy(_QWORD, _QWORD, _QWORD); // weak
int __fastcall main(int argc, const char **argv, const char **envp);
_BOOL8 __fastcall comprobacion(char a1, char a2, int a3);
void _libc_csu_fini(void); // idb
void term_proc();
// int __fastcall _libc_start_main(int (__fastcall *main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void *stack_end);
// int __fastcall __cxa_finalize(void *);
// __int64 _gmon_start__(void); weak
//-------------------------------------------------------------------------
// Data declarations
_UNKNOWN _libc_csu_init;
__int64 (__fastcall *_frame_dummy_init_array_entry)() = &frame_dummy; // weak
__int64 (__fastcall *_do_global_dtors_aux_fini_array_entry)() = &_do_global_dtors_aux; // weak
void *_dso_handle = &_dso_handle; // idb
char *a = "MdfnJk"; // weak
char *b = "jYx}"; // weak
char *c = "gWmfk"; // weak
char *d = "mlvpc"; // weak
char *f = "neU++w"; // weak
FILE *_bss_start; // idb
char completed_0; // weak
//----- (0000000000001000) ----------------------------------------------------
__int64 (**init_proc())(void)
{
__int64 (**result)(void); // rax
result = &_gmon_start__;
if ( &_gmon_start__ )
return (__int64 (**)(void))_gmon_start__();
return result;
}
// 40D0: using guessed type __int64 _gmon_start__(void);
//----- (0000000000001020) ----------------------------------------------------
void sub_1020()
{
JUMPOUT(0LL);
}
// 1026: control flows out of bounds to 0
//----- (0000000000001090) ----------------------------------------------------
// positive sp value has been detected, the output may be wrong!
void __fastcall __noreturn start(__int64 a1, __int64 a2, void (*a3)(void))
{
__int64 v3; // rax
int v4; // esi
__int64 v5; // [rsp-8h] [rbp-8h] BYREF
char *retaddr; // [rsp+0h] [rbp+0h] BYREF
v4 = v5;
v5 = v3;
_libc_start_main(
(int (__fastcall *)(int, char **, char **))main,
v4,
&retaddr,
(void (*)(void))_libc_csu_init,
_libc_csu_fini,
a3,
&v5);
__halt();
}
// 1096: positive sp value 8 has been found
// 109D: variable 'v3' is possibly undefined
//----- (00000000000010C0) ----------------------------------------------------
FILE **deregister_tm_clones()
{
return &_bss_start;
}
//----- (00000000000010F0) ----------------------------------------------------
__int64 register_tm_clones()
{
return 0LL;
}
// 10F0: using guessed type __int64 register_tm_clones();
//----- (0000000000001130) ----------------------------------------------------
FILE **_do_global_dtors_aux()
{
FILE **result; // rax
if ( !completed_0 )
{
if ( &__cxa_finalize )
_cxa_finalize(_dso_handle);
result = deregister_tm_clones();
completed_0 = 1;
}
return result;
}
// 4080: using guessed type char completed_0;
//----- (0000000000001175) ----------------------------------------------------
int __fastcall main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+7h] [rbp-9h] BYREF
int v5; // [rsp+8h] [rbp-8h]
bool v6; // [rsp+Fh] [rbp-1h]
v6 = 1;
v5 = 0;
while ( v6 )
{
fflush(_bss_start);
printf("\nIntroduce la letra correcta:\t");
__isoc99_scanf("%c", &v4);
getchar();
if ( v5 > 5 )
{
if ( v5 > 9 )
{
if ( v5 > 14 )
{
if ( v5 > 19 )
v6 = comprobacion(v4, f[v5 - 20], 10);
else
v6 = comprobacion(v4, d[v5 - 15], 2);
}
else
{
v6 = comprobacion(v4, c[v5 - 10], 8);
}
}
else
{
v6 = comprobacion(v4, b[v5 - 6], 17);
}
}
else
{
v6 = comprobacion(v4, a[v5], 5);
}
if ( !v6 )
{
printf("Incorrecta");
exit(1);
}
printf("\n%c\n", (unsigned int)v4);
if ( v5 == 25 )
{
printf("Ya tienes la flag!!");
exit(1);
}
++v5;
}
return 0;
}
// 1060: using guessed type __int64 __isoc99_scanf(const char *, ...);
// 4050: using guessed type char *a;
// 4058: using guessed type char *b;
// 4060: using guessed type char *c;
// 4068: using guessed type char *d;
// 4070: using guessed type char *f;
//----- (0000000000001352) ----------------------------------------------------
_BOOL8 __fastcall comprobacion(char a1, char a2, int a3)
{
return a1 == (a3 ^ a2);
}
//----- (0000000000001390) ----------------------------------------------------
void __fastcall _libc_csu_init(unsigned int a1, __int64 a2, __int64 a3)
{
signed __int64 v4; // rbp
__int64 i; // rbx
init_proc();
v4 = &_do_global_dtors_aux_fini_array_entry - &_frame_dummy_init_array_entry;
if ( v4 )
{
for ( i = 0LL; i != v4; ++i )
((void (__fastcall *)(_QWORD, __int64, __int64))*(&_frame_dummy_init_array_entry + i))(a1, a2, a3);
}
}
// 1170: using guessed type __int64 __fastcall frame_dummy(_QWORD, _QWORD, _QWORD);
// 3DE8: using guessed type __int64 (__fastcall *_frame_dummy_init_array_entry)();
// 3DF0: using guessed type __int64 (__fastcall *_do_global_dtors_aux_fini_array_entry)();
//----- (00000000000013F4) ----------------------------------------------------
void term_proc()
{
;
}
Extracción de la Flag
Si nos fijamos en las líneas 41 a la 45 vemos las siguientes cadenas:
a = «MdfnJk»
b = «jYx}»
c = «gWmfk»
d = «mlvpc»
f = «neU++w»
Usaremos las cadenas y los valores XOR especificados para cada rango de v5 en el main (líneas 123 a 182) para determinar los caracteres correctos.
Para v5 de 0 a 5: v6 = comprobacion(v4, a[v5], 5);
Significa que: v4 debe ser igual a a[v5] ^ 5
Para v5 de 6 a 9: v6 = comprobacion(v4, b[v5 – 6], 17);
Significa que: v4 debe ser igual a b[v5 – 6] ^ 17
Para v5 de 10 a 14: v6 = comprobacion(v4, c[v5 – 10], 8);
Significa que: v4 debe ser igual a c[v5 – 10] ^ 8
Para v5 de 15 a 19: v6 = comprobacion(v4, d[v5 – 15], 2);
Significa que: v4 debe ser igual a d[v5 – 15] ^ 2
Para v5 de 20 a 25: v6 = comprobacion(v4, f[v5 – 20], 10);
Significa que: v4 debe ser igual a f[v5 – 20] ^ 10
Cálculo de los caracteres correctos:
Para v5 de 0 a 5:
a[0] = ‘M’, M ^ 5 = 0x4D ^ 0x05 = 0x48 -> ‘H’
a[1] = ‘d’, d ^ 5 = 0x64 ^ 0x05 = 0x61 -> ‘a’
a[2] = ‘f’, f ^ 5 = 0x66 ^ 0x05 = 0x63 -> ‘c’
a[3] = ‘n’, n ^ 5 = 0x6E ^ 0x05 = 0x6B -> ‘k’
a[4] = ‘J’, J ^ 5 = 0x4A ^ 0x05 = 0x4F -> ‘O’
a[5] = ‘k’, k ^ 5 = 0x6B ^ 0x05 = 0x6E -> ‘n’
Resulta en la cadena: HackOn
Para v5 de 6 a 9:
b[0] = ‘j’, j ^ 17 = 0x6A ^ 0x11 = 0x7B -> ‘{‘
b[1] = ‘Y’, Y ^ 17 = 0x59 ^ 0x11 = 0x48 -> ‘H’
b[2] = ‘x’, x ^ 17 = 0x78 ^ 0x11 = 0x69 -> ‘i’
b[3] = ‘}’, } ^ 17 = 0x7D ^ 0x11 = 0x6C -> ‘l’
Resulta en la cadena: {Hil
Para v5 de 10 a 14:
c[0] = ‘g’, g ^ 8 = 0x67 ^ 0x08 = 0x6F -> ‘o’
c[1] = ‘W’, W ^ 8 = 0x57 ^ 0x08 = 0x5F -> ‘_’
c[2] = ‘m’, m ^ 8 = 0x6D ^ 0x08 = 0x65 -> ‘e’
c[3] = ‘f’, f ^ 8 = 0x66 ^ 0x08 = 0x6E -> ‘n’
c[4] = ‘k’, k ^ 8 = 0x6B ^ 0x08 = 0x63 -> ‘c’
Resulta en la cadena: o_enc
Para v5 de 15 a 19:
d[0] = ‘m’, m ^ 2 = 0x6D ^ 0x02 = 0x6F -> ‘o’
d[1] = ‘l’, l ^ 2 = 0x6C ^ 0x02 = 0x6E -> ‘n’
d[2] = ‘v’, v ^ 2 = 0x76 ^ 0x02 = 0x74 -> ‘t’
d[3] = ‘p’, p ^ 2 = 0x70 ^ 0x02 = 0x72 -> ‘r’
d[4] = ‘c’, c ^ 2 = 0x63 ^ 0x02 = 0x61 -> ‘a’
Resulta en la cadena: ontra
Para v5 de 20 a 25:
f[0] = ‘n’, n ^ 10 = 0x6E ^ 0x0A = 0x64 -> ‘d’
f[1] = ‘e’, e ^ 10 = 0x65 ^ 0x0A = 0x6F -> ‘o’
f[2] = ‘U’, U ^ 10 = 0x55 ^ 0x0A = 0x5F -> ‘_‘
f[3] = ‘+’, + ^ 10 = 0x2B ^ 0x0A = 0x21 -> ‘!’
f[4] = ‘+’, + ^ 10 = 0x2B ^ 0x0A = 0x21 -> ‘!’
f[5] = ‘w’, w ^ 10 = 0x77 ^ 0x0A = 0x7D -> ‘}’
Resulta en la cadena: do_!!}
Uniendo todas las partes, obtenemos la flag completa: HackOn{Hilo_enc_ontrado_!!}
VideoTutorial – KeyGen para el Crackme#2 de AfKayAs

Retos reversing de Challengeland.co

Introducción
Recién rescatados del inframundo que es mi disco duro, os traigo un paquete de seis crackmes facilones para vuestro uso y disfrute. Desgraciadamente ya no está en activo la web de retos de donde los saqué así que os los dejo en descargas.
Los cuatro primero están realizados en Dev-C++ 4.9.9.2 siendo de estilo consola de comandos. Los dos restantes compilados con MingWin32 GCC 3.x carecen de GUI y vamos, que no se han esmerado mucho en darles forma.
Level 1
No cuesta mucho dar con el código interesante mediante las referencias de texto. En Ollydbg clic derecho sobre el código y Search for > All referenced text strings.
004012E1 |. 8845 E8 MOV BYTE PTR SS:[EBP-18],AL ; |||| 004012E4 |. C70424 11304000 MOV DWORD PTR SS:[ESP],level1.00403011 ; ||||ASCII "Input Serial: " 004012EB |. E8 C0050000 CALL <JMP.&msvcrt.printf> ; |||\printf 004012F0 |. 8D45 C8 LEA EAX,[LOCAL.14] ; ||| 004012F3 |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; ||| 004012F7 |. C70424 20304000 MOV DWORD PTR SS:[ESP],level1.00403020 ; |||ASCII "%s" 004012FE |. E8 9D050000 CALL <JMP.&msvcrt.scanf> ; ||\scanf 00401303 |. 8D45 D8 LEA EAX,[LOCAL.10] ; || 00401306 |. 8D55 C8 LEA EDX,[LOCAL.14] ; || 00401309 |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; || 0040130D |. 891424 MOV DWORD PTR SS:[ESP],EDX ; ||level1.00403022 00401310 |. E8 7B050000 CALL <JMP.&msvcrt.strcmp> ; |\strcmp 00401315 |. 8945 C4 MOV [LOCAL.15],EAX ; | 00401318 |. 837D C4 00 CMP [LOCAL.15],0 ; | 0040131C |. 75 0E JNZ SHORT level1.0040132C ; | 0040131E |. C70424 23304000 MOV DWORD PTR SS:[ESP],level1.00403023 ; |ASCII "Well done. \n" 00401325 |. E8 86050000 CALL <JMP.&msvcrt.printf> ; \printf 0040132A |. EB 0C JMP SHORT level1.00401338 0040132C |> C70424 30304000 MOV DWORD PTR SS:[ESP],level1.00403030 ; |ASCII "Wrong. \n" 00401333 |. E8 78050000 CALL <JMP.&msvcrt.printf> ; \printf 00401338 |> C70424 39304000 MOV DWORD PTR SS:[ESP],level1.00403039 ; |ASCII "PAUSE" 0040133F |. E8 3C050000 CALL <JMP.&msvcrt.system> ; \system 00401344 |. B8 00000000 MOV EAX,0 00401349 |. C9 LEAVE 0040134A \. C3 RETN
La madre del cordero está en la dirección 401310 que es donde se lleva a cabo la función de comparación strcmp.
756296A0 msvcrt.strcmp 8B5424 04 MOV EDX,DWORD PTR SS:[ESP+4] 756296A4 8B4C24 08 MOV ECX,DWORD PTR SS:[ESP+8] 756296A8 F7C2 03000000 TEST EDX,3 ; 0-3 = 4 bucles. Divide la comprobación en 4 bloques 756296AE 75 3C JNZ SHORT msvcrt.756296EC ; salta si hemos terminado los 4 bucles 756296B0 > 8B02 MOV EAX,DWORD PTR DS:[EDX] ; coge 4 caracteres del serial (INICIO BUCLE) 756296B2 3A01 CMP AL,BYTE PTR DS:[ECX] ; compara el 1º/5º/9º/13º dígito en función del bucle 756296B4 75 2E JNZ SHORT msvcrt.756296E4 ; salto a zona mala 756296B6 0AC0 OR AL,AL 756296B8 74 26 JE SHORT msvcrt.756296E0 756296BA 3A61 01 CMP AH,BYTE PTR DS:[ECX+1] ; compara el 2º/6º/10º/14º dígito en función del bucle 756296BD 75 25 JNZ SHORT msvcrt.756296E4 ; salto a zona mala 756296BF 0AE4 OR AH,AH 756296C1 74 1D JE SHORT msvcrt.756296E0 756296C3 C1E8 10 SHR EAX,10 756296C6 3A41 02 CMP AL,BYTE PTR DS:[ECX+2] ; compara el 3º/7º/11º/15º dígito en función del bucle 756296C9 75 19 JNZ SHORT msvcrt.756296E4 ; salto a zona mala 756296CB 0AC0 OR AL,AL 756296CD 74 11 JE SHORT msvcrt.756296E0 756296CF 3A61 03 CMP AH,BYTE PTR DS:[ECX+3] ; compara el 4º/8º/12º/16º dígito en función del bucle 756296D2 75 10 JNZ SHORT msvcrt.756296E4 ; salto a zona mala 756296D4 83C1 04 ADD ECX,4 756296D7 83C2 04 ADD EDX,4 756296DA 0AE4 OR AH,AH 756296DC ^ 75 D2 JNZ SHORT msvcrt.756296B0 ; Si no hemos terminado... 756296DE 8BFF MOV EDI,EDI 756296E0 33C0 XOR EAX,EAX ; EAX = 0 que es lo deseado 756296E2 C3 RETN ; salimos de la función superando la comprobación 756296E3 90 NOP 756296E4 1BC0 SBB EAX,EAX ; Zona mala 756296E6 D1E0 SHL EAX,1 756296E8 83C0 01 ADD EAX,1 ; EAX = 1 implica bad boy 756296EB C3 RETN ; salimos de la función
Si atendemos al volcado vemos el serial bueno Kcgcv8LsmV3nizfJ.
0060FEF0 31 32 33 34 35 36 37 38 39 30 00 75 40 19 18 00 1234567890.u@. 0060FF00 4B 63 67 63 76 38 4C 73 6D 56 33 6E 69 7A 66 4A Kcgcv8LsmV3nizfJ
Curiosamente, si introducimos el serial bueno el crackme no lo acepta. Fijándome en la comprobación veo que al introducir un serial de 16 caracteres inserta un carácter nulo (0x00) alterando el serial correcto y falseando la comprobación.
0060FEF0 4B 63 67 63 76 38 4C 73 6D 56 33 6E 69 7A 66 4A Kcgcv8LsmV3nizfJ 0060FF00 00 63 67 63 76 38 4C 73 6D 56 33 6E 69 7A 66 4A .cgcv8LsmV3nizfJ
Ahora ya no podemos comprobarlo pero recuerdo que la web consideraba válido el serial Kcgcv8LsmV3nizfJ, por lo que considero lo anteriormente citado un bug o un intento de despiste del autor.
Level 2
Es exactamente igual que el anterior cambiando el serial por 6LPw3vDYja9KrT2V.
Level 3
La comprobación del serial es igual a las dos anteriores pero añade una función intermedia que suma 0xD a cada carácter de nuestro serial
00401355 |. A1 03304000 MOV EAX,DWORD PTR DS:[403003] ; || 0040135A |. 8945 E8 MOV [LOCAL.6],EAX ; || 0040135D |. A1 07304000 MOV EAX,DWORD PTR DS:[403007] ; || 00401362 |. 8945 EC MOV [LOCAL.5],EAX ; || 00401365 |. A1 0B304000 MOV EAX,DWORD PTR DS:[40300B] ; || 0040136A |. 8945 F0 MOV [LOCAL.4],EAX ; || 0040136D |. A1 0F304000 MOV EAX,DWORD PTR DS:[40300F] ; || 00401372 |. 8945 F4 MOV [LOCAL.3],EAX ; || 00401375 |. C70424 13304000 MOV DWORD PTR SS:[ESP],level3.00403013 ; ||ASCII "Input Serial: " 0040137C |. E8 CF050000 CALL <JMP.&msvcrt.printf> ; |\printf 00401381 |. 8D45 D8 LEA EAX,[LOCAL.10] ; | 00401384 |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; | 00401388 |. C70424 00304000 MOV DWORD PTR SS:[ESP],level3.00403000 ; |ASCII "%s" 0040138F |. E8 AC050000 CALL <JMP.&msvcrt.scanf> ; \scanf 00401394 |. 8D5D E8 LEA EBX,[LOCAL.6] 00401397 |. 8D45 D8 LEA EAX,[LOCAL.10] 0040139A |. 890424 MOV DWORD PTR SS:[ESP],EAX 0040139D |. E8 EEFEFFFF CALL level3.00401290 ; NUEVA FUNCIÓN SUMA 004013A2 |. 895C24 04 MOV DWORD PTR SS:[ESP+4],EBX ; || 004013A6 |. 890424 MOV DWORD PTR SS:[ESP],EAX ; || 004013A9 |. E8 82050000 CALL <JMP.&msvcrt.strcmp> ; |\strcmp 004013AE |. 8945 D4 MOV [LOCAL.11],EAX ; | 004013B1 |. 837D D4 00 CMP [LOCAL.11],0 ; | 004013B5 |. 75 0E JNZ SHORT level3.004013C5 ; | 004013B7 |. C70424 22304000 MOV DWORD PTR SS:[ESP],level3.00403022 ; |ASCII "Well done." 004013BE |. E8 8D050000 CALL <JMP.&msvcrt.printf> ; \printf 004013C3 |. EB 0C JMP SHORT level3.004013D1 004013C5 |> C70424 2D304000 MOV DWORD PTR SS:[ESP],level3.0040302D ; |ASCII "Wrong. \n" 004013CC |. E8 7F050000 CALL <JMP.&msvcrt.printf> ; \printf 004013D1 |> C70424 36304000 MOV DWORD PTR SS:[ESP],level3.00403036 ; |ASCII "PAUSE" 004013D8 |. E8 43050000 CALL <JMP.&msvcrt.system> ; \system 004013DD |. B8 00000000 MOV EAX,0 004013E2 |. 8B5D FC MOV EBX,[LOCAL.1] 004013E5 |. C9 LEAVE 004013E6 \. C3 RETN -------- 004012A4 |> /8B45 08 /MOV EAX,[ARG.1] ; | 004012A7 |. |890424 |MOV DWORD PTR SS:[ESP],EAX ; | 004012AA |. |E8 B1060000 |CALL <JMP.&msvcrt.strlen> ; \strlen 004012AF |. |3945 FC |CMP [LOCAL.1],EAX 004012B2 |. |73 1C |JNB SHORT level3.004012D0 004012B4 |. |8B45 08 |MOV EAX,[ARG.1] 004012B7 |. |8B55 FC |MOV EDX,[LOCAL.1] 004012BA |. |01C2 |ADD EDX,EAX 004012BC |. |8B45 08 |MOV EAX,[ARG.1] 004012BF |. |0345 FC |ADD EAX,[LOCAL.1] 004012C2 |. |0FB600 |MOVZX EAX,BYTE PTR DS:[EAX] 004012C5 |. |04 0D |ADD AL,0D ; char + 0xD 004012C7 |. |8802 |MOV BYTE PTR DS:[EDX],AL 004012C9 |. |8D45 FC |LEA EAX,[LOCAL.1] 004012CC |. |FF00 |INC DWORD PTR DS:[EAX] 004012CE |.^\EB D4 \JMP SHORT level3.004012A4 -------- 756296A0 msvcrt.strcmp 8B5424 04 MOV EDX,DWORD PTR SS:[ESP+4] 756296A4 8B4C24 08 MOV ECX,DWORD PTR SS:[ESP+8] 756296A8 F7C2 03000000 TEST EDX,3 756296AE 75 3C JNZ SHORT msvcrt.756296EC 756296B0 8B02 MOV EAX,DWORD PTR DS:[EDX] 756296B2 3A01 CMP AL,BYTE PTR DS:[ECX] 756296B4 75 2E JNZ SHORT msvcrt.756296E4 756296B6 0AC0 OR AL,AL 756296B8 74 26 JE SHORT msvcrt.756296E0 756296BA 3A61 01 CMP AH,BYTE PTR DS:[ECX+1] 756296BD 75 25 JNZ SHORT msvcrt.756296E4 756296BF 0AE4 OR AH,AH 756296C1 74 1D JE SHORT msvcrt.756296E0 756296C3 C1E8 10 SHR EAX,10 756296C6 3A41 02 CMP AL,BYTE PTR DS:[ECX+2] 756296C9 75 19 JNZ SHORT msvcrt.756296E4 756296CB 0AC0 OR AL,AL 756296CD 74 11 JE SHORT msvcrt.756296E0 756296CF 3A61 03 CMP AH,BYTE PTR DS:[ECX+3] 756296D2 75 10 JNZ SHORT msvcrt.756296E4 756296D4 83C1 04 ADD ECX,4 756296D7 83C2 04 ADD EDX,4 756296DA 0AE4 OR AH,AH 756296DC ^ 75 D2 JNZ SHORT msvcrt.756296B0 756296DE 8BFF MOV EDI,EDI 756296E0 33C0 XOR EAX,EAX 756296E2 C3 RETN 756296E3 90 NOP 756296E4 1BC0 SBB EAX,EAX 756296E6 D1E0 SHL EAX,1 756296E8 83C0 01 ADD EAX,1 756296EB C3 RETN
En la comparación vemos que el serial bueno es AvrQQsXjDk25Jrh por lo que si restamos 0xD (13 en decimal) a cada carácter obtendremos el serial bueno.
0060FF10 41 76 72 51 51 73 58 6A 44 6B 32 35 4A 72 68 00 AvrQQsXjDk25Jrh. 41 76 72 51 51 73 58 6A 44 6B 32 35 4A 72 68 - D 34 69 65 44 44 66 4B 5D 37 5E 25 28 3D 65 5B 4 i e D D f K ] 7 ^ % ( = e [ Serial bueno: 4ieDDfK]7^%(=e[
Level 4
La comprobación del serial es igual que la anterior pero sustituyendo la función que sumaba un valor a cada dígito del serial por una que genera un hash con nuestro serial y después lo compara con otro hash almacenado en memoria. Si no nos viene a la mente el tipo de hash que puede ser PEiD ya nos avisaba de que efectivamente el crackme incorpora la función MD5.
La función MD5 hace tiempo que no se considera segura debido a la existencia de numerosos «diccionarios» de hashes que hacen que encontremos la solución en segundos. Yo he utilizado la web MD5 online pero existen muchas más.
0060FE5C 004013BF RETURN to level4.004013BF from <JMP.&msvcrt.strcmp> 0060FE60 0060FEA0 ASCII "e807f1fcf82d132f9bb018ca6738a19f" 0060FE64 0060FEE0 ASCII "fe01d67a002dfa0f3ac084298142eccd" e807f1fcf82d132f9bb018ca6738a19f == 1234567890 fe01d67a002dfa0f3ac084298142eccd == orange
Level 5
La carta de presentación de este crackme es la imagen que veis arriba. Al explorarlo unos minutos enseguida nos damos cuenta de que no realiza ninguna comprobación y que nos está haciendo perder el tiempo. Ahí es cuando empezamos a revisar el ejecutable más a fondo y enseguida encontramos la solución con nuestro amigo el editor hexadecimal.
Level 6
Misma carta de presentación que el anterior y misma ausencia de comprobación del serial. En esta ocasión echando un vistazo a los recursos encontramos la solución rápidamente.
Enlaces
Solución al Crackme RSA (RSA24) de TSC

Introducción
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.
Resumen RSA
Parámetros
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.
Funciones de Cifrado/Descifrado
cifrado = descifrado ^ e mod n
descifrado = cifrado ^ d mod n
OllyDbg
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
Enlaces
- RSA (wikipedia)
- Exponenciación modular (wikipedia)
- RSA Tool
- Big Integer Calculator v1.14
- Crackme


































