StartClean v1.24 Key Generator By Black Fenix

Bienvenidos a mi tercer tutorial sobre cracking, en esta entrega vamos a seguir con los keygens, esta vez haremos uno para el programa StartClean v1.24.

necesita.gif (15203 bytes)

marcador.gif (1024 bytes)SoftIce para Windows
marcador.gif (1024 bytes) Vista Rápida (QuickView de Windows)
marcador.gif (1024 bytes) Turbo Pascal, para hacer el Key Generator (Opcional)

Para poder hacer el programa que nos genere los numeros válidos, necesitamos saber como hace el programa para generar un numero de serie a partir del nombre que le hemos dado, esto significa encontrar la rutina que se encarga de esto dentro del ejecutable.

comenza.gif (16493 bytes)

marcador.gif (1024 bytes) Iremos a lo fácil, haremos click con el botón derecho del mouse sobre el ejecutable y pincharemos en Vista Rápida. Hechamos un vistazo a la lista de funciones que importa de la libreria KERNEL32.DLL y vemos que entre ellas se encuentra lstrcmpA, está función sirve para comparar dos cadenas de caracteres aquí tienes su descripción detallada:

intlstrcmp(
LPCTSTR lpString1, // puntero a la primera cadena
LPCTSTR lpString2 // puntero a la segunda cadena
);

Si la cadena lpString1 = lpString2 el valor de retorno es igual a 0 si no será diferente que 0. ¿ Es probable que el programa use esta función para comparar el numero de serie válido con el numero de serie que nosotros le introducimos aleatoriamente ?, Si en el caso que nos ocupa, pero no suele ser muy normal ya que la mayoria de programas usan sus propias funciones de comparación para dificultar el trabajo a los crackers. Dado que este programa es pequeño en tamaño yo supongo que el autor prefirió usar las funciones del Kernel a crearse las suyas propias para así no aumentar el tamaño del ejecutable. Pero es igual más fácil para nosotros. Manos a la obra.

marcador.gif (1024 bytes) Con el SoftIce residente ejecutamos el programa y hacemos click en el botón Register. aparece una ventana donde podemos introducir el Nombre y el numero de serie, yo he usado lo siguiente:

Nombre: Black Fenix
Key: 12345

Antes de pulsar Ok, entramos en el SoftIce (Ctrl+D) y ponemos un bpx en lstrcmpA, salimos del SoftIce (Ctrl+D) y pulsamos Ok. Boom!! El breakpoint a surtido efecto, estamos al inicio de la rutina lstrcmpA. Ahora vamos a ver quien la llamó, pulsamos F12 una vez y estaremos debajo de la llamada a lstrcmpA, veremos algo así:

PUSH 406030 -> Pasa una dirección (donde se guardará el numero de serie válido)
PUSH 406130 -> Y otra que es nuestro nombre 'Black Fenix' en mi caso
CALL 401280 -> Llama a la rutina que calcula el numero de serie válido
LEA EAX,[ESP+18] -> Dirección de nuestro numero de serie '12345'
ADD ESP,08 -> reajusta la pila
PUSH EAX -> Primera cadena a comparar es nuestro numero de serie '12345'
PUSH 406030 -> se comparará con el numero de seria válido
CALL [KERNEL32!lstrcmp] -> llama a lstrcmp
TEST EAX,EAX -> EAX es el resultado de la comparación (Estamos aquí !! )
JNZ 401271 -> si <> 0 no són iguales ( nos envia al mensaje de error)

Puedes comprobar las cadenas dentro del SoftIce poniendo un bpx en la llamada a lstrcmp pulsando g y de nuevo en el botón OK, ahora puedes hacer:

d 406030 -> Veras el numero de serie válido '2136-20406-2641-509'
d eax -> Nuestro numero '12345'

Bueno, ahora podriamos modificar el salto despues del TEST EAX,EAX y el programa se registraria con cualquier numero, pero como lo que nos interesa es no modificar el ejecutable y crear un generador de números válidos, lo que tenemos que examinar a continuacion es el CALL 401280 que es la rutina que se encarga de generar el número válido:

