WinFan’s NETCrackMe#1 Keygen



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()
{
;
}
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_!!}


Rebuscando entre todo el caos que puede llegar a ser mi disco duro, he encontrado una serie de programas que utilizaba antiguamente cuando empezó a interesarme el Cracking. Ya sabéis que no soy partidario de crackear programas comerciales pero hoy voy a hacer una excepción ya que la versión del programa es muy vieja (1997) e incluso podría considerarse abandonware.
Este ejercicio es ideal para los que están empezando ya que es fácil localizar donde está el algoritmo y éste es sumamente sencillo.
Address Hex dump Command Comments
00402213 E8 78170000 CALL HEdit.00403990
........
004039C0 8BC1 MOV EAX,ECX
004039C2 99 CDQ
004039C3 33C2 XOR EAX,EDX
004039C5 2BC2 SUB EAX,EDX
004039C7 83E0 03 AND EAX,00000003
004039CA 33C2 XOR EAX,EDX
004039CC 2BC2 SUB EAX,EDX
004039CE 8A540C 04 MOV DL,BYTE PTR SS:[ECX+ESP+4] ;Coge el dígito i*3
004039D2 8A5C04 04 MOV BL,BYTE PTR SS:[EAX+ESP+4] ;Coge el dígito i
004039D6 8D4404 04 LEA EAX,[EAX+ESP+4] ;Guarda en memoria 12EE90
004039DA 32DA XOR BL,DL
004039DC 41 INC ECX ; i +=1
004039DD 81F9 00010000 CMP ECX,100 ;El bucle se repite 256 veces (0x100)
004039E3 8818 MOV BYTE PTR DS:[EAX],BL
004039E5 ^ 7C D9 JL SHORT HEdit.004039C0
004039E7 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4]
004039EB 85C0 TEST EAX,EAX ;Comprueba que el serial no sea 0
004039ED 7D 02 JGE SHORT HEdit.004039F1 ;Si es 0 se acabó
004039EF F7D8 NEG EAX
004039F1 3B8424 0C010000 CMP EAX,DWORD PTR SS:[ESP+10C] ;Comprobación de serial válido
004039F8 75 13 JNE SHORT HEdit.00403A0D ;Si no es igual bad boy
004039FA 85C0 TEST EAX,EAX ;Comprueba que el serial no sea 0
004039FC 74 0F JE SHORT HEdit.00403A0D ;Si es 0 se acabó
004039FE B8 01000000 MOV EAX,1
00403A03 5B POP EBX
00403A04 81C4 00010000 ADD ESP,100
00403A0A C2 0800 RETN 8
En resumen hace esto:
- Nombre introducido: deurus
- Convierte el nombre a mayúsculas
D E U R U S
44 45 55 52 55 53 (En Hexadecimal)
1) 55 xor 44 = 11
2) 53 xor 45 = 16
3) 00 xor 55 = 55
4) 00 xor 52 = 52
--------------- solo vale hasta aquí EAX(32 bits)
5) 00 xor 55 = 55
6) 00 xor 53 = 53
7) 00 xor 00 = 00
8) ...
HEX DEC
Serial = 52551611 = 1381307921
Como veis, realiza un bucle 256 veces pero como al final utiliza el registro EAX para hacer la comparación, solamente nos sirven las cuatro primeras operaciones. De hecho, no comprueba ni la longitud del nombre por lo que si introducimos un solo dígito como nombre, el serial será el valor ascii de ese dígito en decimal. La única comprobación que realiza es que el serial no sea 0.
Os dejo una prueba de concepto en Javascript.
var nombre = "deurus";
nombre = nombre.toUpperCase();
var serial = "";
serial = serial + nombre.charCodeAt(3).toString(16) + nombre.charCodeAt(2).toString(16);
serial = serial + (nombre.charCodeAt(5) ^ nombre.charCodeAt(1)).toString(16);
serial = serial + (nombre.charCodeAt(2) ^ nombre.charCodeAt(0)).toString(16);
serial = "Nº Serie: " + parseInt(serial,16);
document.write(serial);

