/* * md5crack.c * $Id: md5crack.c,v 1.3 2008/04/14 08:21:28 candy Exp candy $ */ #include #include #include #include #include #include #include #ifdef unix #include #else #include "getopt.h" extern char *crypt(const char *key, const char *salt); #endif #ifdef USE_SSL #include #define MD5Init(a) MD5_Init(a) #define MD5Update(a,b,c) MD5_Update(a,b,c) #define MD5Final(a,b) MD5_Final(a,b) #else #include "global.h" #include "md5.h" #endif static char *myname; static int verbose; static int no_out; static int istrip; static char *vbuf; static size_t vbufsize; static char *basename; static char *log_name_fmt = "%scrack_log.txt"; static char log_name[FILENAME_MAX]; static char *config_name_fmt = "%scrack_config.txt"; static char config_name[FILENAME_MAX]; static char *out_name_fmt = "%scrack_out.%06lu"; static char out_name[FILENAME_MAX]; static size_t digest_size; /* hash_size: 16 if md5, 8 if trip */ static size_t hash_len; /* hash output size: 32 if md5, 8 if trip */ #define STUB_SIZE 8 /* stub size */ #define GIGA 1073741824LU static char stub[STUB_SIZE + 1]; static FILE *out_fp = NULL; static unsigned long nrec = 0; static unsigned long nrecGIGA = 0; static void stub_init(void) { memset(stub, '\t', sizeof(stub) - 1); }/* stub_init */ static int out_open(void) { int err = -1; if (no_out) err = 0; else { sprintf(out_name, out_name_fmt, basename, nrecGIGA); out_fp = fopen(out_name, "ab"); if (out_fp != NULL) { if (vbuf) setvbuf(out_fp, vbuf, _IOFBF, vbufsize); err = 0; } else fprintf(stderr, "%s: %s: %s\n", myname, out_name, strerror(errno)); } return err; }/* out_open */ static void out_close(void) { if (out_fp != NULL) { fclose(out_fp); out_fp = NULL; } }/* out_close */ static int hex_to_i(int c) { int x = 0; if (isdigit(c)) { x = c - '0'; } else if (isxdigit(c)) { x = toupper(c) - 'A' + 10; } return x; }/* hex_to_i */ static int hex_decode(const char *hex, char *buf, size_t bufsize) { size_t i = 0; int len = strlen(hex); const unsigned char *s = (const unsigned char *)hex; char *d = buf; while (i < bufsize && isxdigit(*s)) { if (len & 1) { *d++ = hex_to_i(*s++); len--; } else { int h = hex_to_i(*s++); int l = hex_to_i(*s++); *d++ = (h << 4) | l; } i++; }/* while */ return i; }/* hex_decode */ static char *chset_default = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 _!\"#$%&'()*+,-./:;<=>?@[\\\\]^`{|}~"; #define CHSETMAX 256 static int chset_size; static char chset[CHSETMAX]; #define LENMAX 64 static int cur_len; static int idx[LENMAX]; static unsigned char target_digest[16]; #define E9 1000000000LU static unsigned long ntry = 0; static unsigned long ntryE9 = 0; static sig_atomic_t ringing; static void sig_handler(int sig) { ringing = sig; }/* sig_handler */ static void sig_setup(void) { #ifdef SIGHUP signal(SIGHUP, sig_handler); #endif #ifdef SIGINT signal(SIGINT, sig_handler); #endif #ifdef SIGTERM signal(SIGTERM, sig_handler); #endif #ifdef SIGINFO signal(SIGINFO, sig_handler); #endif }/* sig_setup */ static char *ato2x[256] = { "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f", "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f", "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f", "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f", "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f", "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f", "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f", "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f", "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f", "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f", "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af", "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf", "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf", "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df", "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef", "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff", }; static int print_digest(FILE *fp, const unsigned char digest[16]) { static char buf[16 * 2 + 1]; int i; for (i = 0; i < 16; i++) { memcpy(buf + i * 2, ato2x[digest[i]], 2); }/* for */ buf[16 * 2] = '\0'; fputs(buf, fp); return 0; }/* print_digest */ static int print_str(FILE *fp, const char *s, size_t n) { size_t i; for (i = 0; i < n; i++) { int ch = (unsigned char)s[i]; const char *fmt = "%c"; if (ch == '\\') { fmt = "\\%c"; } else if ((ch >= 0 && ch <= ' ') || (ch >= 0x7f && ch <= 0xff) || ch == '\'') { fmt = "\\x%02x"; } fprintf(fp, fmt, ch); }/* for */ return 0; }/* print_str */ static int atox(int c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'A' && c <= 'F') return c - 'A' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10; return 0; }/* atox */ #define isoctal(c) ((c) >= '0' && (c) <= '7') static size_t lex(const char *str, unsigned char *buf, size_t size) { const unsigned char *s = (const unsigned char *)str; unsigned char *d = buf; size_t i = 0; while (i < size && *s != '\0') { int x = 0; switch (*s++) { case '\\': if (*s == 'x') { if (isxdigit(s[1])) { if (isxdigit(s[2])) { x = (atox(s[1]) << 4) | atox(s[2]); s += 3; } else { x = atox(s[1]); s += 2; } } else { x = *s++; } } else if (isoctal(s[0])) { if (isoctal(s[1])) { if (isoctal(s[2])) { x = (((atox(s[0]) << 3) | atox(s[1])) << 3) | atox(s[2]); s += 3; } else { x = (atox(s[0]) << 3) | atox(s[1]); s += 2; } } else { x = atox(s[0]); s += 1; } } else { x = *s++; } break; default: x = s[-1]; break; }/* switch */ *d++ = x; i++; }/* while */ if (verbose & 2) { size_t j; printf("key:"); for (j = 0; j < i; j++) printf(" %02x", buf[j]); printf("\n"); } return i; }/* lex */ static void status_report(FILE *fp) { int i; char date[64]; time_t t = time(NULL); struct tm tm = *localtime(&t); strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", &tm); fprintf(fp, "%s ", date); if (ntryE9 != 0) fprintf(fp, "%lu%09lu ", ntryE9, ntry); else fprintf(fp, "%lu ", ntry); fprintf(fp, "-c '"); print_str(fp, chset, chset_size); fprintf(fp, "' "); if (istrip) fputs(target_digest, fp); else print_digest(fp, target_digest); fprintf(fp, " %d", cur_len); for (i = 0; i < cur_len; i++) { fprintf(fp, " %d", idx[i]); }/* for */ fprintf(fp, "\n"); }/* status_report */ static void logging(const char *msg) { FILE *fp; #ifndef unix status_report(stderr); #endif fp = fopen(log_name, "a"); if (fp != NULL) { status_report(fp); if (msg != NULL) { fprintf(fp, "%s\n", msg); } fclose(fp); } }/* logging */ static int config_save(const char *name) { int err = -1; FILE *fp = fopen(name, "w"); if (fp != NULL) { int i; if (istrip) fputs(target_digest, fp); else print_digest(fp, target_digest); fprintf(fp, "\n"); print_str(fp, chset, chset_size); fprintf(fp, "\n"); fprintf(fp, "%d", cur_len); for (i = 0; i < cur_len; i++) fprintf(fp, " %d", idx[i]); fprintf(fp, "\n"); fprintf(fp, "%lu %lu\n", ntryE9, ntry); fprintf(fp, "%lu %lu\n", nrecGIGA, nrec); fclose(fp); err = 0; } else { fprintf(stderr, "%s: %s: %s\n", myname, name, strerror(errno)); } return err; }/* config_save */ static int config_digest(const char *name, const char *str) { int err = -1; size_t l; if (istrip) { l = strlen(str); if (l == digest_size) { err = 0; strcpy(target_digest, str); } else fprintf(stderr, "%s: %s: hash must be %d characters\n", myname, name, hash_len); } else { l = hex_decode(str, target_digest, sizeof(target_digest)); if (l == digest_size) err = 0; else fprintf(stderr, "%s: %s: hash must have %d hexadicimal characters\n", myname, name, hash_len); } return err; }/* config_digest */ static int config_chset(const char *name, const char *str) { int err = -1; chset_size = lex(str, chset, sizeof(chset)); if (chset_size > 0 && chset_size <= CHSETMAX) err = 0; else fprintf(stderr, "%s: %s: chset len must be > 0 and <= %d \n", myname, name, CHSETMAX); return err; }/* config_chset */ static int config_cur_len(const char *name, const char *str, char **v) { int err = -1; char *p; cur_len = strtol(str, &p, 10); if (cur_len <= LENMAX) { int i; err = 0; for (i = 0; i < cur_len && err == 0; i++) { int x = 0; if (v != NULL) { if (*v != NULL) x = strtol(*v++, 0, 10); else { fprintf(stderr, "%s: %s: no data for index[%d]\n", myname, name, i); err = -1; } } else { if (str == p) { fprintf(stderr, "%s: %s: no data for index[%d]\n", myname, name, i); err = -1; } else { str = p; x = strtol(str, &p, 10); } } if (x <= chset_size) idx[i] = x; else { fprintf(stderr, "%s: %s: index[%d]=%d > chset_size (%d)\n", myname, name, i, x, chset_size); err = -1; } }/* for */ } else fprintf(stderr, "%s: %s: cur_len must b <= %d \n", myname, name, LENMAX); return err; }/* config_cur_len */ static int config_load(const char *name) { int err = -1; FILE *fp = fopen(name, "r"); if (fp != NULL) { int st = 0; char lbuf[2048]; err = 0; while (err == 0 && fgets(lbuf, sizeof(lbuf), fp) != NULL) { if (strrchr(lbuf, '\n') != NULL) *strrchr(lbuf, '\n') = '\0'; else { int ch; while ((ch = getc(fp)) != EOF && ch != '\n') ; } switch (st) { case 0: if ((err = config_digest(name, lbuf)) == 0) st++; break; case 1: if ((err = config_chset(name, lbuf)) == 0) st++; break; case 2: if ((err = config_cur_len(name, lbuf, NULL)) == 0) st++; break; case 3: sscanf(lbuf, "%lu %lu", &ntryE9, &ntry); st++; break; case 4: sscanf(lbuf, "%lu %lu", &nrecGIGA, &nrec); st++; break; }/* switch */ }/* while */ fclose(fp); } return err; }/* config_load */ static unsigned char trtab[256]; static void tr_init(void) { char *p1 = ":;<=>?@[\\]^_`"; char *p2 = "ABCDEFGabcdef"; while (*p1 != '\0') trtab[(unsigned char)*p1++] = *p2++; }/* tr_init */ static char * tr_exec(char *buf) { unsigned char *p = (unsigned char *)buf; while (*p != '\0') { if (trtab[*p] != 0) *p =trtab[*p]; p++; }/* while */ return buf; }/* tr_exec */ static unsigned char * trip(const char *key) { char salt[6]; char *ret; { strncpy(salt, key, 3); salt[4] = '\0'; strcat(salt, "H."); } { unsigned char *p = (unsigned char *)salt + 1; while (*p != '\0') { if (*p < '.' || *p > 'z') *p = '.'; p++; }/* for */ } tr_exec(salt + 1); ret = crypt(key, salt + 1); if (ret == NULL) ret = "!!!"; return ret + 3; }/* trip */ static unsigned char * md5(const char *key, size_t klen) { static unsigned char digest[16]; MD5_CTX md5buf; MD5Init(&md5buf); MD5Update(&md5buf, key, klen); MD5Final(digest, &md5buf); return digest; }/* md5 */ static int tryn(const unsigned char goal[16]) { int err = 0; size_t rec_size = hash_len + 2 + STUB_SIZE; size_t rec_max = GIGA / rec_size; char wbuf[LENMAX + 1]; { int i; for (i = 0; i < cur_len; i++) wbuf[i] = chset[idx[i]]; } logging(NULL); while (err == 0) { { int carry = 1; int i = 0; while (i < cur_len && cur_len <= LENMAX && carry) { if (++idx[i] >= chset_size) { idx[i] = 0; wbuf[i] = chset[0]; i++; if (i >= cur_len) cur_len++; } else { wbuf[i] = chset[idx[i]]; carry = 0; } }/* for */ if (cur_len > LENMAX) err = -1; else wbuf[cur_len] = '\0'; } { unsigned char *digest = istrip ? trip(wbuf) : md5(wbuf, cur_len); int ok = memcmp(digest, goal, digest_size) == 0; if (out_fp) { if (istrip) fputs(digest, out_fp); else print_digest(out_fp, digest); putc('\t', out_fp); fputs(wbuf, out_fp); if (cur_len < STUB_SIZE) fputs(stub + cur_len, out_fp); putc('\n', out_fp); if (++nrec >= rec_max) { nrec = 0; nrecGIGA++; out_close(); out_open(); } } if (++ntry == E9) { ntry = 0; ntryE9++; config_save(config_name); logging(NULL); } if (ok) { config_save(config_name); logging(wbuf); puts(wbuf); out_close(); exit(0); } } switch (ringing) { #ifdef SIGINFO case SIGINFO: #endif status_report(stderr); fputs(wbuf, stderr); fputs("\n", stderr); ringing = 0; break; #ifdef SIGHUP case SIGHUP: #endif #ifdef SIGINT case SIGINT: #endif #ifdef TERM case SIGTERM: #endif config_save(config_name); logging(NULL); status_report(stdout); out_close(); exit(0); break; }/* switch */ }/* while */ config_save(config_name); logging(NULL); return err; }/* tryn */ static int nain(const unsigned char goal[16]) { int err = out_open(); if (err == 0) { err = tryn(goal); if (err < 0) fprintf(stderr, "%s: md5 not found\n", myname); out_close(); } return err; }/* nain */ static int oain(char *argv[]) { char **mv = argv; while (*mv != NULL) { if (istrip) puts(trip(*mv)); else { print_digest(stdout, md5(*mv, strlen(*mv))); putchar('\n'); } mv++; }/* while */ return 0; }/* oain */ static char *usage_msg = "usage: %s [-V][-c char_set][-k key] md5\n"; int main(int argc, char *argv[]) { int ex = 1, show_usage = 0, ch; int opt_k = 0; myname = argv[0]; { int i; for (i = 0; i < LENMAX; i++) idx[i] = -1; } tr_init(); stub_init(); while ((ch = getopt(argc, argv, "b:c:f:kMnTV")) != EOF) { switch (ch) { default: case 'V': show_usage++; break; case 'b': vbufsize = strtol(optarg, NULL, 0); break; case 'f': if (strlen(optarg) >= sizeof(config_name)) { fprintf(stderr, "%s: %s: too long name\n", myname, optarg); show_usage++; } else strcpy(config_name, optarg); break; case 'c': chset_default = optarg; break; case 'k': opt_k++; break; case 'n': no_out++; break; case 'M': istrip = 0; break; case 'T': istrip = 1; break; }/* switch */ }/* while */ if (config_chset("", chset_default) != 0) show_usage++; if (argc - optind >= 2) show_usage++; if (show_usage) fprintf(stderr, usage_msg, myname); else { ex = 0; if (opt_k) oain(&argv[optind]); else { /* do crack */ if (istrip) { digest_size = 10; hash_len = 10; basename = "trip"; } else { digest_size = 16; hash_len = 32; basename = "md5"; } sprintf(log_name, log_name_fmt, basename); sprintf(config_name, config_name_fmt, basename); if (vbufsize != 0) { vbuf = malloc(vbufsize); if (vbuf == NULL) fprintf(stderr, "%s: %s: buffer allocation faild.\n", myname, strerror(errno)); else fprintf(stderr, "%s: buffer[%#x] allocated\n", myname, vbufsize); } if (argc - optind == 1) { /* ONE ARG, normal operation */ if (config_digest("", argv[optind++]) < 0) ex = 1; } else { /* NO ARG, continue mode */ if (config_load(config_name) < 0) ex = 1; } if (ex == 0) { if (cur_len < 1) cur_len = 1; sig_setup(); nain(target_digest); } } } return ex; }/* main */