/******************************************************************************
 *
 *  autor: Daniel Lerch Hostalot
 *  url: http://daniellerch.com  
 *
 ******************************************************************************
 *
 *  DESCRIPCIÓN:
 *  Sistema de búsqueda de resumenes MD5 mediante fuerza bruta.
 *  Prueba combinaciones de letras hasta la longitud máxima, especificada en
 *  PASSWD_LENGTH. Para contraseñas de más de 7 caracteres el tiempo resulta
 *  excesivo. Coge las contraseñas enctriptadas de un archivo pasado como
 *  parámetro.
 *
 ******************************************************************************
 *
 *  Compilación:
 *  $ gcc cracker_md5.c -o cracker_md5 -lcrypt
 *
 *****************************************************************************/ 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/evp.h>

#define ASCII_MIN 32 		/* Número inicial en la tabla ascii */
#define ASCII_MAX 126		/* Número final en la tabla ascii */
#define PASSWD_LENGTH 10	/* Longitud máxima del password */

/* Cadena donde se almacena el password antes de cada encriptación */
char password_string[PASSWD_LENGTH];

/* Cadena donde se almacena el código ascii de cada carácter */
int  password[PASSWD_LENGTH];


/****************************************************************************** 
 *  Bucle que genera todas las combinaciones posibles de los carácteres
 *  situados entre ASCII_MIN y ASCII_MAX. Para cada combinación comprueba
 *  si el texto encriptado es el buscado 
 ******************************************************************************/
int brute_force_loop (int col, char *encrypted_pass) {

   int i;
   int res=0;

   for ( password[col]=ASCII_MIN-1;
         password[col]<ASCII_MAX+1;
         password[col]++) {

      if (col != 0) res = brute_force_loop (col - 1, encrypted_pass);
      else {
         memset (password_string, '\0', sizeof(password_string));   
         for (i=0;i<PASSWD_LENGTH;i++) 
            if (password[i]>=ASCII_MIN) 
               password_string[i] = password[i];


         printf ("%s\n", password_string);
         if (md5_cmp(password_string,encrypted_pass)==0) {

            printf ("Encontrado: %s\n", password_string);
            return 1;
         }
      }
      if (res == 1) return 1;
   }
   return 0;
}





/******************************************************************************
 *  Función de resumen MD5
 *****************************************************************************/
unsigned char *simple_digest (char *alg, 
                              char *buffer, 
                              unsigned int len, 
                              int *olen)
{
   EVP_MD *m;
   EVP_MD_CTX ctx;
   unsigned char *ret;

   OpenSSL_add_all_digests ();
   if (!(m = (EVP_MD*) EVP_get_digestbyname (alg)))
      return NULL;
   if (!(ret = (unsigned char *) malloc (EVP_MAX_MD_SIZE)))
      return NULL;
   EVP_DigestInit (&ctx, m);
   EVP_DigestUpdate (&ctx, buffer, len);
   EVP_DigestFinal (&ctx, ret, olen);
   return ret;
}

/******************************************************************************
 * Función de comparación de resúmenes MD5
 *****************************************************************************/ 
int md5_cmp (char *password_string, char *encrypted_pass) {

   unsigned int olen;
   unsigned char *ret;
   char resumen[34];
   
   memset (resumen, '\0', sizeof(resumen));
   ret = simple_digest ("md5",password_string,strlen(password_string),&olen);
   snprintf (resumen, sizeof(resumen), 
   "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
   ret[0], ret[1], ret[2], ret[3], ret[4], ret[5], ret[6], ret[7], 
   ret[8], ret[9], ret[10], ret[11], ret[12], ret[13], ret[14], ret[15]);


   return strcmp(resumen, encrypted_pass);
}


/*****************************************************************************
 *  Función principal 
 *****************************************************************************/
int main (int argc, char **argv) {


   if (argc != 2) {

      printf ("Uso: %s password.txt\n",argv[0]);
      exit (0);
   }


   FILE *file;
   char buffer[34];

   /* Apertura del fichero de contraseñas */
   file = fopen (argv[1], "r");
   if (!file) {

      perror ("fopen()");
      exit(0);
   }

   while (!feof(file)) {

      fscanf (file, "%s", buffer);
      brute_force_loop(PASSWD_LENGTH, buffer);
      getchar();
   }

   fclose(file);

   return 0;
}