marcador.gif (1024 bytes) Pues ponemos un bpx en esta y volvemos a ejecutar el programa con los mismo datos que hasta ahora, pulsamos Ok y ya estaremos de nuevo en SoftIce pulsamos F8 y entraremos en la rutina 401280 iremos paso a paso hasta llegar a la dirección 4012D2 (Todo lo que hay antes basicamente se encarga de inicializar la cadena donde se copiará el numero válido con ceros ):

 

:004012A2BD6A000000mov ebp, 0000006A // Inicializa ebp con 6A (ebp es donde se guarda el código en formato numérico. El valor inicial del código será 6Ah = 106 decimal Nota: EDI contiene la dirección de la función CharNext
//......................... //.................... //........................................... ...................................................................................................................
:004012D20FBE08movsx ecx, byte ptr [eax] // Lee caracter del nombre Black Fenix
:004012D550push eax // pasa la cadena como parametro de CharNext
:004012D68D6C4D00lea ebp, dword ptr [ebp+2*ecx] // codigo1 = codigo1 + 2 x caracter
:004012DAFFD7call edi // llama a CharNext
:004012DC803800cmp byte ptr [eax], 00 // es el último caracter (si acaba en NULL)
:004012DF75F1jne 004012D2 // No, pasa al siguiente caracter
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004012D0(C)
:004012E18D442410lea eax, dword ptr [esp+10] // carga puntero a buffer vacio
:004012E555push ebp // Pasa el primer codigo codigo1
* Possible StringData Ref from Data Obj //"%d-"
:004012E66874624000push 00406274 // "%d-" // como numero + un guión de separación
:004012EB50push eax // pasa dirección del buffer vacio donde copiar el numero
* Reference To: USER32.wsprintfA, Ord:0249h
:004012ECFF15D4924000Call dword ptr [004092D4] // llama a wsprintfA (Esto escribe en la cadena pasada el primer codigo con formato XXXXX-
:004012F28D44241Clea eax, dword ptr [esp+1C] // y lo añade a la cadena donde se almacenará el numero
:004012F683C40Cadd esp, 0000000C // resultante
:004012F950push eax
:004012FA56push esi
* Reference To: KERNEL32.lstrcatA, Ord:0266h
:004012FBFF153C924000Call dword ptr [0040923C] // llama a lstrcatA (concatena dos cadenas, ver más abajo)
:004013018BC3mov eax, ebx
:00401303803B00cmp byte ptr [ebx], 00
:004013067412je 0040131A
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401318(C)
:004013080FBE08movsx ecx, byte ptr [eax] // Lee caracter del nombre 'Black Fenix'
:0040130B03C9add ecx, ecx // caracter=caracter+caracter
:0040130D50push eax // pasa puntero al nombre para CharNext
:0040130E8D14C9lea edx, dword ptr [ecx+8*ecx] // codigoaux = caracter*8+caracter
:0040131103EAadd ebp, edx // codigo = codigoaux+codigo
:00401313FFD7call edi // Llama a CharNext
:00401315803800cmp byte ptr [eax], 00 // es el último caracter?
:0040131875EEjne 00401308 // No, pasa al siguiente
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401306(C)
:0040131A8D442410lea eax, dword ptr [esp+10] // esto
:0040131E55push ebp // da formato
* Possible StringData Ref from Data Obj //"%d-"
:0040131F6874624000push 00406274 // al segundo código
:0040132450push eax // de la forma
* Reference To: USER32.wsprintfA, Ord:0249h
:00401325FF15D4924000Call dword ptr [004092D4] // XXXXX-
:0040132B8D44241Clea eax, dword ptr [esp+1C] // y
:0040132F83C40Cadd esp, 0000000C // lo
:0040133250push eax // concatena
:0040133356push esi // al primer
* Reference To: KERNEL32.lstrcatA, Ord:0266h
:00401334FF153C924000Call dword ptr [0040923C] // codigo quedando XXXXX-XXXXX-
:0040133A8BC3mov eax, ebx
:0040133C803B00cmp byte ptr [ebx], 00
:0040133F7418je 00401359
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401357(C)
:004013410FBE08movsx ecx, byte ptr [eax] // Lee caracter de 'Black Fenix'
:0040134450push eax // pasa puntero a la cadena para CharNext
:004013458D2C89lea ebp, dword ptr [ecx+4*ecx] // código = caracter*4+caracter
:004013488D0C69lea ecx, dword ptr [ecx+2*ebp] // codigo = codigo*2+caracter
:0040134B8D2C4D01000000lea ebp, dword ptr [2*ecx+00000001] // codigo = codigo*2+1
:00401352FFD7call edi // llama a CharNext
:00401354803800cmp byte ptr [eax], 00 // es el último caracter ?
:0040135775E8jne 00401341 // No, pasa al siguiente

