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