]> git.sesse.net Git - freerainbowtables/blob - Client Applications/rcracki_mt/RainbowCrack.cpp
185dd1afadd34a857dad4c91218f3653b6f153f4
[freerainbowtables] / Client Applications / rcracki_mt / RainbowCrack.cpp
1 /*
2    RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique.
3
4    Copyright (C) Zhu Shuanglei <shuanglei@hotmail.com>
5
6    Modified by Martin Westergaard Jørgensen <martinwj2005@gmail.com> to support indexed and hybrid tables
7
8    Modified by neinbrucke to support multi threading and a bunch of other stuff :)
9
10       2009-01-04 - <james.dickson@comhem.se> - Slightly modified (or "fulhack" as we say in sweden)  
11                                 to support cain .lst files.
12 */
13
14 #ifdef _WIN32
15         #pragma warning(disable : 4786 4267 4018)
16 #endif
17
18 #include "CrackEngine.h"
19 #include "lm2ntlm.h"
20 #include <algorithm>
21
22
23
24 #ifdef _WIN32
25         #include <io.h>
26 #else
27         #include <sys/types.h>
28         #include <sys/stat.h>
29         #include <unistd.h>
30         #include <dirent.h>
31 #endif
32
33 #include <openssl/md4.h>
34 #ifdef _WIN32
35         #pragma comment(lib, "libeay32.lib")
36 #endif
37
38 //////////////////////////////////////////////////////////////////////
39
40 #ifdef _WIN32
41 void GetTableList(string sWildCharPathName, vector<string>& vPathName)
42 {
43         //vPathName.clear();
44
45         string sPath;
46         int n = sWildCharPathName.find_last_of('\\');
47
48         if (n == (sWildCharPathName.size() - 1))
49         {
50                 sWildCharPathName = sWildCharPathName.substr(0, n);
51                 n = sWildCharPathName.find_last_of('\\');
52         }
53
54         if (n != -1)
55                 sPath = sWildCharPathName.substr(0, n + 1);
56
57         _finddata_t fd;
58
59         long handle = _findfirst(sWildCharPathName.c_str(), &fd);
60         if (handle != -1)
61         {
62                 do
63                 {
64                         string sName = fd.name;
65                         if (sName.size()>3) {
66                                 if (sName.substr(sName.size()-3, 3) == ".rt" && !(fd.attrib & _A_SUBDIR))
67                                 {
68                                         string sPathName = sPath + sName;
69                                         vPathName.push_back(sPathName);
70                                 }
71                         }
72                         if (sName.size()>4) {
73                                 if (sName.substr(sName.size()-4, 4) == ".rti" && !(fd.attrib & _A_SUBDIR))
74                                 {
75                                         string sPathName = sPath + sName;
76                                         vPathName.push_back(sPathName);
77                                 }
78                         }
79                         if (sName.size()>5) {
80                                 if (sName.substr(sName.size()-5, 5) == ".rti2" && !(fd.attrib & _A_SUBDIR))
81                                 {
82                                         string sPathName = sPath + sName;
83                                         vPathName.push_back(sPathName);
84                                 }
85                         }
86
87                         if (sName != "." && sName != ".." && (fd.attrib & _A_SUBDIR))
88                         {
89                                 string sPath_sub = sPath + sName + '\\';
90                                 string sWildCharPathName_sub = sPath_sub + '*';
91                                 GetTableList(sWildCharPathName_sub, vPathName);
92                         }
93
94                 } while (_findnext(handle, &fd) == 0);
95
96                 _findclose(handle);
97         }
98         //printf("Found %d rainbowtables (files) in %d sub directories...\n", vPathName.size(), subDir_count);
99 }
100 #else
101 //void GetTableList(int argc, char* argv[], vector<string>& vPathName)
102 void GetTableList(string sWildCharPathName, vector<string>& vPathName)
103 {
104         //vPathName.clear();
105
106         struct stat buf;
107         if (lstat(sWildCharPathName.c_str(), &buf) == 0)
108         {
109                 if (S_ISDIR(buf.st_mode))
110                 {
111                         DIR *dir = opendir(sWildCharPathName.c_str());
112                         if(dir)
113                         {
114                                 struct dirent *pDir=NULL;
115                                 while((pDir = readdir(dir)) != NULL)
116                                 {
117                                         string filename = "";
118                                         filename += (*pDir).d_name;
119                                         if (filename != "." && filename != "..")
120                                         {
121                                                 string new_filename = sWildCharPathName + '/' + filename;
122                                                 GetTableList(new_filename, vPathName);
123                                         }
124                                 }
125                                 closedir(dir);
126                         }
127                 }
128                 else if (S_ISREG(buf.st_mode))
129                 {
130                         if (sWildCharPathName.size()>3)
131                         {
132                                 if (sWildCharPathName.substr(sWildCharPathName.size()-3, 3) == ".rt")
133                                 {
134                                         vPathName.push_back(sWildCharPathName);
135                                 }
136                         }
137                         if (sWildCharPathName.size()>4)
138                         {
139                                 if (sWildCharPathName.substr(sWildCharPathName.size()-4, 4) == ".rti")
140                                 {
141                                         //string sPathName_sub = sPath_sub + sName_sub;
142                                         vPathName.push_back(sWildCharPathName);
143                                         //printf("sPathName_sub: %s\n", sPathName_sub.c_str());
144                                 }
145                         }
146                 }
147         }
148 }
149 #endif
150
151 bool NormalizeHash(string& sHash)
152 {
153         string sNormalizedHash = sHash;
154
155         if (   sNormalizedHash.size() % 2 != 0
156                 || sNormalizedHash.size() < MIN_HASH_LEN * 2
157                 || sNormalizedHash.size() > MAX_HASH_LEN * 2)
158                 return false;
159
160         // Make lower
161         int i;
162         for (i = 0; i < sNormalizedHash.size(); i++)
163         {
164                 if (sNormalizedHash[i] >= 'A' && sNormalizedHash[i] <= 'F')
165                         sNormalizedHash[i] = sNormalizedHash[i] - 'A' + 'a';
166         }
167
168         // Character check
169         for (i = 0; i < sNormalizedHash.size(); i++)
170         {
171                 if (   (sNormalizedHash[i] < 'a' || sNormalizedHash[i] > 'f')
172                         && (sNormalizedHash[i] < '0' || sNormalizedHash[i] > '9'))
173                         return false;
174         }
175
176         sHash = sNormalizedHash;
177         return true;
178 }
179
180 void LoadLMHashFromPwdumpFile(string sPathName, vector<string>& vUserName, vector<string>& vLMHash, vector<string>& vNTLMHash)
181 {
182         vector<string> vLine;
183         if (ReadLinesFromFile(sPathName, vLine))
184         {
185                 int i;
186                 for (i = 0; i < vLine.size(); i++)
187                 {
188                         vector<string> vPart;
189                         if (SeperateString(vLine[i], "::::", vPart))
190                         {
191                                 string sUserName = vPart[0];
192                                 string sLMHash   = vPart[2];
193                                 string sNTLMHash = vPart[3];
194
195                                 if (sLMHash.size() == 32 && sNTLMHash.size() == 32)
196                                 {
197                                         if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash))
198                                         {
199                                                 vUserName.push_back(sUserName);
200                                                 vLMHash.push_back(sLMHash);
201                                                 vNTLMHash.push_back(sNTLMHash);
202                                         }
203                                         else
204                                                 printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str());
205                                 }
206                         }
207                 }
208         }
209         else
210                 printf("can't open %s\n", sPathName.c_str());
211 }
212
213 // 2009-01-04 - james - Added this so we can load hashes from cain .LST files.
214 void LoadLMHashFromCainLSTFile(string sPathName, vector<string>& vUserName, vector<string>& vLMHash, vector<string>& vNTLMHash)
215 {
216         vector<string> vLine;
217         if (ReadLinesFromFile(sPathName, vLine))
218         {
219                 int i;
220                 for (i = 0; i < vLine.size(); i++)
221                 {
222                         vector<string> vPart;
223                         if (SeperateString(vLine[i], "\t\t\t\t\t\t", vPart))
224                         {
225                                 string sUserName = vPart[0];
226                                 string sLMHash   = vPart[4];
227                                 string sNTLMHash = vPart[5];
228
229                                 if (sLMHash.size() == 32 && sNTLMHash.size() == 32)
230                                 {
231                                         if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash))
232                                         {
233                                                 vUserName.push_back(sUserName);
234                                                 vLMHash.push_back(sLMHash);
235                                                 vNTLMHash.push_back(sNTLMHash);
236                                         }
237                                         else
238                                                 printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str());
239                                 }
240                         }
241                 }
242         }
243         else
244                 printf("can't open %s\n", sPathName.c_str());
245 }
246
247 bool NTLMPasswordSeek(unsigned char* pLMPassword, int nLMPasswordLen, int nLMPasswordNext,
248                                           unsigned char* pNTLMHash, string& sNTLMPassword)
249 {
250         if (nLMPasswordNext == nLMPasswordLen)
251         {
252                 unsigned char md[16];
253                 MD4(pLMPassword, nLMPasswordLen * 2, md);
254                 if (memcmp(md, pNTLMHash, 16) == 0)
255                 {
256                         sNTLMPassword = "";
257                         int i;
258                         for (i = 0; i < nLMPasswordLen; i++)
259                                 sNTLMPassword += char(pLMPassword[i * 2]);
260                         return true;
261                 }
262                 else
263                         return false;
264         }
265
266         if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword))
267                 return true;
268
269         if (   pLMPassword[nLMPasswordNext * 2] >= 'A'
270                 && pLMPassword[nLMPasswordNext * 2] <= 'Z')
271         {
272                 pLMPassword[nLMPasswordNext * 2] = pLMPassword[nLMPasswordNext * 2] - 'A' + 'a';
273                 if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword))
274                         return true;
275                 pLMPassword[nLMPasswordNext * 2] = pLMPassword[nLMPasswordNext * 2] - 'a' + 'A';
276         }
277
278         return false;
279 }
280
281 bool LMPasswordCorrectCase(string sLMPassword, unsigned char* pNTLMHash, string& sNTLMPassword)
282 {
283         if (sLMPassword.size() == 0)
284         {
285                 sNTLMPassword = "";
286                 return true;
287         }
288
289         unsigned char* pLMPassword = new unsigned char[sLMPassword.size() * 2];
290         int i;
291         for (i = 0; i < sLMPassword.size(); i++)
292         {
293                 pLMPassword[i * 2    ] = sLMPassword[i];
294                 pLMPassword[i * 2 + 1] = 0x00;
295         }
296         bool fRet = NTLMPasswordSeek(pLMPassword, sLMPassword.size(), 0, pNTLMHash, sNTLMPassword);
297
298         delete pLMPassword;
299
300         return fRet;
301 }
302
303 void Usage()
304 {
305         Logo();
306
307         printf("usage: rcracki_mt -h hash rainbow_table_pathname\n");
308         printf("       rcracki_mt -l hash_list_file rainbow_table_pathname\n");
309         printf("       rcracki_mt -f pwdump_file rainbow_table_pathname\n");
310         printf("       rcracki_mt -c lst_file rainbow_table_pathname\n");
311         printf("\n");
312         printf("-h hash:                use raw hash as input\n");
313         printf("-l hash_list_file:      use hash list file as input, each hash in a line\n");
314         printf("-f pwdump_file:         use pwdump file as input, handles lanmanager hash only\n");
315         printf("-c lst_file:            use .lst (cain format) file as input\n");
316         printf("-r [-s session_name]:   resume from previous session, optional session name\n");
317         printf("rainbow_table_pathname: pathname(s) of the rainbow table(s)\n");
318         printf("\n");
319         printf("Extra options:    -t [nr] use this amount of threads/cores, default is 1\n");
320         printf("                  -o [output_file] write (temporary) results to this file\n");
321         printf("                  -s [session_name] write session data with this name\n");
322         printf("                  -k keep precalculation on disk\n");
323         printf("                  -v show debug information\n");
324         printf("\n");
325 #ifdef _WIN32
326         printf("example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 2 [path]\\MD5\n");
327         printf("         rcracki_mt -l hash.txt [path_to_specific_table]\\*\n");
328 #else
329         printf("example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 2 [path]/MD5\n");
330         printf("         rcracki_mt -l hash.txt [path_to_specific_table]/*\n");
331 #endif
332         printf("         rcracki_mt -f hash.txt -t 4 -o results.txt *.rti\n");
333
334 }
335
336 int main(int argc, char* argv[])
337 {
338         if (argc < 2)
339         {
340                 Usage();
341                 return 0;
342         }
343
344         vector<string> vPathName;
345         vector<string> vDefaultRainbowTablePath;
346         string sWildCharPathName = "";
347         string sInputType        = "";
348         string sInput            = "";
349         string outputFile                = "";
350         string sApplicationPath  = "";
351         string sIniPathName      = "rcracki_mt.ini";
352         bool writeOutput                 = false;
353         string sSessionPathName  = "rcracki.session";
354         string sProgressPathName = "rcracki.progress";
355         string sPrecalcPathName  = "rcracki.precalc";
356         bool resumeSession       = false;
357         bool useDefaultRainbowTablePath = false;
358         bool debug               = false;
359         bool keepPrecalcFiles    = false;
360         string sAlgorithm                = "";
361         int maxThreads                   = 1;
362         CHashSet hs;
363
364         // Read defaults from ini file;
365         bool readFromIni = false;
366         vector<string> vLine;
367         if (ReadLinesFromFile(sIniPathName, vLine)) {
368                 readFromIni = true;
369         }
370         else if (ReadLinesFromFile(GetApplicationPath() + sIniPathName, vLine)) {
371                 readFromIni = true;
372         }
373         if (readFromIni)
374         {
375                 int i;
376                 for (i = 0; i < vLine.size(); i++)
377                 {
378                         if (vLine[i].substr(0,1) != "#")
379                         {
380                                 vector<string> vPart;
381                                 if (SeperateString(vLine[i], "=", vPart))
382                                 {
383                                         string sOption = vPart[0];
384                                         string sValue  = vPart[1];
385                                         
386                                         if (sOption == "Threads") {
387                                                 maxThreads = atoi(sValue.c_str());
388                                         }
389                                         else if (sOption == "DefaultResultsFile") {
390                                                 outputFile = sValue;
391                                         }
392                                         else if (sOption == "AlwaysStoreResultsToFile") {
393                                                 if (sValue == "1")
394                                                         writeOutput = true;
395                                         }
396                                         else if (sOption.substr(0,24) == "DefaultRainbowTablePath.") {
397                                                 //printf("Default RT path: %s\n", sValue.c_str());
398                                                 vDefaultRainbowTablePath.push_back(vLine[i]);
399                                         }
400                                         else if (sOption == "DefaultAlgorithm") {
401                                                 useDefaultRainbowTablePath = true;
402                                                 sAlgorithm = sValue;
403                                         }
404                                         else if (sOption == "AlwaysDebug") {
405                                                 if (sValue == "1")
406                                                         debug = true;
407                                         }
408                                         else if (sOption == "AlwaysKeepPrecalcFiles") {
409                                                 if (sValue == "1")
410                                                         keepPrecalcFiles = true;
411                                         }
412                                         else {
413                                                 printf("illegal option %s in ini file %s\n", sOption.c_str(), sIniPathName.c_str());
414                                                 return 0;
415                                         }
416                                 }
417                         }
418                 }
419                 if (writeOutput && outputFile == "")
420                 {
421                         printf("You need to specify a 'DefaultResultsFile' with 'AlwaysStoreResultsToFile=1'\n");
422                         writeOutput = false;
423                 }
424         }
425
426         // Parse command line arguments
427         int i;
428         for (i = 1; i < argc; i++)
429         {
430                 string cla = argv[i];
431                 if (cla == "-h") {
432                         sInputType = cla;
433                         i++;
434                         if (i < argc)
435                                 sInput = argv[i];
436                 }
437                 else if (cla == "-l") {
438                         sInputType = cla;
439                         i++;
440                         if (i < argc)
441                                 sInput = argv[i];
442                 }
443                 else if (cla == "-f") {
444                         sInputType = cla;
445                         i++;
446                         if (i < argc)
447                                 sInput = argv[i];
448                 }
449                 else if (cla == "-c") {
450                         sInputType = cla;
451                         i++;
452                         if (i < argc)
453                                 sInput = argv[i];
454                 }
455                 else if (cla == "-t") {
456                         i++;
457                         if (i < argc)
458                                 maxThreads = atoi(argv[i]);
459                 }
460                 else if (cla == "-o") {
461                         writeOutput = true;
462                         i++;
463                         if (i < argc)
464                                 outputFile = argv[i];
465                 }
466                 else if (cla == "-r") {
467                         resumeSession = true;
468                 }
469                 else if (cla == "-s") {
470                         i++;
471                         if (i < argc)
472                         {
473                                 sSessionPathName                =  argv[i];
474                                 sSessionPathName                += ".session";
475                                 sProgressPathName               =  argv[i];
476                                 sProgressPathName               += ".progress";
477                                 sPrecalcPathName                =  argv[i];
478                                 sPrecalcPathName                += ".precalc";
479                         }
480                 }
481                 else if (cla == "-v") {
482                         debug = true;
483                 }
484                 else if (cla == "-k") {
485                         keepPrecalcFiles = true;
486                 }
487                 else if (cla == "-a") {
488                         useDefaultRainbowTablePath = true;
489                         i++;
490                         if (i < argc)
491                                 sAlgorithm = argv[i];
492                 }
493                 else {
494                         GetTableList(cla, vPathName);
495                 }
496         }
497
498         if (debug && !readFromIni)
499                 printf("Debug: Couldn't read rcracki_mt.ini, continuing anyway.\n");
500
501         // Load session data if we are resuming
502         if (resumeSession)
503         {
504                 vPathName.clear();
505                 vector<string> sSessionData;
506                 if (ReadLinesFromFile(sSessionPathName.c_str(), sSessionData))
507                 {
508                         int i;
509                         for (i = 0; i < sSessionData.size(); i++)
510                         {
511                                 vector<string> vPart;
512                                 if (SeperateString(sSessionData[i], "=", vPart))
513                                 {
514                                         string sOption = vPart[0];
515                                         string sValue  = vPart[1];
516                                         
517                                         if (sOption == "sPathName") {
518                                                 vPathName.push_back(sValue);
519                                         }
520                                         else if (sOption == "sInputType") {
521                                                 sInputType = sValue;
522                                         }
523                                         else if (sOption == "sInput") {
524                                                 sInput = sValue;
525                                         }
526                                         else if (sOption == "outputFile") {
527                                                 writeOutput = true;
528                                                 outputFile = sValue;
529                                         }
530                                         else if (sOption == "keepPrecalcFiles") {
531                                                 if (sValue == "1")
532                                                         keepPrecalcFiles = true;
533                                         }
534                                 }
535                         }
536                 }
537                 else {
538                         printf("Couldn't open session file %s\n", sSessionPathName.c_str());
539                         return 0;
540                 }
541         }
542
543         if (maxThreads<1)
544                 maxThreads = 1;
545
546         // don't load these if we are resuming a session that already has a list of tables
547         if (useDefaultRainbowTablePath && !resumeSession)
548         {
549                 int i;
550                 for (i = 0; i < vDefaultRainbowTablePath.size(); i++)
551                 {
552                         vector<string> vPart;
553                         if (SeperateString(vDefaultRainbowTablePath[i], ".=", vPart))
554                         {
555                                 string lineAlgorithm = vPart[1];
556                                 string linePath = vPart[2];
557
558                                 if (lineAlgorithm == sAlgorithm)
559                                         GetTableList(linePath, vPathName);
560                         }
561                 }
562         }
563
564         printf("Using %d threads for pre-calculation and false alarm checking...\n", maxThreads);
565
566         setvbuf(stdout, NULL, _IONBF,0);
567         if (vPathName.size() == 0)
568         {
569                 printf("no rainbow table found\n");
570                 return 0;
571         }
572         printf("Found %d rainbowtable files...\n\n", vPathName.size());
573
574         bool fCrackerType;                      // true: hash cracker, false: lm cracker
575         vector<string> vHash;           // hash cracker
576         vector<string> vUserName;       // lm cracker
577         vector<string> vLMHash;         // lm cracker
578         vector<string> vNTLMHash;       // lm cracker
579         if (sInputType == "-h")
580         {
581                 fCrackerType = true;
582
583                 string sHash = sInput;
584                 if (NormalizeHash(sHash))
585                         vHash.push_back(sHash);
586                 else
587                         printf("invalid hash: %s\n", sHash.c_str());
588         }
589         else if (sInputType == "-l")
590         {
591                 fCrackerType = true;
592
593                 string sPathName = sInput;
594                 vector<string> vLine;
595                 if (ReadLinesFromFile(sPathName, vLine))
596                 {
597                         int i;
598                         for (i = 0; i < vLine.size(); i++)
599                         {
600                                 string sHash = vLine[i];
601                                 if (NormalizeHash(sHash))
602                                         vHash.push_back(sHash);
603                                 else
604                                         printf("invalid hash: %s\n", sHash.c_str());
605                         }
606                 }
607                 else
608                         printf("can't open %s\n", sPathName.c_str());
609         }
610         else if (sInputType == "-f")
611         {
612                 fCrackerType = false;
613
614                 string sPathName = sInput;
615                 LoadLMHashFromPwdumpFile(sPathName, vUserName, vLMHash, vNTLMHash);
616         }
617         else if (sInputType == "-c")
618         {
619                 // 2009-01-04 - james - Added this for cain-files.
620                 fCrackerType = false;
621                 string sPathName = sInput;
622                 LoadLMHashFromCainLSTFile(sPathName, vUserName, vLMHash, vNTLMHash);
623         }
624         else
625         {
626                 Usage();
627                 return 0;
628         }
629
630         if (fCrackerType && vHash.size() == 0)
631         {
632                 printf("no hashes found");
633                 return 0;
634         }
635         if (!fCrackerType && vLMHash.size() == 0)
636         {
637                 return 0;
638                 printf("no hashes found");
639         }
640
641         if (fCrackerType)
642         {
643                 int i;
644                 for (i = 0; i < vHash.size(); i++)
645                         hs.AddHash(vHash[i]);
646         }
647         else
648         {
649                 int i;
650                 for (i = 0; i < vLMHash.size(); i++)
651                 {
652                         hs.AddHash(vLMHash[i].substr(0, 16));
653                         hs.AddHash(vLMHash[i].substr(16, 16));
654                 }
655         }
656
657         // Load found hashes from session file
658         if (resumeSession)
659         {
660                 vector<string> sSessionData;
661                 if (ReadLinesFromFile(sSessionPathName.c_str(), sSessionData))
662                 {
663                         int i;
664                         for (i = 0; i < sSessionData.size(); i++)
665                         {
666                                 vector<string> vPart;
667                                 if (SeperateString(sSessionData[i], "=", vPart))
668                                 {
669                                         string sOption = vPart[0];
670                                         string sValue  = vPart[1];
671                                         
672                                         if (sOption == "sHash") {
673                                                 vector<string> vPartHash;
674                                                 if (SeperateString(sValue, "::", vPartHash))
675                                                 {
676                                                         string sHash = vPartHash[0];
677                                                         string sBinary = vPartHash[1];
678                                                         string sPlain = vPartHash[2];
679                                                         
680                                                         hs.SetPlain(sHash, sPlain, sBinary);
681                                                 }
682                                         }
683                                 }
684                         }
685                 }
686         }
687
688         // (Over)write session data if we are not resuming
689         if (!resumeSession)
690         {
691                 FILE* file = fopen(sSessionPathName.c_str(), "w");
692                 string buffer = "";
693
694                 if (file!=NULL)
695                 {
696                         buffer += "sInputType=" + sInputType + "\n";
697                         buffer += "sInput=" + sInput + "\n";
698
699                         int i;
700                         for (i = 0; i < vPathName.size(); i++)
701                         {
702                                 buffer += "sPathName=" + vPathName[i] + "\n";
703                         }
704
705                         if (writeOutput)
706                                 buffer += "outputFile=" + outputFile + "\n";
707
708                         if (keepPrecalcFiles)
709                                 buffer += "keepPrecalcFiles=1\n";
710
711                         fputs (buffer.c_str(), file);
712                         fclose (file);
713                 }
714                 file = fopen(sProgressPathName.c_str(), "w");
715                 fclose (file);
716         }
717
718         // Run
719         CCrackEngine ce;
720         if (writeOutput)
721                 ce.setOutputFile(outputFile);
722         ce.setSession(sSessionPathName, sProgressPathName, sPrecalcPathName, keepPrecalcFiles);
723         ce.Run(vPathName, hs, maxThreads, resumeSession, debug);
724
725         // Remove session files
726         if (debug) printf("Debug: Removing session files.\n");
727         if (remove(sSessionPathName.c_str()) == 0)
728                 remove(sProgressPathName.c_str());
729         else
730                 if (debug) printf("Debug: Failed removing session files.\n");
731
732         // Statistics
733         printf("statistics\n");
734         printf("-------------------------------------------------------\n");
735         printf("plaintext found:          %d of %d (%.2f%%)\n", hs.GetStatHashFound(),
736                                                                                                                         hs.GetStatHashTotal(),
737                                                                                                                         100.0f * hs.GetStatHashFound() / hs.GetStatHashTotal());
738         printf("total disk access time:   %.2f s\n", ce.GetStatTotalDiskAccessTime());
739         printf("total cryptanalysis time: %.2f s\n", ce.GetStatTotalCryptanalysisTime());
740         printf("total chain walk step:    %d\n",     ce.GetStatTotalChainWalkStep());
741         printf("total false alarm:        %d\n",     ce.GetStatTotalFalseAlarm());
742         printf("total chain walk step due to false alarm: %d\n", ce.GetStatTotalChainWalkStepDueToFalseAlarm());
743 //      printf("total chain walk step skipped due to checkpoints: %d\n", ce.GetStatTotalFalseAlarmSkipped()); // Checkpoints not used - yet
744         printf("\n");
745
746         // Result
747         printf("result\n");
748         printf("-------------------------------------------------------\n");
749         if (fCrackerType)
750         {
751                 int i;
752                 for (i = 0; i < vHash.size(); i++)
753                 {
754                         string sPlain, sBinary;
755                         if (!hs.GetPlain(vHash[i], sPlain, sBinary))
756                         {
757                                 sPlain  = "<notfound>";
758                                 sBinary = "<notfound>";
759                         }
760
761                         printf("%s\t%s\thex:%s\n", vHash[i].c_str(), sPlain.c_str(), sBinary.c_str());
762                 }
763         }
764         else
765         {
766                 int i;
767                 for (i = 0; i < vLMHash.size(); i++)
768                 {
769                         string sPlain1, sBinary1;
770                         bool fPart1Found = hs.GetPlain(vLMHash[i].substr(0, 16), sPlain1, sBinary1);
771                         if (!fPart1Found)
772                         {
773                                 sPlain1  = "<notfound>";
774                                 sBinary1 = "<notfound>";
775                         }
776
777                         string sPlain2, sBinary2;
778                         bool fPart2Found = hs.GetPlain(vLMHash[i].substr(16, 16), sPlain2, sBinary2);
779                         if (!fPart2Found)
780                         {
781                                 sPlain2  = "<notfound>";
782                                 sBinary2 = "<notfound>";
783                         }
784
785                         string sPlain = sPlain1 + sPlain2;
786                         string sBinary = sBinary1 + sBinary2;
787
788                         // Correct case
789                         if (fPart1Found && fPart2Found)
790                         {
791                                 unsigned char NTLMHash[16];
792                                 int nHashLen;
793                                 ParseHash(vNTLMHash[i], NTLMHash, nHashLen);
794                                 if (nHashLen != 16)
795                                         printf("debug: nHashLen mismatch\n");
796                                 string sNTLMPassword;
797                                 if (LMPasswordCorrectCase(sPlain, NTLMHash, sNTLMPassword))
798                                 {
799                                         sPlain = sNTLMPassword;
800                                         sBinary = HexToStr((const unsigned char*)sNTLMPassword.c_str(), sNTLMPassword.size());
801                                 }
802                                 else
803                                 {
804                                         printf("%-14s\t%s\thex:%s\n", vUserName[i].c_str(), sPlain.c_str(), sBinary.c_str());
805                                         LM2NTLMcorrector corrector;
806                                         if (corrector.LMPasswordCorrectUnicode(sBinary, NTLMHash, sNTLMPassword))
807                                         {
808                                                 sPlain = sNTLMPassword;
809                                                 sBinary = corrector.getBinary();
810                                                 if (writeOutput)
811                                                 {
812                                                         if (!writeResultLineToFile(outputFile, vNTLMHash[i].c_str(), sPlain.c_str(), sBinary.c_str()))
813                                                                 printf("Couldn't write final result to file!\n");
814                                                 }
815                                         }
816                                         else {
817                                                 printf("case correction for password %s failed!\n", sPlain.c_str());
818                                         }
819                                 }
820                         }
821
822                         // Display
823                         printf("%-14s\t%s\thex:%s\n", vUserName[i].c_str(),
824                                                                                   sPlain.c_str(),
825                                                                                   sBinary.c_str());
826                         
827                 }
828         }
829
830         return 0;
831 }