Nota: este bucle es un poco inutil ya que no se suman los valores que se obtiene con cada caracter, esto significa que el codigo siempre será sobre el último caracter de la cadena

* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040133F(C)
:004013598D442410lea eax, dword ptr [esp+10] // esto
:0040135D55push ebp // da formato
* Possible StringData Ref from Data Obj //"%d-"
:0040135E6874624000push 00406274 // al tercer codigo
:0040136350push eax // de la forma
* Reference To: USER32.wsprintfA, Ord:0249h
:00401364FF15D4924000Call dword ptr [004092D4] // XXXXX-
:0040136A8D44241Clea eax, dword ptr [esp+1C] // y lo concatena
:0040136E83C40Cadd esp, 0000000C // al
:0040137150push eax // primero
:0040137256push esi // y segundo
* Reference To: KERNEL32.lstrcatA, Ord:0266h
:00401373FF153C924000Call dword ptr [0040923C] // quedando XXXXX-XXXXX-XXXXX-
:004013798BC3mov eax, ebx
:0040137B803B00cmp byte ptr [ebx], 00
:0040137E7412je 00401392
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401390(C)
:004013800FBE08movsx ecx, byte ptr [eax] // lee caracter de 'Black Fenix'
:0040138350push eax // pasa la cadena como argumento de CharNext
:004013848D2C8D1D000000lea ebp, dword ptr [4*ecx+0000001D] // código = 4*caracter+1d
:0040138BFFD7call edi // llama a CharNext
:0040138D803800cmp byte ptr [eax], 00 // es el último caracter ?
:0040139075EEjne 00401380 // No, pasa al siguiente caracter

Nota: este bucle es un poco inutil ya que no se suman los valores que se obtiene con cada caracter, esto significa que el codigo siempre será sobre el último caracter de la cadena

 

* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040137E(C)
:004013928D442410lea eax, dword ptr [esp+10] // da formato
:0040139655push ebp // al último código
* Possible StringData Ref from Data Obj //"%d" // de la forma
:004013976870624000push 00406270 // XXXXX
:0040139C50push eax
* Reference To: USER32.wsprintfA, Ord:0249h
:0040139DFF15D4924000Call dword ptr [004092D4]
:004013A38D44241Clea eax, dword ptr [esp+1C] // concatena
:004013A783C40Cadd esp, 0000000C // el último
:004013AA50push eax // código
:004013AB56push esi // con los restantes
* Reference To: KERNEL32.lstrcatA, Ord:0266h
:004013ACFF153C924000Call dword ptr [0040923C] // quedando XXXXX-XXXXX-XXXXX-XXXXX
:004013B25Dpop ebp
:004013B35Fpop edi
:004013B45Epop esi
:004013B55Bpop ebx
:004013B681C400010000add esp, 00000100
:004013BCC3ret

En el código aparece un función llamada lstrcat, esta función se encarga de concatenar dos cadenas terminadas en nulo, aquí tienes una descripción de su funcionamiento.

