1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #ifdef HAVE_STDBOOL_H
8 #include <stdbool.h>
9 #else
10 typedef enum { FALSE = 0, TRUE = 1 } bool;
11 #endif
12 #ifdef HAVE_GETOPT_H
13 #include <getopt.h>
14 #else
15 #include "getopt.h"
16 #endif
17 #ifdef HAVE_GETTIMEOFDAY
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #ifdef HAVE_SYS_TIME_H
22 #include <sys/time.h>
23 #endif
24 #endif
25
26 #include <string.h>
27
28 #ifdef USE_UNIX_REDIRECTION
29 #define DEVNULL ">/dev/null 2>&1"
30 #else
31 #define DEVNULL ">NUL 2>&1"
32 #endif
33
34 #include "crack.h"
35
36 int use_unzip;
37
38 static method *crack_method = methods;
39 static int method_number = -1;
40 static int min_length = -1;
41 static int max_length = -1;
42 static int residuent = 0;
43 static int modul = 1;
44
45 static FILE *dict_file;
46
47 char *
path_for_shell(char * dest,const char * str)48 path_for_shell (char *dest, const char *str)
49 {
50 /* backslash shell special charatcers */
51
52 char ch, *p = dest;
53 size_t len = strlen(str);
54 int i;
55
56 for (i = 0; i < len; i++)
57 {
58 ch = str[i];
59
60 switch (ch)
61 {
62 /* ASCII table order */
63 case 0x20: /* space */
64 case '!':
65 case '"':
66 case '#':
67 case '$':
68 case '&':
69 case 0x27: /* single quote */
70 case '(':
71 case ')':
72 case '*':
73 case '+':
74 case 0x2C: /* comma */
75 case ':':
76 case ';':
77 case '<':
78 case '>':
79 case '?':
80 case '[':
81 case '\\':
82 case ']':
83 case '^':
84 case '`':
85 case '{':
86 case '|':
87 case '}':
88 case '~':
89 /* backslash special characters */
90 *p++ = '\\';
91 *p++ = ch;
92 break;
93 default:
94 *p++ = ch;
95 }
96 }
97
98 /* terminate string */
99 *p = '\0';
100
101 return dest;
102 }
103
104 char *
escape_pw(char * dest,const char * str)105 escape_pw (char *dest, const char *str)
106 {
107 /* backslash shell special charatcers */
108
109 char ch, *p = dest;
110 size_t len = strlen(str);
111 int i;
112
113 for (i = 0; i < len; i++)
114 {
115 ch = str[i];
116
117 switch (ch)
118 {
119 /* ASCII table order */
120 case '"':
121 case '$':
122 case 0x27: /* single quote */
123 case '\\':
124 case '`':
125 /* backslash special characters */
126 *p++ = '\\';
127 *p++ = ch;
128 break;
129 default:
130 *p++ = ch;
131 }
132 }
133
134 /* terminate string */
135 *p = '\0';
136
137 return dest;
138 }
139
140 int REGPARAM
check_unzip(const char * pw)141 check_unzip (const char *pw)
142 {
143 char buff[1024];
144 char path[1024];
145 char escpw[256];
146 int status;
147
148 escape_pw (escpw, pw);
149 path_for_shell (path, file_path[0]);
150
151 sprintf (buff, "unzip -qqtP \"%s\" %s " DEVNULL, escpw, path);
152
153 status = system (buff);
154
155 #undef REDIR
156
157 if (status == EXIT_SUCCESS)
158 {
159 printf("\n\nPASSWORD FOUND!!!!: pw == %s\n", pw);
160 exit (EXIT_SUCCESS);
161 }
162
163 return !status;
164 }
165
166 /* misc. callbacks. */
167
168 static int
false_callback(const char * pw,const char * info)169 false_callback (const char *pw, const char *info)
170 {
171 (void) pw;
172 (void) info; /* suppress warning */
173 return 0;
174 }
175
176 static int
true_callback(const char * pw,const char * info)177 true_callback (const char *pw, const char *info)
178 {
179 (void) pw;
180 (void) info; /* suppress warning */
181 return 1;
182 }
183
184 static int
print_callback(const char * pw,const char * info)185 print_callback (const char *pw, const char *info)
186 {
187 if (!use_unzip || check_unzip (pw))
188 {
189 printf ("possible pw found: %s (%s)\n", pw, info ? info : "");
190 /*exit(0); */
191 }
192
193 return 0;
194 }
195
196 static int
brute_force_gen(void)197 brute_force_gen (void)
198 {
199 u8 *p = pw_end;
200
201 do
202 {
203 u8 o = *--p;
204 *p = bf_next[o];
205 if (o != bf_last)
206 return pw_end - p;
207 }
208 while (p > pw);
209
210 if (pw_end - pw < max_length)
211 {
212 p = ++pw_end;
213 *p = 0;
214
215 while (p > pw)
216 *--p = bf_next[255];
217
218 return -1;
219 }
220 else
221 return 0;
222 }
223
224 static int
dictionary_gen(void)225 dictionary_gen (void)
226 {
227 /* should optimize this, comparing prefixes would be a net win.
228 * however, not using fgets but something better might be an
229 * even higher win :(
230 */
231 if (fgets (pw, MAX_PW+1, dict_file))
232 {
233 pw[strlen (pw) - 1] = 0;
234 return -1;
235 }
236 else
237 {
238 if (!feof (dict_file))
239 perror ("dictionary_read_next_password");
240
241 return 0;
242 }
243 }
244
245 static int
validate_gen(void)246 validate_gen (void)
247 {
248 return 0;
249 }
250
251 static void
validate(void)252 validate (void)
253 {
254 u8 header[HEADER_SIZE + 1] =
255 {0xf4, 0x28, 0xd6, 0xee, 0xd7, 0xd2,
256 0x3c, 0x1a, 0x20, 0xab, 0xdf, 0x73,
257 0xd6, 0xba, 0}; /* PW: "Martha" */
258 strcpy ((char *) files, (char *) header); /* yeah, dirty... */
259 file_count = 1;
260
261 if (crack_method->desc[0] == 'z')
262 {
263 crack_method->init_crack_pw ();
264
265 strcpy (pw, "Martha");
266 if (crack_method->crack_pw (validate_gen, true_callback))
267 printf ("validate ok (%s == Martha)\n", pw);
268 else
269 printf ("validation error (%s != Martha)\n", pw);
270 }
271 else
272 printf ("validate only works for zip methods, use --method to select one.\n");
273 }
274
275 static void
parse_charset(char * cs)276 parse_charset (char *cs)
277 {
278 u8 chars[800];
279 u8 map[256];
280 u8 *p = chars;
281
282 while (*cs)
283 switch (*cs++)
284 {
285 case 'a':
286 strcpy ((char *) p, "abcdefghijklmnopqrstuvwxyz");
287 p += 26;
288 break;
289
290 case 'A':
291 strcpy ((char *) p, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
292 p += 26;
293 break;
294
295 case '1':
296 strcpy ((char *) p, "0123456789");
297 p += 10;
298 break;
299
300 case '!':
301 strcpy ((char *) p, "!:$%&/()=?{[]}+-*~#");
302 p += 18;
303 break;
304
305 case ':':
306 while (*cs)
307 *p++ = *cs++;
308 break;
309
310 default:
311 fprintf (stderr, "unknown charset specifier, only 'aA1!:' recognized\n");
312 exit (1);
313 }
314
315 *p = 0;
316
317 p = chars;
318 bf_last = *p++;
319 memset (bf_next, bf_last, sizeof bf_next);
320 memset (map, 0, 256);
321
322 for (; *p; p++)
323 if (!map[*p])
324 {
325 map[*p] = 1;
326 bf_next[bf_last] = *p;
327 bf_last = *p;
328 }
329
330 bf_next[bf_last] = chars[0];
331
332 /* { int i; for (i = 0; i < 255; i++) printf ("bf_next [%3d] = %3d\n", i, bf_next[i]);}; */
333 }
334
335 static int benchmark_count;
336
337 static int
benchmark_gen(void)338 benchmark_gen (void)
339 {
340 if (!--benchmark_count)
341 return 0;
342
343 return brute_force_gen ();
344 }
345
346 static void
benchmark(void)347 benchmark (void)
348 {
349 #ifdef HAVE_GETTIMEOFDAY
350 int i;
351 long j, k;
352 struct timeval tv1, tv2;
353
354 do
355 {
356 for (i = 0; i < HEADER_SIZE * 3; i++)
357 files[i] = i ^ (i * 3);
358
359 file_count = 3;
360 strcpy (pw, "abcdefghij");
361 parse_charset ("a");
362 benchmark_count = BENCHMARK_LOOPS;
363
364 verbosity = 0;
365
366 printf ("%c%s: ",
367 (crack_method - methods == default_method) ? '*' : ' ',
368 crack_method->desc);
369
370 if (strncmp ("zip", crack_method->desc, 3))
371 printf ("(skipped)");
372 else
373 {
374 fflush (stdout);
375
376 crack_method->init_crack_pw ();
377 gettimeofday (&tv1, 0);
378 crack_method->crack_pw (benchmark_gen, false_callback);
379 gettimeofday (&tv2, 0);
380 tv2.tv_sec -= tv1.tv_sec;
381 tv2.tv_usec -= tv1.tv_usec;
382
383 j = tv2.tv_sec * 1000000 + tv2.tv_usec;
384 k = BENCHMARK_LOOPS;
385
386 printf ("cracks/s = ");
387
388 for (i = 7; i--;)
389 printf ("%ld", k / j), k = (k - k / j * j) * 10;
390 }
391
392 printf ("\n");
393 crack_method++;
394 }
395 while (method_number < 0 && crack_method->desc);
396 #else
397 fprintf (stderr, "This executable was compiled without support for benchmarking\n");
398 exit (1);
399 #endif
400 }
401
402 static void
usage(int ec)403 usage (int ec)
404 {
405 printf ("\n"
406 PACKAGE " version " VERSION ", a fast/free zip password cracker\n"
407 "written by Marc Lehmann <pcg@goof.com> You can find more info on\n"
408 "http://www.goof.com/pcg/marc/\n"
409 "\n"
410 "USAGE: fcrackzip\n"
411 " [-b|--brute-force] use brute force algorithm\n"
412 " [-D|--dictionary] use a dictionary\n"
413 " [-B|--benchmark] execute a small benchmark\n"
414 " [-c|--charset characterset] use characters from charset\n"
415 " [-h|--help] show this message\n"
416 " [--version] show the version of this program\n"
417 " [-V|--validate] sanity-check the algortihm\n"
418 " [-v|--verbose] be more verbose\n"
419 " [-p|--init-password string] use string as initial password/file\n"
420 " [-l|--length min-max] check password with length min to max\n"
421 " [-u|--use-unzip] use unzip to weed out wrong passwords\n"
422 " [-m|--method num] use method number \"num\" (see below)\n"
423 " [-2|--modulo r/m] only calculcate 1/m of the password\n"
424 " file... the zipfiles to crack\n"
425 "\n"
426 );
427
428 printf ("methods compiled in (* = default):\n\n");
429 for (crack_method = methods; crack_method->desc; crack_method++)
430 printf ("%c%d: %s\n",
431 (crack_method - methods == default_method) ? '*' : ' ',
432 crack_method - methods,
433 crack_method->desc);
434
435 printf ("\n");
436 exit (ec);
437 }
438
439 static struct option options[] =
440 {
441 {"version", no_argument, 0, 'R'},
442 {"brute-force", no_argument, 0, 'b'},
443 {"dictionary", no_argument, 0, 'D'},
444 {"benchmark", no_argument, 0, 'B'},
445 {"charset", required_argument, 0, 'c'},
446 {"help", no_argument, 0, 'h'},
447 {"validate", no_argument, 0, 'V'},
448 {"verbose", no_argument, 0, 'v'},
449 {"init-password", required_argument, 0, 'p'},
450 {"length", required_argument, 0, 'l'},
451 {"use-unzip", no_argument, 0, 'u'},
452 {"method", required_argument, 0, 'm'},
453 {"modulo", required_argument, 0, 2},
454 {0, 0, 0, 0},
455 };
456
457 int
main(int argc,char * argv[])458 main (int argc, char *argv[])
459 {
460 int c;
461 int option_index = 0;
462 char *charset = "aA1!";
463 enum { m_benchmark, m_brute_force, m_dictionary } mode = m_brute_force;
464
465 while ((c = getopt_long (argc, argv, "DbBc:hVvp:l:um:2:", options, &option_index)) != -1)
466 switch (c)
467 {
468 case 'b':
469 mode = m_brute_force;
470 break;
471
472 case 'D':
473 mode = m_dictionary;
474 break;
475
476 case 'p':
477 strcpy (pw, optarg);
478 break;
479
480 case 'l':
481 pw[0] = 0;
482 switch (sscanf (optarg, "%d-%d", &min_length, &max_length))
483 {
484 default:
485 fprintf (stderr, "'%s' is an incorrect length specification\n", optarg);
486 exit (1);
487 case 1:
488 max_length = min_length;
489 case 2:
490 ;
491 }
492 break;
493
494 case 2:
495 if (sscanf (optarg, "%d/%d", &residuent, &modul) != 2)
496 fprintf (stderr, "malformed --modulo option, expected 'residuent/modul'\n"), exit (1);
497
498 if (residuent < 0 || modul <= 0)
499 fprintf (stderr, "residuent and modul must be positive\n"), exit (1);
500
501 if (residuent >= modul)
502 fprintf (stderr, "residuent must be less than modul\n"), exit (1);
503
504 break;
505
506 case 'B':
507 mode = m_benchmark;
508 benchmark ();
509 exit (0);
510
511 case 'v':
512 verbosity++;
513 break;
514
515 case 'm':
516 {
517 for (method_number = 0; methods[method_number].desc; method_number++)
518 if (!strncmp (methods[method_number].desc, optarg, strlen (optarg)))
519 break;
520
521 if (!methods[method_number].desc)
522 method_number = atoi (optarg);
523
524 crack_method = methods + method_number;
525 }
526 break;
527
528 case 'V':
529 validate ();
530 exit (0);
531
532 case 'c':
533 charset = optarg;
534 break;
535
536 case 'u':
537 use_unzip = 1;
538 break;
539
540 case 'h':
541 usage (0);
542 case 'R':
543 printf (PACKAGE " version " VERSION "\n");
544 exit (0);
545
546 case ':':
547 fprintf (stderr, "required argument missing\n");
548 exit (1);
549
550 case '?':
551 fprintf (stderr, "unknown option\n");
552 exit (1);
553
554 default:
555 usage (1);
556 }
557
558 if (method_number < 0)
559 {
560 method_number = default_method;
561 crack_method = methods + default_method;
562 }
563
564 if (optind >= argc)
565 {
566 fprintf (stderr, "you have to specify one or more zip files (try --help)\n");
567 exit (1);
568 }
569
570 for (; optind < argc; optind++)
571 if (file_count < MAX_FILES)
572 crack_method->load_file (argv[optind]);
573 else if (verbosity)
574 printf ("%d file maximum reached, ignoring '%s'\n", MAX_FILES, argv[optind]);
575
576 if (file_count == 0)
577 {
578 fprintf (stderr, "no usable files found\n");
579 exit (1);
580 }
581
582 crack_method->init_crack_pw ();
583
584 switch (mode)
585 {
586 case m_brute_force:
587 parse_charset (charset);
588
589 if (!pw[0])
590 {
591 if (min_length < 0)
592 {
593 fprintf (stderr, "you have to specify either --init-password or --length with --brute-force\n");
594 exit (1);
595 }
596 else
597 {
598 u8 *p = pw;
599 while (p < pw + min_length)
600 *p++ = bf_next[255];
601
602 *p++ = 0;
603 }
604 }
605
606 if (residuent)
607 {
608 int xmodul = modul;
609 modul = residuent;
610 pw_end = pw + strlen (pw);
611 brute_force_gen ();
612 printf ("%s\n", pw);
613 modul = xmodul;
614 printf ("WARNING: residuent mode NOT supported YET!\n");
615 }
616
617 crack_method->crack_pw (brute_force_gen, print_callback);
618 break;
619
620 case m_dictionary:
621 if (!pw[0])
622 {
623 fprintf (stderr, "you have to specify a file to read passwords from using the -p switch\n");
624 exit (1);
625 }
626
627 if (!(dict_file = fopen (pw, "r")))
628 {
629 perror (pw);
630 exit (1);
631 }
632 else
633 {
634 *(pw_end = pw) = 0;
635 dictionary_gen (); /* fetch first password */
636 crack_method->crack_pw (dictionary_gen, print_callback);
637
638 fclose (dict_file);
639 }
640
641 break;
642
643 default:
644 fprintf (stderr, "specified mode not supported in this version\n");
645 exit (1);
646 }
647
648 return 0;
649 }
650