Solucion al reverseme 2 de Victor Bien, lo primero que hare, antes de dar una solucion al reverseme, sera explicar brevemente su funcionamiento. Como bien comentaba Victor, construir un fichero de licencia valido no era una tarea complicada, en comparacion con la complejidad que el algoritmo de comprobacion tenia. Veamos primero un pequeño pseudo-codigo que muestra que es lo que hace el reverseme: main() { FILE *f; if(!fopen_s(&f,"reverseme.lic","rb")) { BYTE control; if(1==fread(&control, 1, 1, f)) { DWORD ValidResult; if(1==fread(&ValidResult,4,1,f)) { if(car != 0) { DWORD Result; if(CheckLicense(f,8,car,&Result)) { if(Result == ValidResult) { ok(Cojones, lo has conseguido); } } } } } fclose(f); } } BYTE CheckLicense(FILE*f,int Depth, char Control, DWORD *pResult) { BYTE RetValue = 0; DWORD LocalResult = 0; DWORD LocalResult2 = 0; BYTE NewCar = 0; if(Value<=0 || CheckLicense(f,Depth-1,Control,&LocalResult)!=0) { if(fread(&NewCar,1,1,f)==1 && NewCar!=0) { if(Value<=0 || CheckLicense(f,Depth-1,Control,&LocalResult2)!=0) { RetValue = 1; if(Depth) { Depth -= 1; Control >>= Depth; Control &= 1; if(Control) { (*pResult) = NewCar + LocalResult2; } else { (*pResult) = NewCar + LocalResult; } } else { (*pResult) = NewCar; } } } } return RetValue; } Una vez que tenemos el algoritmo expresado en más alto niveL, ya es muy facil analizar el reverseme y hacer un generador de licencias para el. Podemos ver como CheckLicense no puede devolver 0 en ningun caso, que es equivalente a que fread no puede fallar y ademas el nuevo caracter leido no puede ser igual a 0. Veamos con un ejemplo como se irian produciendo las llamadas y calculando los resultados parciales (Estoy llamando resultados a el valor devuelto por referencia y no al resultado de la funcion, que solo indica si la accion se pudo llevar a cabo con exito) Digito de control: CB que en binario es 11001011 Resultado : ??? el valor que estamos intentando calcular Bytes aleatorios de nuestra licencia: 27 11 fb e5 cf b9 b4 9e 88 72 5c 46 30 1a 15... Esquema de las llamadas: . . . CheckLicense(Depth = 3) | CheckLicense(Depth = 2)---(etc. etc.) | CheckLicense(Depth = 1)------------------------------------------------------ | | | | CheckLicense(Depth = 0)-- Read(Obtiene 11) CheckLicense(Depth=0)-- Resultado(11+FB) | | | | Read( Obtiene 27) Resultado(27) Read(Obtiene fb) Resultado(fb) De este ejempo podemos sacar como conclusión varias cosas. Sobre la cadena anterior podemos indicar con que Depth se ha leido cara caracter: 0 1 0 2 0 1 0 3 0 1 0 2 1 0 1 27 11 fb e5 cf b9 b4 9e 88 72 5c 46 30 1a 15... Y podemos ver como para la posición 3 en la que esta 9E, el segundo resultado sera el obtenido por los 7 valores a su derecha, mientras el primer resultado sera el obtenido por los de la izquierda. Para el byte de control que hemos dado, se sumaria el resultado de la izquierda ( bit de su posicion el control a 0). También podemos ver que el nivel inferior se encuentra situado a 2^(Depth-1), que el valor maximo estará en el centro, y que el numero totales de valores que hay que leer es 2^(Depth+1)-1. Con toda esta informacion tenemos mas que suficiente para escribir un generador de licencias, asi que... ahi va el mio (Tratandose solo de un juego hoy voy a dejar de lado mis paranoias como programador, y muestro esta chapuza tal y como esta): typedef struct _LICENSE { LPBYTE pBuffer; DWORD dwSize; UINT Depth; }LICENSE,*PLICENSE; BYTE GetRandomByte() { // TODO: Make a real random generator LARGE_INTEGER li; BYTE Result; do { QueryPerformanceCounter(&li); Result = LOBYTE(LOWORD(li.LowPart)); } while(Result == 0); return Result; } void MakeLicenseValid(PLICENSE pLicense) { LPDWORD pCrcResult; LPBYTE pCheckBuffer; UINT Depth; UINT Offset; pCrcResult = (LPDWORD)(&(pLicense->pBuffer[1])); pCheckBuffer = &(pLicense->pBuffer[5]); Offset = (pLicense->dwSize - 5)/2; Depth = pLicense->Depth; *pCrcResult = 0; while(Depth-- > 0) { (*pCrcResult) += pCheckBuffer[Offset]; if((pLicense->pBuffer[0]>>(Depth))&1) { Offset += (1 << Depth); } else { Offset -= (1 << Depth); } } (*pCrcResult) += pCheckBuffer[Offset]; } PLICENSE InitializeLicense(UINT Depth) { PLICENSE pLicense = NULL; if(Depth && Depth <=8) { pLicense = (PLICENSE) HeapAlloc(GetProcessHeap(),0, sizeof(LICENSE)); if(pLicense) { pLicense->pBuffer = (LPBYTE) HeapAlloc(GetProcessHeap(),0,pLicense->dwSize); if(pLicense->pBuffer) { DWORD i; pLicense->Depth = Depth; pLicense->dwSize = (1<<(Depth+1))+4; for(i=0;idwSize;i++) { pLicense->pBuffer[i] = GetRandomByte(); } MakeLicenseValid(pLicense); } else { HeapFree(GetProcessHeap(),0,pLicense); pLicense = NULL; } } } return pLicense; } VOID FreeLicense(PLICENSE pLicense) { if(pLicense) { if(pLicense->pBuffer) { HeapFree(GetProcessHeap(),0,pLicense->pBuffer); } HeapFree(GetProcessHeap(),0,pLicense); } } BOOL WriteLicenseToFile(PLICENSE pLicense,LPCSTR szFileName) { // TODO : Return error codes // TODO : dont close handle & delete file! BOOL Result = FALSE; HANDLE hFile; hFile = CreateFileA(szFileName,FILE_GENERIC_READ + FILE_GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); if(hFile != INVALID_HANDLE_VALUE) { DWORD BytesWritten; BOOL WriteResult; WriteResult = WriteFile(hFile, pLicense->pBuffer, pLicense->dwSize, &BytesWritten, NULL); CloseHandle(hFile); if(WriteResult && BytesWritten==pLicense->dwSize) { Result = TRUE; } else { DeleteFileA(szFileName); } } return Result; } int main(int argc, char * argv[]) { PLICENSE pLicense; UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv); #ifdef _DEBUG DebugBreak(); #endif // TODO: receive filename as input parameter // TODO: output messages to user! pLicense = InitializeLicense(8); if(pLicense) { WriteLicenseToFile(pLicense, "reverseme.lic"); FreeLicense(pLicense); } return 0; } Para terminar, un saludo para la gentuza de 48 bits, y felicitaciones para Victor por el reverseme. De nuevo he vuelto a pasar un rato divertido. Miguel