2 * rcracki_mt is a multithreaded implementation and fork of the original
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
12 * Modified by Martin Westergaard Jørgensen <martinwj2005@gmail.com> to support * indexed and hybrid tables
14 * Modified by neinbrucke to support multi threading and a bunch of other stuff :)
16 * 2009-01-04 - <james.dickson@comhem.se> - Slightly modified (or "fulhack" as
17 * we say in sweden) to support cain .lst files.
19 * This file is part of rcracki_mt.
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.
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.
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/>.
35 #if defined(_WIN32) && !defined(__GNUC__)
36 #pragma warning(disable : 4786 4267 4018)
39 #include "CrackEngine.h"
46 #include <sys/types.h>
52 #if defined(_WIN32) && !defined(__GNUC__)
53 #pragma comment(lib, "libeay32.lib")
56 //////////////////////////////////////////////////////////////////////
59 void GetTableList(string sWildCharPathName, vector<string>& vPathName)
64 string::size_type n = sWildCharPathName.find_last_of('\\');
66 if ( n == (sWildCharPathName.size() - 1) )
68 sWildCharPathName = sWildCharPathName.substr(0, n);
69 n = sWildCharPathName.find_last_of('\\');
72 if (n != string::npos)
73 sPath = sWildCharPathName.substr(0, n + 1);
77 long handle = _findfirst(sWildCharPathName.c_str(), &fd);
82 string sName = fd.name;
84 if (sName.substr(sName.size()-3, 3) == ".rt" && !(fd.attrib & _A_SUBDIR))
86 string sPathName = sPath + sName;
87 vPathName.push_back(sPathName);
91 if (sName.substr(sName.size()-4, 4) == ".rti" && !(fd.attrib & _A_SUBDIR))
93 string sPathName = sPath + sName;
94 vPathName.push_back(sPathName);
98 if (sName.substr(sName.size()-5, 5) == ".rti2" && !(fd.attrib & _A_SUBDIR))
100 string sPathName = sPath + sName;
101 vPathName.push_back(sPathName);
105 if (sName != "." && sName != ".." && (fd.attrib & _A_SUBDIR))
107 string sPath_sub = sPath + sName + '\\';
108 string sWildCharPathName_sub = sPath_sub + '*';
109 GetTableList(sWildCharPathName_sub, vPathName);
112 } while (_findnext(handle, &fd) == 0);
116 //printf("Found %d rainbowtables (files) in %d sub directories...\n", vPathName.size(), subDir_count);
119 //void GetTableList(int argc, char* argv[], vector<string>& vPathName)
120 void GetTableList(string sWildCharPathName, vector<string>& vPathName)
125 if (lstat(sWildCharPathName.c_str(), &buf) == 0)
127 if (S_ISDIR(buf.st_mode))
129 DIR *dir = opendir(sWildCharPathName.c_str());
132 struct dirent *pDir=NULL;
133 while((pDir = readdir(dir)) != NULL)
135 string filename = "";
136 filename += (*pDir).d_name;
137 if (filename != "." && filename != "..")
139 string new_filename = sWildCharPathName + '/' + filename;
140 GetTableList(new_filename, vPathName);
146 else if (S_ISREG(buf.st_mode))
148 if (sWildCharPathName.size()>3)
150 if (sWildCharPathName.substr(sWildCharPathName.size()-3, 3) == ".rt")
152 vPathName.push_back(sWildCharPathName);
155 if (sWildCharPathName.size()>4)
157 if (sWildCharPathName.substr(sWildCharPathName.size()-4, 4) == ".rti")
159 //string sPathName_sub = sPath_sub + sName_sub;
160 vPathName.push_back(sWildCharPathName);
161 //printf("sPathName_sub: %s\n", sPathName_sub.c_str());
164 if ( sWildCharPathName.size() > 5 )
166 if ( sWildCharPathName.substr( sWildCharPathName.size() - 5, 5 ) == ".rti2" )
168 vPathName.push_back( sWildCharPathName );
176 bool NormalizeHash(string& sHash)
178 string sNormalizedHash = sHash;
180 if ( sNormalizedHash.size() % 2 != 0
181 || sNormalizedHash.size() < MIN_HASH_LEN * 2
182 || sNormalizedHash.size() > MAX_HASH_LEN * 2)
187 for (i = 0; i < sNormalizedHash.size(); i++)
189 if (sNormalizedHash[i] >= 'A' && sNormalizedHash[i] <= 'F')
190 sNormalizedHash[i] = (char) sNormalizedHash[i] - 'A' + 'a';
194 for (i = 0; i < sNormalizedHash.size(); i++)
196 if ( (sNormalizedHash[i] < 'a' || sNormalizedHash[i] > 'f')
197 && (sNormalizedHash[i] < '0' || sNormalizedHash[i] > '9'))
201 sHash = sNormalizedHash;
205 void LoadLMHashFromPwdumpFile(string sPathName, vector<string>& vUserName, vector<string>& vLMHash, vector<string>& vNTLMHash)
207 vector<string> vLine;
208 if (ReadLinesFromFile(sPathName, vLine))
211 for (i = 0; i < vLine.size(); i++)
213 vector<string> vPart;
214 if (SeperateString(vLine[i], "::::", vPart))
216 string sUserName = vPart[0];
217 string sLMHash = vPart[2];
218 string sNTLMHash = vPart[3];
220 if (sLMHash.size() == 32 && sNTLMHash.size() == 32)
222 if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash))
224 vUserName.push_back(sUserName);
225 vLMHash.push_back(sLMHash);
226 vNTLMHash.push_back(sNTLMHash);
229 printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str());
235 printf("can't open %s\n", sPathName.c_str());
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)
241 vector<string> vLine;
242 if (ReadLinesFromFile(sPathName, vLine))
245 for (i = 0; i < vLine.size(); i++)
247 vector<string> vPart;
248 if (SeperateString(vLine[i], "\t\t\t\t\t\t", vPart))
250 string sUserName = vPart[0];
251 string sLMHash = vPart[4];
252 string sNTLMHash = vPart[5];
254 if (sLMHash.size() == 32 && sNTLMHash.size() == 32)
256 if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash))
258 vUserName.push_back(sUserName);
259 vLMHash.push_back(sLMHash);
260 vNTLMHash.push_back(sNTLMHash);
263 printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str());
269 printf("can't open %s\n", sPathName.c_str());
272 bool NTLMPasswordSeek(unsigned char* pLMPassword, int nLMPasswordLen, int nLMPasswordNext,
273 unsigned char* pNTLMHash, string& sNTLMPassword)
275 if (nLMPasswordNext == nLMPasswordLen)
277 unsigned char md[MD4_DIGEST_LENGTH];
278 MD4_NEW(pLMPassword, nLMPasswordLen * 2, md);
280 if (memcmp(md, pNTLMHash, MD4_DIGEST_LENGTH) == 0)
284 for (i = 0; i < nLMPasswordLen; i++)
285 sNTLMPassword += char(pLMPassword[i * 2]);
292 if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword))
295 if ( pLMPassword[nLMPasswordNext * 2] >= 'A'
296 && pLMPassword[nLMPasswordNext * 2] <= 'Z')
298 pLMPassword[nLMPasswordNext * 2] = (unsigned char) pLMPassword[nLMPasswordNext * 2] - 'A' + 'a';
299 if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword))
301 pLMPassword[nLMPasswordNext * 2] = (unsigned char) pLMPassword[nLMPasswordNext * 2] - 'a' + 'A';
307 bool LMPasswordCorrectCase(string sLMPassword, unsigned char* pNTLMHash, string& sNTLMPassword)
309 if (sLMPassword.size() == 0)
315 unsigned char* pLMPassword = new unsigned char[sLMPassword.size() * 2];
317 for (i = 0; i < sLMPassword.size(); i++)
319 pLMPassword[i * 2 ] = sLMPassword[i];
320 pLMPassword[i * 2 + 1] = 0x00;
322 bool fRet = NTLMPasswordSeek(pLMPassword, sLMPassword.size(), 0, pNTLMHash, sNTLMPassword);
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");
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");
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");
353 printf("example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 2 [path]\\MD5\n");
354 printf(" rcracki_mt -l hash.txt [path_to_specific_table]\\*\n");
356 printf("example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 2 [path]/MD5\n");
357 printf(" rcracki_mt -l hash.txt [path_to_specific_table]/*\n");
359 printf(" rcracki_mt -f hash.txt -t 4 -o results.txt *.rti\n");
362 int main(int argc, char* argv[])
370 vector<string> vPathName;
371 vector<string> vDefaultRainbowTablePath;
372 string sWildCharPathName = "";
373 string sInputType = "";
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;
385 bool keepPrecalcFiles = false;
386 string sAlgorithm = "";
391 // Read defaults from ini file;
392 bool readFromIni = false;
393 vector<string> vLine;
394 if (ReadLinesFromFile(sIniPathName, vLine)) {
397 else if (ReadLinesFromFile(GetApplicationPath() + sIniPathName, vLine)) {
403 for (i = 0; i < vLine.size(); i++)
405 if (vLine[i].substr(0,1) != "#")
407 vector<string> vPart;
408 if (SeperateString(vLine[i], "=", vPart))
410 string sOption = vPart[0];
411 string sValue = vPart[1];
413 if (sOption == "Threads") {
414 maxThreads = atoi(sValue.c_str());
416 else if (sOption == "MaxMemoryUsage" ) {
417 maxMem = atoi(sValue.c_str()) * 1024 *1024;
419 else if (sOption == "DefaultResultsFile") {
422 else if (sOption == "AlwaysStoreResultsToFile") {
426 else if (sOption.substr(0,24) == "DefaultRainbowTablePath.") {
427 //printf("Default RT path: %s\n", sValue.c_str());
428 vDefaultRainbowTablePath.push_back(vLine[i]);
430 else if (sOption == "DefaultAlgorithm") {
431 useDefaultRainbowTablePath = true;
434 else if (sOption == "AlwaysDebug") {
438 else if (sOption == "AlwaysKeepPrecalcFiles") {
440 keepPrecalcFiles = true;
443 printf("illegal option %s in ini file %s\n", sOption.c_str(), sIniPathName.c_str());
449 if (writeOutput && outputFile == "")
451 printf("You need to specify a 'DefaultResultsFile' with 'AlwaysStoreResultsToFile=1'\n");
456 // Parse command line arguments
458 for (i = 1; i < argc; i++)
460 string cla = argv[i];
467 else if (cla == "-l") {
473 else if (cla == "-f") {
479 else if (cla == "-c") {
485 else if (cla == "-t") {
488 maxThreads = atoi(argv[i]);
490 else if ( cla == "-m" ) {
493 maxMem = atoi(argv[i]) * 1024 * 1024;
495 else if (cla == "-o") {
499 outputFile = argv[i];
501 else if (cla == "-r") {
502 resumeSession = true;
504 else if (cla == "-s") {
508 sSessionPathName = argv[i];
509 sSessionPathName += ".session";
510 sProgressPathName = argv[i];
511 sProgressPathName += ".progress";
512 sPrecalcPathName = argv[i];
513 sPrecalcPathName += ".precalc";
516 else if (cla == "-v") {
519 else if (cla == "-k") {
520 keepPrecalcFiles = true;
522 else if (cla == "-a") {
523 useDefaultRainbowTablePath = true;
526 sAlgorithm = argv[i];
529 GetTableList(cla, vPathName);
533 if (debug && !readFromIni)
534 printf("Debug: Couldn't read rcracki_mt.ini, continuing anyway.\n");
536 // Load session data if we are resuming
540 vector<string> sSessionData;
541 if (ReadLinesFromFile(sSessionPathName.c_str(), sSessionData))
544 for (i = 0; i < sSessionData.size(); i++)
546 vector<string> vPart;
547 if (SeperateString(sSessionData[i], "=", vPart))
549 string sOption = vPart[0];
550 string sValue = vPart[1];
552 if (sOption == "sPathName") {
553 vPathName.push_back(sValue);
555 else if (sOption == "sInputType") {
558 else if (sOption == "sInput") {
561 else if (sOption == "outputFile") {
565 else if (sOption == "keepPrecalcFiles") {
567 keepPrecalcFiles = true;
573 printf("Couldn't open session file %s\n", sSessionPathName.c_str());
581 // don't load these if we are resuming a session that already has a list of tables
582 if (useDefaultRainbowTablePath && !resumeSession)
585 for (i = 0; i < vDefaultRainbowTablePath.size(); i++)
587 vector<string> vPart;
588 if (SeperateString(vDefaultRainbowTablePath[i], ".=", vPart))
590 string lineAlgorithm = vPart[1];
591 string linePath = vPart[2];
593 if (lineAlgorithm == sAlgorithm)
594 GetTableList(linePath, vPathName);
599 printf("Using %d threads for pre-calculation and false alarm checking...\n", maxThreads);
601 setvbuf(stdout, NULL, _IONBF,0);
602 if (vPathName.size() == 0)
604 printf("no rainbow table found\n");
607 printf("Found %lu rainbowtable files...\n\n",
608 (unsigned long)vPathName.size());
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")
619 string sHash = sInput;
620 if (NormalizeHash(sHash))
621 vHash.push_back(sHash);
623 printf("invalid hash: %s\n", sHash.c_str());
625 else if (sInputType == "-l")
629 string sPathName = sInput;
630 vector<string> vLine;
631 if (ReadLinesFromFile(sPathName, vLine))
634 for (i = 0; i < vLine.size(); i++)
636 string sHash = vLine[i];
637 if (NormalizeHash(sHash))
638 vHash.push_back(sHash);
640 printf("invalid hash: %s\n", sHash.c_str());
644 printf("can't open %s\n", sPathName.c_str());
646 else if (sInputType == "-f")
648 fCrackerType = false;
650 string sPathName = sInput;
651 LoadLMHashFromPwdumpFile(sPathName, vUserName, vLMHash, vNTLMHash);
653 else if (sInputType == "-c")
655 // 2009-01-04 - james.dickson - Added this for cain-files.
656 fCrackerType = false;
657 string sPathName = sInput;
658 LoadLMHashFromCainLSTFile(sPathName, vUserName, vLMHash, vNTLMHash);
666 if (fCrackerType && vHash.size() == 0)
668 printf("no hashes found");
671 if (!fCrackerType && vLMHash.size() == 0)
674 printf("no hashes found");
680 for (i = 0; i < vHash.size(); i++)
681 hs.AddHash(vHash[i]);
686 for (i = 0; i < vLMHash.size(); i++)
688 hs.AddHash(vLMHash[i].substr(0, 16));
689 hs.AddHash(vLMHash[i].substr(16, 16));
693 // Load found hashes from session file
696 vector<string> sSessionData;
697 if (ReadLinesFromFile(sSessionPathName.c_str(), sSessionData))
700 for (i = 0; i < sSessionData.size(); i++)
702 vector<string> vPart;
703 if (SeperateString(sSessionData[i], "=", vPart))
705 string sOption = vPart[0];
706 string sValue = vPart[1];
708 if (sOption == "sHash") {
709 vector<string> vPartHash;
710 if (SeperateString(sValue, "::", vPartHash))
712 string sHash = vPartHash[0];
713 string sBinary = vPartHash[1];
714 string sPlain = vPartHash[2];
716 hs.SetPlain(sHash, sPlain, sBinary);
724 // (Over)write session data if we are not resuming
727 FILE* file = fopen(sSessionPathName.c_str(), "w");
732 buffer += "sInputType=" + sInputType + "\n";
733 buffer += "sInput=" + sInput + "\n";
736 for (i = 0; i < vPathName.size(); i++)
738 buffer += "sPathName=" + vPathName[i] + "\n";
742 buffer += "outputFile=" + outputFile + "\n";
744 if (keepPrecalcFiles)
745 buffer += "keepPrecalcFiles=1\n";
747 fputs (buffer.c_str(), file);
750 file = fopen(sProgressPathName.c_str(), "w");
757 ce.setOutputFile(outputFile);
758 ce.setSession(sSessionPathName, sProgressPathName, sPrecalcPathName, keepPrecalcFiles);
759 ce.Run(vPathName, hs, maxThreads, maxMem, resumeSession, debug);
761 // Remove session files
762 if (debug) printf("Debug: Removing session files.\n");
764 if (remove(sSessionPathName.c_str()) == 0)
765 remove(sProgressPathName.c_str());
767 if (debug) printf("Debug: Failed removing session files.\n");
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
786 printf("-------------------------------------------------------\n");
790 for (i = 0; i < vHash.size(); i++)
792 string sPlain, sBinary;
793 if (!hs.GetPlain(vHash[i], sPlain, sBinary))
795 sPlain = "<notfound>";
796 sBinary = "<notfound>";
799 printf("%s\t%s\thex:%s\n", vHash[i].c_str(), sPlain.c_str(), sBinary.c_str());
805 for (i = 0; i < vLMHash.size(); i++)
807 string sPlain1, sBinary1;
808 bool fPart1Found = hs.GetPlain(vLMHash[i].substr(0, 16), sPlain1, sBinary1);
811 sPlain1 = "<notfound>";
812 sBinary1 = "<notfound>";
815 string sPlain2, sBinary2;
816 bool fPart2Found = hs.GetPlain(vLMHash[i].substr(16, 16), sPlain2, sBinary2);
819 sPlain2 = "<notfound>";
820 sBinary2 = "<notfound>";
823 string sPlain = sPlain1 + sPlain2;
824 string sBinary = sBinary1 + sBinary2;
827 if (fPart1Found && fPart2Found)
829 unsigned char NTLMHash[16];
831 ParseHash(vNTLMHash[i], NTLMHash, nHashLen);
833 printf("debug: nHashLen mismatch\n");
834 string sNTLMPassword;
835 if (LMPasswordCorrectCase(sPlain, NTLMHash, sNTLMPassword))
837 sPlain = sNTLMPassword;
838 sBinary = HexToStr((const unsigned char*)sNTLMPassword.c_str(), sNTLMPassword.size());
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))
846 sPlain = sNTLMPassword;
847 sBinary = corrector.getBinary();
850 if (!writeResultLineToFile(outputFile, vNTLMHash[i].c_str(), sPlain.c_str(), sBinary.c_str()))
851 printf("Couldn't write final result to file!\n");
855 printf("case correction for password %s failed!\n", sPlain.c_str());
861 printf("%-14s\t%s\thex:%s\n", vUserName[i].c_str(),