Hoy vamos a desmitificar un poco a Visual Basic. El Crackme reza que acabemos con la nag y hagamos un keygen.
Lo primero que debemos establecer es a que tipo de Nag nos estamos enfrentando, si es una MessageBox, se localiza y se parchea, si es un formulario la cosa ya cambia un poco, ya que encontrar esa parte del código puede resultar tedioso.
Yo voy a utilizar el camino fácil. En VB contamos con una estupenda herramienta como es VBReformer, que nos muestra las entrañas del crackme y nos posibilita cambiar ciertos parametros.
Abrimos el crackme con VBReformer y vemos su contenido. Localizamos rápidamente el formulario que aparece inicialmente (CMSplash) y el temporizador que contiene (Timer1). Inicialmente el timer tiene un interval de 7000, es decir, la nag se muestra durante 7 segundos. Es tan sencillo como cambiarlo por 1 y guardar el ejecutable (File > Save binary as).
Si no disponemos de esta estupenda herramienta, siempre podemos localizar el timer con un editor hexadecimal. Localizamos en Olly el nombre del timer (en las string references lo veréis), y lo buscamos en el editor hexadecimal. La duración de la nag la debemos estimar nosotros y a continuación buscar ese valor hexadecimal. Imaginemos que estimamos que son 7 segundos, lo pasamos a milisegundos y a hexadecimal. 7000 = 0x1B58. Buscamos en el editor hexadecimal «Timer1» y a continuación localizamos el valor 1B58 invertido, es decir, 581B. En las imágenes inferiores se ve claro.
Cambiando el valor ya lo tendriámos.
El algoritmo de este crackme es muy sencillo pero he elegido este en concreto para que veáis las vueltas que da VB para hacer 6 operaciones matemáticas. A continuación tenéis el código íntegro comentado y debajo el resumen.
004081F5 . FF15 F8B04000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaLenBstr>] ; MSVBVM50.__vbaLenBstr 004081FB . 8BF8 MOV EDI,EAX 004081FD . 8B4D E8 MOV ECX,DWORD PTR SS:[EBP-18] 00408200 . 69FF 385B0100 IMUL EDI,EDI,15B38 ; TEMP = Len(nombre) *15B38 00408206 . 51 PUSH ECX 00408207 . 0F80 B7050000 JO CrackmeV.004087C4 0040820D . FF15 0CB14000 CALL DWORD PTR DS:[<&MSVBVM50.#516>] ; MSVBVM50.rtcAnsiValueBstr 00408213 . 0FBFD0 MOVSX EDX,AX ; digito a ax 00408216 . 03FA ADD EDI,EDX ; TEMP +=1digito 00408218 . 0F80 A6050000 JO CrackmeV.004087C4 0040821E . 57 PUSH EDI ; MSVBVM50.__vbaObjSet 0040821F . FF15 F4B04000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrI4>] ; MSVBVM50.__vbaStrI4 00408225 . 8BD0 MOV EDX,EAX 00408227 . 8D4D E0 LEA ECX,DWORD PTR SS:[EBP-20] 0040822A . FF15 94B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrMove>] ; MSVBVM50.__vbaStrMove 00408230 . 8BBD 50FFFFFF MOV EDI,DWORD PTR SS:[EBP-B0] 00408236 . 50 PUSH EAX 00408237 . 57 PUSH EDI ; MSVBVM50.__vbaObjSet 00408238 . FF93 A4000000 CALL DWORD PTR DS:[EBX+A4] ; MSVBVM50.0F050D32 0040823E . 85C0 TEST EAX,EAX 00408240 . 7D 12 JGE SHORT CrackmeV.00408254 00408242 . 68 A4000000 PUSH 0A4 00408247 . 68 AC6F4000 PUSH CrackmeV.00406FAC 0040824C . 57 PUSH EDI ; MSVBVM50.__vbaObjSet 0040824D . 50 PUSH EAX 0040824E . FF15 18B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaHresultCheck>; MSVBVM50.__vbaHresultCheckObj 00408254 > 8D45 E0 LEA EAX,DWORD PTR SS:[EBP-20] 00408257 . 8D4D E4 LEA ECX,DWORD PTR SS:[EBP-1C] 0040825A . 50 PUSH EAX 0040825B . 8D55 E8 LEA EDX,DWORD PTR SS:[EBP-18] 0040825E . 51 PUSH ECX 0040825F . 52 PUSH EDX 00408260 . 6A 03 PUSH 3 00408262 . FF15 80B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaFreeStrList>>; MSVBVM50.__vbaFreeStrList 00408268 . 83C4 10 ADD ESP,10 0040826B . 8D45 D4 LEA EAX,DWORD PTR SS:[EBP-2C] 0040826E . 8D4D D8 LEA ECX,DWORD PTR SS:[EBP-28] 00408271 . 8D55 DC LEA EDX,DWORD PTR SS:[EBP-24] 00408274 . 50 PUSH EAX 00408275 . 51 PUSH ECX 00408276 . 52 PUSH EDX 00408277 . 6A 03 PUSH 3 00408279 . FF15 08B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaFreeObjList>>; MSVBVM50.__vbaFreeObjList 0040827F . 8B9D 40FFFFFF MOV EBX,DWORD PTR SS:[EBP-C0] ; CrackmeV.00409A68 00408285 . 83C4 10 ADD ESP,10 00408288 . 8B83 FC020000 MOV EAX,DWORD PTR DS:[EBX+2FC] 0040828E . 56 PUSH ESI 0040828F . 8985 38FFFFFF MOV DWORD PTR SS:[EBP-C8],EAX 00408295 . FFD0 CALL EAX 00408297 . 8B3D 20B14000 MOV EDI,DWORD PTR DS:[<&MSVBVM50.__vbaObjSet>] ; MSVBVM50.__vbaObjSet 0040829D . 50 PUSH EAX 0040829E . 8D45 D8 LEA EAX,DWORD PTR SS:[EBP-28] 004082A1 . 50 PUSH EAX 004082A2 . FFD7 CALL EDI ; MSVBVM50.__vbaObjSet; <&MSVBVM50.__vbaObjSet> 004082A4 . 56 PUSH ESI 004082A5 . 8985 58FFFFFF MOV DWORD PTR SS:[EBP-A8],EAX 004082AB . FF93 08030000 CALL DWORD PTR DS:[EBX+308] 004082B1 . 8D4D DC LEA ECX,DWORD PTR SS:[EBP-24] 004082B4 . 50 PUSH EAX 004082B5 . 51 PUSH ECX 004082B6 . FFD7 CALL EDI ; MSVBVM50.__vbaObjSet 004082B8 . 8BD8 MOV EBX,EAX 004082BA . 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18] 004082BD . 50 PUSH EAX 004082BE . 53 PUSH EBX 004082BF . 8B13 MOV EDX,DWORD PTR DS:[EBX] ; MSVBVM50.0F025A95 004082C1 . FF92 A0000000 CALL DWORD PTR DS:[EDX+A0] 004082C7 . 85C0 TEST EAX,EAX 004082C9 . 7D 12 JGE SHORT CrackmeV.004082DD 004082CB . 68 A0000000 PUSH 0A0 004082D0 . 68 AC6F4000 PUSH CrackmeV.00406FAC 004082D5 . 53 PUSH EBX 004082D6 . 50 PUSH EAX 004082D7 . FF15 18B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaHresultCheck>; MSVBVM50.__vbaHresultCheckObj 004082DD > 8B8D 58FFFFFF MOV ECX,DWORD PTR SS:[EBP-A8] 004082E3 . 8B55 E8 MOV EDX,DWORD PTR SS:[EBP-18] 004082E6 . 52 PUSH EDX 004082E7 . 8B19 MOV EBX,DWORD PTR DS:[ECX] 004082E9 . FF15 74B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8Str>] ; MSVBVM50.__vbaR8Str 004082EF . D905 08104000 FLD DWORD PTR DS:[401008] 004082F5 . 833D 00904000 00 CMP DWORD PTR DS:[409000],0 004082FC . 75 08 JNZ SHORT CrackmeV.00408306 004082FE . D835 0C104000 FDIV DWORD PTR DS:[40100C] ; 10 / 5 = 2 00408304 . EB 0B JMP SHORT CrackmeV.00408311 00408306 > FF35 0C104000 PUSH DWORD PTR DS:[40100C] 0040830C . E8 578DFFFF CALL <JMP.&MSVBVM50._adj_fdiv_m32> 00408311 > 83EC 08 SUB ESP,8 00408314 . DFE0 FSTSW AX 00408316 . A8 0D TEST AL,0D 00408318 . 0F85 A1040000 JNZ CrackmeV.004087BF 0040831E . DEC1 FADDP ST(1),ST ; TEMP +=2 00408320 . DFE0 FSTSW AX 00408322 . A8 0D TEST AL,0D 00408324 . 0F85 95040000 JNZ CrackmeV.004087BF 0040832A . DD1C24 FSTP QWORD PTR SS:[ESP] 0040832D . FF15 48B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrR8>] ; MSVBVM50.__vbaStrR8 00408333 . 8BD0 MOV EDX,EAX 00408335 . 8D4D E4 LEA ECX,DWORD PTR SS:[EBP-1C] 00408338 . FF15 94B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrMove>] ; MSVBVM50.__vbaStrMove 0040833E . 899D 34FFFFFF MOV DWORD PTR SS:[EBP-CC],EBX 00408344 . 8B9D 58FFFFFF MOV EBX,DWORD PTR SS:[EBP-A8] 0040834A . 50 PUSH EAX 0040834B . 8B85 34FFFFFF MOV EAX,DWORD PTR SS:[EBP-CC] 00408351 . 53 PUSH EBX 00408352 . FF90 A4000000 CALL DWORD PTR DS:[EAX+A4] 00408358 . 85C0 TEST EAX,EAX 0040835A . 7D 12 JGE SHORT CrackmeV.0040836E 0040835C . 68 A4000000 PUSH 0A4 00408361 . 68 AC6F4000 PUSH CrackmeV.00406FAC 00408366 . 53 PUSH EBX 00408367 . 50 PUSH EAX 00408368 . FF15 18B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaHresultCheck>; MSVBVM50.__vbaHresultCheckObj 0040836E > 8D4D E4 LEA ECX,DWORD PTR SS:[EBP-1C] 00408371 . 8D55 E8 LEA EDX,DWORD PTR SS:[EBP-18] 00408374 . 51 PUSH ECX 00408375 . 52 PUSH EDX 00408376 . 6A 02 PUSH 2 00408378 . FF15 80B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaFreeStrList>>; MSVBVM50.__vbaFreeStrList 0040837E . 83C4 0C ADD ESP,0C 00408381 . 8D45 D8 LEA EAX,DWORD PTR SS:[EBP-28] 00408384 . 8D4D DC LEA ECX,DWORD PTR SS:[EBP-24] 00408387 . 50 PUSH EAX 00408388 . 51 PUSH ECX 00408389 . 6A 02 PUSH 2 0040838B . FF15 08B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaFreeObjList>>; MSVBVM50.__vbaFreeObjList 00408391 . 8B95 40FFFFFF MOV EDX,DWORD PTR SS:[EBP-C0] ; CrackmeV.00409A68 00408397 . 83C4 0C ADD ESP,0C 0040839A . 8B82 00030000 MOV EAX,DWORD PTR DS:[EDX+300] 004083A0 . 56 PUSH ESI 004083A1 . 8985 30FFFFFF MOV DWORD PTR SS:[EBP-D0],EAX 004083A7 . FFD0 CALL EAX 004083A9 . 50 PUSH EAX 004083AA . 8D45 D8 LEA EAX,DWORD PTR SS:[EBP-28] 004083AD . 50 PUSH EAX 004083AE . FFD7 CALL EDI ; MSVBVM50.__vbaObjSet 004083B0 . 56 PUSH ESI 004083B1 . 8985 58FFFFFF MOV DWORD PTR SS:[EBP-A8],EAX 004083B7 . FF95 38FFFFFF CALL DWORD PTR SS:[EBP-C8] ; MSVBVM50.0F10C340 004083BD . 8D4D DC LEA ECX,DWORD PTR SS:[EBP-24] 004083C0 . 50 PUSH EAX 004083C1 . 51 PUSH ECX 004083C2 . FFD7 CALL EDI ; MSVBVM50.__vbaObjSet 004083C4 . 8BD8 MOV EBX,EAX 004083C6 . 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18] 004083C9 . 50 PUSH EAX 004083CA . 53 PUSH EBX 004083CB . 8B13 MOV EDX,DWORD PTR DS:[EBX] ; MSVBVM50.0F025A95 004083CD . FF92 A0000000 CALL DWORD PTR DS:[EDX+A0] 004083D3 . 85C0 TEST EAX,EAX 004083D5 . 7D 12 JGE SHORT CrackmeV.004083E9 004083D7 . 68 A0000000 PUSH 0A0 004083DC . 68 AC6F4000 PUSH CrackmeV.00406FAC 004083E1 . 53 PUSH EBX 004083E2 . 50 PUSH EAX 004083E3 . FF15 18B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaHresultCheck>; MSVBVM50.__vbaHresultCheckObj 004083E9 > 8B8D 58FFFFFF MOV ECX,DWORD PTR SS:[EBP-A8] 004083EF . 8B55 E8 MOV EDX,DWORD PTR SS:[EBP-18] 004083F2 . 52 PUSH EDX 004083F3 . 8B19 MOV EBX,DWORD PTR DS:[ECX] 004083F5 . FF15 74B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8Str>] ; MSVBVM50.__vbaR8Str 004083FB . DC0D 10104000 FMUL QWORD PTR DS:[401010] ; *3 00408401 . 83EC 08 SUB ESP,8 ; -2 00408404 . DC25 18104000 FSUB QWORD PTR DS:[401018] 0040840A . DFE0 FSTSW AX 0040840C . A8 0D TEST AL,0D 0040840E . 0F85 AB030000 JNZ CrackmeV.004087BF 00408414 . DD1C24 FSTP QWORD PTR SS:[ESP] 00408417 . FF15 48B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrR8>] ; MSVBVM50.__vbaStrR8 0040841D . 8BD0 MOV EDX,EAX 0040841F . 8D4D E4 LEA ECX,DWORD PTR SS:[EBP-1C] 00408422 . FF15 94B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrMove>] ; MSVBVM50.__vbaStrMove 00408428 . 899D 2CFFFFFF MOV DWORD PTR SS:[EBP-D4],EBX 0040842E . 8B9D 58FFFFFF MOV EBX,DWORD PTR SS:[EBP-A8] 00408434 . 50 PUSH EAX 00408435 . 8B85 2CFFFFFF MOV EAX,DWORD PTR SS:[EBP-D4] 0040843B . 53 PUSH EBX 0040843C . FF90 A4000000 CALL DWORD PTR DS:[EAX+A4] 00408442 . 85C0 TEST EAX,EAX 00408444 . 7D 12 JGE SHORT CrackmeV.00408458 00408446 . 68 A4000000 PUSH 0A4 0040844B . 68 AC6F4000 PUSH CrackmeV.00406FAC 00408450 . 53 PUSH EBX 00408451 . 50 PUSH EAX 00408452 . FF15 18B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaHresultCheck>; MSVBVM50.__vbaHresultCheckObj 00408458 > 8D4D E4 LEA ECX,DWORD PTR SS:[EBP-1C] 0040845B . 8D55 E8 LEA EDX,DWORD PTR SS:[EBP-18] 0040845E . 51 PUSH ECX 0040845F . 52 PUSH EDX 00408460 . 6A 02 PUSH 2 00408462 . FF15 80B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaFreeStrList>>; MSVBVM50.__vbaFreeStrList 00408468 . 83C4 0C ADD ESP,0C 0040846B . 8D45 D8 LEA EAX,DWORD PTR SS:[EBP-28] 0040846E . 8D4D DC LEA ECX,DWORD PTR SS:[EBP-24] 00408471 . 50 PUSH EAX 00408472 . 51 PUSH ECX 00408473 . 6A 02 PUSH 2 00408475 . FF15 08B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaFreeObjList>>; MSVBVM50.__vbaFreeObjList 0040847B . 8B95 40FFFFFF MOV EDX,DWORD PTR SS:[EBP-C0] ; CrackmeV.00409A68 00408481 . 83C4 0C ADD ESP,0C 00408484 . 8B82 04030000 MOV EAX,DWORD PTR DS:[EDX+304] 0040848A . 56 PUSH ESI 0040848B . 8985 28FFFFFF MOV DWORD PTR SS:[EBP-D8],EAX 00408491 . FFD0 CALL EAX 00408493 . 50 PUSH EAX 00408494 . 8D45 D8 LEA EAX,DWORD PTR SS:[EBP-28] 00408497 . 50 PUSH EAX 00408498 . FFD7 CALL EDI ; MSVBVM50.__vbaObjSet 0040849A . 56 PUSH ESI 0040849B . 8985 58FFFFFF MOV DWORD PTR SS:[EBP-A8],EAX 004084A1 . FF95 30FFFFFF CALL DWORD PTR SS:[EBP-D0] ; MSVBVM50.0F10C348 004084A7 . 8D4D DC LEA ECX,DWORD PTR SS:[EBP-24] 004084AA . 50 PUSH EAX 004084AB . 51 PUSH ECX 004084AC . FFD7 CALL EDI ; MSVBVM50.__vbaObjSet 004084AE . 8BD8 MOV EBX,EAX 004084B0 . 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18] 004084B3 . 50 PUSH EAX 004084B4 . 53 PUSH EBX 004084B5 . 8B13 MOV EDX,DWORD PTR DS:[EBX] ; MSVBVM50.0F025A95 004084B7 . FF92 A0000000 CALL DWORD PTR DS:[EDX+A0] 004084BD . 85C0 TEST EAX,EAX 004084BF . 7D 12 JGE SHORT CrackmeV.004084D3 004084C1 . 68 A0000000 PUSH 0A0 004084C6 . 68 AC6F4000 PUSH CrackmeV.00406FAC 004084CB . 53 PUSH EBX 004084CC . 50 PUSH EAX 004084CD . FF15 18B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaHresultCheck>; MSVBVM50.__vbaHresultCheckObj 004084D3 > 8B8D 58FFFFFF MOV ECX,DWORD PTR SS:[EBP-A8] 004084D9 . 8B55 E8 MOV EDX,DWORD PTR SS:[EBP-18] 004084DC . 52 PUSH EDX 004084DD . 8B19 MOV EBX,DWORD PTR DS:[ECX] 004084DF . FF15 74B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8Str>] ; MSVBVM50.__vbaR8Str 004084E5 . DC25 20104000 FSUB QWORD PTR DS:[401020] ; TEMP -= -15 004084EB . 83EC 08 SUB ESP,8 004084EE . DFE0 FSTSW AX 004084F0 . A8 0D TEST AL,0D 004084F2 . 0F85 C7020000 JNZ CrackmeV.004087BF ........ 00408677 . /74 62 JE SHORT CrackmeV.004086DB 00408679 . |8B35 14B14000 MOV ESI,DWORD PTR DS:[<&MSVBVM50.__vbaStrCat>] ; MSVBVM50.__vbaStrCat 0040867F . |68 C06F4000 PUSH CrackmeV.00406FC0 ; UNICODE "You Get It" 00408684 . |68 DC6F4000 PUSH CrackmeV.00406FDC ; UNICODE "\r\n" 00408689 . |FFD6 CALL ESI ; <&MSVBVM50.__vbaStrCat> 0040868B . |8BD0 MOV EDX,EAX 0040868D . |8D4D E8 LEA ECX,DWORD PTR SS:[EBP-18] 00408690 . |FF15 94B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrMove>] ; MSVBVM50.__vbaStrMove 00408696 . |50 PUSH EAX 00408697 . |68 E86F4000 PUSH CrackmeV.00406FE8 ; UNICODE "KeyGen It Now"
Que se puede resumir en esto.
004081F5 . FF15 F8B04000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaLenBstr>] ; MSVBVM50.__vbaLenBstr 004081FB . 8BF8 MOV EDI,EAX 004081FD . 8B4D E8 MOV ECX,DWORD PTR SS:[EBP-18] 00408200 . 69FF 385B0100 IMUL EDI,EDI,15B38 ; TEMP = Len(nombre) *15B38 00408206 . 51 PUSH ECX 00408207 . 0F80 B7050000 JO CrackmeV.004087C4 0040820D . FF15 0CB14000 CALL DWORD PTR DS:[<&MSVBVM50.#516>] ; MSVBVM50.rtcAnsiValueBstr 00408213 . 0FBFD0 MOVSX EDX,AX ; digito a ax 00408216 . 03FA ADD EDI,EDX ; TEMP +=1digito 00408218 . 0F80 A6050000 JO CrackmeV.004087C4 ........ 004082E9 . FF15 74B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8Str>] ; MSVBVM50.__vbaR8Str 004082EF . D905 08104000 FLD DWORD PTR DS:[401008] 004082F5 . 833D 00904000 00 CMP DWORD PTR DS:[409000],0 004082FC . 75 08 JNZ SHORT CrackmeV.00408306 004082FE . D835 0C104000 FDIV DWORD PTR DS:[40100C] ; 10 / 5 = 2 00408304 . EB 0B JMP SHORT CrackmeV.00408311 00408306 > FF35 0C104000 PUSH DWORD PTR DS:[40100C] 0040830C . E8 578DFFFF CALL <JMP.&MSVBVM50._adj_fdiv_m32> 00408311 > 83EC 08 SUB ESP,8 00408314 . DFE0 FSTSW AX 00408316 . A8 0D TEST AL,0D 00408318 . 0F85 A1040000 JNZ CrackmeV.004087BF 0040831E . DEC1 FADDP ST(1),ST ; TEMP +=2 00408320 . DFE0 FSTSW AX 00408322 . A8 0D TEST AL,0D ........ 004083F5 . FF15 74B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8Str>] ; MSVBVM50.__vbaR8Str 004083FB . DC0D 10104000 FMUL QWORD PTR DS:[401010] ; TEMP *=3 00408401 . 83EC 08 SUB ESP,8 ; TEMP -=2 00408404 . DC25 18104000 FSUB QWORD PTR DS:[401018] 0040840A . DFE0 FSTSW AX 0040840C . A8 0D TEST AL,0D ........ 004084DF . FF15 74B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaR8Str>] ; MSVBVM50.__vbaR8Str 004084E5 . DC25 20104000 FSUB QWORD PTR DS:[401020] ; TEMP -= -15 004084EB . 83EC 08 SUB ESP,8 004084EE . DFE0 FSTSW AX 004084F0 . A8 0D TEST AL,0D 004084F2 . 0F85 C7020000 JNZ CrackmeV.004087BF ........ 00408677 . /74 62 JE SHORT CrackmeV.004086DB 00408679 . |8B35 14B14000 MOV ESI,DWORD PTR DS:[<&MSVBVM50.__vbaStrCat>] ; MSVBVM50.__vbaStrCat 0040867F . |68 C06F4000 PUSH CrackmeV.00406FC0 ; UNICODE "You Get It" 00408684 . |68 DC6F4000 PUSH CrackmeV.00406FDC ; UNICODE "\r\n" 00408689 . |FFD6 CALL ESI ; <&MSVBVM50.__vbaStrCat> 0040868B . |8BD0 MOV EDX,EAX 0040868D . |8D4D E8 LEA ECX,DWORD PTR SS:[EBP-18] 00408690 . |FF15 94B14000 CALL DWORD PTR DS:[<&MSVBVM50.__vbaStrMove>] ; MSVBVM50.__vbaStrMove 00408696 . |50 PUSH EAX 00408697 . |68 E86F4000 PUSH CrackmeV.00406FE8 ; UNICODE "KeyGen It Now"
Y en esto:
TEMP = Len(nombre) *15B38 TEMP += 1ºdigitoascii TEMP +=2 TEMP *=3 TEMP -=2 TEMP +=15
Y el KeyGen nos ocupa una sola línea
txtserial.Text = (((((Len(txtnombre.Text) * 88888) + Asc(Mid(txtn.Text, 1, 1))) + 2) * 3) - 2) + 15
Si nos fijamos en el VBReformer, en el formulario principal vemos muchas cajas de texto.
Las hacemos visibles, guardamos y ejecutamos haber que pasa.
Lo que pasa es que va guardando el resultado de las operaciones en ellas y en la de arriba concretamente está nuestro serial bueno, lo que nos hace poseedores de un KeyGen encubierto.

Hace poco me puse a leer El oscuro pasajero de Jeff Lindsay, novela que inspiró la serie Dexter. La nostalgia me invadió y al final decidí volver a ver la primera temporada que tanto me gustó hace unos años. Para mi sorpresa, muchos de los detalles que recordaba de la serie eran incorrectos o incompletos. Bueno, el caso es que en esta ocasión me he fijado más en los detalles y he descubierto una pequeña perla en el capítulo 8 de la primera temporada.
ALERTA DE SPOILER: Aunque la serie tiene unos añitos no quisiera fastidiarsela a nadie. Si continuas leyendo puede que te enteres de algo que no quieras.
En un momento dado, a Dexter se le ocurre la feliz idea de contactar con el asesino en serie que le está dejando regalitos y no se le ocurre mejor idea que hacerlo en una web de contactos cualquiera. La web en cuestión es www.miamilist12.com/miami/main y Dexter decide escribir un mensaje en el hilo missed connections. A continuación la secuencia de imágenes.
La simple idea de escribir en un tablón, foro, lista, etc y esperar que el asesino en serie lo lea ya es una locura. Pero señor@s, esto es ficción, y por supuesto el asesino no solo ve el mensaje si no que responde a Dexter creando un pequeño error con las direcciones de email. Y es que cuando el asesino ve el mensaje se puede apreciar que la dirección de email de Dexter es frozenbarbie@hotmail.web y cuando el asesino le responde, se ve claramente que lo hace a la dirección frozenbarbie@hotmail.com. A continuación las imágenes.
Además me ha llamado la atención que aunque es evidente que el asesino usa Windows XP, se puede apreciar que han retocado en post-producción el botón de inicio para que quede oculto.
Nos vemos en el siguiente BTM.

Aquí tenemos un crackme fuera de lo común, más que nada por que está programado en Brainfuck, un lenguaje de programación esotérico bastante complejo.
[-]>[-]<>++++++++[<++++++++++>-]<.+++++++++++++++++.>+++[<++++++>-]<..++++.- -------.+++.--------------.>++++++[<------>-]<-.>+++++[<------>-]<-.,>,>,>,> >>>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++>>>>+++++++++++++++++++++++++++++++++++++++++ +++++++++++>>>>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++>>>>++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<<< <<<<<<<<<<<<<<<<[>>>>>>>-<<<<<<<-]>>>>>>><<+>>[[-]++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++.<]<[>]<<<<<[>>>>>>>>>>-<<<<<<<< <<-]>>>>>>>>>><<+>>[[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++.<]<[>]<<<<<<<<[>>>>>>>>>>>>>-<<<<<<<<<<<<<-]>>>>>>>>>>>>><<+>>[ [-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .<]<[>]<<<<<<<<<<<[>>>>>>>>>>>>>>>>-<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>><<+>>[ [-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++.<]<[>]>>>[-]>[-]<+++++++++++++.---.>+++++++[<++++++++++++>-]<.>+++++++[< --------->-]<+.>+++++[<++++++++>-]<+.>++++[<+++++++>-]<+.>+++++++[<--------- ->-]<.>++++++++[<+++++++++>-]<+.+++++++++++.>+++++++[<----------->-]<.>+++++ ++[<+++++++++++>-]<-.>+++++++[<------------>-]<+.>++++++++[<+++++++++++>-]<- .-----.---------.+++++++++++..---------------.+++++++++.>++++++[<----------- -->-]<.+++++++.>+++++[<++++++>-]<+.-----.++++++++.+++.>++++++[<------>-]<-.- ------.>++++++++[<++++++++++>-]<-.+++.>+++++++++[<--------->-]<-.+++++++.>++ +++[<++++++>-]<+.-----.+++++++++++.>++++++[<------>-]<-.-------.>++++++++[<+ +++++++++>-]<-.+++.>+++++++++[<--------->-]<-.+++++++.>+++++[<+++++>-]<+.+++ +++++.+++.>++++++[<------>-]<-.+++++++...--------------.>++++++++[<+++++++++ ++>-]<+.----------.++++++.>+++++++[<------------>-]<-.>++++++++[<+++++++++>- ]<.-------.>+++[<+++++++>-]<.-----------------.>+++++++[<---------->-]<+.>++ +++++[<++++++++++>-]<.-----.++++++++.+++.-------.-.>++++++[<--------->-]<.-- ------------.>+++++[<++++++++>-]<+.>++++[<+++++++>-]<+.>+++++++[<----------> -]<.>+++++++[<++++++++++++>-]<.------------.---.+++++++++++++.-------------. >+++++++[<---------->-]<+.>++++++++[<+++++++++>-]<+.++++++++++.>+++++++[<--- --------->-]<+.>++++++[<+++++++++++++>-]<.+.+++++.------------.+.+++++.----- --.>++++++[<---------->-]<+.------------.>++++++++[<+++++++++++>-]<+.------- ---.++++++.>+++++++[<------------>-]<-.>++++++++[<+++++++++>-]<.-------.>+++ [<+++++++>-]<.-----------------.>+++++++[<---------->-]<+.>++++++++[<+++++++ ++++>-]<-.--------------.+++++.>++++++[<------------->-]<.+.
La solución que he encontrado yo, es convertir el código brainfuck a algo más amigable y depurarlo hasta encontrar la solución. La conversión la he realizado con VBBrainFNET y luego la depuración con Visual Studio. El crackme te pide una clave de cuatro cifras para darte la solución, pero si no quieres volverte loco puedes amañar los bucles para encontrar la solución.
¡SUERTE!

Esta es la tercera y última entrega de los crackmes de Cruehead. En esta ocasión nos enfrentamos a un «keyfile«, un archivo llave para que nos entendamos. Tiene un poco más de dificultad que los anteriores pero es ideal para los que empiezan.
Si iniciamos el crackme no pasa nada, lo único que vemos es la palabra «UNCRACKED» en el título. Abrimos el crackme con Olly y empezamos. En las «string references» vemos el nombre del archivo llave «crackme3.key«. Lo creamos y escribimos el serial 12345678 y empezamos a tracear.
El CMP EAX,-1 significa que está comprobando que el archivo no esté vacio, como no es nuestro caso continuamos.
A continuación vemos que compara nuestra longitud de serial con 0x12 (18 en decimal). Nuestro serial tiene 8 dígitos así que nos tira fuera.
Escribimos en el archivo llave el serial «deurus123456789012» y volvemos a tracear. Vemos que ahora si pasa los filtros iniciales y llegamos a la primera zona interesante. En la imágen está explicado pero os hago un resumen. En el bucle lo que hace es un XOR a los primeros 14 dígitos de nuestro serial con los valores del 41 al 4E (4F finaliza). El bucle solo se rompe si llegamos a 4F o si el resultado del XOR da 0. Además en EAX acumula la suma del resultado del XOR.
Ejemplo:
d e u r u s 1 2 3 4 5 6 7 8 9 0 1 2
64 65 75 72 75 73 31 32 33 34 35 36 37 38
XOR
41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E
-----------------------------------------
25 27 36 36 30 35 76 7A 7A 7E 7E 7A 7A 76 = 4ED (Suma)
A continuación hace XOR entre 12345678 y 4ED, coge los 4 últimos dígitos de nuestro serial y los compara.
Ejemplo:
12345678 XOR 4ED = 12345295 Compara 12345295 con 32313039 32313039 = 2109, nuestros 4 últimos dígitos al revés. Recordemos que nuestro serial era "deurus123456789012"
El serial bueno para el nombre deurus12345678 serían los bytes correspondientes de «12345295», es decir, nuestro serial bueno sería:
Ejemplo:
Necesitamos 12345295 para la comparación. 12 34 52 95 hexadecimal 18 52 82 149 decimal Tenemos que escribirlo al revés. Con Alt-Izq + 149 escribimos el primer caracter, el resto igual. Nuestro serial quedaría: deurus12345678òR4↕
Metemos el serial y vemos que lo acepta pero que nos muestra un nombre extraño. Esto es por que nos está mostrando los bytes del nombre xoreados, tendremos que hacer un XOR antes al nombre que queramos para que lo muestre correctamente.
Con lo que sabemos ahora hay que empezar a coger el toro por los cuernos. Lo primero que queremos que muestre el nombre deurus y no deurus12345678. Para ello debemos cortar el bucle y eso solo lo podemos hacer forzando que el resultado del XOR sea 0. Ok pues para deurus el siguiente valor de BL, es decir el séptimo, en el bucle sería 47 lo que corresponde a la letra G. Pues si ponemos de serial deurusGxxxxxxxxxxx ya tendríamos la primera parte solucionada.
Pero recordemos que necesitamos XORear el nombre inicialmente, luego debemos escribir el resultado del XOR.
Ejemplo:
d e u r u s
64 65 75 72 75 73
XOR
41 42 43 44 45 46
-----------------
25 27 36 36 30 35
25 27 36 36 30 35 ----- debemos meter esto en el archivo llave.
XOR
41 42 43 44 45 46
-----------------
64 65 75 72 75 73 ----- Al desencriptarlo el bucle se verá nuestro nombre correctamente.
En el archivo llave escribiriamos: %'6605
Ahora nos faltaría calcular el nuevo SUM. Como el resultado del XOR ahora es nuestro nombre, basta con sumar sus valores ascii (64+65+75+72+75+73 == 0x298)
0x12345678 XOR 0x298 == 0x123454E0
Luego nuestros 4 últimos dígitos deben ser lo correspondiente a los bytes E0, 54, 34, 12. Los pasamos a decimal y los escribimos en el archivo llave con el truco del ALT-Izq que hemos comentado antes.
El contenido final del archivo llave para el nombre deurus sería:
%’6605GxxxxxxxÓT4↕
Aquí vemos el archivo llave normal.
Y aquí lo vemos con un editor hexadecimal. Como veis se ven claramente los bytes E0, 54, 34, 12.
Os dejo un keygen hecho en .Net para que probéis. Os genera el contenido del archivo y el archivo «crackme3.key».

File carving is the process of reassembling computer files from fragments in the absence of filesystem metadata. Wikipedia.
«File carving», literalmente tallado de archivos aunque lo traduciremos como extracción, es el proceso de re-ensamblado de archivos extraídos de un conjunto de mayor tamaño.
Header = Cabecera
Footer or tail = Pie
All this documents have the same header and footer, because of this, we need search the middle bytes. This type uses a ZIP file package.
Los documentos de Microsoft Office >2007 tienen la misma cabecera y pie, por lo que necesitamos bytes intermedios para distinguirlos. Usan encapsulado ZIP.
All this documents have the same header and footer, because of this, we need some bytes to differentiate them. In this case we can do this jumping 73 bytes from header. This type uses a ZIP file package.
Los documentos de OpenOffice tienen la misma cabecera y pie, por lo que necesitamos bytes intermedios para distinguirlos. Usan encapsulado ZIP.
Note: >2007 versions have two patterns and the key is the position 0x80. If in this position we get the bytes «68 40 F8 F7 92», we need to search again for this bytes and displace 107 bytes to find the end of the file. If in the position 0x80 we get another different bytes, we need to search again this bytes and displace 1024 bytes to find the end of the file.
Nota: Las versiones >2007 siguen dos patrones y la clave está en la posición 0x80. Si en la posicion 0x80 obtenemos los bytes «68 40 F8 F7 92», los buscamos una segunda vez y ha 107 bytes encontramos el final del archivo. Si en la posición 0x80 obtenemos otros bytes diferentes a los del primer caso, los volvemos a buscar y a 1024 bytes hallaremos el final del archivo.

Hoy vamos a ver como extraer el script de un ejecutable compilado por Autoit, modificarlo y recompilarlo como nuestro keygen. Como comprobareis si no se ofusca o se toman otro tipo de medidas recuperar información sensible es muy sencillo.
AutoIt es un lenguaje freeware multiproposito y de automatización para Microsoft Windows. Es un Visual Basic Killer, ya que mejora las características de los ejecutables (entre otras portabilidad, velocidad y peso, no fat-coding), y facilitan la programación con un buen repertorio de funciones «pre-diseñadas», y usando un Basic de fácil aprendizaje. Se ha expandido desde sus comienzos de automatización incluyendo muchas mejoras en el diseño del lenguaje de programación y sobre todo en nuevas funcionalidades.
Func CHECKKEY($USER, $PASS) Local $OPKEY = "", $SIG = "" Local $USER_LEN = StringLen($USER) Local $PASS_LEN = StringLen($PASS) If $USER_LEN < $PASS_LEN Then MsgBox(0, "ERROR", "Invalid username or key.") Exit ElseIf $USER_LEN < 4 Then MsgBox(0, "ERROR", "Invalid username or key.") Exit EndIf $PASS_INT = Int($USER_LEN / $PASS_LEN) $PASS_MOD = Mod($USER_LEN, $PASS_LEN) $OPKEY = _STRINGREPEAT($PASS, $PASS_INT) & StringLeft($PASS, $PASS_MOD) For $INDEX = 1 To $USER_LEN $SIG &= Chr(BitXOR(Asc(StringMid($USER, $INDEX, 1)), Asc(StringMid($OPKEY, $USER_LEN - $INDEX + 1, 1)))) Next If $SIG = _STRINGREPEAT(Chr(32), $USER_LEN) Then MsgBox(0, "INFO", "Your key was registered.") Exit Else MsgBox(0, "INFO", "Your key is invalid.") EndIf EndFunc
El algoritmo es tremendamente sencillo ya que es nuestro nombre al revés y en mayúsculas.
El decompilador se llama myAut2exe y tiene este aspecto.
Programar en AutoIt es muy sencillo e intuitivo. Nuestro keygen quedaría así.
$MAIN = GUICreate("Another keygen by deurus", 300, 80, -1, -1, 382205952, 385)
$NAME_LBL = GUICtrlCreateLabel("Username", 5, 5, 60, 20, BitOR(4096, 1))
$NAME_INP = GUICtrlCreateInput("", 70, 5, 225, 20, 1)
$PASS_LBL = GUICtrlCreateLabel("Key", 5, 30, 60, 20, BitOR(4096, 1))
$PASS_INP = GUICtrlCreateInput("", 70, 30, 225, 20, 1)
$REGISTER = GUICtrlCreateButton("Register", 5, 55, 60, 20)
$GIVE_UP = GUICtrlCreateButton("Generate", 70, 55, 60, 20) <- Change name of button, Give Up by Generate
$TASK = GUICtrlCreateButton("?", 140, 55, 20, 20)
$AUTHOR = GUICtrlCreateLabel("keygen by deurus", 165, 55, 130, 20, BitOR(4096, 1))
GUISetState(@SW_SHOW, $MAIN)
While True
$MSG = GUIGetMsg()
Switch $MSG
Case $REGISTER
Call("CHECKKEY", GUICtrlRead($NAME_INP), GUICtrlRead($PASS_INP))
Case $GIVE_UP
Call("GETKEY", GUICtrlRead($NAME_INP), GUICtrlRead($PASS_INP)) <- Add the function to the button
Case $TASK
MsgBox(0, "Info", "keygen by deurus")
Case - 3
Exit
EndSwitch
Sleep(15)
WEnd
Func GETKEY($USER, $PASS) <- this is our keygen function
Local $OPKEY = "", $SIG = ""
Local $USER_LEN = StringLen($USER)
Local $PASS_LEN = StringLen($USER)
If $USER_LEN < 4 Then
GUICtrlSetData($PASS_INP ,"min 4 chars");
Else
For $INDEX = 1 To $USER_LEN
$SIG = Chr(BitXOR(Asc(StringMid($USER, $INDEX, 1)), 32)) & $SIG
Next
GUICtrlSetData($PASS_INP ,$SIG);
EndIf
EndFunc
Func CHECKKEY($USER, $PASS) <- check function, the original
Local $OPKEY = "", $SIG = ""
Local $USER_LEN = StringLen($USER)
Local $PASS_LEN = StringLen($PASS)
If $USER_LEN < $PASS_LEN Then
MsgBox(0, "ERROR", "Invalid username or key.")
ElseIf $USER_LEN < 4 Then
MsgBox(0, "ERROR", "Invalid username or key.")
Exit
EndIf
$PASS_INT = Int($USER_LEN / $PASS_LEN)
$PASS_MOD = Mod($USER_LEN, $PASS_LEN)
$OPKEY = _STRINGREPEAT($PASS, $PASS_INT) & StringLeft($PASS, $PASS_MOD)
For $INDEX = 1 To $USER_LEN
$SIG &= Chr(BitXOR(Asc(StringMid($USER, $INDEX, 1)), Asc(StringMid($OPKEY, $USER_LEN - $INDEX + 1, 1))))
Next
If $SIG = _STRINGREPEAT(Chr(32), $USER_LEN) Then
MsgBox(0, "INFO", "Your key was registered.")
Else
MsgBox(0, "INFO", "Your key is invalid.")
EndIf
EndFunc
Este es el resultado.

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


Warning: This challenge is still active and therefore should not be resolved using this information.
Aviso: Este reto sigue en activo y por lo tanto no se debería resolver utilizando esta información.
Resumidamente, esta técnica consiste en ocultar información en el bit menos significativo de cada uno de los píxeles de una imagen, consiguiendo así que el cambio realizado sea invisible al ojo humano. El problema de esta técnica, es que la información oculta puede obtenerse fácilmente si esta no se ha codificado previamente o si no se sigue un patrón concreto a la hora de ocultarla.
Desde la web del reto nos avisan de que esto es un simple truco pero espectacular. Nos animan a descargar una imágen y a encontrar la solución oculta.
Aprovecho este reto para presentaros una herramienta vital al enfrentaros a ciertos retos sobre esteganografía, steganabara.
Steganabara tiene dos apartados muy interesantes, uno es «color table» y otro «bit mask«, hoy veremos en acción a «bit mask».
No os preocupéis por la solución ya que cambia para cada usuario y sesión.
Abrimos steganabara y empezamos a trastear con bit mask.
Al poco tiempo ya vemos que vamos bien encaminados.
Finalmente no nos cuesta dar con la solución.

Hoy tenemos aquí otro crackme sacado del baúl de los recuerdos. En este caso se trata de una protección por tiempo límite a través de un keyfile llamado «data.det«. Disponemos de tres días o nueve sesiones antes de que el crackme expire.
La primera vez que ejecutamos el crackme, crea el fichero «data.det» y realiza lo siguiente:
00401000 t>/$ 6A 00 PUSH 0 ; /pModule = NULL 00401002 |. E8 0B020000 CALL <JMP.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA 00401007 |. A3 F4304000 MOV DWORD PTR DS:[4030F4],EAX ; kernel32.BaseThreadInitThunk 0040100C |. 6A 00 PUSH 0 ; /hTemplateFile = NULL 0040100E |. 68 80000000 PUSH 80 ; |Attributes = NORMAL 00401013 |. 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING 00401015 |. 6A 00 PUSH 0 ; |pSecurity = NULL 00401017 |. 6A 00 PUSH 0 ; |ShareMode = 0 00401019 |. 68 000000C0 PUSH C0000000 ; |Access = GENERIC_READ|GENERIC_WRITE 0040101E |. 68 A2304000 PUSH timetria.004030A2 ; |FileName = "DATA.DET" 00401023 |. E8 DE010000 CALL <JMP.&KERNEL32.CreateFileA> ; \CreateFileA 00401028 |. 83F8 FF CMP EAX,-1 0040102B |. 74 07 JE SHORT timetria.00401034 0040102D |. A3 14314000 MOV DWORD PTR DS:[403114],EAX ; kernel32.BaseThreadInitThunk 00401032 |. EB 18 JMP SHORT timetria.0040104C 00401034 |> 6A 30 PUSH 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL 00401036 |. 68 38304000 PUSH timetria.00403038 ; |Title = "I don't like this !" 0040103B |. 68 02304000 PUSH timetria.00403002 ; |Text = "Where is my DATA.DET file?\r\nI can't run without it..." 00401040 |. 6A 00 PUSH 0 ; |hOwner = NULL 00401042 |. E8 B3010000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA 00401047 |. E9 22010000 JMP timetria.0040116E 0040104C |> 6A 00 PUSH 0 ; /pOverlapped = NULL 0040104E |. 68 E0304000 PUSH timetria.004030E0 ; |pBytesRead = timetria.004030E0 00401053 |. 6A 32 PUSH 32 ; |BytesToRead = 32 (50.) 00401055 |. 68 AB304000 PUSH timetria.004030AB ; |Buffer = timetria.004030AB 0040105A |. FF35 14314000 PUSH DWORD PTR DS:[403114] ; |hFile = NULL 00401060 |. E8 B9010000 CALL <JMP.&KERNEL32.ReadFile> ; \ReadFile 00401065 |. 833D E0304000 0B CMP DWORD PTR DS:[4030E0],0B 0040106C |. 0F85 E9000000 JNZ timetria.0040115B 00401072 |. BB AB304000 MOV EBX,timetria.004030AB 00401077 |. 68 E4304000 PUSH timetria.004030E4 ; /pSystemTime = timetria.004030E4 0040107C |. E8 97010000 CALL <JMP.&KERNEL32.GetSystemTime> ; \GetSystemTime 00401081 |. 803B 00 CMP BYTE PTR DS:[EBX],0 00401084 |. 75 22 JNZ SHORT timetria.004010A8 ; Si existe el fichero salta a las comprobaciones 00401086 |. 51 PUSH ECX 00401087 |. 33C9 XOR ECX,ECX 00401089 |. EB 15 JMP SHORT timetria.004010A0 0040108B |> 66:8B81 E4304000 /MOV AX,WORD PTR DS:[ECX+4030E4] ; | 00401092 |. 66:35 6969 |XOR AX,6969 ; | 00401096 |. 66:8981 AB304000 |MOV WORD PTR DS:[ECX+4030AB],AX ; | 0040109D |. 83C1 02 |ADD ECX,2 ; | Bucle de codificacion de data.det por primera vez 004010A0 |> 83F9 08 CMP ECX,8 ; | 004010A3 |.^ 76 E6 \JBE SHORT timetria.0040108B ; | 004010A5 |. 59 POP ECX ; kernel32.7580EE1C 004010A6 |. EB 3A JMP SHORT timetria.004010E2
Vigilando el proceso de creación del archivo podemos llegar a la conclusión de como se genera.
Estado de la memoria:
004030AB B7 6E 63 69 6D 69 6B 69 68 69 60 ·ncimikihi`
004010E2 |> \A0 B5304000 MOV AL,BYTE PTR DS:[4030B5] 004010E7 |. 34 69 XOR AL,69 004010E9 |. 3C 00 CMP AL,0 004010EB |. /74 59 JE SHORT timetria.00401146 004010ED |. |FEC8 DEC AL 004010EF |. |A2 01304000 MOV BYTE PTR DS:[403001],AL 004010F4 |. |34 69 XOR AL,69 004010F6 |. |A2 B5304000 MOV BYTE PTR DS:[4030B5],AL 004010FB |. |6A 00 PUSH 0 ; /Origin = FILE_BEGIN 004010FD |. |6A 00 PUSH 0 ; |pOffsetHi = NULL 004010FF |. |6A 00 PUSH 0 ; |OffsetLo = 0 00401101 |. |FF35 14314000 PUSH DWORD PTR DS:[403114] ; |hFile = 00000034 (window) 00401107 |. |E8 18010000 CALL <JMP.&KERNEL32.SetFilePointer> ; \SetFilePointer 0040110C |. |6A 00 PUSH 0 ; /pOverlapped = NULL 0040110E |. |68 E0304000 PUSH timetria.004030E0 ; |pBytesWritten = timetria.004030E0 00401113 |. |6A 0B PUSH 0B ; |nBytesToWrite = B (11.) 00401115 |. |68 AB304000 PUSH timetria.004030AB ; |Buffer = timetria.004030AB 0040111A |. |FF35 14314000 PUSH DWORD PTR DS:[403114] ; |hFile = 00000034 (window) 00401120 |. |E8 05010000 CALL <JMP.&KERNEL32.WriteFile> ; \WriteFile
En cada ejecución realiza tres comprobaciones.
Recordemos el contenido del fichero:
B7 6E 63 69 6D 69 6B 69 68 69 60 ·ncimikihi`
1) Mes y año (4 primeros bytes)
004010A8 |> \8B0D AB304000 MOV ECX,DWORD PTR DS:[4030AB] ; ECX=69636EB7 004010AE |. 81F1 69696969 XOR ECX,69696969 ; 69636EB7 xor 69696969 = A07DE (A = mes y 7DE = año) 004010B4 |. A1 E4304000 MOV EAX,DWORD PTR DS:[4030E4] 004010B9 |. 3BC1 CMP EAX,ECX ; Compara con mes y año actuales 004010BB |. 0F85 85000000 JNZ timetria.00401146 ; Bad boy
2) Día (7º y 8º byte)
004010C1 |. 66:8B0D B1304000 MOV CX,WORD PTR DS:[4030B1] ; CX = 696B 004010C8 |. 66:81F1 6969 XOR CX,6969 ; 696B xor 6969 = 2 004010CD |. 66:A1 EA304000 MOV AX,WORD PTR DS:[4030EA] ; AX = día actual obtenido con GetSystemTime 004010D3 |. 66:2BC1 SUB AX,CX ; Los resta 004010D6 |. 66:83F8 03 CMP AX,3 ; Compara con 3 004010DA |. 77 6A JA SHORT timetria.00401146 ; Si el resultado >=3 Bad Boy
3) Sesiones (11º byte)
004010DC |. 2805 00304000 SUB BYTE PTR DS:[403000],AL ; 004010E2 |> A0 B5304000 MOV AL,BYTE PTR DS:[4030B5] ; AL = numero de sesiones actual 004010E7 |. 34 69 XOR AL,69 ; 61 Xor 69 = 8 004010E9 |. 3C 00 CMP AL,0 ; Compara con 0 004010EB |. 74 59 JE SHORT timetria.00401146 ; Salta si hemos superado las 9 sesiones. Bad boy 004010ED |. FEC8 DEC AL ; Si no le resta 1 004010EF |. A2 01304000 MOV BYTE PTR DS:[403001],AL 004010F4 |. 34 69 XOR AL,69 ; y le hace xor 69 para codificar el nuevo valor de sesión 004010F6 |. A2 B5304000 MOV BYTE PTR DS:[4030B5],AL
Con esto ya podemos alterar el archivo a nuestro antojo sin necesidad de parchear.
Try
ano = ano Xor 26985
mes = mes Xor 26985
dia = dia Xor 26985
anos = Hex(ano).ToString
mess = Hex(mes).ToString
dias = Hex(dia).ToString
If txtsesiones.Text <= 255 Then
sesioness = Hex(sesiones)
Else
sesiones = 255
End If
sesioness = Hex(sesiones)
'key = 00 00 00 00 00 00 00 00 00 00 00
'key = año+año+mes+mes+X+X+dia+dia+X+sesiones
key = Chr(Convert.ToInt32(anos.Substring(2, 2), 16)) & Chr(Convert.ToInt32(anos.Substring(0, 2), 16)) _
& Chr(Convert.ToInt32(mess.Substring(2, 2), 16)) & Chr(Convert.ToInt32(mess.Substring(0, 2), 16)) _
& Chr(106) & Chr(105) _
& Chr(Convert.ToInt32(dias.Substring(2, 2), 16)) & Chr(Convert.ToInt32(dias.Substring(0, 2), 16)) _
& Chr(103) & Chr(105) _
& Chr(Convert.ToInt32(sesioness.Substring(0, 2), 16))
'Creo el archivo llave
Dim ruta As String = Application.StartupPath & "\DATA.DET"
If File.Exists(ruta) Then
File.Delete(ruta)
End If
Using sw As StreamWriter = New StreamWriter(ruta, True, System.Text.Encoding.Default)
sw.Write(key)
sw.Close()
End Using
MsgBox("DATA.DET generado correctamente", MsgBoxStyle.Information + MsgBoxStyle.OkOnly, "Info")
Catch ex As Exception
MsgBox("Ocurrió algún error" & vbCrLf & ex.Message)
End Try

Hoy analizamos Copycat, un thriller psicológico de 1995 que, como muchas películas de la época, no pudo resistirse a incorporar elementos tecnológicos que, vistos desde una perspectiva actual, nos sacan una sonrisa. Vamos a desmontar algunos gazapos tecnológicos y curiosidades relacionadas con los sistemas informáticos que aparecen en la película.
La protagonista, la Dra. Helen Hudson (Sigourney Weaver), trabaja en un escritorio con tres pantallas, algo futurista para la época. En 1995, esto no era tan común como hoy en día. Para lograrlo, probablemente necesitaría tres ordenadores conectados de forma independiente, ya que los sistemas operativos y hardware de la época no solían soportar múltiples monitores en una sola máquina. Esto plantea preguntas interesantes sobre la logística de su set-up: ¿Cómo sincronizaba su trabajo entre tres PCs?

Un detalle curioso es que, en algunas tomas, se distingue la marca Compaq en los equipos. Compaq era una de las compañías líderes en la fabricación de ordenadores personales durante los 90 y conocida por sus soluciones de alta calidad. Este dato refuerza la idea de que el set-up de Helen estaba diseñado para representar lo último en tecnología de la época, aunque hoy resulte un tanto rudimentario. La elección de Compaq no es casual: en ese momento, era sinónimo de equipos potentes, usados tanto en oficinas como en entornos domésticos avanzados.
En una escena, Helen navega por internet con lo que suponemos es un módem de 28.8 kbps (o como mucho, un flamante 33.6 kbps, tecnología de vanguardia allá por 1995). Hasta ahí, vale. Sin embargo, la fluidez de su conexión sorprende: carga archivos, recibe correos y no se queda esperando con una pantalla de “Conectando…”. Pero lo mejor llega cuando, estando conectada, ¡suena el teléfono! En la realidad, esto cortaría la conexión o comunicaría, a menos que tuviera dos líneas telefónicas (algo raro en domicilios particulares de la época) o algún dispositivo milagroso que no conocemos.
Aunque no se distingue claramente el sistema operativo, vemos una interfaz gráfica con ventanas y una consola de comandos. Esto podría ser un guiño a Windows 3.1 o Windows 3.11, ya maduro en esa época aunque la interfaz no termina de encajar. Sin embargo, también podría ser una mezcla ficticia para hacer que el entorno luciera “tecnológico” sin comprometerse demasiado con la realidad. Detalle curioso: en los 90, las películas solían personalizar las interfaces para no tener problemas legales.
En los 90, el email era el rey. En las películas, los escritorios siempre tenían un gran icono de correo (a menudo animado, porque lo cool siempre parpadeaba). En Copycat, Helen recibe un correo con un archivo AVI de unos 30 segundos, lo cual plantea otra duda técnica: ¿Cuánto espacio ocupaba ese archivo en 1995?
Un AVI de 30 segundos probablemente tendría una resolución baja (320×240 píxeles o menos) y una tasa de compresión eficiente para la época, pero aun así podría pesar entre 2 y 5 MB, dependiendo de la calidad del audio y vídeo. Eso hubiera supuesto una odisea por email, ya que los servidores de la época limitaban los adjuntos a unos pocos cientos de KB. ¿Quizás el villano usó un protocolo privado para saltarse las restricciones?
Tras recibir un inquietante archivo AVI, la protagonista llama a la policía, lo que desencadena una conversación cargada de decisiones tecnológicas cuestionables:
Copycat (1995) es un buen ejemplo de cómo el cine de los 90 abordaba la tecnología con una mezcla de admiración y confusión. Desde la exageración de tener tres monitores en el escritorio de Helen hasta la torpe gestión del archivo Tomorrow.AVI, la película refleja tanto las limitaciones tecnológicas de la época como las libertades creativas de los guionistas.
En el caso del archivo AVI, los personajes deciden que no se puede gestionar digitalmente y optan por convertirlo a vídeo analógico, ignorando soluciones más simples como volver a enviarlo por correo electrónico (suponiendo que fuera posible). Este detalle, combinado con la ausencia aparente de personal técnico en la policía, subraya una desconexión entre la narrativa y las capacidades reales de la tecnología, incluso para 1995.
Aunque estos detalles pueden parecer cómicos 30 años después, forman parte del encanto de un cine que imaginaba el futuro sin comprender del todo su presente. Más que errores, son un recordatorio de cómo la tecnología ha evolucionado y de cómo nuestra percepción de ella también lo ha hecho.














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??}

While Crackmes.de returns, I leave a couple of files for practice.
Mientras vuelve Crackmes.de, os dejo un par de archivos para practicar.
In the folder crackmes.de_mirror you have two files:
En la carpeta crackmes.de_mirror tienes dos archivos:
password of files = deurus.info

Hoy tenemos aquí un crackme del 2009 originario de crackmes.de. El Crackme está hecho en VB6, sin empacar y consta de 4 tareas a superar. Un anti-debugger, un parcheo, una sorpresa y finalmente un algoritmo sencillo.
Nuestro primer incordio es el anti-debbuger. Este lo podemos afrontar de diferentes maneras, con un plugin desde Olly o de forma permanente parcheando. Si elegimos parchear debemos hacerlo en el offset 408328, cambiando el salto je por jmp.
00408328 /0F84 69030000 je T0RNAD0'.00408697
Si iniciamos el crackme nos encontramos con la siguiente nag que nos impide el arranque.
Las referencias de texto parecen encriptadas así que, ponemos un breakpoint a MSVBVM60.rtcMsgBox y vemos que la llamada se hace desde el offset 406897. Un poco más arriba encontramos un salto condicional muy interesante, concretamente en el offset 40677B. Lo cambiamos por un jmp y arrancamos el programa.
A continuación arranca el crackme y vemos lo siguiente.
La sorpresa es que el formulario no se mueve y no hay rastro de las cajas de texto del keygenme. Por suerte para nosotros este crackme está hecho en vb6 y como tal podemos abrirlo con VB Reformer para ver que se nos ofrece.
Abrimos VB Reformer y cambiamos la propiedad «Moveable» del formulario a true.
Ahora ya podemos mover el formulario y por suerte para nosotros, si lo movemos hacia la esquina superior izquierda aparecen las cajas de texto por arte de magia.
Como hemos dicho antes, las referencias de texto son inútiles, de modo que ponemos un breakpoint a MSVBVM60.__vbaStrCmp y enseguida obtenemos nuestro primer serial válido. También nos percatamos de que hasta que no metemos en el nombre 8 dígitos, no nos muestra un mensaje de error. De este mismo modo obtenemos que el nombre más grande puede tener 30 dígitos.
Username: deurusab (lenght 8)
0012F3F0 0040533A RETURN to T0RNAD0'.0040533A from MSVBVM60.__vbaStrCmp
0012F3F4 0015C954 UNICODE "L-8-deurus-0199F9CA"
Username: abcdefghijklmnopqrstuvwxyz1234 (lenght 30)
0012F3F0 0040533A RETURN to T0RNAD0'.0040533A from MSVBVM60.__vbaStrCmp
0012F3F4 0015F40C UNICODE "L-30-lmnopq-DD19F9CA"
Finalmente llegamos a la rutina de comprobación del serial. Usaremos como nombre: abcdefghijklmnopqrstuvwxyz1234.
00404E82 . 52 push edx 00404E83 . 56 push esi 00404E84 . C746 34 0DF0D1BA mov dword ptr ds:[esi+34],BAD1F00D |Variables cachondas 00404E8B . C746 38 01ADDE10 mov dword ptr ds:[esi+38],10DEAD01 |Variables cachondas 00404E92 . C746 3C EFBE1010 mov dword ptr ds:[esi+3C],1010BEEF |Variables cachondas 00404E99 . C746 40 D0BA0110 mov dword ptr ds:[esi+40],1001BAD0 |Variables cachondas 00404EA0 . FF91 2C070000 call ds:[ecx+72C] 00404EA6 . 3BC7 cmp eax,edi 00404EA8 . DBE2 fclex 00404EAA . 7D 12 jge short T0RNAD0'.00404EBE 00404EAC . 68 2C070000 push 72C 00404EB1 . 68 14404000 push T0RNAD0'.00404014 00404EB6 . 56 push esi 00404EB7 . 50 push eax 00404EB8 . FF15 40104000 call ds:[<&MSVBVM60.__vbaHresultCheckObj>] ; MSVBVM60.__vbaHresultCheckObj 00404EBE > 8B45 B4 mov eax,ss:[ebp-4C] 00404EC1 . 8D55 E0 lea edx,ss:[ebp-20] 00404EC4 . 52 push edx 00404EC5 . 50 push eax 00404EC6 . 8B08 mov ecx,ds:[eax] 00404EC8 . 8985 48FFFFFF mov ss:[ebp-B8],eax 00404ECE . FF91 A0000000 call ds:[ecx+A0] 00404ED4 . 3BC7 cmp eax,edi 00404ED6 . DBE2 fclex 00404ED8 . 7D 18 jge short T0RNAD0'.00404EF2 00404EDA . 8B8D 48FFFFFF mov ecx,ss:[ebp-B8] 00404EE0 . 68 A0000000 push 0A0 00404EE5 . 68 7C414000 push T0RNAD0'.0040417C 00404EEA . 51 push ecx 00404EEB . 50 push eax 00404EEC . FF15 40104000 call ds:[<&MSVBVM60.__vbaHresultCheckObj>] ; MSVBVM60.__vbaHresultCheckObj 00404EF2 > 8B45 E0 mov eax,ss:[ebp-20] |Mueve el nombre a eax 00404EF5 . 8D55 A0 lea edx,ss:[ebp-60] 00404EF8 . 8945 A8 mov ss:[ebp-58],eax 00404EFB . 6A 01 push 1 00404EFD . 8D45 90 lea eax,ss:[ebp-70] 00404F00 . 52 push edx 00404F01 . 50 push eax 00404F02 . 897D E0 mov ss:[ebp-20],edi 00404F05 . C745 A0 08000000 mov dword ptr ss:[ebp-60],8 00404F0C . FF15 40114000 call ds:[<&MSVBVM60.#619>] ; MSVBVM60.rtcRightCharVar 00404F12 . 8B3D D0104000 mov edi,ds:[<&MSVBVM60.__vbaStrVarVal>] ; MSVBVM60.__vbaStrVarVal 00404F18 . 8D4D 90 lea ecx,ss:[ebp-70] 00404F1B . 8D55 DC lea edx,ss:[ebp-24] 00404F1E . 51 push ecx 00404F1F . 52 push edx 00404F20 . FFD7 call edi ; <&MSVBVM60.__vbaStrVarVal> 00404F22 . 50 push eax 00404F23 . FF15 30104000 call ds:[<&MSVBVM60.#516>] ; MSVBVM60.rtcAnsiValueBstr |Toma el último dígito en ascii (4 asc = 34) 00404F29 . 66:6BC0 7B imul ax,ax,7B |34 * 7B = 18FC 00404F2D . 8B4E 34 mov ecx,ds:[esi+34] |Mueve BAD1F00D a ecx 00404F30 . 0F80 05070000 jo T0RNAD0'.0040563B 00404F36 . 0FBFC0 movsx eax,ax 00404F39 . 33C8 xor ecx,eax |18FC xor BAD1F00D = BAD1E8F1 00404F3B . 894E 34 mov ds:[esi+34],ecx 00404F3E . 8D4D DC lea ecx,ss:[ebp-24] 00404F41 . FF15 5C114000 call ds:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 00404F47 . 8D4D B4 lea ecx,ss:[ebp-4C] 00404F4A . FF15 60114000 call ds:[<&MSVBVM60.__vbaFreeObj>] ; MSVBVM60.__vbaFreeObj 00404F50 . 8D4D 90 lea ecx,ss:[ebp-70] 00404F53 . 8D55 A0 lea edx,ss:[ebp-60] 00404F56 . 51 push ecx 00404F57 . 52 push edx 00404F58 . 6A 02 push 2 00404F5A . FF15 20104000 call ds:[<&MSVBVM60.__vbaFreeVarList>] ; MSVBVM60.__vbaFreeVarList 00404F60 . 8B06 mov eax,ds:[esi] 00404F62 . 83C4 0C add esp,0C 00404F65 . 8D4D B4 lea ecx,ss:[ebp-4C] 00404F68 . 51 push ecx 00404F69 . 56 push esi 00404F6A . FF90 2C070000 call ds:[eax+72C] 00404F70 . 85C0 test eax,eax 00404F72 . DBE2 fclex 00404F74 . 7D 12 jge short T0RNAD0'.00404F88 00404F76 . 68 2C070000 push 72C 00404F7B . 68 14404000 push T0RNAD0'.00404014 00404F80 . 56 push esi 00404F81 . 50 push eax 00404F82 . FF15 40104000 call ds:[<&MSVBVM60.__vbaHresultCheckObj>] ; MSVBVM60.__vbaHresultCheckObj 00404F88 > 8B45 B4 mov eax,ss:[ebp-4C] 00404F8B . 8D4D E0 lea ecx,ss:[ebp-20] 00404F8E . 51 push ecx 00404F8F . 50 push eax 00404F90 . 8B10 mov edx,ds:[eax] 00404F92 . 8985 48FFFFFF mov ss:[ebp-B8],eax 00404F98 . FF92 A0000000 call ds:[edx+A0] 00404F9E . 85C0 test eax,eax 00404FA0 . DBE2 fclex 00404FA2 . 7D 18 jge short T0RNAD0'.00404FBC 00404FA4 . 8B95 48FFFFFF mov edx,ss:[ebp-B8] 00404FAA . 68 A0000000 push 0A0 00404FAF . 68 7C414000 push T0RNAD0'.0040417C 00404FB4 . 52 push edx 00404FB5 . 50 push eax 00404FB6 . FF15 40104000 call ds:[<&MSVBVM60.__vbaHresultCheckObj>] ; MSVBVM60.__vbaHresultCheckObj 00404FBC > 8B45 E0 mov eax,ss:[ebp-20] 00404FBF . 6A 01 push 1 00404FC1 . 8945 A8 mov ss:[ebp-58],eax 00404FC4 . 8D45 A0 lea eax,ss:[ebp-60] 00404FC7 . 8D4D 90 lea ecx,ss:[ebp-70] 00404FCA . 50 push eax 00404FCB . 51 push ecx 00404FCC . C745 E0 00000000 mov dword ptr ss:[ebp-20],0 00404FD3 . C745 A0 08000000 mov dword ptr ss:[ebp-60],8 00404FDA . FF15 2C114000 call ds:[<&MSVBVM60.#617>] ; MSVBVM60.rtcLeftCharVar 00404FE0 . 8D55 90 lea edx,ss:[ebp-70] 00404FE3 . 8D45 DC lea eax,ss:[ebp-24] 00404FE6 . 52 push edx 00404FE7 . 50 push eax 00404FE8 . FFD7 call edi 00404FEA . 50 push eax 00404FEB . FF15 30104000 call ds:[<&MSVBVM60.#516>] ; MSVBVM60.rtcAnsiValueBstr |Toma el primer dígito en ascii (a asc = 61) 00404FF1 . 66:6BC0 7B imul ax,ax,7B |61 * 7B = 2E9B 00404FF5 . 8B56 3C mov edx,ds:[esi+3C] |Mueve 1010BEEF a edx 00404FF8 . 0F80 3D060000 jo T0RNAD0'.0040563B 00404FFE . 0FBFC8 movsx ecx,ax 00405001 . 33D1 xor edx,ecx | 2E9B xor 1010BEEF = 10109074 00405003 . 8D4D DC lea ecx,ss:[ebp-24] 00405006 . 8956 3C mov ds:[esi+3C],edx 00405009 . FF15 5C114000 call ds:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 0040500F . 8D4D B4 lea ecx,ss:[ebp-4C] 00405012 . FF15 60114000 call ds:[<&MSVBVM60.__vbaFreeObj>] ; MSVBVM60.__vbaFreeObj 00405018 . 8D55 90 lea edx,ss:[ebp-70] 0040501B . 8D45 A0 lea eax,ss:[ebp-60] 0040501E . 52 push edx 0040501F . 50 push eax 00405020 . 6A 02 push 2 00405022 . FF15 20104000 call ds:[<&MSVBVM60.__vbaFreeVarList>] ; MSVBVM60.__vbaFreeVarList 00405028 . 66:8BCB mov cx,bx |Mueve a CX el tamaño del nombre 0040502B . 83C4 0C add esp,0C 0040502E . 66:69C9 4101 imul cx,cx,141 |Tamaño nombre(1E) * 141 = 259E 00405033 . 8B46 3C mov eax,ds:[esi+3C] 00405036 . 0F80 FF050000 jo T0RNAD0'.0040563B 0040503C . 0FBFD1 movsx edx,cx 0040503F . 8B4E 38 mov ecx,ds:[esi+38] |Mueve a ECX 10DEAD01 00405042 . 33D0 xor edx,eax |10109074 xor 259E = 1010B5BA 00405044 . 33CA xor ecx,edx |1010B5BA xor 10DEAD01 = 00CE18EB 00405046 . 66:8BD3 mov dx,bx 00405049 . 66:69D2 4101 imul dx,dx,141 |Tamaño nombre(1E) * 141 = 259E 0040504E . 0F80 E7050000 jo T0RNAD0'.0040563B 00405054 . 894E 38 mov ds:[esi+38],ecx 00405057 . 81F1 01010101 xor ecx,1010101 |00CE18EB xor 1010101 = 01CF19EA (Temp1) 0040505D . 0FBFD2 movsx edx,dx 00405060 . 3356 34 xor edx,ds:[esi+34] |BAD1E8F1 xor 259E = BAD1CD6F 00405063 . 894E 38 mov ds:[esi+38],ecx 00405066 . 35 10101010 xor eax,10101010 |10109074 xor 10101010 = 8064 0040506B . 8D4D B4 lea ecx,ss:[ebp-4C] 0040506E . 3156 40 xor ds:[esi+40],edx |BAD1CD6F xor 1001BAD0 = AAD077BF (Temp2) 00405071 . 8946 3C mov ds:[esi+3C],eax 00405074 . 8B06 mov eax,ds:[esi] 00405076 . 51 push ecx 00405077 . 56 push esi 00405078 . FF90 2C070000 call ds:[eax+72C] 0040507E . 85C0 test eax,eax 00405080 . DBE2 fclex 00405082 . 7D 12 jge short T0RNAD0'.00405096 00405084 . 68 2C070000 push 72C 00405089 . 68 14404000 push T0RNAD0'.00404014 0040508E . 56 push esi 0040508F . 50 push eax 00405090 . FF15 40104000 call ds:[<&MSVBVM60.__vbaHresultCheckObj>] ; MSVBVM60.__vbaHresultCheckObj 00405096 > 8B45 B4 mov eax,ss:[ebp-4C] 00405099 . 8D4D DC lea ecx,ss:[ebp-24] 0040509C . 51 push ecx 0040509D . 50 push eax 0040509E . 8B10 mov edx,ds:[eax] 004050A0 . 8985 48FFFFFF mov ss:[ebp-B8],eax 004050A6 . FF92 A0000000 call ds:[edx+A0] 004050AC . 85C0 test eax,eax 004050AE . DBE2 fclex 004050B0 . 7D 18 jge short T0RNAD0'.004050CA 004050B2 . 8B95 48FFFFFF mov edx,ss:[ebp-B8] 004050B8 . 68 A0000000 push 0A0 004050BD . 68 7C414000 push T0RNAD0'.0040417C 004050C2 . 52 push edx 004050C3 . 50 push eax 004050C4 . FF15 40104000 call ds:[<&MSVBVM60.__vbaHresultCheckObj>] ; MSVBVM60.__vbaHresultCheckObj 004050CA > 8B06 mov eax,ds:[esi] 004050CC . 8D4D B0 lea ecx,ss:[ebp-50] 004050CF . 51 push ecx 004050D0 . 56 push esi 004050D1 . FF90 2C070000 call ds:[eax+72C] 004050D7 . 85C0 test eax,eax 004050D9 . DBE2 fclex 004050DB . 7D 12 jge short T0RNAD0'.004050EF 004050DD . 68 2C070000 push 72C 004050E2 . 68 14404000 push T0RNAD0'.00404014 004050E7 . 56 push esi 004050E8 . 50 push eax 004050E9 . FF15 40104000 call ds:[<&MSVBVM60.__vbaHresultCheckObj>] ; MSVBVM60.__vbaHresultCheckObj 004050EF > 8B45 B0 mov eax,ss:[ebp-50] 004050F2 . 8D4D D0 lea ecx,ss:[ebp-30] 004050F5 . 51 push ecx 004050F6 . 50 push eax 004050F7 . 8B10 mov edx,ds:[eax] 004050F9 . 8985 3CFFFFFF mov ss:[ebp-C4],eax 004050FF . FF92 A0000000 call ds:[edx+A0] 00405105 . 85C0 test eax,eax 00405107 . DBE2 fclex 00405109 . 7D 18 jge short T0RNAD0'.00405123 0040510B . 8B95 3CFFFFFF mov edx,ss:[ebp-C4] 00405111 . 68 A0000000 push 0A0 00405116 . 68 7C414000 push T0RNAD0'.0040417C 0040511B . 52 push edx 0040511C . 50 push eax 0040511D . FF15 40104000 call ds:[<&MSVBVM60.__vbaHresultCheckObj>] ; MSVBVM60.__vbaHresultCheckObj 00405123 > 8B45 D0 mov eax,ss:[ebp-30] 00405126 . 6A 01 push 1 00405128 . 8945 98 mov ss:[ebp-68],eax 0040512B . 8D45 90 lea eax,ss:[ebp-70] 0040512E . 8D4D 80 lea ecx,ss:[ebp-80] 00405131 . 50 push eax 00405132 . 51 push ecx 00405133 . C745 A8 06000000 mov dword ptr ss:[ebp-58],6 0040513A . C745 A0 02000000 mov dword ptr ss:[ebp-60],2 00405141 . C745 D0 00000000 mov dword ptr ss:[ebp-30],0 00405148 . C745 90 08000000 mov dword ptr ss:[ebp-70],8 0040514F . FF15 40114000 call ds:[<&MSVBVM60.#619>] ; MSVBVM60.rtcRightCharVar 00405155 . 8D55 80 lea edx,ss:[ebp-80] 00405158 . 8D45 CC lea eax,ss:[ebp-34] 0040515B . 52 push edx 0040515C . 50 push eax 0040515D . FFD7 call edi 0040515F . 50 push eax 00405160 . FF15 30104000 call ds:[<&MSVBVM60.#516>] ; MSVBVM60.rtcAnsiValueBstr 00405166 . 8B56 40 mov edx,ds:[esi+40] |Mueve a EDX AAD077BF (Temp2) 00405169 . 68 90414000 push T0RNAD0'.00404190 ; UNICODE "L-" |Comienza el serial 0040516E . 0FBFC8 movsx ecx,ax |Mueve a ECX último dígito en ascii (34) 00405171 . 8B46 38 mov eax,ds:[esi+38] |Mueve a EAX 01CF19EA (Temp1) 00405174 . 53 push ebx 00405175 . 03D0 add edx,eax |AAD077BF + 01CF19EA = AC9F91A9 00405177 . C785 70FFFFFF 0300000>mov dword ptr ss:[ebp-90],3 00405181 . 0F80 B4040000 jo T0RNAD0'.0040563B 00405187 . 03CA add ecx,edx |AC9F91A9 + 34 = AC9F91DD (Nuestro serial) 00405189 . 0F80 AC040000 jo T0RNAD0'.0040563B 0040518F . 898D 78FFFFFF mov ss:[ebp-88],ecx 00405195 . FF15 04104000 call ds:[<&MSVBVM60.__vbaStrI2>] ; MSVBVM60.__vbaStrI2 0040519B . 8B3D 38114000 mov edi,ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove 004051A1 . 8BD0 mov edx,eax 004051A3 . 8D4D E0 lea ecx,ss:[ebp-20] 004051A6 . FFD7 call edi ; <&MSVBVM60.__vbaStrMove> 004051A8 . 50 push eax |EAX = tamaño del nombre 004051A9 . FF15 38104000 call ds:[<&MSVBVM60.__vbaStrCat>] ; MSVBVM60.__vbaStrCat |Concatena con "L-" 004051AF . 8BD0 mov edx,eax 004051B1 . 8D4D BC lea ecx,ss:[ebp-44] 004051B4 . FFD7 call edi 004051B6 . 66:83EB 06 sub bx,6 |Tamaño nombre - 6 = 18 004051BA . 50 push eax 004051BB . 0F80 7A040000 jo T0RNAD0'.0040563B 004051C1 . 0FBFCB movsx ecx,bx 004051C4 . 898D 08FFFFFF mov ss:[ebp-F8],ecx 004051CA . 8D45 A0 lea eax,ss:[ebp-60] 004051CD . DB85 08FFFFFF fild dword ptr ss:[ebp-F8] 004051D3 . 68 9C414000 push T0RNAD0'.0040419C 004051D8 . 50 push eax 004051D9 . DD9D 00FFFFFF fstp qword ptr ss:[ebp-100] 004051DF . DD85 00FFFFFF fld qword ptr ss:[ebp-100] 004051E5 . 833D 00A04000 00 cmp dword ptr ds:[40A000],0 004051EC . 75 08 jnz short T0RNAD0'.004051F6 004051EE . DC35 78114000 fdiv qword ptr ds:[401178] |18 / 2 = C (C es la posición para cojer dígitos del nombre, coje 6) 004051F4 . EB 11 jmp short T0RNAD0'.00405207 004051F6 > FF35 7C114000 push dword ptr ds:[40117C] 004051FC . FF35 78114000 push dword ptr ds:[401178] 00405202 . E8 3DC0FFFF call <jmp.&MSVBVM60._adj_fdiv_m64> 00405207 > DFE0 fstsw ax 00405209 . A8 0D test al,0D 0040520B . 0F85 25040000 jnz T0RNAD0'.00405636 00405211 . FF15 44114000 call ds:[<&MSVBVM60.__vbaR8IntI4>] ; MSVBVM60.__vbaR8IntI4 00405217 . 8B55 DC mov edx,ss:[ebp-24] |EDX = nombre 0040521A . 50 push eax |Eax = C 0040521B . 52 push edx 0040521C . FF15 6C104000 call ds:[<&MSVBVM60.#631>] ; MSVBVM60.rtcMidCharBstr |Mid(nombre,C,6) = "lmnopq" 00405222 . 8BD0 mov edx,eax 00405224 . 8D4D D8 lea ecx,ss:[ebp-28] 00405227 . FFD7 call edi 00405229 . 8B1D 38104000 mov ebx,ds:[<&MSVBVM60.__vbaStrCat>] ; MSVBVM60.__vbaStrCat 0040522F . 50 push eax 00405230 . FFD3 call ebx ; <&MSVBVM60.__vbaStrCat> |Concatena "-lmnopq" 00405232 . 8BD0 mov edx,eax 00405234 . 8D4D D4 lea ecx,ss:[ebp-2C] 00405237 . FFD7 call edi 00405239 . 50 push eax 0040523A . 68 9C414000 push T0RNAD0'.0040419C 0040523F . FFD3 call ebx |Concatena "-lmnopq-" 00405241 . 8BD0 mov edx,eax 00405243 . 8D4D C4 lea ecx,ss:[ebp-3C] 00405246 . FFD7 call edi 00405248 . 50 push eax 00405249 . 8D85 70FFFFFF lea eax,ss:[ebp-90] 0040524F . 50 push eax 00405250 . FF15 F0104000 call ds:[<&MSVBVM60.#572>] ; MSVBVM60.rtcHexBstrFromVar |serial "AC9F91DD" 00405256 . 8BD0 mov edx,eax 00405258 . 8D4D C8 lea ecx,ss:[ebp-38] 0040525B . FFD7 call edi 0040525D . 50 push eax 0040525E . FF15 B4104000 call ds:[<&MSVBVM60.#713>] ; MSVBVM60.rtcStrReverse |Invierte el serial "DD19F9CA" 00405264 . 8BD0 mov edx,eax 00405266 . 8D4D C0 lea ecx,ss:[ebp-40] 00405269 . FFD7 call edi 0040526B . 50 push eax 0040526C . FFD3 call ebx |Concatena "-lmnopq-DD19F9CA" 0040526E . 8BD0 mov edx,eax 00405270 . 8D4D B8 lea ecx,ss:[ebp-48] 00405273 . FFD7 call edi 00405275 . 50 push eax 00405276 . FFD3 call ebx |Concatena "L-30-lmnopq-DD19F9CA" ... 00405334 . FF15 80104000 call ds:[<&MSVBVM60.__vbaStrCmp>] ; MSVBVM60.__vbaStrCmp |Comparación final
Ejemplos:
Nombre: abcdefghijklmnopqrstuvwxyz1234 Serial: L-30-lmnopq-DD19F9CA Nombre: deurus2014 Serial: L-10-eurus2-84D8F9CA

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

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.
n = p * q
fi(n)=(p-1)(q-1)
Puede calcularse d=((Y*fi(n))+1)/e para Y=1,2,3,... hasta encontrar un d entero.
c = m^e mod n
m = c^d mod n
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).
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.
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.
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.
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());
}
});

