Tuturial:
empiezo poniendo bp en GetDlgItemTextA meto cualquier dato en el crackme y vemos que para en el kernel, execute until usercode u caemos en
00401F03 |. E8 78100000 CALL <JMP.&USER32.GetDlgItemTextA>
q es caundo lee el nombre, mas abajo hay otro similar al del pass, asi que aqui estamos, el problema es que si seguimos debugando vemos que estamos en una especie de "message handler", y aqui no es donde procesa los datos, bueno alli vimos que guardo el nombre en 4061A0, asi que ponemos dump 4061A0, y le colocamos un bp Memory on access
alli paro en
0040142B |. 8038 00 CMP BYTE PTR DS:[EAX],0
alli vemos una funcion que nos recuerda al crackme 1 de karman, comprobamos las entradas y salidas , la salida de EAX esta sospechosa, pero si ejecutamos hasta el ret vemos que esta al final del "message handler" y no hace anda con EAX, asi que no le damos mucha importancia a esta parte
vuelvo a colocar un bpm on acesss pues lo habia quitado para no tener q dar f9 20 veces en la funcion anterior
esta ves cae en 00401759 MOV DWORD PTR SS[ESP+4],crackme.004061A0 , esta metiendo el dato en el stack para llamar un CALL que tiene ad+ de ese otros dos argumentos, uno es
00401755 MOV DWORD PTR SS:[ESP+8],EAX que es la longitud del nombre y otro q es una zona de memoria con ceros, otra ves quito el bpm para que no moleste debugueo la funcion, no voy a explicar el ensamblador, supongo que todos podeis saber lo q hace debugandola, basicamente va haciendo un xor de todas los caracteres del nombre y un caracter constante que obtiene de [EAX+404078], los resultados de la operacion los guarda en la zona de ceros
al salir del CALL va entrar en otro, esta ves con 2 argumentos , uno es el pass y otro otra zona de ceros
esta tiene dos bucles uno recorre el pass restandole 'A' a cada caracter si es mayor de '?' y restandole 16h en caso contrario, despues entra en otro bucle que hace operaciones logicas con el pass y guarda los resultados en la zona de ceros, pero cada caracter de salida esta formado por 2 de entrada, los caracters pares del pass (0, 2 , 4 ) forman los 4 bits de mayor peso de los BYTEs y los impares la parte baja.
al salir de ese CALL, hace una llamada a(kernel32.SetEvent), y no podemos seguir traceando a mano, bueno seguimos la misma estrategia
buscamos 00401761 en el dump (aqui se guardo el resultado de las operaciones con el nombre) y le ponemos un bpm on acesss, f9 y caemos en
0040167E MOV EAX,DWORD PTR DS:[EDX+EAX]
se trata de una funcion que tratando los priemros 8 bytes los de los resultados anteriores como 2 DWORD, le hace un XOR a estos con otro DWORD que optiene con un AND de otras 2 constantes, asi que lo podemos ver en conjunto como un simple XOR
cuando termina , hace lo mismo con el pass procesado.
despues hace un XOR con las 4 DWORDs, (2 del pass y 2 del name) y comprueba q este sea 0, comprobamos que cuando es 0 nos da el mensaje bueno y cuando es diferente de 0 nos da el mensaje malo, asi que tenemos que lograr que este XOR sea 0.
sabemos que si (a XOR b)==0 entonces a==b
llamando pf al pass procesado final y nf al nombre prosesado final , y p1,n1 al primer procesado del nombre y pass respectivamente
ad+ pf[0] es la primera DWORD obtenida y pf[1] la segunda
j={0,1}
0 = pf[j] XOR nf[j]
pf[j] = (p1[j] XOR c1[j]) <-- si le mandamos una cadena
de nulos a la funcion que hace el XOR final del pass podemos obtener c1[j] pues (a XOR 0) == a
0 = pf[j] XOR p1[j] XOR c1[j]
p1[j] = pf[j] XOR c1[j]
ahi ya sabemos cuando tiene que dar el primer procesado del pass, asi que solo queda revertir esa funcion
aqui os pongo el codigo en C++ del generador de claves que hize yo
#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#define DWORD unsigned int
#define BYTE unsigned char
//0404078h
char const1[8*10];
char const2[16];
char const3[16];
void ShowDump(char* buff, DWORD s)
{
//muestra el contenido de una zona de memoria en hexdecimal
int j,i;
i=0;
for (i=0;i<s;i+=8) {
for (j=i;(j-i)<8;j++){
if (!(j<s)) {
printf (" ");
continue;
}
if ((unsigned char)buff[j] <= 0xF) printf("0");
printf("%X ",(unsigned char)buff[j]);
if ((j-i)==3) printf("| ");
}
for (j=i;j<s && (j-i)<8;j++){
if (j==i) printf("| ");
if (buff[j]) printf("%c ",buff[j]);
else printf(". ");
}
printf("\n");
}
}
void name1(char * nombre,DWORD len,char * buff)
{
//primer procesamiento del name
int j,i;
char c;
for (j=0;j<=7;j++)
{
buff[j]=const1[j];
for (i=0;i<len;i++)
{
c=nombre[i];
c+=c;
c=c^buff[j];
buff[j]=c;
}
}
}
void pass1(char *pass,char *buff) {
//primer procesamiento del pass
DWORD j;
BYTE b;
char l,h;
for (j=0;pass[j];j++) {
if (pass[j] > '?') pass[j]-='A'; //se queda con el valor numerico
else pass[j]-=0x16; //???
}
BYTE i;
for (i=0;i<=7;i++){
l = pass[2*i];
_asm SAR l,1
l &= 0xF; //bit bajo
h = pass[2*i+1];
_asm SAR h,1
_asm SHL h,4
buff[i]=h|l;
}
}
void reversePass1(char *pass,char *buff) {
//revierte el primer proceso del pass
BYTE i,j,b,h,l;
for (j=0;j<8;j++)
{
//bit de menor peso
b=buff[j];
b&=0xF;
b<<=1;
pass[j*2]=b+'A';
//bit de mayor peso
b=buff[j];
b>>=4;
b<<=1;
pass[j*2+1]=b+'A';
}
}
void getDWORD(char * buff,char *cadena,char * garb)
{
DWORD j,i,k;
DWORD *ptrG,*ptrG2,*ptrB;
for (j=0;j<8;j++) buff[j]=cadena[j];
for (j=0;j<=1;j++)
{
for (i=0;i<=1;i++){
if (j==i) continue;
k=2*j+i;
k <<= 2;
ptrG = (DWORD*)((2*i+j)*4+garb);
ptrG2 = (DWORD*)(garb+k);
ptrB = (DWORD*)(buff+(j*4));
k = ptrG[0] & ptrG2[0];
ptrB[0] ^= k;
}
}
}
int main(int argc, char *argv[])
{
char username[16],pass[32],nbuff[16],pbuff[16],n2buff[16],p2buff[16];
memset(const1,0,sizeof(const1));
memset(username,0,sizeof(username));
memset(pass,0,sizeof(pass));
memset(nbuff,0,sizeof(nbuff));
memset(n2buff,0,sizeof(n2buff));
memset(pbuff,0,sizeof(pbuff));
memset(p2buff,0,sizeof(p2buff));
memcpy(const1,"\x4C\x0D\x18\x6A\x3F\xB9\x25\xE7\xBD\x96\x9A\x91\xDF\x8F\x9E\x8F\x1E\xDE\xDE\xDE\xDF\xA6\x9E\xDF\x8B\x9A\x91\x9A\x8C\xDF\x9E\x93\x98\x90\xDF\x8F\x9E\x8D\x9E\xDF\x8D\x9A\x9C\x90\x8D\x9B\x9E\x8D\xDF\x9C\x8A\x9E\x91\x9B\x90\xDF\x8C\x9A\x9E\x8C\xDF\x89\x96\x9A\x95\x90\xD7\xC0\xC0\xC0\xC0\xD6\x00\x00\x00\x00\x00\x00\x00\x00",sizeof(const1));
memcpy(const2,"\x0D\xEB\xD0\x68\xE4\x6A\x9D\x48\x81\x56\xA3\xE3\x9E\x85\xF1\x7D",sizeof(const2));
memcpy(const3,"\xCA\x78\x8E\x2C\xD5\x61\xA9\x17\xB4\xA5\x96\x87\xF0\xE1\xD2\xC3",sizeof(const3));
system("CLS");
printf("Usuario:");
scanf("%s",username);
strcpy(pass,"");
system("CLS");
ShowDump(username,sizeof(username));
ShowDump(pass,sizeof(pass));
name1(username,strlen(username),nbuff);
ShowDump(nbuff,sizeof(nbuff));
pass1(pass,pbuff);
ShowDump(pbuff,sizeof(pbuff));
getDWORD(n2buff,nbuff,const2);
ShowDump(n2buff,sizeof(n2buff));
getDWORD(p2buff,pbuff,const3);
ShowDump(p2buff,sizeof(p2buff));
//final
DWORD *pdw1,*pdw2;
pdw1=(DWORD*)n2buff;
pdw2=(DWORD*)p2buff;
DWORD res[2];
char * ver= (char*)&res;
memset(pbuff,0,sizeof(pbuff));
memset(pass,0,sizeof(pass));
res[0]=pdw1[0]^pdw2[0];
res[1]=pdw1[1]^pdw2[1];
for (int j=0;j<8;j++) pbuff[j]=ver[j];
reversePass1(pass,pbuff);
ShowDump(ver,8);
ShowDump(pass,sizeof(pass));
for (int j=0;j<80;j++) printf("-");
printf("Usuario:%s\nClave:%s\n",username,pass);
system("PAUSE");
return EXIT_SUCCESS;
}
podeis bajaros el keygen ya compilado aqui ->
http://rapidshare.com/files/68490778/karman02gen.rar.htmluna combinacion valida
user: x4uth
pass: ECIEGKOMCMAS]MU]