/* * reversecrc.c * reversecrc program entry point. */ #include #include #include #include #include #include #include #include #include #include #include /* program information. */ char* program_name = "reversecrc"; char* bug_email = "bas.westerbaan@gmail.com"; char* version_number = "0.1.1" #ifdef TARGET_WINDOWS "-win" #ifdef TARGET_FL "-fl"; #else ; #endif #else #ifdef TARGET_FL "-fl"; #else ; #endif #endif char* release_date = __DATE__ " " __TIME__; char* copyright = "(c) 2005 - Bas Westerbaan, Intrepid Software"; /* * cmp_dword * used for qsort to compare dwords. */ int cmp_dword (const void *e1, const void *e2) { dword x = *(dword*)e1, y = *(dword*)e2; if (x < y) { return -1; } else if (x == y) { return 0; } else { return 1; } } /* * main2 function * Here the actual work is done. In the main function only the * command line arguments are processed. * * return codes * 0 success * 1 too much duplicates in table */ int main2 (unsigned int hash, int type, byte* charset, size_t charset_size, dword* table, char table_is_custom, dword end_depth, dword start_depth, int verboseness) { char verbose = verboseness > 0; char vverbose = verboseness > 1; char vvverbose = verboseness > 2; byte max_duplicates = 1; dword buffer_size; dword i; int exit_code = 0; byte* buffer; byte* base; byte* base_string; dword depth = start_depth; dword* hashes; dword* hashes_raw; dword hashes_count; dword* sorted_table; byte* entry_offsets; byte* entry_counts; byte* entry_old_offsets; byte* super_buffer; clock_t start_time = clock(); /* Show some information */ if (verbose) { printf("# %s %s, %s\n" "# %s\n" "# <%s>\n", program_name, version_number, release_date, copyright, bug_email); if (vverbose) { printf("# hash\t%x\ttype\t%d\n" "# start\t%d\tend\t%d\n", hash, type, start_depth, end_depth); if (vvverbose) { char* charset_string = stresc(charset, charset_size); printf("# charset:\n" "# %s\n", charset_string); free(charset_string); } } } /* Determine the maximum amounts of duplicates of a single entry */ if (table_is_custom) { /* TODO: intergrate this with later table processing */ byte entry_occurance[256]; memset(entry_occurance, 0, 256); for (i = 0; i < 256; i++) { entry_occurance[table[i] >> 24]++; } for (i = 0; i < 256; i++) { if (entry_occurance[i] > max_duplicates) { max_duplicates = entry_occurance[i]; } } } else { if (type == FLCRC) { max_duplicates = flcrc_table_entry_occurance; } else if (type == CRC32) { max_duplicates = crc32_table_entry_occurance; } } /* FIXME: the size calc. is very pessimistic, for usually max_duplicates * 2 will do. */ buffer_size = max_duplicates * max_duplicates * max_duplicates * max_duplicates * 2; /* avoid double conversion */ if (buffer_size > 65536) { printf("Error: Buffer would require %d elements, which would get too inefficient", buffer_size); return 1; } if (vverbose) { printf("# max. duplicates in table\t%d\n", max_duplicates); printf("# work buffer size\t%d\n", buffer_size); } if (type == FLCRC) { hashes_raw = rev_flcrc_swap(hash); hashes_count = 4; } else if (type == CRC32) { hashes_raw = malloc(sizeof(dword)); hashes_raw[0] = hash ^ 0xFFFFFFFF; hashes_count = 1; } else { hashes_raw = malloc(sizeof(dword)); hashes_count = 1; hashes_raw[0] = hash; } /* * TODO: * preserve place for base and base_string. */ super_buffer = malloc(buffer_size * 8 + 1024 + 256 + 256 + 256 + hashes_count * 4); sorted_table = (void*)&super_buffer[0]; entry_offsets = &super_buffer[1024]; entry_old_offsets = &super_buffer[1024 + 256]; entry_counts = &super_buffer[1024 + 256 + 256]; hashes = (void*)&super_buffer[1024 + 256 + 256 + 256]; buffer = &super_buffer[1024 + 256 + 256 + 256 + hashes_count * 4]; memcpy(sorted_table, table, sizeof(dword) * 256); qsort(sorted_table, 256, sizeof(dword), cmp_dword); memset(entry_offsets, 0, 256); memset(entry_counts, 0, 256); memset(entry_old_offsets, 0, 256); memcpy(hashes, hashes_raw, hashes_count * 4); free(hashes_raw); /* * TODO: * should this all be cached in a `crc` file with charset and table instead of calcing it * here. files could even be slower. */ for (i = 0; i < 256; i++) { dword j; char found = 0; for (j = 0; j < 256; j++) { if (sorted_table[i] == table[j]) { entry_old_offsets[i] = j; } if (sorted_table[j] >> 24 == i) { if (found) { entry_counts[i]++; } else { entry_offsets[i] = j; entry_counts[i] = 1; found = 1; } } } } if (vverbose) { printf("# Initialization time: %lfs\n", ((double)clock() - (double)start_time) / CLOCKS_PER_SEC); } /* * from here the actual works begins * TODO: when the algorithm is stable, convert it to assembly. */ while (depth <= end_depth) { clock_t depth_start_time; base = malloc(depth); base_string = malloc(depth); memset(base, 0, depth); depth_start_time = clock(); if (verbose) { printf("\n# Depth %d\n", depth); } while (1) { dword pre_hash; dword j; for (i = 0; i < depth; i++) { base_string[i] = charset[base[i]]; } if (depth > 0) { if (type == FLCRC) { pre_hash = flcrc_compute_raw(base_string, depth, table); } else if (type == CRC32) { pre_hash = crc32_compute_raw(base_string, depth, table); } else { pre_hash = 0; } } else { pre_hash = 0; } for (j = 0; j < hashes_count; j++) { int p; dword buffer_old = 1; /* amount of worked on hashes in buffer */ dword buffer_new = 0; /* amount of new hashes after the old */ buffer[0] = (byte)pre_hash; buffer[1] = (byte)(pre_hash >> 8); buffer[2] = (byte)(pre_hash >> 16); buffer[3] = (byte)(pre_hash >> 24); buffer[4] = (byte)hashes[j]; buffer[5] = (byte)(hashes[j] >> 8); buffer[6] = (byte)(hashes[j] >> 16); buffer[7] = (byte)(hashes[j] >> 24); /* * TODO: * the buffer is valuefull p + 5 in size. * when only using there p + 5 instead of 8 the maximum size could * go down and performance would go up. */ for (p = 3; p >= 0; p--) { dword no; for (no = 0; no < buffer_old; no++) { byte oc = entry_counts[buffer[no * 8 + p + 4]]; byte oo = entry_offsets[buffer[no * 8 + p + 4]]; byte oi; /* * TODO: * dead-end tracks are eliminates here but still use * memory. prestore counts when the entry is made instead, * and do the elimination there? */ for (oi = 0; oi < oc; oi++) { memcpy(&buffer[(buffer_old + buffer_new) * 8], &buffer[no * 8], 8); *(dword*)(&buffer[(buffer_old + buffer_new) * 8 + p + 1]) ^= sorted_table[oo + oi]; buffer[(buffer_old + buffer_new) * 8 + p] ^= entry_old_offsets[oo + oi]; buffer_new++; } } /* TODO: is memmove overhead it worth, for one buffer is possible? */ if (p != 0) { /* we won't move when it isn't required. */ memmove(buffer, &buffer[8 * buffer_old], buffer_new * 8); buffer_old = buffer_new; buffer_new = 0; } } for (i = 0; i < buffer_new; i++) { dword r; char valid = 1; /* TODO: is there a good stdlib function for? */ for (r = 0; r < 4; r++) { dword s; char found = 0; for (s = 0; s < charset_size; s++) { if (buffer[(buffer_old + i) * 8 + r] == charset[s]) { found = 1; break; } } if (!found) { valid = 0; break; } } if (valid) { /* * TODO: add a check whether to escape or not, for escaping yields a lot of overhead, * when there is no need to escape. */ char* s1 = stresc(base_string, depth); char* s2 = stresc(&buffer[(buffer_old + i) * 8], 4); printf("%s%s\n", s1, s2); free(s1); free(s2); } } } /* TODO: can the base incrementation be more efficient? */ i = 0; while (i < depth && base[i] == charset_size - 1) { base[i] = 0; i++; } if (i == depth) { break; } base[i] += 1; } depth++; free(base); free(base_string); if (vverbose) { printf("# Depth time: %lfs\n", ((double)clock() - (double)depth_start_time) / CLOCKS_PER_SEC); } } if (verbose) { printf("# Total time: %lfs\n", ((double)clock() - (double)start_time) / CLOCKS_PER_SEC); } /* put exit label here */ free(super_buffer); return exit_code; } /* * main function * program entry point, contains the code to parse the command line. * the code in here is pretty straight forward. * * return codes * 0 success * -1 insufficient memory * -2 command line error * -3 crc type not found * -4 no charset file specified * -5 invalid charset specified * -6 couldn't work with charset file * -7 invalid charset size * -8 no table available for type * -9 couldn't load table from file * NB. check the other functions for other possible return codes. */ int main (int argc, char* argv[]) { /* set up argument table. */ struct arg_str *hash = arg_str1 (NULL, NULL, "", "The hash to reverse"); struct arg_str *type = arg_str0 ("t", "type", "{crc32,flcrc}", "The type of the hash"); struct arg_str *charset = arg_str0 ("c", "charset", "{all,text,custom}", "The character set to use"); struct arg_file *charset_file = arg_file0("C", "charset-file", "", "The file containing the characters"); struct arg_rem *rem1 = arg_rem (NULL, " implies --charset=custom"); struct arg_file *table = arg_file0("T", "table", "", "The file containing the table"); struct arg_rem *rem2 = arg_rem (NULL, " the default table depends on --type"); struct arg_int *end_depth = arg_int0 ("ed", "end-depth", "", "The maximum length of the bruteforced part"); struct arg_int *start_depth = arg_int0 ("s", "start-depth", "", "The starting depth of the bruteforced path"); struct arg_lit *force_tcheck = arg_lit0 (NULL, "force-table-check", "Forces to check internal tables too"); struct arg_lit *help = arg_lit0 (NULL, "help", "Displays this help message"); struct arg_lit *version = arg_lit0 (NULL, "version", "Displays the version"); struct arg_lit *verboseness = arg_litn ("v", "verbose", 0, 3, "Be verbose; more mean more verbose"); struct arg_end *end = arg_end (20); void* argt[] = {hash, type, charset, charset_file, rem1, table, rem2, end_depth, start_depth, force_tcheck, help, version, verboseness, end}; int exit_code; int error_count; int integer_type; dword i; byte* final_charset = malloc(256); /* inefficient to allocate it now, * but it prevents mem leaks. */ dword* final_table = malloc(256 * sizeof(dword)); size_t final_charset_len; char* type_default = strclone("crc32"); char* charset_default = strclone("text"); char* charset_custom = strclone("custom"); char using_custom_table = 0; if (arg_nullcheck(argt) != 0) { printf("Error: insufficient memory, or data corruption\n"); exit_code = -1; goto exit; } type->sval[0] = type_default; charset->sval[0] = charset_default; end_depth->ival[0] = 10; start_depth->ival[0] = 0; /* parse it */ error_count = arg_parse(argc, argv, argt); /* do help and version even if there would be errors (special case). */ if (help->count > 0) { printf("Usage: %s ", program_name); arg_print_syntax(stdout, argt, "\n"); printf("Determines the possible plain texts of a hash by reversing the last 4 " "bytes and bruteforcing the first.\n\n"); arg_print_glossary(stdout, argt, " %-31s %s\n"); printf("\n\nReport bugs to <%s>.\n", bug_email); #if TARGET_WINDOWS printf("Windows version maintained by John Turner \n"); #endif exit_code = 0; goto exit; } if (version->count > 0) { printf("%s crc reversal tool\n" " version %s, %s\n" " %s\n\n", program_name, version_number, release_date, copyright); exit_code = 0; goto exit; } /* check for errors. */ if (error_count > 0) { arg_print_errors(stdout, end, program_name); printf("Try '%s --help' for more information.\n",program_name); exit_code = -2; goto exit; } /* process our arguments */ /* first off: crc-type */ strlower((char*)type->sval[0]); if (strcmp("crc32", type->sval[0]) == 0) { integer_type = CRC32; } else if (strcmp("flcrc", type->sval[0]) == 0) { integer_type = FLCRC; } else { printf("Error: the crc type '%s' isn't regognized\n", type->sval[0]); exit_code = -3; goto exit; } /* charset */ if (charset_file->count > 0 && charset->count <= 0 ) { charset->sval[0] = charset_custom; } strlower((char*)charset->sval[0]); if (strcmp("all", charset->sval[0]) == 0) { for (i = 0; i < 256; i++) { final_charset[i] = i; } final_charset_len = 256; } else if (strcmp("text", charset->sval[0]) == 0) { char* text_charset = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; memcpy(final_charset, text_charset, strlen(text_charset)); final_charset_len = strlen(text_charset); } else if (strcmp("custom", charset->sval[0]) == 0) { if (charset_file->count > 0) { FILE* f = fopen(charset_file->filename[0], "r"); size_t charset_size; if (!f) { printf("Error: couldn't open charset file\n"); exit_code = -6; goto exit; } fseek(f, 0, SEEK_END); charset_size = ftell(f); final_charset_len = charset_size; if (charset_size == 0 || charset_size > 256) { printf("Error: invalid charset size\n"); fclose(f); exit_code = -7; goto exit; } fseek(f, 0, SEEK_SET); if ((dword)fread(final_charset, 1, charset_size, f) != charset_size) { printf("Error: couldn't read charset from file\n"); fclose(f); exit_code = -6; goto exit; } fclose(f); } else { printf("Error: custom charset specified, although there isn't a file specified\n"); exit_code = -4; goto exit; } } else { printf("Error: '%s' isn't a valid charset\n", charset->sval[0]); exit_code = -5; goto exit; } /* finally the table */ if (table->count > 0) { FILE* f = fopen(table->filename[0], "r"); using_custom_table = 1; if (!f) { printf("Error: Couldn't open '%s'\n", table->filename[0]); exit_code = -9; goto exit; } if(fread(final_table, 1, 1024, f) != 1024) { printf("Error: Couldn't read table from '%s'\n", table->filename[0]); fclose(f); exit_code = -10; goto exit; } fclose(f); } else { if (integer_type == CRC32) { memcpy(final_table, crc32_table, sizeof(dword) * 256); } else if (integer_type == FLCRC) { memcpy(final_table, flcrc_table, sizeof(dword) * 256); } else { printf("Error: there isn't a default table available for the specified type,\n" " please provide your own\n"); exit_code = -8; goto exit; } } /* misc. */ if (force_tcheck->count > 0) { using_custom_table = 1; } /* and run it */ exit_code = main2(string_to_uint32((char*)hash->sval[0]), integer_type, final_charset, final_charset_len, final_table, using_custom_table, end_depth->ival[0], start_depth->ival[0], verboseness->count); exit: free(final_charset); free(final_table); free(charset_default); free(charset_custom); free(type_default); arg_freetable(argt, sizeof(argt) / sizeof(argt[0])); return exit_code; }