Aquí tenemos un CrackMe diferente a lo que estamos acostumbrados, ya que en vez del típico número de serie asociado a un nombre la comprobación se realiza mediante checkboxes con una matriz de 7×3. El CrackMe está realizado en Visual C++ lo que facilita en parte encontrar rápidamente la rutina de comprobación.
004013C5 > /8B7424 10 MOV ESI,[DWORD SS:ESP+10] ; 004013C9 . |33FF XOR EDI,EDI 004013CB > |8B86 74304000 MOV EAX,[DWORD DS:ESI+403074] ; 004013D1 . |8BCB MOV ECX,EBX 004013D3 . |50 PUSH EAX 004013D4 . |E8 6F020000 CALL <JMP.&MFC42.#3092_CWnd::GetDlgItem> ; Lee el estado del checkbox 004013D9 . |8B48 20 MOV ECX,[DWORD DS:EAX+20] 004013DC . |6A 00 PUSH 0 004013DE . |6A 00 PUSH 0 004013E0 . |68 F0000000 PUSH 0F0 004013E5 . |51 PUSH ECX ; 004013E6 . |FFD5 CALL NEAR EBP 004013E8 . |3B86 20304000 CMP EAX,[DWORD DS:ESI+403020] ; Comprueba el estado del checkbox (1 activado 0 desactivado) 004013EE . |75 20 JNZ SHORT Matrix_C.00401410 ; Salto a chico malo 004013F0 . |47 INC EDI ; Incrementa contador 004013F1 . |83C6 04 ADD ESI,4 004013F4 . |83FF 07 CMP EDI,7 ; ¿Hemos terminado de leer las columnas? ¿contador = 7? 004013F7 .^|7C D2 JL SHORT Matrix_C.004013CB ; si terminan las columnas deja pasar 004013F9 . |8B4424 10 MOV EAX,[DWORD SS:ESP+10] 004013FD . |83C0 1C ADD EAX,1C ; contador de filas 00401400 . |83F8 54 CMP EAX,54 ; 3 filas = 1C+1C+1C=54 00401403 . |894424 10 MOV [DWORD SS:ESP+10],EAX 00401407 .^\7C BC JL SHORT Matrix_C.004013C5 ; ¿Hemos terminado de leer la fila? ¿contador = 54? 00401409 . 68 D8304000 PUSH Matrix_C.004030D8 ; ASCII "Registration successful!" 0040140E . EB 05 JMP SHORT Matrix_C.00401415 00401410 > 68 C8304000 PUSH Matrix_C.004030C8 ; ASCII "Not registered!"
En la rutina de comprobación se ve fácil un CMP EDI,7 por lo que podemos deducir que si el creador no se ha molestado mucho la comprobación se realiza de izquierda a derecha y de arriba hacia abajo.
Tal es así que si ponemos un breakpoint en 4013E8, podemos ir sacando el estado correcto de los checkboxes sin mucha molestia.