La función lstrcat añade una cadena al final de otra (concatena cadenas)

LPTSTRlstrcat(
LPTSTR lpString1, // dirección del buffer para las cadenas contatenadas
LPCTSTR lpString2 // dirección de la cadena que se añadira a la cadena lpString1
);

marcador.gif (1024 bytes)Parametros:

lpString1 -> Puntero a una cadena terminadas en nulo. El buffer debe ser lo suficientemente grande para contener las dos cadenas.
lpString2 -> Puntero a una cadena terminada en nulo que será añadida a lpString1

marcador.gif (1024 bytes)Retorna:

Si la función tiene exito retorna un puntero a lpString1.
Si la función falla retorna NULL.

keygen.gif (14559 bytes)

Como puedes comprobar una rutina bastante sencilla. Se basa en la suma de los codigos ASCII de los caracteres que componen el nombre introducido.

El primer codigo se calcularia así:

codigo=6Ah

inicio
leer caracter
codigo=codigo ASCII caracter * 2 + codigo
pasar a siguiente caracter
si caracter <>0 ir a inicio

El segundo

codigo=Primer codigo

inicio
leer caracter
codigoaux=codigo ASCII caracter * 8 + codigo ASCII caracter
codigo=codigo+codigoaux
pasar a siguiente caracter
si caracter <>0 ir a inicio

El tercero

codigo = 0

inicio
leer caracter
codigo=((código ASCII caracter*4+codigo ASCII caracter)*2+codigo ASCII caracter)*2+1
pasar al siguiente caracter
si caracter <>0 ir a inicio

Nota: este bucle es una chorrada ya que el código siempre será en base al último caracter de la cadena, pero es así como lo calcula el programa (probablemente sea un fallo del programador).

El cuarto

codigo = 0

inicio
leer caracter
codigo:=código ASCII caracter*4+$1d;
pasar al siguiente caracter
si caracter <>0 ir a inicio

Nota: este bucle tambien es una chorrada ya que el código siempre sera en base al último caracter de la cadena, pero es así como lo calcula el programa (probablemente sea otro fallo del programador)

Bueno ahora ya puedes escribir un programilla en C o Pascal que genere los numeros. Para los más vagos aquí teneis el código fuente de un programilla en Pascal que genera los numeros ( no esta muy currao pero funciona) :

Nota: Para des-registrar el programa una vez registrado busca la cadena Start Clean en el editor de registros de Windows y elimina la carpeta del mismo nombre una vez encontrada.


Program StartClean_KeyGenerator;

Uses crt;

Var auxcode,code1,code2,code3,code4,i:word;
caracter:byte;
Name:string[80];
strlen:word;

Begin


Writeln('StartClean v1.2 Key Generator by Black Fenix.');
Writeln('---------------------------------------------');
Write('Enter your name:');
read(Name);
Writeln;
strlen:=length(Name);
code1:=$6a;
for i:=1 to strlen do
Begin

caracter:=ord(Name[i]);
code1:=caracter*2+code1;

End;
code2:=code1;
for i:=1 to strlen do
Begin

caracter:=ord(Name[i]);
caracter:=caracter*2;
auxcode:=caracter*8+caracter;
code2:=auxcode+code2;

End;
code3:=0;
for i:=1 to strlen do
Begin

caracter:=ord(Name[i]);
code3:=((caracter*4+caracter)*2+caracter)*2+1;

End;
code4:=0;
for i:=1 to strlen do
Begin

caracter:=ord(Name[i]);
code4:=caracter*4+$1d;

End;
Write('The registration number is: ');
Writeln(code1,'-',code2,'-',code3,'-',code4);
Writeln('Enjoy it!');

End.


You are inside Reversed Minds pages.

por Mr. Silver / WKT!
La información aquí vertida es exclusivamente para uso educacional, no puedo hacerme responsable del uso que se haga de esta, por lo que atiendo a la honradez de cada uno :), recuerda que debes comprar el software que utilices :)