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