Se nos entrega un ELF que decompilado presenta este aspecto:
/* 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);
__int64 sub_401020();
__int64 sub_401030(); // weak
__int64 sub_401040(); // weak
__int64 sub_401050(); // weak
__int64 sub_401060(); // weak
__int64 sub_401070(); // weak
// int puts(const char *s);
// int printf(const char *format, ...);
// __int64 __isoc99_scanf(const char *, ...); weak
// void __noreturn exit(int status);
void __fastcall __noreturn start(__int64 a1, __int64 a2, void (*a3)(void));
void dl_relocate_static_pie();
char *deregister_tm_clones();
__int64 register_tm_clones();
char *_do_global_dtors_aux();
__int64 frame_dummy();
int __fastcall main(int argc, const char **argv, const char **envp);
_BYTE *__fastcall encode(__int64 a1);
__int64 __fastcall validar(const char *a1);
int banner();
int comprar();
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);
// __int64 _gmon_start__(void); weak
//-------------------------------------------------------------------------
// Data declarations
_UNKNOWN _libc_csu_init;
const char a31mparaSeguirU[43] = "\x1B[31mPara seguir usando este producto deber"; // idb
const char a32myaPuedesSeg[61] = "\x1B[32mYa puedes seguir afinando tus instrumentos (y tus flags "; // idb
const char aDirigaseANuest[21] = "\nDirigase a nuestra p"; // idb
__int64 (__fastcall *_frame_dummy_init_array_entry)() = &frame_dummy; // weak
__int64 (__fastcall *_do_global_dtors_aux_fini_array_entry)() = &_do_global_dtors_aux; // weak
__int64 (*qword_404010)(void) = NULL; // weak
char _bss_start; // weak
//----- (0000000000401000) ----------------------------------------------------
__int64 (**init_proc())(void)
{
__int64 (**result)(void); // rax
result = &_gmon_start__;
if ( &_gmon_start__ )
return (__int64 (**)(void))_gmon_start__();
return result;
}
// 404090: using guessed type __int64 _gmon_start__(void);
//----- (0000000000401020) ----------------------------------------------------
__int64 sub_401020()
{
return qword_404010();
}
// 404010: using guessed type __int64 (*qword_404010)(void);
//----- (0000000000401030) ----------------------------------------------------
__int64 sub_401030()
{
return sub_401020();
}
// 401030: using guessed type __int64 sub_401030();
//----- (0000000000401040) ----------------------------------------------------
__int64 sub_401040()
{
return sub_401020();
}
// 401040: using guessed type __int64 sub_401040();
//----- (0000000000401050) ----------------------------------------------------
__int64 sub_401050()
{
return sub_401020();
}
// 401050: using guessed type __int64 sub_401050();
//----- (0000000000401060) ----------------------------------------------------
__int64 sub_401060()
{
return sub_401020();
}
// 401060: using guessed type __int64 sub_401060();
//----- (0000000000401070) ----------------------------------------------------
__int64 sub_401070()
{
return sub_401020();
}
// 401070: using guessed type __int64 sub_401070();
//----- (00000000004010D0) ----------------------------------------------------
// 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();
}
// 4010DA: positive sp value 8 has been found
// 4010E1: variable 'v3' is possibly undefined
//----- (0000000000401100) ----------------------------------------------------
void dl_relocate_static_pie()
{
;
}
//----- (0000000000401110) ----------------------------------------------------
char *deregister_tm_clones()
{
return &_bss_start;
}
// 404050: using guessed type char _bss_start;
//----- (0000000000401140) ----------------------------------------------------
__int64 register_tm_clones()
{
return 0LL;
}
//----- (0000000000401180) ----------------------------------------------------
char *_do_global_dtors_aux()
{
char *result; // rax
if ( !_bss_start )
{
result = deregister_tm_clones();
_bss_start = 1;
}
return result;
}
// 404050: using guessed type char _bss_start;
//----- (00000000004011B0) ----------------------------------------------------
__int64 frame_dummy()
{
return register_tm_clones();
}
//----- (00000000004011B6) ----------------------------------------------------
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+10h] [rbp-10h] BYREF
int v5; // [rsp+14h] [rbp-Ch]
unsigned __int64 v6; // [rsp+18h] [rbp-8h]
v6 = __readfsqword(0x28u);
v5 = 0;
puts("\n\x1B[31m -----------Se le ha acabado el periodo de prueba gratuito-----------\n");
puts(a31mparaSeguirU);
do
{
banner();
__isoc99_scanf("%d", &v4);
if ( v4 == 3 )
exit(0);
if ( v4 > 3 )
goto LABEL_10;
if ( v4 == 1 )
{
comprar();
continue;
}
if ( v4 == 2 )
v5 = validar("%d");
else
LABEL_10:
puts("Opcion invalida, pruebe otra vez");
}
while ( !v5 );
puts(a32myaPuedesSeg);
return 0;
}
// 4010B0: using guessed type __int64 __isoc99_scanf(const char *, ...);
//----- (0000000000401291) ----------------------------------------------------
_BYTE *__fastcall encode(__int64 a1)
{
_BYTE *result; // rax
int i; // [rsp+14h] [rbp-4h]
for ( i = 0; i <= 33; ++i )
{
if ( *(char *)(i + a1) <= 96 || *(char *)(i + a1) > 122 )
{
if ( *(char *)(i + a1) <= 64 || *(char *)(i + a1) > 90 )
{
result = (_BYTE *)*(unsigned __int8 *)(i + a1);
*(_BYTE *)(i + a1) = (_BYTE)result;
}
else
{
result = (_BYTE *)(i + a1);
*result = (5 * ((char)*result - 65) + 8) % 26 + 65;
}
}
else
{
result = (_BYTE *)(i + a1);
*result = (5 * ((char)*result - 97) + 8) % 26 + 97;
}
}
return result;
}
//----- (00000000004013DB) ----------------------------------------------------
__int64 __fastcall validar(const char *a1)
{
int i; // [rsp+Ch] [rbp-64h]
char v3[48]; // [rsp+10h] [rbp-60h] BYREF
__int64 v4[6]; // [rsp+40h] [rbp-30h] BYREF
v4[5] = __readfsqword(0x28u);
qmemcpy(v4, "RisgAv{rIU_ihHwvIxA_sAppCsziq3vzC}", 34);
printf("\nIntroduce tu licencia: ");
__isoc99_scanf("%s", v3);
encode((__int64)v3);
for ( i = 0; i <= 33; ++i )
{
if ( v3[i] != *((_BYTE *)v4 + i) )
{
puts("\n\x1B[31mTu licencia es incorrecta\x1B[37m\n");
return 0LL;
}
}
puts("\n\x1B[32mEres un crack, lo conseguiste\x1B[37m");
return 1LL;
}
// 4010B0: using guessed type __int64 __isoc99_scanf(const char *, ...);
// 4013DB: using guessed type char var_60[48];
//----- (00000000004014CE) ----------------------------------------------------
int banner()
{
puts(" ___________OPCIONES___________");
puts(" | 1: Comprar licencia premium |");
puts(" | 2: Validar clave de licencia |");
puts(" | 3: Salir |");
puts(" ------------------------------");
return printf("> ");
}
//----- (0000000000401526) ----------------------------------------------------
int comprar()
{
return puts(aDirigaseANuest);
}
//----- (0000000000401540) ----------------------------------------------------
void __fastcall _libc_csu_init(unsigned int a1, __int64 a2, __int64 a3)
{
signed __int64 v3; // rbp
__int64 i; // rbx
init_proc();
v3 = &_do_global_dtors_aux_fini_array_entry - &_frame_dummy_init_array_entry;
if ( v3 )
{
for ( i = 0LL; i != v3; ++i )
(*(&_frame_dummy_init_array_entry + i))();
}
}
// 403E10: using guessed type __int64 (__fastcall *_frame_dummy_init_array_entry)();
// 403E18: using guessed type __int64 (__fastcall *_do_global_dtors_aux_fini_array_entry)();
//----- (00000000004015B0) ----------------------------------------------------
void _libc_csu_fini(void)
{
;
}
//----- (00000000004015B8) ----------------------------------------------------
void term_proc()
{
;
}
// nfuncs=33 queued=21 decompiled=21 lumina nreq=0 worse=0 better=0
// ALL OK, 21 function(s) have been successfully decompiled
Para resolver el juego y obtener una licencia válida, nos fijamos en el proceso de validación que se encuentra en la función validar (líneas 237 a 258). Esta función compara una entrada de licencia codificada con una licencia codificada almacenada en el programa.
La licencia almacenada es "RisgAv{rIU_ihHwvIxA_sAppCsziq3vzC}", y se utiliza la función encode (líneas 207 a 234) para codificar la entrada del usuario antes de compararla. La función encode aplica un cifrado simple a la entrada, alterando los caracteres alfabéticos según una fórmula específica.
La función de cifrado encode realiza lo siguiente:
(5 * (char - 97) + 8) % 26 + 97.(5 * (char - 65) + 8) % 26 + 65.Nos construimos una función en Python para decodificar la Flag y reto superado.
def decode(encoded_char):
if 'a' <= encoded_char <= 'z':
original_char = chr(((ord(encoded_char) - 97 - 8) * 21) % 26 + 97)
elif 'A' <= encoded_char <= 'Z':
original_char = chr(((ord(encoded_char) - 65 - 8) * 21) % 26 + 65)
else:
original_char = encoded_char
return original_char
encoded_license = "RisgAv{rIU_ihHwvIxA_sAppCsziq3vzC}"
decoded_license = "".join(decode(char) for char in encoded_license)
print("Licencia descifrada:", decoded_license)

Hace tiempo que me aficioné a los retos de Hacking y Cracking, y si bien la mayoría de ellos consisten en desencriptar una clave o realizar ingeniería inversa sobre un ejecutable, también los hay sobre programación pura y dura.
En esta ocasión se nos proporciona un código «muestra» parecido a PHP o C++ y tenemos que ingeniarnoslas para mejorarlo y ganar a la máquina.
El objetivo de esta misión es ganar a Tr0n en su propio juego: las carreras de motos. Se te proporcionará un programa (código) funcional para que veas como se controla el vehiculo. Usando tu inteligencia, tendrás que entender su uso y mejorarlo, ya que no es lo suficientemente bueno como para ganar a Tr0n. Tr0n lleva ya bastante tiempo en la parrilla de juegos y es bastante habilidoso 🙂
Cuando venzas a Tr0n un mínimo de 5 veces consecutivas, se te dará por superada esta prueba.
Buena suerte!!!
[ Available functions / Funciones disponibles ]
direction() returns current direction, change to a new one with direction([newdir])
getX(), getY() returns X and Y coordinates
collisionDistance() | collisionDistance([anydir]) returns the distance until collision
Note: parameters [*dir] can be empty or one of this values: UP DOWN LEFT or RIGHT[ Constants / Constantes ]
UP DOWN LEFT RIGHT MAX_X MAX_Y[ Rules / Reglas ]
Try to survive driving your bike and … / Intenta sobrevivir conduciendo tu moto y…
Don’t cross any line / No cruces ninguna línea
or crash with the corners! / o choques con las esquinas![ Mission / Mision ]
Use well this controller and beat Tr0n 5 consecutive times to score in this game
Usa bien este controlador y vence a Tr0n 5 veces consecutivas para puntuar en este juego
Nada más comenzar vemos que hemos perdido nuestra primera partida con el siguiente código:
function controller(playerController $c){
if($c->direction()==UP && $c->getY()<10){
if(rand(0,1)==0) $c->direction(LEFT);
else $c->direction(RIGHT);
goto done;
}
if($c->direction()==DOWN && MAX_Y-$c->getY()<10){
if(rand(0,1)==0) $c->direction(LEFT);
else $c->direction(RIGHT);
goto done;
}
if($c->direction()==LEFT && $c->getX()<10){
if(rand(0,1)==0) $c->direction(UP);
else $c->direction(DOWN);
goto done;
}
if($c->direction()==RIGHT && MAX_X-$c->getX()<10){
if(rand(0,1)==0) $c->direction(UP);
else $c->direction(DOWN);
}
done:
}
Nosotros somos el AZUL y la máquina es el VERDE.
Lo primero que tenemos que modificar son las distancias de las coordenadas que estan puestas en «<10» al mínimo, que sería «<2«. También sustituir la aleatoriedad «rand(0,1)==0» por algo más útil y comenzar a usar la función «collisionDistance()«.
Como podéis observar en el código inferior, usamos la función «collisionDistance()» para detectar cuando estamos a punto de chocar «collisionDistance() ==1» y para detectar a que lado nos conviene más girar en función de donde podamos recorrer más distancia «if($c->collisionDistance([LEFT]) >2) $c->direction(LEFT); else $c->direction(RIGHT);«.
if($c->direction()==UP && $c->getY()==1 && $c->collisionDistance() ==1){
if($c->collisionDistance([LEFT]) >2) $c->direction(LEFT);
else $c->direction(RIGHT);
}
if($c->direction()==DOWN && MAX_Y-$c->getY()<2 || $c->collisionDistance() ==1){
if($c->collisionDistance([LEFT]) >2) $c->direction(LEFT);
else $c->direction(RIGHT);
}
if($c->direction()==LEFT && $c->getX()==1 && $c->collisionDistance() ==1){
if($c->collisionDistance([UP]) >2)
$c->direction(UP);
else
$c->direction(DOWN);
}
if($c->direction()==RIGHT && MAX_X-$c->getX()<2 || $c->collisionDistance() ==1){
if($c->collisionDistance([UP]) >2) $c->direction(UP);
else $c->direction(DOWN);
}
El código anterior de por sí no nos resuelve mucho si no afinamos un poco más, comprobando todos las posibles colisiones y tomando la dirección correcta en función de la mayor distancia a recorrer.
if($c->collisionDistance([UP])==1 || $c->collisionDistance() ==1){
if($c->collisionDistance([LEFT]) > $c->collisionDistance([RIGHT]))
$c->direction(LEFT);
else
$c->direction(RIGHT);
}
if($c->collisionDistance([DOWN])==1 || $c->collisionDistance() ==1){
if($c->collisionDistance([LEFT]) > $c->collisionDistance([RIGHT]))
$c->direction(LEFT);
else
$c->direction(RIGHT);
}
if($c->collisionDistance([RIGHT])==1 || $c->collisionDistance() ==1){
if($c->collisionDistance([UP]) > $c->collisionDistance([DOWN]))
$c->direction(UP);
else
$c->direction(DOWN);
}
if($c->collisionDistance([LEFT])==1 || $c->collisionDistance() ==1){
if($c->collisionDistance([UP]) > $c->collisionDistance([DOWN]))
$c->direction(UP);
else
$c->direction(DOWN);
}
El código que utilicé yo para ganar a Tron es el siguiente:
function controller(playerController $c){
uno:
if($c->direction()==UP && $c->getY()==1 && $c->collisionDistance() ==1){
if($c->collisionDistance([LEFT]) >2) $c->direction(LEFT);
else $c->direction(RIGHT);
}
if($c->direction()==DOWN && MAX_Y-$c->getY()<2 || $c->collisionDistance() ==1){
if($c->collisionDistance([LEFT]) >2) $c->direction(LEFT);
else $c->direction(RIGHT);
}
if($c->direction()==LEFT && $c->getX()==1 && $c->collisionDistance() ==1){
if($c->collisionDistance([UP]) >2)
$c->direction(UP);
else
$c->direction(DOWN);
}
if($c->direction()==RIGHT && MAX_X-$c->getX()<2 || $c->collisionDistance() ==1){
if($c->collisionDistance([UP]) >2) $c->direction(UP);
else $c->direction(DOWN);
}
dos:
if($c->collisionDistance([UP])==1 || $c->collisionDistance() ==1){
if($c->collisionDistance([LEFT]) > $c->collisionDistance([RIGHT]))
$c->direction(LEFT);
else
$c->direction(RIGHT);
}
if($c->collisionDistance([DOWN])==1 || $c->collisionDistance() ==1){
if($c->collisionDistance([LEFT]) > $c->collisionDistance([RIGHT]))
$c->direction(LEFT);
else
$c->direction(RIGHT);
}
if($c->collisionDistance([RIGHT])==1 || $c->collisionDistance() ==1){
if($c->collisionDistance([UP]) > $c->collisionDistance([DOWN]))
$c->direction(UP);
else
$c->direction(DOWN);
}
if($c->collisionDistance([LEFT])==1 || $c->collisionDistance() ==1){
if($c->collisionDistance([UP]) > $c->collisionDistance([DOWN]))
$c->direction(UP);
else
$c->direction(DOWN);
}
done:
}
Mis jugadas ganadoras:
El código no es infalible ya que como comprabaréis vosotros mismos, no se puede ganar siempre por el mero hecho de la aleatoriedad y de la suerte. Cuando dispongais de un código decente, ejecutarlo varias veces para estar seguros antes de desecharlo.
Como se suele decir, la banca siempre gana, y en este caso no iba a ser menos y es que en caso de empate ¡la banca gana!
Por último deciros que podéis utilizar el código ya que la web detecta los códigos ganadores para que no se repitan.

Al descargar la imagen de la web del reto vemos que tiene la extensión php y lo más probable es que no nos abra correctamente.
... <br/> <img src="steg1img.php"/> <br/> ...
Abrimos la imagen con nuestro editor hexadecimal favorito y nos fijamos en la cabecera.
00000000h: 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 ; ‰PNG........IHDR
Renombramos entonces la extensión a png y continuamos.
Esta parte la afrontaremos con Steganabara, una herramienta muy útil que siempre uso cuando me enfrento a un reto «stego». En esta ocasión utilizaremos el análisis de color. Para ello pulsamos sobre «Analyse > Color table«.
En la tabla de colores tenemos la descomposición de colores RGBA y su frecuencia de aparición. Ordenamos por frecuencia descendiente y hacemos doble clic sobre la fila para abrir la imagen resultante.
A continuación un resumen de las imágenes obtenidas.
Como podéis observar, la imagen oculta es un código QR. Lo escaneamos con nuestra app preferida y obtenemos un texto encriptado.
dtsfwqutisvqtesymkuvabbujwhfecuvlshwopcyeghguywjvlaibflcacyahckyqvypjntfhihgtvyxeqakjwouldltuiuhbhjumgkxuugqahvwhotduqtahcknheypjetxpvlhxtlrpjagyjzcgijgfjmcupsslkzpuxegaillytlfbygeptzjtuzlvlwkzdznxqwpabbe
A partir de aquí el reto pasa a ser de encriptación. Con el tiempo diferenciareis fácilmente el tipo de cifrado con sólo ver el texto. En este caso lo primero que se nos ocurre es comprobar dos cifrados clásicos como son el cifrado César y el Vigenere.
Tras desestimar el cifrado César realizamos un ataque de «fuerza bruta» al cifrado Vigenere mediante análisis estadístico. En la imagen que muestro a continuación se puede ver que la clave está cerca de ser «HPHQTC» pero todavía no se lee correctamente.
Ya que la fuerza bruta de por sí no termina de darnos la respuesta correcta, pasamos a probar algo muy útil, esto es, descifrar por fuerza bruta pero dándole una palabra para comparar. En este caso en concreto vemos que una posible palabra que pudiera estar en el texto encriptado es «PASSWORD», probamos y reto terminado.