From ce75ca839a9cd1863ea13636ffebf7ee1dcc4267 Mon Sep 17 00:00:00 2001 From: James Nobis Date: Sat, 9 Oct 2010 15:49:19 -0500 Subject: [PATCH] remove old deprecated rcracki commit rcracki_mt with rti2 support --- Client Applications/rcracki/CrackEngine.cpp | 394 -- Client Applications/rcracki/CrackEngine.h | 54 - Client Applications/rcracki/HashSet.cpp | 136 - Client Applications/rcracki/HashSet.h | 37 - Client Applications/rcracki/RainbowCrack.cpp | 401 -- Client Applications/rcracki/charset.txt | 61 - Client Applications/rcracki/rcracki.sln | 20 - Client Applications/rcracki/rcracki.suo | Bin 63488 -> 0 bytes Client Applications/rcracki/rcracki.vcproj | 327 -- .../rcracki_mt/BaseRTReader.cpp | 25 +- Client Applications/rcracki_mt/BaseRTReader.h | 30 +- .../rcracki_mt/ChainWalkContext.cpp | 1241 ++--- .../rcracki_mt/ChainWalkContext.h | 189 +- .../rcracki_mt/ChainWalkSet.cpp | 629 +-- Client Applications/rcracki_mt/ChainWalkSet.h | 135 +- .../rcracki_mt/CrackEngine.cpp | 2679 +++++----- Client Applications/rcracki_mt/CrackEngine.h | 173 +- .../rcracki_mt/HashAlgorithm.cpp | 850 ++-- .../rcracki_mt/HashAlgorithm.h | 103 +- .../rcracki_mt/HashRoutine.cpp | 174 +- Client Applications/rcracki_mt/HashRoutine.h | 88 +- Client Applications/rcracki_mt/HashSet.cpp | 347 +- Client Applications/rcracki_mt/HashSet.h | 99 +- Client Applications/rcracki_mt/MemoryPool.cpp | 187 +- Client Applications/rcracki_mt/MemoryPool.h | 75 +- Client Applications/rcracki_mt/Public.cpp | 743 +-- Client Applications/rcracki_mt/Public.h | 253 +- Client Applications/rcracki_mt/README.txt | 332 +- Client Applications/rcracki_mt/RTI2Reader.cpp | 34 +- Client Applications/rcracki_mt/RTI2Reader.h | 31 +- .../rcracki_mt/RainbowCrack.cpp | 1700 ++++--- Client Applications/rcracki_mt/fast_md5.cpp | 31 +- Client Applications/rcracki_mt/fast_md5.h | 21 + Client Applications/rcracki_mt/lm2ntlm.cpp | 4525 +++++++++-------- Client Applications/rcracki_mt/lm2ntlm.h | 135 +- Client Applications/rcracki_mt/md4.cpp | 819 ++- Client Applications/rcracki_mt/md4.h | 45 +- .../rcracki_mt/rcrackiThread.cpp | 486 +- .../rcracki_mt/rcrackiThread.h | 145 +- Client Applications/rcracki_mt/rcracki_mt.exe | Bin 289280 -> 0 bytes Client Applications/rcracki_mt/rcracki_mt.sln | 10 +- Client Applications/rcracki_mt/rcracki_mt.suo | Bin 9216 -> 63488 bytes .../rcracki_mt/rcracki_mt.vcproj | 78 +- Client Applications/rcracki_mt/sha1.cpp | 703 ++- Client Applications/rcracki_mt/sha1.h | 34 +- 45 files changed, 8911 insertions(+), 9668 deletions(-) delete mode 100644 Client Applications/rcracki/CrackEngine.cpp delete mode 100644 Client Applications/rcracki/CrackEngine.h delete mode 100644 Client Applications/rcracki/HashSet.cpp delete mode 100644 Client Applications/rcracki/HashSet.h delete mode 100644 Client Applications/rcracki/RainbowCrack.cpp delete mode 100644 Client Applications/rcracki/charset.txt delete mode 100644 Client Applications/rcracki/rcracki.sln delete mode 100644 Client Applications/rcracki/rcracki.suo delete mode 100644 Client Applications/rcracki/rcracki.vcproj delete mode 100644 Client Applications/rcracki_mt/rcracki_mt.exe diff --git a/Client Applications/rcracki/CrackEngine.cpp b/Client Applications/rcracki/CrackEngine.cpp deleted file mode 100644 index 23bb838..0000000 --- a/Client Applications/rcracki/CrackEngine.cpp +++ /dev/null @@ -1,394 +0,0 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786) -#endif - -#include "CrackEngine.h" -#include "BaseRTReader.h" -#include "RTReader.h" -#include "RTIReader.h" -#include "RTI2Reader.h" -#include - -CCrackEngine::CCrackEngine() -{ - ResetStatistics(); -} - -CCrackEngine::~CCrackEngine() -{ -} - -////////////////////////////////////////////////////////////////////// - -void CCrackEngine::ResetStatistics() -{ - m_fTotalDiskAccessTime = 0.0f; - m_fTotalCryptanalysisTime = 0.0f; - m_nTotalChainWalkStep = 0; - m_nTotalFalseAlarm = 0; - m_nTotalChainWalkStepDueToFalseAlarm = 0; -// m_nTotalFalseAlarmSkipped = 0; -} - -int CCrackEngine::BinarySearch(RainbowChainCP* pChain, int nRainbowChainCount, uint64 nIndex) -{ - int nLow = 0; - int nHigh = nRainbowChainCount - 1; - while (nLow <= nHigh) - { - int nMid = (nLow + nHigh) / 2; - if (nIndex == pChain[nMid].nIndexE) - return nMid; - else if (nIndex < pChain[nMid].nIndexE) - nHigh = nMid - 1; - else - nLow = nMid + 1; - } - - return -1; -} -bool CCrackEngine::CheckAlarm(RainbowChainCP* pChain, int nGuessedPos, unsigned char* pHash, CHashSet& hs) -{ - CChainWalkContext cwc; - cwc.SetIndex(pChain->nIndexS); -// printf("Checking alarm for %ui64\n", pChain->nIndexS); - int nPos, i = 0; - if(m_pHeader != NULL) - { - int nNextPos = m_pHeader->m_cppos[i]; - for (nPos = 0; nPos < nGuessedPos; nPos++) - { - cwc.IndexToPlain(); - cwc.PlainToHash(); - cwc.HashToIndex(nPos); - if(nPos == nNextPos) // Check if we reached the next checkpoint position - { - if(i <= m_pHeader->rti_cplength) - nNextPos = m_pHeader->m_cppos[++i]; - if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & (1 << 15 - m_pHeader->rti_cplength - i) >> 15 - m_pHeader->rti_cplength - i)) - { -// m_nTotalFalseAlarmSkipped += 10000 - 5000; - printf("CheckPoint caught false alarm at position %i\n", nPos); - return false; - } - } - } - } - else - { - for (nPos = 0; nPos < nGuessedPos; nPos++) - { - cwc.IndexToPlain(); - cwc.PlainToHash(); - cwc.HashToIndex(nPos); - } - } - cwc.IndexToPlain(); - cwc.PlainToHash(); - - if (cwc.CheckHash(pHash)) - { - printf("plaintext of %s is %s\n", cwc.GetHash().c_str(), cwc.GetPlain().c_str()); - hs.SetPlain(cwc.GetHash(), cwc.GetPlain(), cwc.GetBinary()); - return true; - } - - return false; -} - - -void CCrackEngine::GetChainIndexRangeWithSameEndpoint(RainbowChainCP* pChain, - int nRainbowChainCount, - int nMatchingIndexE, - int& nMatchingIndexEFrom, - int& nMatchingIndexETo) -{ - nMatchingIndexEFrom = nMatchingIndexE; - nMatchingIndexETo = nMatchingIndexE; - while (nMatchingIndexEFrom > 0) - { - if (pChain[nMatchingIndexEFrom - 1].nIndexE == pChain[nMatchingIndexE].nIndexE) - nMatchingIndexEFrom--; - else - break; - } - while (nMatchingIndexETo < nRainbowChainCount - 1) - { - if (pChain[nMatchingIndexETo + 1].nIndexE == pChain[nMatchingIndexE].nIndexE) - nMatchingIndexETo++; - else - break; - } -} - -void CCrackEngine::SearchTableChunk(RainbowChainCP* pChain, int nRainbowChainLen, int nRainbowChainCount, CHashSet& hs) -{ - vector vHash; - hs.GetLeftHashWithLen(vHash, CChainWalkContext::GetHashLen()); - printf("searching for %d hash%s...\n", vHash.size(), - vHash.size() > 1 ? "es" : ""); - - int nChainWalkStep = 0; - int nFalseAlarm = 0; - int nChainWalkStepDueToFalseAlarm = 0; - - int nHashIndex; - for (nHashIndex = 0; nHashIndex < vHash.size(); nHashIndex++) - { - unsigned char TargetHash[MAX_HASH_LEN]; - int nHashLen; - ParseHash(vHash[nHashIndex], TargetHash, nHashLen); - if (nHashLen != CChainWalkContext::GetHashLen()) - printf("debug: nHashLen mismatch\n"); - - // Rqeuest ChainWalk - bool fNewlyGenerated; - uint64* pStartPosIndexE = m_cws.RequestWalk(TargetHash, - nHashLen, - CChainWalkContext::GetHashRoutineName(), - CChainWalkContext::GetPlainCharsetName(), - CChainWalkContext::GetPlainLenMin(), - CChainWalkContext::GetPlainLenMax(), - CChainWalkContext::GetRainbowTableIndex(), - nRainbowChainLen, - fNewlyGenerated); - //printf("debug: using %s walk for %s\n", fNewlyGenerated ? "newly generated" : "existing", - // vHash[nHashIndex].c_str()); - - // Walk - int nPos; - for (nPos = nRainbowChainLen - 2; nPos >= 0; nPos--) - { - if (fNewlyGenerated) - { - CChainWalkContext cwc; - cwc.SetHash(TargetHash); - cwc.HashToIndex(nPos); - int i; - for (i = nPos + 1; i <= nRainbowChainLen - 2; i++) - { - cwc.IndexToPlain(); - cwc.PlainToHash(); - cwc.HashToIndex(i); - } - - pStartPosIndexE[nPos] = cwc.GetIndex(); - nChainWalkStep += nRainbowChainLen - 2 - nPos; - } - uint64 nIndexEOfCurPos = pStartPosIndexE[nPos]; - - // Search matching nIndexE - int nMatchingIndexE = BinarySearch(pChain, nRainbowChainCount, nIndexEOfCurPos); - if (nMatchingIndexE != -1) - { - int nMatchingIndexEFrom, nMatchingIndexETo; - GetChainIndexRangeWithSameEndpoint(pChain, nRainbowChainCount, - nMatchingIndexE, - nMatchingIndexEFrom, nMatchingIndexETo); - int i; - for (i = nMatchingIndexEFrom; i <= nMatchingIndexETo; i++) - { - if (CheckAlarm(pChain + i, nPos, TargetHash, hs)) - { - //printf("debug: discarding walk for %s\n", vHash[nHashIndex].c_str()); - m_cws.DiscardWalk(pStartPosIndexE); - goto NEXT_HASH; - } - else - { - nChainWalkStepDueToFalseAlarm += nPos + 1; - nFalseAlarm++; - } - } - } - } -NEXT_HASH:; - } - - //printf("debug: chain walk step: %d\n", nChainWalkStep); - //printf("debug: false alarm: %d\n", nFalseAlarm); - //printf("debug: chain walk step due to false alarm: %d\n", nChainWalkStepDueToFalseAlarm); - - m_nTotalChainWalkStep += nChainWalkStep; - m_nTotalFalseAlarm += nFalseAlarm; - m_nTotalChainWalkStepDueToFalseAlarm += nChainWalkStepDueToFalseAlarm; -} - -void CCrackEngine::SearchRainbowTable(string sPathName, CHashSet& hs) -{ - // FileName -#ifdef _WIN32 - int nIndex = sPathName.find_last_of('\\'); -#else - int nIndex = sPathName.find_last_of('/'); -#endif - string sFileName; - if (nIndex != -1) - sFileName = sPathName.substr(nIndex + 1); - else - sFileName = sPathName; - - // Info - printf("%s:\n", sFileName.c_str()); - - // Setup - int nRainbowChainLen, nRainbowChainCount; - if (!CChainWalkContext::SetupWithPathName(sPathName, nRainbowChainLen, nRainbowChainCount)) - return; - //printf("keyspace: %u\n", CChainWalkContext::GetPlainSpaceTotal()); - // Already finished? - if (!hs.AnyHashLeftWithLen(CChainWalkContext::GetHashLen())) - { - printf("this table contains hashes with length %d only\n", CChainWalkContext::GetHashLen()); - return; - } - BaseRTReader *reader = NULL; - if(sFileName.substr(sFileName.length() - 4, sFileName.length()) == "rti2") - { - reader = (BaseRTReader*)new RTI2Reader(sPathName); - m_pHeader = ((RTI2Reader*)reader)->GetHeader(); - m_TableType = RTI2; - } - else if(sFileName.substr(sFileName.length() - 3, sFileName.length()) == "rti") - { - reader = (BaseRTReader*)new RTIReader(sPathName); - m_TableType = RTI; - } - else if(sFileName.substr(sFileName.length() - 2, sFileName.length()) == "rt") - { - reader = (BaseRTReader*)new RTReader(sPathName); - m_TableType = RT; - } - else - { - printf("Invalid rainbow table type"); - return; - } - static CMemoryPool mp; - unsigned int nAllocatedSize; - int chainsleft = reader->GetChainsLeft(); - RainbowChainCP* pChain = (RainbowChainCP*)mp.Allocate(chainsleft * sizeof(RainbowChainCP), nAllocatedSize); - nAllocatedSize = nAllocatedSize / sizeof(RainbowChainCP) * sizeof(RainbowChainCP); // Round to boundary - unsigned int nChains = nAllocatedSize / sizeof(RainbowChainCP); - bool fVerified = false; - while (reader->GetChainsLeft() > 0) // Chunk read loop - { - clock_t t1 = clock(); - reader->ReadChains(nChains, pChain); - clock_t t2 = clock(); - float fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - printf("%u chains read, disk access time: %.2f s\n", nChains, fTime); - m_fTotalDiskAccessTime += fTime; - int nRainbowChainCountRead = nChains; - - if (!fVerified) - { - printf("verifying the file...\n"); - - // Chain length test - int nIndexToVerify = nRainbowChainCountRead / 2; - CChainWalkContext cwc; - cwc.SetIndex(pChain[nIndexToVerify].nIndexS); - int nPos; - for (nPos = 0; nPos < nRainbowChainLen - 1; nPos++) - { - cwc.IndexToPlain(); - cwc.PlainToHash(); - cwc.HashToIndex(nPos); - } - if (cwc.GetIndex() != pChain[nIndexToVerify].nIndexE) - { - printf("rainbow chain length verify fail\n"); - break; - } - - // Chain sort test - int i; - for (i = 0; i < nRainbowChainCountRead - 1; i++) - { - if (pChain[i].nIndexE > pChain[i + 1].nIndexE) - break; - } - if (i != nRainbowChainCountRead - 1) - { - printf("this file is not sorted\n"); - break; - } - - fVerified = true; - - } - // Search table chunk - t1 = clock(); - SearchTableChunk(pChain, nRainbowChainLen, nRainbowChainCountRead, hs); - t2 = clock(); - fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - printf("cryptanalysis time: %.2f s\n", fTime); - m_fTotalCryptanalysisTime += fTime; - - // Already finished? - if (!hs.AnyHashLeftWithLen(CChainWalkContext::GetHashLen())) - break; - } -} - -void CCrackEngine::Run(vector vPathName, CHashSet& hs) -{ - // Reset statistics - ResetStatistics(); - - // Sort vPathName (CChainWalkSet need it) - int i, j; - for (i = 0; i < vPathName.size() - 1; i++) - for (j = 0; j < vPathName.size() - i - 1; j++) - { - if (vPathName[j] > vPathName[j + 1]) - { - string sTemp; - sTemp = vPathName[j]; - vPathName[j] = vPathName[j + 1]; - vPathName[j + 1] = sTemp; - } - } - - // Run - for (i = 0; i < vPathName.size() && hs.AnyhashLeft(); i++) - { - SearchRainbowTable(vPathName[i], hs); - printf("\n"); - } -} - -float CCrackEngine::GetStatTotalDiskAccessTime() -{ - return m_fTotalDiskAccessTime; -} -/*float CCrackEngine::GetWastedTime() -{ - return m_fIndexTime; -}*/ -float CCrackEngine::GetStatTotalCryptanalysisTime() -{ - return m_fTotalCryptanalysisTime; -} - -int CCrackEngine::GetStatTotalChainWalkStep() -{ - return m_nTotalChainWalkStep; -} - -int CCrackEngine::GetStatTotalFalseAlarm() -{ - return m_nTotalFalseAlarm; -} - -int CCrackEngine::GetStatTotalChainWalkStepDueToFalseAlarm() -{ - return m_nTotalChainWalkStepDueToFalseAlarm; -} diff --git a/Client Applications/rcracki/CrackEngine.h b/Client Applications/rcracki/CrackEngine.h deleted file mode 100644 index 210ad87..0000000 --- a/Client Applications/rcracki/CrackEngine.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifndef _CRACKENGINE_H -#define _CRACKENGINE_H - -#include "Public.h" -#include "HashSet.h" -#include "ChainWalkContext.h" -#include "MemoryPool.h" -#include "ChainWalkSet.h" -#include "RTI2Reader.h" -enum RTTYPE { RT, RTI, RTI2 }; -class CCrackEngine -{ -public: - CCrackEngine(); - virtual ~CCrackEngine(); - -private: - CChainWalkSet m_cws; - RTI2Header *m_pHeader; - // Statistics - float m_fTotalDiskAccessTime; - float m_fTotalCryptanalysisTime; - int m_nTotalChainWalkStep; - int m_nTotalFalseAlarm; - int m_nTotalChainWalkStepDueToFalseAlarm; - FILE *m_fChains; - RTTYPE m_TableType; -private: - void ResetStatistics(); - int BinarySearch(RainbowChainCP* pChain, int nRainbowChainCount, uint64 nIndex); - void SearchTableChunk(RainbowChainCP* pChain, int nRainbowChainLen, int nRainbowChainCount, CHashSet& hs); - bool CheckAlarm(RainbowChainCP* pChain, int nGuessedPos, unsigned char* pHash, CHashSet& hs); -void GetChainIndexRangeWithSameEndpoint(RainbowChainCP* pChain, - int nRainbowChainCount, - int nMatchingIndexE, - int& nMatchingIndexEFrom, - int& nMatchingIndexETo); -public: - void SearchRainbowTable(string sPathName, CHashSet& hs); - void Run(vector vPathName, CHashSet& hs); - float GetStatTotalDiskAccessTime(); - float GetStatTotalCryptanalysisTime(); - int GetStatTotalChainWalkStep(); - int GetStatTotalFalseAlarm(); - int GetStatTotalChainWalkStepDueToFalseAlarm(); -}; - -#endif diff --git a/Client Applications/rcracki/HashSet.cpp b/Client Applications/rcracki/HashSet.cpp deleted file mode 100644 index 5b15661..0000000 --- a/Client Applications/rcracki/HashSet.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786) -#endif - -#include "HashSet.h" - -CHashSet::CHashSet() -{ -} - -CHashSet::~CHashSet() -{ -} - -void CHashSet::AddHash(string sHash) -{ - if (sHash == "aad3b435b51404ee") - return; - - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (m_vHash[i] == sHash) - return; - } - - //printf("debug: adding hash %s\n", sHash.c_str()); - - m_vHash.push_back(sHash); - m_vFound.push_back(false); - m_vPlain.push_back(""); - m_vBinary.push_back(""); -} - -bool CHashSet::AnyhashLeft() -{ - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (!m_vFound[i]) - return true; - } - - return false; -} - -bool CHashSet::AnyHashLeftWithLen(int nLen) -{ - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (!m_vFound[i]) - if (m_vHash[i].size() == nLen * 2) - return true; - } - - return false; -} - -void CHashSet::GetLeftHashWithLen(vector& vHash, int nLen) -{ - vHash.clear(); - - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (!m_vFound[i]) - if (m_vHash[i].size() == nLen * 2) - vHash.push_back(m_vHash[i]); - } -} - -void CHashSet::SetPlain(string sHash, string sPlain, string sBinary) -{ - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (m_vHash[i] == sHash) - { - m_vFound[i] = true; - m_vPlain[i] = sPlain; - m_vBinary[i] = sBinary; - return; - } - } -} - -bool CHashSet::GetPlain(string sHash, string& sPlain, string& sBinary) -{ - if (sHash == "aad3b435b51404ee") - { - sPlain = ""; - sBinary = ""; - return true; - } - - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (m_vHash[i] == sHash) - { - if (m_vFound[i]) - { - sPlain = m_vPlain[i]; - sBinary = m_vBinary[i]; - return true; - } - } - } - - return false; -} - -int CHashSet::GetStatHashFound() -{ - int nHashFound = 0; - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (m_vFound[i]) - nHashFound++; - } - - return nHashFound; -} - -int CHashSet::GetStatHashTotal() -{ - return m_vHash.size(); -} diff --git a/Client Applications/rcracki/HashSet.h b/Client Applications/rcracki/HashSet.h deleted file mode 100644 index 38b5f42..0000000 --- a/Client Applications/rcracki/HashSet.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifndef _HASHSET_H -#define _HASHSET_H - -#include "Public.h" - -class CHashSet -{ -public: - CHashSet(); - virtual ~CHashSet(); - -private: - vector m_vHash; - vector m_vFound; - vector m_vPlain; - vector m_vBinary; - -public: - void AddHash(string sHash); // lowercase, len % 2 == 0, MIN_HASH_LEN * 2 <= len <= MAX_HASH_LEN * 2 - bool AnyhashLeft(); - bool AnyHashLeftWithLen(int nLen); - void GetLeftHashWithLen(vector& vHash, int nLen); - - void SetPlain(string sHash, string sPlain, string sBinary); - bool GetPlain(string sHash, string& sPlain, string& sBinary); - - int GetStatHashFound(); - int GetStatHashTotal(); -}; - -#endif diff --git a/Client Applications/rcracki/RainbowCrack.cpp b/Client Applications/rcracki/RainbowCrack.cpp deleted file mode 100644 index a6ec66e..0000000 --- a/Client Applications/rcracki/RainbowCrack.cpp +++ /dev/null @@ -1,401 +0,0 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei - - Modified by Martin Westergaard Jørgensen to support indexed and hybrid tables -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786) -#endif - -#include "CrackEngine.h" - -#ifdef _WIN32 - #include -#else - #include - #include - #include -#endif -#include "md4.h" - -////////////////////////////////////////////////////////////////////// - -#ifdef _WIN32 -void GetTableList(string sWildCharPathName, vector& vPathName) -{ - vPathName.clear(); - - string sPath; - int n = sWildCharPathName.find_last_of('\\'); - if (n != -1) - sPath = sWildCharPathName.substr(0, n + 1); - - _finddata_t fd; - long handle = _findfirst(sWildCharPathName.c_str(), &fd); - if (handle != -1) - { - do - { - string sName = fd.name; - if (sName != "." && sName != ".." && !(fd.attrib & _A_SUBDIR)) - { - string sPathName = sPath + sName; - vPathName.push_back(sPathName); - } - } while (_findnext(handle, &fd) == 0); - - _findclose(handle); - } -} -#else -void GetTableList(int argc, char* argv[], vector& vPathName) -{ - vPathName.clear(); - - int i; - for (i = 1; i <= argc - 3; i++) - { - string sPathName = argv[i]; - - struct stat buf; - if (lstat(sPathName.c_str(), &buf) == 0) - { - if (S_ISREG(buf.st_mode)) - vPathName.push_back(sPathName); - } - } -} -#endif - -bool NormalizeHash(string& sHash) -{ - string sNormalizedHash = sHash; - - if ( sNormalizedHash.size() % 2 != 0 - || sNormalizedHash.size() < MIN_HASH_LEN * 2 - || sNormalizedHash.size() > MAX_HASH_LEN * 2) - return false; - - // Make lower - int i; - for (i = 0; i < sNormalizedHash.size(); i++) - { - if (sNormalizedHash[i] >= 'A' && sNormalizedHash[i] <= 'F') - sNormalizedHash[i] = sNormalizedHash[i] - 'A' + 'a'; - } - - // Character check - for (i = 0; i < sNormalizedHash.size(); i++) - { - if ( (sNormalizedHash[i] < 'a' || sNormalizedHash[i] > 'f') - && (sNormalizedHash[i] < '0' || sNormalizedHash[i] > '9')) - return false; - } - - sHash = sNormalizedHash; - return true; -} - -void LoadLMHashFromPwdumpFile(string sPathName, vector& vUserName, vector& vLMHash, vector& vNTLMHash) -{ - vector vLine; - if (ReadLinesFromFile(sPathName, vLine)) - { - int i; - for (i = 0; i < vLine.size(); i++) - { - vector vPart; - if (SeperateString(vLine[i], "::::", vPart)) - { - string sUserName = vPart[0]; - string sLMHash = vPart[2]; - string sNTLMHash = vPart[3]; - - if (sLMHash.size() == 32 && sNTLMHash.size() == 32) - { - if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash)) - { - vUserName.push_back(sUserName); - vLMHash.push_back(sLMHash); - vNTLMHash.push_back(sNTLMHash); - } - else - printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str()); - } - } - } - } - else - printf("can't open %s\n", sPathName.c_str()); -} - -bool NTLMPasswordSeek(unsigned char* pLMPassword, int nLMPasswordLen, int nLMPasswordNext, - unsigned char* pNTLMHash, string& sNTLMPassword) -{ - if (nLMPasswordNext == nLMPasswordLen) - { - unsigned char md[16]; - MD4_NEW(pLMPassword, nLMPasswordLen * 2, md); - if (memcmp(md, pNTLMHash, 16) == 0) - { - sNTLMPassword = ""; - int i; - for (i = 0; i < nLMPasswordLen; i++) - sNTLMPassword += char(pLMPassword[i * 2]); - return true; - } - else - return false; - } - - if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword)) - return true; - - if ( pLMPassword[nLMPasswordNext * 2] >= 'A' - && pLMPassword[nLMPasswordNext * 2] <= 'Z') - { - pLMPassword[nLMPasswordNext * 2] = pLMPassword[nLMPasswordNext * 2] - 'A' + 'a'; - if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword)) - return true; - pLMPassword[nLMPasswordNext * 2] = pLMPassword[nLMPasswordNext * 2] - 'a' + 'A'; - } - - return false; -} - -bool LMPasswordCorrectCase(string sLMPassword, - unsigned char* pNTLMHash, string& sNTLMPassword) -{ - if (sLMPassword.size() == 0) - { - sNTLMPassword = ""; - return true; - } - - unsigned char* pLMPassword = new unsigned char[sLMPassword.size() * 2]; - int i; - for (i = 0; i < sLMPassword.size(); i++) - { - pLMPassword[i * 2 ] = sLMPassword[i]; - pLMPassword[i * 2 + 1] = 0x00; - } - bool fRet = NTLMPasswordSeek(pLMPassword, sLMPassword.size(), 0, pNTLMHash, sNTLMPassword); - delete pLMPassword; - - return fRet; -} - -void Usage() -{ - Logo(); - - printf("usage: rcracki rainbow_table_pathname -h hash\n"); - printf(" rcracki rainbow_table_pathname -l hash_list_file\n"); - printf(" rcracki rainbow_table_pathname -f pwdump_file\n"); - printf("rainbow_table_pathname: pathname of the rainbow table(s), wildchar(*, ?) supported\n"); - printf("-h hash: use raw hash as input\n"); - printf("-l hash_list_file: use hash list file as input, each hash in a line\n"); - printf("-f pwdump_file: use pwdump file as input, this will handle lanmanager hash only\n"); - printf("\n"); - printf("example: rcracki *.rti -h 5d41402abc4b2a76b9719d911017c592\n"); - printf(" rcracki *.rti -l hash.txt\n"); - printf(" rcracki *.rti -f hash.txt\n"); -} - -int main(int argc, char* argv[]) -{ -#ifdef _WIN32 - if (argc != 4) - { - Usage(); - return 0; - } - string sWildCharPathName = argv[1]; - string sInputType = argv[2]; - string sInput = argv[3]; - - // vPathName - vector vPathName; - GetTableList(sWildCharPathName, vPathName); -#else - if (argc < 4) - { - Usage(); - return 0; - } - string sInputType = argv[argc - 2]; - string sInput = argv[argc - 1]; - - // vPathName - vector vPathName; - GetTableList(argc, argv, vPathName); -#endif - if (vPathName.size() == 0) - { - printf("no rainbow table found\n"); - return 0; - } - - // fCrackerType, vHash, vUserName, vLMHash - bool fCrackerType; // true: hash cracker, false: lm cracker - vector vHash; // hash cracker - vector vUserName; // lm cracker - vector vLMHash; // lm cracker - vector vNTLMHash; // lm cracker - if (sInputType == "-h") - { - fCrackerType = true; - - string sHash = sInput; - if (NormalizeHash(sHash)) - vHash.push_back(sHash); - else - printf("invalid hash: %s\n", sHash.c_str()); - } - else if (sInputType == "-l") - { - fCrackerType = true; - - string sPathName = sInput; - vector vLine; - if (ReadLinesFromFile(sPathName, vLine)) - { - int i; - for (i = 0; i < vLine.size(); i++) - { - string sHash = vLine[i]; - if (NormalizeHash(sHash)) - vHash.push_back(sHash); - else - printf("invalid hash: %s\n", sHash.c_str()); - } - } - else - printf("can't open %s\n", sPathName.c_str()); - } - else if (sInputType == "-f") - { - fCrackerType = false; - - string sPathName = sInput; - LoadLMHashFromPwdumpFile(sPathName, vUserName, vLMHash, vNTLMHash); - } - else - { - Usage(); - return 0; - } - - if (fCrackerType && vHash.size() == 0) - return 0; - if (!fCrackerType && vLMHash.size() == 0) - return 0; - - // hs - CHashSet hs; - if (fCrackerType) - { - int i; - for (i = 0; i < vHash.size(); i++) - hs.AddHash(vHash[i]); - } - else - { - int i; - for (i = 0; i < vLMHash.size(); i++) - { - hs.AddHash(vLMHash[i].substr(0, 16)); - hs.AddHash(vLMHash[i].substr(16, 16)); - } - } - - // Run - CCrackEngine ce; - ce.Run(vPathName, hs); - - // Statistics - printf("statistics\n"); - printf("-------------------------------------------------------\n"); - printf("plaintext found: %d of %d (%.2f%%)\n", hs.GetStatHashFound(), - hs.GetStatHashTotal(), - 100.0f * hs.GetStatHashFound() / hs.GetStatHashTotal()); - printf("total disk access time: %.2f s\n", ce.GetStatTotalDiskAccessTime()); - printf("total cryptanalysis time: %.2f s\n", ce.GetStatTotalCryptanalysisTime()); - printf("total chain walk step: %d\n", ce.GetStatTotalChainWalkStep()); - printf("total false alarm: %d\n", ce.GetStatTotalFalseAlarm()); - printf("total chain walk step due to false alarm: %d\n", ce.GetStatTotalChainWalkStepDueToFalseAlarm()); -// printf("total chain walk step skipped due to checkpoints: %d\n", ce.GetStatTotalFalseAlarmSkipped()); // Checkpoints not used - yet - printf("\n"); - - // Result - printf("result\n"); - printf("-------------------------------------------------------\n"); - if (fCrackerType) - { - int i; - for (i = 0; i < vHash.size(); i++) - { - string sPlain, sBinary; - if (!hs.GetPlain(vHash[i], sPlain, sBinary)) - { - sPlain = ""; - sBinary = ""; - } - - printf("%s %s hex:%s\n", vHash[i].c_str(), sPlain.c_str(), sBinary.c_str()); - } - } - else - { - int i; - for (i = 0; i < vLMHash.size(); i++) - { - string sPlain1, sBinary1; - bool fPart1Found = hs.GetPlain(vLMHash[i].substr(0, 16), sPlain1, sBinary1); - if (!fPart1Found) - { - sPlain1 = ""; - sBinary1 = ""; - } - - string sPlain2, sBinary2; - bool fPart2Found = hs.GetPlain(vLMHash[i].substr(16, 16), sPlain2, sBinary2); - if (!fPart2Found) - { - sPlain2 = ""; - sBinary2 = ""; - } - - string sPlain = sPlain1 + sPlain2; - string sBinary = sBinary1 + sBinary2; - - // Correct case - if (fPart1Found && fPart2Found) - { - unsigned char NTLMHash[16]; - int nHashLen; - ParseHash(vNTLMHash[i], NTLMHash, nHashLen); - if (nHashLen != 16) - printf("debug: nHashLen mismatch\n"); - string sNTLMPassword; - if (LMPasswordCorrectCase(sPlain, NTLMHash, sNTLMPassword)) - { - sPlain = sNTLMPassword; - sBinary = HexToStr((const unsigned char*)sNTLMPassword.c_str(), sNTLMPassword.size()); - } - else - printf("case correction for password %s fail!\n", sPlain.c_str()); - } - - // Display - printf("%-14s %s hex:%s\n", vUserName[i].c_str(), - sPlain.c_str(), - sBinary.c_str()); - } - } - - return 0; -} diff --git a/Client Applications/rcracki/charset.txt b/Client Applications/rcracki/charset.txt deleted file mode 100644 index d1e0179..0000000 --- a/Client Applications/rcracki/charset.txt +++ /dev/null @@ -1,61 +0,0 @@ -# charset configuration file for DistrRTgen v3.2 by Martin Westergaard (martinwj2005@gmail.com) - -byte = [] -alpha = [ABCDEFGHIJKLMNOPQRSTUVWXYZ] -alpha-space = [ABCDEFGHIJKLMNOPQRSTUVWXYZ ] -alpha-numeric = [ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789] -alpha-numeric-space = [ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ] -alpha-numeric-symbol14 = [ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=] -alpha-numeric-symbol14-space= [ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+= ] -all = [ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}|\:;"'<>,.?/] -all-space = [ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}|\:;"'<>,.?/ ] -alpha-numeric-symbol32-space = [ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}|\:;"'<>,.?/ ] -lm-frt-cp437 = [ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`{|}~€Ž’™š›œžŸ¥àáâãäæçèéêëî] -lm-frt-cp850 = [ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`{|}~€Ž’™šœŸ¥µ¶·½¾ÇÏÑÒÓÔÕÖ×ØÞàáâãåæèéêëíï] -lm-frt-cp437-850 = [ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`{|}~€Ž’™š›œžŸ¥µ¶·½¾ÇÏÑÒÓÔÕÖ×ØÞàáâãäåæçèéêëíîï] - -numeric = [0123456789] -numeric-space = [0123456789 ] -loweralpha = [abcdefghijklmnopqrstuvwxyz] -loweralpha-space = [abcdefghijklmnopqrstuvwxyz ] -loweralpha-numeric = [abcdefghijklmnopqrstuvwxyz0123456789] -loweralpha-numeric-space = [abcdefghijklmnopqrstuvwxyz0123456789 ] -loweralpha-numeric-symbol14 = [abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_+=] -loweralpha-numeric-all = [abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_+=~`[]{}|\:;"'<>,.?/] -loweralpha-numeric-symbol32-space= [abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_+=~`[]{}|\:;"'<>,.?/ ] - -mixalpha = [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ] -mixalpha-space = [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ] -mixalpha-numeric = [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789] -mixalpha-numeric-space = [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ] -mixalpha-numeric-symbol14 = [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=] -mixalpha-numeric-all = [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}|\:;"'<>,.?/] -mixalpha-numeric-symbol32-space = [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}|\:;"'<>,.?/ ] -mixalpha-numeric-all-space = [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}|\:;"'<>,.?/ ] - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Client Applications/rcracki/rcracki.sln b/Client Applications/rcracki/rcracki.sln deleted file mode 100644 index 18468b3..0000000 --- a/Client Applications/rcracki/rcracki.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rcracki", "rcracki.vcproj", "{966DA4B4-E13C-449D-9A93-303C6FEA25C4}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {966DA4B4-E13C-449D-9A93-303C6FEA25C4}.Debug|Win32.ActiveCfg = Debug|Win32 - {966DA4B4-E13C-449D-9A93-303C6FEA25C4}.Debug|Win32.Build.0 = Debug|Win32 - {966DA4B4-E13C-449D-9A93-303C6FEA25C4}.Release|Win32.ActiveCfg = Release|Win32 - {966DA4B4-E13C-449D-9A93-303C6FEA25C4}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/Client Applications/rcracki/rcracki.suo b/Client Applications/rcracki/rcracki.suo deleted file mode 100644 index 7159e215ea203d92e6338daa7b00382979c17913..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63488 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Client Applications/rcracki_mt/BaseRTReader.cpp b/Client Applications/rcracki_mt/BaseRTReader.cpp index d762e26..cae94f8 100644 --- a/Client Applications/rcracki_mt/BaseRTReader.cpp +++ b/Client Applications/rcracki_mt/BaseRTReader.cpp @@ -1,2 +1,25 @@ -#include "BaseRTReader.h" +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright 2010 Martin Westergaard Jørgensen + * Copyright 2010 Daniël Niggebrugge + * Copyright 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ +#include "BaseRTReader.h" diff --git a/Client Applications/rcracki_mt/BaseRTReader.h b/Client Applications/rcracki_mt/BaseRTReader.h index 4dbaf86..b95f930 100644 --- a/Client Applications/rcracki_mt/BaseRTReader.h +++ b/Client Applications/rcracki_mt/BaseRTReader.h @@ -1,11 +1,37 @@ +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright 2010 Martin Westergaard Jørgensen + * Copyright 2010 Daniël Niggebrugge + * Copyright 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + #ifndef __BASERTREADER_H__ #define __BASERTREADER_H__ #include "Public.h" #include -#ifdef WIN32 -#include + +#if defined(_WIN32) && !defined(__GNUC__) + #include #endif + using namespace std; class BaseRTReader diff --git a/Client Applications/rcracki_mt/ChainWalkContext.cpp b/Client Applications/rcracki_mt/ChainWalkContext.cpp index f6e24b3..1158dc6 100644 --- a/Client Applications/rcracki_mt/ChainWalkContext.cpp +++ b/Client Applications/rcracki_mt/ChainWalkContext.cpp @@ -1,612 +1,629 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786 4267 4018) -#endif - -#include "ChainWalkContext.h" - -#include - -////////////////////////////////////////////////////////////////////// - -string CChainWalkContext::m_sHashRoutineName; -HASHROUTINE CChainWalkContext::m_pHashRoutine; -int CChainWalkContext::m_nHashLen; -int CChainWalkContext::m_nPlainLenMinTotal = 0; -int CChainWalkContext::m_nPlainLenMaxTotal = 0; -int CChainWalkContext::m_nHybridCharset = 0; -bool CChainWalkContext::isOldRtFormat = false; -bool CChainWalkContext::isNewRtFormat = false; -vector CChainWalkContext::m_vCharset; -uint64 CChainWalkContext::m_nPlainSpaceUpToX[MAX_PLAIN_LEN + 1]; -uint64 CChainWalkContext::m_nPlainSpaceTotal; -unsigned char CChainWalkContext::m_Salt[MAX_SALT_LEN]; -int CChainWalkContext::m_nSaltLen = 0; -int CChainWalkContext::m_nRainbowTableIndex; -uint64 CChainWalkContext::m_nReduceOffset; - -////////////////////////////////////////////////////////////////////// - -CChainWalkContext::CChainWalkContext() -{ -} - -CChainWalkContext::~CChainWalkContext() -{ -} - -bool CChainWalkContext::LoadCharset(string sName) -{ - m_vCharset.clear(); - if (sName == "byte") - { - stCharset tCharset; - int i; - for (i = 0x00; i <= 0xff; i++) - tCharset.m_PlainCharset[i] = i; - tCharset.m_nPlainCharsetLen = 256; - tCharset.m_sPlainCharsetName = sName; - tCharset.m_sPlainCharsetContent = "0x00, 0x01, ... 0xff"; - m_vCharset.push_back(tCharset); - return true; - } - if(sName.substr(0, 6) == "hybrid") // Hybrid charset consisting of 2 charsets - { - m_nHybridCharset = 1; - } - else - { - m_nHybridCharset = 0; - } - - bool readCharset = false; - vector vLine; - if (ReadLinesFromFile("charset.txt", vLine)) { - readCharset = true; - } - else if (ReadLinesFromFile(GetApplicationPath() + "charset.txt", vLine)) { - readCharset = true; - } - if (readCharset) - { - int i; - for (i = 0; i < vLine.size(); i++) - { - // Filter comment - if (vLine[i][0] == '#') - continue; - - vector vPart; - if (SeperateString(vLine[i], "=", vPart)) - { - // sCharsetName - string sCharsetName = TrimString(vPart[0]); - if (sCharsetName == "") - continue; - - // sCharsetName charset check - bool fCharsetNameCheckPass = true; - int j; - for (j = 0; j < sCharsetName.size(); j++) - { - if ( !isalpha(sCharsetName[j]) - && !isdigit(sCharsetName[j]) - && (sCharsetName[j] != '-')) - { - fCharsetNameCheckPass = false; - break; - } - } - if (!fCharsetNameCheckPass) - { - printf("invalid charset name %s in charset configuration file\n", sCharsetName.c_str()); - continue; - } - - // sCharsetContent - string sCharsetContent = TrimString(vPart[1]); - if (sCharsetContent == "" || sCharsetContent == "[]") - continue; - if (sCharsetContent[0] != '[' || sCharsetContent[sCharsetContent.size() - 1] != ']') - { - printf("invalid charset content %s in charset configuration file\n", sCharsetContent.c_str()); - continue; - } - sCharsetContent = sCharsetContent.substr(1, sCharsetContent.size() - 2); - if (sCharsetContent.size() > 256) - { - printf("charset content %s too long\n", sCharsetContent.c_str()); - continue; - } - - //printf("%s = [%s]\n", sCharsetName.c_str(), sCharsetContent.c_str()); - - // Is it the wanted charset? - if(m_nHybridCharset == 1) - { - vector vCharsets; - GetHybridCharsets(sName, vCharsets); - if(sCharsetName == vCharsets[m_vCharset.size()].sName) - { - stCharset tCharset = {0}; - tCharset.m_nPlainCharsetLen = sCharsetContent.size(); - memcpy(tCharset.m_PlainCharset, sCharsetContent.c_str(), tCharset.m_nPlainCharsetLen); - tCharset.m_sPlainCharsetName = sCharsetName; - tCharset.m_sPlainCharsetContent = sCharsetContent; - tCharset.m_nPlainLenMin = vCharsets[m_vCharset.size()].nPlainLenMin; - tCharset.m_nPlainLenMax = vCharsets[m_vCharset.size()].nPlainLenMax; - m_vCharset.push_back(tCharset); - if(vCharsets.size() == m_vCharset.size()) - return true; - i = 0; // Start the lookup over again for the next charset - } - } - else if (sCharsetName == sName) - { - stCharset tCharset; - tCharset.m_nPlainCharsetLen = sCharsetContent.size(); - memcpy(tCharset.m_PlainCharset, sCharsetContent.c_str(), tCharset.m_nPlainCharsetLen); - tCharset.m_sPlainCharsetName = sCharsetName; - tCharset.m_sPlainCharsetContent = sCharsetContent; - m_vCharset.push_back(tCharset); - return true; - } - } - } - printf("charset %s not found in charset.txt\n", sName.c_str()); - } - else - printf("can't open charset configuration file\n"); - return false; -} - -////////////////////////////////////////////////////////////////////// - -bool CChainWalkContext::SetHashRoutine(string sHashRoutineName) -{ - CHashRoutine hr; - hr.GetHashRoutine(sHashRoutineName, m_pHashRoutine, m_nHashLen); - if (m_pHashRoutine != NULL) - { - m_sHashRoutineName = sHashRoutineName; - return true; - } - else - return false; -} - -bool CChainWalkContext::SetPlainCharset(string sCharsetName, int nPlainLenMin, int nPlainLenMax) -{ - // m_PlainCharset, m_nPlainCharsetLen, m_sPlainCharsetName, m_sPlainCharsetContent - if (!LoadCharset(sCharsetName)) - return false; - - if(m_vCharset.size() == 1) // Not hybrid charset - { - // m_nPlainLenMin, m_nPlainLenMax - if (nPlainLenMin < 1 || nPlainLenMax > MAX_PLAIN_LEN || nPlainLenMin > nPlainLenMax) - { - printf("invalid plaintext length range: %d - %d\n", nPlainLenMin, nPlainLenMax); - return false; - } - m_vCharset[0].m_nPlainLenMin = nPlainLenMin; - m_vCharset[0].m_nPlainLenMax = nPlainLenMax; - } - // m_nPlainSpaceUpToX - m_nPlainSpaceUpToX[0] = 0; - m_nPlainLenMaxTotal = 0; - m_nPlainLenMinTotal = 0; - uint64 nTemp = 1; - int j, k = 1; - for(j = 0; j < m_vCharset.size(); j++) - { - int i; - m_nPlainLenMaxTotal += m_vCharset[j].m_nPlainLenMax; - m_nPlainLenMinTotal += m_vCharset[j].m_nPlainLenMin; - for (i = 1; i <= m_vCharset[j].m_nPlainLenMax; i++) - { - nTemp *= m_vCharset[j].m_nPlainCharsetLen; - if (i < m_vCharset[j].m_nPlainLenMin) - m_nPlainSpaceUpToX[k] = 0; - else - m_nPlainSpaceUpToX[k] = m_nPlainSpaceUpToX[k - 1] + nTemp; - k++; - } - } - // m_nPlainSpaceTotal - m_nPlainSpaceTotal = m_nPlainSpaceUpToX[m_nPlainLenMaxTotal]; - - return true; -} - -bool CChainWalkContext::SetRainbowTableIndex(int nRainbowTableIndex) -{ - if (nRainbowTableIndex < 0) - return false; - m_nRainbowTableIndex = nRainbowTableIndex; - m_nReduceOffset = 65536 * nRainbowTableIndex; - - return true; -} - -bool CChainWalkContext::SetSalt(unsigned char *Salt, int nSaltLength) -{ - memcpy(&m_Salt[0], Salt, nSaltLength); - - m_nSaltLen = nSaltLength; -// m_sSalt = sSalt; - return true; -} - -bool CChainWalkContext::SetupWithPathName(string sPathName, int& nRainbowChainLen, int& nRainbowChainCount) -{ - // something like lm_alpha#1-7_0_100x16_test.rt - -#ifdef _WIN32 - int nIndex = sPathName.find_last_of('\\'); -#else - int nIndex = sPathName.find_last_of('/'); -#endif - if (nIndex != -1) - sPathName = sPathName.substr(nIndex + 1); - - if (sPathName.size() < 3) - { - printf("%s is not a rainbow table\n", sPathName.c_str()); - return false; - } - if (sPathName.substr(sPathName.size() - 5) == ".rti2") - { - isNewRtFormat = true; - } - else if (sPathName.substr(sPathName.size() - 4) == ".rti") - { - isOldRtFormat = false; - } - else if (sPathName.substr(sPathName.size() - 3) == ".rt") - { - isOldRtFormat = true; - } - else - { - printf("%s is not a rainbow table\n", sPathName.c_str()); - return false; - } - - // Parse - vector vPart; - if (!SeperateString(sPathName, "___x_", vPart)) - { - printf("filename %s not identified\n", sPathName.c_str()); - return false; - } - - string sHashRoutineName = vPart[0]; - int nRainbowTableIndex = atoi(vPart[2].c_str()); - nRainbowChainLen = atoi(vPart[3].c_str()); - nRainbowChainCount = atoi(vPart[4].c_str()); - - // Parse charset definition - string sCharsetDefinition = vPart[1]; - string sCharsetName; - int nPlainLenMin = 0, nPlainLenMax = 0; - -// printf("Charset: %s", sCharsetDefinition.c_str()); - - if(sCharsetDefinition.substr(0, 6) == "hybrid") // Hybrid table - { - sCharsetName = sCharsetDefinition; - } - else - { - if (sCharsetDefinition.find('#') == -1) // For backward compatibility, "#1-7" is implied - { - sCharsetName = sCharsetDefinition; - nPlainLenMin = 1; - nPlainLenMax = 7; - } - else - { - vector vCharsetDefinitionPart; - if (!SeperateString(sCharsetDefinition, "#-", vCharsetDefinitionPart)) - { - printf("filename %s not identified\n", sPathName.c_str()); - return false; - } - else - { - sCharsetName = vCharsetDefinitionPart[0]; - nPlainLenMin = atoi(vCharsetDefinitionPart[1].c_str()); - nPlainLenMax = atoi(vCharsetDefinitionPart[2].c_str()); - } - } - } - // Setup - if (!SetHashRoutine(sHashRoutineName)) - { - printf("hash routine %s not supported\n", sHashRoutineName.c_str()); - return false; - } - if (!SetPlainCharset(sCharsetName, nPlainLenMin, nPlainLenMax)) - return false; - if (!SetRainbowTableIndex(nRainbowTableIndex)) - { - printf("invalid rainbow table index %d\n", nRainbowTableIndex); - return false; - } - m_nPlainSpaceTotal = m_nPlainSpaceUpToX[m_nPlainLenMaxTotal]; - return true; -} - -string CChainWalkContext::GetHashRoutineName() -{ - return m_sHashRoutineName; -} - -int CChainWalkContext::GetHashLen() -{ - return m_nHashLen; -} - -string CChainWalkContext::GetPlainCharsetName() -{ - return m_vCharset[0].m_sPlainCharsetName; -} - -string CChainWalkContext::GetPlainCharsetContent() -{ - return m_vCharset[0].m_sPlainCharsetContent; -} - -int CChainWalkContext::GetPlainLenMin() -{ - return m_vCharset[0].m_nPlainLenMin; -} - -int CChainWalkContext::GetPlainLenMax() -{ - return m_vCharset[0].m_nPlainLenMax; -} - -uint64 CChainWalkContext::GetPlainSpaceTotal() -{ - return m_nPlainSpaceTotal; -} - -int CChainWalkContext::GetRainbowTableIndex() -{ - return m_nRainbowTableIndex; -} - -void CChainWalkContext::Dump() -{ - printf("hash routine: %s\n", m_sHashRoutineName.c_str()); - printf("hash length: %d\n", m_nHashLen); - - printf("plain charset: "); - int i; - for (i = 0; i < m_vCharset[0].m_nPlainCharsetLen; i++) - { - if (isprint(m_vCharset[0].m_PlainCharset[i])) - printf("%c", m_vCharset[0].m_PlainCharset[i]); - else - printf("?"); - } - printf("\n"); - - printf("plain charset in hex: "); - for (i = 0; i < m_vCharset[0].m_nPlainCharsetLen; i++) - printf("%02x ", m_vCharset[0].m_PlainCharset[i]); - printf("\n"); - - printf("plain length range: %d - %d\n", m_vCharset[0].m_nPlainLenMin, m_vCharset[0].m_nPlainLenMax); - printf("plain charset name: %s\n", m_vCharset[0].m_sPlainCharsetName.c_str()); - //printf("plain charset content: %s\n", m_sPlainCharsetContent.c_str()); - //for (i = 0; i <= m_nPlainLenMax; i++) - // printf("plain space up to %d: %s\n", i, uint64tostr(m_nPlainSpaceUpToX[i]).c_str()); - printf("plain space total: %s\n", uint64tostr(m_nPlainSpaceTotal).c_str()); - - printf("rainbow table index: %d\n", m_nRainbowTableIndex); - printf("reduce offset: %s\n", uint64tostr(m_nReduceOffset).c_str()); - printf("\n"); -} - - -void CChainWalkContext::SetIndex(uint64 nIndex) -{ - m_nIndex = nIndex; -} - -void CChainWalkContext::SetHash(unsigned char* pHash) -{ - memcpy(m_Hash, pHash, m_nHashLen); -} - -void CChainWalkContext::IndexToPlain() -{ - int i; - m_nPlainLen = 0; - for (i = m_nPlainLenMaxTotal - 1; i >= m_nPlainLenMinTotal - 1; i--) - { - if (m_nIndex >= m_nPlainSpaceUpToX[i]) - { - m_nPlainLen = i + 1; - break; - } - } - if(m_nPlainLen == 0) - m_nPlainLen = m_nPlainLenMinTotal; - uint64 nIndexOfX = m_nIndex - m_nPlainSpaceUpToX[m_nPlainLen - 1]; - -// maybe this code should be used for some other 64 bit systems as well, added check for LP64 to try this -#if defined(_WIN64) || defined(_LP64) - - // Slow version - for (i = m_nPlainLen - 1; i >= 0; i--) - { - int nCharsetLen = 0; - for(int j = 0; j < m_vCharset.size(); i++) - { - nCharsetLen += m_vCharset[j].m_nPlainLenMax; - if(i < nCharsetLen) // We found the correct charset - { - m_Plain[i] = m_vCharset[j].m_PlainCharset[nIndexOfX % m_nPlainCharsetLen]; - nIndexOfX /= m_vCharset[j].m_nPlainCharsetLen; - } - } - } -#else - - - // Fast version - for (i = m_nPlainLen - 1; i >= 0; i--) - { -#ifdef _WIN32 - if (nIndexOfX < 0x100000000I64) - break; -#else - if (nIndexOfX < 0x100000000llu) - break; -#endif - int nCharsetLen = 0; - for(int j = 0; j < m_vCharset.size(); j++) - { - nCharsetLen += m_vCharset[j].m_nPlainLenMax; - if(i < nCharsetLen) // We found the correct charset - { - m_Plain[i] = m_vCharset[j].m_PlainCharset[nIndexOfX % m_vCharset[j].m_nPlainCharsetLen]; - nIndexOfX /= m_vCharset[j].m_nPlainCharsetLen; - break; - } - } - } - - unsigned int nIndexOfX32 = (unsigned int)nIndexOfX; - for (; i >= 0; i--) - { - int nCharsetLen = 0; - for(int j = 0; j < m_vCharset.size(); j++) - { - nCharsetLen += m_vCharset[j].m_nPlainLenMax; - if(i < nCharsetLen) // We found the correct charset - { - -// m_Plain[i] = m_PlainCharset[nIndexOfX32 % m_vCharset[j].m_nPlainCharsetLen]; -// nIndexOfX32 /= m_vCharset[j].m_nPlainCharsetLen; - - unsigned int nPlainCharsetLen = m_vCharset[j].m_nPlainCharsetLen; - unsigned int nTemp; -#ifdef _WIN32 - __asm - { - mov eax, nIndexOfX32 - xor edx, edx - div nPlainCharsetLen - mov nIndexOfX32, eax - mov nTemp, edx - } - m_Plain[i] = m_vCharset[j].m_PlainCharset[nTemp]; -#else - __asm__ __volatile__ ( "mov %2, %%eax;" - "xor %%edx, %%edx;" - "divl %3;" - "mov %%eax, %0;" - "mov %%edx, %1;" - : "=m"(nIndexOfX32), "=m"(nTemp) - : "m"(nIndexOfX32), "m"(nPlainCharsetLen) - : "%eax", "%edx" - ); - m_Plain[i] = m_vCharset[j].m_PlainCharset[nTemp]; -#endif - break; - } - } - } -#endif -} - -void CChainWalkContext::PlainToHash() -{ - m_pHashRoutine(m_Plain, m_nPlainLen, m_Hash); -} - -void CChainWalkContext::HashToIndex(int nPos) -{ - m_nIndex = (*(uint64*)m_Hash + m_nReduceOffset + nPos) % m_nPlainSpaceTotal; -} - -uint64 CChainWalkContext::GetIndex() -{ - return m_nIndex; -} -const uint64 *CChainWalkContext::GetIndexPtr() -{ - return &m_nIndex; -} - -string CChainWalkContext::GetPlain() -{ - string sRet; - int i; - for (i = 0; i < m_nPlainLen; i++) - { - char c = m_Plain[i]; - //if (c >= 32 && c <= 126) - //if (c >= 32) - sRet += c; - //else - // sRet += '?'; - } - - return sRet; -} - -string CChainWalkContext::GetBinary() -{ - return HexToStr(m_Plain, m_nPlainLen); -} -/* -string CChainWalkContext::GetPlainBinary() -{ - string sRet; - sRet += GetPlain(); - int i; - for (i = 0; i < m_nPlainLenMax - m_nPlainLen; i++) - sRet += ' '; - - sRet += "|"; - - sRet += GetBinary(); - for (i = 0; i < m_nPlainLenMax - m_nPlainLen; i++) - sRet += " "; - - return sRet; -} -*/ -string CChainWalkContext::GetHash() -{ - return HexToStr(m_Hash, m_nHashLen); -} - -bool CChainWalkContext::CheckHash(unsigned char* pHash) -{ - if (memcmp(m_Hash, pHash, m_nHashLen) == 0) - return true; - - return false; -} - -bool CChainWalkContext::isOldFormat() -{ - return isOldRtFormat; -} -bool CChainWalkContext::isNewFormat() -{ - return isNewRtFormat; -} - +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * Copyright 2010 Yngve AAdlandsvik + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#if defined(_WIN32) && !defined(__GNUC__) + #pragma warning(disable : 4786 4267 4018) +#endif + +#include "ChainWalkContext.h" + +#include + +////////////////////////////////////////////////////////////////////// + +string CChainWalkContext::m_sHashRoutineName; +HASHROUTINE CChainWalkContext::m_pHashRoutine; +int CChainWalkContext::m_nHashLen; +int CChainWalkContext::m_nPlainLenMinTotal = 0; +int CChainWalkContext::m_nPlainLenMaxTotal = 0; +int CChainWalkContext::m_nHybridCharset = 0; +bool CChainWalkContext::isOldRtFormat = false; +bool CChainWalkContext::isRti2RtFormat = false; +vector CChainWalkContext::m_vCharset; +uint64 CChainWalkContext::m_nPlainSpaceUpToX[MAX_PLAIN_LEN + 1]; +uint64 CChainWalkContext::m_nPlainSpaceTotal; +unsigned char CChainWalkContext::m_Salt[MAX_SALT_LEN]; +int CChainWalkContext::m_nSaltLen = 0; +int CChainWalkContext::m_nRainbowTableIndex; +uint64 CChainWalkContext::m_nReduceOffset; + +////////////////////////////////////////////////////////////////////// + +CChainWalkContext::CChainWalkContext() +{ +} + +CChainWalkContext::~CChainWalkContext() +{ +} + +bool CChainWalkContext::LoadCharset(string sName) +{ + m_vCharset.clear(); + if (sName == "byte") + { + stCharset tCharset; + int i; + for (i = 0x00; i <= 0xff; i++) + tCharset.m_PlainCharset[i] = (unsigned char) i; + tCharset.m_nPlainCharsetLen = 256; + tCharset.m_sPlainCharsetName = sName; + tCharset.m_sPlainCharsetContent = "0x00, 0x01, ... 0xff"; + m_vCharset.push_back(tCharset); + return true; + } + if(sName.substr(0, 6) == "hybrid") // Hybrid charset consisting of 2 charsets + { + m_nHybridCharset = 1; + } + else + { + m_nHybridCharset = 0; + } + + bool readCharset = false; + vector vLine; + if (ReadLinesFromFile("charset.txt", vLine)) { + readCharset = true; + } + else if (ReadLinesFromFile(GetApplicationPath() + "charset.txt", vLine)) { + readCharset = true; + } + if (readCharset) + { + UINT4 i; + for (i = 0; i < vLine.size(); i++) + { + // Filter comment + if (vLine[i][0] == '#') + continue; + + vector vPart; + if (SeperateString(vLine[i], "=", vPart)) + { + // sCharsetName + string sCharsetName = TrimString(vPart[0]); + if (sCharsetName == "") + continue; + + // sCharsetName charset check + bool fCharsetNameCheckPass = true; + UINT4 j; + for (j = 0; j < sCharsetName.size(); j++) + { + if ( !isalpha(sCharsetName[j]) + && !isdigit(sCharsetName[j]) + && (sCharsetName[j] != '-')) + { + fCharsetNameCheckPass = false; + break; + } + } + if (!fCharsetNameCheckPass) + { + printf("invalid charset name %s in charset configuration file\n", sCharsetName.c_str()); + continue; + } + + // sCharsetContent + string sCharsetContent = TrimString(vPart[1]); + if (sCharsetContent == "" || sCharsetContent == "[]") + continue; + if (sCharsetContent[0] != '[' || sCharsetContent[sCharsetContent.size() - 1] != ']') + { + printf("invalid charset content %s in charset configuration file\n", sCharsetContent.c_str()); + continue; + } + sCharsetContent = sCharsetContent.substr(1, sCharsetContent.size() - 2); + if (sCharsetContent.size() > 256) + { + printf("charset content %s too long\n", sCharsetContent.c_str()); + continue; + } + + //printf("%s = [%s]\n", sCharsetName.c_str(), sCharsetContent.c_str()); + + // Is it the wanted charset? + if(m_nHybridCharset == 1) + { + vector vCharsets; + GetHybridCharsets(sName, vCharsets); + if(sCharsetName == vCharsets[m_vCharset.size()].sName) + { + stCharset tCharset; + tCharset.m_nPlainCharsetLen = sCharsetContent.size(); + memcpy(tCharset.m_PlainCharset, sCharsetContent.c_str(), tCharset.m_nPlainCharsetLen); + tCharset.m_sPlainCharsetName = sCharsetName; + tCharset.m_sPlainCharsetContent = sCharsetContent; + tCharset.m_nPlainLenMin = vCharsets[m_vCharset.size()].nPlainLenMin; + tCharset.m_nPlainLenMax = vCharsets[m_vCharset.size()].nPlainLenMax; + m_vCharset.push_back(tCharset); + if(vCharsets.size() == m_vCharset.size()) + return true; + i = 0; // Start the lookup over again for the next charset + } + } + else if (sCharsetName == sName) + { + stCharset tCharset; + tCharset.m_nPlainCharsetLen = sCharsetContent.size(); + memcpy(tCharset.m_PlainCharset, sCharsetContent.c_str(), tCharset.m_nPlainCharsetLen); + tCharset.m_sPlainCharsetName = sCharsetName; + tCharset.m_sPlainCharsetContent = sCharsetContent; + m_vCharset.push_back(tCharset); + return true; + } + } + } + printf("charset %s not found in charset.txt\n", sName.c_str()); + } + else + printf("can't open charset configuration file\n"); + return false; +} + +////////////////////////////////////////////////////////////////////// + +bool CChainWalkContext::SetHashRoutine(string sHashRoutineName) +{ + CHashRoutine hr; + hr.GetHashRoutine(sHashRoutineName, m_pHashRoutine, m_nHashLen); + if (m_pHashRoutine != NULL) + { + m_sHashRoutineName = sHashRoutineName; + return true; + } + else + return false; +} + +bool CChainWalkContext::SetPlainCharset(string sCharsetName, int nPlainLenMin, int nPlainLenMax) +{ + // m_PlainCharset, m_nPlainCharsetLen, m_sPlainCharsetName, m_sPlainCharsetContent + if (!LoadCharset(sCharsetName)) + return false; + + if(m_vCharset.size() == 1) // Not hybrid charset + { + // m_nPlainLenMin, m_nPlainLenMax + if (nPlainLenMin < 1 || nPlainLenMax > MAX_PLAIN_LEN || nPlainLenMin > nPlainLenMax) + { + printf("invalid plaintext length range: %d - %d\n", nPlainLenMin, nPlainLenMax); + return false; + } + m_vCharset[0].m_nPlainLenMin = nPlainLenMin; + m_vCharset[0].m_nPlainLenMax = nPlainLenMax; + } + // m_nPlainSpaceUpToX + m_nPlainSpaceUpToX[0] = 0; + m_nPlainLenMaxTotal = 0; + m_nPlainLenMinTotal = 0; + uint64 nTemp = 1; + UINT4 j, k = 1; + for(j = 0; j < m_vCharset.size(); j++) + { + int i; + m_nPlainLenMaxTotal += m_vCharset[j].m_nPlainLenMax; + m_nPlainLenMinTotal += m_vCharset[j].m_nPlainLenMin; + for (i = 1; i <= m_vCharset[j].m_nPlainLenMax; i++) + { + nTemp *= m_vCharset[j].m_nPlainCharsetLen; + if (i < m_vCharset[j].m_nPlainLenMin) + m_nPlainSpaceUpToX[k] = 0; + else + m_nPlainSpaceUpToX[k] = m_nPlainSpaceUpToX[k - 1] + nTemp; + k++; + } + } + // m_nPlainSpaceTotal + m_nPlainSpaceTotal = m_nPlainSpaceUpToX[m_nPlainLenMaxTotal]; + + return true; +} + +bool CChainWalkContext::SetRainbowTableIndex(int nRainbowTableIndex) +{ + if (nRainbowTableIndex < 0) + return false; + m_nRainbowTableIndex = nRainbowTableIndex; + m_nReduceOffset = 65536 * nRainbowTableIndex; + + return true; +} + +bool CChainWalkContext::SetSalt(unsigned char *Salt, int nSaltLength) +{ + memcpy(&m_Salt[0], Salt, nSaltLength); + + m_nSaltLen = nSaltLength; +// m_sSalt = sSalt; + return true; +} + +bool CChainWalkContext::SetupWithPathName(string sPathName, int& nRainbowChainLen, int& nRainbowChainCount) +{ + // something like lm_alpha#1-7_0_100x16_test.rt + +#ifdef _WIN32 + int nIndex = sPathName.find_last_of('\\'); +#else + int nIndex = (int) sPathName.find_last_of('/'); +#endif + if (nIndex != -1) + sPathName = sPathName.substr(nIndex + 1); + + if (sPathName.size() < 3) + { + printf("%s is not a rainbow table\n", sPathName.c_str()); + return false; + } + if (sPathName.substr(sPathName.size() - 5) == ".rti2") + { + isRti2RtFormat = true; + } + else if (sPathName.substr(sPathName.size() - 4) == ".rti") + { + isOldRtFormat = false; + } + else if (sPathName.substr(sPathName.size() - 3) == ".rt") + { + isOldRtFormat = true; + } + else + { + printf("%s is not a rainbow table\n", sPathName.c_str()); + return false; + } + + // Parse + vector vPart; + if (!SeperateString(sPathName, "___x_", vPart)) + { + printf("filename %s not identified\n", sPathName.c_str()); + return false; + } + + string sHashRoutineName = vPart[0]; + int nRainbowTableIndex = atoi(vPart[2].c_str()); + nRainbowChainLen = atoi(vPart[3].c_str()); + nRainbowChainCount = atoi(vPart[4].c_str()); + + // Parse charset definition + string sCharsetDefinition = vPart[1]; + string sCharsetName; + int nPlainLenMin = 0, nPlainLenMax = 0; + +// printf("Charset: %s", sCharsetDefinition.c_str()); + + if(sCharsetDefinition.substr(0, 6) == "hybrid") // Hybrid table + { + sCharsetName = sCharsetDefinition; + } + else + { + if (sCharsetDefinition.find('#') == (unsigned long)-1) // For backward compatibility, "#1-7" is implied + { + sCharsetName = sCharsetDefinition; + nPlainLenMin = 1; + nPlainLenMax = 7; + } + else + { + vector vCharsetDefinitionPart; + if (!SeperateString(sCharsetDefinition, "#-", vCharsetDefinitionPart)) + { + printf("filename %s not identified\n", sPathName.c_str()); + return false; + } + else + { + sCharsetName = vCharsetDefinitionPart[0]; + nPlainLenMin = atoi(vCharsetDefinitionPart[1].c_str()); + nPlainLenMax = atoi(vCharsetDefinitionPart[2].c_str()); + } + } + } + // Setup + if (!SetHashRoutine(sHashRoutineName)) + { + printf("hash routine %s not supported\n", sHashRoutineName.c_str()); + return false; + } + if (!SetPlainCharset(sCharsetName, nPlainLenMin, nPlainLenMax)) + return false; + if (!SetRainbowTableIndex(nRainbowTableIndex)) + { + printf("invalid rainbow table index %d\n", nRainbowTableIndex); + return false; + } + m_nPlainSpaceTotal = m_nPlainSpaceUpToX[m_nPlainLenMaxTotal]; + return true; +} + +string CChainWalkContext::GetHashRoutineName() +{ + return m_sHashRoutineName; +} + +int CChainWalkContext::GetHashLen() +{ + return m_nHashLen; +} + +string CChainWalkContext::GetPlainCharsetName() +{ + return m_vCharset[0].m_sPlainCharsetName; +} + +string CChainWalkContext::GetPlainCharsetContent() +{ + return m_vCharset[0].m_sPlainCharsetContent; +} + +int CChainWalkContext::GetPlainLenMin() +{ + return m_vCharset[0].m_nPlainLenMin; +} + +int CChainWalkContext::GetPlainLenMax() +{ + return m_vCharset[0].m_nPlainLenMax; +} + +uint64 CChainWalkContext::GetPlainSpaceTotal() +{ + return m_nPlainSpaceTotal; +} + +int CChainWalkContext::GetRainbowTableIndex() +{ + return m_nRainbowTableIndex; +} + +void CChainWalkContext::Dump() +{ + printf("hash routine: %s\n", m_sHashRoutineName.c_str()); + printf("hash length: %d\n", m_nHashLen); + + printf("plain charset: "); + unsigned int i; + for (i = 0; i < m_vCharset[0].m_nPlainCharsetLen; i++) + { + if (isprint(m_vCharset[0].m_PlainCharset[i])) + printf("%c", m_vCharset[0].m_PlainCharset[i]); + else + printf("?"); + } + printf("\n"); + + printf("plain charset in hex: "); + for (i = 0; i < m_vCharset[0].m_nPlainCharsetLen; i++) + printf("%02x ", m_vCharset[0].m_PlainCharset[i]); + printf("\n"); + + printf("plain length range: %d - %d\n", m_vCharset[0].m_nPlainLenMin, m_vCharset[0].m_nPlainLenMax); + printf("plain charset name: %s\n", m_vCharset[0].m_sPlainCharsetName.c_str()); + //printf("plain charset content: %s\n", m_sPlainCharsetContent.c_str()); + //for (i = 0; i <= m_nPlainLenMax; i++) + // printf("plain space up to %d: %s\n", i, uint64tostr(m_nPlainSpaceUpToX[i]).c_str()); + printf("plain space total: %s\n", uint64tostr(m_nPlainSpaceTotal).c_str()); + + printf("rainbow table index: %d\n", m_nRainbowTableIndex); + printf("reduce offset: %s\n", uint64tostr(m_nReduceOffset).c_str()); + printf("\n"); +} + +void CChainWalkContext::SetIndex(uint64 nIndex) +{ + m_nIndex = nIndex; +} + +void CChainWalkContext::SetHash(unsigned char* pHash) +{ + memcpy(m_Hash, pHash, m_nHashLen); +} + +void CChainWalkContext::IndexToPlain() +{ + int i; + m_nPlainLen = 0; +///* + for (i = m_nPlainLenMaxTotal - 1; i >= m_nPlainLenMinTotal - 1; i--) + { + if (m_nIndex >= m_nPlainSpaceUpToX[i]) + { + m_nPlainLen = i + 1; + break; + } + } + + // this is an optimized version of the above +/* + for (i = m_nPlainLenMaxTotal - 1; i >= m_nPlainLenMinTotal - 1 + && m_nIndex < m_nPlainSpaceUpToX[i]; i--) + { } + + m_nPlainLen = i + 1; +*/ + + if(m_nPlainLen == 0) + m_nPlainLen = m_nPlainLenMinTotal; + uint64 nIndexOfX = m_nIndex - m_nPlainSpaceUpToX[m_nPlainLen - 1]; + +// this is the generic code for non x86/x86-64 platforms +#if !defined(_M_X64) && !defined(_M_X86) && !defined(__i386__) && !defined(__x86_64__) + + // Slow/generic version + for (i = m_nPlainLen - 1; i >= 0; i--) + { + int nCharsetLen = 0; + for(UINT4 j = 0; j < m_vCharset.size(); j++) + { + nCharsetLen += m_vCharset[j].m_nPlainLenMax; + if(i < nCharsetLen) // We found the correct charset + { + m_Plain[i] = m_vCharset[j].m_PlainCharset[nIndexOfX % m_vCharset[j].m_nPlainCharsetLen]; + nIndexOfX /= m_vCharset[j].m_nPlainCharsetLen; + break; + } + } + } +#else + + + // Fast ia32 version + for (i = m_nPlainLen - 1; i >= 0; i--) + { + // 0x100000000 = 2^32 +#if defined(_M_X64) || defined(_M_X86) + if (nIndexOfX < 0x100000000I64) + break; +#else + if (nIndexOfX < 0x100000000llu) + break; +#endif + + int nCharsetLen = 0; + for(UINT4 j = 0; j < m_vCharset.size(); j++) + { + nCharsetLen += m_vCharset[j].m_nPlainLenMax; + if(i < nCharsetLen) // We found the correct charset + { + m_Plain[i] = m_vCharset[j].m_PlainCharset[nIndexOfX % m_vCharset[j].m_nPlainCharsetLen]; + nIndexOfX /= m_vCharset[j].m_nPlainCharsetLen; + break; + } + } + } + + unsigned int nIndexOfX32 = (unsigned int)nIndexOfX; + for (; i >= 0; i--) + { + int nCharsetLen = 0; + for(UINT4 j = 0; j < m_vCharset.size(); j++) + { + nCharsetLen += m_vCharset[j].m_nPlainLenMax; + if(i < nCharsetLen) // We found the correct charset + { + +// m_Plain[i] = m_vCharset[j].m_PlainCharset[nIndexOfX32 % m_vCharset[j].m_nPlainCharsetLen]; +// nIndexOfX32 /= m_vCharset[j].m_nPlainCharsetLen; + + +// moving nPlainCharsetLen into the asm body and avoiding the extra temp +// variable results in a performance gain +// unsigned int nPlainCharsetLen = m_vCharset[j].m_nPlainCharsetLen; + unsigned int nTemp; + +#if defined(_WIN32) && !defined(__GNUC__) + + __asm + { + mov eax, nIndexOfX32 + xor edx, edx + div m_vCharset[j].m_nPlainCharsetLen + mov nIndexOfX32, eax + mov nTemp, edx + } + m_Plain[i] = m_vCharset[j].m_PlainCharset[nTemp]; +#else + __asm__ __volatile__ ( "mov %2, %%eax;" + "xor %%edx, %%edx;" + "divl %3;" + "mov %%eax, %0;" + "mov %%edx, %1;" + : "=m"(nIndexOfX32), "=m"(nTemp) + : "m"(nIndexOfX32), "m"(m_vCharset[j].m_nPlainCharsetLen) + : "%eax", "%edx" + ); + m_Plain[i] = m_vCharset[j].m_PlainCharset[nTemp]; +#endif + break; + } + } + } +#endif +} + +void CChainWalkContext::PlainToHash() +{ + m_pHashRoutine(m_Plain, m_nPlainLen, m_Hash); +} + +void CChainWalkContext::HashToIndex(int nPos) +{ + m_nIndex = (*(uint64*)m_Hash + m_nReduceOffset + nPos) % m_nPlainSpaceTotal; +} + +uint64 CChainWalkContext::GetIndex() +{ + return m_nIndex; +} +const uint64 *CChainWalkContext::GetIndexPtr() +{ + return &m_nIndex; +} + +string CChainWalkContext::GetPlain() +{ + string sRet; + int i; + for (i = 0; i < m_nPlainLen; i++) + { + char c = m_Plain[i]; + sRet += c; + } + + return sRet; +} + +string CChainWalkContext::GetBinary() +{ + return HexToStr(m_Plain, m_nPlainLen); +} + +string CChainWalkContext::GetHash() +{ + return HexToStr(m_Hash, m_nHashLen); +} + +bool CChainWalkContext::CheckHash(unsigned char* pHash) +{ + if (memcmp(m_Hash, pHash, m_nHashLen) == 0) + return true; + + return false; +} + +bool CChainWalkContext::isOldFormat() +{ + return isOldRtFormat; +} + +bool CChainWalkContext::isRti2Format() +{ + return isRti2RtFormat; +} diff --git a/Client Applications/rcracki_mt/ChainWalkContext.h b/Client Applications/rcracki_mt/ChainWalkContext.h index e7aea41..566f9f2 100644 --- a/Client Applications/rcracki_mt/ChainWalkContext.h +++ b/Client Applications/rcracki_mt/ChainWalkContext.h @@ -1,85 +1,104 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifndef _CHAINWALKCONTEXT_H -#define _CHAINWALKCONTEXT_H - -#include "HashRoutine.h" -#include "Public.h" - -typedef struct -{ - unsigned char m_PlainCharset[255]; - int m_nPlainCharsetLen; - int m_nPlainLenMin; - int m_nPlainLenMax; - string m_sPlainCharsetName; - string m_sPlainCharsetContent; -} stCharset; -class CChainWalkContext -{ -public: - CChainWalkContext(); - virtual ~CChainWalkContext(); - -private: - static string m_sHashRoutineName; - static HASHROUTINE m_pHashRoutine; // Configuration - static int m_nHashLen; // Configuration - static bool isOldRtFormat; - static bool isNewRtFormat; - static vector m_vCharset; - static int m_nPlainLenMinTotal, m_nPlainLenMaxTotal; - static uint64 m_nPlainSpaceUpToX[MAX_PLAIN_LEN + 1]; // Performance consideration - static uint64 m_nPlainSpaceTotal; // Performance consideration - static int m_nHybridCharset; - static int m_nRainbowTableIndex; // Configuration - static uint64 m_nReduceOffset; // Performance consideration - - // Context - uint64 m_nIndex; - unsigned char m_Plain[MAX_PLAIN_LEN]; - int m_nPlainLen; - unsigned char m_Hash[MAX_HASH_LEN]; - static unsigned char m_Salt[MAX_SALT_LEN]; - static int m_nSaltLen; -private: - static bool LoadCharset(string sCharset); - -public: - static bool SetHashRoutine(string sHashRoutineName); // Configuration - static bool SetPlainCharset(string sCharsetName, int nPlainLenMin, int nPlainLenMax); // Configuration - static bool SetRainbowTableIndex(int nRainbowTableIndex); - static bool SetSalt(unsigned char *Salt, int nSaltLength);// Configuration - static bool SetupWithPathName(string sPathName, int& nRainbowChainLen, int& nRainbowChainCount); // Wrapper - static string GetHashRoutineName(); - static int GetHashLen(); - static string GetPlainCharsetName(); - static string GetPlainCharsetContent(); - static int GetPlainLenMin(); - static int GetPlainLenMax(); - static uint64 GetPlainSpaceTotal(); - static int GetRainbowTableIndex(); - static void Dump(); - static bool isOldFormat(); - static bool isNewFormat(); - - void SetIndex(uint64 nIndex); - void SetHash(unsigned char* pHash); // The length should be m_nHashLen - - void IndexToPlain(); - void PlainToHash(); - void HashToIndex(int nPos); - - uint64 GetIndex(); - const uint64* GetIndexPtr(); - string GetPlain(); - string GetBinary(); - string GetHash(); - bool CheckHash(unsigned char* pHash); // The length should be m_nHashLen -}; - -#endif +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifndef _CHAINWALKCONTEXT_H +#define _CHAINWALKCONTEXT_H + +#include "HashRoutine.h" +#include "Public.h" + +typedef struct +{ + unsigned char m_PlainCharset[255]; + unsigned int m_nPlainCharsetLen; + int m_nPlainLenMin; + int m_nPlainLenMax; + string m_sPlainCharsetName; + string m_sPlainCharsetContent; +} stCharset; +class CChainWalkContext +{ +public: + CChainWalkContext(); + virtual ~CChainWalkContext(); + +private: + static string m_sHashRoutineName; + static HASHROUTINE m_pHashRoutine; // Configuration + static int m_nHashLen; // Configuration + static bool isOldRtFormat; + static bool isRti2RtFormat; + static vector m_vCharset; + static int m_nPlainLenMinTotal, m_nPlainLenMaxTotal; + static uint64 m_nPlainSpaceUpToX[MAX_PLAIN_LEN + 1]; // Performance consideration + static uint64 m_nPlainSpaceTotal; // Performance consideration + static int m_nHybridCharset; + static int m_nRainbowTableIndex; // Configuration + static uint64 m_nReduceOffset; // Performance consideration + + // Context + uint64 m_nIndex; + unsigned char m_Plain[MAX_PLAIN_LEN]; + int m_nPlainLen; + unsigned char m_Hash[MAX_HASH_LEN]; + static unsigned char m_Salt[MAX_SALT_LEN]; + static int m_nSaltLen; +private: + static bool LoadCharset(string sCharset); + +public: + static bool SetHashRoutine(string sHashRoutineName); // Configuration + static bool SetPlainCharset(string sCharsetName, int nPlainLenMin, int nPlainLenMax); // Configuration + static bool SetRainbowTableIndex(int nRainbowTableIndex); + static bool SetSalt(unsigned char *Salt, int nSaltLength);// Configuration + static bool SetupWithPathName(string sPathName, int& nRainbowChainLen, int& nRainbowChainCount); // Wrapper + static string GetHashRoutineName(); + static int GetHashLen(); + static string GetPlainCharsetName(); + static string GetPlainCharsetContent(); + static int GetPlainLenMin(); + static int GetPlainLenMax(); + static uint64 GetPlainSpaceTotal(); + static int GetRainbowTableIndex(); + static void Dump(); + static bool isOldFormat(); + static bool isRti2Format(); + + + void SetIndex(uint64 nIndex); + void SetHash(unsigned char* pHash); // The length should be m_nHashLen + + void IndexToPlain(); + void PlainToHash(); + void HashToIndex(int nPos); + + uint64 GetIndex(); + const uint64* GetIndexPtr(); + string GetPlain(); + string GetBinary(); + string GetHash(); + bool CheckHash(unsigned char* pHash); // The length should be m_nHashLen +}; + +#endif diff --git a/Client Applications/rcracki_mt/ChainWalkSet.cpp b/Client Applications/rcracki_mt/ChainWalkSet.cpp index 3069fee..488280a 100644 --- a/Client Applications/rcracki_mt/ChainWalkSet.cpp +++ b/Client Applications/rcracki_mt/ChainWalkSet.cpp @@ -1,305 +1,324 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786) -#endif - -#include "ChainWalkSet.h" - -CChainWalkSet::CChainWalkSet() -{ - m_sHashRoutineName = ""; - m_sPlainCharsetName = ""; - m_nPlainLenMin = 0; - m_nPlainLenMax = 0; - m_nRainbowTableIndex = 0; - m_nRainbowChainLen = 0; - debug = false; - sPrecalcPathName = ""; - preCalcPart = 0; -} - -CChainWalkSet::~CChainWalkSet() -{ - DiscardAll(); -} - -void CChainWalkSet::DiscardAll() -{ - //printf("debug: discarding all walk...\n"); - - list::iterator it; - for (it = m_lChainWalk.begin(); it != m_lChainWalk.end(); it++) - delete it->pIndexE; - m_lChainWalk.clear(); -} - -string CChainWalkSet::CheckOrRotatePreCalcFile() -{ - char sPreCalcFileName[255]; - - // 255 files limit to be sure - for (; preCalcPart < 255; preCalcPart++) - { - sprintf(sPreCalcFileName, "%s.%d", sPrecalcPathName.c_str(), preCalcPart); - string sReturnPreCalcPath(sPreCalcFileName); - - unsigned int fileLen = 0; - - FILE* file = fopen(sReturnPreCalcPath.c_str(), "ab"); - if(file!=NULL) - { - fileLen = GetFileLen(file); - unsigned int nextFileLen = fileLen + (sizeof(uint64) * (m_nRainbowChainLen-1)); - // Rotate to next file if we are going to pass 2GB filesize - if (nextFileLen < ((unsigned)2 * 1024 * 1024 * 1024)) - { - // We might want to vPrecalcFiles.push_back(sReturnPreCalcPath) if we just created this file - // We don't as only newly generated chainwalksets will be stored to this new file, so we don't have to look there - if (debug) printf("Debug: Using for precalc: %s\n", sReturnPreCalcPath.c_str()); - fclose(file); - return sReturnPreCalcPath; - } - fclose(file); - } - } -} - -void CChainWalkSet::updateUsedPrecalcFiles() -{ - // we might also use this function to search a wildcard path of precalc files - vPrecalcFiles.clear(); - char sPreCalcFileName[255]; - - int i; - // 255 files max - for (i = 0; i < 255; i++) - { - sprintf(sPreCalcFileName, "%s.%d", sPrecalcPathName.c_str(), i); - string sTryPreCalcPath(sPreCalcFileName); - FILE* file = fopen(sTryPreCalcPath.c_str(), "rb"); - if(file!=NULL) { - vPrecalcFiles.push_back(sTryPreCalcPath); - fclose(file); - } - else { - break; - } - } -} - -void CChainWalkSet::removePrecalcFiles() -{ - if (debug) printf("Debug: Removing precalc files.\n"); - updateUsedPrecalcFiles(); - string sCurrentPrecalcPathName = ""; - string sCurrentPrecalcIndexPathName = ""; - - int i; - for (i = 0; i < (int)vPrecalcFiles.size(); i++) - { - sCurrentPrecalcPathName = vPrecalcFiles[i]; - sCurrentPrecalcIndexPathName = sCurrentPrecalcPathName + ".index"; - - if (debug) printf("Debug: Removing precalc file: %s\n", sCurrentPrecalcPathName.c_str()); - - if (remove(sCurrentPrecalcPathName.c_str()) != 0) - if (debug) printf("Debug: Failed removing precalc file: %s\n", sCurrentPrecalcPathName.c_str()); - - if (debug) printf("Debug: Removing precalc index file: %s\n", sCurrentPrecalcIndexPathName.c_str()); - - if (remove(sCurrentPrecalcIndexPathName.c_str()) != 0) - if (debug) printf("Debug: Failed removing precalc index file: %s\n", sCurrentPrecalcIndexPathName.c_str()); - - } -} - -bool CChainWalkSet::FindInFile(uint64* pIndexE, unsigned char* pHash, int nHashLen) -{ - int gotPrecalcOnLine = -1; - char precalculationLine[255]; - sprintf(precalculationLine, "%s_%s#%d-%d_%d_%d:%s\n", m_sHashRoutineName.c_str(), m_sPlainCharsetName.c_str(), m_nPlainLenMin, m_nPlainLenMax, m_nRainbowTableIndex, m_nRainbowChainLen, HexToStr(pHash, nHashLen).c_str() ); - string precalcString(precalculationLine); - - string sCurrentPrecalcPathName = ""; - string sCurrentPrecalcIndexPathName = ""; - int offset; - - int i; - for (i = 0; i < (int)vPrecalcFiles.size() && gotPrecalcOnLine == -1; i++) - { - sCurrentPrecalcPathName = vPrecalcFiles[i]; - sCurrentPrecalcIndexPathName = sCurrentPrecalcPathName + ".index"; - - offset = 0; - - vector precalcLines; - if (ReadLinesFromFile(sCurrentPrecalcIndexPathName.c_str(), precalcLines)) - { - int j; - for (j = 0; j < (int)precalcLines.size(); j++) - { - if (precalcString.compare(0, precalcString.size()-1, precalcLines[j]) == 0) - { - gotPrecalcOnLine = j; - break; - } - - // Parse - vector vPart; - if (SeperateString(precalcLines[j], "___:", vPart)) - { - // add to offset - offset += ((atoi(vPart[3].c_str())-1) * sizeof(uint64)); - } - else { - // corrupt file - printf("Corrupted precalculation file!\n"); - gotPrecalcOnLine = -1; - break; - } - } - } - } - - if (gotPrecalcOnLine > -1) - { - if (debug) printf("Debug: Reading pre calculations from file, line %d offset %d\n", gotPrecalcOnLine, offset); - - FILE* fp = fopen(sCurrentPrecalcPathName.c_str(), "rb"); - - if (fp!=NULL) { - fseek(fp, offset, SEEK_SET); - - // We should do some verification here, for example by recalculating the middle chain, to catch corrupted files - if(fread(pIndexE, sizeof(uint64), m_nRainbowChainLen-1, fp) != m_nRainbowChainLen-1) - printf("File read error."); - fclose(fp); - } - else - printf("Cannot open precalculation file %s.\n", sCurrentPrecalcPathName.c_str()); - - //printf("\npIndexE[0]: %s\n", uint64tostr(pIndexE[0]).c_str()); - //printf("\npIndexE[nRainbowChainLen-2]: %s\n", uint64tostr(pIndexE[m_nRainbowChainLen-2]).c_str()); - - return true; - } - - return false; - -} - -void CChainWalkSet::StoreToFile(uint64* pIndexE, unsigned char* pHash, int nHashLen) -{ - if (debug) printf("\nDebug: Storing precalc\n"); - - string sCurrentPrecalcPathName = CheckOrRotatePreCalcFile(); - string sCurrentPrecalcIndexPathName = sCurrentPrecalcPathName + ".index"; - - FILE* fp = fopen(sCurrentPrecalcPathName.c_str(), "ab"); - if(fp!=NULL) - { - if(fwrite(pIndexE, sizeof(uint64), m_nRainbowChainLen-1, fp) != m_nRainbowChainLen-1) - printf("File write error."); - else - { - FILE* file = fopen(sCurrentPrecalcIndexPathName.c_str(), "a"); - if (file!=NULL) - { - char precalculationLine[255]; - sprintf(precalculationLine, "%s_%s#%d-%d_%d_%d:%s\n", m_sHashRoutineName.c_str(), m_sPlainCharsetName.c_str(), m_nPlainLenMin, m_nPlainLenMax, m_nRainbowTableIndex, m_nRainbowChainLen, HexToStr(pHash, nHashLen).c_str() ); - fputs (precalculationLine, file); - fclose (file); - } - } - fclose(fp); - } - else - printf("Cannot open precalculation file %s\n", sCurrentPrecalcPathName.c_str()); - -} - -uint64* CChainWalkSet::RequestWalk(unsigned char* pHash, int nHashLen, - string sHashRoutineName, - string sPlainCharsetName, int nPlainLenMin, int nPlainLenMax, - int nRainbowTableIndex, - int nRainbowChainLen, - bool& fNewlyGenerated, - bool setDebug, - string sPrecalc) -{ - debug = setDebug; - sPrecalcPathName = sPrecalc; - - if ( m_sHashRoutineName != sHashRoutineName - || m_sPlainCharsetName != sPlainCharsetName - || m_nPlainLenMin != nPlainLenMin - || m_nPlainLenMax != nPlainLenMax - || m_nRainbowTableIndex != nRainbowTableIndex - || m_nRainbowChainLen != nRainbowChainLen) - { - DiscardAll(); - - m_sHashRoutineName = sHashRoutineName; - m_sPlainCharsetName = sPlainCharsetName; - m_nPlainLenMin = nPlainLenMin; - m_nPlainLenMax = nPlainLenMax; - m_nRainbowTableIndex = nRainbowTableIndex; - m_nRainbowChainLen = nRainbowChainLen; - - ChainWalk cw; - memcpy(cw.Hash, pHash, nHashLen); - cw.pIndexE = new uint64[nRainbowChainLen - 1]; - m_lChainWalk.push_back(cw); - - // Only update this list when we search through another rainbow table - updateUsedPrecalcFiles(); - - if (!FindInFile(cw.pIndexE, pHash, nHashLen)) - fNewlyGenerated = true; - else - fNewlyGenerated = false; - return cw.pIndexE; - } - - list::iterator it; - for (it = m_lChainWalk.begin(); it != m_lChainWalk.end(); it++) - { - if (memcmp(it->Hash, pHash, nHashLen) == 0) - { - fNewlyGenerated = false; - return it->pIndexE; - } - } - - ChainWalk cw; - memcpy(cw.Hash, pHash, nHashLen); - cw.pIndexE = new uint64[nRainbowChainLen - 1]; - m_lChainWalk.push_back(cw); - - if (!FindInFile(cw.pIndexE, pHash, nHashLen)) - fNewlyGenerated = true; - else - fNewlyGenerated = false; - return cw.pIndexE; -} - -void CChainWalkSet::DiscardWalk(uint64* pIndexE) -{ - list::iterator it; - for (it = m_lChainWalk.begin(); it != m_lChainWalk.end(); it++) - { - if (it->pIndexE == pIndexE) - { - delete it->pIndexE; - m_lChainWalk.erase(it); - return; - } - } - - printf("debug: pIndexE not found\n"); -} +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifdef _WIN32 + #pragma warning(disable : 4786) +#endif + +#include "ChainWalkSet.h" + +CChainWalkSet::CChainWalkSet() +{ + m_sHashRoutineName = ""; + m_sPlainCharsetName = ""; + m_nPlainLenMin = 0; + m_nPlainLenMax = 0; + m_nRainbowTableIndex = 0; + m_nRainbowChainLen = 0; + debug = false; + sPrecalcPathName = ""; + preCalcPart = 0; +} + +CChainWalkSet::~CChainWalkSet() +{ + DiscardAll(); +} + +void CChainWalkSet::DiscardAll() +{ + //printf("debug: discarding all walk...\n"); + + list::iterator it; + for (it = m_lChainWalk.begin(); it != m_lChainWalk.end(); it++) + delete [] it->pIndexE; + m_lChainWalk.clear(); +} + +string CChainWalkSet::CheckOrRotatePreCalcFile() +{ + char sPreCalcFileName[255]; + + // 255 files limit to be sure + for (; preCalcPart < 255; preCalcPart++) + { + sprintf(sPreCalcFileName, "%s.%d", sPrecalcPathName.c_str(), preCalcPart); + string sReturnPreCalcPath(sPreCalcFileName); + + unsigned int fileLen = 0; + + FILE* file = fopen(sReturnPreCalcPath.c_str(), "ab"); + if(file!=NULL) + { + fileLen = GetFileLen(file); + long unsigned int nextFileLen = fileLen + (sizeof(uint64) * (m_nRainbowChainLen-1)); + // Rotate to next file if we are going to pass 2GB filesize + if (nextFileLen < ((unsigned)2 * 1024 * 1024 * 1024)) + { + // We might want to vPrecalcFiles.push_back(sReturnPreCalcPath) if we just created this file + // We don't as only newly generated chainwalksets will be stored to this new file, so we don't have to look there + if (debug) printf("Debug: Using for precalc: %s\n", sReturnPreCalcPath.c_str()); + fclose(file); + return sReturnPreCalcPath; + } + fclose(file); + } + } + + return string(""); +} + +void CChainWalkSet::updateUsedPrecalcFiles() +{ + // we might also use this function to search a wildcard path of precalc files + vPrecalcFiles.clear(); + char sPreCalcFileName[255]; + + int i; + // 255 files max + for (i = 0; i < 255; i++) + { + sprintf(sPreCalcFileName, "%s.%d", sPrecalcPathName.c_str(), i); + string sTryPreCalcPath(sPreCalcFileName); + FILE* file = fopen(sTryPreCalcPath.c_str(), "rb"); + if(file!=NULL) { + vPrecalcFiles.push_back(sTryPreCalcPath); + fclose(file); + } + else { + break; + } + } +} + +void CChainWalkSet::removePrecalcFiles() +{ + if (debug) printf("Debug: Removing precalc files.\n"); + updateUsedPrecalcFiles(); + string sCurrentPrecalcPathName = ""; + string sCurrentPrecalcIndexPathName = ""; + + int i; + for (i = 0; i < (int)vPrecalcFiles.size(); i++) + { + sCurrentPrecalcPathName = vPrecalcFiles[i]; + sCurrentPrecalcIndexPathName = sCurrentPrecalcPathName + ".index"; + + if (debug) printf("Debug: Removing precalc file: %s\n", sCurrentPrecalcPathName.c_str()); + + if (remove(sCurrentPrecalcPathName.c_str()) != 0) + if (debug) printf("Debug: Failed removing precalc file: %s\n", sCurrentPrecalcPathName.c_str()); + + if (debug) printf("Debug: Removing precalc index file: %s\n", sCurrentPrecalcIndexPathName.c_str()); + + if (remove(sCurrentPrecalcIndexPathName.c_str()) != 0) + if (debug) printf("Debug: Failed removing precalc index file: %s\n", sCurrentPrecalcIndexPathName.c_str()); + + } +} + +bool CChainWalkSet::FindInFile(uint64* pIndexE, unsigned char* pHash, int nHashLen) +{ + int gotPrecalcOnLine = -1; + char precalculationLine[255]; + sprintf(precalculationLine, "%s_%s#%d-%d_%d_%d:%s\n", m_sHashRoutineName.c_str(), m_sPlainCharsetName.c_str(), m_nPlainLenMin, m_nPlainLenMax, m_nRainbowTableIndex, m_nRainbowChainLen, HexToStr(pHash, nHashLen).c_str() ); + string precalcString(precalculationLine); + + string sCurrentPrecalcPathName = ""; + string sCurrentPrecalcIndexPathName = ""; + long unsigned int offset; + + int i; + for (i = 0; i < (int)vPrecalcFiles.size() && gotPrecalcOnLine == -1; i++) + { + sCurrentPrecalcPathName = vPrecalcFiles[i]; + sCurrentPrecalcIndexPathName = sCurrentPrecalcPathName + ".index"; + + offset = 0; + + vector precalcLines; + if (ReadLinesFromFile(sCurrentPrecalcIndexPathName.c_str(), precalcLines)) + { + int j; + for (j = 0; j < (int)precalcLines.size(); j++) + { + if (precalcString.compare(0, precalcString.size()-1, precalcLines[j]) == 0) + { + gotPrecalcOnLine = j; + break; + } + + // Parse + vector vPart; + if (SeperateString(precalcLines[j], "___:", vPart)) + { + // add to offset + offset += ((atoi(vPart[3].c_str())-1) * sizeof(uint64)); + } + else { + // corrupt file + printf("Corrupted precalculation file!\n"); + gotPrecalcOnLine = -1; + break; + } + } + } + } + + if (gotPrecalcOnLine > -1) + { + if (debug) printf("Debug: Reading pre calculations from file, line %d offset %lu\n", gotPrecalcOnLine, offset); + + FILE* fp = fopen(sCurrentPrecalcPathName.c_str(), "rb"); + + if (fp!=NULL) { + fseek(fp, offset, SEEK_SET); + + // We should do some verification here, for example by recalculating the middle chain, to catch corrupted files + if(fread(pIndexE, sizeof(uint64), (unsigned long)m_nRainbowChainLen-1, fp) != (unsigned long)m_nRainbowChainLen-1) + printf("File read error."); + fclose(fp); + } + else + printf("Cannot open precalculation file %s.\n", sCurrentPrecalcPathName.c_str()); + + //printf("\npIndexE[0]: %s\n", uint64tostr(pIndexE[0]).c_str()); + //printf("\npIndexE[nRainbowChainLen-2]: %s\n", uint64tostr(pIndexE[m_nRainbowChainLen-2]).c_str()); + + return true; + } + + return false; +} + +void CChainWalkSet::StoreToFile(uint64* pIndexE, unsigned char* pHash, int nHashLen) +{ + if (debug) printf("\nDebug: Storing precalc\n"); + + string sCurrentPrecalcPathName = CheckOrRotatePreCalcFile(); + string sCurrentPrecalcIndexPathName = sCurrentPrecalcPathName + ".index"; + + FILE* fp = fopen(sCurrentPrecalcPathName.c_str(), "ab"); + if(fp!=NULL) + { + if(fwrite(pIndexE, sizeof(uint64), (unsigned long)m_nRainbowChainLen-1, fp) != (unsigned long)m_nRainbowChainLen-1) + printf("File write error."); + else + { + FILE* file = fopen(sCurrentPrecalcIndexPathName.c_str(), "a"); + if (file!=NULL) + { + char precalculationLine[255]; + sprintf(precalculationLine, "%s_%s#%d-%d_%d_%d:%s\n", m_sHashRoutineName.c_str(), m_sPlainCharsetName.c_str(), m_nPlainLenMin, m_nPlainLenMax, m_nRainbowTableIndex, m_nRainbowChainLen, HexToStr(pHash, nHashLen).c_str() ); + fputs (precalculationLine, file); + fclose (file); + } + } + fclose(fp); + } + else + printf("Cannot open precalculation file %s\n", sCurrentPrecalcPathName.c_str()); +} + +uint64* CChainWalkSet::RequestWalk(unsigned char* pHash, int nHashLen, + string sHashRoutineName, + string sPlainCharsetName, int nPlainLenMin, int nPlainLenMax, + int nRainbowTableIndex, + int nRainbowChainLen, + bool& fNewlyGenerated, + bool setDebug, + string sPrecalc) +{ + debug = setDebug; + sPrecalcPathName = sPrecalc; + + if ( m_sHashRoutineName != sHashRoutineName + || m_sPlainCharsetName != sPlainCharsetName + || m_nPlainLenMin != nPlainLenMin + || m_nPlainLenMax != nPlainLenMax + || m_nRainbowTableIndex != nRainbowTableIndex + || m_nRainbowChainLen != nRainbowChainLen) + { + DiscardAll(); + + m_sHashRoutineName = sHashRoutineName; + m_sPlainCharsetName = sPlainCharsetName; + m_nPlainLenMin = nPlainLenMin; + m_nPlainLenMax = nPlainLenMax; + m_nRainbowTableIndex = nRainbowTableIndex; + m_nRainbowChainLen = nRainbowChainLen; + + ChainWalk cw; + memcpy(cw.Hash, pHash, nHashLen); + cw.pIndexE = new uint64[nRainbowChainLen - 1]; + m_lChainWalk.push_back(cw); + + // Only update this list when we search through another rainbow table + updateUsedPrecalcFiles(); + + if (!FindInFile(cw.pIndexE, pHash, nHashLen)) + fNewlyGenerated = true; + else + fNewlyGenerated = false; + return cw.pIndexE; + } + + list::iterator it; + for (it = m_lChainWalk.begin(); it != m_lChainWalk.end(); it++) + { + if (memcmp(it->Hash, pHash, nHashLen) == 0) + { + fNewlyGenerated = false; + return it->pIndexE; + } + } + + ChainWalk cw; + memcpy(cw.Hash, pHash, nHashLen); + cw.pIndexE = new uint64[nRainbowChainLen - 1]; + m_lChainWalk.push_back(cw); + + if (!FindInFile(cw.pIndexE, pHash, nHashLen)) + fNewlyGenerated = true; + else + fNewlyGenerated = false; + return cw.pIndexE; +} + +void CChainWalkSet::DiscardWalk(uint64* pIndexE) +{ + list::iterator it; + for (it = m_lChainWalk.begin(); it != m_lChainWalk.end(); it++) + { + if (it->pIndexE == pIndexE) + { + delete it->pIndexE; + m_lChainWalk.erase(it); + return; + } + } + + printf("debug: pIndexE not found\n"); +} diff --git a/Client Applications/rcracki_mt/ChainWalkSet.h b/Client Applications/rcracki_mt/ChainWalkSet.h index 05622cf..3c29bcf 100644 --- a/Client Applications/rcracki_mt/ChainWalkSet.h +++ b/Client Applications/rcracki_mt/ChainWalkSet.h @@ -1,58 +1,77 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifndef _CHAINWALKSET_H -#define _CHAINWALKSET_H - -#include "Public.h" - -struct ChainWalk -{ - unsigned char Hash[MAX_HASH_LEN]; - //int nHashLen; // Implied - uint64* pIndexE; // mapStartPosIndexE, Len = nRainbowChainLen - 1 -}; - -class CChainWalkSet -{ -public: - CChainWalkSet(); - virtual ~CChainWalkSet(); - -private: - string m_sHashRoutineName; // Discard all if not match - string m_sPlainCharsetName; // Discard all if not match - int m_nPlainLenMin; // Discard all if not match - int m_nPlainLenMax; // Discard all if not match - int m_nRainbowTableIndex; // Discard all if not match - int m_nRainbowChainLen; // Discard all if not match - list m_lChainWalk; - bool debug; - string sPrecalcPathName; - int preCalcPart; - vector vPrecalcFiles; - -private: - void DiscardAll(); - bool FindInFile(uint64* pIndexE, unsigned char* pHash, int nHashLen); - string CheckOrRotatePreCalcFile(); - void updateUsedPrecalcFiles(); - -public: - uint64* RequestWalk(unsigned char* pHash, int nHashLen, - string sHashRoutineName, - string sPlainCharsetName, int nPlainLenMin, int nPlainLenMax, - int nRainbowTableIndex, - int nRainbowChainLen, - bool& fNewlyGenerated, - bool setDebug, - string sPrecalc); - void DiscardWalk(uint64* pIndexE); - void StoreToFile(uint64* pIndexE, unsigned char* pHash, int nHashLen); - void removePrecalcFiles(); -}; - -#endif +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifndef _CHAINWALKSET_H +#define _CHAINWALKSET_H + +#include "Public.h" + +struct ChainWalk +{ + unsigned char Hash[MAX_HASH_LEN]; + //int nHashLen; // Implied + uint64* pIndexE; // mapStartPosIndexE, Len = nRainbowChainLen - 1 +}; + +class CChainWalkSet +{ +public: + CChainWalkSet(); + virtual ~CChainWalkSet(); + +private: + string m_sHashRoutineName; // Discard all if not match + string m_sPlainCharsetName; // Discard all if not match + int m_nPlainLenMin; // Discard all if not match + int m_nPlainLenMax; // Discard all if not match + int m_nRainbowTableIndex; // Discard all if not match + int m_nRainbowChainLen; // Discard all if not match + list m_lChainWalk; + bool debug; + string sPrecalcPathName; + int preCalcPart; + vector vPrecalcFiles; + +private: + void DiscardAll(); + bool FindInFile(uint64* pIndexE, unsigned char* pHash, int nHashLen); + string CheckOrRotatePreCalcFile(); + void updateUsedPrecalcFiles(); + +public: + uint64* RequestWalk(unsigned char* pHash, int nHashLen, + string sHashRoutineName, + string sPlainCharsetName, int nPlainLenMin, int nPlainLenMax, + int nRainbowTableIndex, + int nRainbowChainLen, + bool& fNewlyGenerated, + bool setDebug, + string sPrecalc); + void DiscardWalk(uint64* pIndexE); + void StoreToFile(uint64* pIndexE, unsigned char* pHash, int nHashLen); + void removePrecalcFiles(); +}; + +#endif diff --git a/Client Applications/rcracki_mt/CrackEngine.cpp b/Client Applications/rcracki_mt/CrackEngine.cpp index 3ed82de..0021610 100644 --- a/Client Applications/rcracki_mt/CrackEngine.cpp +++ b/Client Applications/rcracki_mt/CrackEngine.cpp @@ -1,1291 +1,1388 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786 4267 4018) -#endif - -#include "CrackEngine.h" - -#include -#include "RTI2Reader.h" - -CCrackEngine::CCrackEngine() -{ - ResetStatistics(); - writeOutput = false; - resumeSession = false; - debug = false; - keepPrecalcFiles = false; - - sSessionPathName = ""; - sProgressPathName = ""; -} - -CCrackEngine::~CCrackEngine() -{ -} - -////////////////////////////////////////////////////////////////////// - -void CCrackEngine::ResetStatistics() -{ - m_fTotalDiskAccessTime = 0.0f; - m_fTotalCryptanalysisTime = 0.0f; - m_nTotalChainWalkStep = 0; - m_nTotalFalseAlarm = 0; - m_nTotalChainWalkStepDueToFalseAlarm = 0; -// m_nTotalFalseAlarmSkipped = 0; -} - -int CCrackEngine::BinarySearchOld(RainbowChainO* pChain, int nRainbowChainCount, uint64 nIndex) -{ - int nLow = 0; - int nHigh = nRainbowChainCount - 1; - while (nLow <= nHigh) - { - int nMid = (nLow + nHigh) / 2; - if (nIndex == pChain[nMid].nIndexE) - return nMid; - else if (nIndex < pChain[nMid].nIndexE) - nHigh = nMid - 1; - else - nLow = nMid + 1; - } - - return -1; -} - -RainbowChain *CCrackEngine::BinarySearch(RainbowChain *pChain, int nChainCountRead, uint64 nIndex, IndexChain *pIndex, int nIndexSize, int nIndexStart) -{ - uint64 nPrefix = nIndex >> 16; - int nLow, nHigh; - bool found = false; - int nChains = 0; - - if(nPrefix > (pIndex[nIndexSize-1].nPrefix & 0x000000FFFFFFFFFFULL)) // check if its in the index file - { - return NULL; - } - - int nBLow = 0; - int nBHigh = nIndexSize - 1; - while (nBLow <= nBHigh) - { - int nBMid = (nBLow + nBHigh) / 2; - if (nPrefix == (pIndex[nBMid].nPrefix & 0x000000FFFFFFFFFFULL)) - { - //nLow = nChains; - int nChains = 0; - - nLow = pIndex[nBMid].nFirstChain; - nHigh = nLow + pIndex[nBMid].nChainCount; - if(nLow >= nIndexStart && nLow <= nIndexStart + nChainCountRead) - { - if(nHigh > nIndexStart + nChainCountRead) - nHigh = nIndexStart + nChainCountRead; - } - else if(nLow < nIndexStart && nHigh >= nIndexStart) - { - nLow = nIndexStart; - } - else break; - found = true; - break; - } - else if (nPrefix < (pIndex[nBMid].nPrefix & 0x000000FFFFFFFFFFULL)) - nBHigh = nBMid - 1; - else - nBLow = nBMid + 1; - } - if(found == true) - { - for(int i = nLow - nIndexStart; i < nHigh - nIndexStart; i++) - { - int nSIndex = ((int)nIndex) & 0x0000FFFF; - - if (nSIndex == pChain[i].nIndexE) - { - return &pChain[i]; - } - else if(pChain[i].nIndexE > nSIndex) - break; - } - } - return NULL; -} - -// not used currently, leaving code for future checkpoints -//bool CCrackEngine::CheckAlarm(RainbowChain* pChain, int nGuessedPos, unsigned char* pHash, CHashSet& hs) -//{ -// CChainWalkContext cwc; -// //uint64 nIndexS = pChain->nIndexS >> 16; -// uint64 nIndexS = pChain->nIndexS & 0x0000FFFFFFFFFFFFULL; // for first 6 bytes -// cwc.SetIndex(nIndexS); -// int nPos; -// for (nPos = 0; nPos < nGuessedPos; nPos++) -// { -// cwc.IndexToPlain(); -// cwc.PlainToHash(); -// cwc.HashToIndex(nPos); -// // Not using checkpoints atm -// /* -// switch(nPos) -// { -// case 5000: -// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000080) >> 7) -// { -// m_nTotalFalseAlarmSkipped += 10000 - 5000; -//// printf("CheckPoint caught false alarm at position 7600\n"); -// return false; -// } -// break; -// case 6000: -// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000040) >> 6) -// { -//// printf("CheckPoint caught false alarm at position 8200\n"); -// m_nTotalFalseAlarmSkipped += 10000 - 6000; -// return false; -// } -// break; -// -// case 7600: -// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000020) >> 5) -// { -//// printf("CheckPoint caught false alarm at position 8700\n"); -// m_nTotalFalseAlarmSkipped += 10000 - 7600; -// return false; -// } -// break; -// -// case 8200: -// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000010) >> 4) -// { -//// printf("CheckPoint caught false alarm at position 9000\n"); -// m_nTotalFalseAlarmSkipped += 10000 - 8200; -// return false; -// } -// break; -// -// case 8700: -// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000008) >> 3) -// { -//// printf("CheckPoint caught false alarm at position 9300\n"); -// m_nTotalFalseAlarmSkipped += 10000 - 8700; -// return false; -// } -// -// break; -// case 9000: -// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000004) >> 2) -// { -//// printf("CheckPoint caught false alarm at position 9600\n"); -// m_nTotalFalseAlarmSkipped += 10000 - 9000; -// return false; -// } -// -// break; -// case 9300: -// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000002) >> 1) -// { -//// printf("CheckPoint caught false alarm at position 9600\n"); -// m_nTotalFalseAlarmSkipped += 10000 - 9300; -// return false; -// } -// break; -// case 9600: -// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000001)) -// { -//// printf("CheckPoint caught false alarm at position 9600\n"); -// m_nTotalFalseAlarmSkipped += 10000 - 9600; -// return false; -// } -// break; -// -// }*/ -// } -// cwc.IndexToPlain(); -// cwc.PlainToHash(); -// if (cwc.CheckHash(pHash)) -// { -// printf("plaintext of %s is %s\n", cwc.GetHash().c_str(), cwc.GetPlain().c_str()); -// hs.SetPlain(cwc.GetHash(), cwc.GetPlain(), cwc.GetBinary()); -// return true; -// } -// -// return false; -//} - -//bool CCrackEngine::CheckAlarmOld(RainbowChainO* pChain, int nGuessedPos, unsigned char* pHash, CHashSet& hs) -//{ -// CChainWalkContext cwc; -// cwc.SetIndex(pChain->nIndexS); -// int nPos; -// for (nPos = 0; nPos < nGuessedPos; nPos++) -// { -// cwc.IndexToPlain(); -// cwc.PlainToHash(); -// cwc.HashToIndex(nPos); -// } -// cwc.IndexToPlain(); -// cwc.PlainToHash(); -// if (cwc.CheckHash(pHash)) -// { -// printf("plaintext of %s is %s\n", cwc.GetHash().c_str(), cwc.GetPlain().c_str()); -// hs.SetPlain(cwc.GetHash(), cwc.GetPlain(), cwc.GetBinary()); -// return true; -// } -// -// return false; -//} - -void CCrackEngine::GetChainIndexRangeWithSameEndpoint(RainbowChainO* pChain, - int nRainbowChainCount, - int nMatchingIndexE, - int& nMatchingIndexEFrom, - int& nMatchingIndexETo) -{ - nMatchingIndexEFrom = nMatchingIndexE; - nMatchingIndexETo = nMatchingIndexE; - while (nMatchingIndexEFrom > 0) - { - if (pChain[nMatchingIndexEFrom - 1].nIndexE == pChain[nMatchingIndexE].nIndexE) - nMatchingIndexEFrom--; - else - break; - } - while (nMatchingIndexETo < nRainbowChainCount - 1) - { - if (pChain[nMatchingIndexETo + 1].nIndexE == pChain[nMatchingIndexE].nIndexE) - nMatchingIndexETo++; - else - break; - } -} - -void CCrackEngine::SearchTableChunkOld(RainbowChainO* pChain, int nRainbowChainLen, int nRainbowChainCount, CHashSet& hs) -{ - vector vHash; - hs.GetLeftHashWithLen(vHash, CChainWalkContext::GetHashLen()); - printf("searching for %d hash%s...\n", vHash.size(), - vHash.size() > 1 ? "es" : ""); - - int nChainWalkStep = 0; - int nFalseAlarm = 0; - int nChainWalkStepDueToFalseAlarm = 0; - - vector threadPool; - vector pThreads; - - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - #ifdef _WIN32 - sched_param param; - param.sched_priority = THREAD_PRIORITY_BELOW_NORMAL; - pthread_attr_setschedparam (&attr, ¶m); - #endif - // else set it to 5 or something (for linux)? - - bool pausing = false; - - int nHashIndex; - for (nHashIndex = 0; nHashIndex < vHash.size(); nHashIndex++) - { - #ifdef _WIN32 - if (_kbhit()) - { - int ch = _getch(); - ch = toupper(ch); - if (ch == 'P') - { - pausing = true; - printf( "\nPausing, press P again to continue... "); - clock_t t1 = clock(); - while (pausing) - { - if (_kbhit()) - { - ch = _getch(); - ch = toupper(ch); - if (ch == 'P') - { - printf( " [Continuing]\n"); - pausing = false; - clock_t t2 = clock(); - float fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - m_fTotalCryptanalysisTime -= fTime; - } - } - Sleep(500); - } - } - else - { - printf( "\nPress 'P' to pause...\n"); - } - } - #else - int c = tty_getchar(); - if (c >= 0) { - tty_flush(); - if (c==112) { // = p - pausing = true; - printf( "\nPausing, press 'p' again to continue... "); - clock_t t1 = clock(); - while (pausing) - { - if ((c = tty_getchar()) >= 0) - { - tty_flush(); - if (c == 112) - { - printf( " [Continuing]\n"); - pausing = false; - clock_t t2 = clock(); - float fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - m_fTotalCryptanalysisTime -= fTime; - } - } - usleep(500*1000); - } - } - else { - printf( "\nPress 'p' to pause...\n"); - } - } - #endif - unsigned char TargetHash[MAX_HASH_LEN]; - int nHashLen; - ParseHash(vHash[nHashIndex], TargetHash, nHashLen); - if (nHashLen != CChainWalkContext::GetHashLen()) - printf("debug: nHashLen mismatch\n"); - - // Rqeuest ChainWalk - bool fNewlyGenerated; - uint64* pStartPosIndexE = m_cws.RequestWalk(TargetHash, - nHashLen, - CChainWalkContext::GetHashRoutineName(), - CChainWalkContext::GetPlainCharsetName(), - CChainWalkContext::GetPlainLenMin(), - CChainWalkContext::GetPlainLenMax(), - CChainWalkContext::GetRainbowTableIndex(), - nRainbowChainLen, - fNewlyGenerated, - debug, - sPrecalcPathName); - //printf("debug: using %s walk for %s\n", fNewlyGenerated ? "newly generated" : "existing", - // vHash[nHashIndex].c_str()); - - // Walk - if (fNewlyGenerated) - { - //printf("Pre-calculating hash %d of %d.\t\t\r", nHashIndex+1, vHash.size()); - printf("Pre-calculating hash %d of %d.%-20s\r", nHashIndex+1, vHash.size(), ""); - threadPool.clear(); - pThreads.clear(); - - int thread_ID; - for (thread_ID = 0; thread_ID < maxThreads; thread_ID++) - { - rcrackiThread* r_Thread = new rcrackiThread(TargetHash, thread_ID, nRainbowChainLen, maxThreads, pStartPosIndexE); - if (r_Thread) - { - pthread_t pThread; - int returnValue = pthread_create( &pThread, &attr, rcrackiThread::rcrackiThreadStaticEntryPointPthread, (void *) r_Thread); - - if( returnValue != 0 ) - { - printf("pThread creation failed, returnValue: %d\n", returnValue); - } - else - { - pThreads.push_back(pThread); - } - - threadPool.push_back(r_Thread); - } - else - { - printf("r_Thread creation failed!\n"); - } - } - - //printf("%d r_Threads created\t\t\n", threadPool.size()); - - for (thread_ID = 0; thread_ID < threadPool.size(); thread_ID++) - { - pthread_t pThread = pThreads[thread_ID]; - int returnValue = pthread_join(pThread, NULL); - if( returnValue != 0 ) - { - printf("pThread join failed, returnValue: %d\n", returnValue); - } - - rcrackiThread* rThread = threadPool[thread_ID]; - nChainWalkStep += rThread->GetChainWalkStep(); - } - - //printf("\t\t\t\t\r"); - printf("%-50s\r", ""); - - - } - - //printf("Checking false alarms for hash %d of %d.\t\t\r", nHashIndex+1, vHash.size()); - printf("Checking false alarms for hash %d of %d.%-20s\r", nHashIndex+1, vHash.size(), ""); - - threadPool.clear(); - pThreads.clear(); - - int i; - for (i = 0; i < maxThreads; i++) - { - rcrackiThread* r_Thread = new rcrackiThread(TargetHash, true); - threadPool.push_back(r_Thread); - } - - int thread_ID = 0; - int nPos; - for (nPos = nRainbowChainLen - 2; nPos >= 0; nPos--) - { - uint64 nIndexEOfCurPos = pStartPosIndexE[nPos]; - - // Search matching nIndexE - int nMatchingIndexE = BinarySearchOld(pChain, nRainbowChainCount, nIndexEOfCurPos); - if (nMatchingIndexE != -1) - { - int nMatchingIndexEFrom, nMatchingIndexETo; - GetChainIndexRangeWithSameEndpoint(pChain, nRainbowChainCount, - nMatchingIndexE, - nMatchingIndexEFrom, nMatchingIndexETo); - int i; - for (i = nMatchingIndexEFrom; i <= nMatchingIndexETo; i++) - { - rcrackiThread* rThread = threadPool[thread_ID]; - rThread->AddAlarmCheckO(pChain + i, nPos); - if (thread_ID < maxThreads - 1 ) { - thread_ID++; - } else { - thread_ID = 0; - } - } - } - } - - for (thread_ID = 0; thread_ID < maxThreads; thread_ID++) - { - rcrackiThread* r_Thread = threadPool[thread_ID]; - pthread_t pThread; - - int returnValue = pthread_create( &pThread, &attr, rcrackiThread::rcrackiThreadStaticEntryPointPthread, (void *) r_Thread); - - if( returnValue != 0 ) - { - printf("pThread creation failed, returnValue: %d\n", returnValue); - } - else - { - pThreads.push_back(pThread); - } - } - - //printf("%d r_Threads created\t\t\n", threadPool.size()); - - bool foundHashInThread = false; - for (thread_ID = 0; thread_ID < threadPool.size(); thread_ID++) - { - rcrackiThread* rThread = threadPool[thread_ID]; - pthread_t pThread = pThreads[thread_ID]; - - int returnValue = pthread_join(pThread, NULL); - if( returnValue != 0 ) - { - printf("pThread join failed, returnValue: %d\n", returnValue); - } - - nChainWalkStepDueToFalseAlarm += rThread->GetChainWalkStepDueToFalseAlarm(); - nFalseAlarm += rThread->GetnFalseAlarm(); - - if (rThread->FoundHash() && !foundHashInThread) { - //printf("\t\t\t\t\t\t\r"); - printf("%-50s\r", ""); - - printf("plaintext of %s is %s\n", rThread->GetHash().c_str(), rThread->GetPlain().c_str()); - if (writeOutput) - { - if (!writeResultLineToFile(outputFile, rThread->GetHash(), rThread->GetPlain(), rThread->GetBinary())) - printf("Couldn't write this result to file!\n"); - } - hs.SetPlain(rThread->GetHash(), rThread->GetPlain(), rThread->GetBinary()); - - FILE* file = fopen(sSessionPathName.c_str(), "a"); - if (file!=NULL) - { - string buffer = "sHash=" + rThread->GetHash() + ":" + rThread->GetBinary() + ":" + rThread->GetPlain() + "\n"; - fputs (buffer.c_str(), file); - fclose (file); - } - - m_cws.DiscardWalk(pStartPosIndexE); - foundHashInThread = true; - } - } - - pThreads.clear(); - threadPool.clear(); - } - - //printf("\t\t\t\t\t\t\t\r"); - printf("%-50s\r", ""); - pThreads.clear(); - threadPool.clear(); - pthread_attr_destroy(&attr); - - //printf("debug: chain walk step: %d\n", nChainWalkStep); - //printf("debug: false alarm: %d\n", nFalseAlarm); - //printf("debug: chain walk step due to false alarm: %d\n", nChainWalkStepDueToFalseAlarm); - - m_nTotalChainWalkStep += nChainWalkStep; - m_nTotalFalseAlarm += nFalseAlarm; - m_nTotalChainWalkStepDueToFalseAlarm += nChainWalkStepDueToFalseAlarm; -} - -void CCrackEngine::SearchTableChunk(RainbowChain* pChain, int nRainbowChainLen, int nRainbowChainCount, CHashSet& hs, IndexChain *pIndex, int nIndexSize, int nChainStart) -{ - vector vHash; - //vector vIndices; - //vector vChains; - hs.GetLeftHashWithLen(vHash, CChainWalkContext::GetHashLen()); - printf("searching for %d hash%s...\n", vHash.size(), - vHash.size() > 1 ? "es" : ""); - - int nChainWalkStep = 0; - int nFalseAlarm = 0; - int nChainWalkStepDueToFalseAlarm = 0; - - vector threadPool; - vector pThreads; - - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - #ifdef _WIN32 - sched_param param; - param.sched_priority = THREAD_PRIORITY_BELOW_NORMAL; - pthread_attr_setschedparam (&attr, ¶m); - #endif - // else set it to 5 or something (for linux)? - - bool pausing = false; - - int nHashIndex; - for (nHashIndex = 0; nHashIndex < vHash.size(); nHashIndex++) - { - #ifdef _WIN32 - if (_kbhit()) - { - int ch = _getch(); - ch = toupper(ch); - if (ch == 'P') - { - pausing = true; - printf( "\nPausing, press P again to continue... "); - clock_t t1 = clock(); - while (pausing) - { - if (_kbhit()) - { - ch = _getch(); - ch = toupper(ch); - if (ch == 'P') - { - printf( " [Continuing]\n"); - pausing = false; - clock_t t2 = clock(); - float fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - m_fTotalCryptanalysisTime -= fTime; - } - } - Sleep(500); - } - } - else - { - printf( "\nPress 'P' to pause...\n"); - } - } - #else - int c = tty_getchar(); - if (c >= 0) { - tty_flush(); - if (c==112) { // = p - pausing = true; - printf( "\nPausing, press 'p' again to continue... "); - clock_t t1 = clock(); - while (pausing) - { - if ((c = tty_getchar()) >= 0) - { - tty_flush(); - if (c == 112) - { - printf( " [Continuing]\n"); - pausing = false; - clock_t t2 = clock(); - float fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - m_fTotalCryptanalysisTime -= fTime; - } - } - usleep(500*1000); - } - } - else { - printf( "\nPress 'p' to pause...\n"); - } - } - #endif - unsigned char TargetHash[MAX_HASH_LEN]; - int nHashLen; - ParseHash(vHash[nHashIndex], TargetHash, nHashLen); - if (nHashLen != CChainWalkContext::GetHashLen()) - printf("debug: nHashLen mismatch\n"); - - // Request ChainWalk - bool fNewlyGenerated; -// printf("Requesting walk..."); - - - uint64* pStartPosIndexE = m_cws.RequestWalk(TargetHash, - nHashLen, - CChainWalkContext::GetHashRoutineName(), - CChainWalkContext::GetPlainCharsetName(), - CChainWalkContext::GetPlainLenMin(), - CChainWalkContext::GetPlainLenMax(), - CChainWalkContext::GetRainbowTableIndex(), - nRainbowChainLen, - fNewlyGenerated, - debug, - sPrecalcPathName); -// printf("done!\n"); -// printf("debug: using %s walk for %s\n", fNewlyGenerated ? "newly generated" : "existing", -// vHash[nHashIndex].c_str()); - - if (fNewlyGenerated) - { - //printf("Pre-calculating hash %d of %d.\t\t\r", nHashIndex+1, vHash.size()); - printf("Pre-calculating hash %d of %d.%-20s\r", nHashIndex+1, vHash.size(), ""); - threadPool.clear(); - pThreads.clear(); - - int thread_ID; - for (thread_ID = 0; thread_ID < maxThreads; thread_ID++) - { - rcrackiThread* r_Thread = new rcrackiThread(TargetHash, thread_ID, nRainbowChainLen, maxThreads, pStartPosIndexE); - if (r_Thread) - { - pthread_t pThread; - int returnValue = pthread_create( &pThread, &attr, rcrackiThread::rcrackiThreadStaticEntryPointPthread, (void *) r_Thread); - - if( returnValue != 0 ) - { - printf("pThread creation failed, returnValue: %d\n", returnValue); - } - else - { - pThreads.push_back(pThread); - } - - threadPool.push_back(r_Thread); - } - else - { - printf("r_Thread creation failed!\n"); - } - } - - //printf("%d r_Threads created\t\t\n", threadPool.size()); - - for (thread_ID = 0; thread_ID < threadPool.size(); thread_ID++) - { - pthread_t pThread = pThreads[thread_ID]; - int returnValue = pthread_join(pThread, NULL); - if( returnValue != 0 ) - { - printf("pThread join failed, returnValue: %d\n", returnValue); - } - - rcrackiThread* rThread = threadPool[thread_ID]; - nChainWalkStep += rThread->GetChainWalkStep(); - } - - m_cws.StoreToFile(pStartPosIndexE, TargetHash, nHashLen); - - //printf("\npStartPosIndexE[0]: %s\n", uint64tostr(pStartPosIndexE[0]).c_str()); - //printf("\npStartPosIndexE[nRainbowChainLen-2]: %s\n", uint64tostr(pStartPosIndexE[nRainbowChainLen-2]).c_str()); - - printf("%-50s\r", ""); - - - } - - threadPool.clear(); - pThreads.clear(); - - //printf("Checking false alarms for hash %d of %d.\t\t\r", nHashIndex+1, vHash.size()); - printf("Checking false alarms for hash %d of %d.%-20s\r", nHashIndex+1, vHash.size(), ""); - - int i; - for (i = 0; i < maxThreads; i++) - { - rcrackiThread* r_Thread = new rcrackiThread(TargetHash); - threadPool.push_back(r_Thread); - } - - int thread_ID = 0; - int nPos; - for (nPos = nRainbowChainLen - 2; nPos >= 0; nPos--) - { - uint64 nIndexEOfCurPos = pStartPosIndexE[nPos]; - - // Search matching nIndexE - RainbowChain *pChainFound = BinarySearch(pChain, nRainbowChainCount, nIndexEOfCurPos, pIndex, nIndexSize, nChainStart); - if (pChainFound != NULL) // For perfected indexed tables we only recieve 1 result (huge speed increase!) - { - rcrackiThread* rThread = threadPool[thread_ID]; - rThread->AddAlarmCheck(pChainFound, nPos); - if (thread_ID < maxThreads - 1 ) { - thread_ID++; - } else { - thread_ID = 0; - } - } - } - - for (thread_ID = 0; thread_ID < maxThreads; thread_ID++) - { - rcrackiThread* r_Thread = threadPool[thread_ID]; - pthread_t pThread; - - int returnValue = pthread_create( &pThread, &attr, rcrackiThread::rcrackiThreadStaticEntryPointPthread, (void *) r_Thread); - - if( returnValue != 0 ) - { - printf("pThread creation failed, returnValue: %d\n", returnValue); - } - else - { - pThreads.push_back(pThread); - } - } - - //printf("%d r_Threads created\t\t\n", threadPool.size()); - - bool foundHashInThread = false; - for (thread_ID = 0; thread_ID < threadPool.size(); thread_ID++) - { - rcrackiThread* rThread = threadPool[thread_ID]; - pthread_t pThread = pThreads[thread_ID]; - - int returnValue = pthread_join(pThread, NULL); - if( returnValue != 0 ) - { - printf("pThread join failed, returnValue: %d\n", returnValue); - } - - nChainWalkStepDueToFalseAlarm += rThread->GetChainWalkStepDueToFalseAlarm(); - nFalseAlarm += rThread->GetnFalseAlarm(); - - if (rThread->FoundHash() && !foundHashInThread) { - //printf("\t\t\t\t\t\t\r"); - printf("%-50s\r", ""); - printf("plaintext of %s is %s\n", rThread->GetHash().c_str(), rThread->GetPlain().c_str()); - if (writeOutput) - { - if (!writeResultLineToFile(outputFile, rThread->GetHash(), rThread->GetPlain(), rThread->GetBinary())) - printf("Couldn't write this result to file!\n"); - } - hs.SetPlain(rThread->GetHash(), rThread->GetPlain(), rThread->GetBinary()); - - FILE* file = fopen(sSessionPathName.c_str(), "a"); - if (file!=NULL) - { - string buffer = "sHash=" + rThread->GetHash() + ":" + rThread->GetBinary() + ":" + rThread->GetPlain() + "\n"; - fputs (buffer.c_str(), file); - fclose (file); - } - - m_cws.DiscardWalk(pStartPosIndexE); - foundHashInThread = true; - } - //pthread - delete rThread; - } - - pThreads.clear(); - threadPool.clear(); - - //printf("\t\t\t\t\r"); - //printf("pChainFounds: %d\n", pChainsFound.size()); -//NEXT_HASH:; - } - //printf("\t\t\t\t\t\t\t\r"); - printf("%-50s\r", ""); - pThreads.clear(); - threadPool.clear(); - pthread_attr_destroy(&attr); - - //printf("debug: chain walk step: %d\n", nChainWalkStep); - //printf("debug: false alarm: %d\n", nFalseAlarm); - //printf("debug: chain walk step due to false alarm: %d\n", nChainWalkStepDueToFalseAlarm); - - m_nTotalChainWalkStep += nChainWalkStep; - m_nTotalFalseAlarm += nFalseAlarm; - m_nTotalChainWalkStepDueToFalseAlarm += nChainWalkStepDueToFalseAlarm; -} - -void CCrackEngine::SearchRainbowTable(string sPathName, CHashSet& hs) -{ - // Did we already go through this file in this session? - if (resumeSession) - { - vector sessionFinishedPathNames; - if (ReadLinesFromFile(sProgressPathName.c_str(), sessionFinishedPathNames)) - { - int i; - for (i = 0; i < sessionFinishedPathNames.size(); i++) - { - if (sessionFinishedPathNames[i] == sPathName) - { - printf("Skipping %s\n", sPathName.c_str()); - return; - } - } - } - } - - // FileName -#ifdef _WIN32 - int nIndex = sPathName.find_last_of('\\'); -#else - int nIndex = sPathName.find_last_of('/'); -#endif - string sFileName; - if (nIndex != -1) - sFileName = sPathName.substr(nIndex + 1); - else - sFileName = sPathName; - - // Info - printf("%s:\n", sFileName.c_str()); - - // Setup - int nRainbowChainLen, nRainbowChainCount; - if (!CChainWalkContext::SetupWithPathName(sPathName, nRainbowChainLen, nRainbowChainCount)) - return; - //printf("keyspace: %u\n", CChainWalkContext::GetPlainSpaceTotal()); - // Already finished? - if (!hs.AnyHashLeftWithLen(CChainWalkContext::GetHashLen())) - { - printf("this table contains hashes with length %d only\n", CChainWalkContext::GetHashLen()); - return; - } - - // Open - FILE* file = fopen(sPathName.c_str(), "rb"); - if (file != NULL) - { - // File length check - bool doOldFormat = CChainWalkContext::isOldFormat(); - bool doNewFormat = CChainWalkContext::isNewFormat(); - int sizeOfChain; - bool fVerified = false; - unsigned int nFileLen = GetFileLen(file); - - if (doOldFormat) - sizeOfChain = 16; - else - sizeOfChain = 8; - - //if (nFileLen % 8 != 0 || nRainbowChainCount * 8 != nFileLen) - if ((nFileLen % sizeOfChain != 0 || nRainbowChainCount * sizeOfChain != nFileLen) && doNewFormat == false) - printf("file length mismatch\n"); - else - { - fseek(file, 0, SEEK_SET); - - unsigned int bytesForChainWalkSet = hs.GetStatHashTotal() * (nRainbowChainLen-1) * 8; - if (debug) printf("Debug: Saving %u bytes of memory for chainwalkset.\n", bytesForChainWalkSet); - - static CMemoryPool mp(bytesForChainWalkSet); - unsigned int nAllocatedSize; - if (doNewFormat || doOldFormat) - { - RTI2Reader *pReader; - if(doNewFormat) { - pReader = new RTI2Reader(sPathName); - - } - if (debug) printf("Debug: This is a table in the old .rt format.\n"); - RainbowChainO* pChain = (RainbowChainO*)mp.Allocate(nFileLen, nAllocatedSize); - if (debug) printf("Allocated %u bytes, filelen %u\n", nAllocatedSize, nFileLen); - if (pChain != NULL) - { - nAllocatedSize = nAllocatedSize / sizeOfChain * sizeOfChain; // Round to sizeOfChain boundary - - //fseek(file, 0, SEEK_SET); - //bool fVerified = false; - while (true) // Chunk read loop - { - if (ftell(file) == nFileLen) - break; - - // Load table chunk - if (debug) printf("reading...\n"); - unsigned int nDataRead = 0; - clock_t t1 = clock(); - if (doNewFormat) - { - nDataRead = nAllocatedSize / 16; - pReader->ReadChains(nDataRead, pChain); - nDataRead *= 8; // Convert from chains read to bytes - if(nDataRead == 0) // No more data - break; - } - else { - nDataRead = fread(pChain, 1, nAllocatedSize, file); - } - clock_t t2 = clock(); - float fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - printf("%u bytes read, disk access time: %.2f s\n", nDataRead, fTime); - m_fTotalDiskAccessTime += fTime; - - int nRainbowChainCountRead = nDataRead / 16; - - // Verify table chunk - if (!fVerified) - { - printf("verifying the file...\n"); - - // Chain length test - int nIndexToVerify = nRainbowChainCountRead / 2; - CChainWalkContext cwc; - cwc.SetIndex(pChain[nIndexToVerify].nIndexS); - int nPos; - for (nPos = 0; nPos < nRainbowChainLen - 1; nPos++) - { - cwc.IndexToPlain(); - cwc.PlainToHash(); - cwc.HashToIndex(nPos); - } - if (cwc.GetIndex() != pChain[nIndexToVerify].nIndexE) - { - printf("rainbow chain length verify fail\n"); - break; - } - - // Chain sort test - int i; - for (i = 0; i < nRainbowChainCountRead - 1; i++) - { - if (pChain[i].nIndexE > pChain[i + 1].nIndexE) - break; - } - if (i != nRainbowChainCountRead - 1) - { - printf("this file is not sorted\n"); - break; - } - - fVerified = true; - } - - // Search table chunk - t1 = clock(); - SearchTableChunkOld(pChain, nRainbowChainLen, nRainbowChainCountRead, hs); - t2 = clock(); - fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - printf("cryptanalysis time: %.2f s\n", fTime); - m_fTotalCryptanalysisTime += fTime; - - // Already finished? - if (!hs.AnyHashLeftWithLen(CChainWalkContext::GetHashLen())) - break; - } - } - else - printf("memory allocation fail\n"); - - //delete pChain; - } - else - { - static CMemoryPool mpIndex(bytesForChainWalkSet); - unsigned int nAllocatedSizeIndex; - - //int nIndexSize = 0; - //IndexChain *pIndex = NULL; - - FILE* fIndex = fopen(((string)(sPathName + string(".index"))).c_str(), "rb"); - if(fIndex != NULL) - { - // File length check - unsigned int nFileLenIndex = GetFileLen(fIndex); - unsigned int nRows = nFileLenIndex / 11; - unsigned int nSize = nRows * sizeof(IndexChain); - //printf("Debug: 8\n"); - if (nFileLenIndex % 11 != 0) - printf("index file length mismatch (%u bytes)\n", nFileLenIndex); - else - { - //printf("index nSize: %d\n", nSize); - //pIndex = (IndexChain*)new unsigned char[nSize]; - IndexChain *pIndex = (IndexChain*)mpIndex.Allocate(nFileLenIndex, nAllocatedSizeIndex); - if (debug) printf("Debug: Allocated %u bytes for index with filelen %u\n", nAllocatedSizeIndex, nFileLenIndex); - - if (pIndex != NULL && nAllocatedSizeIndex > 0) - { - nAllocatedSizeIndex = nAllocatedSizeIndex / sizeof(IndexChain) * sizeof(IndexChain); // Round to sizeOfIndexChain boundary - - fseek(fIndex, 0, SEEK_SET); - int nProcessedIndexChains = 0; - - while (true) // Index chunk read loop - { - if (ftell(fIndex) == nFileLenIndex) - break; - - // Load index chunk - if (debug) printf("Debug: Setting index to 0x00 in memory, %u bytes\n", nAllocatedSizeIndex); - memset(pIndex, 0x00, nAllocatedSizeIndex); - printf("reading index... "); - clock_t t1 = clock(); - unsigned int nDataRead = fread(pIndex, 1, nAllocatedSizeIndex, fIndex); - clock_t t2 = clock(); - - float fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - printf("%u bytes read, disk access time: %.2f s\n", nDataRead, fTime); - m_fTotalDiskAccessTime += fTime; - - //nIndexSize = nFileLenIndex / 11; - int nIndexChainCountRead = nDataRead / sizeof(IndexChain); - //fclose(fIndex); - unsigned int nCoveredRainbowTableChains = 0; - for(int i = 0; i < nIndexChainCountRead; i++) - { - nCoveredRainbowTableChains += pIndex[i].nChainCount; - } - - //RainbowChain* pChain = (RainbowChain*)mp.Allocate(nFileLen, nAllocatedSize); - RainbowChain* pChain = (RainbowChain*)mp.Allocate(nCoveredRainbowTableChains * sizeOfChain, nAllocatedSize); - if (debug) printf("Debug: Allocated %u bytes for %u chains, filelen %u\n", nAllocatedSize, nCoveredRainbowTableChains, nFileLen); - - if (pChain != NULL && nAllocatedSize > 0) - { - nAllocatedSize = nAllocatedSize / sizeOfChain * sizeOfChain; // Round to sizeOfChain boundary - - //fseek(file, 0, SEEK_SET); - //bool fVerified = false; - int nProcessedChains = 0; - while (true) // Chunk read loop - { - if (ftell(file) == nFileLen) - break; - - if (nProcessedChains >= nCoveredRainbowTableChains) - break; - - // Load table chunk - if (debug) printf("Debug: Setting pChain to 0x00 in memory\n"); - memset(pChain, 0x00, nAllocatedSize); - printf("reading table... "); - clock_t t1 = clock(); - unsigned int nDataRead = fread(pChain, 1, nAllocatedSize, file); - clock_t t2 = clock(); - - float fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - printf("%u bytes read, disk access time: %.2f s\n", nDataRead, fTime); - m_fTotalDiskAccessTime += fTime; - int nRainbowChainCountRead = nDataRead / sizeOfChain; - // Verify table chunk (Too lazy to implement this) - - if (!fVerified) - { - printf("verifying the file...\n"); - - // Chain length test - int nIndexToVerify = nRainbowChainCountRead / 2; - CChainWalkContext cwc; - uint64 nIndexS; - nIndexS = pChain[nIndexToVerify].nIndexS & 0x0000FFFFFFFFFFFFULL; // for first 6 bytes - - //printf("nIndexS: %s\n", uint64tostr(nIndexS).c_str()); - cwc.SetIndex(nIndexS); - - int nPos; - for (nPos = 0; nPos < nRainbowChainLen - 1; nPos++) - { - cwc.IndexToPlain(); - cwc.PlainToHash(); - cwc.HashToIndex(nPos); - } - uint64 nEndPoint = 0; - - //for(int i = 0; i < nIndexSize; i++) - for(int i = 0; i < nIndexChainCountRead; i++) - { - if(nIndexToVerify >= pIndex[i].nFirstChain && nIndexToVerify < pIndex[i].nFirstChain + pIndex[i].nChainCount) // We found the matching index - { // Now we need to seek nIndexToVerify into the chains - nEndPoint += (pIndex[i].nPrefix & 0x000000FFFFFFFFFFULL) << 16; // & 0x000000FFFFFFFFFFULL for first 5 bytes - //printf("nPrefix: %s\n", uint64tostr(pIndex[i].nPrefix & 0x000000FFFFFFFFFF).c_str()); - //printf("nFirstChain: %d\n", pIndex[i].nFirstChain); - //printf("nChainCount: %d\n", pIndex[i].nChainCount); - nEndPoint += pChain[nIndexToVerify].nIndexE; - break; - } - } - - if (cwc.GetIndex() != nEndPoint) - { - printf("rainbow chain length verify fail\n"); - break; - } - - fVerified = true; - } - - // Search table chunk - t1 = clock(); - float preTime = m_fTotalCryptanalysisTime; - - SearchTableChunk(pChain, nRainbowChainLen, nRainbowChainCountRead, hs, pIndex, nIndexChainCountRead, nProcessedChains); - float postTime = m_fTotalCryptanalysisTime; - t2 = clock(); - fTime = 1.0f * (t2 - t1) / CLOCKS_PER_SEC; - printf("cryptanalysis time: %.2f s\n", fTime + postTime - preTime); - m_fTotalCryptanalysisTime += fTime; - nProcessedChains += nRainbowChainCountRead; - // Already finished? - if (!hs.AnyHashLeftWithLen(CChainWalkContext::GetHashLen())) - break; - } - } - else printf("memory allocation failed for rainbow table\n"); - - //delete pChain; - } - } - else printf("memory allocation failed for index\n"); - } - } - else - { - printf("Can't load index\n"); - return; - } - fclose(fIndex); - - //delete pIndex; - } - } - fclose(file); - - if (debug) printf("Debug: writing progress to %s\n", sProgressPathName.c_str()); - FILE* file = fopen(sProgressPathName.c_str(), "a"); - if (file!=NULL) - { - string buffer = sPathName + "\n"; - fputs (buffer.c_str(), file); - fclose (file); - } - } - else - printf("can't open file\n"); -} - -void CCrackEngine::Run(vector vPathName, CHashSet& hs, int i_maxThreads, bool resume, bool bDebug) -{ -#ifndef _WIN32 - tty_init(); -#endif - resumeSession = resume; - debug = bDebug; - - maxThreads = i_maxThreads; - // Reset statistics - ResetStatistics(); - - // Sort vPathName (CChainWalkSet need it) - int i, j; - for (i = 0; i < vPathName.size() - 1; i++) - for (j = 0; j < vPathName.size() - i - 1; j++) - { - if (vPathName[j] > vPathName[j + 1]) - { - string sTemp; - sTemp = vPathName[j]; - vPathName[j] = vPathName[j + 1]; - vPathName[j + 1] = sTemp; - } - } - - // Run - for (i = 0; i < vPathName.size() && hs.AnyhashLeft(); i++) - { - SearchRainbowTable(vPathName[i], hs); - printf("\n"); - } - - // delete precalc files - if (!keepPrecalcFiles) - m_cws.removePrecalcFiles(); - -#ifndef _WIN32 - tty_done(); -#endif -} - -void CCrackEngine::setOutputFile(string sPathName) -{ - writeOutput = true; - outputFile = sPathName; -} - -void CCrackEngine::setSession(string sSession, string sProgress, string sPrecalc, bool keepPrecalc) -{ - sSessionPathName = sSession; - sProgressPathName = sProgress; - sPrecalcPathName = sPrecalc; - keepPrecalcFiles = keepPrecalc; -} - -float CCrackEngine::GetStatTotalDiskAccessTime() -{ - return m_fTotalDiskAccessTime; -} -/*float CCrackEngine::GetWastedTime() -{ - return m_fIndexTime; -}*/ -float CCrackEngine::GetStatTotalCryptanalysisTime() -{ - return m_fTotalCryptanalysisTime; -} - -int CCrackEngine::GetStatTotalChainWalkStep() -{ - return m_nTotalChainWalkStep; -} - -int CCrackEngine::GetStatTotalFalseAlarm() -{ - return m_nTotalFalseAlarm; -} - -int CCrackEngine::GetStatTotalChainWalkStepDueToFalseAlarm() -{ - return m_nTotalChainWalkStepDueToFalseAlarm; -} +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * Copyright 2010 uroskn + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifdef _WIN32 + #pragma warning(disable : 4786 4267 4018) +#endif + +#include "CrackEngine.h" +#include "RTI2Reader.h" + +CCrackEngine::CCrackEngine() +{ + ResetStatistics(); + writeOutput = false; + resumeSession = false; + debug = false; + keepPrecalcFiles = false; + + sSessionPathName = ""; + sProgressPathName = ""; +} + +CCrackEngine::~CCrackEngine() +{ +} + +////////////////////////////////////////////////////////////////////// + +void CCrackEngine::ResetStatistics() +{ + m_fTotalDiskAccessTime = 0.0f; + m_fTotalCryptanalysisTime = 0.0f; + m_fTotalPrecalculationTime = 0.0f; + m_nTotalChainWalkStep = 0; + m_nTotalFalseAlarm = 0; + m_nTotalChainWalkStepDueToFalseAlarm = 0; +// m_nTotalFalseAlarmSkipped = 0; +} + +int CCrackEngine::BinarySearchOld(RainbowChainO* pChain, int nRainbowChainCount, uint64 nIndex) +{ + int nLow = 0; + int nHigh = nRainbowChainCount - 1; + while (nLow <= nHigh) + { + int nMid = (nLow + nHigh) / 2; + if (nIndex == pChain[nMid].nIndexE) + return nMid; + else if (nIndex < pChain[nMid].nIndexE) + nHigh = nMid - 1; + else + nLow = nMid + 1; + } + + return -1; +} + +RainbowChain *CCrackEngine::BinarySearch(RainbowChain *pChain, int nChainCountRead, uint64 nIndex, IndexChain *pIndex, int nIndexSize, int nIndexStart) +{ + uint64 nPrefix = nIndex >> 16; + int nLow, nHigh; + bool found = false; + //int nChains = 0; + + if(nPrefix > (pIndex[nIndexSize-1].nPrefix & 0x000000FFFFFFFFFFULL)) // check if its in the index file + { + return NULL; + } + + int nBLow = 0; + int nBHigh = nIndexSize - 1; + while (nBLow <= nBHigh) + { + int nBMid = (nBLow + nBHigh) / 2; + if (nPrefix == (pIndex[nBMid].nPrefix & 0x000000FFFFFFFFFFULL)) + { + //nLow = nChains; + //int nChains = 0; + + nLow = pIndex[nBMid].nFirstChain; + nHigh = nLow + pIndex[nBMid].nChainCount; + if(nLow >= nIndexStart && nLow <= nIndexStart + nChainCountRead) + { + if(nHigh > nIndexStart + nChainCountRead) + nHigh = nIndexStart + nChainCountRead; + } + else if(nLow < nIndexStart && nHigh >= nIndexStart) + { + nLow = nIndexStart; + } + else break; + found = true; + break; + } + else if (nPrefix < (pIndex[nBMid].nPrefix & 0x000000FFFFFFFFFFULL)) + nBHigh = nBMid - 1; + else + nBLow = nBMid + 1; + } + if(found == true) + { + for(int i = nLow - nIndexStart; i < nHigh - nIndexStart; i++) + { + int nSIndex = ((int)nIndex) & 0x0000FFFF; + + if (nSIndex == pChain[i].nIndexE) + { + return &pChain[i]; + } + else if(pChain[i].nIndexE > nSIndex) + break; + } + } + return NULL; +} + +// not used currently, leaving code for future checkpoints +//bool CCrackEngine::CheckAlarm(RainbowChain* pChain, int nGuessedPos, unsigned char* pHash, CHashSet& hs) +//{ +// CChainWalkContext cwc; +// //uint64 nIndexS = pChain->nIndexS >> 16; +// uint64 nIndexS = pChain->nIndexS & 0x0000FFFFFFFFFFFFULL; // for first 6 bytes +// cwc.SetIndex(nIndexS); +// int nPos; +// for (nPos = 0; nPos < nGuessedPos; nPos++) +// { +// cwc.IndexToPlain(); +// cwc.PlainToHash(); +// cwc.HashToIndex(nPos); +// // Not using checkpoints atm +// /* +// switch(nPos) +// { +// case 5000: +// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000080) >> 7) +// { +// m_nTotalFalseAlarmSkipped += 10000 - 5000; +//// printf("CheckPoint caught false alarm at position 7600\n"); +// return false; +// } +// break; +// case 6000: +// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000040) >> 6) +// { +//// printf("CheckPoint caught false alarm at position 8200\n"); +// m_nTotalFalseAlarmSkipped += 10000 - 6000; +// return false; +// } +// break; +// +// case 7600: +// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000020) >> 5) +// { +//// printf("CheckPoint caught false alarm at position 8700\n"); +// m_nTotalFalseAlarmSkipped += 10000 - 7600; +// return false; +// } +// break; +// +// case 8200: +// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000010) >> 4) +// { +//// printf("CheckPoint caught false alarm at position 9000\n"); +// m_nTotalFalseAlarmSkipped += 10000 - 8200; +// return false; +// } +// break; +// +// case 8700: +// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000008) >> 3) +// { +//// printf("CheckPoint caught false alarm at position 9300\n"); +// m_nTotalFalseAlarmSkipped += 10000 - 8700; +// return false; +// } +// +// break; +// case 9000: +// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000004) >> 2) +// { +//// printf("CheckPoint caught false alarm at position 9600\n"); +// m_nTotalFalseAlarmSkipped += 10000 - 9000; +// return false; +// } +// +// break; +// case 9300: +// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000002) >> 1) +// { +//// printf("CheckPoint caught false alarm at position 9600\n"); +// m_nTotalFalseAlarmSkipped += 10000 - 9300; +// return false; +// } +// break; +// case 9600: +// if((cwc.GetIndex() & 0x00000001) != (pChain->nCheckPoint & 0x00000001)) +// { +//// printf("CheckPoint caught false alarm at position 9600\n"); +// m_nTotalFalseAlarmSkipped += 10000 - 9600; +// return false; +// } +// break; +// +// }*/ +// } +// cwc.IndexToPlain(); +// cwc.PlainToHash(); +// if (cwc.CheckHash(pHash)) +// { +// printf("plaintext of %s is %s\n", cwc.GetHash().c_str(), cwc.GetPlain().c_str()); +// hs.SetPlain(cwc.GetHash(), cwc.GetPlain(), cwc.GetBinary()); +// return true; +// } +// +// return false; +//} + +//bool CCrackEngine::CheckAlarmOld(RainbowChainO* pChain, int nGuessedPos, unsigned char* pHash, CHashSet& hs) +//{ +// CChainWalkContext cwc; +// cwc.SetIndex(pChain->nIndexS); +// int nPos; +// for (nPos = 0; nPos < nGuessedPos; nPos++) +// { +// cwc.IndexToPlain(); +// cwc.PlainToHash(); +// cwc.HashToIndex(nPos); +// } +// cwc.IndexToPlain(); +// cwc.PlainToHash(); +// if (cwc.CheckHash(pHash)) +// { +// printf("plaintext of %s is %s\n", cwc.GetHash().c_str(), cwc.GetPlain().c_str()); +// hs.SetPlain(cwc.GetHash(), cwc.GetPlain(), cwc.GetBinary()); +// return true; +// } +// +// return false; +//} + +void CCrackEngine::GetChainIndexRangeWithSameEndpoint(RainbowChainO* pChain, + int nRainbowChainCount, + int nMatchingIndexE, + int& nMatchingIndexEFrom, + int& nMatchingIndexETo) +{ + nMatchingIndexEFrom = nMatchingIndexE; + nMatchingIndexETo = nMatchingIndexE; + while (nMatchingIndexEFrom > 0) + { + if (pChain[nMatchingIndexEFrom - 1].nIndexE == pChain[nMatchingIndexE].nIndexE) + nMatchingIndexEFrom--; + else + break; + } + while (nMatchingIndexETo < nRainbowChainCount - 1) + { + if (pChain[nMatchingIndexETo + 1].nIndexE == pChain[nMatchingIndexE].nIndexE) + nMatchingIndexETo++; + else + break; + } +} + +void CCrackEngine::SearchTableChunkOld(RainbowChainO* pChain, int nRainbowChainLen, int nRainbowChainCount, CHashSet& hs) +{ + vector vHash; + hs.GetLeftHashWithLen(vHash, CChainWalkContext::GetHashLen()); + printf("searching for %lu hash%s...\n", (unsigned long)vHash.size(), + vHash.size() > 1 ? "es" : ""); + + int nChainWalkStep = 0; + int nFalseAlarm = 0; + int nChainWalkStepDueToFalseAlarm = 0; + + vector threadPool; + vector pThreads; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + #ifdef _WIN32 + sched_param param; + param.sched_priority = THREAD_PRIORITY_BELOW_NORMAL; + pthread_attr_setschedparam (&attr, ¶m); + #endif + // XXX else set it to 5 or something (for linux)? + + bool pausing = false; + + UINT4 nHashIndex; + for (nHashIndex = 0; nHashIndex < vHash.size(); nHashIndex++) + { + #ifdef _WIN32 + if (_kbhit()) + { + int ch = _getch(); + ch = toupper(ch); + if (ch == 'P') + { + pausing = true; + printf( "\nPausing, press P again to continue... "); + + timeval tv; + timeval tv2; + timeval final; + gettimeofday( &tv, NULL ); + + while (pausing) + { + if (_kbhit()) + { + ch = _getch(); + ch = toupper(ch); + if (ch == 'P') + { + printf( " [Continuing]\n"); + pausing = false; + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + float fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + m_fTotalCryptanalysisTime -= fTime; + } + } + Sleep(500); + } + } + else + { + printf( "\nPress 'P' to pause...\n"); + } + } + #else + int c = tty_getchar(); + if (c >= 0) { + tty_flush(); + if (c==112) { // = p + pausing = true; + printf( "\nPausing, press 'p' again to continue... "); + + timeval tv; + timeval tv2; + timeval final; + gettimeofday( &tv, NULL ); + + while (pausing) + { + if ((c = tty_getchar()) >= 0) + { + tty_flush(); + if (c == 112) + { + printf( " [Continuing]\n"); + pausing = false; + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + float fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + m_fTotalCryptanalysisTime -= fTime; + } + } + usleep(500*1000); + } + } + else { + printf( "\nPress 'p' to pause...\n"); + } + } + #endif + unsigned char TargetHash[MAX_HASH_LEN]; + int nHashLen; + ParseHash(vHash[nHashIndex], TargetHash, nHashLen); + if (nHashLen != CChainWalkContext::GetHashLen()) + printf("debug: nHashLen mismatch\n"); + + // Rqeuest ChainWalk + bool fNewlyGenerated; + uint64* pStartPosIndexE = m_cws.RequestWalk(TargetHash, + nHashLen, + CChainWalkContext::GetHashRoutineName(), + CChainWalkContext::GetPlainCharsetName(), + CChainWalkContext::GetPlainLenMin(), + CChainWalkContext::GetPlainLenMax(), + CChainWalkContext::GetRainbowTableIndex(), + nRainbowChainLen, + fNewlyGenerated, + debug, + sPrecalcPathName); + //printf("debug: using %s walk for %s\n", fNewlyGenerated ? "newly generated" : "existing", + // vHash[nHashIndex].c_str()); + + // Walk + if (fNewlyGenerated) + { + timeval tv; + timeval tv2; + timeval final; + + gettimeofday( &tv, NULL ); + + printf("Pre-calculating hash %lu of %lu.%-20s\r", + (unsigned long)nHashIndex+1, (unsigned long)vHash.size(), ""); + threadPool.clear(); + pThreads.clear(); + + UINT4 thread_ID; + for (thread_ID = 0; thread_ID < (unsigned long)maxThreads; thread_ID++) + { + rcrackiThread* r_Thread = new rcrackiThread(TargetHash, thread_ID, nRainbowChainLen, maxThreads, pStartPosIndexE); + if (r_Thread) + { + pthread_t pThread; + int returnValue = pthread_create( &pThread, &attr, rcrackiThread::rcrackiThreadStaticEntryPointPthread, (void *) r_Thread); + + if( returnValue != 0 ) + { + printf("pThread creation failed, returnValue: %d\n", returnValue); + } + else + { + pThreads.push_back(pThread); + } + + threadPool.push_back(r_Thread); + } + else + { + printf("r_Thread creation failed!\n"); + } + } + + //printf("%d r_Threads created\t\t\n", threadPool.size()); + + for (thread_ID = 0; thread_ID < threadPool.size(); thread_ID++) + { + pthread_t pThread = pThreads[thread_ID]; + int returnValue = pthread_join(pThread, NULL); + if( returnValue != 0 ) + { + printf("pThread join failed, returnValue: %d\n", returnValue); + } + + rcrackiThread* rThread = threadPool[thread_ID]; + nChainWalkStep += rThread->GetChainWalkStep(); + } + + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + + float fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + + m_fTotalPrecalculationTime += fTime; + m_fTotalCryptanalysisTime -= fTime; + + printf("%-50s\r", ""); + + if ( debug ) + printf("pre-calculation time: %.2f s\n", fTime); + } + + //printf("Checking false alarms for hash %d of %d.\t\t\r", nHashIndex+1, vHash.size()); + printf("Checking false alarms for hash %lu of %lu.%-20s\r", + (unsigned long)nHashIndex+1, (unsigned long)vHash.size(), ""); + + threadPool.clear(); + pThreads.clear(); + + int i; + for (i = 0; i < maxThreads; i++) + { + rcrackiThread* r_Thread = new rcrackiThread(TargetHash, true); + threadPool.push_back(r_Thread); + } + + UINT4 thread_ID = 0; + int nPos; + for (nPos = nRainbowChainLen - 2; nPos >= 0; nPos--) + { + uint64 nIndexEOfCurPos = pStartPosIndexE[nPos]; + + // Search matching nIndexE + int nMatchingIndexE = BinarySearchOld(pChain, nRainbowChainCount, nIndexEOfCurPos); + if (nMatchingIndexE != -1) + { + int nMatchingIndexEFrom, nMatchingIndexETo; + GetChainIndexRangeWithSameEndpoint(pChain, nRainbowChainCount, + nMatchingIndexE, + nMatchingIndexEFrom, nMatchingIndexETo); + int i; + for (i = nMatchingIndexEFrom; i <= nMatchingIndexETo; i++) + { + rcrackiThread* rThread = threadPool[thread_ID]; + rThread->AddAlarmCheckO(pChain + i, nPos); + if (thread_ID < (unsigned long)maxThreads - 1 ) { + thread_ID++; + } else { + thread_ID = 0; + } + } + } + } + + for (thread_ID = 0; thread_ID < (unsigned long)maxThreads; thread_ID++) + { + rcrackiThread* r_Thread = threadPool[thread_ID]; + pthread_t pThread; + + int returnValue = pthread_create( &pThread, &attr, rcrackiThread::rcrackiThreadStaticEntryPointPthread, (void *) r_Thread); + + if( returnValue != 0 ) + { + printf("pThread creation failed, returnValue: %d\n", returnValue); + } + else + { + pThreads.push_back(pThread); + } + } + + //printf("%d r_Threads created\t\t\n", threadPool.size()); + + bool foundHashInThread = false; + for (thread_ID = 0; thread_ID < threadPool.size(); thread_ID++) + { + rcrackiThread* rThread = threadPool[thread_ID]; + pthread_t pThread = pThreads[thread_ID]; + + int returnValue = pthread_join(pThread, NULL); + if( returnValue != 0 ) + { + printf("pThread join failed, returnValue: %d\n", returnValue); + } + + nChainWalkStepDueToFalseAlarm += rThread->GetChainWalkStepDueToFalseAlarm(); + nFalseAlarm += rThread->GetnFalseAlarm(); + + if (rThread->FoundHash() && !foundHashInThread) { + //printf("\t\t\t\t\t\t\r"); + printf("%-50s\r", ""); + + printf("plaintext of %s is %s\n", rThread->GetHash().c_str(), rThread->GetPlain().c_str()); + if (writeOutput) + { + if (!writeResultLineToFile(outputFile, rThread->GetHash(), rThread->GetPlain(), rThread->GetBinary())) + printf("Couldn't write this result to file!\n"); + } + hs.SetPlain(rThread->GetHash(), rThread->GetPlain(), rThread->GetBinary()); + + FILE* file = fopen(sSessionPathName.c_str(), "a"); + if (file!=NULL) + { + string buffer = "sHash=" + rThread->GetHash() + ":" + rThread->GetBinary() + ":" + rThread->GetPlain() + "\n"; + fputs (buffer.c_str(), file); + fclose (file); + } + + m_cws.DiscardWalk(pStartPosIndexE); + foundHashInThread = true; + } + } + + pThreads.clear(); + threadPool.clear(); + } + + //printf("\t\t\t\t\t\t\t\r"); + printf("%-50s\r", ""); + pThreads.clear(); + threadPool.clear(); + pthread_attr_destroy(&attr); + + //printf("debug: chain walk step: %d\n", nChainWalkStep); + //printf("debug: false alarm: %d\n", nFalseAlarm); + //printf("debug: chain walk step due to false alarm: %d\n", nChainWalkStepDueToFalseAlarm); + + m_nTotalChainWalkStep += nChainWalkStep; + m_nTotalFalseAlarm += nFalseAlarm; + m_nTotalChainWalkStepDueToFalseAlarm += nChainWalkStepDueToFalseAlarm; +} + +void CCrackEngine::SearchTableChunk(RainbowChain* pChain, int nRainbowChainLen, int nRainbowChainCount, CHashSet& hs, IndexChain *pIndex, int nIndexSize, int nChainStart) +{ + vector vHash; + //vector vIndices; + //vector vChains; + hs.GetLeftHashWithLen(vHash, CChainWalkContext::GetHashLen()); + printf("searching for %lu hash%s...\n", (unsigned long)vHash.size(), + vHash.size() > 1 ? "es" : ""); + + int nChainWalkStep = 0; + int nFalseAlarm = 0; + int nChainWalkStepDueToFalseAlarm = 0; + + vector threadPool; + vector pThreads; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + #ifdef _WIN32 + sched_param param; + param.sched_priority = THREAD_PRIORITY_BELOW_NORMAL; + pthread_attr_setschedparam (&attr, ¶m); + #endif + // else set it to 5 or something (for linux)? + + bool pausing = false; + + UINT4 nHashIndex; + for (nHashIndex = 0; nHashIndex < vHash.size(); nHashIndex++) + { + #ifdef _WIN32 + if (_kbhit()) + { + int ch = _getch(); + ch = toupper(ch); + if (ch == 'P') + { + pausing = true; + printf( "\nPausing, press P again to continue... "); + + timeval tv; + timeval tv2; + timeval final; + gettimeofday( &tv, NULL ); + + while (pausing) + { + if (_kbhit()) + { + ch = _getch(); + ch = toupper(ch); + if (ch == 'P') + { + printf( " [Continuing]\n"); + pausing = false; + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + float fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + m_fTotalCryptanalysisTime -= fTime; + } + } + Sleep(500); + } + } + else + { + printf( "\nPress 'P' to pause...\n"); + } + } + #else + int c = tty_getchar(); + if (c >= 0) { + tty_flush(); + if (c==112) { // = p + pausing = true; + printf( "\nPausing, press 'p' again to continue... "); + + timeval tv; + timeval tv2; + timeval final; + gettimeofday( &tv, NULL ); + + while (pausing) + { + if ((c = tty_getchar()) >= 0) + { + tty_flush(); + if (c == 112) + { + printf( " [Continuing]\n"); + pausing = false; + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + float fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + m_fTotalCryptanalysisTime -= fTime; + } + } + usleep(500*1000); + } + } + else { + printf( "\nPress 'p' to pause...\n"); + } + } + #endif + unsigned char TargetHash[MAX_HASH_LEN]; + int nHashLen; + ParseHash(vHash[nHashIndex], TargetHash, nHashLen); + if (nHashLen != CChainWalkContext::GetHashLen()) + printf("debug: nHashLen mismatch\n"); + + // Request ChainWalk + bool fNewlyGenerated; +// printf("Requesting walk..."); + + + uint64* pStartPosIndexE = m_cws.RequestWalk(TargetHash, + nHashLen, + CChainWalkContext::GetHashRoutineName(), + CChainWalkContext::GetPlainCharsetName(), + CChainWalkContext::GetPlainLenMin(), + CChainWalkContext::GetPlainLenMax(), + CChainWalkContext::GetRainbowTableIndex(), + nRainbowChainLen, + fNewlyGenerated, + debug, + sPrecalcPathName); +// printf("done!\n"); +// printf("debug: using %s walk for %s\n", fNewlyGenerated ? "newly generated" : "existing", +// vHash[nHashIndex].c_str()); + + if (fNewlyGenerated) + { + timeval tv; + timeval tv2; + timeval final; + + gettimeofday( &tv, NULL ); + + printf("Pre-calculating hash %lu of %lu.%-20s\r", + (unsigned long)nHashIndex+1, (unsigned long)vHash.size(), ""); + threadPool.clear(); + pThreads.clear(); + + UINT4 thread_ID; + for (thread_ID = 0; thread_ID < (unsigned long)maxThreads; thread_ID++) + { + rcrackiThread* r_Thread = new rcrackiThread(TargetHash, thread_ID, nRainbowChainLen, maxThreads, pStartPosIndexE); + if (r_Thread) + { + pthread_t pThread; + int returnValue = pthread_create( &pThread, &attr, rcrackiThread::rcrackiThreadStaticEntryPointPthread, (void *) r_Thread); + + if( returnValue != 0 ) + { + printf("pThread creation failed, returnValue: %d\n", returnValue); + } + else + { + pThreads.push_back(pThread); + } + + threadPool.push_back(r_Thread); + } + else + { + printf("r_Thread creation failed!\n"); + } + } + + //printf("%d r_Threads created\t\t\n", threadPool.size()); + + for (thread_ID = 0; thread_ID < threadPool.size(); thread_ID++) + { + pthread_t pThread = pThreads[thread_ID]; + int returnValue = pthread_join(pThread, NULL); + if( returnValue != 0 ) + { + printf("pThread join failed, returnValue: %d\n", returnValue); + } + + rcrackiThread* rThread = threadPool[thread_ID]; + nChainWalkStep += rThread->GetChainWalkStep(); + delete rThread; + } + + m_cws.StoreToFile(pStartPosIndexE, TargetHash, nHashLen); + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + + float fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + + m_fTotalPrecalculationTime += fTime; + m_fTotalCryptanalysisTime -= fTime; + + //printf("\npStartPosIndexE[0]: %s\n", uint64tostr(pStartPosIndexE[0]).c_str()); + //printf("\npStartPosIndexE[nRainbowChainLen-2]: %s\n", uint64tostr(pStartPosIndexE[nRainbowChainLen-2]).c_str()); + + printf("%-50s\r", ""); + + if ( debug ) + printf("pre-calculation time: %.2f s\n", fTime); + } + + threadPool.clear(); + pThreads.clear(); + + //printf("Checking false alarms for hash %d of %d.\t\t\r", nHashIndex+1, vHash.size()); + printf("Checking false alarms for hash %lu of %lu.%-20s\r", + (unsigned long)nHashIndex+1, (unsigned long)vHash.size(), ""); + + int i; + for (i = 0; i < maxThreads; i++) + { + rcrackiThread* r_Thread = new rcrackiThread(TargetHash); + threadPool.push_back(r_Thread); + } + + UINT4 thread_ID = 0; + int nPos; + for (nPos = nRainbowChainLen - 2; nPos >= 0; nPos--) + { + uint64 nIndexEOfCurPos = pStartPosIndexE[nPos]; + + // Search matching nIndexE + RainbowChain *pChainFound = BinarySearch(pChain, nRainbowChainCount, nIndexEOfCurPos, pIndex, nIndexSize, nChainStart); + if (pChainFound != NULL) // For perfected indexed tables we only recieve 1 result (huge speed increase!) + { + rcrackiThread* rThread = threadPool[thread_ID]; + rThread->AddAlarmCheck(pChainFound, nPos); + if (thread_ID < (unsigned long)maxThreads - 1 ) { + thread_ID++; + } else { + thread_ID = 0; + } + } + } + + for (thread_ID = 0; thread_ID < (unsigned long)maxThreads; thread_ID++) + { + rcrackiThread* r_Thread = threadPool[thread_ID]; + pthread_t pThread; + + int returnValue = pthread_create( &pThread, &attr, rcrackiThread::rcrackiThreadStaticEntryPointPthread, (void *) r_Thread); + + if( returnValue != 0 ) + { + printf("pThread creation failed, returnValue: %d\n", returnValue); + } + else + { + pThreads.push_back(pThread); + } + } + + //printf("%d r_Threads created\t\t\n", threadPool.size()); + + bool foundHashInThread = false; + for (thread_ID = 0; thread_ID < threadPool.size(); thread_ID++) + { + rcrackiThread* rThread = threadPool[thread_ID]; + pthread_t pThread = pThreads[thread_ID]; + + int returnValue = pthread_join(pThread, NULL); + if( returnValue != 0 ) + { + printf("pThread join failed, returnValue: %d\n", returnValue); + } + + nChainWalkStepDueToFalseAlarm += rThread->GetChainWalkStepDueToFalseAlarm(); + nFalseAlarm += rThread->GetnFalseAlarm(); + + if (rThread->FoundHash() && !foundHashInThread) { + //printf("\t\t\t\t\t\t\r"); + printf("%-50s\r", ""); + printf("plaintext of %s is %s\n", rThread->GetHash().c_str(), rThread->GetPlain().c_str()); + if (writeOutput) + { + if (!writeResultLineToFile(outputFile, rThread->GetHash(), rThread->GetPlain(), rThread->GetBinary())) + printf("Couldn't write this result to file!\n"); + } + hs.SetPlain(rThread->GetHash(), rThread->GetPlain(), rThread->GetBinary()); + + FILE* file = fopen(sSessionPathName.c_str(), "a"); + if (file!=NULL) + { + string buffer = "sHash=" + rThread->GetHash() + ":" + rThread->GetBinary() + ":" + rThread->GetPlain() + "\n"; + fputs (buffer.c_str(), file); + fclose (file); + } + + m_cws.DiscardWalk(pStartPosIndexE); + foundHashInThread = true; + } + //pthread + delete rThread; + } + + pThreads.clear(); + threadPool.clear(); + + //printf("\t\t\t\t\r"); + //printf("pChainFounds: %d\n", pChainsFound.size()); +//NEXT_HASH:; + } + //printf("\t\t\t\t\t\t\t\r"); + printf("%-50s\r", ""); + pThreads.clear(); + threadPool.clear(); + pthread_attr_destroy(&attr); + + //printf("debug: chain walk step: %d\n", nChainWalkStep); + //printf("debug: false alarm: %d\n", nFalseAlarm); + //printf("debug: chain walk step due to false alarm: %d\n", nChainWalkStepDueToFalseAlarm); + + m_nTotalChainWalkStep += nChainWalkStep; + m_nTotalFalseAlarm += nFalseAlarm; + m_nTotalChainWalkStepDueToFalseAlarm += nChainWalkStepDueToFalseAlarm; +} + +void CCrackEngine::SearchRainbowTable(string sPathName, CHashSet& hs) +{ + // Did we already go through this file in this session? + if (resumeSession) + { + vector sessionFinishedPathNames; + if (ReadLinesFromFile(sProgressPathName.c_str(), sessionFinishedPathNames)) + { + UINT4 i; + for (i = 0; i < sessionFinishedPathNames.size(); i++) + { + if (sessionFinishedPathNames[i] == sPathName) + { + printf("Skipping %s\n", sPathName.c_str()); + return; + } + } + } + } + + // FileName +#ifdef _WIN32 + int nIndex = sPathName.find_last_of('\\'); +#else + int nIndex = (int) sPathName.find_last_of('/'); +#endif + string sFileName; + if (nIndex != -1) + sFileName = sPathName.substr(nIndex + 1); + else + sFileName = sPathName; + + // Info + printf("%s:\n", sFileName.c_str()); + + // Setup + int nRainbowChainLen, nRainbowChainCount; + if (!CChainWalkContext::SetupWithPathName(sPathName, nRainbowChainLen, nRainbowChainCount)) + return; + //printf("keyspace: %llu\n", CChainWalkContext::GetPlainSpaceTotal()); + // Already finished? + if (!hs.AnyHashLeftWithLen(CChainWalkContext::GetHashLen())) + { + printf("this table contains hashes with length %d only\n", CChainWalkContext::GetHashLen()); + return; + } + + // Open + FILE* file = fopen(sPathName.c_str(), "rb"); + if (file != NULL) + { + // File length check + bool doOldFormat = CChainWalkContext::isOldFormat(); + bool doRti2Format = CChainWalkContext::isRti2Format(); + UINT4 sizeOfChain; + bool fVerified = false; + UINT4 nFileLen = GetFileLen(file); + + if (doOldFormat) + sizeOfChain = 16; + else + sizeOfChain = 8; + + //if (nFileLen % 8 != 0 || nRainbowChainCount * 8 != nFileLen) + if ( (nFileLen % sizeOfChain != 0 || nRainbowChainCount * sizeOfChain != nFileLen) && doRti2Format == false ) + printf("file length mismatch\n"); + else + { + fseek(file, 0, SEEK_SET); + timeval tv; + timeval tv2; + timeval final; + + unsigned int bytesForChainWalkSet = hs.GetStatHashTotal() * (nRainbowChainLen-1) * 8; + if (debug) printf("Debug: Saving %u bytes of memory for chainwalkset.\n", bytesForChainWalkSet); + + uint64 nAllocatedSize; + + if (doRti2Format || doOldFormat) + { + RTI2Reader *pReader = NULL; + + if(doRti2Format) { + pReader = new RTI2Reader(sPathName); + + } + + if (debug) + { + if ( doOldFormat ) + printf("Debug: This is a table in the old .rt format.\n"); + else if ( doRti2Format ) + printf("Debug: This is a table in the .rti2 format.\n"); + } + + static CMemoryPool mp(bytesForChainWalkSet, debug, maxMem); + RainbowChainO* pChain = (RainbowChainO*)mp.Allocate(nFileLen, nAllocatedSize); + if (debug) printf("Allocated %llu bytes, filelen %lu\n", nAllocatedSize, (unsigned long)nFileLen); + if (pChain != NULL) + { + nAllocatedSize = nAllocatedSize / sizeOfChain * sizeOfChain; // Round to sizeOfChain boundary + + //fseek(file, 0, SEEK_SET); + //bool fVerified = false; + while (true) // Chunk read loop + { + if ((unsigned long)ftell(file) == nFileLen) + break; + + // Load table chunk + if (debug) printf("reading...\n"); + unsigned int nDataRead = 0; + gettimeofday( &tv, NULL ); + if ( doRti2Format ) + { + nDataRead = nAllocatedSize / 16; + pReader->ReadChains(nDataRead, pChain); + nDataRead *= 8; // Convert from chains read to bytes + + if ( nDataRead == 0 ) // No more data + break; + } + else + { + nDataRead = fread(pChain, 1, nAllocatedSize, file); + } + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + + float fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + printf("%u bytes read, disk access time: %.2f s\n", nDataRead, fTime); + m_fTotalDiskAccessTime += fTime; + + int nRainbowChainCountRead = nDataRead / 16; + + // Verify table chunk + if (!fVerified) + { + printf("verifying the file...\n"); + + // Chain length test + int nIndexToVerify = nRainbowChainCountRead / 2; + CChainWalkContext cwc; + cwc.SetIndex(pChain[nIndexToVerify].nIndexS); + int nPos; + for (nPos = 0; nPos < nRainbowChainLen - 1; nPos++) + { + cwc.IndexToPlain(); + cwc.PlainToHash(); + cwc.HashToIndex(nPos); + } + if (cwc.GetIndex() != pChain[nIndexToVerify].nIndexE) + { + printf("rainbow chain length verify fail\n"); + break; + } + + // Chain sort test + int i; + for (i = 0; i < nRainbowChainCountRead - 1; i++) + { + if (pChain[i].nIndexE > pChain[i + 1].nIndexE) + break; + } + if (i != nRainbowChainCountRead - 1) + { + printf("this file is not sorted\n"); + break; + } + + fVerified = true; + } + + // Search table chunk + gettimeofday( &tv, NULL ); + SearchTableChunkOld(pChain, nRainbowChainLen, nRainbowChainCountRead, hs); + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + printf("cryptanalysis time: %.2f s\n", fTime); + m_fTotalCryptanalysisTime += fTime; + + // Already finished? + if (!hs.AnyHashLeftWithLen(CChainWalkContext::GetHashLen())) + break; + } + } + else + printf("memory allocation fail\n"); + + //delete pChain; + } + else + { + static CMemoryPool mpIndex(bytesForChainWalkSet, debug, maxMem); + uint64 nAllocatedSizeIndex; + + //int nIndexSize = 0; + //IndexChain *pIndex = NULL; + + FILE* fIndex = fopen(((string)(sPathName + string(".index"))).c_str(), "rb"); + if(fIndex != NULL) + { + // File length check + unsigned int nFileLenIndex = GetFileLen(fIndex); + //unsigned int nRows = nFileLenIndex / 11; + //unsigned int nSize = nRows * sizeof(IndexChain); + //printf("Debug: 8\n"); + if (nFileLenIndex % 11 != 0) + printf("index file length mismatch (%u bytes)\n", nFileLenIndex); + else + { + //printf("index nSize: %d\n", nSize); + //pIndex = (IndexChain*)new unsigned char[nSize]; + IndexChain *pIndex = (IndexChain*)mpIndex.Allocate(nFileLenIndex, nAllocatedSizeIndex); + if (debug) printf("Debug: Allocated %llu bytes for index with filelen %u\n", nAllocatedSizeIndex, nFileLenIndex); + + static CMemoryPool mp(bytesForChainWalkSet + nAllocatedSizeIndex, debug, maxMem); + + if (pIndex != NULL && nAllocatedSizeIndex > 0) + { + nAllocatedSizeIndex = nAllocatedSizeIndex / sizeof(IndexChain) * sizeof(IndexChain); // Round to sizeOfIndexChain boundary + + fseek(fIndex, 0, SEEK_SET); + + while ( (unsigned long)ftell(fIndex) != nFileLenIndex ) // Index chunk read loop + { + // Load index chunk + if (debug) printf("Debug: Setting index to 0x00 in memory, %llu bytes\n", nAllocatedSizeIndex); + memset(pIndex, 0x00, nAllocatedSizeIndex); + printf("reading index... "); + gettimeofday( &tv, NULL ); + unsigned int nDataRead = fread(pIndex, 1, nAllocatedSizeIndex, fIndex); + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + + float fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + printf("%u bytes read, disk access time: %.2f s\n", nDataRead, fTime); + m_fTotalDiskAccessTime += fTime; + + //nIndexSize = nFileLenIndex / 11; + int nIndexChainCountRead = nDataRead / sizeof(IndexChain); + //fclose(fIndex); + unsigned int nCoveredRainbowTableChains = 0; + for(int i = 0; i < nIndexChainCountRead; i++) + { + nCoveredRainbowTableChains += pIndex[i].nChainCount; + } + + //RainbowChain* pChain = (RainbowChain*)mp.Allocate(nFileLen, nAllocatedSize); + RainbowChain* pChain = (RainbowChain*)mp.Allocate(nCoveredRainbowTableChains * sizeOfChain, nAllocatedSize); + if (debug) printf("Debug: Allocated %llu bytes for %u chains, filelen %lu\n", nAllocatedSize, nCoveredRainbowTableChains, (unsigned long)nFileLen); + + if (pChain != NULL && nAllocatedSize > 0) + { + nAllocatedSize = nAllocatedSize / sizeOfChain * sizeOfChain; // Round to sizeOfChain boundary + + //fseek(file, 0, SEEK_SET); + //bool fVerified = false; + UINT4 nProcessedChains = 0; + while ( (unsigned long)ftell(file) != nFileLen + && nProcessedChains < nCoveredRainbowTableChains ) // Chunk read loop + { + // Load table chunk + if (debug) printf("Debug: Setting pChain to 0x00 in memory\n"); + memset(pChain, 0x00, nAllocatedSize); + printf("reading table... "); + gettimeofday( &tv, NULL ); + unsigned int nDataRead = fread(pChain, 1, nAllocatedSize, file); + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + + float fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + printf("%u bytes read, disk access time: %.2f s\n", nDataRead, fTime); + m_fTotalDiskAccessTime += fTime; + int nRainbowChainCountRead = nDataRead / sizeOfChain; + // Verify table chunk (Too lazy to implement this) + + if (!fVerified) + { + printf("verifying the file... "); + + // Chain length test + int nIndexToVerify = nRainbowChainCountRead / 2; + CChainWalkContext cwc; + uint64 nIndexS; + nIndexS = pChain[nIndexToVerify].nIndexS & 0x0000FFFFFFFFFFFFULL; // for first 6 bytes + + //printf("nIndexS: %s\n", uint64tostr(nIndexS).c_str()); + cwc.SetIndex(nIndexS); + + int nPos; + for (nPos = 0; nPos < nRainbowChainLen - 1; nPos++) + { + cwc.IndexToPlain(); + cwc.PlainToHash(); + cwc.HashToIndex(nPos); + } + + uint64 nEndPoint = 0; + + //for(int i = 0; i < nIndexSize; i++) + for(int i = 0; i < nIndexChainCountRead; i++) + { + if(nIndexToVerify >= pIndex[i].nFirstChain && nIndexToVerify < pIndex[i].nFirstChain + pIndex[i].nChainCount) // We found the matching index + { // Now we need to seek nIndexToVerify into the chains + nEndPoint += (pIndex[i].nPrefix & 0x000000FFFFFFFFFFULL) << 16; // & 0x000000FFFFFFFFFFULL for first 5 bytes + //printf("nPrefix: %s\n", uint64tostr(pIndex[i].nPrefix & 0x000000FFFFFFFFFF).c_str()); + //printf("nFirstChain: %d\n", pIndex[i].nFirstChain); + //printf("nChainCount: %d\n", pIndex[i].nChainCount); + nEndPoint += pChain[nIndexToVerify].nIndexE; + break; + } + } + + if (cwc.GetIndex() != nEndPoint) + { + printf("rainbow chain length verify fail\n"); + break; + } + + fVerified = true; + printf("ok\n"); + } + + // Search table chunk + gettimeofday( &tv, NULL ); + float preTime = m_fTotalCryptanalysisTime; + + SearchTableChunk(pChain, nRainbowChainLen, nRainbowChainCountRead, hs, pIndex, nIndexChainCountRead, nProcessedChains); + float postTime = m_fTotalCryptanalysisTime; + gettimeofday( &tv2, NULL ); + final = sub_timeofday( tv2, tv ); + + fTime = 1.0f * final.tv_sec + 1.0f * final.tv_usec / 1000000; + printf("cryptanalysis time: %.2f s\n", fTime + postTime - preTime); + m_fTotalCryptanalysisTime += fTime; + nProcessedChains += nRainbowChainCountRead; + // Already finished? + if (!hs.AnyHashLeftWithLen(CChainWalkContext::GetHashLen())) + break; + } + } + else printf("memory allocation failed for rainbow table\n"); + + //delete pChain; + } + } + else printf("memory allocation failed for index\n"); + } + } + else + { + printf("Can't load index\n"); + return; + } + fclose(fIndex); + + //delete pIndex; + } + } + fclose(file); + + if (debug) printf("Debug: writing progress to %s\n", sProgressPathName.c_str()); + FILE* file = fopen(sProgressPathName.c_str(), "a"); + if (file!=NULL) + { + string buffer = sPathName + "\n"; + fputs (buffer.c_str(), file); + fclose (file); + } + } + else + printf("can't open file\n"); +} + +void CCrackEngine::Run(vector vPathName, CHashSet& hs, int i_maxThreads, uint64 i_maxMem, bool resume, bool bDebug) +{ +#ifndef _WIN32 + tty_init(); +#endif + resumeSession = resume; + debug = bDebug; + + maxThreads = i_maxThreads; + maxMem = i_maxMem; + // Reset statistics + ResetStatistics(); + + // Sort vPathName (CChainWalkSet need it) + UINT4 i, j; + for (i = 0; i < vPathName.size() - 1; i++) + for (j = 0; j < vPathName.size() - i - 1; j++) + { + if (vPathName[j] > vPathName[j + 1]) + { + string sTemp; + sTemp = vPathName[j]; + vPathName[j] = vPathName[j + 1]; + vPathName[j + 1] = sTemp; + } + } + + // Run + for (i = 0; i < vPathName.size() && hs.AnyhashLeft(); i++) + { + SearchRainbowTable(vPathName[i], hs); + printf("\n"); + } + + // delete precalc files + if (!keepPrecalcFiles) + m_cws.removePrecalcFiles(); + +#ifndef _WIN32 + tty_done(); +#endif +} + +void CCrackEngine::setOutputFile(string sPathName) +{ + writeOutput = true; + outputFile = sPathName; +} + +void CCrackEngine::setSession(string sSession, string sProgress, string sPrecalc, bool keepPrecalc) +{ + sSessionPathName = sSession; + sProgressPathName = sProgress; + sPrecalcPathName = sPrecalc; + keepPrecalcFiles = keepPrecalc; +} + +float CCrackEngine::GetStatTotalDiskAccessTime() +{ + return m_fTotalDiskAccessTime; +} +/*float CCrackEngine::GetWastedTime() +{ + return m_fIndexTime; +}*/ +float CCrackEngine::GetStatTotalCryptanalysisTime() +{ + return m_fTotalCryptanalysisTime; +} + +float CCrackEngine::GetStatTotalPrecalculationTime() +{ + return m_fTotalPrecalculationTime; +} + +int CCrackEngine::GetStatTotalChainWalkStep() +{ + return m_nTotalChainWalkStep; +} + +int CCrackEngine::GetStatTotalFalseAlarm() +{ + return m_nTotalFalseAlarm; +} + +int CCrackEngine::GetStatTotalChainWalkStepDueToFalseAlarm() +{ + return m_nTotalChainWalkStepDueToFalseAlarm; +} diff --git a/Client Applications/rcracki_mt/CrackEngine.h b/Client Applications/rcracki_mt/CrackEngine.h index e69bb0f..5826e7f 100644 --- a/Client Applications/rcracki_mt/CrackEngine.h +++ b/Client Applications/rcracki_mt/CrackEngine.h @@ -1,75 +1,98 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifndef _CRACKENGINE_H -#define _CRACKENGINE_H - -#include "Public.h" -#include "HashSet.h" -#include "ChainWalkContext.h" -#include "MemoryPool.h" -#include "ChainWalkSet.h" -#include "rcrackiThread.h" -#ifdef _WIN32 -#include -#include -#endif -#include - -class CCrackEngine -{ -public: - CCrackEngine(); - virtual ~CCrackEngine(); - -private: - CChainWalkSet m_cws; - int maxThreads; - bool writeOutput; - bool resumeSession; - string outputFile; - string sSessionPathName; - string sProgressPathName; - string sPrecalcPathName; - //string sPrecalcIndexPathName; - bool debug; - bool keepPrecalcFiles; - - // Statistics - float m_fTotalDiskAccessTime; - float m_fTotalCryptanalysisTime; - int m_nTotalChainWalkStep; - int m_nTotalFalseAlarm; - int m_nTotalChainWalkStepDueToFalseAlarm; - FILE *m_fChains; - -private: - void ResetStatistics(); - RainbowChain *BinarySearch(RainbowChain *pChain, int nChainCountRead, uint64 nIndex, IndexChain *pIndex, int nIndexSize, int nIndexStart); - int BinarySearchOld(RainbowChainO* pChain, int nRainbowChainCount, uint64 nIndex); - void GetChainIndexRangeWithSameEndpoint(RainbowChainO* pChain, - int nRainbowChainCount, - int nChainIndex, - int& nChainIndexFrom, - int& nChainIndexTo); - void SearchTableChunk(RainbowChain* pChain, int nRainbowChainLen, int nRainbowChainCount, CHashSet& hs, IndexChain *pIndex, int nIndexSize, int nChainStart); - void SearchTableChunkOld(RainbowChainO* pChain, int nRainbowChainLen, int nRainbowChainCount, CHashSet& hs); - //bool CheckAlarm(RainbowChain* pChain, int nGuessedPos, unsigned char* pHash, CHashSet& hs); - //bool CheckAlarmOld(RainbowChainO* pChain, int nGuessedPos, unsigned char* pHash, CHashSet& hs); - -public: - void SearchRainbowTable(string sPathName, CHashSet& hs); - void Run(vector vPathName, CHashSet& hs, int i_maxThreads, bool resume, bool bDebug); - float GetStatTotalDiskAccessTime(); - float GetStatTotalCryptanalysisTime(); - int GetStatTotalChainWalkStep(); - int GetStatTotalFalseAlarm(); - int GetStatTotalChainWalkStepDueToFalseAlarm(); - void setOutputFile(string sPathName); - void setSession(string sSessionPathName, string sProgressPathName, string sPrecalcPathName, bool keepPrecalc); -}; - -#endif +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * Copyright 2010 uroskn + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifndef _CRACKENGINE_H +#define _CRACKENGINE_H + +#include "Public.h" +#include "HashSet.h" +#include "ChainWalkContext.h" +#include "MemoryPool.h" +#include "ChainWalkSet.h" +#include "rcrackiThread.h" +#ifdef _WIN32 +#include +#include +#endif +#include + +class CCrackEngine +{ +public: + CCrackEngine(); + virtual ~CCrackEngine(); + +private: + CChainWalkSet m_cws; + int maxThreads; + uint64 maxMem; + bool writeOutput; + bool resumeSession; + string outputFile; + string sSessionPathName; + string sProgressPathName; + string sPrecalcPathName; + //string sPrecalcIndexPathName; + bool debug; + bool keepPrecalcFiles; + + // Statistics + float m_fTotalDiskAccessTime; + float m_fTotalCryptanalysisTime; + float m_fTotalPrecalculationTime; + int m_nTotalChainWalkStep; + int m_nTotalFalseAlarm; + int m_nTotalChainWalkStepDueToFalseAlarm; + FILE *m_fChains; + +private: + void ResetStatistics(); + RainbowChain *BinarySearch(RainbowChain *pChain, int nChainCountRead, uint64 nIndex, IndexChain *pIndex, int nIndexSize, int nIndexStart); + int BinarySearchOld(RainbowChainO* pChain, int nRainbowChainCount, uint64 nIndex); + void GetChainIndexRangeWithSameEndpoint(RainbowChainO* pChain, + int nRainbowChainCount, + int nChainIndex, + int& nChainIndexFrom, + int& nChainIndexTo); + void SearchTableChunk(RainbowChain* pChain, int nRainbowChainLen, int nRainbowChainCount, CHashSet& hs, IndexChain *pIndex, int nIndexSize, int nChainStart); + void SearchTableChunkOld(RainbowChainO* pChain, int nRainbowChainLen, int nRainbowChainCount, CHashSet& hs); + //bool CheckAlarm(RainbowChain* pChain, int nGuessedPos, unsigned char* pHash, CHashSet& hs); + //bool CheckAlarmOld(RainbowChainO* pChain, int nGuessedPos, unsigned char* pHash, CHashSet& hs); + +public: + void SearchRainbowTable(string sPathName, CHashSet& hs); + void Run(vector vPathName, CHashSet& hs, int i_maxThreads, uint64 i_maxMem, bool resume, bool bDebug); + float GetStatTotalDiskAccessTime(); + float GetStatTotalCryptanalysisTime(); + float GetStatTotalPrecalculationTime(); + int GetStatTotalChainWalkStep(); + int GetStatTotalFalseAlarm(); + int GetStatTotalChainWalkStepDueToFalseAlarm(); + void setOutputFile(string sPathName); + void setSession(string sSessionPathName, string sProgressPathName, string sPrecalcPathName, bool keepPrecalc); +}; + +#endif diff --git a/Client Applications/rcracki_mt/HashAlgorithm.cpp b/Client Applications/rcracki_mt/HashAlgorithm.cpp index 66513fc..caf30d6 100644 --- a/Client Applications/rcracki_mt/HashAlgorithm.cpp +++ b/Client Applications/rcracki_mt/HashAlgorithm.cpp @@ -1,414 +1,436 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei - - Changes: not using OpenSSL routines the slow way anymore, as suggested by jci. -*/ - -#include "HashAlgorithm.h" - -#include "Public.h" - -#include -#include -#include -#include -#include -#include - -//#include "md5.h" -#include "fast_md5.h" -#include "md4.h" -#include "sha1.h" -#ifdef _WIN32 - #pragma comment(lib, "libeay32.lib") -#endif -#define MSCACHE_HASH_SIZE 16 -void setup_des_key(unsigned char key_56[], des_key_schedule &ks) -{ - des_cblock key; - - key[0] = key_56[0]; - key[1] = (key_56[0] << 7) | (key_56[1] >> 1); - key[2] = (key_56[1] << 6) | (key_56[2] >> 2); - key[3] = (key_56[2] << 5) | (key_56[3] >> 3); - key[4] = (key_56[3] << 4) | (key_56[4] >> 4); - key[5] = (key_56[4] << 3) | (key_56[5] >> 5); - key[6] = (key_56[5] << 2) | (key_56[6] >> 6); - key[7] = (key_56[6] << 1); - - //des_set_odd_parity(&key); - des_set_key(&key, ks); -} - -void HashLM(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) -{ - /* - unsigned char data[7] = {0}; - memcpy(data, pPlain, nPlainLen > 7 ? 7 : nPlainLen); - */ - - int i; - for (i = nPlainLen; i < 7; i++) - pPlain[i] = 0; - - static unsigned char magic[] = {0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; - des_key_schedule ks; - //setup_des_key(data, ks); - setup_des_key(pPlain, ks); - des_ecb_encrypt((des_cblock*)magic, (des_cblock*)pHash, ks, DES_ENCRYPT); -} - -void HashLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) -{ - unsigned char pass[14]; - unsigned char pre_lmresp[21]; - static unsigned char magic[] = {0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; - static unsigned char spoofed_challange[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; - des_key_schedule ks; - - memset (pass,0,sizeof(pass)); - memset (pre_lmresp,0,sizeof(pre_lmresp)); - - memcpy (pass,pPlain, nPlainLen); - - setup_des_key(pass, ks); - des_ecb_encrypt((des_cblock*)magic, (des_cblock*)pre_lmresp, ks, DES_ENCRYPT); - - setup_des_key(&pass[7], ks); - des_ecb_encrypt((des_cblock*)magic, (des_cblock*)&pre_lmresp[8], ks, DES_ENCRYPT); - - setup_des_key(pre_lmresp, ks); - des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)pHash, ks, DES_ENCRYPT); - - setup_des_key(&pre_lmresp[7], ks); - des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)&pHash[8], ks, DES_ENCRYPT); - - setup_des_key(&pre_lmresp[14], ks); - des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)&pHash[16], ks, DES_ENCRYPT); - -} - -void HashHALFLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) -{ - unsigned char pre_lmresp[8]; - static unsigned char magic[] = {0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; - static unsigned char salt[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; - - des_key_schedule ks; - unsigned char plain[8] = {0}; - memcpy(plain, pPlain, nPlainLen); - setup_des_key(plain, ks); - des_ecb_encrypt((des_cblock*)magic, (des_cblock*)pre_lmresp, ks, DES_ENCRYPT); - - setup_des_key(pre_lmresp, ks); - des_ecb_encrypt((des_cblock*)salt, (des_cblock*)pHash, ks, DES_ENCRYPT); -} - - - -void HashNTLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) -{ - unsigned char UnicodePlain[MAX_PLAIN_LEN]; - static unsigned char spoofed_challange[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; - - int len = (nPlainLen < 127) ? nPlainLen : 127; - int i; - - for (i = 0; i < len; i++) - { - UnicodePlain[i * 2] = pPlain[i]; - UnicodePlain[i * 2 + 1] = 0x00; - } - - des_key_schedule ks; - unsigned char lm[21]; - - /*MD4_CTX ctx; - MD4_Init(&ctx); - MD4_Update(&ctx, UnicodePlain, len * 2); - MD4_Final(lm, &ctx); */ - MD4_NEW(UnicodePlain, len * 2, lm); - - //MD4(UnicodePlain, len * 2, lm); - lm[16] = lm[17] = lm[18] = lm[19] = lm[20] = 0; - - setup_des_key(lm, ks); - des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)pHash, ks, DES_ENCRYPT); - - setup_des_key(&lm[7], ks); - des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)&pHash[8], ks, DES_ENCRYPT); - - setup_des_key(&lm[14], ks); - des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)&pHash[16], ks, DES_ENCRYPT); -} - - -void HashORACLE(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) -{ - char ToEncrypt[256]; - char temp[256]; - char username[256]; - - DES_cblock iv,iv2; - DES_key_schedule ks1,ks2; - unsigned char deskey_fixed[]={ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; - int i,j; - - strcpy (username, "SYS"); - int userlen = 3; - - strupr ((char*) pPlain); - memset (ToEncrypt,0,sizeof(ToEncrypt)); - - for (i=1,j=0; j ascii - 64 */ - "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -void _crypt_to64(char *s, unsigned long v, int n) -{ - while (--n >= 0) { - *s++ = itoa64[v&0x3f]; - v >>= 6; - } -} - -void HashPIX(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) -{ - char temp[MD5_DIGEST_LENGTH+1]; - unsigned char final[MD5_DIGEST_LENGTH]; - char* pass = (char*) calloc (nPlainLen+MD5_DIGEST_LENGTH,sizeof(char)); - - memcpy (pass,pPlain,nPlainLen); - - /*MD5_CTX ctx; - MD5_Init(&ctx); - MD5_Update(&ctx, (unsigned char *) pass, MD5_DIGEST_LENGTH); - MD5_Final(final, &ctx);*/ - fast_MD5((unsigned char *) pass, MD5_DIGEST_LENGTH, final); - - char* p = (char*) temp; - _crypt_to64(p,*(unsigned long*) (final+0),4); p += 4; - _crypt_to64(p,*(unsigned long*) (final+4),4); p += 4; - _crypt_to64(p,*(unsigned long*) (final+8),4); p += 4; - _crypt_to64(p,*(unsigned long*) (final+12),4); p += 4; - *p=0; - - memcpy(pHash,temp,MD5_DIGEST_LENGTH); - - free (pass); -} - -#ifndef _WIN32 -char *strupr(char *s1) -{ - char *p = s1; - while(*p) - { - toupper(*p); - p++; - } - return s1; -} -#endif +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + * + * Changes: not using OpenSSL routines the slow way anymore, as suggested by jci. + */ + +#include "HashAlgorithm.h" + +#include "Public.h" + +#include +//#include +#include +//#include +#include +//#include +#include "fast_md5.h" +#include "md4.h" +//#include "sha1.h" +#ifdef _WIN32 + #pragma comment(lib, "libeay32.lib") +#endif + +#ifdef __NetBSD__ + #include +#endif + +#define MSCACHE_HASH_SIZE 16 +void setup_des_key(unsigned char key_56[], des_key_schedule &ks) +{ + des_cblock key; + + key[0] = key_56[0]; + key[1] = (key_56[0] << 7) | (key_56[1] >> 1); + key[2] = (key_56[1] << 6) | (key_56[2] >> 2); + key[3] = (key_56[2] << 5) | (key_56[3] >> 3); + key[4] = (key_56[3] << 4) | (key_56[4] >> 4); + key[5] = (key_56[4] << 3) | (key_56[5] >> 5); + key[6] = (key_56[5] << 2) | (key_56[6] >> 6); + key[7] = (key_56[6] << 1); + + //des_set_odd_parity(&key); + des_set_key(&key, ks); +} + +void HashLM(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) +{ + /* + unsigned char data[7] = {0}; + memcpy(data, pPlain, nPlainLen > 7 ? 7 : nPlainLen); + */ + + int i; + for (i = nPlainLen; i < 7; i++) + pPlain[i] = 0; + + static unsigned char magic[] = {0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + des_key_schedule ks; + //setup_des_key(data, ks); + setup_des_key(pPlain, ks); + des_ecb_encrypt((des_cblock*)magic, (des_cblock*)pHash, ks, DES_ENCRYPT); +} + +void HashLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) +{ + unsigned char pass[14]; + unsigned char pre_lmresp[21]; + static unsigned char magic[] = {0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + static unsigned char spoofed_challange[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + des_key_schedule ks; + + memset (pass,0,sizeof(pass)); + memset (pre_lmresp,0,sizeof(pre_lmresp)); + + memcpy (pass,pPlain, nPlainLen); + + setup_des_key(pass, ks); + des_ecb_encrypt((des_cblock*)magic, (des_cblock*)pre_lmresp, ks, DES_ENCRYPT); + + setup_des_key(&pass[7], ks); + des_ecb_encrypt((des_cblock*)magic, (des_cblock*)&pre_lmresp[8], ks, DES_ENCRYPT); + + setup_des_key(pre_lmresp, ks); + des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)pHash, ks, DES_ENCRYPT); + + setup_des_key(&pre_lmresp[7], ks); + des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)&pHash[8], ks, DES_ENCRYPT); + + setup_des_key(&pre_lmresp[14], ks); + des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)&pHash[16], ks, DES_ENCRYPT); + +} + +void HashHALFLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) +{ + unsigned char pre_lmresp[8]; + static unsigned char magic[] = {0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + static unsigned char salt[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + + des_key_schedule ks; + unsigned char plain[8] = {0}; + memcpy(plain, pPlain, nPlainLen); + setup_des_key(plain, ks); + des_ecb_encrypt((des_cblock*)magic, (des_cblock*)pre_lmresp, ks, DES_ENCRYPT); + + setup_des_key(pre_lmresp, ks); + des_ecb_encrypt((des_cblock*)salt, (des_cblock*)pHash, ks, DES_ENCRYPT); +} + + + +void HashNTLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) +{ + unsigned char UnicodePlain[MAX_PLAIN_LEN]; + static unsigned char spoofed_challange[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + + int len = (nPlainLen < 127) ? nPlainLen : 127; + int i; + + for (i = 0; i < len; i++) + { + UnicodePlain[i * 2] = pPlain[i]; + UnicodePlain[i * 2 + 1] = 0x00; + } + + des_key_schedule ks; + unsigned char lm[21]; + + /*MD4_CTX ctx; + MD4_Init(&ctx); + MD4_Update(&ctx, UnicodePlain, len * 2); + MD4_Final(lm, &ctx); */ + MD4_NEW(UnicodePlain, len * 2, lm); + + //MD4(UnicodePlain, len * 2, lm); + lm[16] = lm[17] = lm[18] = lm[19] = lm[20] = 0; + + setup_des_key(lm, ks); + des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)pHash, ks, DES_ENCRYPT); + + setup_des_key(&lm[7], ks); + des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)&pHash[8], ks, DES_ENCRYPT); + + setup_des_key(&lm[14], ks); + des_ecb_encrypt((des_cblock*)spoofed_challange, (des_cblock*)&pHash[16], ks, DES_ENCRYPT); +} + + +void HashORACLE(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) +{ + char ToEncrypt[256]; + char temp[256]; + char username[256]; + + DES_cblock iv,iv2; + DES_key_schedule ks1,ks2; + unsigned char deskey_fixed[]={ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + int i,j; + + strcpy (username, "SYS"); + int userlen = 3; + + strupr ((char*) pPlain); + memset (ToEncrypt,0,sizeof(ToEncrypt)); + + for (i=1,j=0; j ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +void _crypt_to64(char *s, unsigned long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +void HashPIX(unsigned char* pPlain, int nPlainLen, unsigned char* pHash) +{ + char temp[MD5_DIGEST_LENGTH+1]; + unsigned char final[MD5_DIGEST_LENGTH]; + char* pass = (char*) calloc (nPlainLen+MD5_DIGEST_LENGTH,sizeof(char)); + + memcpy (pass,pPlain,nPlainLen); + + /*MD5_CTX ctx; + MD5_Init(&ctx); + MD5_Update(&ctx, (unsigned char *) pass, MD5_DIGEST_LENGTH); + MD5_Final(final, &ctx);*/ + fast_MD5((unsigned char *) pass, MD5_DIGEST_LENGTH, final); + + char* p = (char*) temp; + _crypt_to64(p,*(unsigned long*) (final+0),4); p += 4; + _crypt_to64(p,*(unsigned long*) (final+4),4); p += 4; + _crypt_to64(p,*(unsigned long*) (final+8),4); p += 4; + _crypt_to64(p,*(unsigned long*) (final+12),4); p += 4; + *p=0; + + memcpy(pHash,temp,MD5_DIGEST_LENGTH); + + free (pass); +} + +#if !defined(_WIN32) || defined(__GNUC__) +char *strupr(char *s1) +{ + char *p = s1; + while(*p) + { + *p = (char) toupper(*p); + p++; + } + return s1; +} +#endif diff --git a/Client Applications/rcracki_mt/HashAlgorithm.h b/Client Applications/rcracki_mt/HashAlgorithm.h index 9958cbc..c22a047 100644 --- a/Client Applications/rcracki_mt/HashAlgorithm.h +++ b/Client Applications/rcracki_mt/HashAlgorithm.h @@ -1,42 +1,61 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifndef _HASHALGORITHM_H -#define _HASHALGORITHM_H - -void HashLM(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashNTLM(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashMD2(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashMD4(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashMD5(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashDoubleMD5(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashSHA1(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashRIPEMD160(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashMSCACHE(unsigned char *pPlain, int nPlainLen, unsigned char* pHash); -//**************************************************************************** -// MySQL Password Hashing -//**************************************************************************** -void HashMySQL323(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashMySQLSHA1(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); - -//**************************************************************************** -// Cisco PIX Password Hashing -//**************************************************************************** -void HashPIX(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); - -//**************************************************************************** -// (HALF) LM CHALL hashing -void HashLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashHALFLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); - -// From mao -void HashNTLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); -void HashORACLE(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); - -#ifndef _WIN32 -char *strupr(char *s1); -#endif -#endif +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifndef _HASHALGORITHM_H +#define _HASHALGORITHM_H + +void HashLM(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +void HashNTLM(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +void HashMD2(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +void HashMD4(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +void HashMD5(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +void HashDoubleMD5(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +void HashSHA1(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +//void HashRIPEMD160(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +void HashMSCACHE(unsigned char *pPlain, int nPlainLen, unsigned char* pHash); +//**************************************************************************** +// MySQL Password Hashing +//**************************************************************************** +void HashMySQL323(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +void HashMySQLSHA1(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); + +//**************************************************************************** +// Cisco PIX Password Hashing +//**************************************************************************** +void HashPIX(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); + +//**************************************************************************** +// (HALF) LM CHALL hashing +void HashLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +void HashHALFLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); + +// From mao +void HashNTLMCHALL(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); +void HashORACLE(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); + +#if !defined(_WIN32) || defined(__GNUC__) +char *strupr(char *s1); +#endif +#endif diff --git a/Client Applications/rcracki_mt/HashRoutine.cpp b/Client Applications/rcracki_mt/HashRoutine.cpp index 02fbe71..e4dc811 100644 --- a/Client Applications/rcracki_mt/HashRoutine.cpp +++ b/Client Applications/rcracki_mt/HashRoutine.cpp @@ -1,78 +1,96 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786 4267 4018) -#endif - -#include "HashRoutine.h" -#include "HashAlgorithm.h" - -////////////////////////////////////////////////////////////////////// - -CHashRoutine::CHashRoutine() -{ - // Notice: MIN_HASH_LEN <= nHashLen <= MAX_HASH_LEN - - - AddHashRoutine("lm", HashLM, 8); - AddHashRoutine("ntlm", HashNTLM, 16); - AddHashRoutine("md2", HashMD2, 16); - AddHashRoutine("md4", HashMD4, 16); - AddHashRoutine("md5", HashMD5, 16); - AddHashRoutine("doublemd5", HashDoubleMD5, 16); - AddHashRoutine("sha1", HashSHA1, 20); - AddHashRoutine("ripemd160", HashRIPEMD160, 20); - AddHashRoutine("mysql323", HashMySQL323, 8); - AddHashRoutine("mysqlsha1", HashMySQLSHA1, 20); - AddHashRoutine("ciscopix", HashPIX, 16); - AddHashRoutine("mscache", HashMSCACHE, 16); - AddHashRoutine("halflmchall", HashHALFLMCHALL, 8); - - // Added from mao - AddHashRoutine("lmchall", HashLMCHALL, 24); - AddHashRoutine("ntlmchall", HashNTLMCHALL, 24); - AddHashRoutine("oracle", HashORACLE, 8); - -} - -CHashRoutine::~CHashRoutine() -{ -} - -void CHashRoutine::AddHashRoutine(string sHashRoutineName, HASHROUTINE pHashRoutine, int nHashLen) -{ - vHashRoutineName.push_back(sHashRoutineName); - vHashRoutine.push_back(pHashRoutine); - vHashLen.push_back(nHashLen); -} - -string CHashRoutine::GetAllHashRoutineName() -{ - string sRet; - int i; - for (i = 0; i < vHashRoutineName.size(); i++) - sRet += vHashRoutineName[i] + " "; - - return sRet; -} - -void CHashRoutine::GetHashRoutine(string sHashRoutineName, HASHROUTINE& pHashRoutine, int& nHashLen) -{ - int i; - for (i = 0; i < vHashRoutineName.size(); i++) - { - if (sHashRoutineName == vHashRoutineName[i]) - { - pHashRoutine = vHashRoutine[i]; - nHashLen = vHashLen[i]; - return; - } - } - - pHashRoutine = NULL; - nHashLen = 0; -} +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifdef _WIN32 + #pragma warning(disable : 4786 4267 4018) +#endif + +#include "HashRoutine.h" +#include "HashAlgorithm.h" + +////////////////////////////////////////////////////////////////////// + +CHashRoutine::CHashRoutine() +{ + // Notice: MIN_HASH_LEN <= nHashLen <= MAX_HASH_LEN + + + AddHashRoutine("lm", HashLM, 8); + AddHashRoutine("ntlm", HashNTLM, 16); +// AddHashRoutine("md2", HashMD2, 16); + AddHashRoutine("md4", HashMD4, 16); + AddHashRoutine("md5", HashMD5, 16); + AddHashRoutine("doublemd5", HashDoubleMD5, 16); + AddHashRoutine("sha1", HashSHA1, 20); +// AddHashRoutine("ripemd160", HashRIPEMD160, 20); + AddHashRoutine("mysql323", HashMySQL323, 8); + AddHashRoutine("mysqlsha1", HashMySQLSHA1, 20); + AddHashRoutine("ciscopix", HashPIX, 16); + AddHashRoutine("mscache", HashMSCACHE, 16); + AddHashRoutine("halflmchall", HashHALFLMCHALL, 8); + + // Added from mao + AddHashRoutine("lmchall", HashLMCHALL, 24); + AddHashRoutine("ntlmchall", HashNTLMCHALL, 24); + AddHashRoutine("oracle", HashORACLE, 8); +} + +CHashRoutine::~CHashRoutine() +{ +} + +void CHashRoutine::AddHashRoutine(string sHashRoutineName, HASHROUTINE pHashRoutine, int nHashLen) +{ + vHashRoutineName.push_back(sHashRoutineName); + vHashRoutine.push_back(pHashRoutine); + vHashLen.push_back(nHashLen); +} + +string CHashRoutine::GetAllHashRoutineName() +{ + string sRet; + UINT4 i; + for (i = 0; i < vHashRoutineName.size(); i++) + sRet += vHashRoutineName[i] + " "; + + return sRet; +} + +void CHashRoutine::GetHashRoutine(string sHashRoutineName, HASHROUTINE& pHashRoutine, int& nHashLen) +{ + UINT4 i; + for (i = 0; i < vHashRoutineName.size(); i++) + { + if (sHashRoutineName == vHashRoutineName[i]) + { + pHashRoutine = vHashRoutine[i]; + nHashLen = vHashLen[i]; + return; + } + } + + pHashRoutine = NULL; + nHashLen = 0; +} diff --git a/Client Applications/rcracki_mt/HashRoutine.h b/Client Applications/rcracki_mt/HashRoutine.h index 681fa78..ebd0a17 100644 --- a/Client Applications/rcracki_mt/HashRoutine.h +++ b/Client Applications/rcracki_mt/HashRoutine.h @@ -1,33 +1,55 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifndef _HASHROUTINE_H -#define _HASHROUTINE_H - -#include -#include -using namespace std; - -typedef void (*HASHROUTINE)(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); - -class CHashRoutine -{ -public: - CHashRoutine(); - virtual ~CHashRoutine(); - -private: - vector vHashRoutineName; - vector vHashRoutine; - vector vHashLen; - void AddHashRoutine(string sHashRoutineName, HASHROUTINE pHashRoutine, int nHashLen); - -public: - string GetAllHashRoutineName(); - void GetHashRoutine(string sHashRoutineName, HASHROUTINE& pHashRoutine, int& nHashLen); -}; - -#endif +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifndef _HASHROUTINE_H +#define _HASHROUTINE_H + +#include +#include + +#include "global.h" + +using namespace std; + +typedef void (*HASHROUTINE)(unsigned char* pPlain, int nPlainLen, unsigned char* pHash); + +class CHashRoutine +{ +public: + CHashRoutine(); + virtual ~CHashRoutine(); + +private: + vector vHashRoutineName; + vector vHashRoutine; + vector vHashLen; + void AddHashRoutine(string sHashRoutineName, HASHROUTINE pHashRoutine, int nHashLen); + +public: + string GetAllHashRoutineName(); + void GetHashRoutine(string sHashRoutineName, HASHROUTINE& pHashRoutine, int& nHashLen); +}; + +#endif diff --git a/Client Applications/rcracki_mt/HashSet.cpp b/Client Applications/rcracki_mt/HashSet.cpp index cf8afdb..853efca 100644 --- a/Client Applications/rcracki_mt/HashSet.cpp +++ b/Client Applications/rcracki_mt/HashSet.cpp @@ -1,164 +1,183 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786 4267 4018) -#endif - -#include "HashSet.h" - -CHashSet::CHashSet() -{ -} - -CHashSet::~CHashSet() -{ -} - -void CHashSet::AddHash(string sHash) -{ - if (sHash == "aad3b435b51404ee") - return; - - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (m_vHash[i] == sHash) - return; - } - - //printf("debug: adding hash %s\n", sHash.c_str()); - - m_vHash.push_back(sHash); - m_vFound.push_back(false); - m_vPlain.push_back(""); - m_vBinary.push_back(""); -} - -string CHashSet::GetHashInfo(int i) -{ - string found; - if (m_vFound[i]) - found = "1"; - else - found = "0"; - - string buffer = m_vHash[i] + ":" + found + ":" + m_vPlain[i] + ":" + m_vBinary[i]; - - return buffer; -} - -bool CHashSet::AnyhashLeft() -{ - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (!m_vFound[i]) - return true; - } - - return false; -} - -bool CHashSet::AnyHashLeftWithLen(int nLen) -{ - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (!m_vFound[i]) - if (m_vHash[i].size() == nLen * 2) - return true; - } - - return false; -} - -void CHashSet::GetLeftHashWithLen(vector& vHash, int nLen) -{ - vHash.clear(); - - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (!m_vFound[i]) - if (m_vHash[i].size() == nLen * 2) - vHash.push_back(m_vHash[i]); - } -} - -void CHashSet::AddHashInfo(string sHash, bool found, string sPlain, string sBinary) -{ - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (m_vHash[i] == sHash) - return; - } - - m_vHash.push_back(sHash); - m_vFound.push_back(found); - m_vPlain.push_back(sPlain); - m_vBinary.push_back(sBinary); -} - -void CHashSet::SetPlain(string sHash, string sPlain, string sBinary) -{ - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (m_vHash[i] == sHash) - { - m_vFound[i] = true; - m_vPlain[i] = sPlain; - m_vBinary[i] = sBinary; - return; - } - } -} - -bool CHashSet::GetPlain(string sHash, string& sPlain, string& sBinary) -{ - if (sHash == "aad3b435b51404ee") - { - sPlain = ""; - sBinary = ""; - return true; - } - - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (m_vHash[i] == sHash) - { - if (m_vFound[i]) - { - sPlain = m_vPlain[i]; - sBinary = m_vBinary[i]; - return true; - } - } - } - - return false; -} - -int CHashSet::GetStatHashFound() -{ - int nHashFound = 0; - int i; - for (i = 0; i < m_vHash.size(); i++) - { - if (m_vFound[i]) - nHashFound++; - } - - return nHashFound; -} - -int CHashSet::GetStatHashTotal() -{ - return m_vHash.size(); -} +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifdef _WIN32 + #pragma warning(disable : 4786 4267 4018) +#endif + +#include "HashSet.h" + +CHashSet::CHashSet() +{ +} + +CHashSet::~CHashSet() +{ +} + +void CHashSet::AddHash(string sHash) +{ + if (sHash == "aad3b435b51404ee") + return; + + UINT4 i; + for (i = 0; i < m_vHash.size(); i++) + { + if (m_vHash[i] == sHash) + return; + } + + //printf("debug: adding hash %s\n", sHash.c_str()); + + m_vHash.push_back(sHash); + m_vFound.push_back(false); + m_vPlain.push_back(""); + m_vBinary.push_back(""); +} + +string CHashSet::GetHashInfo(int i) +{ + string found; + if (m_vFound[i]) + found = "1"; + else + found = "0"; + + string buffer = m_vHash[i] + ":" + found + ":" + m_vPlain[i] + ":" + m_vBinary[i]; + + return buffer; +} + +bool CHashSet::AnyhashLeft() +{ + UINT4 i; + for (i = 0; i < m_vHash.size(); i++) + { + if (!m_vFound[i]) + return true; + } + + return false; +} + +bool CHashSet::AnyHashLeftWithLen(int nLen) +{ + UINT4 i; + for (i = 0; i < m_vHash.size(); i++) + { + if (!m_vFound[i]) + if (m_vHash[i].size() == (unsigned long)nLen * 2) + return true; + } + + return false; +} + +void CHashSet::GetLeftHashWithLen(vector& vHash, int nLen) +{ + vHash.clear(); + + UINT4 i; + for (i = 0; i < m_vHash.size(); i++) + { + if (!m_vFound[i]) + if (m_vHash[i].size() == (unsigned long)nLen * 2) + vHash.push_back(m_vHash[i]); + } +} + +void CHashSet::AddHashInfo(string sHash, bool found, string sPlain, string sBinary) +{ + UINT4 i; + for (i = 0; i < m_vHash.size(); i++) + { + if (m_vHash[i] == sHash) + return; + } + + m_vHash.push_back(sHash); + m_vFound.push_back(found); + m_vPlain.push_back(sPlain); + m_vBinary.push_back(sBinary); +} + +void CHashSet::SetPlain(string sHash, string sPlain, string sBinary) +{ + UINT4 i; + for (i = 0; i < m_vHash.size(); i++) + { + if (m_vHash[i] == sHash) + { + m_vFound[i] = true; + m_vPlain[i] = sPlain; + m_vBinary[i] = sBinary; + return; + } + } +} + +bool CHashSet::GetPlain(string sHash, string& sPlain, string& sBinary) +{ + if (sHash == "aad3b435b51404ee") + { + sPlain = ""; + sBinary = ""; + return true; + } + + UINT4 i; + for (i = 0; i < m_vHash.size(); i++) + { + if (m_vHash[i] == sHash) + { + if (m_vFound[i]) + { + sPlain = m_vPlain[i]; + sBinary = m_vBinary[i]; + return true; + } + } + } + + return false; +} + +int CHashSet::GetStatHashFound() +{ + int nHashFound = 0; + UINT4 i; + for (i = 0; i < m_vHash.size(); i++) + { + if (m_vFound[i]) + nHashFound++; + } + + return nHashFound; +} + +int CHashSet::GetStatHashTotal() +{ + return (int) m_vHash.size(); +} diff --git a/Client Applications/rcracki_mt/HashSet.h b/Client Applications/rcracki_mt/HashSet.h index 746bb72..17ed129 100644 --- a/Client Applications/rcracki_mt/HashSet.h +++ b/Client Applications/rcracki_mt/HashSet.h @@ -1,40 +1,59 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifndef _HASHSET_H -#define _HASHSET_H - -#include "Public.h" - -class CHashSet -{ -public: - CHashSet(); - virtual ~CHashSet(); - -private: - vector m_vHash; - vector m_vFound; - vector m_vPlain; - vector m_vBinary; - -public: - void AddHash(string sHash); // lowercase, len % 2 == 0, MIN_HASH_LEN * 2 <= len <= MAX_HASH_LEN * 2 - bool AnyhashLeft(); - bool AnyHashLeftWithLen(int nLen); - void GetLeftHashWithLen(vector& vHash, int nLen); - - void SetPlain(string sHash, string sPlain, string sBinary); - bool GetPlain(string sHash, string& sPlain, string& sBinary); - - int GetStatHashFound(); - int GetStatHashTotal(); - - string GetHashInfo(int i); - void AddHashInfo(string sHash, bool found, string sPlain, string sBinary); -}; - -#endif +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifndef _HASHSET_H +#define _HASHSET_H + +#include "Public.h" + +class CHashSet +{ +public: + CHashSet(); + virtual ~CHashSet(); + +private: + vector m_vHash; + vector m_vFound; + vector m_vPlain; + vector m_vBinary; + +public: + void AddHash(string sHash); // lowercase, len % 2 == 0, MIN_HASH_LEN * 2 <= len <= MAX_HASH_LEN * 2 + bool AnyhashLeft(); + bool AnyHashLeftWithLen(int nLen); + void GetLeftHashWithLen(vector& vHash, int nLen); + + void SetPlain(string sHash, string sPlain, string sBinary); + bool GetPlain(string sHash, string& sPlain, string& sBinary); + + int GetStatHashFound(); + int GetStatHashTotal(); + + string GetHashInfo(int i); + void AddHashInfo(string sHash, bool found, string sPlain, string sBinary); +}; + +#endif diff --git a/Client Applications/rcracki_mt/MemoryPool.cpp b/Client Applications/rcracki_mt/MemoryPool.cpp index 2129daa..f48961d 100644 --- a/Client Applications/rcracki_mt/MemoryPool.cpp +++ b/Client Applications/rcracki_mt/MemoryPool.cpp @@ -1,81 +1,106 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#include "MemoryPool.h" -#include "Public.h" - -CMemoryPool::CMemoryPool(unsigned int bytesForChainWalkSet) -{ - m_pMem = NULL; - m_nMemSize = 0; - - unsigned int nAvailPhys = GetAvailPhysMemorySize(); - if (nAvailPhys < 32 * 1024 * 1024) - { - nAvailPhys = 256 * 1024 * 1024; // There is atleast 256 mb available (Some Linux distros returns a really low GetAvailPhysMemorySize()) - } - - m_nMemMax = nAvailPhys - bytesForChainWalkSet; // Leave memory for CChainWalkSet - - if (m_nMemMax < 16 * 1024 * 1024) - m_nMemMax = 16 * 1024 * 1024; - -} - -CMemoryPool::~CMemoryPool() -{ - if (m_pMem != NULL) - { - delete m_pMem; - m_pMem = NULL; - m_nMemSize = 0; - } -} - -unsigned char* CMemoryPool::Allocate(unsigned int nFileLen, unsigned int& nAllocatedSize) -{ - if (nFileLen <= m_nMemSize) - { - nAllocatedSize = nFileLen; - return m_pMem; - } - - unsigned int nTargetSize; - if (nFileLen < m_nMemMax) - nTargetSize = nFileLen; - else - nTargetSize = m_nMemMax; - - // Free existing memory - if (m_pMem != NULL) - { - delete m_pMem; - m_pMem = NULL; - m_nMemSize = 0; - } - - // Allocate new memory - //printf("allocating %u bytes memory\n", nTargetSize); - m_pMem = new (nothrow) unsigned char[nTargetSize]; - while (m_pMem == NULL && nTargetSize >= 32 * 1024 * 1024 ) - { - nTargetSize -= 16 * 1024 * 1024; - m_pMem = new (nothrow) unsigned char[nTargetSize]; - } - - if (m_pMem != NULL) - { - m_nMemSize = nTargetSize; - nAllocatedSize = nTargetSize; - return m_pMem; - } - else - { - m_nMemSize = 0; - nAllocatedSize = 0; - return NULL; - } -} +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * Copyright 2010 uroskn + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#include "MemoryPool.h" +#include "Public.h" + +CMemoryPool::CMemoryPool(unsigned int bytesSaved, bool bDebug, uint64 maxMem) +{ + m_pMem = NULL; + m_nMemSize = 0; + debug = bDebug; + + uint64 nAvailPhys = GetAvailPhysMemorySize(); + + if ( debug ) + { + printf( "Debug: nAvailPhys: %llu\n", nAvailPhys ); + printf( "Debug: bytesSaved: %d\n", bytesSaved ); + } + + if ( maxMem > 0 && maxMem < nAvailPhys ) + nAvailPhys = maxMem; + + m_nMemMax = nAvailPhys - bytesSaved; // Leave memory for CChainWalkSet + + if (m_nMemMax < 16 * 1024 * 1024) + m_nMemMax = 16 * 1024 * 1024; +} + +CMemoryPool::~CMemoryPool() +{ + if (m_pMem != NULL) + { + delete [] m_pMem; + m_pMem = NULL; + m_nMemSize = 0; + } +} + +unsigned char* CMemoryPool::Allocate(unsigned int nFileLen, uint64& nAllocatedSize) +{ + if (nFileLen <= m_nMemSize) + { + nAllocatedSize = nFileLen; + return m_pMem; + } + + unsigned int nTargetSize; + if (nFileLen < m_nMemMax) + nTargetSize = nFileLen; + else + nTargetSize = m_nMemMax; + + // Free existing memory + if (m_pMem != NULL) + { + delete [] m_pMem; + m_pMem = NULL; + m_nMemSize = 0; + } + + // Allocate new memory + //printf("allocating %u bytes memory\n", nTargetSize); + m_pMem = new (nothrow) unsigned char[nTargetSize]; + while (m_pMem == NULL && nTargetSize >= 32 * 1024 * 1024 ) + { + nTargetSize -= 16 * 1024 * 1024; + m_pMem = new (nothrow) unsigned char[nTargetSize]; + } + + if (m_pMem != NULL) + { + m_nMemSize = nTargetSize; + nAllocatedSize = nTargetSize; + return m_pMem; + } + else + { + m_nMemSize = 0; + nAllocatedSize = 0; + return NULL; + } +} diff --git a/Client Applications/rcracki_mt/MemoryPool.h b/Client Applications/rcracki_mt/MemoryPool.h index 91d8214..a53c486 100644 --- a/Client Applications/rcracki_mt/MemoryPool.h +++ b/Client Applications/rcracki_mt/MemoryPool.h @@ -1,26 +1,49 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifndef _MEMORYPOOL_H -#define _MEMORYPOOL_H - -class CMemoryPool -{ -public: - CMemoryPool(unsigned int bytesForChainWalkSet); - virtual ~CMemoryPool(); - -private: - unsigned char* m_pMem; - unsigned int m_nMemSize; - - unsigned int m_nMemMax; - -public: - unsigned char* Allocate(unsigned int nFileLen, unsigned int& nAllocatedSize); -}; - -#endif +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * Copyright 2010 uroskn + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifndef _MEMORYPOOL_H +#define _MEMORYPOOL_H + +#include "global.h" + +class CMemoryPool +{ +public: + CMemoryPool(unsigned int bytesSaved, bool bDebug, uint64 maxMem); + virtual ~CMemoryPool(); + +private: + bool debug; + unsigned char* m_pMem; + uint64 m_nMemSize; + + uint64 m_nMemMax; + +public: + unsigned char* Allocate(unsigned int nFileLen, uint64& nAllocatedSize); +}; + +#endif diff --git a/Client Applications/rcracki_mt/Public.cpp b/Client Applications/rcracki_mt/Public.cpp index 6b98157..0aba1a8 100644 --- a/Client Applications/rcracki_mt/Public.cpp +++ b/Client Applications/rcracki_mt/Public.cpp @@ -1,311 +1,432 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786 4267 4018) -#endif - -#include "Public.h" - -#ifdef _WIN32 - #include -#else - #include -#endif - -////////////////////////////////////////////////////////////////////// - -unsigned int GetFileLen(FILE* file) -{ - unsigned int pos = ftell(file); - fseek(file, 0, SEEK_END); - unsigned int len = ftell(file); - fseek(file, pos, SEEK_SET); - - return len; -} - -string TrimString(string s) -{ - while (s.size() > 0) - { - if (s[0] == ' ' || s[0] == '\t') - s = s.substr(1); - else - break; - } - - while (s.size() > 0) - { - if (s[s.size() - 1] == ' ' || s[s.size() - 1] == '\t') - s = s.substr(0, s.size() - 1); - else - break; - } - - return s; -} -bool GetHybridCharsets(string sCharset, vector& vCharset) -{ - // Example: hybrid(mixalpha-numeric-all-space#1-6,numeric#1-4) - if(sCharset.substr(0, 6) != "hybrid") // Not hybrid charset - return false; - size_t nEnd = sCharset.rfind(')'); - size_t nStart = sCharset.rfind('('); - string sChar = sCharset.substr(nStart + 1, nEnd - nStart - 1); - vector vParts; - SeperateString(sChar, ",", vParts); - for(int i = 0; i < vParts.size(); i++) - { - tCharset stCharset; - vector vParts2; - SeperateString(vParts[i], "#", vParts2); - stCharset.sName = vParts2[0]; - vector vParts3; - SeperateString(vParts2[1], "-", vParts3); - stCharset.nPlainLenMin = atoi(vParts3[0].c_str()); - stCharset.nPlainLenMax = atoi(vParts3[1].c_str()); - vCharset.push_back(stCharset); - } - return true; -} -bool ReadLinesFromFile(string sPathName, vector& vLine) -{ - vLine.clear(); - - FILE* file = fopen(sPathName.c_str(), "rb"); - if (file != NULL) - { - unsigned int len = GetFileLen(file); - char* data = new char[len + 1]; - fread(data, 1, len, file); - data[len] = '\0'; - string content = data; - content += "\n"; - delete data; - - unsigned int i; - for (i = 0; i < content.size(); i++) - { - if (content[i] == '\r') - content[i] = '\n'; - } - - int n; - while ((n = content.find("\n", 0)) != -1) - { - string line = content.substr(0, n); - line = TrimString(line); - if (line != "") - vLine.push_back(line); - content = content.substr(n + 1); - } - - fclose(file); - } - else - return false; - - return true; -} - -bool writeResultLineToFile(string sOutputFile, string sHash, string sPlain, string sBinary) -{ - FILE* file = fopen(sOutputFile.c_str(), "a"); - if (file!=NULL) - { - string buffer = sHash + ":" + sPlain + ":" + sBinary + "\n"; - fputs (buffer.c_str(), file); - fclose (file); - return true; - } - else - return false; -} - -bool SeperateString(string s, string sSeperator, vector& vPart) -{ - vPart.clear(); - - unsigned int i; - for (i = 0; i < sSeperator.size(); i++) - { - int n = s.find(sSeperator[i]); - if (n != -1) - { - vPart.push_back(s.substr(0, n)); - s = s.substr(n + 1); - } - else - { - printf("not found: %c\n", sSeperator[i]); - printf("s: %s\n", s.c_str()); - return false; - } - } - vPart.push_back(s); - - return true; -} - -string uint64tostr(uint64 n) -{ - char str[32]; - -#ifdef _WIN32 - sprintf(str, "%I64u", n); -#else - sprintf(str, "%llu", n); -#endif - - return str; -} - -string uint64tohexstr(uint64 n) -{ - char str[32]; - -#ifdef _WIN32 - sprintf(str, "%016I64x", n); -#else - sprintf(str, "%016llx", n); -#endif - - return str; -} - -string HexToStr(const unsigned char* pData, int nLen) -{ - string sRet; - int i; - for (i = 0; i < nLen; i++) - { - char szByte[3]; - sprintf(szByte, "%02x", pData[i]); - sRet += szByte; - } - - return sRet; -} - -unsigned int GetAvailPhysMemorySize() -{ -#ifdef _WIN32 - MEMORYSTATUS ms; - GlobalMemoryStatus(&ms); - return ms.dwAvailPhys; -#else - struct sysinfo info; - sysinfo(&info); // This function is Linux-specific - return info.freeram; -#endif -} - -string GetApplicationPath() -{ - char fullPath[FILENAME_MAX]; - -#ifdef _WIN32 - GetModuleFileName(NULL, fullPath, FILENAME_MAX); -#else - char szTmp[32]; - sprintf(szTmp, "/proc/%d/exe", getpid()); - int bytes = readlink(szTmp, fullPath, FILENAME_MAX); - if(bytes >= 0) - fullPath[bytes] = '\0'; -#endif - - string sApplicationPath = fullPath; -#ifdef _WIN32 - int nIndex = sApplicationPath.find_last_of('\\'); -#else - int nIndex = sApplicationPath.find_last_of('/'); -#endif - - if (nIndex != -1) - sApplicationPath = sApplicationPath.substr(0, nIndex+1); - - //printf ("\n\nDebug: The application directory is %s\n", sApplicationPath.c_str()); - return sApplicationPath; -} - -void ParseHash(string sHash, unsigned char* pHash, int& nHashLen) -{ - int i; - for (i = 0; i < sHash.size() / 2; i++) - { - string sSub = sHash.substr(i * 2, 2); - int nValue; - sscanf(sSub.c_str(), "%02x", &nValue); - pHash[i] = (unsigned char)nValue; - } - - nHashLen = sHash.size() / 2; -} - -void Logo() -{ - printf("RainbowCrack (improved, multi-threaded) - Making a Faster Cryptanalytic Time-Memory Trade-Off\n"); - printf("by Martin Westergaard \n"); - printf("multi-threaded and enhanced by neinbrucke (version 0.6-svn)\n"); - printf("http://www.freerainbowtables.com/\n"); - printf("original code by Zhu Shuanglei \n"); - printf("http://www.antsight.com/zsl/rainbowcrack/\n\n"); -} - -// Code comes from nmap, used for the linux implementation of kbhit() -#ifndef _WIN32 - -static int tty_fd = 0; -struct termios saved_ti; - -int tty_getchar() -{ - int c, numChars; - - if (tty_fd && tcgetpgrp(tty_fd) == getpid()) { - c = 0; - numChars = read(tty_fd, &c, 1); - if (numChars > 0) return c; - } - - return -1; -} - -void tty_done() -{ - if (!tty_fd) return; - - tcsetattr(tty_fd, TCSANOW, &saved_ti); - - close(tty_fd); - tty_fd = 0; -} - -void tty_init() -{ - struct termios ti; - - if (tty_fd) - return; - - if ((tty_fd = open("/dev/tty", O_RDONLY | O_NONBLOCK)) < 0) return; - - tcgetattr(tty_fd, &ti); - saved_ti = ti; - ti.c_lflag &= ~(ICANON | ECHO); - ti.c_cc[VMIN] = 1; - ti.c_cc[VTIME] = 0; - tcsetattr(tty_fd, TCSANOW, &ti); - - atexit(tty_done); -} - -void tty_flush(void) -{ - tcflush(tty_fd, TCIFLUSH); -} -#endif +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifdef _WIN32 + #pragma warning(disable : 4786 4267 4018) +#endif + +#include "Public.h" + +#ifdef _WIN32 + #include + #include + + #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 + #else + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL + #endif + + struct timezone + { + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ + }; + + int gettimeofday(struct timeval *tv, struct timezone *tz) + { + // Define a structure to receive the current Windows filetime + FILETIME ft; + + // Initialize the present time to 0 and the timezone to UTC + unsigned __int64 tmpres = 0; + static int tzflag = 0; + + if (NULL != tv) + { + GetSystemTimeAsFileTime(&ft); + + // The GetSystemTimeAsFileTime returns the number of 100 nanosecond + // intervals since Jan 1, 1601 in a structure. Copy the high bits to + // the 64 bit tmpres, shift it left by 32 then or in the low 32 bits. + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + // Convert to microseconds by dividing by 10 + tmpres /= 10; + + // The Unix epoch starts on Jan 1 1970. Need to subtract the difference + // in seconds from Jan 1 1601. + tmpres -= DELTA_EPOCH_IN_MICROSECS; + + // Finally change microseconds to seconds and place in the seconds value. + // The modulus picks up the microseconds. + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) + { + if (!tzflag) + { + _tzset(); + tzflag++; + } + + // Adjust for the timezone west of Greenwich + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; + } + +#elif defined(__APPLE__) || \ + ((defined(__unix__) || defined(unix)) && !defined(USG)) + + #include + + #if defined(BSD) + #include + #elif defined(__linux__) + #include + #else + #error Unsupported Operating System + #endif +#endif + +////////////////////////////////////////////////////////////////////// + +timeval sub_timeofday( timeval tv2, timeval tv ) +{ + timeval final; + + final.tv_usec = tv2.tv_usec - tv.tv_usec; + final.tv_sec = tv2.tv_sec - tv.tv_sec; + + if ( final.tv_usec < 0 ) + { + final.tv_usec += 1000000; + --final.tv_sec; + } + + return final; +} + +unsigned int GetFileLen(FILE* file) +{ + long int pos = ftell(file); + fseek(file, 0, SEEK_END); + long int len = ftell(file); + fseek(file, pos, SEEK_SET); + + return len; +} + +string TrimString(string s) +{ + while (s.size() > 0) + { + if (s[0] == ' ' || s[0] == '\t') + s = s.substr(1); + else + break; + } + + while (s.size() > 0) + { + if (s[s.size() - 1] == ' ' || s[s.size() - 1] == '\t') + s = s.substr(0, s.size() - 1); + else + break; + } + + return s; +} +bool GetHybridCharsets(string sCharset, vector& vCharset) +{ + // Example: hybrid(mixalpha-numeric-all-space#1-6,numeric#1-4) + if(sCharset.substr(0, 6) != "hybrid") // Not hybrid charset + return false; + + UINT4 nEnd = (int) sCharset.rfind(')'); + UINT4 nStart = (int) sCharset.rfind('('); + string sChar = sCharset.substr(nStart + 1, nEnd - nStart - 1); + vector vParts; + SeperateString(sChar, ",", vParts); + for(UINT4 i = 0; i < vParts.size(); i++) + { + tCharset stCharset; + vector vParts2; + SeperateString(vParts[i], "#", vParts2); + stCharset.sName = vParts2[0]; + vector vParts3; + SeperateString(vParts2[1], "-", vParts3); + stCharset.nPlainLenMin = atoi(vParts3[0].c_str()); + stCharset.nPlainLenMax = atoi(vParts3[1].c_str()); + vCharset.push_back(stCharset); + } + return true; +} +bool ReadLinesFromFile(string sPathName, vector& vLine) +{ + vLine.clear(); + + FILE* file = fopen(sPathName.c_str(), "rb"); + if (file != NULL) + { + unsigned int len = GetFileLen(file); + char* data = new char[len + 1]; + fread(data, 1, len, file); + data[len] = '\0'; + string content = data; + content += "\n"; + delete [] data; + + unsigned int i; + for (i = 0; i < content.size(); i++) + { + if (content[i] == '\r') + content[i] = '\n'; + } + + int n; + while ((n = content.find("\n", 0)) != -1) + { + string line = content.substr(0, n); + line = TrimString(line); + if (line != "") + vLine.push_back(line); + content = content.substr(n + 1); + } + + fclose(file); + } + else + return false; + + return true; +} + +bool writeResultLineToFile(string sOutputFile, string sHash, string sPlain, string sBinary) +{ + FILE* file = fopen(sOutputFile.c_str(), "a"); + if (file!=NULL) + { + string buffer = sHash + ":" + sPlain + ":" + sBinary + "\n"; + fputs (buffer.c_str(), file); + fclose (file); + return true; + } + else + return false; +} + +bool SeperateString(string s, string sSeperator, vector& vPart) +{ + vPart.clear(); + + unsigned int i; + for (i = 0; i < sSeperator.size(); i++) + { + int n = s.find(sSeperator[i]); + if (n != -1) + { + vPart.push_back(s.substr(0, n)); + s = s.substr(n + 1); + } + else + { + printf("not found: %c\n", sSeperator[i]); + printf("s: %s\n", s.c_str()); + return false; + } + } + vPart.push_back(s); + + return true; +} + +string uint64tostr(uint64 n) +{ + char str[32]; + +#ifdef _WIN32 + sprintf(str, "%I64u", n); +#else + sprintf(str, "%llu", n); +#endif + + return str; +} + +string uint64tohexstr(uint64 n) +{ + char str[32]; + +#ifdef _WIN32 + sprintf(str, "%016I64x", n); +#else + sprintf(str, "%016llx", n); +#endif + + return str; +} + +string HexToStr(const unsigned char* pData, int nLen) +{ + string sRet; + int i; + for (i = 0; i < nLen; i++) + { + char szByte[3]; + sprintf(szByte, "%02x", pData[i]); + sRet += szByte; + } + + return sRet; +} + +uint64 GetAvailPhysMemorySize() +{ +#ifdef _WIN32 + MEMORYSTATUS ms; + GlobalMemoryStatus(&ms); + return ms.dwAvailPhys; +#elif defined(BSD) + int mib[2] = { CTL_HW, HW_PHYSMEM }; + uint64 physMem; + //XXX warning size_t isn't portable + size_t len; + len = sizeof(physMem); + sysctl(mib, 2, &physMem, &len, NULL, 0); + return physMem; +#elif defined(__linux__) + struct sysinfo info; + sysinfo(&info); + return ( info.freeram + info.bufferram ) * (unsigned long) info.mem_unit; +#else + return 0; + #error Unsupported Operating System +#endif +} + +string GetApplicationPath() +{ + char fullPath[FILENAME_MAX]; + +#ifdef _WIN32 + GetModuleFileName(NULL, fullPath, FILENAME_MAX); +#else + char szTmp[32]; + sprintf(szTmp, "/proc/%d/exe", getpid()); + int bytes = readlink(szTmp, fullPath, FILENAME_MAX); + if(bytes >= 0) + fullPath[bytes] = '\0'; +#endif + + string sApplicationPath = fullPath; +#ifdef _WIN32 + int nIndex = sApplicationPath.find_last_of('\\'); +#else + int nIndex = sApplicationPath.find_last_of('/'); +#endif + + if (nIndex != -1) + sApplicationPath = sApplicationPath.substr(0, nIndex+1); + + //printf ("\n\nDebug: The application directory is %s\n", sApplicationPath.c_str()); + return sApplicationPath; +} + +void ParseHash(string sHash, unsigned char* pHash, int& nHashLen) +{ + UINT4 i; + for (i = 0; i < sHash.size() / 2; i++) + { + string sSub = sHash.substr(i * 2, 2); + int nValue; + sscanf(sSub.c_str(), "%02x", &nValue); + pHash[i] = (unsigned char)nValue; + } + + nHashLen = (int) sHash.size() / 2; +} + +void Logo() +{ + printf("RainbowCrack (improved, multi-threaded) - Making a Faster Cryptanalytic Time-Memory Trade-Off\n"); + printf("by Martin Westergaard \n"); + printf("multi-threaded and enhanced by neinbrucke (version 0.6.3)\n"); + printf("http://www.freerainbowtables.com/\n"); + printf("original code by Zhu Shuanglei \n"); + printf("http://www.antsight.com/zsl/rainbowcrack/\n\n"); +} + +// XXX nmap is GPL2, will check newer releases regarding license +// Code comes from nmap, used for the linux implementation of kbhit() +#ifndef _WIN32 + +static int tty_fd = 0; +struct termios saved_ti; + +int tty_getchar() +{ + int c, numChars; + + if (tty_fd && tcgetpgrp(tty_fd) == getpid()) { + c = 0; + numChars = read(tty_fd, &c, 1); + if (numChars > 0) return c; + } + + return -1; +} + +void tty_done() +{ + if (!tty_fd) return; + + tcsetattr(tty_fd, TCSANOW, &saved_ti); + + close(tty_fd); + tty_fd = 0; +} + +void tty_init() +{ + struct termios ti; + + if (tty_fd) + return; + + if ((tty_fd = open("/dev/tty", O_RDONLY | O_NONBLOCK)) < 0) return; + + tcgetattr(tty_fd, &ti); + saved_ti = ti; + ti.c_lflag &= ~(ICANON | ECHO); + ti.c_cc[VMIN] = 1; + ti.c_cc[VTIME] = 0; + tcsetattr(tty_fd, TCSANOW, &ti); + + atexit(tty_done); +} + +void tty_flush(void) +{ + tcflush(tty_fd, TCIFLUSH); +} +// end nmap code +#endif diff --git a/Client Applications/rcracki_mt/Public.h b/Client Applications/rcracki_mt/Public.h index 5e5e8d5..13fb0d3 100644 --- a/Client Applications/rcracki_mt/Public.h +++ b/Client Applications/rcracki_mt/Public.h @@ -1,118 +1,135 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei -*/ - -#ifndef _PUBLIC_H -#define _PUBLIC_H - -#include -#include -#include - -#include -#include -#include -using namespace std; - -#ifdef _WIN32 - #define uint64 unsigned __int64 -#else - #ifndef u_int64_t - #define uint64 unsigned long long - #else - #define uint64 u_int64_t - #endif -#endif - -struct RainbowChainO -{ - uint64 nIndexS; - uint64 nIndexE; -}; -//struct RainbowChain -//{ -// //uint64 nChain; -// uint64 nIndexS; -// int nIndexE; -// int nCheckPoint; -//}; - -union RainbowChain -{ - //uint64 nChain; - uint64 nIndexS; - struct - { - unsigned short foo[3]; - unsigned short nIndexE; - }; - //int nCheckPoint; -}; -//struct RainbowChain -//{ -// uint64 nIndexS : 48; -// unsigned short nIndexE : 16; -//}; -//struct IndexChain -//{ -// uint64 nPrefix; -// int nFirstChain; -// int nChainCount; -// //unsigned short nChainCount; //(maybe union with nPrefix, 1 byte spoiled) -//}; -#pragma pack(1) -union IndexChain -{ - uint64 nPrefix; //5 - struct - { - unsigned char foo[5]; - int nFirstChain; //4 - unsigned short nChainCount; //2 - }; - //unsigned short nChainCount; (maybe union with nPrefix, 1 byte spoiled, no pack(1) needed) -}; -#pragma pack() -typedef struct -{ - string sName; - int nPlainLenMin; - int nPlainLenMax; -} tCharset; - -#define MAX_PLAIN_LEN 256 -#define MIN_HASH_LEN 8 -#define MAX_HASH_LEN 256 -#define MAX_SALT_LEN 256 - -// Code comes from nmap, used for the linux implementation of kbhit() -#ifndef _WIN32 -#include -#include -#include - -int tty_getchar(); -void tty_done(); -void tty_init(); -void tty_flush(void); - -#endif - - -unsigned int GetFileLen(FILE* file); -string TrimString(string s); -bool ReadLinesFromFile(string sPathName, vector& vLine); -bool SeperateString(string s, string sSeperator, vector& vPart); -string uint64tostr(uint64 n); -string uint64tohexstr(uint64 n); -string HexToStr(const unsigned char* pData, int nLen); -unsigned int GetAvailPhysMemorySize(); -string GetApplicationPath(); -void ParseHash(string sHash, unsigned char* pHash, int& nHashLen); -bool GetHybridCharsets(string sCharset, vector& vCharset); -void Logo(); -bool writeResultLineToFile(string sOutputFile, string sHash, string sPlain, string sBinary); - -#endif +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifndef _PUBLIC_H +#define _PUBLIC_H + +#include +#include +#include + +#include +#include +#include + +#include "global.h" + +using namespace std; + + +struct RainbowChainO +{ + uint64 nIndexS; + uint64 nIndexE; +}; +//struct RainbowChain +//{ +// //uint64 nChain; +// uint64 nIndexS; +// int nIndexE; +// int nCheckPoint; +//}; +union RainbowChain +{ + //uint64 nChain; + uint64 nIndexS; + struct + { + unsigned short foo[3]; + unsigned short nIndexE; + }; + //int nCheckPoint; +}; +//struct RainbowChain +//{ +// uint64 nIndexS : 48; +// unsigned short nIndexE : 16; +//}; +//struct IndexChain +//{ +// uint64 nPrefix; +// int nFirstChain; +// int nChainCount; +// //unsigned short nChainCount; //(maybe union with nPrefix, 1 byte spoiled) +//}; +#pragma pack(1) +union IndexChain +{ + uint64 nPrefix; //5 + struct + { + unsigned char foo[5]; + int nFirstChain; //4 + unsigned short nChainCount; //2 + }; + //unsigned short nChainCount; (maybe union with nPrefix, 1 byte spoiled, no pack(1) needed) +}; +#pragma pack() +typedef struct +{ + string sName; + int nPlainLenMin; + int nPlainLenMax; +} tCharset; + +#define MAX_PLAIN_LEN 256 +#define MIN_HASH_LEN 8 +#define MAX_HASH_LEN 256 +#define MAX_SALT_LEN 256 + +// XXX nmap is GPL2, will check newer releases regarding license +// Code comes from nmap, used for the linux implementation of kbhit() +#ifndef _WIN32 +#include +#include +#include + +int tty_getchar(); +void tty_done(); +void tty_init(); +void tty_flush(void); +// end nmap code + +#include + +#else + int gettimeofday( struct timeval *tv, struct timezone *tz ); +#endif + +timeval sub_timeofday( timeval tv2, timeval tv ); + +unsigned int GetFileLen(FILE* file); +string TrimString(string s); +bool ReadLinesFromFile(string sPathName, vector& vLine); +bool SeperateString(string s, string sSeperator, vector& vPart); +string uint64tostr(uint64 n); +string uint64tohexstr(uint64 n); +string HexToStr(const unsigned char* pData, int nLen); +uint64 GetAvailPhysMemorySize(); +string GetApplicationPath(); +void ParseHash(string sHash, unsigned char* pHash, int& nHashLen); +bool GetHybridCharsets(string sCharset, vector& vCharset); +void Logo(); +bool writeResultLineToFile(string sOutputFile, string sHash, string sPlain, string sBinary); + +#endif diff --git a/Client Applications/rcracki_mt/README.txt b/Client Applications/rcracki_mt/README.txt index b4b8cd8..873c868 100644 --- a/Client Applications/rcracki_mt/README.txt +++ b/Client Applications/rcracki_mt/README.txt @@ -1,162 +1,170 @@ -[rcracki_mt README] - -USAGE -================ -example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 4 -o save.txt C:\md5 - -Start rcracki_mt without any arguments to view usage information in short. This README describes the various -options in more detail. Many options can be set to a default value by editing rcracki_mt.ini. Command line -arguments get priority over settings in the ini file. - -INPUT ----------------- -rcracki_mt takes one hash on the command line (using -h) or an input file containing the hashes. rcracki_mt supports -three formats for the input file. Use one of the following options to specify the format followed by the filename: - --l: specify a list of hashes (one hash per line) --f: specify a pwdump file --c: specify a .lst file (format in which Cain stores hashes and results) - -SELECTING RAINBOW TABLES ----------------- -Any command line argument that is not an option will be interpreted as a directory to search for rainbow tables, -multiple directories can be specified. rcracki_mt recursively scans all specified directories for *.rti (indexed) -and *.rt (old/original) files. You can use .rt & .rti files at once, but this hasn't been tested thoroughly. - -You can set default locations to search for rainbow tables in rcracki_mt.ini. You need to use these in combination -with the command line argument -a [algorithm]. See the comments in the ini file for examples. - -SESSIONS & RESUMING ----------------- -Rcracki_mt has session support, which means that it stores its progress. This allows you to interrupt the session -and resume later on. This also allows sessions that stopped because of a crash (application or even system) to -resume. To use this feature, start rcracki_mt with all the options you'd like, then specify a session name with: - --s session_name: specify a session name - -Now during cracking, all your valuable precalculations are stored to disk, as well as progress (which files have -been checked) and cracked hashes. If you decide to interrupt the session (using CTRL+C), you can resume it using -the -r option. For example: - -rcracki_mt -r -s my_personal_hashes - -While resuming rcracki_mt you can/have to specify the less important options again, like number of threads and -showing debug information. Usually you will have these settings set to a default value in the .ini file anyway. -Session are deleted after the run is completed. You can choose to keep the precalculation work on disk, for example -if you want to reuse your session later on. Use the '-k' option to enable this feature. - -Rcracki_mt has a default session which gets overwritten every time you start a new job without specifying a session -name. It might be interesting to always keep precalculation work by enabling this feature in rcracki_mt.ini. But -pay attention, these precalculations can become quite large on disk. Currently there is a maximum of around 500 GB -of storage for these precalculations. You can always decide to manually remove the .precalc and .precalc.index -files from disk. Always remove both at the same time, you will screw up your results if you don't. A possible -'todo' for development is to do some verification before using stored precalculations. - -OPTIONAL ----------------- --t: Number of threads to use (for precalculation and false alarm checking) -Note: In Windows the crack threads run with lower priority. - --o: specify an output file to store found hashes in a colon (:) separated format. - Hashes are saved immediately when found. Especially useful if you have a large list of hashes. - --v: Show more information during cracking, for debugging purposes. Please use this flag if you want to show -output and report a bug. - - -EXTRA FEATURES ----------------- -You can pause a running rcracki_mt by using 'P'. It might not pause right away, it actually pauses after doing -precalculation or false alarm checking for one hash. Resume by pressing 'P' again. This pause option is different -from the session/resume feature, as this just pauses a running job, you don't stop rcracki_mt this way. - -If you are trying to crack a pwdump or Cain (.lst) file, containing both LM and NTLM hashes, rcracki_mt will try -and crack the LM hashes. The result will be an uppercase password, which rcracki_mt will then try to correct with -the right casing, using the NTLM hashes. If this fails it will try and perform Unicode correction, using a built-in -mapping. If you happen to have an LM hash coupled with the wrong NTLM hash, this attempt to perform Unicode -correction might take 'forever'. You can press 'S' to skip this step for the current hash. - - -HISTORY AND AUTHORS -================ -rcracki_mt originally started as a modification of a modification (rcracki) of the original RainbowCrack (rcrack). -These programs are all used to perform a rainbow table attack on password hashes, implementing Philippe Oechslin's -faster time-memory trade-off technique. - -Original rcrack code was written by Zhu Shuanglei . - -Martin Westergaard Jørgensen wrote rcracki (improved) to support the rainbow tables -generated by the distributed project www.freerainbowtables.com. These tables are perfected and indexed, making them -faster and smaller. Rcracki also supported hybrid tables. - -Daniël Niggebrugge further enhanced this version and made it multi threaded, creating rcracki_mt. More -features were added over time, making it less of an unofficial version with every release. - - -SUPPORTED HASH ALGORITHMS -================ -Hash types supported by rcracki_mt are: LM, NTLM, MD2, MD4, MD5, DoubleMD5, SHA1, RIPEMD160, MSCACHE, MySQL323, -MySQLSHA1, PIX, LMCHALL, HALFLMCHALL, NTLMCHALL, ORACLE - -Actual indexed&perfected tables that were generated by the Free Rainbow Tables project: LM, MD5, NTL, FASTLM, -HALFLMCHALL, SHA1 - - -SUPPORTED PLATFORMS -================ -Rcracki_mt is released both as win32 binary and as source package. Rcracki_mt should work on any Microsoft Windows system, but is only tested on a 32 bit Windows XP. The source should work on Linux versions, but this has only been tested on 32 bit Ubuntu (8.10). - -Some people have reported issues with compiling on 64 bit Linux. If you have trouble, please report details, as workaround try this suggested solution for AMD64 UBUNTU 8.10: - - sudo apt-get install ia32-libs - - -you might need to do: - cd /usr/lib32/ - sudo ln -s libstdc++.so.6 libstdc++.so - sudo ln -s libssl.so.0.9.8 libssl.so - - -add -m32 to the compilerline in the Makefile - g++ -m32 *.cpp -L/usr/lib32 -lssl -lpthread -O3 -o rcracki_mt - - -'OPTIONAL' TODO -================ -- verification of an endpoint when restoring a chainwalkset from disk. -- read multiple chainwalksets from disk at once to try and speed up this process. -- read next table (part) from disk while doing cryptanalysis - - -LINKS -================ -rcracki_mt @ SourceForge: https://sourceforge.net/projects/rcracki/ -Original rcrack: http://www.antsight.com/zsl/rainbowcrack/ -Free Rainbow Tables: http://www.freerainbowtables.com/ -My personal blog: http://blog.distracted.nl/ -Download free rainbow tables: http://tbhost.eu/ -Download free rainbow tables (mirror): http://freerainbowtables.mirror.garr.it/mirrors/freerainbowtables/ - - -THANKS -================ -the_drag0n Writing part of this README - Patch to support Cain .lst files -Joao Inacio Supplying some faster algorithm implementations - - -FAQ -================ -Q: Why do I get this message all the time? "this table contains hashes with length 8 only" -A: You are probably trying to crack LM hashes. You have to split up the hash in 2 parts of 16 hex characters each. - -Q: rcracki_mt is so slow when I'm cracking 5000 hashes, why is that? -A: Rainbow table attacks are only useful for a certain amount of hashes, mainly because of the precalculations that -are needed for every hash you are cracking. At a certain point it is faster to brute force the same key space then -to try and use rainbow tables. Especially if you use a GPU enabled brute forcer, this limit might be reached very -soon. Play around with these to find you limits. - -Q: How can I speed up rcracki_mt? -A: This depends on quite some factors. If your jobs usually comprise of disk access time, you can try and speed up -your storage. For example by using RAID and/or by using solid state disks. If you are trying to crack many hashes -at the same time, you might be better off with buying a faster CPU. +[rcracki_mt README] + +USAGE +================ +example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 4 -o save.txt C:\md5 + +Start rcracki_mt without any arguments to view usage information in short. This README describes the various +options in more detail. Many options can be set to a default value by editing rcracki_mt.ini. Command line +arguments get priority over settings in the ini file. + +INPUT +---------------- +rcracki_mt takes one hash on the command line (using -h) or an input file containing the hashes. rcracki_mt supports +three formats for the input file. Use one of the following options to specify the format followed by the filename: + +-l: specify a list of hashes (one hash per line) +-f: specify a pwdump file +-c: specify a .lst file (format in which Cain stores hashes and results) + +SELECTING RAINBOW TABLES +---------------- +Any command line argument that is not an option will be interpreted as a directory to search for rainbow tables, +multiple directories can be specified. rcracki_mt recursively scans all specified directories for *.rti (indexed) +and *.rt (old/original) files. You can use .rt & .rti files at once, but this hasn't been tested thoroughly. + +You can set default locations to search for rainbow tables in rcracki_mt.ini. You need to use these in combination +with the command line argument -a [algorithm]. See the comments in the ini file for examples. + +SESSIONS & RESUMING +---------------- +Rcracki_mt has session support, which means that it stores its progress. This allows you to interrupt the session +and resume later on. This also allows sessions that stopped because of a crash (application or even system) to +resume. To use this feature, start rcracki_mt with all the options you'd like, then specify a session name with: + +-s session_name: specify a session name + +Now during cracking, all your valuable precalculations are stored to disk, as well as progress (which files have +been checked) and cracked hashes. If you decide to interrupt the session (using CTRL+C), you can resume it using +the -r option. For example: + +rcracki_mt -r -s my_personal_hashes + +While resuming rcracki_mt you can/have to specify the less important options again, like number of threads and +showing debug information. Usually you will have these settings set to a default value in the .ini file anyway. +Session are deleted after the run is completed. You can choose to keep the precalculation work on disk, for example +if you want to reuse your session later on. Use the '-k' option to enable this feature. + +Rcracki_mt has a default session which gets overwritten every time you start a new job without specifying a session +name. It might be interesting to always keep precalculation work by enabling this feature in rcracki_mt.ini. But +pay attention, these precalculations can become quite large on disk. Currently there is a maximum of around 500 GB +of storage for these precalculations. You can always decide to manually remove the .precalc and .precalc.index +files from disk. Always remove both at the same time, you will screw up your results if you don't. A possible +'todo' for development is to do some verification before using stored precalculations. + +OPTIONAL +---------------- +-t: Number of threads to use (for precalculation and false alarm checking) +Note: In Windows the crack threads run with lower priority. + +-o: specify an output file to store found hashes in a colon (:) separated format. + Hashes are saved immediately when found. Especially useful if you have a large list of hashes. + +-v: Show more information during cracking, for debugging purposes. Please use this flag if you want to show +output and report a bug. + + +EXTRA FEATURES +---------------- +You can pause a running rcracki_mt by using 'P'. It might not pause right away, it actually pauses after doing +precalculation or false alarm checking for one hash. Resume by pressing 'P' again. This pause option is different +from the session/resume feature, as this just pauses a running job, you don't stop rcracki_mt this way. + +If you are trying to crack a pwdump or Cain (.lst) file, containing both LM and NTLM hashes, rcracki_mt will try +and crack the LM hashes. The result will be an uppercase password, which rcracki_mt will then try to correct with +the right casing, using the NTLM hashes. If this fails it will try and perform Unicode correction, using a built-in +mapping. If you happen to have an LM hash coupled with the wrong NTLM hash, this attempt to perform Unicode +correction might take 'forever'. You can press 'S' to skip this step for the current hash. + + +HISTORY AND AUTHORS +================ +rcracki_mt originally started as a modification of a modification (rcracki) of the original RainbowCrack (rcrack). +These programs are all used to perform a rainbow table attack on password hashes, implementing Philippe Oechslin's +faster time-memory trade-off technique. + +Original rcrack code was written by Zhu Shuanglei . + +Martin Westergaard Jørgensen wrote rcracki (improved) to support the rainbow tables +generated by the distributed project www.freerainbowtables.com. These tables are perfected and indexed, making them +faster and smaller. Rcracki also supported hybrid tables. + +Daniël Niggebrugge further enhanced this version and made it multi threaded, creating rcracki_mt. More +features were added over time, making it less of an unofficial version with every release. + +James Nobis - is making improvements on the *nix compatibility. + + +SUPPORTED HASH ALGORITHMS +================ +Hash types supported by rcracki_mt are: LM, NTLM, MD2, MD4, MD5, DoubleMD5, SHA1, RIPEMD160, MSCACHE, MySQL323, +MySQLSHA1, PIX, LMCHALL, HALFLMCHALL, NTLMCHALL, ORACLE + +Actual indexed&perfected tables that were generated by the Free Rainbow Tables project: LM, MD5, NTL, FASTLM, +HALFLMCHALL, SHA1 + + +SUPPORTED PLATFORMS +================ +Rcracki_mt is released both as win32 binary and as source package. Rcracki_mt should work on any Microsoft Windows system, but is only tested on a 32 bit Windows XP. + +The source should work on Linux versions. It has been tested on: +32-bit Ubuntu 8.10 (Intrepid Ibex) +32-bit Debian GNU/Linux 5.0 (Lenny) +64-bit Debian GNU/Linux 5.0 (Lenny) + +The source should also work on other platforms and has been tested on: +32-bit MacOSX (10.5) + +32-bit FreeBSD (7.2) +64-bit FreeBSD (7.2) +32-bit NetBSD (5.0) +32-bit OpenBSD (4.5) - you must install and use eg++ (g++ 4.2 from ports) +64-bit OpenBSD (4.5) + +Only compilation has been tested on: +64-bit MacOSX (10.6) + +Please note that to compile under the BSDs you must use gmake. + +OpenBSD threading is a work in progress. + +'OPTIONAL' TODO +================ +- verification of an endpoint when restoring a chainwalkset from disk. +- read multiple chainwalksets from disk at once to try and speed up this process. +- read next table (part) from disk while doing cryptanalysis + + +LINKS +================ +rcracki_mt @ SourceForge: https://sourceforge.net/projects/rcracki/ +Original rcrack: http://www.antsight.com/zsl/rainbowcrack/ +Free Rainbow Tables: http://www.freerainbowtables.com/ +My personal blog: http://blog.distracted.nl/ +Download free rainbow tables: http://tbhost.eu/ +Download free rainbow tables (mirror): http://freerainbowtables.mirror.garr.it/mirrors/freerainbowtables/ + + +THANKS +================ +the_drag0n Writing part of this README + Patch to support Cain .lst files +Joao Inacio Supplying some faster algorithm implementations + + +FAQ +================ +Q: Why do I get this message all the time? "this table contains hashes with length 8 only" +A: You are probably trying to crack LM hashes. You have to split up the hash in 2 parts of 16 hex characters each. + +Q: rcracki_mt is so slow when I'm cracking 5000 hashes, why is that? +A: Rainbow table attacks are only useful for a certain amount of hashes, mainly because of the precalculations that +are needed for every hash you are cracking. At a certain point it is faster to brute force the same key space then +to try and use rainbow tables. Especially if you use a GPU enabled brute forcer, this limit might be reached very +soon. Play around with these to find you limits. + +Q: How can I speed up rcracki_mt? +A: This depends on quite some factors. If your jobs usually comprise of disk access time, you can try and speed up +your storage. For example by using RAID and/or by using solid state disks. If you are trying to crack many hashes +at the same time, you might be better off with buying a faster CPU. diff --git a/Client Applications/rcracki_mt/RTI2Reader.cpp b/Client Applications/rcracki_mt/RTI2Reader.cpp index d58e881..3d8154d 100644 --- a/Client Applications/rcracki_mt/RTI2Reader.cpp +++ b/Client Applications/rcracki_mt/RTI2Reader.cpp @@ -1,6 +1,31 @@ +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright 2010 Martin Westergaard Jørgensen + * Copyright 2010 Daniël Niggebrugge + * Copyright 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + #include "RTI2Reader.h" #include + RTI2Header *RTI2Reader::m_pHeader = NULL; RTI2Reader::RTI2Reader(string Filename) { @@ -78,7 +103,12 @@ int RTI2Reader::ReadChains(unsigned int &numChains, RainbowChainO *pData) { // ALERT: Possible problem here if m_indexrowsizebytes > 1 as pNumChains is a unsigned char. unsigned int NumChainsInRow = (unsigned int)*(pNumChains + indexRow * m_indexrowsizebytes); - if(m_indexrowsizebytes > 1) { printf("Have to find a solution to this problem"); exit(2);} + if(m_indexrowsizebytes > 1) + { + //XXX Have to find a solution to this problem + printf( "FATAL: m_indexrowsizebytes > 1: %d\n", m_indexrowsizebytes ); + exit(2); + } if(i + NumChainsInRow > m_chainPosition) { curRowPosition = m_chainPosition - i; @@ -114,7 +144,7 @@ int RTI2Reader::ReadChains(unsigned int &numChains, RainbowChainO *pData) // Load the ending point prefix pData[chainsProcessed].nIndexE = m_pHeader->prefixstart + indexRow << m_pHeader->rti_endptlength; // Append the ending point suffix - pData[chainsProcessed].nIndexE |= (chainrow & (0xFFFFFFFFFFFFFFFF >> m_pHeader->rti_cplength)) >> m_pHeader->rti_startptlength; + pData[chainsProcessed].nIndexE |= (chainrow & (0xFFFFFFFFFFFFFFFFllu >> m_pHeader->rti_cplength)) >> m_pHeader->rti_startptlength; //pData[chainsProcessed].nCheckPoint = (chainrow >> m_pHeader->rti_startptlength + m_pHeader->rti_endptlength); curRowPosition++; chainsProcessed++; diff --git a/Client Applications/rcracki_mt/RTI2Reader.h b/Client Applications/rcracki_mt/RTI2Reader.h index 6934e6c..c3fc3bb 100644 --- a/Client Applications/rcracki_mt/RTI2Reader.h +++ b/Client Applications/rcracki_mt/RTI2Reader.h @@ -1,13 +1,40 @@ +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright 2010 Martin Westergaard Jørgensen + * Copyright 2010 Daniël Niggebrugge + * Copyright 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + #ifndef __RTI2READER_H__ #define __RTI2READER_H__ #include "Public.h" #include -#ifdef WIN32 -#include + +#if defined(_WIN32) && !defined(__GNUC__) + #include #endif + #include #include "BaseRTReader.h" + using namespace std; typedef struct diff --git a/Client Applications/rcracki_mt/RainbowCrack.cpp b/Client Applications/rcracki_mt/RainbowCrack.cpp index 185dd1a..9814834 100644 --- a/Client Applications/rcracki_mt/RainbowCrack.cpp +++ b/Client Applications/rcracki_mt/RainbowCrack.cpp @@ -1,831 +1,869 @@ -/* - RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. - - Copyright (C) Zhu Shuanglei - - Modified by Martin Westergaard Jørgensen to support indexed and hybrid tables - - Modified by neinbrucke to support multi threading and a bunch of other stuff :) - - 2009-01-04 - - Slightly modified (or "fulhack" as we say in sweden) - to support cain .lst files. -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786 4267 4018) -#endif - -#include "CrackEngine.h" -#include "lm2ntlm.h" -#include - - - -#ifdef _WIN32 - #include -#else - #include - #include - #include - #include -#endif - -#include -#ifdef _WIN32 - #pragma comment(lib, "libeay32.lib") -#endif - -////////////////////////////////////////////////////////////////////// - -#ifdef _WIN32 -void GetTableList(string sWildCharPathName, vector& vPathName) -{ - //vPathName.clear(); - - string sPath; - int n = sWildCharPathName.find_last_of('\\'); - - if (n == (sWildCharPathName.size() - 1)) - { - sWildCharPathName = sWildCharPathName.substr(0, n); - n = sWildCharPathName.find_last_of('\\'); - } - - if (n != -1) - sPath = sWildCharPathName.substr(0, n + 1); - - _finddata_t fd; - - long handle = _findfirst(sWildCharPathName.c_str(), &fd); - if (handle != -1) - { - do - { - string sName = fd.name; - if (sName.size()>3) { - if (sName.substr(sName.size()-3, 3) == ".rt" && !(fd.attrib & _A_SUBDIR)) - { - string sPathName = sPath + sName; - vPathName.push_back(sPathName); - } - } - if (sName.size()>4) { - if (sName.substr(sName.size()-4, 4) == ".rti" && !(fd.attrib & _A_SUBDIR)) - { - string sPathName = sPath + sName; - vPathName.push_back(sPathName); - } - } - if (sName.size()>5) { - if (sName.substr(sName.size()-5, 5) == ".rti2" && !(fd.attrib & _A_SUBDIR)) - { - string sPathName = sPath + sName; - vPathName.push_back(sPathName); - } - } - - if (sName != "." && sName != ".." && (fd.attrib & _A_SUBDIR)) - { - string sPath_sub = sPath + sName + '\\'; - string sWildCharPathName_sub = sPath_sub + '*'; - GetTableList(sWildCharPathName_sub, vPathName); - } - - } while (_findnext(handle, &fd) == 0); - - _findclose(handle); - } - //printf("Found %d rainbowtables (files) in %d sub directories...\n", vPathName.size(), subDir_count); -} -#else -//void GetTableList(int argc, char* argv[], vector& vPathName) -void GetTableList(string sWildCharPathName, vector& vPathName) -{ - //vPathName.clear(); - - struct stat buf; - if (lstat(sWildCharPathName.c_str(), &buf) == 0) - { - if (S_ISDIR(buf.st_mode)) - { - DIR *dir = opendir(sWildCharPathName.c_str()); - if(dir) - { - struct dirent *pDir=NULL; - while((pDir = readdir(dir)) != NULL) - { - string filename = ""; - filename += (*pDir).d_name; - if (filename != "." && filename != "..") - { - string new_filename = sWildCharPathName + '/' + filename; - GetTableList(new_filename, vPathName); - } - } - closedir(dir); - } - } - else if (S_ISREG(buf.st_mode)) - { - if (sWildCharPathName.size()>3) - { - if (sWildCharPathName.substr(sWildCharPathName.size()-3, 3) == ".rt") - { - vPathName.push_back(sWildCharPathName); - } - } - if (sWildCharPathName.size()>4) - { - if (sWildCharPathName.substr(sWildCharPathName.size()-4, 4) == ".rti") - { - //string sPathName_sub = sPath_sub + sName_sub; - vPathName.push_back(sWildCharPathName); - //printf("sPathName_sub: %s\n", sPathName_sub.c_str()); - } - } - } - } -} -#endif - -bool NormalizeHash(string& sHash) -{ - string sNormalizedHash = sHash; - - if ( sNormalizedHash.size() % 2 != 0 - || sNormalizedHash.size() < MIN_HASH_LEN * 2 - || sNormalizedHash.size() > MAX_HASH_LEN * 2) - return false; - - // Make lower - int i; - for (i = 0; i < sNormalizedHash.size(); i++) - { - if (sNormalizedHash[i] >= 'A' && sNormalizedHash[i] <= 'F') - sNormalizedHash[i] = sNormalizedHash[i] - 'A' + 'a'; - } - - // Character check - for (i = 0; i < sNormalizedHash.size(); i++) - { - if ( (sNormalizedHash[i] < 'a' || sNormalizedHash[i] > 'f') - && (sNormalizedHash[i] < '0' || sNormalizedHash[i] > '9')) - return false; - } - - sHash = sNormalizedHash; - return true; -} - -void LoadLMHashFromPwdumpFile(string sPathName, vector& vUserName, vector& vLMHash, vector& vNTLMHash) -{ - vector vLine; - if (ReadLinesFromFile(sPathName, vLine)) - { - int i; - for (i = 0; i < vLine.size(); i++) - { - vector vPart; - if (SeperateString(vLine[i], "::::", vPart)) - { - string sUserName = vPart[0]; - string sLMHash = vPart[2]; - string sNTLMHash = vPart[3]; - - if (sLMHash.size() == 32 && sNTLMHash.size() == 32) - { - if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash)) - { - vUserName.push_back(sUserName); - vLMHash.push_back(sLMHash); - vNTLMHash.push_back(sNTLMHash); - } - else - printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str()); - } - } - } - } - else - printf("can't open %s\n", sPathName.c_str()); -} - -// 2009-01-04 - james - Added this so we can load hashes from cain .LST files. -void LoadLMHashFromCainLSTFile(string sPathName, vector& vUserName, vector& vLMHash, vector& vNTLMHash) -{ - vector vLine; - if (ReadLinesFromFile(sPathName, vLine)) - { - int i; - for (i = 0; i < vLine.size(); i++) - { - vector vPart; - if (SeperateString(vLine[i], "\t\t\t\t\t\t", vPart)) - { - string sUserName = vPart[0]; - string sLMHash = vPart[4]; - string sNTLMHash = vPart[5]; - - if (sLMHash.size() == 32 && sNTLMHash.size() == 32) - { - if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash)) - { - vUserName.push_back(sUserName); - vLMHash.push_back(sLMHash); - vNTLMHash.push_back(sNTLMHash); - } - else - printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str()); - } - } - } - } - else - printf("can't open %s\n", sPathName.c_str()); -} - -bool NTLMPasswordSeek(unsigned char* pLMPassword, int nLMPasswordLen, int nLMPasswordNext, - unsigned char* pNTLMHash, string& sNTLMPassword) -{ - if (nLMPasswordNext == nLMPasswordLen) - { - unsigned char md[16]; - MD4(pLMPassword, nLMPasswordLen * 2, md); - if (memcmp(md, pNTLMHash, 16) == 0) - { - sNTLMPassword = ""; - int i; - for (i = 0; i < nLMPasswordLen; i++) - sNTLMPassword += char(pLMPassword[i * 2]); - return true; - } - else - return false; - } - - if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword)) - return true; - - if ( pLMPassword[nLMPasswordNext * 2] >= 'A' - && pLMPassword[nLMPasswordNext * 2] <= 'Z') - { - pLMPassword[nLMPasswordNext * 2] = pLMPassword[nLMPasswordNext * 2] - 'A' + 'a'; - if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword)) - return true; - pLMPassword[nLMPasswordNext * 2] = pLMPassword[nLMPasswordNext * 2] - 'a' + 'A'; - } - - return false; -} - -bool LMPasswordCorrectCase(string sLMPassword, unsigned char* pNTLMHash, string& sNTLMPassword) -{ - if (sLMPassword.size() == 0) - { - sNTLMPassword = ""; - return true; - } - - unsigned char* pLMPassword = new unsigned char[sLMPassword.size() * 2]; - int i; - for (i = 0; i < sLMPassword.size(); i++) - { - pLMPassword[i * 2 ] = sLMPassword[i]; - pLMPassword[i * 2 + 1] = 0x00; - } - bool fRet = NTLMPasswordSeek(pLMPassword, sLMPassword.size(), 0, pNTLMHash, sNTLMPassword); - - delete pLMPassword; - - return fRet; -} - -void Usage() -{ - Logo(); - - printf("usage: rcracki_mt -h hash rainbow_table_pathname\n"); - printf(" rcracki_mt -l hash_list_file rainbow_table_pathname\n"); - printf(" rcracki_mt -f pwdump_file rainbow_table_pathname\n"); - printf(" rcracki_mt -c lst_file rainbow_table_pathname\n"); - printf("\n"); - printf("-h hash: use raw hash as input\n"); - printf("-l hash_list_file: use hash list file as input, each hash in a line\n"); - printf("-f pwdump_file: use pwdump file as input, handles lanmanager hash only\n"); - printf("-c lst_file: use .lst (cain format) file as input\n"); - printf("-r [-s session_name]: resume from previous session, optional session name\n"); - printf("rainbow_table_pathname: pathname(s) of the rainbow table(s)\n"); - printf("\n"); - printf("Extra options: -t [nr] use this amount of threads/cores, default is 1\n"); - printf(" -o [output_file] write (temporary) results to this file\n"); - printf(" -s [session_name] write session data with this name\n"); - printf(" -k keep precalculation on disk\n"); - printf(" -v show debug information\n"); - printf("\n"); -#ifdef _WIN32 - printf("example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 2 [path]\\MD5\n"); - printf(" rcracki_mt -l hash.txt [path_to_specific_table]\\*\n"); -#else - printf("example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 2 [path]/MD5\n"); - printf(" rcracki_mt -l hash.txt [path_to_specific_table]/*\n"); -#endif - printf(" rcracki_mt -f hash.txt -t 4 -o results.txt *.rti\n"); - -} - -int main(int argc, char* argv[]) -{ - if (argc < 2) - { - Usage(); - return 0; - } - - vector vPathName; - vector vDefaultRainbowTablePath; - string sWildCharPathName = ""; - string sInputType = ""; - string sInput = ""; - string outputFile = ""; - string sApplicationPath = ""; - string sIniPathName = "rcracki_mt.ini"; - bool writeOutput = false; - string sSessionPathName = "rcracki.session"; - string sProgressPathName = "rcracki.progress"; - string sPrecalcPathName = "rcracki.precalc"; - bool resumeSession = false; - bool useDefaultRainbowTablePath = false; - bool debug = false; - bool keepPrecalcFiles = false; - string sAlgorithm = ""; - int maxThreads = 1; - CHashSet hs; - - // Read defaults from ini file; - bool readFromIni = false; - vector vLine; - if (ReadLinesFromFile(sIniPathName, vLine)) { - readFromIni = true; - } - else if (ReadLinesFromFile(GetApplicationPath() + sIniPathName, vLine)) { - readFromIni = true; - } - if (readFromIni) - { - int i; - for (i = 0; i < vLine.size(); i++) - { - if (vLine[i].substr(0,1) != "#") - { - vector vPart; - if (SeperateString(vLine[i], "=", vPart)) - { - string sOption = vPart[0]; - string sValue = vPart[1]; - - if (sOption == "Threads") { - maxThreads = atoi(sValue.c_str()); - } - else if (sOption == "DefaultResultsFile") { - outputFile = sValue; - } - else if (sOption == "AlwaysStoreResultsToFile") { - if (sValue == "1") - writeOutput = true; - } - else if (sOption.substr(0,24) == "DefaultRainbowTablePath.") { - //printf("Default RT path: %s\n", sValue.c_str()); - vDefaultRainbowTablePath.push_back(vLine[i]); - } - else if (sOption == "DefaultAlgorithm") { - useDefaultRainbowTablePath = true; - sAlgorithm = sValue; - } - else if (sOption == "AlwaysDebug") { - if (sValue == "1") - debug = true; - } - else if (sOption == "AlwaysKeepPrecalcFiles") { - if (sValue == "1") - keepPrecalcFiles = true; - } - else { - printf("illegal option %s in ini file %s\n", sOption.c_str(), sIniPathName.c_str()); - return 0; - } - } - } - } - if (writeOutput && outputFile == "") - { - printf("You need to specify a 'DefaultResultsFile' with 'AlwaysStoreResultsToFile=1'\n"); - writeOutput = false; - } - } - - // Parse command line arguments - int i; - for (i = 1; i < argc; i++) - { - string cla = argv[i]; - if (cla == "-h") { - sInputType = cla; - i++; - if (i < argc) - sInput = argv[i]; - } - else if (cla == "-l") { - sInputType = cla; - i++; - if (i < argc) - sInput = argv[i]; - } - else if (cla == "-f") { - sInputType = cla; - i++; - if (i < argc) - sInput = argv[i]; - } - else if (cla == "-c") { - sInputType = cla; - i++; - if (i < argc) - sInput = argv[i]; - } - else if (cla == "-t") { - i++; - if (i < argc) - maxThreads = atoi(argv[i]); - } - else if (cla == "-o") { - writeOutput = true; - i++; - if (i < argc) - outputFile = argv[i]; - } - else if (cla == "-r") { - resumeSession = true; - } - else if (cla == "-s") { - i++; - if (i < argc) - { - sSessionPathName = argv[i]; - sSessionPathName += ".session"; - sProgressPathName = argv[i]; - sProgressPathName += ".progress"; - sPrecalcPathName = argv[i]; - sPrecalcPathName += ".precalc"; - } - } - else if (cla == "-v") { - debug = true; - } - else if (cla == "-k") { - keepPrecalcFiles = true; - } - else if (cla == "-a") { - useDefaultRainbowTablePath = true; - i++; - if (i < argc) - sAlgorithm = argv[i]; - } - else { - GetTableList(cla, vPathName); - } - } - - if (debug && !readFromIni) - printf("Debug: Couldn't read rcracki_mt.ini, continuing anyway.\n"); - - // Load session data if we are resuming - if (resumeSession) - { - vPathName.clear(); - vector sSessionData; - if (ReadLinesFromFile(sSessionPathName.c_str(), sSessionData)) - { - int i; - for (i = 0; i < sSessionData.size(); i++) - { - vector vPart; - if (SeperateString(sSessionData[i], "=", vPart)) - { - string sOption = vPart[0]; - string sValue = vPart[1]; - - if (sOption == "sPathName") { - vPathName.push_back(sValue); - } - else if (sOption == "sInputType") { - sInputType = sValue; - } - else if (sOption == "sInput") { - sInput = sValue; - } - else if (sOption == "outputFile") { - writeOutput = true; - outputFile = sValue; - } - else if (sOption == "keepPrecalcFiles") { - if (sValue == "1") - keepPrecalcFiles = true; - } - } - } - } - else { - printf("Couldn't open session file %s\n", sSessionPathName.c_str()); - return 0; - } - } - - if (maxThreads<1) - maxThreads = 1; - - // don't load these if we are resuming a session that already has a list of tables - if (useDefaultRainbowTablePath && !resumeSession) - { - int i; - for (i = 0; i < vDefaultRainbowTablePath.size(); i++) - { - vector vPart; - if (SeperateString(vDefaultRainbowTablePath[i], ".=", vPart)) - { - string lineAlgorithm = vPart[1]; - string linePath = vPart[2]; - - if (lineAlgorithm == sAlgorithm) - GetTableList(linePath, vPathName); - } - } - } - - printf("Using %d threads for pre-calculation and false alarm checking...\n", maxThreads); - - setvbuf(stdout, NULL, _IONBF,0); - if (vPathName.size() == 0) - { - printf("no rainbow table found\n"); - return 0; - } - printf("Found %d rainbowtable files...\n\n", vPathName.size()); - - bool fCrackerType; // true: hash cracker, false: lm cracker - vector vHash; // hash cracker - vector vUserName; // lm cracker - vector vLMHash; // lm cracker - vector vNTLMHash; // lm cracker - if (sInputType == "-h") - { - fCrackerType = true; - - string sHash = sInput; - if (NormalizeHash(sHash)) - vHash.push_back(sHash); - else - printf("invalid hash: %s\n", sHash.c_str()); - } - else if (sInputType == "-l") - { - fCrackerType = true; - - string sPathName = sInput; - vector vLine; - if (ReadLinesFromFile(sPathName, vLine)) - { - int i; - for (i = 0; i < vLine.size(); i++) - { - string sHash = vLine[i]; - if (NormalizeHash(sHash)) - vHash.push_back(sHash); - else - printf("invalid hash: %s\n", sHash.c_str()); - } - } - else - printf("can't open %s\n", sPathName.c_str()); - } - else if (sInputType == "-f") - { - fCrackerType = false; - - string sPathName = sInput; - LoadLMHashFromPwdumpFile(sPathName, vUserName, vLMHash, vNTLMHash); - } - else if (sInputType == "-c") - { - // 2009-01-04 - james - Added this for cain-files. - fCrackerType = false; - string sPathName = sInput; - LoadLMHashFromCainLSTFile(sPathName, vUserName, vLMHash, vNTLMHash); - } - else - { - Usage(); - return 0; - } - - if (fCrackerType && vHash.size() == 0) - { - printf("no hashes found"); - return 0; - } - if (!fCrackerType && vLMHash.size() == 0) - { - return 0; - printf("no hashes found"); - } - - if (fCrackerType) - { - int i; - for (i = 0; i < vHash.size(); i++) - hs.AddHash(vHash[i]); - } - else - { - int i; - for (i = 0; i < vLMHash.size(); i++) - { - hs.AddHash(vLMHash[i].substr(0, 16)); - hs.AddHash(vLMHash[i].substr(16, 16)); - } - } - - // Load found hashes from session file - if (resumeSession) - { - vector sSessionData; - if (ReadLinesFromFile(sSessionPathName.c_str(), sSessionData)) - { - int i; - for (i = 0; i < sSessionData.size(); i++) - { - vector vPart; - if (SeperateString(sSessionData[i], "=", vPart)) - { - string sOption = vPart[0]; - string sValue = vPart[1]; - - if (sOption == "sHash") { - vector vPartHash; - if (SeperateString(sValue, "::", vPartHash)) - { - string sHash = vPartHash[0]; - string sBinary = vPartHash[1]; - string sPlain = vPartHash[2]; - - hs.SetPlain(sHash, sPlain, sBinary); - } - } - } - } - } - } - - // (Over)write session data if we are not resuming - if (!resumeSession) - { - FILE* file = fopen(sSessionPathName.c_str(), "w"); - string buffer = ""; - - if (file!=NULL) - { - buffer += "sInputType=" + sInputType + "\n"; - buffer += "sInput=" + sInput + "\n"; - - int i; - for (i = 0; i < vPathName.size(); i++) - { - buffer += "sPathName=" + vPathName[i] + "\n"; - } - - if (writeOutput) - buffer += "outputFile=" + outputFile + "\n"; - - if (keepPrecalcFiles) - buffer += "keepPrecalcFiles=1\n"; - - fputs (buffer.c_str(), file); - fclose (file); - } - file = fopen(sProgressPathName.c_str(), "w"); - fclose (file); - } - - // Run - CCrackEngine ce; - if (writeOutput) - ce.setOutputFile(outputFile); - ce.setSession(sSessionPathName, sProgressPathName, sPrecalcPathName, keepPrecalcFiles); - ce.Run(vPathName, hs, maxThreads, resumeSession, debug); - - // Remove session files - if (debug) printf("Debug: Removing session files.\n"); - if (remove(sSessionPathName.c_str()) == 0) - remove(sProgressPathName.c_str()); - else - if (debug) printf("Debug: Failed removing session files.\n"); - - // Statistics - printf("statistics\n"); - printf("-------------------------------------------------------\n"); - printf("plaintext found: %d of %d (%.2f%%)\n", hs.GetStatHashFound(), - hs.GetStatHashTotal(), - 100.0f * hs.GetStatHashFound() / hs.GetStatHashTotal()); - printf("total disk access time: %.2f s\n", ce.GetStatTotalDiskAccessTime()); - printf("total cryptanalysis time: %.2f s\n", ce.GetStatTotalCryptanalysisTime()); - printf("total chain walk step: %d\n", ce.GetStatTotalChainWalkStep()); - printf("total false alarm: %d\n", ce.GetStatTotalFalseAlarm()); - printf("total chain walk step due to false alarm: %d\n", ce.GetStatTotalChainWalkStepDueToFalseAlarm()); -// printf("total chain walk step skipped due to checkpoints: %d\n", ce.GetStatTotalFalseAlarmSkipped()); // Checkpoints not used - yet - printf("\n"); - - // Result - printf("result\n"); - printf("-------------------------------------------------------\n"); - if (fCrackerType) - { - int i; - for (i = 0; i < vHash.size(); i++) - { - string sPlain, sBinary; - if (!hs.GetPlain(vHash[i], sPlain, sBinary)) - { - sPlain = ""; - sBinary = ""; - } - - printf("%s\t%s\thex:%s\n", vHash[i].c_str(), sPlain.c_str(), sBinary.c_str()); - } - } - else - { - int i; - for (i = 0; i < vLMHash.size(); i++) - { - string sPlain1, sBinary1; - bool fPart1Found = hs.GetPlain(vLMHash[i].substr(0, 16), sPlain1, sBinary1); - if (!fPart1Found) - { - sPlain1 = ""; - sBinary1 = ""; - } - - string sPlain2, sBinary2; - bool fPart2Found = hs.GetPlain(vLMHash[i].substr(16, 16), sPlain2, sBinary2); - if (!fPart2Found) - { - sPlain2 = ""; - sBinary2 = ""; - } - - string sPlain = sPlain1 + sPlain2; - string sBinary = sBinary1 + sBinary2; - - // Correct case - if (fPart1Found && fPart2Found) - { - unsigned char NTLMHash[16]; - int nHashLen; - ParseHash(vNTLMHash[i], NTLMHash, nHashLen); - if (nHashLen != 16) - printf("debug: nHashLen mismatch\n"); - string sNTLMPassword; - if (LMPasswordCorrectCase(sPlain, NTLMHash, sNTLMPassword)) - { - sPlain = sNTLMPassword; - sBinary = HexToStr((const unsigned char*)sNTLMPassword.c_str(), sNTLMPassword.size()); - } - else - { - printf("%-14s\t%s\thex:%s\n", vUserName[i].c_str(), sPlain.c_str(), sBinary.c_str()); - LM2NTLMcorrector corrector; - if (corrector.LMPasswordCorrectUnicode(sBinary, NTLMHash, sNTLMPassword)) - { - sPlain = sNTLMPassword; - sBinary = corrector.getBinary(); - if (writeOutput) - { - if (!writeResultLineToFile(outputFile, vNTLMHash[i].c_str(), sPlain.c_str(), sBinary.c_str())) - printf("Couldn't write final result to file!\n"); - } - } - else { - printf("case correction for password %s failed!\n", sPlain.c_str()); - } - } - } - - // Display - printf("%-14s\t%s\thex:%s\n", vUserName[i].c_str(), - sPlain.c_str(), - sBinary.c_str()); - - } - } - - return 0; -} +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright (C) Zhu Shuanglei + * Copyright Martin Westergaard Jørgensen + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009 James Dickson + * Copyright 2009, 2010 James Nobis + * Copyright 2010 uroskn + * + * Modified by Martin Westergaard Jørgensen to support * indexed and hybrid tables + * + * Modified by neinbrucke to support multi threading and a bunch of other stuff :) + * + * 2009-01-04 - - Slightly modified (or "fulhack" as + * we say in sweden) to support cain .lst files. + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifdef _WIN32 + #pragma warning(disable : 4786 4267 4018) +#endif + +#include "CrackEngine.h" +#include "lm2ntlm.h" +#include + +#ifdef _WIN32 + #include +#else + #include + #include + #include + #include +#endif + +#ifdef _WIN32 + #pragma comment(lib, "libeay32.lib") +#endif + +////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +void GetTableList(string sWildCharPathName, vector& vPathName) +{ + //vPathName.clear(); + + string sPath; + int n = sWildCharPathName.find_last_of('\\'); + + if (n == (sWildCharPathName.size() - 1)) + { + sWildCharPathName = sWildCharPathName.substr(0, n); + n = sWildCharPathName.find_last_of('\\'); + } + + if (n != -1) + sPath = sWildCharPathName.substr(0, n + 1); + + _finddata_t fd; + + long handle = _findfirst(sWildCharPathName.c_str(), &fd); + if (handle != -1) + { + do + { + string sName = fd.name; + if (sName.size()>3) { + if (sName.substr(sName.size()-3, 3) == ".rt" && !(fd.attrib & _A_SUBDIR)) + { + string sPathName = sPath + sName; + vPathName.push_back(sPathName); + } + } + if (sName.size()>4) { + if (sName.substr(sName.size()-4, 4) == ".rti" && !(fd.attrib & _A_SUBDIR)) + { + string sPathName = sPath + sName; + vPathName.push_back(sPathName); + } + } + if (sName.size()>5) { + if (sName.substr(sName.size()-5, 5) == ".rti2" && !(fd.attrib & _A_SUBDIR)) + { + string sPathName = sPath + sName; + vPathName.push_back(sPathName); + } + } + + if (sName != "." && sName != ".." && (fd.attrib & _A_SUBDIR)) + { + string sPath_sub = sPath + sName + '\\'; + string sWildCharPathName_sub = sPath_sub + '*'; + GetTableList(sWildCharPathName_sub, vPathName); + } + + } while (_findnext(handle, &fd) == 0); + + _findclose(handle); + } + //printf("Found %d rainbowtables (files) in %d sub directories...\n", vPathName.size(), subDir_count); +} +#else +//void GetTableList(int argc, char* argv[], vector& vPathName) +void GetTableList(string sWildCharPathName, vector& vPathName) +{ + //vPathName.clear(); + + struct stat buf; + if (lstat(sWildCharPathName.c_str(), &buf) == 0) + { + if (S_ISDIR(buf.st_mode)) + { + DIR *dir = opendir(sWildCharPathName.c_str()); + if(dir) + { + struct dirent *pDir=NULL; + while((pDir = readdir(dir)) != NULL) + { + string filename = ""; + filename += (*pDir).d_name; + if (filename != "." && filename != "..") + { + string new_filename = sWildCharPathName + '/' + filename; + GetTableList(new_filename, vPathName); + } + } + closedir(dir); + } + } + else if (S_ISREG(buf.st_mode)) + { + if (sWildCharPathName.size()>3) + { + if (sWildCharPathName.substr(sWildCharPathName.size()-3, 3) == ".rt") + { + vPathName.push_back(sWildCharPathName); + } + } + if (sWildCharPathName.size()>4) + { + if (sWildCharPathName.substr(sWildCharPathName.size()-4, 4) == ".rti") + { + //string sPathName_sub = sPath_sub + sName_sub; + vPathName.push_back(sWildCharPathName); + //printf("sPathName_sub: %s\n", sPathName_sub.c_str()); + } + } + if ( sWildCharPathName.size() > 5 ) + { + if ( sWildCharPathName.substr( sWildCharPathName.size() - 5, 5 ) == ".rti2" ) + { + vPathName.push_back( sWildCharPathName ); + } + } + } + } +} +#endif + +bool NormalizeHash(string& sHash) +{ + string sNormalizedHash = sHash; + + if ( sNormalizedHash.size() % 2 != 0 + || sNormalizedHash.size() < MIN_HASH_LEN * 2 + || sNormalizedHash.size() > MAX_HASH_LEN * 2) + return false; + + // Make lower + UINT4 i; + for (i = 0; i < sNormalizedHash.size(); i++) + { + if (sNormalizedHash[i] >= 'A' && sNormalizedHash[i] <= 'F') + sNormalizedHash[i] = (char) sNormalizedHash[i] - 'A' + 'a'; + } + + // Character check + for (i = 0; i < sNormalizedHash.size(); i++) + { + if ( (sNormalizedHash[i] < 'a' || sNormalizedHash[i] > 'f') + && (sNormalizedHash[i] < '0' || sNormalizedHash[i] > '9')) + return false; + } + + sHash = sNormalizedHash; + return true; +} + +void LoadLMHashFromPwdumpFile(string sPathName, vector& vUserName, vector& vLMHash, vector& vNTLMHash) +{ + vector vLine; + if (ReadLinesFromFile(sPathName, vLine)) + { + UINT4 i; + for (i = 0; i < vLine.size(); i++) + { + vector vPart; + if (SeperateString(vLine[i], "::::", vPart)) + { + string sUserName = vPart[0]; + string sLMHash = vPart[2]; + string sNTLMHash = vPart[3]; + + if (sLMHash.size() == 32 && sNTLMHash.size() == 32) + { + if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash)) + { + vUserName.push_back(sUserName); + vLMHash.push_back(sLMHash); + vNTLMHash.push_back(sNTLMHash); + } + else + printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str()); + } + } + } + } + else + printf("can't open %s\n", sPathName.c_str()); +} + +// 2009-01-04 - james.dickson - Added this so we can load hashes from cain .LST files. +void LoadLMHashFromCainLSTFile(string sPathName, vector& vUserName, vector& vLMHash, vector& vNTLMHash) +{ + vector vLine; + if (ReadLinesFromFile(sPathName, vLine)) + { + UINT4 i; + for (i = 0; i < vLine.size(); i++) + { + vector vPart; + if (SeperateString(vLine[i], "\t\t\t\t\t\t", vPart)) + { + string sUserName = vPart[0]; + string sLMHash = vPart[4]; + string sNTLMHash = vPart[5]; + + if (sLMHash.size() == 32 && sNTLMHash.size() == 32) + { + if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash)) + { + vUserName.push_back(sUserName); + vLMHash.push_back(sLMHash); + vNTLMHash.push_back(sNTLMHash); + } + else + printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str()); + } + } + } + } + else + printf("can't open %s\n", sPathName.c_str()); +} + +bool NTLMPasswordSeek(unsigned char* pLMPassword, int nLMPasswordLen, int nLMPasswordNext, + unsigned char* pNTLMHash, string& sNTLMPassword) +{ + if (nLMPasswordNext == nLMPasswordLen) + { + unsigned char md[MD4_DIGEST_LENGTH]; + MD4_NEW(pLMPassword, nLMPasswordLen * 2, md); + + if (memcmp(md, pNTLMHash, MD4_DIGEST_LENGTH) == 0) + { + sNTLMPassword = ""; + int i; + for (i = 0; i < nLMPasswordLen; i++) + sNTLMPassword += char(pLMPassword[i * 2]); + return true; + } + else + return false; + } + + if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword)) + return true; + + if ( pLMPassword[nLMPasswordNext * 2] >= 'A' + && pLMPassword[nLMPasswordNext * 2] <= 'Z') + { + pLMPassword[nLMPasswordNext * 2] = (unsigned char) pLMPassword[nLMPasswordNext * 2] - 'A' + 'a'; + if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword)) + return true; + pLMPassword[nLMPasswordNext * 2] = (unsigned char) pLMPassword[nLMPasswordNext * 2] - 'a' + 'A'; + } + + return false; +} + +bool LMPasswordCorrectCase(string sLMPassword, unsigned char* pNTLMHash, string& sNTLMPassword) +{ + if (sLMPassword.size() == 0) + { + sNTLMPassword = ""; + return true; + } + + unsigned char* pLMPassword = new unsigned char[sLMPassword.size() * 2]; + UINT4 i; + for (i = 0; i < sLMPassword.size(); i++) + { + pLMPassword[i * 2 ] = sLMPassword[i]; + pLMPassword[i * 2 + 1] = 0x00; + } + bool fRet = NTLMPasswordSeek(pLMPassword, sLMPassword.size(), 0, pNTLMHash, sNTLMPassword); + + delete pLMPassword; + + return fRet; +} + +void Usage() +{ + Logo(); + + printf("usage: rcracki_mt -h hash rainbow_table_pathname\n"); + printf(" rcracki_mt -l hash_list_file rainbow_table_pathname\n"); + printf(" rcracki_mt -f pwdump_file rainbow_table_pathname\n"); + printf(" rcracki_mt -c lst_file rainbow_table_pathname\n"); + printf("\n"); + printf("-h hash: use raw hash as input\n"); + printf("-l hash_list_file: use hash list file as input, each hash in a line\n"); + printf("-f pwdump_file: use pwdump file as input, handles lanmanager hash only\n"); + printf("-c lst_file: use .lst (cain format) file as input\n"); + printf("-r [-s session_name]: resume from previous session, optional session name\n"); + printf("rainbow_table_pathname: pathname(s) of the rainbow table(s)\n"); + printf("\n"); + printf("Extra options: -t [nr] use this amount of threads/cores, default is 1\n"); + printf(" -o [output_file] write (temporary) results to this file\n"); + printf(" -s [session_name] write session data with this name\n"); + printf(" -k keep precalculation on disk\n"); + printf(" -m [megabytes] limit memory usage\n"); + printf(" -v show debug information\n"); + printf("\n"); +#ifdef _WIN32 + printf("example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 2 [path]\\MD5\n"); + printf(" rcracki_mt -l hash.txt [path_to_specific_table]\\*\n"); +#else + printf("example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 2 [path]/MD5\n"); + printf(" rcracki_mt -l hash.txt [path_to_specific_table]/*\n"); +#endif + printf(" rcracki_mt -f hash.txt -t 4 -o results.txt *.rti\n"); +} + +int main(int argc, char* argv[]) +{ + if (argc < 2) + { + Usage(); + return 0; + } + + vector vPathName; + vector vDefaultRainbowTablePath; + string sWildCharPathName = ""; + string sInputType = ""; + string sInput = ""; + string outputFile = ""; + string sApplicationPath = ""; + string sIniPathName = "rcracki_mt.ini"; + bool writeOutput = false; + string sSessionPathName = "rcracki.session"; + string sProgressPathName = "rcracki.progress"; + string sPrecalcPathName = "rcracki.precalc"; + bool resumeSession = false; + bool useDefaultRainbowTablePath = false; + bool debug = false; + bool keepPrecalcFiles = false; + string sAlgorithm = ""; + int maxThreads = 1; + uint64 maxMem = 0; + CHashSet hs; + + // Read defaults from ini file; + bool readFromIni = false; + vector vLine; + if (ReadLinesFromFile(sIniPathName, vLine)) { + readFromIni = true; + } + else if (ReadLinesFromFile(GetApplicationPath() + sIniPathName, vLine)) { + readFromIni = true; + } + if (readFromIni) + { + UINT4 i; + for (i = 0; i < vLine.size(); i++) + { + if (vLine[i].substr(0,1) != "#") + { + vector vPart; + if (SeperateString(vLine[i], "=", vPart)) + { + string sOption = vPart[0]; + string sValue = vPart[1]; + + if (sOption == "Threads") { + maxThreads = atoi(sValue.c_str()); + } + else if (sOption == "MaxMemoryUsage" ) { + maxMem = atoi(sValue.c_str()) * 1024 *1024; + } + else if (sOption == "DefaultResultsFile") { + outputFile = sValue; + } + else if (sOption == "AlwaysStoreResultsToFile") { + if (sValue == "1") + writeOutput = true; + } + else if (sOption.substr(0,24) == "DefaultRainbowTablePath.") { + //printf("Default RT path: %s\n", sValue.c_str()); + vDefaultRainbowTablePath.push_back(vLine[i]); + } + else if (sOption == "DefaultAlgorithm") { + useDefaultRainbowTablePath = true; + sAlgorithm = sValue; + } + else if (sOption == "AlwaysDebug") { + if (sValue == "1") + debug = true; + } + else if (sOption == "AlwaysKeepPrecalcFiles") { + if (sValue == "1") + keepPrecalcFiles = true; + } + else { + printf("illegal option %s in ini file %s\n", sOption.c_str(), sIniPathName.c_str()); + return 0; + } + } + } + } + if (writeOutput && outputFile == "") + { + printf("You need to specify a 'DefaultResultsFile' with 'AlwaysStoreResultsToFile=1'\n"); + writeOutput = false; + } + } + + // Parse command line arguments + int i; + for (i = 1; i < argc; i++) + { + string cla = argv[i]; + if (cla == "-h") { + sInputType = cla; + i++; + if (i < argc) + sInput = argv[i]; + } + else if (cla == "-l") { + sInputType = cla; + i++; + if (i < argc) + sInput = argv[i]; + } + else if (cla == "-f") { + sInputType = cla; + i++; + if (i < argc) + sInput = argv[i]; + } + else if (cla == "-c") { + sInputType = cla; + i++; + if (i < argc) + sInput = argv[i]; + } + else if (cla == "-t") { + i++; + if (i < argc) + maxThreads = atoi(argv[i]); + } + else if ( cla == "-m" ) { + i++; + if ( i < argc ) + maxMem = atoi(argv[i]) * 1024 * 1024; + } + else if (cla == "-o") { + writeOutput = true; + i++; + if (i < argc) + outputFile = argv[i]; + } + else if (cla == "-r") { + resumeSession = true; + } + else if (cla == "-s") { + i++; + if (i < argc) + { + sSessionPathName = argv[i]; + sSessionPathName += ".session"; + sProgressPathName = argv[i]; + sProgressPathName += ".progress"; + sPrecalcPathName = argv[i]; + sPrecalcPathName += ".precalc"; + } + } + else if (cla == "-v") { + debug = true; + } + else if (cla == "-k") { + keepPrecalcFiles = true; + } + else if (cla == "-a") { + useDefaultRainbowTablePath = true; + i++; + if (i < argc) + sAlgorithm = argv[i]; + } + else { + GetTableList(cla, vPathName); + } + } + + if (debug && !readFromIni) + printf("Debug: Couldn't read rcracki_mt.ini, continuing anyway.\n"); + + // Load session data if we are resuming + if (resumeSession) + { + vPathName.clear(); + vector sSessionData; + if (ReadLinesFromFile(sSessionPathName.c_str(), sSessionData)) + { + UINT4 i; + for (i = 0; i < sSessionData.size(); i++) + { + vector vPart; + if (SeperateString(sSessionData[i], "=", vPart)) + { + string sOption = vPart[0]; + string sValue = vPart[1]; + + if (sOption == "sPathName") { + vPathName.push_back(sValue); + } + else if (sOption == "sInputType") { + sInputType = sValue; + } + else if (sOption == "sInput") { + sInput = sValue; + } + else if (sOption == "outputFile") { + writeOutput = true; + outputFile = sValue; + } + else if (sOption == "keepPrecalcFiles") { + if (sValue == "1") + keepPrecalcFiles = true; + } + } + } + } + else { + printf("Couldn't open session file %s\n", sSessionPathName.c_str()); + return 0; + } + } + + if (maxThreads<1) + maxThreads = 1; + + // don't load these if we are resuming a session that already has a list of tables + if (useDefaultRainbowTablePath && !resumeSession) + { + UINT4 i; + for (i = 0; i < vDefaultRainbowTablePath.size(); i++) + { + vector vPart; + if (SeperateString(vDefaultRainbowTablePath[i], ".=", vPart)) + { + string lineAlgorithm = vPart[1]; + string linePath = vPart[2]; + + if (lineAlgorithm == sAlgorithm) + GetTableList(linePath, vPathName); + } + } + } + + printf("Using %d threads for pre-calculation and false alarm checking...\n", maxThreads); + + setvbuf(stdout, NULL, _IONBF,0); + if (vPathName.size() == 0) + { + printf("no rainbow table found\n"); + return 0; + } + printf("Found %lu rainbowtable files...\n\n", + (unsigned long)vPathName.size()); + + bool fCrackerType; // true: hash cracker, false: lm cracker + vector vHash; // hash cracker + vector vUserName; // lm cracker + vector vLMHash; // lm cracker + vector vNTLMHash; // lm cracker + if (sInputType == "-h") + { + fCrackerType = true; + + string sHash = sInput; + if (NormalizeHash(sHash)) + vHash.push_back(sHash); + else + printf("invalid hash: %s\n", sHash.c_str()); + } + else if (sInputType == "-l") + { + fCrackerType = true; + + string sPathName = sInput; + vector vLine; + if (ReadLinesFromFile(sPathName, vLine)) + { + UINT4 i; + for (i = 0; i < vLine.size(); i++) + { + string sHash = vLine[i]; + if (NormalizeHash(sHash)) + vHash.push_back(sHash); + else + printf("invalid hash: %s\n", sHash.c_str()); + } + } + else + printf("can't open %s\n", sPathName.c_str()); + } + else if (sInputType == "-f") + { + fCrackerType = false; + + string sPathName = sInput; + LoadLMHashFromPwdumpFile(sPathName, vUserName, vLMHash, vNTLMHash); + } + else if (sInputType == "-c") + { + // 2009-01-04 - james.dickson - Added this for cain-files. + fCrackerType = false; + string sPathName = sInput; + LoadLMHashFromCainLSTFile(sPathName, vUserName, vLMHash, vNTLMHash); + } + else + { + Usage(); + return 0; + } + + if (fCrackerType && vHash.size() == 0) + { + printf("no hashes found"); + return 0; + } + if (!fCrackerType && vLMHash.size() == 0) + { + return 0; + printf("no hashes found"); + } + + if (fCrackerType) + { + UINT4 i; + for (i = 0; i < vHash.size(); i++) + hs.AddHash(vHash[i]); + } + else + { + UINT4 i; + for (i = 0; i < vLMHash.size(); i++) + { + hs.AddHash(vLMHash[i].substr(0, 16)); + hs.AddHash(vLMHash[i].substr(16, 16)); + } + } + + // Load found hashes from session file + if (resumeSession) + { + vector sSessionData; + if (ReadLinesFromFile(sSessionPathName.c_str(), sSessionData)) + { + UINT4 i; + for (i = 0; i < sSessionData.size(); i++) + { + vector vPart; + if (SeperateString(sSessionData[i], "=", vPart)) + { + string sOption = vPart[0]; + string sValue = vPart[1]; + + if (sOption == "sHash") { + vector vPartHash; + if (SeperateString(sValue, "::", vPartHash)) + { + string sHash = vPartHash[0]; + string sBinary = vPartHash[1]; + string sPlain = vPartHash[2]; + + hs.SetPlain(sHash, sPlain, sBinary); + } + } + } + } + } + } + + // (Over)write session data if we are not resuming + if (!resumeSession) + { + FILE* file = fopen(sSessionPathName.c_str(), "w"); + string buffer = ""; + + if (file!=NULL) + { + buffer += "sInputType=" + sInputType + "\n"; + buffer += "sInput=" + sInput + "\n"; + + UINT4 i; + for (i = 0; i < vPathName.size(); i++) + { + buffer += "sPathName=" + vPathName[i] + "\n"; + } + + if (writeOutput) + buffer += "outputFile=" + outputFile + "\n"; + + if (keepPrecalcFiles) + buffer += "keepPrecalcFiles=1\n"; + + fputs (buffer.c_str(), file); + fclose (file); + } + file = fopen(sProgressPathName.c_str(), "w"); + fclose (file); + } + + // Run + CCrackEngine ce; + if (writeOutput) + ce.setOutputFile(outputFile); + ce.setSession(sSessionPathName, sProgressPathName, sPrecalcPathName, keepPrecalcFiles); + ce.Run(vPathName, hs, maxThreads, maxMem, resumeSession, debug); + + // Remove session files + if (debug) printf("Debug: Removing session files.\n"); + + if (remove(sSessionPathName.c_str()) == 0) + remove(sProgressPathName.c_str()); + else + if (debug) printf("Debug: Failed removing session files.\n"); + + // Statistics + printf("statistics\n"); + printf("-------------------------------------------------------\n"); + printf("plaintext found: %d of %d (%.2f%%)\n", hs.GetStatHashFound(), + hs.GetStatHashTotal(), + 100.0f * hs.GetStatHashFound() / hs.GetStatHashTotal()); + printf("total disk access time: %.2f s\n", ce.GetStatTotalDiskAccessTime()); + printf("total cryptanalysis time: %.2f s\n", ce.GetStatTotalCryptanalysisTime()); + printf("total pre-calculation time: %.2f s\n", ce.GetStatTotalPrecalculationTime()); + printf("total chain walk step: %d\n", ce.GetStatTotalChainWalkStep()); + printf("total false alarm: %d\n", ce.GetStatTotalFalseAlarm()); + printf("total chain walk step due to false alarm: %d\n", ce.GetStatTotalChainWalkStepDueToFalseAlarm()); +// printf("total chain walk step skipped due to checkpoints: %d\n", ce.GetStatTotalFalseAlarmSkipped()); // Checkpoints not used - yet + printf("\n"); + + // Result + printf("result\n"); + printf("-------------------------------------------------------\n"); + if (fCrackerType) + { + UINT4 i; + for (i = 0; i < vHash.size(); i++) + { + string sPlain, sBinary; + if (!hs.GetPlain(vHash[i], sPlain, sBinary)) + { + sPlain = ""; + sBinary = ""; + } + + printf("%s\t%s\thex:%s\n", vHash[i].c_str(), sPlain.c_str(), sBinary.c_str()); + } + } + else + { + UINT4 i; + for (i = 0; i < vLMHash.size(); i++) + { + string sPlain1, sBinary1; + bool fPart1Found = hs.GetPlain(vLMHash[i].substr(0, 16), sPlain1, sBinary1); + if (!fPart1Found) + { + sPlain1 = ""; + sBinary1 = ""; + } + + string sPlain2, sBinary2; + bool fPart2Found = hs.GetPlain(vLMHash[i].substr(16, 16), sPlain2, sBinary2); + if (!fPart2Found) + { + sPlain2 = ""; + sBinary2 = ""; + } + + string sPlain = sPlain1 + sPlain2; + string sBinary = sBinary1 + sBinary2; + + // Correct case + if (fPart1Found && fPart2Found) + { + unsigned char NTLMHash[16]; + int nHashLen; + ParseHash(vNTLMHash[i], NTLMHash, nHashLen); + if (nHashLen != 16) + printf("debug: nHashLen mismatch\n"); + string sNTLMPassword; + if (LMPasswordCorrectCase(sPlain, NTLMHash, sNTLMPassword)) + { + sPlain = sNTLMPassword; + sBinary = HexToStr((const unsigned char*)sNTLMPassword.c_str(), sNTLMPassword.size()); + } + else + { + printf("%-14s\t%s\thex:%s\n", vUserName[i].c_str(), sPlain.c_str(), sBinary.c_str()); + LM2NTLMcorrector corrector; + if (corrector.LMPasswordCorrectUnicode(sBinary, NTLMHash, sNTLMPassword)) + { + sPlain = sNTLMPassword; + sBinary = corrector.getBinary(); + if (writeOutput) + { + if (!writeResultLineToFile(outputFile, vNTLMHash[i].c_str(), sPlain.c_str(), sBinary.c_str())) + printf("Couldn't write final result to file!\n"); + } + } + else { + printf("case correction for password %s failed!\n", sPlain.c_str()); + } + } + } + + // Display + printf("%-14s\t%s\thex:%s\n", vUserName[i].c_str(), + sPlain.c_str(), + sBinary.c_str()); + + } + } + + return 0; +} diff --git a/Client Applications/rcracki_mt/fast_md5.cpp b/Client Applications/rcracki_mt/fast_md5.cpp index 8d60aee..3df6afb 100644 --- a/Client Applications/rcracki_mt/fast_md5.cpp +++ b/Client Applications/rcracki_mt/fast_md5.cpp @@ -9,11 +9,30 @@ * - For lengths < 16, transformation steps are "unrolled" using macros/defines * - Constants used whenever possible, it's the compiler's job to sort them out * - Padding is done on 4-byte words, and memory copied as last resort. + * + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . */ - -typedef unsigned int UINT4; - +#include "fast_md5.h" /* MD5 defines as per RFC reference implementation */ @@ -115,9 +134,9 @@ typedef unsigned int UINT4; // md5 step #define MD5STEP(f, a, b, c, d, AC, x, s) { \ - (a) += f ((b), (c), (d)); \ + (a) += f ((b), (c), (d)); \ (a) += (AC) + (x); \ - (a) = ROTATE_LEFT ((a), (s)); \ + (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } @@ -228,6 +247,7 @@ typedef unsigned int UINT4; #ifndef MD5_pad_w1 static inline UINT4 MD5_pad_w1(UINT4 data) { +// XXX x86 specific __asm__ ( "movb %%al, %%cl \n\t" "xorl %1, %1 \n\t" @@ -245,6 +265,7 @@ static inline UINT4 MD5_pad_w1(UINT4 data) #ifndef MD5_pad_w3 static inline UINT4 MD5_pad_w3(UINT4 data) { +// XXX x86 specific __asm__ ( "roll $8, %1 \n\t" "movb $128, %%al \n\t" diff --git a/Client Applications/rcracki_mt/fast_md5.h b/Client Applications/rcracki_mt/fast_md5.h index efea781..47549f7 100644 --- a/Client Applications/rcracki_mt/fast_md5.h +++ b/Client Applications/rcracki_mt/fast_md5.h @@ -9,12 +9,33 @@ * - For lengths < 16, transformation steps are "unrolled" using macros/defines * - Constants used whenever possible, it's the compiler's job to sort them out * - Padding is done on 4-byte words, and memory copied as last resort. + * + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . */ #ifndef FAST_MD5_H #define FAST_MD5_H +#include "global.h" + +#define MD5_DIGEST_LENGTH 16 void fast_MD5(unsigned char *pData, int len, unsigned char *pDigest); diff --git a/Client Applications/rcracki_mt/lm2ntlm.cpp b/Client Applications/rcracki_mt/lm2ntlm.cpp index 1b80e9c..d2afd27 100644 --- a/Client Applications/rcracki_mt/lm2ntlm.cpp +++ b/Client Applications/rcracki_mt/lm2ntlm.cpp @@ -1,2256 +1,2269 @@ -/* -* Copyright (C) Daniël Niggebrugge -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, [...] etc :p -*/ - -#ifdef _WIN32 - #pragma warning(disable : 4786 4267 4018) -#endif - -#include "lm2ntlm.h" - -LM2NTLMcorrector::LM2NTLMcorrector() -{ - progressCurrentCombination = 0; - totalCurrentCombination = 1; - counterOverall = 0; - countCombinations = 0; - countTotalCombinations = 0; - counter = 0; - fillMapW(); - aborting = false; - sBinary = ""; - -} - -string LM2NTLMcorrector::getBinary() -{ - return sBinary; -} - -bool LM2NTLMcorrector::LMPasswordCorrectUnicode(string hexPassword, unsigned char* pNTLMHash, string& sNTLMPassword) //, unsigned char* pLMPassword -{ - string sPlain = ""; - - int i; - for (i = 0; i < hexPassword.size() / 2; i++) - { - string sSub = hexPassword.substr(i * 2, 2); - int nValue; - sscanf(sSub.c_str(), "%02x", &nValue); - sPlain += (unsigned char)nValue; - } - - memcpy(NTLMHash, pNTLMHash, 16); - - - int tmpLength = sPlain.size() * 2; - unsigned char* pLMPassword = new unsigned char[tmpLength]; - - //printf("Searching for unicode password.\n"); - printf("Failed case correction, trying unicode correction for: %s\n", sPlain.c_str()); - //printf("NTLM hash: %s\n\n", sNTLMHash.c_str()); - - setvbuf(stdout, NULL, _IONBF,0); - - startClock = clock(); - previousClock = clock(); - -#ifndef _WIN32 - tty_init(); -#endif - - if (startCorrecting(sPlain, NTLMHash, sNTLMPassword, pLMPassword)) - { - sBinary = ByteToStr(pLMPassword, tmpLength).c_str(); - //printf("\nFound unicode password: %s\n", sNTLMPassword.c_str()); - //printf("Password in hex: %s\n", sBinary.c_str()); - writeEndStats(); -#ifndef _WIN32 - tty_done(); -#endif - return true; - } - else - { - //printf("\ncase correction for password %s fail!\n", sPlain.c_str()); - writeEndStats(); -#ifndef _WIN32 - tty_done(); -#endif - return false; - } -} - -bool LM2NTLMcorrector::startCorrecting(string sLMPassword, unsigned char* pNTLMHash, string& sNTLMPassword, unsigned char* pLMPassword) -{ - if (sLMPassword.size() == 0) - { - sNTLMPassword = ""; - return true; - } - - string muteMe = sLMPassword; - int length = muteMe.size(); - - unsigned char* pMuteMe = new unsigned char[length]; - unsigned char* pTempMute = new unsigned char[length * 2]; - - int i; - for (i = 0; i < length; i++) - { - pMuteMe[i] = muteMe[i]; - pTempMute[i * 2 ] = muteMe[i]; - pTempMute[i * 2 + 1] = 0x00; - unsigned char muteChar = pMuteMe[i]; - int sizeMapForChar = m_mapChar[muteChar].size(); - int j; - for (j = 0; j < sizeMapForChar; j++) - { - currentCharmap[i][j] = m_mapChar[muteChar][j]; - } - } - - int* jAtPos = new int[length]; - int* sizeAtPos = new int[length]; - bool* fullAtPos = new bool[length]; - - int setSize; - for (setSize = 0; setSize <= length; setSize++) - { - int cntFull = 0; - - // clear all 'fullatpos' before new setSize - int i; - for (i=0; i < length; i++) - { - fullAtPos[i] = false; - } - - //printf("Trying full unicode map for %d/%d characters...\t\t\n", setSize, length); - printf("Trying full unicode map for %d/%d characters...%-20s\n", setSize, length, ""); - - bool notFirst = true; - - // start at end and set 'full' combination - countCombinations = 0; - countTotalCombinations = calculateTotalCombinations(length, setSize); - - int sPos = length - 1; - while (sPos >= 0 && notFirst) // finding combinations for current 'setSize' - { - if (aborting) - return false; - - if (cntFull < setSize) - { - if (fullAtPos[sPos] == false) - { - fullAtPos[sPos] = true; - cntFull++; - } - sPos--; - } - else - { - if (fullAtPos[sPos] == false && setSize > 0) - { - fullAtPos[sPos] = true; - cntFull++; - - // reset positions after sPos - int k; - for (k = sPos+1; k < length; k++) - { - if (fullAtPos[k] == true) - { - fullAtPos[k] = false; - cntFull--; - } - } - // start at end again - sPos = length - 1; - } - else - { - sPos--; - } - } - // we have a combination - if (cntFull == setSize) - { - countCombinations++; - - setupCombinationAtPositions(length, pMuteMe, pTempMute, jAtPos, fullAtPos, sizeAtPos); - - if (checkPermutations(length, pMuteMe, pTempMute, jAtPos, sizeAtPos, pLMPassword, sNTLMPassword)) - { - return true; - } - } - - if (setSize == 0) - notFirst = false; - } - } - return false; -} - -// set up combination at positions -void LM2NTLMcorrector::setupCombinationAtPositions(int length, unsigned char* pMuteMe, unsigned char* pTempMute, int* jAtPos, bool* fullAtPos, int* sizeAtPos) -{ - progressCurrentCombination = 0; - totalCurrentCombination = 1; - - int i; - for (i=0; i < length; i++) - { - pTempMute[i*2] = currentCharmap[i][0]; - pTempMute[i*2+1] = currentCharmap[i][1]; // reset to first char in map - - jAtPos[i] = 0; // reset charcounter for this char (that is all chars) - - // based on combination, set full map or only upper/lowercase - if (fullAtPos[i] == true) - { - unsigned char muteChar = pMuteMe[i]; - int sizeMapForChar = m_mapChar[muteChar].size()/2; // 2 bytes per char - sizeAtPos[i] = sizeMapForChar; - } - else - { - sizeAtPos[i] = 2; - } - - totalCurrentCombination *= sizeAtPos[i]; - } - //printf("Trying %I64u passwords for current combination\t\t\r", totalCurrentCombination); -} - -// go check all permutations for this combination -bool LM2NTLMcorrector::checkPermutations(int length, unsigned char* pMuteMe, unsigned char* pTempMute, int* jAtPos, int* sizeAtPos, unsigned char* pLMPassword, string& sNTLMPassword) -{ - int pos = length - 1; - - while (pos >= 0) - { - counter++; - - pos = length - 1; - - int jAtCurPos = jAtPos[pos]; - int sizeMapForCharPos = sizeAtPos[pos]; - // move to start of string and find character with permutations left - while (jAtCurPos >= sizeMapForCharPos-1 && pos >= -1) - { - pos--; - if (pos >= 0 ) - { - jAtCurPos = jAtPos[pos]; - sizeMapForCharPos = sizeAtPos[pos]; - } - } - if (pos < 0) - continue; - - // next permutation for character - jAtCurPos++; - jAtPos[pos] = jAtCurPos; - - pTempMute[pos*2] = currentCharmap[pos][jAtCurPos*2]; - pTempMute[pos*2+1] = currentCharmap[pos][jAtCurPos*2+1]; - - // reset positions after pos - int k; - for (k = pos+1; k < length; k++) - { - jAtPos[k] = 0; - pTempMute[k*2] = currentCharmap[k][0]; // reset to first char in map - pTempMute[k*2+1] = currentCharmap[k][1]; - } - - if (checkNTLMPassword(pTempMute, length, sNTLMPassword) == true) - { - int i; - for (i = 0; i < length*2; i++) - pLMPassword[i] = pTempMute[i]; - return true; - } - - if (counter > 10000) // don't check clocks too often - { - clock_t currentClock = clock(); - float fTime = 1.0f * (currentClock - previousClock); - if (fTime > 1.0f * CLOCKS_PER_SEC) - { - float progressPercentageCurrentCombination = progressCurrentCombination * 100.0f / totalCurrentCombination; - float fTime = 1.0f * (currentClock - startClock) / CLOCKS_PER_SEC; - float currentSpeed = (counterOverall + progressCurrentCombination) / fTime / 1000000; - - //printf("%.2f%% of combination %d/%d (%.2f Mhashes/s)\t\t\t\t\r", progressPercentageCurrentCombination, countCombinations, countTotalCombinations, currentSpeed); - printf("%.2f%% of combination %d/%d (%.2f Mhashes/s)%-30s\r", progressPercentageCurrentCombination, countCombinations, countTotalCombinations, currentSpeed, ""); - - previousClock = clock(); - #ifdef _WIN32 - if (_kbhit()) - { - int ch = _getch(); - ch = toupper(ch); - if (ch == 'S') - { - aborting = true; - printf( "\nAborting unicode correction for this hash...\n"); - } - else - { - printf( "\nPress 'S' to skip unicode correction for this hash...\n"); - } - } - #else - int c = tty_getchar(); - if (c >= 0) { - tty_flush(); - if (c==115) { // = s - aborting = true; - printf( "\nAborting unicode correction for this hash...\n"); - } - else { - printf( "\nPress 's' to skip unicode correction for this hash...\n"); - } - } - #endif - if (aborting) - return false; - } - counter = 0; - } - - progressCurrentCombination++; - counterOverall++; - } - return false; -} - -// check password, maybe integrate this function in checkPermutations() for performance reasons. -bool LM2NTLMcorrector::checkNTLMPassword(unsigned char* pLMPassword, int nLMPasswordLen, string& sNTLMPassword) -{ - unsigned char md[16]; - - //MD4(pLMPassword, nLMPasswordLen * 2, md); - MD4_CTX ctx; - MD4_Init(&ctx); - MD4_Update(&ctx, pLMPassword, nLMPasswordLen * 2); - MD4_Final(md, &ctx); - - if (memcmp(md, NTLMHash, 16) == 0) - { - sNTLMPassword = ""; - int i; - for (i = 0; i < nLMPasswordLen; i++) { - sNTLMPassword += char(pLMPassword[i * 2]); - } - return true; - } - else - return false; -} - -void LM2NTLMcorrector::checkAbort() -{ -#ifdef _WIN32 - if (_kbhit()) - { - int ch = _getch(); - ch = toupper(ch); - if (ch == 'S') - { - aborting = true; - printf( "\nAborting unicode correction for this hash...\n"); - } - else - { - printf( "\nPress 'S' to skip unicode correction for this hash...\n"); - } - } -#endif -} - -void LM2NTLMcorrector::writeEndStats() -{ - clock_t endClock = clock(); - if (endClock - startClock > 0) - { - float fTime = 1.0f * (endClock - startClock) / CLOCKS_PER_SEC; - float speedOverall = counterOverall / fTime / 1000000; - printf("\nTried %s passwords in %.2f s (%.2f Mhashes/s)\n", uint64tostr(counterOverall).c_str(), fTime, speedOverall); - } - - printf("\n"); -} - -int LM2NTLMcorrector::calculateTotalCombinations(int length, int setSize) -{ - return factorial(length) / (factorial(setSize) * factorial(length-setSize)); -} - - -int LM2NTLMcorrector::factorial (int num) -{ - int result = 1; - int i; - for (i = 1; i <= num; ++i) - result *= i; - return result; -} - -// convert some bytes into a string -string LM2NTLMcorrector::ByteToStr(const unsigned char* pData, int nLen) -{ - string sRet = ""; - int i; - for (i = 0; i < nLen/2; i++) - { - char szByte[3]; - sprintf(szByte, "%02x", pData[i*2+1]); // swap 2-byte characters again - sRet += szByte; - sprintf(szByte, "%02x", pData[i*2]); - sRet += szByte; - } - - return sRet; -} - -void LM2NTLMcorrector::addToMapW(unsigned char key, unsigned char value1, unsigned char value2) -{ - int cnt = m_mapChar[key].size(); - m_mapChar[key][cnt] = value2; - m_mapChar[key][cnt+1] = value1; //reverse for endiannes -} - -// construct the mappings, would be nicer in a separate (importable) file -void LM2NTLMcorrector::fillMapW() -{ - addToMapW(0x01, 0x00, 0x01); - addToMapW(0x01, 0x26, 0x3A); - addToMapW(0x02, 0x00, 0x02); - addToMapW(0x02, 0x26, 0x3B); - addToMapW(0x03, 0x00, 0x03); - addToMapW(0x03, 0x26, 0x65); - addToMapW(0x04, 0x00, 0x04); - addToMapW(0x04, 0x26, 0x66); - addToMapW(0x05, 0x00, 0x05); - addToMapW(0x05, 0x26, 0x63); - addToMapW(0x06, 0x00, 0x06); - addToMapW(0x06, 0x26, 0x60); - addToMapW(0x07, 0x00, 0x07); - addToMapW(0x07, 0x00, 0xB7); - addToMapW(0x07, 0x20, 0x22); - addToMapW(0x07, 0x20, 0x24); - addToMapW(0x07, 0x20, 0x26); - addToMapW(0x07, 0x22, 0x19); - addToMapW(0x07, 0x22, 0xC5); - addToMapW(0x07, 0x30, 0xFB); - addToMapW(0x08, 0x00, 0x08); - addToMapW(0x08, 0x25, 0xD8); - addToMapW(0x09, 0x00, 0x09); - addToMapW(0x09, 0x20, 0xDD); - addToMapW(0x09, 0x25, 0xCB); - addToMapW(0x09, 0x30, 0x07); - addToMapW(0x0a, 0x00, 0x0A); - addToMapW(0x0a, 0x25, 0xD9); - addToMapW(0x0b, 0x00, 0x0B); - addToMapW(0x0b, 0x26, 0x42); - addToMapW(0x0c, 0x00, 0x0C); - addToMapW(0x0c, 0x26, 0x40); - addToMapW(0x0d, 0x00, 0x0D); - addToMapW(0x0d, 0x26, 0x6A); - addToMapW(0x0e, 0x00, 0x0E); - addToMapW(0x0e, 0x26, 0x6B); - addToMapW(0x0f, 0x00, 0x0F); - addToMapW(0x0f, 0x00, 0xA4); - addToMapW(0x0f, 0x26, 0x3C); - addToMapW(0x10, 0x00, 0x10); - addToMapW(0x10, 0x25, 0xBA); - addToMapW(0x11, 0x00, 0x11); - addToMapW(0x11, 0x25, 0xC4); - addToMapW(0x12, 0x00, 0x12); - addToMapW(0x12, 0x21, 0x95); - addToMapW(0x13, 0x00, 0x13); - addToMapW(0x13, 0x20, 0x3C); - addToMapW(0x14, 0x00, 0x14); - addToMapW(0x14, 0x00, 0xB6); - addToMapW(0x15, 0x00, 0x15); - addToMapW(0x15, 0x00, 0xA7); - addToMapW(0x16, 0x00, 0x16); - addToMapW(0x16, 0x02, 0xC9); - addToMapW(0x16, 0x25, 0xAC); - addToMapW(0x17, 0x00, 0x17); - addToMapW(0x17, 0x21, 0xA8); - addToMapW(0x18, 0x00, 0x18); - addToMapW(0x18, 0x21, 0x91); - addToMapW(0x19, 0x00, 0x19); - addToMapW(0x19, 0x21, 0x93); - addToMapW(0x1a, 0x00, 0x1A); - addToMapW(0x1a, 0x21, 0x92); - addToMapW(0x1b, 0x00, 0x1B); - addToMapW(0x1b, 0x21, 0x90); - addToMapW(0x1c, 0x00, 0x1C); - addToMapW(0x1c, 0x22, 0x1F); - addToMapW(0x1d, 0x00, 0x1D); - addToMapW(0x1d, 0x21, 0x94); - addToMapW(0x1e, 0x00, 0x1E); - addToMapW(0x1e, 0x25, 0xB2); - addToMapW(0x1f, 0x00, 0x1F); - addToMapW(0x1f, 0x25, 0xBC); - addToMapW(0x20, 0x00, 0x20); - addToMapW(0x20, 0x20, 0x00); - addToMapW(0x20, 0x20, 0x01); - addToMapW(0x20, 0x20, 0x02); - addToMapW(0x20, 0x20, 0x03); - addToMapW(0x20, 0x20, 0x04); - addToMapW(0x20, 0x20, 0x05); - addToMapW(0x20, 0x20, 0x06); - addToMapW(0x20, 0x30, 0x00); - addToMapW(0x21, 0x00, 0x21); - addToMapW(0x21, 0x00, 0xA1); - addToMapW(0x21, 0x01, 0xC3); - addToMapW(0x21, 0xFF, 0x01); - addToMapW(0x22, 0x00, 0x22); - addToMapW(0x22, 0x00, 0xA8); - addToMapW(0x22, 0x02, 0xBA); - addToMapW(0x22, 0x03, 0x08); - addToMapW(0x22, 0x03, 0x0E); - addToMapW(0x22, 0x20, 0x1C); - addToMapW(0x22, 0x20, 0x1D); - addToMapW(0x22, 0x20, 0x1E); - addToMapW(0x22, 0x20, 0x33); - addToMapW(0x22, 0x20, 0x35); - addToMapW(0x22, 0x27, 0x5D); - addToMapW(0x22, 0x27, 0x5E); - addToMapW(0x22, 0x30, 0x1D); - addToMapW(0x22, 0x30, 0x1E); - addToMapW(0x22, 0x30, 0x1F); - addToMapW(0x22, 0xFF, 0x02); - addToMapW(0x23, 0x00, 0x23); - addToMapW(0x23, 0xFF, 0x03); - addToMapW(0x24, 0x00, 0x24); - addToMapW(0x24, 0xFF, 0x04); - addToMapW(0x25, 0x00, 0x25); - addToMapW(0x25, 0x06, 0x6A); - addToMapW(0x25, 0x20, 0x30); - addToMapW(0x25, 0xFF, 0x05); - addToMapW(0x26, 0x00, 0x26); - addToMapW(0x26, 0xFF, 0x06); - addToMapW(0x27, 0x00, 0x27); - addToMapW(0x27, 0x00, 0xB4); - addToMapW(0x27, 0x02, 0xB9); - addToMapW(0x27, 0x02, 0xBB); - addToMapW(0x27, 0x02, 0xBC); - addToMapW(0x27, 0x02, 0xC8); - addToMapW(0x27, 0x02, 0xCA); - addToMapW(0x27, 0x02, 0xCB); - addToMapW(0x27, 0x03, 0x00); - addToMapW(0x27, 0x03, 0x01); - addToMapW(0x27, 0x20, 0x18); - addToMapW(0x27, 0x20, 0x19); - addToMapW(0x27, 0x20, 0x1A); - addToMapW(0x27, 0x20, 0x32); - addToMapW(0x27, 0x27, 0x5B); - addToMapW(0x27, 0x27, 0x5C); - addToMapW(0x27, 0xFF, 0x07); - addToMapW(0x28, 0x00, 0x28); - addToMapW(0x28, 0x23, 0x20); - addToMapW(0x28, 0xFF, 0x08); - addToMapW(0x29, 0x00, 0x29); - addToMapW(0x29, 0x23, 0x21); - addToMapW(0x29, 0xFF, 0x09); - addToMapW(0x2a, 0x00, 0x2A); - addToMapW(0x2a, 0x22, 0x17); - addToMapW(0x2a, 0xFF, 0x0A); - addToMapW(0x2b, 0x00, 0x2B); - addToMapW(0x2b, 0x00, 0xB1); - addToMapW(0x2b, 0x20, 0x20); - addToMapW(0x2b, 0x20, 0x21); - addToMapW(0x2b, 0xFF, 0x0B); - addToMapW(0x2c, 0x00, 0x2C); - addToMapW(0x2c, 0x00, 0xB8); - addToMapW(0x2c, 0x03, 0x27); - addToMapW(0x2c, 0x20, 0x1A); - addToMapW(0x2c, 0x20, 0x1E); - addToMapW(0x2c, 0xFF, 0x0C); - addToMapW(0x2d, 0x00, 0x2D); - addToMapW(0x2d, 0x00, 0xAC); - addToMapW(0x2d, 0x00, 0xAD); - addToMapW(0x2d, 0x20, 0x10); - addToMapW(0x2d, 0x20, 0x11); - addToMapW(0x2d, 0x20, 0x13); - addToMapW(0x2d, 0x20, 0x14); - addToMapW(0x2d, 0x22, 0x12); - addToMapW(0x2d, 0x22, 0x13); - addToMapW(0x2d, 0xFF, 0x0D); - addToMapW(0x2e, 0x00, 0x2E); - addToMapW(0x2e, 0x20, 0x26); - addToMapW(0x2e, 0xFF, 0x0E); - addToMapW(0x2f, 0x00, 0x2F); - addToMapW(0x2f, 0x20, 0x44); - addToMapW(0x2f, 0x22, 0x15); - addToMapW(0x2f, 0x22, 0x16); - addToMapW(0x2f, 0xFF, 0x0F); - addToMapW(0x30, 0x00, 0x30); - addToMapW(0x30, 0x20, 0x70); - addToMapW(0x30, 0x20, 0x80); - addToMapW(0x30, 0xFF, 0x10); - addToMapW(0x31, 0x00, 0x31); - addToMapW(0x31, 0x00, 0xB9); - addToMapW(0x31, 0x00, 0xBC); - addToMapW(0x31, 0x00, 0xBD); - addToMapW(0x31, 0x20, 0x81); - addToMapW(0x31, 0xFF, 0x11); - addToMapW(0x32, 0x00, 0x32); - addToMapW(0x32, 0x00, 0xB2); - addToMapW(0x32, 0x20, 0x82); - addToMapW(0x32, 0xFF, 0x12); - addToMapW(0x33, 0x00, 0x33); - addToMapW(0x33, 0x00, 0xB3); - addToMapW(0x33, 0x00, 0xBE); - addToMapW(0x33, 0x20, 0x83); - addToMapW(0x33, 0xFF, 0x13); - addToMapW(0x34, 0x00, 0x34); - addToMapW(0x34, 0x20, 0x74); - addToMapW(0x34, 0x20, 0x84); - addToMapW(0x34, 0xFF, 0x14); - addToMapW(0x35, 0x00, 0x35); - addToMapW(0x35, 0x20, 0x75); - addToMapW(0x35, 0x20, 0x85); - addToMapW(0x35, 0xFF, 0x15); - addToMapW(0x36, 0x00, 0x36); - addToMapW(0x36, 0x20, 0x76); - addToMapW(0x36, 0x20, 0x86); - addToMapW(0x36, 0xFF, 0x16); - addToMapW(0x37, 0x00, 0x37); - addToMapW(0x37, 0x20, 0x77); - addToMapW(0x37, 0x20, 0x87); - addToMapW(0x37, 0xFF, 0x17); - addToMapW(0x38, 0x00, 0x38); - addToMapW(0x38, 0x20, 0x78); - addToMapW(0x38, 0x20, 0x88); - addToMapW(0x38, 0x22, 0x1E); - addToMapW(0x38, 0xFF, 0x18); - addToMapW(0x39, 0x00, 0x39); - addToMapW(0x39, 0x20, 0x78); - addToMapW(0x39, 0x20, 0x89); - addToMapW(0x39, 0xFF, 0x19); - addToMapW(0x3a, 0x00, 0x3A); - addToMapW(0x3a, 0x05, 0x89); - addToMapW(0x3a, 0x20, 0x26); - addToMapW(0x3a, 0x22, 0x36); - addToMapW(0x3a, 0xFF, 0x1A); - addToMapW(0x3b, 0x00, 0x3B); - addToMapW(0x3b, 0x03, 0x7E); - addToMapW(0x3b, 0xFF, 0x1B); - addToMapW(0x3c, 0x00, 0x3C); - addToMapW(0x3c, 0x00, 0xAB); - addToMapW(0x3c, 0x20, 0x39); - addToMapW(0x3c, 0x23, 0x29); - addToMapW(0x3c, 0x30, 0x08); - addToMapW(0x3c, 0xFF, 0x1C); - addToMapW(0x3d, 0x00, 0x3D); - addToMapW(0x3d, 0x22, 0x61); - addToMapW(0x3d, 0x22, 0x64); - addToMapW(0x3d, 0x22, 0x65); - addToMapW(0x3d, 0xFF, 0x1D); - addToMapW(0x3e, 0x00, 0x3E); - addToMapW(0x3e, 0x00, 0xBB); - addToMapW(0x3e, 0x20, 0x3A); - addToMapW(0x3e, 0x23, 0x2A); - addToMapW(0x3e, 0x30, 0x09); - addToMapW(0x3e, 0xFF, 0x1E); - addToMapW(0x3f, 0x00, 0x3F); - addToMapW(0x40, 0x00, 0x40); - addToMapW(0x40, 0xFF, 0x20); - addToMapW(0x41, 0x00, 0x41); - addToMapW(0x41, 0x00, 0x61); - addToMapW(0x41, 0x00, 0xAA); - addToMapW(0x41, 0x00, 0xC0); - addToMapW(0x41, 0x00, 0xC1); - addToMapW(0x41, 0x00, 0xC2); - addToMapW(0x41, 0x00, 0xC3); - addToMapW(0x41, 0x00, 0xC4); - addToMapW(0x41, 0x00, 0xC5); - addToMapW(0x41, 0x00, 0xC6); - addToMapW(0x41, 0x00, 0xE0); - addToMapW(0x41, 0x00, 0xE1); - addToMapW(0x41, 0x00, 0xE2); - addToMapW(0x41, 0x00, 0xE3); - addToMapW(0x41, 0x00, 0xE4); - addToMapW(0x41, 0x00, 0xE5); - addToMapW(0x41, 0x00, 0xE6); - addToMapW(0x41, 0x01, 0x00); - addToMapW(0x41, 0x01, 0x01); - addToMapW(0x41, 0x01, 0x02); - addToMapW(0x41, 0x01, 0x03); - addToMapW(0x41, 0x01, 0x04); - addToMapW(0x41, 0x01, 0x05); - addToMapW(0x41, 0x01, 0xCD); - addToMapW(0x41, 0x01, 0xCE); - addToMapW(0x41, 0x01, 0xDE); - addToMapW(0x41, 0x01, 0xDF); - addToMapW(0x41, 0x03, 0xB1); - addToMapW(0x41, 0x21, 0x2B); - addToMapW(0x41, 0xFF, 0x21); - addToMapW(0x41, 0xFF, 0x41); - addToMapW(0x42, 0x00, 0x42); - addToMapW(0x42, 0x00, 0x62); - addToMapW(0x42, 0x01, 0x80); - addToMapW(0x42, 0x21, 0x2C); - addToMapW(0x42, 0xFF, 0x22); - addToMapW(0x42, 0xFF, 0x42); - addToMapW(0x43, 0x00, 0x43); - addToMapW(0x43, 0x00, 0x63); - addToMapW(0x43, 0x00, 0xA2); - addToMapW(0x43, 0x00, 0xA9); - addToMapW(0x43, 0x00, 0xC7); - addToMapW(0x43, 0x00, 0xE7); - addToMapW(0x43, 0x00, 0xE8); - addToMapW(0x43, 0x01, 0x06); - addToMapW(0x43, 0x01, 0x07); - addToMapW(0x43, 0x01, 0x08); - addToMapW(0x43, 0x01, 0x09); - addToMapW(0x43, 0x01, 0x0A); - addToMapW(0x43, 0x01, 0x0B); - addToMapW(0x43, 0x01, 0x0C); - addToMapW(0x43, 0x01, 0x0D); - addToMapW(0x43, 0x21, 0x02); - addToMapW(0x43, 0x21, 0x2D); - addToMapW(0x43, 0xFF, 0x23); - addToMapW(0x43, 0xFF, 0x43); - addToMapW(0x44, 0x00, 0x44); - addToMapW(0x44, 0x00, 0x64); - addToMapW(0x44, 0x00, 0xD0); - addToMapW(0x44, 0x00, 0xF0); - addToMapW(0x44, 0x01, 0x0E); - addToMapW(0x44, 0x01, 0x0F); - addToMapW(0x44, 0x01, 0x10); - addToMapW(0x44, 0x01, 0x11); - addToMapW(0x44, 0x01, 0x89); - addToMapW(0x44, 0x03, 0xB4); - addToMapW(0x44, 0x26, 0x6A); - addToMapW(0x44, 0x26, 0x6B); - addToMapW(0x44, 0xFF, 0x24); - addToMapW(0x44, 0xFF, 0x44); - addToMapW(0x45, 0x00, 0x45); - addToMapW(0x45, 0x00, 0x65); - addToMapW(0x45, 0x00, 0xC8); - addToMapW(0x45, 0x00, 0xC9); - addToMapW(0x45, 0x00, 0xCA); - addToMapW(0x45, 0x00, 0xCB); - addToMapW(0x45, 0x00, 0xE8); - addToMapW(0x45, 0x00, 0xE9); - addToMapW(0x45, 0x00, 0xEA); - addToMapW(0x45, 0x00, 0xEB); - addToMapW(0x45, 0x01, 0x12); - addToMapW(0x45, 0x01, 0x13); - addToMapW(0x45, 0x01, 0x14); - addToMapW(0x45, 0x01, 0x15); - addToMapW(0x45, 0x01, 0x16); - addToMapW(0x45, 0x01, 0x17); - addToMapW(0x45, 0x01, 0x18); - addToMapW(0x45, 0x01, 0x19); - addToMapW(0x45, 0x01, 0x1A); - addToMapW(0x45, 0x01, 0x1B); - addToMapW(0x45, 0x03, 0xB5); - addToMapW(0x45, 0x21, 0x07); - addToMapW(0x45, 0x21, 0x2E); - addToMapW(0x45, 0x21, 0x2F); - addToMapW(0x45, 0x21, 0x30); - addToMapW(0x45, 0xFF, 0x25); - addToMapW(0x45, 0xFF, 0x45); - addToMapW(0x46, 0x00, 0x46); - addToMapW(0x46, 0x00, 0x66); - addToMapW(0x46, 0x01, 0x91); - addToMapW(0x46, 0x01, 0x92); - addToMapW(0x46, 0x03, 0xA6); - addToMapW(0x46, 0x03, 0xC6); - addToMapW(0x46, 0x21, 0x31); - addToMapW(0x46, 0xFF, 0x26); - addToMapW(0x46, 0xFF, 0x46); - addToMapW(0x47, 0x00, 0x47); - addToMapW(0x47, 0x00, 0x67); - addToMapW(0x47, 0x01, 0x1C); - addToMapW(0x47, 0x01, 0x1D); - addToMapW(0x47, 0x01, 0x1E); - addToMapW(0x47, 0x01, 0x1F); - addToMapW(0x47, 0x01, 0x20); - addToMapW(0x47, 0x01, 0x21); - addToMapW(0x47, 0x01, 0x22); - addToMapW(0x47, 0x01, 0x23); - addToMapW(0x47, 0x01, 0xE4); - addToMapW(0x47, 0x01, 0xE5); - addToMapW(0x47, 0x01, 0xE6); - addToMapW(0x47, 0x01, 0xE7); - addToMapW(0x47, 0x02, 0x61); - addToMapW(0x47, 0x03, 0x93); - addToMapW(0x47, 0x21, 0x0A); - addToMapW(0x47, 0xFF, 0x27); - addToMapW(0x47, 0xFF, 0x47); - addToMapW(0x48, 0x00, 0x48); - addToMapW(0x48, 0x00, 0x68); - addToMapW(0x48, 0x01, 0x24); - addToMapW(0x48, 0x01, 0x25); - addToMapW(0x48, 0x01, 0x26); - addToMapW(0x48, 0x01, 0x27); - addToMapW(0x48, 0x04, 0xBB); - addToMapW(0x48, 0x21, 0x0B); - addToMapW(0x48, 0x21, 0x0C); - addToMapW(0x48, 0x21, 0x0D); - addToMapW(0x48, 0x21, 0x0E); - addToMapW(0x48, 0xFF, 0x28); - addToMapW(0x48, 0xFF, 0x48); - addToMapW(0x49, 0x00, 0x49); - addToMapW(0x49, 0x00, 0x69); - addToMapW(0x49, 0x00, 0xCC); - addToMapW(0x49, 0x00, 0xCD); - addToMapW(0x49, 0x00, 0xCE); - addToMapW(0x49, 0x00, 0xCF); - addToMapW(0x49, 0x00, 0xEC); - addToMapW(0x49, 0x00, 0xED); - addToMapW(0x49, 0x00, 0xEE); - addToMapW(0x49, 0x00, 0xEF); - addToMapW(0x49, 0x01, 0x28); - addToMapW(0x49, 0x01, 0x29); - addToMapW(0x49, 0x01, 0x2A); - addToMapW(0x49, 0x01, 0x2B); - addToMapW(0x49, 0x01, 0x2C); - addToMapW(0x49, 0x01, 0x2D); - addToMapW(0x49, 0x01, 0x2E); - addToMapW(0x49, 0x01, 0x2F); - addToMapW(0x49, 0x01, 0x30); - addToMapW(0x49, 0x01, 0x31); - addToMapW(0x49, 0x01, 0x97); - addToMapW(0x49, 0x01, 0xCF); - addToMapW(0x49, 0x01, 0xD0); - addToMapW(0x49, 0x21, 0x10); - addToMapW(0x49, 0x21, 0x11); - addToMapW(0x49, 0xFF, 0x29); - addToMapW(0x49, 0xFF, 0x49); - addToMapW(0x4a, 0x00, 0x4A); - addToMapW(0x4a, 0x00, 0x6A); - addToMapW(0x4a, 0x01, 0x34); - addToMapW(0x4a, 0x01, 0x35); - addToMapW(0x4a, 0x01, 0xF0); - addToMapW(0x4a, 0xFF, 0x2A); - addToMapW(0x4a, 0xFF, 0x4A); - addToMapW(0x4b, 0x00, 0x4B); - addToMapW(0x4b, 0x00, 0x6B); - addToMapW(0x4b, 0x01, 0x36); - addToMapW(0x4b, 0x01, 0x37); - addToMapW(0x4b, 0x01, 0xE8); - addToMapW(0x4b, 0x01, 0xE9); - addToMapW(0x4b, 0x21, 0x2A); - addToMapW(0x4b, 0xFF, 0x2B); - addToMapW(0x4b, 0xFF, 0x4B); - addToMapW(0x4c, 0x00, 0x4C); - addToMapW(0x4c, 0x00, 0x6C); - addToMapW(0x4c, 0x00, 0xA3); - addToMapW(0x4c, 0x01, 0x39); - addToMapW(0x4c, 0x01, 0x3A); - addToMapW(0x4c, 0x01, 0x3B); - addToMapW(0x4c, 0x01, 0x3C); - addToMapW(0x4c, 0x01, 0x3D); - addToMapW(0x4c, 0x01, 0x3E); - addToMapW(0x4c, 0x01, 0x41); - addToMapW(0x4c, 0x01, 0x42); - addToMapW(0x4c, 0x01, 0x9A); - addToMapW(0x4c, 0x20, 0xA4); - addToMapW(0x4c, 0x21, 0x12); - addToMapW(0x4c, 0x21, 0x13); - addToMapW(0x4c, 0xFF, 0x2C); - addToMapW(0x4c, 0xFF, 0x4C); - addToMapW(0x4d, 0x00, 0x4D); - addToMapW(0x4d, 0x00, 0x6D); - addToMapW(0x4d, 0x21, 0x33); - addToMapW(0x4d, 0xFF, 0x2D); - addToMapW(0x4d, 0xFF, 0x4D); - addToMapW(0x4e, 0x00, 0x4E); - addToMapW(0x4e, 0x00, 0x6E); - addToMapW(0x4e, 0x00, 0xD1); - addToMapW(0x4e, 0x00, 0xF1); - addToMapW(0x4e, 0x01, 0x43); - addToMapW(0x4e, 0x01, 0x44); - addToMapW(0x4e, 0x01, 0x45); - addToMapW(0x4e, 0x01, 0x46); - addToMapW(0x4e, 0x01, 0x47); - addToMapW(0x4e, 0x01, 0x48); - addToMapW(0x4e, 0x20, 0x7F); - addToMapW(0x4e, 0x21, 0x15); - addToMapW(0x4e, 0x22, 0x29); - addToMapW(0x4e, 0xFF, 0x2E); - addToMapW(0x4e, 0xFF, 0x4E); - addToMapW(0x4f, 0x00, 0x4F); - addToMapW(0x4f, 0x00, 0x6F); - addToMapW(0x4f, 0x00, 0xB0); - addToMapW(0x4f, 0x00, 0xBA); - addToMapW(0x4f, 0x00, 0xD2); - addToMapW(0x4f, 0x00, 0xD3); - addToMapW(0x4f, 0x00, 0xD4); - addToMapW(0x4f, 0x00, 0xD5); - addToMapW(0x4f, 0x00, 0xD6); - addToMapW(0x4f, 0x00, 0xD8); - addToMapW(0x4f, 0x00, 0xF2); - addToMapW(0x4f, 0x00, 0xF3); - addToMapW(0x4f, 0x00, 0xF4); - addToMapW(0x4f, 0x00, 0xF5); - addToMapW(0x4f, 0x00, 0xF6); - addToMapW(0x4f, 0x00, 0xF8); - addToMapW(0x4f, 0x01, 0x4C); - addToMapW(0x4f, 0x01, 0x4D); - addToMapW(0x4f, 0x01, 0x4E); - addToMapW(0x4f, 0x01, 0x4F); - addToMapW(0x4f, 0x01, 0x50); - addToMapW(0x4f, 0x01, 0x51); - addToMapW(0x4f, 0x01, 0x52); - addToMapW(0x4f, 0x01, 0x53); - addToMapW(0x4f, 0x01, 0x9F); - addToMapW(0x4f, 0x01, 0xA0); - addToMapW(0x4f, 0x01, 0xA1); - addToMapW(0x4f, 0x01, 0xD1); - addToMapW(0x4f, 0x01, 0xD2); - addToMapW(0x4f, 0x01, 0xEA); - addToMapW(0x4f, 0x01, 0xEB); - addToMapW(0x4f, 0x01, 0xEC); - addToMapW(0x4f, 0x01, 0xED); - addToMapW(0x4f, 0x03, 0xA9); - addToMapW(0x4f, 0x20, 0xDD); - addToMapW(0x4f, 0x21, 0x26); - addToMapW(0x4f, 0x21, 0x34); - addToMapW(0x4f, 0x22, 0x05); - addToMapW(0x4f, 0x30, 0x07); - addToMapW(0x4f, 0xFF, 0x2F); - addToMapW(0x4f, 0xFF, 0x4F); - addToMapW(0x50, 0x00, 0x50); - addToMapW(0x50, 0x00, 0x70); - addToMapW(0x50, 0x03, 0xC0); - addToMapW(0x50, 0x20, 0xA7); - addToMapW(0x50, 0x21, 0x18); - addToMapW(0x50, 0x21, 0x19); - addToMapW(0x50, 0xFF, 0x30); - addToMapW(0x50, 0xFF, 0x50); - addToMapW(0x51, 0x00, 0x51); - addToMapW(0x51, 0x00, 0x71); - addToMapW(0x51, 0x21, 0x1A); - addToMapW(0x51, 0xFF, 0x31); - addToMapW(0x51, 0xFF, 0x51); - addToMapW(0x52, 0x00, 0x52); - addToMapW(0x52, 0x00, 0x72); - addToMapW(0x52, 0x00, 0xAE); - addToMapW(0x52, 0x01, 0x54); - addToMapW(0x52, 0x01, 0x55); - addToMapW(0x52, 0x01, 0x56); - addToMapW(0x52, 0x01, 0x57); - addToMapW(0x52, 0x01, 0x58); - addToMapW(0x52, 0x01, 0x59); - addToMapW(0x52, 0x21, 0x1B); - addToMapW(0x52, 0x21, 0x1C); - addToMapW(0x52, 0x21, 0x1D); - addToMapW(0x52, 0xFF, 0x32); - addToMapW(0x52, 0xFF, 0x52); - addToMapW(0x53, 0x00, 0x53); - addToMapW(0x53, 0x00, 0x73); - addToMapW(0x53, 0x00, 0xDF); - addToMapW(0x53, 0x01, 0x5A); - addToMapW(0x53, 0x01, 0x5B); - addToMapW(0x53, 0x01, 0x5C); - addToMapW(0x53, 0x01, 0x5D); - addToMapW(0x53, 0x01, 0x5E); - addToMapW(0x53, 0x01, 0x5F); - addToMapW(0x53, 0x01, 0x60); - addToMapW(0x53, 0x01, 0x61); - addToMapW(0x53, 0x01, 0xA9); - addToMapW(0x53, 0x03, 0xA3); - addToMapW(0x53, 0x03, 0xC3); - addToMapW(0x53, 0x22, 0x11); - addToMapW(0x53, 0xFF, 0x33); - addToMapW(0x53, 0xFF, 0x53); - addToMapW(0x54, 0x00, 0x54); - addToMapW(0x54, 0x00, 0x74); - addToMapW(0x54, 0x00, 0xDE); - addToMapW(0x54, 0x00, 0xFE); - addToMapW(0x54, 0x01, 0x62); - addToMapW(0x54, 0x01, 0x63); - addToMapW(0x54, 0x01, 0x64); - addToMapW(0x54, 0x01, 0x65); - addToMapW(0x54, 0x01, 0x66); - addToMapW(0x54, 0x01, 0x67); - addToMapW(0x54, 0x01, 0xAB); - addToMapW(0x54, 0x01, 0xAE); - addToMapW(0x54, 0x03, 0xC4); - addToMapW(0x54, 0x21, 0x22); - addToMapW(0x54, 0xFF, 0x34); - addToMapW(0x54, 0xFF, 0x54); - addToMapW(0x55, 0x00, 0x55); - addToMapW(0x55, 0x00, 0x75); - addToMapW(0x55, 0x00, 0xB5); - addToMapW(0x55, 0x00, 0xD9); - addToMapW(0x55, 0x00, 0xDA); - addToMapW(0x55, 0x00, 0xDB); - addToMapW(0x55, 0x00, 0xDC); - addToMapW(0x55, 0x00, 0xF9); - addToMapW(0x55, 0x00, 0xFA); - addToMapW(0x55, 0x00, 0xFB); - addToMapW(0x55, 0x00, 0xFC); - addToMapW(0x55, 0x01, 0x68); - addToMapW(0x55, 0x01, 0x69); - addToMapW(0x55, 0x01, 0x6A); - addToMapW(0x55, 0x01, 0x6B); - addToMapW(0x55, 0x01, 0x6C); - addToMapW(0x55, 0x01, 0x6D); - addToMapW(0x55, 0x01, 0x6E); - addToMapW(0x55, 0x01, 0x6F); - addToMapW(0x55, 0x01, 0x70); - addToMapW(0x55, 0x01, 0x71); - addToMapW(0x55, 0x01, 0x72); - addToMapW(0x55, 0x01, 0x73); - addToMapW(0x55, 0x01, 0xAF); - addToMapW(0x55, 0x01, 0xB0); - addToMapW(0x55, 0x01, 0xD3); - addToMapW(0x55, 0x01, 0xD4); - addToMapW(0x55, 0x01, 0xD5); - addToMapW(0x55, 0x01, 0xD6); - addToMapW(0x55, 0x01, 0xD7); - addToMapW(0x55, 0x01, 0xD8); - addToMapW(0x55, 0x01, 0xD9); - addToMapW(0x55, 0x01, 0xDA); - addToMapW(0x55, 0x01, 0xDB); - addToMapW(0x55, 0x01, 0xDC); - addToMapW(0x55, 0x03, 0xBC); - addToMapW(0x55, 0xFF, 0x35); - addToMapW(0x55, 0xFF, 0x55); - addToMapW(0x56, 0x00, 0x56); - addToMapW(0x56, 0x00, 0x76); - addToMapW(0x56, 0x22, 0x1A); - addToMapW(0x56, 0x27, 0x13); - addToMapW(0x56, 0xFF, 0x36); - addToMapW(0x56, 0xFF, 0x56); - addToMapW(0x57, 0x00, 0x57); - addToMapW(0x57, 0x00, 0x77); - addToMapW(0x57, 0x01, 0x74); - addToMapW(0x57, 0x01, 0x75); - addToMapW(0x57, 0xFF, 0x37); - addToMapW(0x57, 0xFF, 0x57); - addToMapW(0x58, 0x00, 0x58); - addToMapW(0x58, 0x00, 0x78); - addToMapW(0x58, 0x00, 0xD7); - addToMapW(0x58, 0xFF, 0x38); - addToMapW(0x58, 0xFF, 0x58); - addToMapW(0x59, 0x00, 0x59); - addToMapW(0x59, 0x00, 0x79); - addToMapW(0x59, 0x00, 0xA5); - addToMapW(0x59, 0x00, 0xDD); - addToMapW(0x59, 0x00, 0xFD); - addToMapW(0x59, 0x00, 0xFF); - addToMapW(0x59, 0x01, 0x76); - addToMapW(0x59, 0x01, 0x77); - addToMapW(0x59, 0x01, 0x78); - addToMapW(0x59, 0xFF, 0x39); - addToMapW(0x59, 0xFF, 0x59); - addToMapW(0x5a, 0x00, 0x5A); - addToMapW(0x5a, 0x00, 0x7A); - addToMapW(0x5a, 0x01, 0x79); - addToMapW(0x5a, 0x01, 0x7A); - addToMapW(0x5a, 0x01, 0x7B); - addToMapW(0x5a, 0x01, 0x7C); - addToMapW(0x5a, 0x01, 0x7D); - addToMapW(0x5a, 0x01, 0x7E); - addToMapW(0x5a, 0x01, 0xB6); - addToMapW(0x5a, 0x21, 0x24); - addToMapW(0x5a, 0x21, 0x28); - addToMapW(0x5a, 0xFF, 0x3A); - addToMapW(0x5a, 0xFF, 0x5A); - addToMapW(0x5b, 0x00, 0x5B); - addToMapW(0x5b, 0x30, 0x1A); - addToMapW(0x5b, 0xFF, 0x3B); - addToMapW(0x5c, 0x00, 0x5C); - addToMapW(0x5c, 0x00, 0xA5); - addToMapW(0x5c, 0x22, 0x16); - addToMapW(0x5c, 0xFF, 0x3C); - addToMapW(0x5d, 0x00, 0x5D); - addToMapW(0x5d, 0x30, 0x1B); - addToMapW(0x5d, 0xFF, 0x3D); - addToMapW(0x5e, 0x00, 0x5E); - addToMapW(0x5e, 0x02, 0xC4); - addToMapW(0x5e, 0x02, 0xC6); - addToMapW(0x5e, 0x02, 0xC7); - addToMapW(0x5e, 0x02, 0xD8); - addToMapW(0x5e, 0x03, 0x02); - addToMapW(0x5e, 0x03, 0x06); - addToMapW(0x5e, 0x03, 0x0C); - addToMapW(0x5e, 0x23, 0x03); - addToMapW(0x5e, 0xFF, 0x3E); - addToMapW(0x5f, 0x00, 0x5F); - addToMapW(0x5f, 0x00, 0xAF); - addToMapW(0x5f, 0x00, 0xBE); - addToMapW(0x5f, 0x00, 0xDE); - addToMapW(0x5f, 0x00, 0xFE); - addToMapW(0x5f, 0x02, 0xCD); - addToMapW(0x5f, 0x03, 0x31); - addToMapW(0x5f, 0x03, 0x32); - addToMapW(0x5f, 0x20, 0x17); - addToMapW(0x5f, 0x30, 0xFC); - addToMapW(0x5f, 0xFF, 0x3F); - addToMapW(0x60, 0x00, 0x60); - addToMapW(0x60, 0x02, 0xCB); - addToMapW(0x60, 0x03, 0x00); - addToMapW(0x60, 0x20, 0x18); - addToMapW(0x60, 0x20, 0x35); - addToMapW(0x60, 0xFF, 0x40); - addToMapW(0x7b, 0x00, 0x7B); - addToMapW(0x7b, 0xFF, 0x5B); - addToMapW(0x7c, 0x00, 0x7C); - addToMapW(0x7c, 0x00, 0xA6); - addToMapW(0x7c, 0x01, 0xC0); - addToMapW(0x7c, 0x22, 0x23); - addToMapW(0x7c, 0x27, 0x58); - addToMapW(0x7c, 0xFF, 0x5C); - addToMapW(0x7d, 0x00, 0x7D); - addToMapW(0x7d, 0x30, 0x1B); - addToMapW(0x7d, 0xFF, 0x5D); - addToMapW(0x7e, 0x00, 0x7E); - addToMapW(0x7e, 0x02, 0xDC); - addToMapW(0x7e, 0x03, 0x03); - addToMapW(0x7e, 0x22, 0x3C); - addToMapW(0x7e, 0x22, 0x48); - addToMapW(0x7e, 0xFF, 0x5E); - addToMapW(0x7f, 0x00, 0x7F); - addToMapW(0x7f, 0x23, 0x02); - addToMapW(0x7f, 0x26, 0x60); - addToMapW(0x7f, 0x26, 0x63); - addToMapW(0x7f, 0x26, 0x65); - addToMapW(0x7f, 0x26, 0x66); - addToMapW(0x80, 0x00, 0x80); - addToMapW(0x80, 0x00, 0xC7); - addToMapW(0x80, 0x00, 0xE7); - addToMapW(0x80, 0x01, 0x06); - addToMapW(0x80, 0x01, 0x07); - addToMapW(0x80, 0x03, 0x91); - addToMapW(0x80, 0x03, 0xB1); - addToMapW(0x80, 0x04, 0x10); - addToMapW(0x80, 0x04, 0x30); - addToMapW(0x80, 0x05, 0xD0); - addToMapW(0x80, 0x20, 0xAC); - addToMapW(0x81, 0x00, 0x81); - addToMapW(0x81, 0x03, 0x92); - addToMapW(0x81, 0x03, 0xB2); - addToMapW(0x81, 0x04, 0x02); - addToMapW(0x81, 0x04, 0x11); - addToMapW(0x81, 0x04, 0x31); - addToMapW(0x81, 0x04, 0x52); - addToMapW(0x81, 0x05, 0xD1); - addToMapW(0x82, 0x00, 0x82); - addToMapW(0x82, 0x03, 0x93); - addToMapW(0x82, 0x03, 0xB3); - addToMapW(0x82, 0x04, 0x12); - addToMapW(0x82, 0x04, 0x32); - addToMapW(0x82, 0x05, 0xD2); - addToMapW(0x82, 0x20, 0x1A); - addToMapW(0x83, 0x00, 0x83); - addToMapW(0x83, 0x03, 0x94); - addToMapW(0x83, 0x03, 0xB4); - addToMapW(0x83, 0x04, 0x03); - addToMapW(0x83, 0x04, 0x13); - addToMapW(0x83, 0x04, 0x33); - addToMapW(0x83, 0x04, 0x53); - addToMapW(0x83, 0x05, 0xD3); - addToMapW(0x84, 0x00, 0x84); - addToMapW(0x84, 0x03, 0x95); - addToMapW(0x84, 0x03, 0xB5); - addToMapW(0x84, 0x04, 0x14); - addToMapW(0x84, 0x04, 0x34); - addToMapW(0x84, 0x05, 0xD4); - addToMapW(0x84, 0x20, 0x1E); - addToMapW(0x85, 0x03, 0x96); - addToMapW(0x85, 0x03, 0xB6); - addToMapW(0x85, 0x04, 0x01); - addToMapW(0x85, 0x04, 0x15); - addToMapW(0x85, 0x04, 0x35); - addToMapW(0x85, 0x04, 0x51); - addToMapW(0x85, 0x05, 0xD5); - addToMapW(0x85, 0x20, 0x26); - addToMapW(0x86, 0x00, 0x86); - addToMapW(0x86, 0x03, 0x97); - addToMapW(0x86, 0x03, 0xB7); - addToMapW(0x86, 0x04, 0x16); - addToMapW(0x86, 0x04, 0x36); - addToMapW(0x86, 0x05, 0xD6); - addToMapW(0x86, 0x20, 0x20); - addToMapW(0x87, 0x00, 0x87); - addToMapW(0x87, 0x03, 0x98); - addToMapW(0x87, 0x03, 0xB8); - addToMapW(0x87, 0x04, 0x04); - addToMapW(0x87, 0x04, 0x17); - addToMapW(0x87, 0x04, 0x37); - addToMapW(0x87, 0x04, 0x54); - addToMapW(0x87, 0x05, 0xD7); - addToMapW(0x87, 0x20, 0x21); - addToMapW(0x88, 0x00, 0x88); - addToMapW(0x88, 0x02, 0xC6); - addToMapW(0x88, 0x03, 0x99); - addToMapW(0x88, 0x03, 0xB9); - addToMapW(0x88, 0x04, 0x18); - addToMapW(0x88, 0x04, 0x38); - addToMapW(0x88, 0x05, 0xD8); - addToMapW(0x89, 0x00, 0x89); - addToMapW(0x89, 0x03, 0x9A); - addToMapW(0x89, 0x03, 0xBA); - addToMapW(0x89, 0x04, 0x05); - addToMapW(0x89, 0x04, 0x19); - addToMapW(0x89, 0x04, 0x39); - addToMapW(0x89, 0x04, 0x55); - addToMapW(0x89, 0x05, 0xD9); - addToMapW(0x89, 0x20, 0x30); - addToMapW(0x8a, 0x00, 0x8A); - addToMapW(0x8a, 0x01, 0x50); - addToMapW(0x8a, 0x01, 0x51); - addToMapW(0x8a, 0x01, 0x56); - addToMapW(0x8a, 0x01, 0x57); - addToMapW(0x8a, 0x03, 0x9B); - addToMapW(0x8a, 0x03, 0xBB); - addToMapW(0x8a, 0x04, 0x1A); - addToMapW(0x8a, 0x04, 0x3A); - addToMapW(0x8a, 0x05, 0xDA); - addToMapW(0x8b, 0x00, 0x8B); - addToMapW(0x8b, 0x03, 0x9C); - addToMapW(0x8b, 0x03, 0xBC); - addToMapW(0x8b, 0x04, 0x06); - addToMapW(0x8b, 0x04, 0x1B); - addToMapW(0x8b, 0x04, 0x3B); - addToMapW(0x8b, 0x04, 0x56); - addToMapW(0x8b, 0x05, 0xDB); - addToMapW(0x8b, 0x20, 0x39); - addToMapW(0x8c, 0x00, 0x8C); - addToMapW(0x8c, 0x01, 0x52); - addToMapW(0x8c, 0x01, 0x53); - addToMapW(0x8c, 0x03, 0x9D); - addToMapW(0x8c, 0x03, 0xBD); - addToMapW(0x8c, 0x04, 0x1C); - addToMapW(0x8c, 0x04, 0x3C); - addToMapW(0x8c, 0x05, 0xDC); - addToMapW(0x8d, 0x00, 0x8D); - addToMapW(0x8d, 0x01, 0x31); - addToMapW(0x8d, 0x01, 0x79); - addToMapW(0x8d, 0x01, 0x7A); - addToMapW(0x8d, 0x03, 0x9E); - addToMapW(0x8d, 0x03, 0xBE); - addToMapW(0x8d, 0x04, 0x07); - addToMapW(0x8d, 0x04, 0x1D); - addToMapW(0x8d, 0x04, 0x3D); - addToMapW(0x8d, 0x04, 0x57); - addToMapW(0x8d, 0x05, 0xDD); - addToMapW(0x8e, 0x00, 0x8E); - addToMapW(0x8e, 0x00, 0xC4); - addToMapW(0x8e, 0x00, 0xE4); - addToMapW(0x8e, 0x03, 0x9F); - addToMapW(0x8e, 0x03, 0xBF); - addToMapW(0x8e, 0x04, 0x1E); - addToMapW(0x8e, 0x04, 0x3E); - addToMapW(0x8e, 0x05, 0xDE); - addToMapW(0x8f, 0x00, 0x8F); - addToMapW(0x8f, 0x00, 0xC5); - addToMapW(0x8f, 0x00, 0xE5); - addToMapW(0x8f, 0x01, 0x06); - addToMapW(0x8f, 0x01, 0x07); - addToMapW(0x8f, 0x03, 0xA0); - addToMapW(0x8f, 0x03, 0xC0); - addToMapW(0x8f, 0x04, 0x08); - addToMapW(0x8f, 0x04, 0x1F); - addToMapW(0x8f, 0x04, 0x3F); - addToMapW(0x8f, 0x04, 0x58); - addToMapW(0x8f, 0x05, 0xDF); - addToMapW(0x8f, 0x21, 0x2B); - addToMapW(0x90, 0x00, 0x90); - addToMapW(0x90, 0x00, 0xC9); - addToMapW(0x90, 0x00, 0xE9); - addToMapW(0x90, 0x03, 0xA1); - addToMapW(0x90, 0x03, 0xC1); - addToMapW(0x90, 0x04, 0x20); - addToMapW(0x90, 0x04, 0x40); - addToMapW(0x90, 0x05, 0xE0); - addToMapW(0x91, 0x01, 0x39); - addToMapW(0x91, 0x01, 0x3A); - addToMapW(0x91, 0x03, 0xA3); - addToMapW(0x91, 0x03, 0xC2); - addToMapW(0x91, 0x03, 0xC3); - addToMapW(0x91, 0x04, 0x09); - addToMapW(0x91, 0x04, 0x21); - addToMapW(0x91, 0x04, 0x41); - addToMapW(0x91, 0x04, 0x59); - addToMapW(0x91, 0x05, 0xE1); - addToMapW(0x91, 0x06, 0x51); - addToMapW(0x91, 0x20, 0x18); - addToMapW(0x91, 0xFE, 0x7C); - addToMapW(0x91, 0xFE, 0x7D); - addToMapW(0x92, 0x00, 0xC6); - addToMapW(0x92, 0x00, 0xE6); - addToMapW(0x92, 0x03, 0xA4); - addToMapW(0x92, 0x03, 0xC4); - addToMapW(0x92, 0x04, 0x22); - addToMapW(0x92, 0x04, 0x42); - addToMapW(0x92, 0x05, 0xE2); - addToMapW(0x92, 0x06, 0x52); - addToMapW(0x92, 0x20, 0x19); - addToMapW(0x92, 0xFE, 0x7E); - addToMapW(0x92, 0xFE, 0x7F); - addToMapW(0x93, 0x03, 0xA5); - addToMapW(0x93, 0x03, 0xC5); - addToMapW(0x93, 0x04, 0x0A); - addToMapW(0x93, 0x04, 0x23); - addToMapW(0x93, 0x04, 0x43); - addToMapW(0x93, 0x04, 0x5A); - addToMapW(0x93, 0x05, 0xE3); - addToMapW(0x93, 0x20, 0x1C); - addToMapW(0x94, 0x00, 0xA4); - addToMapW(0x94, 0x03, 0xA6); - addToMapW(0x94, 0x03, 0xC6); - addToMapW(0x94, 0x04, 0x24); - addToMapW(0x94, 0x04, 0x44); - addToMapW(0x94, 0x05, 0xE4); - addToMapW(0x94, 0x20, 0x1D); - addToMapW(0x95, 0x01, 0x22); - addToMapW(0x95, 0x01, 0x23); - addToMapW(0x95, 0x01, 0x3D); - addToMapW(0x95, 0x01, 0x3E); - addToMapW(0x95, 0x03, 0xA7); - addToMapW(0x95, 0x03, 0xC7); - addToMapW(0x95, 0x04, 0x0B); - addToMapW(0x95, 0x04, 0x25); - addToMapW(0x95, 0x04, 0x45); - addToMapW(0x95, 0x04, 0x5B); - addToMapW(0x95, 0x05, 0xE5); - addToMapW(0x95, 0x06, 0x40); - addToMapW(0x95, 0x20, 0x22); - addToMapW(0x96, 0x00, 0xA2); - addToMapW(0x96, 0x03, 0xA8); - addToMapW(0x96, 0x03, 0xC8); - addToMapW(0x96, 0x04, 0x26); - addToMapW(0x96, 0x04, 0x46); - addToMapW(0x96, 0x05, 0xE6); - addToMapW(0x96, 0x20, 0x13); - addToMapW(0x97, 0x00, 0xB5); - addToMapW(0x97, 0x01, 0x5A); - addToMapW(0x97, 0x01, 0x5B); - addToMapW(0x97, 0x03, 0xA9); - addToMapW(0x97, 0x03, 0xC9); - addToMapW(0x97, 0x04, 0x0C); - addToMapW(0x97, 0x04, 0x27); - addToMapW(0x97, 0x04, 0x47); - addToMapW(0x97, 0x04, 0x5C); - addToMapW(0x97, 0x05, 0xE7); - addToMapW(0x97, 0x20, 0x14); - addToMapW(0x98, 0x00, 0x98); - addToMapW(0x98, 0x01, 0x30); - addToMapW(0x98, 0x02, 0xDC); - addToMapW(0x98, 0x04, 0x28); - addToMapW(0x98, 0x04, 0x48); - addToMapW(0x98, 0x05, 0xE8); - addToMapW(0x98, 0x06, 0x21); - addToMapW(0x98, 0xFE, 0x80); - addToMapW(0x99, 0x00, 0x99); - addToMapW(0x99, 0x00, 0xD6); - addToMapW(0x99, 0x00, 0xF6); - addToMapW(0x99, 0x04, 0x0E); - addToMapW(0x99, 0x04, 0x29); - addToMapW(0x99, 0x04, 0x49); - addToMapW(0x99, 0x04, 0x5E); - addToMapW(0x99, 0x05, 0xE9); - addToMapW(0x99, 0x06, 0x22); - addToMapW(0x99, 0x21, 0x22); - addToMapW(0x99, 0xFE, 0x81); - addToMapW(0x99, 0xFE, 0x82); - addToMapW(0x9a, 0x00, 0x9A); - addToMapW(0x9a, 0x00, 0xDC); - addToMapW(0x9a, 0x00, 0xFC); - addToMapW(0x9a, 0x04, 0x2A); - addToMapW(0x9a, 0x04, 0x4A); - addToMapW(0x9a, 0x05, 0xEA); - addToMapW(0x9a, 0x06, 0x23); - addToMapW(0x9a, 0xFE, 0x83); - addToMapW(0x9a, 0xFE, 0x84); - addToMapW(0x9b, 0x00, 0x9B); - addToMapW(0x9b, 0x00, 0xA2); - addToMapW(0x9b, 0x01, 0x64); - addToMapW(0x9b, 0x01, 0x65); - addToMapW(0x9b, 0x04, 0x0F); - addToMapW(0x9b, 0x04, 0x2B); - addToMapW(0x9b, 0x04, 0x4B); - addToMapW(0x9b, 0x04, 0x5F); - addToMapW(0x9b, 0x06, 0x24); - addToMapW(0x9b, 0x20, 0x3A); - addToMapW(0x9b, 0xFE, 0x85); - addToMapW(0x9b, 0xFE, 0x86); - addToMapW(0x9c, 0x00, 0x9C); - addToMapW(0x9c, 0x00, 0xA3); - addToMapW(0x9c, 0x04, 0x2C); - addToMapW(0x9c, 0x04, 0x4C); - addToMapW(0x9c, 0x20, 0xA4); - addToMapW(0x9d, 0x00, 0x9D); - addToMapW(0x9d, 0x00, 0xA5); - addToMapW(0x9d, 0x00, 0xD8); - addToMapW(0x9d, 0x00, 0xF8); - addToMapW(0x9d, 0x01, 0x41); - addToMapW(0x9d, 0x01, 0x42); - addToMapW(0x9d, 0x02, 0x78); - addToMapW(0x9d, 0x03, 0x98); - addToMapW(0x9d, 0x04, 0x2D); - addToMapW(0x9d, 0x04, 0x2E); - addToMapW(0x9d, 0x04, 0x4D); - addToMapW(0x9d, 0x04, 0x4E); - addToMapW(0x9d, 0x06, 0x25); - addToMapW(0x9d, 0x22, 0x05); - addToMapW(0x9d, 0xFE, 0x87); - addToMapW(0x9d, 0xFE, 0x88); - addToMapW(0x9e, 0x00, 0x9E); - addToMapW(0x9e, 0x00, 0xD7); - addToMapW(0x9e, 0x01, 0x5E); - addToMapW(0x9e, 0x01, 0x5F); - addToMapW(0x9e, 0x04, 0x2E); - addToMapW(0x9e, 0x04, 0x4E); - addToMapW(0x9e, 0x06, 0x26); - addToMapW(0x9e, 0x20, 0xA7); - addToMapW(0x9e, 0xFE, 0x89); - addToMapW(0x9e, 0xFE, 0x8A); - addToMapW(0x9e, 0xFE, 0x8B); - addToMapW(0x9e, 0xFE, 0x8C); - addToMapW(0x9f, 0x00, 0x9F); - addToMapW(0x9f, 0x00, 0xA4); - addToMapW(0x9f, 0x00, 0xFF); - addToMapW(0x9f, 0x01, 0x78); - addToMapW(0x9f, 0x01, 0x91); - addToMapW(0x9f, 0x01, 0x92); - addToMapW(0x9f, 0x04, 0x2A); - addToMapW(0x9f, 0x04, 0x2F); - addToMapW(0x9f, 0x04, 0x4A); - addToMapW(0x9f, 0x04, 0x4F); - addToMapW(0x9f, 0x06, 0x27); - addToMapW(0x9f, 0xFE, 0x8D); - addToMapW(0x9f, 0xFE, 0x8E); - addToMapW(0xa0, 0x00, 0xA0); - addToMapW(0xa0, 0x01, 0x00); - addToMapW(0xa0, 0x01, 0x01); - addToMapW(0xa0, 0x06, 0x28); - addToMapW(0xa0, 0xF8, 0xF0); - addToMapW(0xa0, 0xFE, 0x8F); - addToMapW(0xa0, 0xFE, 0x90); - addToMapW(0xa0, 0xFE, 0x91); - addToMapW(0xa0, 0xFE, 0x92); - addToMapW(0xa1, 0x00, 0xA1); - addToMapW(0xa1, 0x01, 0x2A); - addToMapW(0xa1, 0x01, 0x2B); - addToMapW(0xa1, 0x04, 0x10); - addToMapW(0xa1, 0x04, 0x30); - addToMapW(0xa1, 0x06, 0x29); - addToMapW(0xa1, 0x0E, 0x01); - addToMapW(0xa1, 0xFE, 0x93); - addToMapW(0xa1, 0xFE, 0x94); - addToMapW(0xa1, 0xFF, 0x61); - addToMapW(0xa2, 0x00, 0xA2); - addToMapW(0xa2, 0x06, 0x2A); - addToMapW(0xa2, 0x0E, 0x02); - addToMapW(0xa2, 0xFE, 0x95); - addToMapW(0xa2, 0xFE, 0x96); - addToMapW(0xa2, 0xFE, 0x97); - addToMapW(0xa2, 0xFE, 0x98); - addToMapW(0xa2, 0xFF, 0x62); - addToMapW(0xa3, 0x00, 0xA3); - addToMapW(0xa3, 0x01, 0x7B); - addToMapW(0xa3, 0x01, 0x7C); - addToMapW(0xa3, 0x04, 0x11); - addToMapW(0xa3, 0x04, 0x31); - addToMapW(0xa3, 0x06, 0x2B); - addToMapW(0xa3, 0x0E, 0x03); - addToMapW(0xa3, 0xFE, 0x99); - addToMapW(0xa3, 0xFE, 0x9A); - addToMapW(0xa3, 0xFE, 0x9B); - addToMapW(0xa3, 0xFE, 0x9C); - addToMapW(0xa3, 0xFF, 0x63); - addToMapW(0xa4, 0x00, 0xA4); - addToMapW(0xa4, 0x01, 0x04); - addToMapW(0xa4, 0x01, 0x05); - addToMapW(0xa4, 0x06, 0x2C); - addToMapW(0xa4, 0x0E, 0x04); - addToMapW(0xa4, 0xFE, 0x9D); - addToMapW(0xa4, 0xFE, 0x9E); - addToMapW(0xa4, 0xFE, 0x9F); - addToMapW(0xa4, 0xFE, 0xA0); - addToMapW(0xa4, 0xFF, 0x64); - addToMapW(0xa5, 0x00, 0xA5); - addToMapW(0xa5, 0x00, 0xD1); - addToMapW(0xa5, 0x00, 0xF1); - addToMapW(0xa5, 0x04, 0x26); - addToMapW(0xa5, 0x04, 0x46); - addToMapW(0xa5, 0x06, 0x2D); - addToMapW(0xa5, 0x0E, 0x05); - addToMapW(0xa5, 0xFE, 0xA1); - addToMapW(0xa5, 0xFE, 0xA2); - addToMapW(0xa5, 0xFE, 0xA3); - addToMapW(0xa5, 0xFE, 0xA4); - addToMapW(0xa5, 0xFF, 0x65); - addToMapW(0xa6, 0x00, 0xA6); - addToMapW(0xa6, 0x00, 0xAA); - addToMapW(0xa6, 0x01, 0x1E); - addToMapW(0xa6, 0x01, 0x1F); - addToMapW(0xa6, 0x01, 0x7D); - addToMapW(0xa6, 0x01, 0x7E); - addToMapW(0xa6, 0x06, 0x2E); - addToMapW(0xa6, 0x0E, 0x06); - addToMapW(0xa6, 0x20, 0x1D); - addToMapW(0xa6, 0xFE, 0xA5); - addToMapW(0xa6, 0xFE, 0xA6); - addToMapW(0xa6, 0xFE, 0xA7); - addToMapW(0xa6, 0xFE, 0xA8); - addToMapW(0xa6, 0xFF, 0x66); - addToMapW(0xa7, 0x00, 0xA6); - addToMapW(0xa7, 0x00, 0xA7); - addToMapW(0xa7, 0x00, 0xBA); - addToMapW(0xa7, 0x04, 0x14); - addToMapW(0xa7, 0x04, 0x34); - addToMapW(0xa7, 0x06, 0x2F); - addToMapW(0xa7, 0x0E, 0x07); - addToMapW(0xa7, 0xFE, 0xA9); - addToMapW(0xa7, 0xFE, 0xAA); - addToMapW(0xa7, 0xFF, 0x67); - addToMapW(0xa8, 0x00, 0xA8); - addToMapW(0xa8, 0x00, 0xA9); - addToMapW(0xa8, 0x00, 0xBF); - addToMapW(0xa8, 0x01, 0x18); - addToMapW(0xa8, 0x01, 0x19); - addToMapW(0xa8, 0x06, 0x30); - addToMapW(0xa8, 0x0E, 0x08); - addToMapW(0xa8, 0xFE, 0xAB); - addToMapW(0xa8, 0xFE, 0xAC); - addToMapW(0xa8, 0xFF, 0x68); - addToMapW(0xa9, 0x00, 0xA9); - addToMapW(0xa9, 0x00, 0xAE); - addToMapW(0xa9, 0x04, 0x15); - addToMapW(0xa9, 0x04, 0x35); - addToMapW(0xa9, 0x06, 0x31); - addToMapW(0xa9, 0x0E, 0x09); - addToMapW(0xa9, 0x23, 0x10); - addToMapW(0xa9, 0xFE, 0xAD); - addToMapW(0xa9, 0xFE, 0xAE); - addToMapW(0xa9, 0xFF, 0x69); - addToMapW(0xaa, 0x00, 0xAA); - addToMapW(0xaa, 0x00, 0xAC); - addToMapW(0xaa, 0x06, 0x32); - addToMapW(0xaa, 0x0E, 0x0A); - addToMapW(0xaa, 0x23, 0x10); - addToMapW(0xaa, 0xFE, 0xAF); - addToMapW(0xaa, 0xFE, 0xB0); - addToMapW(0xaa, 0xFF, 0x6A); - addToMapW(0xab, 0x00, 0xAB); - addToMapW(0xab, 0x00, 0xBD); - addToMapW(0xab, 0x04, 0x24); - addToMapW(0xab, 0x04, 0x44); - addToMapW(0xab, 0x06, 0x33); - addToMapW(0xab, 0x0E, 0x0B); - addToMapW(0xab, 0xFE, 0xB1); - addToMapW(0xab, 0xFE, 0xB2); - addToMapW(0xab, 0xFE, 0xB3); - addToMapW(0xab, 0xFE, 0xB4); - addToMapW(0xab, 0xFF, 0x6B); - addToMapW(0xac, 0x00, 0xAC); - addToMapW(0xac, 0x00, 0xBC); - addToMapW(0xac, 0x01, 0x0C); - addToMapW(0xac, 0x01, 0x0D); - addToMapW(0xac, 0x06, 0x34); - addToMapW(0xac, 0x0E, 0x0C); - addToMapW(0xac, 0xFE, 0xB5); - addToMapW(0xac, 0xFE, 0xB6); - addToMapW(0xac, 0xFE, 0xB7); - addToMapW(0xac, 0xFE, 0xB8); - addToMapW(0xac, 0xFF, 0x6C); - addToMapW(0xad, 0x00, 0xA1); - addToMapW(0xad, 0x00, 0xAD); - addToMapW(0xad, 0x01, 0x41); - addToMapW(0xad, 0x01, 0x42); - addToMapW(0xad, 0x04, 0x13); - addToMapW(0xad, 0x04, 0x33); - addToMapW(0xad, 0x06, 0x35); - addToMapW(0xad, 0x0E, 0x0D); - addToMapW(0xad, 0xFE, 0xB9); - addToMapW(0xad, 0xFE, 0xBA); - addToMapW(0xad, 0xFE, 0xBB); - addToMapW(0xad, 0xFE, 0xBC); - addToMapW(0xad, 0xFF, 0x6D); - addToMapW(0xae, 0x00, 0xAB); - addToMapW(0xae, 0x00, 0xAE); - addToMapW(0xae, 0x0E, 0x0E); - addToMapW(0xae, 0x22, 0x6A); - addToMapW(0xae, 0x30, 0x0A); - addToMapW(0xae, 0xFF, 0x6E); - addToMapW(0xaf, 0x00, 0xAF); - addToMapW(0xaf, 0x00, 0xBB); - addToMapW(0xaf, 0x0E, 0x0F); - addToMapW(0xaf, 0x22, 0x6B); - addToMapW(0xaf, 0x30, 0x0B); - addToMapW(0xaf, 0xFF, 0x6F); - addToMapW(0xb0, 0x00, 0xB0); - addToMapW(0xb0, 0x0E, 0x10); - addToMapW(0xb0, 0x25, 0x91); - addToMapW(0xb0, 0xFF, 0x70); - addToMapW(0xb1, 0x00, 0xB1); - addToMapW(0xb1, 0x0E, 0x11); - addToMapW(0xb1, 0x25, 0x92); - addToMapW(0xb1, 0xFF, 0x71); - addToMapW(0xb2, 0x00, 0xB2); - addToMapW(0xb2, 0x0E, 0x12); - addToMapW(0xb2, 0x25, 0x93); - addToMapW(0xb2, 0xFF, 0x72); - addToMapW(0xb3, 0x00, 0xA6); - addToMapW(0xb3, 0x00, 0xB3); - addToMapW(0xb3, 0x01, 0xC0); - addToMapW(0xb3, 0x0E, 0x13); - addToMapW(0xb3, 0x22, 0x23); - addToMapW(0xb3, 0x25, 0x02); - addToMapW(0xb3, 0x27, 0x58); - addToMapW(0xb3, 0xFF, 0x73); - addToMapW(0xb4, 0x00, 0xB4); - addToMapW(0xb4, 0x0E, 0x14); - addToMapW(0xb4, 0x25, 0x24); - addToMapW(0xb4, 0xFF, 0x74); - addToMapW(0xb5, 0x00, 0xB5); - addToMapW(0xb5, 0x00, 0xC1); - addToMapW(0xb5, 0x00, 0xE1); - addToMapW(0xb5, 0x01, 0x04); - addToMapW(0xb5, 0x01, 0x05); - addToMapW(0xb5, 0x0E, 0x15); - addToMapW(0xb5, 0x25, 0x61); - addToMapW(0xb5, 0xFF, 0x75); - addToMapW(0xb6, 0x00, 0xB6); - addToMapW(0xb6, 0x00, 0xC2); - addToMapW(0xb6, 0x00, 0xE2); - addToMapW(0xb6, 0x01, 0x0C); - addToMapW(0xb6, 0x01, 0x0D); - addToMapW(0xb6, 0x04, 0x25); - addToMapW(0xb6, 0x04, 0x45); - addToMapW(0xb6, 0x0E, 0x16); - addToMapW(0xb6, 0x25, 0x62); - addToMapW(0xb6, 0xFF, 0x76); - addToMapW(0xb7, 0x00, 0xB7); - addToMapW(0xb7, 0x00, 0xC0); - addToMapW(0xb7, 0x00, 0xE0); - addToMapW(0xb7, 0x01, 0x18); - addToMapW(0xb7, 0x01, 0x19); - addToMapW(0xb7, 0x01, 0x1A); - addToMapW(0xb7, 0x01, 0x1B); - addToMapW(0xb7, 0x0E, 0x17); - addToMapW(0xb7, 0x25, 0x56); - addToMapW(0xb7, 0xFF, 0x77); - addToMapW(0xb8, 0x00, 0xA9); - addToMapW(0xb8, 0x00, 0xB8); - addToMapW(0xb8, 0x01, 0x16); - addToMapW(0xb8, 0x01, 0x17); - addToMapW(0xb8, 0x01, 0x5E); - addToMapW(0xb8, 0x01, 0x5F); - addToMapW(0xb8, 0x04, 0x18); - addToMapW(0xb8, 0x04, 0x38); - addToMapW(0xb8, 0x0E, 0x18); - addToMapW(0xb8, 0x25, 0x55); - addToMapW(0xb8, 0xFF, 0x78); - addToMapW(0xb9, 0x00, 0xB9); - addToMapW(0xb9, 0x0E, 0x19); - addToMapW(0xb9, 0x25, 0x61); - addToMapW(0xb9, 0x25, 0x62); - addToMapW(0xb9, 0x25, 0x63); - addToMapW(0xb9, 0xFF, 0x79); - addToMapW(0xba, 0x00, 0xBA); - addToMapW(0xba, 0x0E, 0x1A); - addToMapW(0xba, 0x25, 0x51); - addToMapW(0xba, 0xFF, 0x7A); - addToMapW(0xbb, 0x00, 0xBB); - addToMapW(0xbb, 0x0E, 0x1B); - addToMapW(0xbb, 0x25, 0x55); - addToMapW(0xbb, 0x25, 0x56); - addToMapW(0xbb, 0x25, 0x57); - addToMapW(0xbb, 0xFF, 0x7B); - addToMapW(0xbc, 0x00, 0xBC); - addToMapW(0xbc, 0x0E, 0x1C); - addToMapW(0xbc, 0x25, 0x5B); - addToMapW(0xbc, 0x25, 0x5C); - addToMapW(0xbc, 0x25, 0x5D); - addToMapW(0xbc, 0xFF, 0x7C); - addToMapW(0xbd, 0x00, 0xA2); - addToMapW(0xbd, 0x00, 0xBD); - addToMapW(0xbd, 0x01, 0x2E); - addToMapW(0xbd, 0x01, 0x2F); - addToMapW(0xbd, 0x01, 0x7B); - addToMapW(0xbd, 0x01, 0x7C); - addToMapW(0xbd, 0x0E, 0x1D); - addToMapW(0xbd, 0x25, 0x5C); - addToMapW(0xbd, 0xFF, 0x7D); - addToMapW(0xbe, 0x00, 0xA5); - addToMapW(0xbe, 0x00, 0xBE); - addToMapW(0xbe, 0x01, 0x60); - addToMapW(0xbe, 0x01, 0x61); - addToMapW(0xbe, 0x04, 0x19); - addToMapW(0xbe, 0x04, 0x39); - addToMapW(0xbe, 0x0E, 0x1E); - addToMapW(0xbe, 0x25, 0x5B); - addToMapW(0xbe, 0xFF, 0x7E); - addToMapW(0xbf, 0x00, 0xAC); - addToMapW(0xbf, 0x00, 0xBF); - addToMapW(0xbf, 0x0E, 0x1F); - addToMapW(0xbf, 0x25, 0x10); - addToMapW(0xbf, 0xFF, 0x7F); - addToMapW(0xc0, 0x00, 0xC0); - addToMapW(0xc0, 0x00, 0xE0); - addToMapW(0xc0, 0x0E, 0x20); - addToMapW(0xc0, 0x25, 0x14); - addToMapW(0xc0, 0xFF, 0x80); - addToMapW(0xc1, 0x00, 0xC1); - addToMapW(0xc1, 0x00, 0xE1); - addToMapW(0xc1, 0x0E, 0x21); - addToMapW(0xc1, 0x25, 0x34); - addToMapW(0xc1, 0xFF, 0x81); - addToMapW(0xc2, 0x00, 0xC2); - addToMapW(0xc2, 0x00, 0xE2); - addToMapW(0xc2, 0x0E, 0x22); - addToMapW(0xc2, 0x25, 0x2C); - addToMapW(0xc2, 0xFF, 0x82); - addToMapW(0xc3, 0x01, 0x02); - addToMapW(0xc3, 0x01, 0x03); - addToMapW(0xc3, 0x0E, 0x23); - addToMapW(0xc3, 0x25, 0x1C); - addToMapW(0xc3, 0xFF, 0x83); - addToMapW(0xc4, 0x00, 0xAF); - addToMapW(0xc4, 0x00, 0xC4); - addToMapW(0xc4, 0x00, 0xE4); - addToMapW(0xc4, 0x02, 0xC9); - addToMapW(0xc4, 0x03, 0x04); - addToMapW(0xc4, 0x03, 0x05); - addToMapW(0xc4, 0x0E, 0x24); - addToMapW(0xc4, 0x25, 0x00); - addToMapW(0xc4, 0xFF, 0x84); - addToMapW(0xc5, 0x00, 0xC5); - addToMapW(0xc5, 0x00, 0xE5); - addToMapW(0xc5, 0x0E, 0x25); - addToMapW(0xc5, 0x20, 0x20); - addToMapW(0xc5, 0x20, 0x21); - addToMapW(0xc5, 0x25, 0x3C); - addToMapW(0xc5, 0xFF, 0x85); - addToMapW(0xc6, 0x00, 0xC6); - addToMapW(0xc6, 0x00, 0xE6); - addToMapW(0xc6, 0x01, 0x02); - addToMapW(0xc6, 0x01, 0x03); - addToMapW(0xc6, 0x01, 0x72); - addToMapW(0xc6, 0x01, 0x73); - addToMapW(0xc6, 0x0E, 0x26); - addToMapW(0xc6, 0x25, 0x5E); - addToMapW(0xc6, 0xFF, 0x86); - addToMapW(0xc7, 0x00, 0xC3); - addToMapW(0xc7, 0x00, 0xC7); - addToMapW(0xc7, 0x00, 0xE3); - addToMapW(0xc7, 0x00, 0xE7); - addToMapW(0xc7, 0x01, 0x6A); - addToMapW(0xc7, 0x01, 0x6B); - addToMapW(0xc7, 0x04, 0x1A); - addToMapW(0xc7, 0x04, 0x3A); - addToMapW(0xc7, 0x0E, 0x27); - addToMapW(0xc7, 0x25, 0x5F); - addToMapW(0xc7, 0xFF, 0x87); - addToMapW(0xc8, 0x00, 0xC8); - addToMapW(0xc8, 0x00, 0xE8); - addToMapW(0xc8, 0x0E, 0x28); - addToMapW(0xc8, 0x25, 0x58); - addToMapW(0xc8, 0x25, 0x59); - addToMapW(0xc8, 0x25, 0x5A); - addToMapW(0xc8, 0xFF, 0x88); - addToMapW(0xc9, 0x00, 0xC9); - addToMapW(0xc9, 0x00, 0xE9); - addToMapW(0xc9, 0x0E, 0x29); - addToMapW(0xc9, 0x25, 0x52); - addToMapW(0xc9, 0x25, 0x53); - addToMapW(0xc9, 0x25, 0x54); - addToMapW(0xc9, 0xFF, 0x89); - addToMapW(0xca, 0x00, 0xCA); - addToMapW(0xca, 0x00, 0xEA); - addToMapW(0xca, 0x0E, 0x2A); - addToMapW(0xca, 0x25, 0x67); - addToMapW(0xca, 0x25, 0x68); - addToMapW(0xca, 0x25, 0x69); - addToMapW(0xca, 0xFF, 0x8A); - addToMapW(0xcb, 0x00, 0xCB); - addToMapW(0xcb, 0x00, 0xEB); - addToMapW(0xcb, 0x0E, 0x2B); - addToMapW(0xcb, 0x25, 0x64); - addToMapW(0xcb, 0x25, 0x65); - addToMapW(0xcb, 0x25, 0x66); - addToMapW(0xcb, 0xFF, 0x8B); - addToMapW(0xcc, 0x03, 0x00); - addToMapW(0xcc, 0x0E, 0x2C); - addToMapW(0xcc, 0x25, 0x5E); - addToMapW(0xcc, 0x25, 0x5F); - addToMapW(0xcc, 0x25, 0x60); - addToMapW(0xcc, 0xFF, 0x8C); - addToMapW(0xcd, 0x00, 0xCD); - addToMapW(0xcd, 0x00, 0xED); - addToMapW(0xcd, 0x0E, 0x2D); - addToMapW(0xcd, 0x25, 0x50); - addToMapW(0xcd, 0xFF, 0x8D); - addToMapW(0xce, 0x00, 0xCE); - addToMapW(0xce, 0x00, 0xEE); - addToMapW(0xce, 0x0E, 0x2E); - addToMapW(0xce, 0x20, 0x21); - addToMapW(0xce, 0x25, 0x6A); - addToMapW(0xce, 0x25, 0x6B); - addToMapW(0xce, 0x25, 0x6C); - addToMapW(0xce, 0xFF, 0x8E); - addToMapW(0xcf, 0x00, 0xA4); - addToMapW(0xcf, 0x00, 0xCF); - addToMapW(0xcf, 0x00, 0xEF); - addToMapW(0xcf, 0x01, 0x7D); - addToMapW(0xcf, 0x01, 0x7E); - addToMapW(0xcf, 0x0E, 0x2F); - addToMapW(0xcf, 0x25, 0x67); - addToMapW(0xcf, 0xFF, 0x8F); - addToMapW(0xd0, 0x00, 0xBA); - addToMapW(0xd0, 0x01, 0x10); - addToMapW(0xd0, 0x01, 0x11); - addToMapW(0xd0, 0x0E, 0x30); - addToMapW(0xd0, 0x25, 0x68); - addToMapW(0xd0, 0xFF, 0x90); - addToMapW(0xd1, 0x00, 0xAA); - addToMapW(0xd1, 0x00, 0xD0); - addToMapW(0xd1, 0x00, 0xD1); - addToMapW(0xd1, 0x00, 0xF0); - addToMapW(0xd1, 0x00, 0xF1); - addToMapW(0xd1, 0x01, 0x10); - addToMapW(0xd1, 0x01, 0x11); - addToMapW(0xd1, 0x01, 0x89); - addToMapW(0xd1, 0x04, 0x1B); - addToMapW(0xd1, 0x04, 0x3B); - addToMapW(0xd1, 0x0E, 0x31); - addToMapW(0xd1, 0x25, 0x64); - addToMapW(0xd1, 0xFF, 0x91); - addToMapW(0xd2, 0x00, 0xCA); - addToMapW(0xd2, 0x00, 0xEA); - addToMapW(0xd2, 0x01, 0x0E); - addToMapW(0xd2, 0x01, 0x0F); - addToMapW(0xd2, 0x03, 0x09); - addToMapW(0xd2, 0x0E, 0x32); - addToMapW(0xd2, 0x25, 0x65); - addToMapW(0xd2, 0xFF, 0x92); - addToMapW(0xd3, 0x00, 0xCB); - addToMapW(0xd3, 0x00, 0xD3); - addToMapW(0xd3, 0x00, 0xEB); - addToMapW(0xd3, 0x00, 0xF3); - addToMapW(0xd3, 0x04, 0x1C); - addToMapW(0xd3, 0x04, 0x3C); - addToMapW(0xd3, 0x0E, 0x33); - addToMapW(0xd3, 0x25, 0x59); - addToMapW(0xd3, 0xFF, 0x93); - addToMapW(0xd4, 0x00, 0xC8); - addToMapW(0xd4, 0x00, 0xD4); - addToMapW(0xd4, 0x00, 0xE8); - addToMapW(0xd4, 0x00, 0xF4); - addToMapW(0xd4, 0x0E, 0x34); - addToMapW(0xd4, 0x25, 0x58); - addToMapW(0xd4, 0xFF, 0x94); - addToMapW(0xd5, 0x01, 0x31); - addToMapW(0xd5, 0x01, 0x47); - addToMapW(0xd5, 0x01, 0x48); - addToMapW(0xd5, 0x01, 0xA0); - addToMapW(0xd5, 0x01, 0xA1); - addToMapW(0xd5, 0x04, 0x1D); - addToMapW(0xd5, 0x04, 0x3D); - addToMapW(0xd5, 0x0E, 0x35); - addToMapW(0xd5, 0x25, 0x52); - addToMapW(0xd5, 0xF8, 0xBB); - addToMapW(0xd5, 0xFF, 0x95); - addToMapW(0xd6, 0x00, 0xCD); - addToMapW(0xd6, 0x00, 0xD6); - addToMapW(0xd6, 0x00, 0xED); - addToMapW(0xd6, 0x00, 0xF6); - addToMapW(0xd6, 0x0E, 0x36); - addToMapW(0xd6, 0x25, 0x53); - addToMapW(0xd6, 0xFF, 0x96); - addToMapW(0xd7, 0x00, 0xCE); - addToMapW(0xd7, 0x00, 0xD7); - addToMapW(0xd7, 0x00, 0xEE); - addToMapW(0xd7, 0x04, 0x1E); - addToMapW(0xd7, 0x04, 0x3E); - addToMapW(0xd7, 0x0E, 0x37); - addToMapW(0xd7, 0x25, 0x6B); - addToMapW(0xd7, 0xFF, 0x97); - addToMapW(0xd8, 0x00, 0xCF); - addToMapW(0xd8, 0x00, 0xD8); - addToMapW(0xd8, 0x00, 0xEF); - addToMapW(0xd8, 0x00, 0xF8); - addToMapW(0xd8, 0x0E, 0x38); - addToMapW(0xd8, 0x20, 0x21); - addToMapW(0xd8, 0x25, 0x6A); - addToMapW(0xd8, 0xFF, 0x98); - addToMapW(0xd9, 0x00, 0xD9); - addToMapW(0xd9, 0x00, 0xF9); - addToMapW(0xd9, 0x0E, 0x39); - addToMapW(0xd9, 0x25, 0x18); - addToMapW(0xd9, 0xFF, 0x99); - addToMapW(0xda, 0x00, 0xDA); - addToMapW(0xda, 0x00, 0xFA); - addToMapW(0xda, 0x0E, 0x3A); - addToMapW(0xda, 0x25, 0x0C); - addToMapW(0xda, 0xFF, 0x9A); - addToMapW(0xdb, 0x00, 0xDB); - addToMapW(0xdb, 0x00, 0xFB); - addToMapW(0xdb, 0x25, 0x88); - addToMapW(0xdb, 0x25, 0x8C); - addToMapW(0xdb, 0x25, 0x90); - addToMapW(0xdb, 0xF8, 0xC1); - addToMapW(0xdb, 0xFF, 0x9B); - addToMapW(0xdc, 0x00, 0xDC); - addToMapW(0xdc, 0x00, 0xFC); - addToMapW(0xdc, 0x25, 0x84); - addToMapW(0xdc, 0xF8, 0xC2); - addToMapW(0xdc, 0xFF, 0x9C); - addToMapW(0xdd, 0x00, 0xA6); - addToMapW(0xdd, 0x01, 0x62); - addToMapW(0xdd, 0x01, 0x63); - addToMapW(0xdd, 0x01, 0xAF); - addToMapW(0xdd, 0x01, 0xB0); - addToMapW(0xdd, 0x04, 0x1F); - addToMapW(0xdd, 0x04, 0x3F); - addToMapW(0xdd, 0x25, 0x8C); - addToMapW(0xdd, 0xF8, 0xC3); - addToMapW(0xdd, 0xFF, 0x9D); - addToMapW(0xde, 0x00, 0xCC); - addToMapW(0xde, 0x00, 0xEC); - addToMapW(0xde, 0x01, 0x6E); - addToMapW(0xde, 0x01, 0x6F); - addToMapW(0xde, 0x03, 0x03); - addToMapW(0xde, 0x25, 0x90); - addToMapW(0xde, 0xF8, 0xC4); - addToMapW(0xde, 0xFF, 0x9E); - addToMapW(0xdf, 0x00, 0xDF); - addToMapW(0xdf, 0x0E, 0x3F); - addToMapW(0xdf, 0x25, 0x80); - addToMapW(0xdf, 0xFF, 0x9F); - addToMapW(0xe0, 0x00, 0xD3); - addToMapW(0xe0, 0x00, 0xF3); - addToMapW(0xe0, 0x03, 0x91); - addToMapW(0xe0, 0x03, 0xB1); - addToMapW(0xe0, 0x04, 0x2F); - addToMapW(0xe0, 0x04, 0x4F); - addToMapW(0xe0, 0x06, 0x36); - addToMapW(0xe0, 0x0E, 0x40); - addToMapW(0xe0, 0xFE, 0xBD); - addToMapW(0xe0, 0xFE, 0xBE); - addToMapW(0xe0, 0xFE, 0xBF); - addToMapW(0xe0, 0xFE, 0xC0); - addToMapW(0xe1, 0x00, 0xDF); - addToMapW(0xe1, 0x03, 0xB2); - addToMapW(0xe1, 0x06, 0x37); - addToMapW(0xe1, 0x0E, 0x41); - addToMapW(0xe1, 0xFE, 0xC1); - addToMapW(0xe1, 0xFE, 0xC2); - addToMapW(0xe1, 0xFE, 0xC3); - addToMapW(0xe1, 0xFE, 0xC4); - addToMapW(0xe2, 0x00, 0xD4); - addToMapW(0xe2, 0x00, 0xF4); - addToMapW(0xe2, 0x01, 0x4C); - addToMapW(0xe2, 0x01, 0x4D); - addToMapW(0xe2, 0x03, 0x93); - addToMapW(0xe2, 0x04, 0x20); - addToMapW(0xe2, 0x04, 0x40); - addToMapW(0xe2, 0x06, 0x38); - addToMapW(0xe2, 0x0E, 0x42); - addToMapW(0xe2, 0xFE, 0xC5); - addToMapW(0xe2, 0xFE, 0xC6); - addToMapW(0xe2, 0xFE, 0xC7); - addToMapW(0xe2, 0xFE, 0xC8); - addToMapW(0xe3, 0x00, 0xD2); - addToMapW(0xe3, 0x00, 0xF2); - addToMapW(0xe3, 0x01, 0x43); - addToMapW(0xe3, 0x01, 0x44); - addToMapW(0xe3, 0x03, 0xA0); - addToMapW(0xe3, 0x03, 0xC0); - addToMapW(0xe3, 0x06, 0x39); - addToMapW(0xe3, 0x0E, 0x43); - addToMapW(0xe3, 0xFE, 0xC9); - addToMapW(0xe3, 0xFE, 0xCA); - addToMapW(0xe3, 0xFE, 0xCB); - addToMapW(0xe3, 0xFE, 0xCC); - addToMapW(0xe4, 0x01, 0xA9); - addToMapW(0xe4, 0x03, 0xA3); - addToMapW(0xe4, 0x03, 0xC3); - addToMapW(0xe4, 0x04, 0x21); - addToMapW(0xe4, 0x04, 0x41); - addToMapW(0xe4, 0x06, 0x3A); - addToMapW(0xe4, 0x0E, 0x44); - addToMapW(0xe4, 0x22, 0x11); - addToMapW(0xe4, 0xFE, 0xCD); - addToMapW(0xe4, 0xFE, 0xCE); - addToMapW(0xe4, 0xFE, 0xCF); - addToMapW(0xe4, 0xFE, 0xD0); - addToMapW(0xe5, 0x00, 0xD5); - addToMapW(0xe5, 0x00, 0xF5); - addToMapW(0xe5, 0x06, 0x41); - addToMapW(0xe5, 0x0E, 0x45); - addToMapW(0xe5, 0xFE, 0xD1); - addToMapW(0xe5, 0xFE, 0xD2); - addToMapW(0xe5, 0xFE, 0xD3); - addToMapW(0xe5, 0xFE, 0xD4); - addToMapW(0xe6, 0x00, 0xB5); - addToMapW(0xe6, 0x01, 0x60); - addToMapW(0xe6, 0x01, 0x61); - addToMapW(0xe6, 0x03, 0xBC); - addToMapW(0xe6, 0x04, 0x22); - addToMapW(0xe6, 0x04, 0x42); - addToMapW(0xe6, 0x0E, 0x46); - addToMapW(0xe7, 0x03, 0xA4); - addToMapW(0xe7, 0x03, 0xC4); - addToMapW(0xe7, 0x06, 0x42); - addToMapW(0xe7, 0x0E, 0x47); - addToMapW(0xe7, 0xF8, 0xBC); - addToMapW(0xe7, 0xFE, 0xD5); - addToMapW(0xe7, 0xFE, 0xD6); - addToMapW(0xe7, 0xFE, 0xD7); - addToMapW(0xe7, 0xFE, 0xD8); - addToMapW(0xe8, 0x00, 0xD7); - addToMapW(0xe8, 0x00, 0xDE); - addToMapW(0xe8, 0x00, 0xFE); - addToMapW(0xe8, 0x01, 0x36); - addToMapW(0xe8, 0x01, 0x37); - addToMapW(0xe8, 0x01, 0x54); - addToMapW(0xe8, 0x01, 0x55); - addToMapW(0xe8, 0x02, 0x78); - addToMapW(0xe8, 0x03, 0xA6); - addToMapW(0xe8, 0x03, 0xC6); - addToMapW(0xe8, 0x04, 0x23); - addToMapW(0xe8, 0x04, 0x43); - addToMapW(0xe8, 0x06, 0x43); - addToMapW(0xe8, 0x0E, 0x48); - addToMapW(0xe8, 0x22, 0x05); - addToMapW(0xe8, 0xFE, 0xD9); - addToMapW(0xe8, 0xFE, 0xDA); - addToMapW(0xe8, 0xFE, 0xDB); - addToMapW(0xe8, 0xFE, 0xDC); - addToMapW(0xe9, 0x00, 0xDA); - addToMapW(0xe9, 0x00, 0xFA); - addToMapW(0xe9, 0x03, 0x98); - addToMapW(0xe9, 0x06, 0x44); - addToMapW(0xe9, 0x0E, 0x49); - addToMapW(0xe9, 0xFE, 0xDD); - addToMapW(0xe9, 0xFE, 0xDE); - addToMapW(0xe9, 0xFE, 0xDF); - addToMapW(0xe9, 0xFE, 0xE0); - addToMapW(0xea, 0x00, 0xDB); - addToMapW(0xea, 0x00, 0xFB); - addToMapW(0xea, 0x01, 0x3B); - addToMapW(0xea, 0x01, 0x3C); - addToMapW(0xea, 0x03, 0x86); - addToMapW(0xea, 0x03, 0xA9); - addToMapW(0xea, 0x03, 0xAC); - addToMapW(0xea, 0x04, 0x16); - addToMapW(0xea, 0x04, 0x36); - addToMapW(0xea, 0x06, 0x45); - addToMapW(0xea, 0x0E, 0x4A); - addToMapW(0xea, 0x21, 0x26); - addToMapW(0xea, 0xFE, 0xE1); - addToMapW(0xea, 0xFE, 0xE2); - addToMapW(0xea, 0xFE, 0xE3); - addToMapW(0xea, 0xFE, 0xE4); - addToMapW(0xeb, 0x00, 0xD9); - addToMapW(0xeb, 0x00, 0xF9); - addToMapW(0xeb, 0x01, 0x70); - addToMapW(0xeb, 0x01, 0x71); - addToMapW(0xeb, 0x03, 0x88); - addToMapW(0xeb, 0x03, 0x94); - addToMapW(0xeb, 0x03, 0xAD); - addToMapW(0xeb, 0x03, 0xB4); - addToMapW(0xeb, 0x06, 0x46); - addToMapW(0xeb, 0x0E, 0x4B); - addToMapW(0xeb, 0xFE, 0xE5); - addToMapW(0xeb, 0xFE, 0xE6); - addToMapW(0xeb, 0xFE, 0xE7); - addToMapW(0xeb, 0xFE, 0xE8); - addToMapW(0xec, 0x03, 0x01); - addToMapW(0xec, 0x03, 0x89); - addToMapW(0xec, 0x03, 0xAE); - addToMapW(0xec, 0x04, 0x12); - addToMapW(0xec, 0x04, 0x32); - addToMapW(0xec, 0x06, 0x47); - addToMapW(0xec, 0x0E, 0x4C); - addToMapW(0xec, 0x22, 0x1E); - addToMapW(0xec, 0xFE, 0xE9); - addToMapW(0xec, 0xFE, 0xEA); - addToMapW(0xec, 0xFE, 0xEB); - addToMapW(0xec, 0xFE, 0xEC); - addToMapW(0xed, 0x00, 0xDD); - addToMapW(0xed, 0x00, 0xFD); - addToMapW(0xed, 0x01, 0x12); - addToMapW(0xed, 0x01, 0x13); - addToMapW(0xed, 0x03, 0x8A); - addToMapW(0xed, 0x03, 0xAF); - addToMapW(0xed, 0x06, 0x48); - addToMapW(0xed, 0x0E, 0x4D); - addToMapW(0xed, 0xFE, 0xED); - addToMapW(0xed, 0xFE, 0xEE); - addToMapW(0xee, 0x00, 0xAF); - addToMapW(0xee, 0x01, 0x45); - addToMapW(0xee, 0x01, 0x46); - addToMapW(0xee, 0x03, 0x04); - addToMapW(0xee, 0x03, 0x05); - addToMapW(0xee, 0x03, 0x8C); - addToMapW(0xee, 0x03, 0x95); - addToMapW(0xee, 0x03, 0xB5); - addToMapW(0xee, 0x03, 0xCC); - addToMapW(0xee, 0x04, 0x2C); - addToMapW(0xee, 0x04, 0x4C); - addToMapW(0xee, 0x06, 0x49); - addToMapW(0xee, 0x0E, 0x4E); - addToMapW(0xee, 0xFE, 0xEF); - addToMapW(0xee, 0xFE, 0xF0); - addToMapW(0xef, 0x00, 0xB4); - addToMapW(0xef, 0x02, 0xB9); - addToMapW(0xef, 0x02, 0xCA); - addToMapW(0xef, 0x03, 0x01); - addToMapW(0xef, 0x03, 0x8E); - addToMapW(0xef, 0x03, 0xCD); - addToMapW(0xef, 0x06, 0x4A); - addToMapW(0xef, 0x0E, 0x4F); - addToMapW(0xef, 0x20, 0x19); - addToMapW(0xef, 0x20, 0x32); - addToMapW(0xef, 0x20, 0x35); - addToMapW(0xef, 0x21, 0x16); - addToMapW(0xef, 0x22, 0x29); - addToMapW(0xef, 0xFE, 0xF1); - addToMapW(0xef, 0xFE, 0xF2); - addToMapW(0xef, 0xFE, 0xF3); - addToMapW(0xef, 0xFE, 0xF4); - addToMapW(0xf0, 0x00, 0xAD); - addToMapW(0xf0, 0x03, 0x8F); - addToMapW(0xf0, 0x03, 0xCE); - addToMapW(0xf0, 0x04, 0x01); - addToMapW(0xf0, 0x04, 0x51); - addToMapW(0xf0, 0x0E, 0x50); - addToMapW(0xf0, 0x22, 0x61); - addToMapW(0xf1, 0x00, 0xB1); - addToMapW(0xf1, 0x02, 0xDD); - addToMapW(0xf1, 0x06, 0x4B); - addToMapW(0xf1, 0x0E, 0x51); - addToMapW(0xf1, 0x22, 0x13); - addToMapW(0xf1, 0xFE, 0x70); - addToMapW(0xf1, 0xFE, 0x71); - addToMapW(0xf2, 0x02, 0xDB); - addToMapW(0xf2, 0x03, 0x23); - addToMapW(0xf2, 0x04, 0x04); - addToMapW(0xf2, 0x04, 0x2B); - addToMapW(0xf2, 0x04, 0x4B); - addToMapW(0xf2, 0x04, 0x54); - addToMapW(0xf2, 0x06, 0x4C); - addToMapW(0xf2, 0x0E, 0x52); - addToMapW(0xf2, 0x20, 0x17); - addToMapW(0xf2, 0x20, 0x1C); - addToMapW(0xf2, 0x22, 0x65); - addToMapW(0xf2, 0xF8, 0xBD); - addToMapW(0xf2, 0xFE, 0x72); - addToMapW(0xf3, 0x00, 0xBE); - addToMapW(0xf3, 0x02, 0xC7); - addToMapW(0xf3, 0x03, 0x0C); - addToMapW(0xf3, 0x06, 0x4D); - addToMapW(0xf3, 0x0E, 0x53); - addToMapW(0xf3, 0x22, 0x64); - addToMapW(0xf3, 0xFE, 0x74); - addToMapW(0xf4, 0x00, 0xB6); - addToMapW(0xf4, 0x02, 0xD8); - addToMapW(0xf4, 0x03, 0x06); - addToMapW(0xf4, 0x03, 0xAA); - addToMapW(0xf4, 0x03, 0xCA); - addToMapW(0xf4, 0x04, 0x07); - addToMapW(0xf4, 0x04, 0x17); - addToMapW(0xf4, 0x04, 0x37); - addToMapW(0xf4, 0x04, 0x57); - addToMapW(0xf4, 0x06, 0x4E); - addToMapW(0xf4, 0x0E, 0x54); - addToMapW(0xf4, 0x23, 0x20); - addToMapW(0xf4, 0xFE, 0x76); - addToMapW(0xf4, 0xFE, 0x77); - addToMapW(0xf5, 0x00, 0xA7); - addToMapW(0xf5, 0x03, 0xAB); - addToMapW(0xf5, 0x03, 0xCB); - addToMapW(0xf5, 0x06, 0x4F); - addToMapW(0xf5, 0x0E, 0x55); - addToMapW(0xf5, 0x23, 0x21); - addToMapW(0xf5, 0xFE, 0x78); - addToMapW(0xf5, 0xFE, 0x79); - addToMapW(0xf6, 0x00, 0xF7); - addToMapW(0xf6, 0x04, 0x0E); - addToMapW(0xf6, 0x04, 0x28); - addToMapW(0xf6, 0x04, 0x48); - addToMapW(0xf6, 0x04, 0x5E); - addToMapW(0xf6, 0x06, 0x50); - addToMapW(0xf6, 0x0E, 0x56); - addToMapW(0xf6, 0xFE, 0x7A); - addToMapW(0xf6, 0xFE, 0x7B); - addToMapW(0xf7, 0x00, 0xB8); - addToMapW(0xf7, 0x00, 0xF7); - addToMapW(0xf7, 0x02, 0xDB); - addToMapW(0xf7, 0x03, 0x27); - addToMapW(0xf7, 0x0E, 0x57); - addToMapW(0xf7, 0x20, 0x1E); - addToMapW(0xf7, 0x22, 0x48); - addToMapW(0xf8, 0x00, 0xB0); - addToMapW(0xf8, 0x02, 0xDA); - addToMapW(0xf8, 0x03, 0x0A); - addToMapW(0xf8, 0x04, 0x2D); - addToMapW(0xf8, 0x04, 0x4D); - addToMapW(0xf8, 0x0E, 0x58); - addToMapW(0xf8, 0x20, 0x70); - addToMapW(0xf8, 0x22, 0x18); - addToMapW(0xf9, 0x00, 0xA8); - addToMapW(0xf9, 0x02, 0xDD); - addToMapW(0xf9, 0x03, 0x08); - addToMapW(0xf9, 0x0E, 0x59); - addToMapW(0xf9, 0x22, 0x19); - addToMapW(0xfa, 0x00, 0xB7); - addToMapW(0xfa, 0x02, 0xD9); - addToMapW(0xfa, 0x03, 0x07); - addToMapW(0xfa, 0x04, 0x29); - addToMapW(0xfa, 0x04, 0x49); - addToMapW(0xfa, 0x0E, 0x5A); - addToMapW(0xfa, 0x20, 0x24); - addToMapW(0xfa, 0x22, 0xC5); - addToMapW(0xfa, 0x30, 0xFB); - addToMapW(0xfb, 0x00, 0xB9); - addToMapW(0xfb, 0x0E, 0x5B); - addToMapW(0xfb, 0x20, 0x81); - addToMapW(0xfb, 0x22, 0x1A); - addToMapW(0xfb, 0x27, 0x13); - addToMapW(0xfc, 0x00, 0xB3); - addToMapW(0xfc, 0x01, 0x58); - addToMapW(0xfc, 0x01, 0x59); - addToMapW(0xfc, 0x04, 0x27); - addToMapW(0xfc, 0x04, 0x47); - addToMapW(0xfc, 0x20, 0x7F); - addToMapW(0xfc, 0x20, 0x83); - addToMapW(0xfc, 0x21, 0x16); - addToMapW(0xfc, 0xF8, 0xC5); - addToMapW(0xfd, 0x00, 0xA4); - addToMapW(0xfd, 0x00, 0xA7); - addToMapW(0xfd, 0x00, 0xB2); - addToMapW(0xfd, 0x20, 0x82); - addToMapW(0xfd, 0xF8, 0xC6); - addToMapW(0xfd, 0xF8, 0xF1); - addToMapW(0xfe, 0x20, 0xAB); - addToMapW(0xfe, 0x25, 0xA0); - addToMapW(0xfe, 0xF8, 0xC7); - addToMapW(0xfe, 0xF8, 0xF2); - addToMapW(0xff, 0x00, 0xA0); - addToMapW(0xff, 0xF8, 0xC8); - addToMapW(0xff, 0xF8, 0xF3); -} +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifdef _WIN32 + #pragma warning(disable : 4786 4267 4018) +#endif + +#include "lm2ntlm.h" + +LM2NTLMcorrector::LM2NTLMcorrector() +{ + progressCurrentCombination = 0; + totalCurrentCombination = 1; + counterOverall = 0; + countCombinations = 0; + countTotalCombinations = 0; + counter = 0; + fillMapW(); + aborting = false; + sBinary = ""; + +} + +string LM2NTLMcorrector::getBinary() +{ + return sBinary; +} + +bool LM2NTLMcorrector::LMPasswordCorrectUnicode(string hexPassword, unsigned char* pNTLMHash, string& sNTLMPassword) //, unsigned char* pLMPassword +{ + string sPlain = ""; + + UINT4 i; + for (i = 0; i < hexPassword.size() / 2; i++) + { + string sSub = hexPassword.substr(i * 2, 2); + int nValue; + sscanf(sSub.c_str(), "%02x", &nValue); + sPlain += (unsigned char)nValue; + } + + memcpy(NTLMHash, pNTLMHash, MD4_DIGEST_LENGTH); + + + unsigned long int tmpLength = sPlain.size() * 2; + unsigned char* pLMPassword = new unsigned char[tmpLength]; + + //printf("Searching for unicode password.\n"); + printf("Failed case correction, trying unicode correction for: %s\n", sPlain.c_str()); + //printf("NTLM hash: %s\n\n", sNTLMHash.c_str()); + + setvbuf(stdout, NULL, _IONBF,0); + + startClock = clock(); + previousClock = clock(); + +#ifndef _WIN32 + tty_init(); +#endif + + if (startCorrecting(sPlain, sNTLMPassword, pLMPassword)) + { + sBinary = ByteToStr(pLMPassword, tmpLength).c_str(); + //printf("\nFound unicode password: %s\n", sNTLMPassword.c_str()); + //printf("Password in hex: %s\n", sBinary.c_str()); + writeEndStats(); +#ifndef _WIN32 + tty_done(); +#endif + return true; + } + else + { + //printf("\ncase correction for password %s fail!\n", sPlain.c_str()); + writeEndStats(); +#ifndef _WIN32 + tty_done(); +#endif + return false; + } +} + +bool LM2NTLMcorrector::startCorrecting(string sLMPassword, string& sNTLMPassword, unsigned char* pLMPassword) +{ + if (sLMPassword.size() == 0) + { + sNTLMPassword = ""; + return true; + } + + string muteMe = sLMPassword; + int length = muteMe.size(); + + unsigned char* pMuteMe = new unsigned char[length]; + unsigned char* pTempMute = new unsigned char[length * 2]; + + int i; + for (i = 0; i < length; i++) + { + pMuteMe[i] = muteMe[i]; + pTempMute[i * 2 ] = muteMe[i]; + pTempMute[i * 2 + 1] = 0x00; + unsigned char muteChar = pMuteMe[i]; + int sizeMapForChar = m_mapChar[muteChar].size(); + int j; + for (j = 0; j < sizeMapForChar; j++) + { + currentCharmap[i][j] = m_mapChar[muteChar][j]; + } + } + + int* jAtPos = new int[length]; + int* sizeAtPos = new int[length]; + bool* fullAtPos = new bool[length]; + + int setSize; + for (setSize = 0; setSize <= length; setSize++) + { + int cntFull = 0; + + // clear all 'fullatpos' before new setSize + int i; + for (i=0; i < length; i++) + { + fullAtPos[i] = false; + } + + //printf("Trying full unicode map for %d/%d characters...\t\t\n", setSize, length); + printf("Trying full unicode map for %d/%d characters...%-20s\n", setSize, length, ""); + + bool notFirst = true; + + // start at end and set 'full' combination + countCombinations = 0; + countTotalCombinations = calculateTotalCombinations(length, setSize); + + int sPos = length - 1; + while (sPos >= 0 && notFirst) // finding combinations for current 'setSize' + { + if (aborting) + return false; + + if (cntFull < setSize) + { + if (fullAtPos[sPos] == false) + { + fullAtPos[sPos] = true; + cntFull++; + } + sPos--; + } + else + { + if (fullAtPos[sPos] == false && setSize > 0) + { + fullAtPos[sPos] = true; + cntFull++; + + // reset positions after sPos + int k; + for (k = sPos+1; k < length; k++) + { + if (fullAtPos[k] == true) + { + fullAtPos[k] = false; + cntFull--; + } + } + // start at end again + sPos = length - 1; + } + else + { + sPos--; + } + } + // we have a combination + if (cntFull == setSize) + { + countCombinations++; + + setupCombinationAtPositions(length, pMuteMe, pTempMute, jAtPos, fullAtPos, sizeAtPos); + + if (checkPermutations(length, pTempMute, jAtPos, sizeAtPos, pLMPassword, sNTLMPassword)) + { + return true; + } + } + + if (setSize == 0) + notFirst = false; + } + } + return false; +} + +// set up combination at positions +void LM2NTLMcorrector::setupCombinationAtPositions(int length, unsigned char* pMuteMe, unsigned char* pTempMute, int* jAtPos, bool* fullAtPos, int* sizeAtPos) +{ + progressCurrentCombination = 0; + totalCurrentCombination = 1; + + int i; + for (i=0; i < length; i++) + { + pTempMute[i*2] = currentCharmap[i][0]; + pTempMute[i*2+1] = currentCharmap[i][1]; // reset to first char in map + + jAtPos[i] = 0; // reset charcounter for this char (that is all chars) + + // based on combination, set full map or only upper/lowercase + if (fullAtPos[i] == true) + { + unsigned char muteChar = pMuteMe[i]; + long unsigned int sizeMapForChar = m_mapChar[muteChar].size()/2; // 2 bytes per char + sizeAtPos[i] = sizeMapForChar; + } + else + { + sizeAtPos[i] = 2; + } + + totalCurrentCombination *= sizeAtPos[i]; + } + //printf("Trying %I64u passwords for current combination\t\t\r", totalCurrentCombination); +} + +// go check all permutations for this combination +bool LM2NTLMcorrector::checkPermutations(int length, unsigned char* pTempMute, int* jAtPos, int* sizeAtPos, unsigned char* pLMPassword, string& sNTLMPassword) +{ + int pos = length - 1; + + while (pos >= 0) + { + counter++; + + pos = length - 1; + + int jAtCurPos = jAtPos[pos]; + int sizeMapForCharPos = sizeAtPos[pos]; + // move to start of string and find character with permutations left + while (jAtCurPos >= sizeMapForCharPos-1 && pos >= -1) + { + pos--; + if (pos >= 0 ) + { + jAtCurPos = jAtPos[pos]; + sizeMapForCharPos = sizeAtPos[pos]; + } + } + if (pos < 0) + continue; + + // next permutation for character + jAtCurPos++; + jAtPos[pos] = jAtCurPos; + + pTempMute[pos*2] = currentCharmap[pos][jAtCurPos*2]; + pTempMute[pos*2+1] = currentCharmap[pos][jAtCurPos*2+1]; + + // reset positions after pos + int k; + for (k = pos+1; k < length; k++) + { + jAtPos[k] = 0; + pTempMute[k*2] = currentCharmap[k][0]; // reset to first char in map + pTempMute[k*2+1] = currentCharmap[k][1]; + } + + if (checkNTLMPassword(pTempMute, length, sNTLMPassword) == true) + { + int i; + for (i = 0; i < length*2; i++) + pLMPassword[i] = pTempMute[i]; + return true; + } + + if (counter > 10000) // don't check clocks too often + { + clock_t currentClock = clock(); + float fTime = 1.0f * (currentClock - previousClock); + if (fTime > 1.0f * CLOCKS_PER_SEC) + { + float progressPercentageCurrentCombination = progressCurrentCombination * 100.0f / totalCurrentCombination; + float fTime = 1.0f * (currentClock - startClock) / CLOCKS_PER_SEC; + float currentSpeed = (counterOverall + progressCurrentCombination) / fTime / 1000000; + + //printf("%.2f%% of combination %d/%d (%.2f Mhashes/s)\t\t\t\t\r", progressPercentageCurrentCombination, countCombinations, countTotalCombinations, currentSpeed); + printf("%.2f%% of combination %d/%d (%.2f Mhashes/s)%-30s\r", progressPercentageCurrentCombination, countCombinations, countTotalCombinations, currentSpeed, ""); + + previousClock = clock(); + #ifdef _WIN32 + if (_kbhit()) + { + int ch = _getch(); + ch = toupper(ch); + if (ch == 'S') + { + aborting = true; + printf( "\nAborting unicode correction for this hash...\n"); + } + else + { + printf( "\nPress 'S' to skip unicode correction for this hash...\n"); + } + } + #else + int c = tty_getchar(); + if (c >= 0) { + tty_flush(); + if (c==115) { // = s + aborting = true; + printf( "\nAborting unicode correction for this hash...\n"); + } + else { + printf( "\nPress 's' to skip unicode correction for this hash...\n"); + } + } + #endif + if (aborting) + return false; + } + counter = 0; + } + + progressCurrentCombination++; + counterOverall++; + } + return false; +} + +// check password, maybe integrate this function in checkPermutations() for performance reasons. +bool LM2NTLMcorrector::checkNTLMPassword(unsigned char* pLMPassword, int nLMPasswordLen, string& sNTLMPassword) +{ + unsigned char md[MD4_DIGEST_LENGTH]; + + //MD4(pLMPassword, nLMPasswordLen * 2, md); + /* + MD4_CTX ctx; + MD4_Init(&ctx); + MD4_Update(&ctx, pLMPassword, nLMPasswordLen * 2); + MD4_Final(md, &ctx);*/ + + MD4_NEW( pLMPassword, nLMPasswordLen * 2, md ); + + if (memcmp(md, NTLMHash, MD4_DIGEST_LENGTH) == 0) + { + sNTLMPassword = ""; + int i; + for (i = 0; i < nLMPasswordLen; i++) { + sNTLMPassword += char(pLMPassword[i * 2]); + } + return true; + } + else + return false; +} + +void LM2NTLMcorrector::checkAbort() +{ +#ifdef _WIN32 + if (_kbhit()) + { + int ch = _getch(); + ch = toupper(ch); + if (ch == 'S') + { + aborting = true; + printf( "\nAborting unicode correction for this hash...\n"); + } + else + { + printf( "\nPress 'S' to skip unicode correction for this hash...\n"); + } + } +#endif +} + +void LM2NTLMcorrector::writeEndStats() +{ + clock_t endClock = clock(); + if (endClock - startClock > 0) + { + float fTime = 1.0f * (endClock - startClock) / CLOCKS_PER_SEC; + float speedOverall = counterOverall / fTime / 1000000; + printf("\nTried %s passwords in %.2f s (%.2f Mhashes/s)\n", uint64tostr(counterOverall).c_str(), fTime, speedOverall); + } + + printf("\n"); +} + +int LM2NTLMcorrector::calculateTotalCombinations(int length, int setSize) +{ + return factorial(length) / (factorial(setSize) * factorial(length-setSize)); +} + +int LM2NTLMcorrector::factorial (int num) +{ + int result = 1; + int i; + for (i = 1; i <= num; ++i) + result *= i; + return result; +} + +// convert some bytes into a string +string LM2NTLMcorrector::ByteToStr(const unsigned char* pData, int nLen) +{ + string sRet = ""; + int i; + for (i = 0; i < nLen/2; i++) + { + char szByte[3]; + sprintf(szByte, "%02x", pData[i*2+1]); // swap 2-byte characters again + sRet += szByte; + sprintf(szByte, "%02x", pData[i*2]); + sRet += szByte; + } + + return sRet; +} + +void LM2NTLMcorrector::addToMapW(unsigned char key, unsigned char value1, unsigned char value2) +{ + unsigned long int cnt = m_mapChar[key].size(); + m_mapChar[key][cnt] = value2; + m_mapChar[key][cnt+1] = value1; //reverse for endiannes +} + +// construct the mappings, would be nicer in a separate (importable) file +void LM2NTLMcorrector::fillMapW() +{ + addToMapW(0x01, 0x00, 0x01); + addToMapW(0x01, 0x26, 0x3A); + addToMapW(0x02, 0x00, 0x02); + addToMapW(0x02, 0x26, 0x3B); + addToMapW(0x03, 0x00, 0x03); + addToMapW(0x03, 0x26, 0x65); + addToMapW(0x04, 0x00, 0x04); + addToMapW(0x04, 0x26, 0x66); + addToMapW(0x05, 0x00, 0x05); + addToMapW(0x05, 0x26, 0x63); + addToMapW(0x06, 0x00, 0x06); + addToMapW(0x06, 0x26, 0x60); + addToMapW(0x07, 0x00, 0x07); + addToMapW(0x07, 0x00, 0xB7); + addToMapW(0x07, 0x20, 0x22); + addToMapW(0x07, 0x20, 0x24); + addToMapW(0x07, 0x20, 0x26); + addToMapW(0x07, 0x22, 0x19); + addToMapW(0x07, 0x22, 0xC5); + addToMapW(0x07, 0x30, 0xFB); + addToMapW(0x08, 0x00, 0x08); + addToMapW(0x08, 0x25, 0xD8); + addToMapW(0x09, 0x00, 0x09); + addToMapW(0x09, 0x20, 0xDD); + addToMapW(0x09, 0x25, 0xCB); + addToMapW(0x09, 0x30, 0x07); + addToMapW(0x0a, 0x00, 0x0A); + addToMapW(0x0a, 0x25, 0xD9); + addToMapW(0x0b, 0x00, 0x0B); + addToMapW(0x0b, 0x26, 0x42); + addToMapW(0x0c, 0x00, 0x0C); + addToMapW(0x0c, 0x26, 0x40); + addToMapW(0x0d, 0x00, 0x0D); + addToMapW(0x0d, 0x26, 0x6A); + addToMapW(0x0e, 0x00, 0x0E); + addToMapW(0x0e, 0x26, 0x6B); + addToMapW(0x0f, 0x00, 0x0F); + addToMapW(0x0f, 0x00, 0xA4); + addToMapW(0x0f, 0x26, 0x3C); + addToMapW(0x10, 0x00, 0x10); + addToMapW(0x10, 0x25, 0xBA); + addToMapW(0x11, 0x00, 0x11); + addToMapW(0x11, 0x25, 0xC4); + addToMapW(0x12, 0x00, 0x12); + addToMapW(0x12, 0x21, 0x95); + addToMapW(0x13, 0x00, 0x13); + addToMapW(0x13, 0x20, 0x3C); + addToMapW(0x14, 0x00, 0x14); + addToMapW(0x14, 0x00, 0xB6); + addToMapW(0x15, 0x00, 0x15); + addToMapW(0x15, 0x00, 0xA7); + addToMapW(0x16, 0x00, 0x16); + addToMapW(0x16, 0x02, 0xC9); + addToMapW(0x16, 0x25, 0xAC); + addToMapW(0x17, 0x00, 0x17); + addToMapW(0x17, 0x21, 0xA8); + addToMapW(0x18, 0x00, 0x18); + addToMapW(0x18, 0x21, 0x91); + addToMapW(0x19, 0x00, 0x19); + addToMapW(0x19, 0x21, 0x93); + addToMapW(0x1a, 0x00, 0x1A); + addToMapW(0x1a, 0x21, 0x92); + addToMapW(0x1b, 0x00, 0x1B); + addToMapW(0x1b, 0x21, 0x90); + addToMapW(0x1c, 0x00, 0x1C); + addToMapW(0x1c, 0x22, 0x1F); + addToMapW(0x1d, 0x00, 0x1D); + addToMapW(0x1d, 0x21, 0x94); + addToMapW(0x1e, 0x00, 0x1E); + addToMapW(0x1e, 0x25, 0xB2); + addToMapW(0x1f, 0x00, 0x1F); + addToMapW(0x1f, 0x25, 0xBC); + addToMapW(0x20, 0x00, 0x20); + addToMapW(0x20, 0x20, 0x00); + addToMapW(0x20, 0x20, 0x01); + addToMapW(0x20, 0x20, 0x02); + addToMapW(0x20, 0x20, 0x03); + addToMapW(0x20, 0x20, 0x04); + addToMapW(0x20, 0x20, 0x05); + addToMapW(0x20, 0x20, 0x06); + addToMapW(0x20, 0x30, 0x00); + addToMapW(0x21, 0x00, 0x21); + addToMapW(0x21, 0x00, 0xA1); + addToMapW(0x21, 0x01, 0xC3); + addToMapW(0x21, 0xFF, 0x01); + addToMapW(0x22, 0x00, 0x22); + addToMapW(0x22, 0x00, 0xA8); + addToMapW(0x22, 0x02, 0xBA); + addToMapW(0x22, 0x03, 0x08); + addToMapW(0x22, 0x03, 0x0E); + addToMapW(0x22, 0x20, 0x1C); + addToMapW(0x22, 0x20, 0x1D); + addToMapW(0x22, 0x20, 0x1E); + addToMapW(0x22, 0x20, 0x33); + addToMapW(0x22, 0x20, 0x35); + addToMapW(0x22, 0x27, 0x5D); + addToMapW(0x22, 0x27, 0x5E); + addToMapW(0x22, 0x30, 0x1D); + addToMapW(0x22, 0x30, 0x1E); + addToMapW(0x22, 0x30, 0x1F); + addToMapW(0x22, 0xFF, 0x02); + addToMapW(0x23, 0x00, 0x23); + addToMapW(0x23, 0xFF, 0x03); + addToMapW(0x24, 0x00, 0x24); + addToMapW(0x24, 0xFF, 0x04); + addToMapW(0x25, 0x00, 0x25); + addToMapW(0x25, 0x06, 0x6A); + addToMapW(0x25, 0x20, 0x30); + addToMapW(0x25, 0xFF, 0x05); + addToMapW(0x26, 0x00, 0x26); + addToMapW(0x26, 0xFF, 0x06); + addToMapW(0x27, 0x00, 0x27); + addToMapW(0x27, 0x00, 0xB4); + addToMapW(0x27, 0x02, 0xB9); + addToMapW(0x27, 0x02, 0xBB); + addToMapW(0x27, 0x02, 0xBC); + addToMapW(0x27, 0x02, 0xC8); + addToMapW(0x27, 0x02, 0xCA); + addToMapW(0x27, 0x02, 0xCB); + addToMapW(0x27, 0x03, 0x00); + addToMapW(0x27, 0x03, 0x01); + addToMapW(0x27, 0x20, 0x18); + addToMapW(0x27, 0x20, 0x19); + addToMapW(0x27, 0x20, 0x1A); + addToMapW(0x27, 0x20, 0x32); + addToMapW(0x27, 0x27, 0x5B); + addToMapW(0x27, 0x27, 0x5C); + addToMapW(0x27, 0xFF, 0x07); + addToMapW(0x28, 0x00, 0x28); + addToMapW(0x28, 0x23, 0x20); + addToMapW(0x28, 0xFF, 0x08); + addToMapW(0x29, 0x00, 0x29); + addToMapW(0x29, 0x23, 0x21); + addToMapW(0x29, 0xFF, 0x09); + addToMapW(0x2a, 0x00, 0x2A); + addToMapW(0x2a, 0x22, 0x17); + addToMapW(0x2a, 0xFF, 0x0A); + addToMapW(0x2b, 0x00, 0x2B); + addToMapW(0x2b, 0x00, 0xB1); + addToMapW(0x2b, 0x20, 0x20); + addToMapW(0x2b, 0x20, 0x21); + addToMapW(0x2b, 0xFF, 0x0B); + addToMapW(0x2c, 0x00, 0x2C); + addToMapW(0x2c, 0x00, 0xB8); + addToMapW(0x2c, 0x03, 0x27); + addToMapW(0x2c, 0x20, 0x1A); + addToMapW(0x2c, 0x20, 0x1E); + addToMapW(0x2c, 0xFF, 0x0C); + addToMapW(0x2d, 0x00, 0x2D); + addToMapW(0x2d, 0x00, 0xAC); + addToMapW(0x2d, 0x00, 0xAD); + addToMapW(0x2d, 0x20, 0x10); + addToMapW(0x2d, 0x20, 0x11); + addToMapW(0x2d, 0x20, 0x13); + addToMapW(0x2d, 0x20, 0x14); + addToMapW(0x2d, 0x22, 0x12); + addToMapW(0x2d, 0x22, 0x13); + addToMapW(0x2d, 0xFF, 0x0D); + addToMapW(0x2e, 0x00, 0x2E); + addToMapW(0x2e, 0x20, 0x26); + addToMapW(0x2e, 0xFF, 0x0E); + addToMapW(0x2f, 0x00, 0x2F); + addToMapW(0x2f, 0x20, 0x44); + addToMapW(0x2f, 0x22, 0x15); + addToMapW(0x2f, 0x22, 0x16); + addToMapW(0x2f, 0xFF, 0x0F); + addToMapW(0x30, 0x00, 0x30); + addToMapW(0x30, 0x20, 0x70); + addToMapW(0x30, 0x20, 0x80); + addToMapW(0x30, 0xFF, 0x10); + addToMapW(0x31, 0x00, 0x31); + addToMapW(0x31, 0x00, 0xB9); + addToMapW(0x31, 0x00, 0xBC); + addToMapW(0x31, 0x00, 0xBD); + addToMapW(0x31, 0x20, 0x81); + addToMapW(0x31, 0xFF, 0x11); + addToMapW(0x32, 0x00, 0x32); + addToMapW(0x32, 0x00, 0xB2); + addToMapW(0x32, 0x20, 0x82); + addToMapW(0x32, 0xFF, 0x12); + addToMapW(0x33, 0x00, 0x33); + addToMapW(0x33, 0x00, 0xB3); + addToMapW(0x33, 0x00, 0xBE); + addToMapW(0x33, 0x20, 0x83); + addToMapW(0x33, 0xFF, 0x13); + addToMapW(0x34, 0x00, 0x34); + addToMapW(0x34, 0x20, 0x74); + addToMapW(0x34, 0x20, 0x84); + addToMapW(0x34, 0xFF, 0x14); + addToMapW(0x35, 0x00, 0x35); + addToMapW(0x35, 0x20, 0x75); + addToMapW(0x35, 0x20, 0x85); + addToMapW(0x35, 0xFF, 0x15); + addToMapW(0x36, 0x00, 0x36); + addToMapW(0x36, 0x20, 0x76); + addToMapW(0x36, 0x20, 0x86); + addToMapW(0x36, 0xFF, 0x16); + addToMapW(0x37, 0x00, 0x37); + addToMapW(0x37, 0x20, 0x77); + addToMapW(0x37, 0x20, 0x87); + addToMapW(0x37, 0xFF, 0x17); + addToMapW(0x38, 0x00, 0x38); + addToMapW(0x38, 0x20, 0x78); + addToMapW(0x38, 0x20, 0x88); + addToMapW(0x38, 0x22, 0x1E); + addToMapW(0x38, 0xFF, 0x18); + addToMapW(0x39, 0x00, 0x39); + addToMapW(0x39, 0x20, 0x78); + addToMapW(0x39, 0x20, 0x89); + addToMapW(0x39, 0xFF, 0x19); + addToMapW(0x3a, 0x00, 0x3A); + addToMapW(0x3a, 0x05, 0x89); + addToMapW(0x3a, 0x20, 0x26); + addToMapW(0x3a, 0x22, 0x36); + addToMapW(0x3a, 0xFF, 0x1A); + addToMapW(0x3b, 0x00, 0x3B); + addToMapW(0x3b, 0x03, 0x7E); + addToMapW(0x3b, 0xFF, 0x1B); + addToMapW(0x3c, 0x00, 0x3C); + addToMapW(0x3c, 0x00, 0xAB); + addToMapW(0x3c, 0x20, 0x39); + addToMapW(0x3c, 0x23, 0x29); + addToMapW(0x3c, 0x30, 0x08); + addToMapW(0x3c, 0xFF, 0x1C); + addToMapW(0x3d, 0x00, 0x3D); + addToMapW(0x3d, 0x22, 0x61); + addToMapW(0x3d, 0x22, 0x64); + addToMapW(0x3d, 0x22, 0x65); + addToMapW(0x3d, 0xFF, 0x1D); + addToMapW(0x3e, 0x00, 0x3E); + addToMapW(0x3e, 0x00, 0xBB); + addToMapW(0x3e, 0x20, 0x3A); + addToMapW(0x3e, 0x23, 0x2A); + addToMapW(0x3e, 0x30, 0x09); + addToMapW(0x3e, 0xFF, 0x1E); + addToMapW(0x3f, 0x00, 0x3F); + addToMapW(0x40, 0x00, 0x40); + addToMapW(0x40, 0xFF, 0x20); + addToMapW(0x41, 0x00, 0x41); + addToMapW(0x41, 0x00, 0x61); + addToMapW(0x41, 0x00, 0xAA); + addToMapW(0x41, 0x00, 0xC0); + addToMapW(0x41, 0x00, 0xC1); + addToMapW(0x41, 0x00, 0xC2); + addToMapW(0x41, 0x00, 0xC3); + addToMapW(0x41, 0x00, 0xC4); + addToMapW(0x41, 0x00, 0xC5); + addToMapW(0x41, 0x00, 0xC6); + addToMapW(0x41, 0x00, 0xE0); + addToMapW(0x41, 0x00, 0xE1); + addToMapW(0x41, 0x00, 0xE2); + addToMapW(0x41, 0x00, 0xE3); + addToMapW(0x41, 0x00, 0xE4); + addToMapW(0x41, 0x00, 0xE5); + addToMapW(0x41, 0x00, 0xE6); + addToMapW(0x41, 0x01, 0x00); + addToMapW(0x41, 0x01, 0x01); + addToMapW(0x41, 0x01, 0x02); + addToMapW(0x41, 0x01, 0x03); + addToMapW(0x41, 0x01, 0x04); + addToMapW(0x41, 0x01, 0x05); + addToMapW(0x41, 0x01, 0xCD); + addToMapW(0x41, 0x01, 0xCE); + addToMapW(0x41, 0x01, 0xDE); + addToMapW(0x41, 0x01, 0xDF); + addToMapW(0x41, 0x03, 0xB1); + addToMapW(0x41, 0x21, 0x2B); + addToMapW(0x41, 0xFF, 0x21); + addToMapW(0x41, 0xFF, 0x41); + addToMapW(0x42, 0x00, 0x42); + addToMapW(0x42, 0x00, 0x62); + addToMapW(0x42, 0x01, 0x80); + addToMapW(0x42, 0x21, 0x2C); + addToMapW(0x42, 0xFF, 0x22); + addToMapW(0x42, 0xFF, 0x42); + addToMapW(0x43, 0x00, 0x43); + addToMapW(0x43, 0x00, 0x63); + addToMapW(0x43, 0x00, 0xA2); + addToMapW(0x43, 0x00, 0xA9); + addToMapW(0x43, 0x00, 0xC7); + addToMapW(0x43, 0x00, 0xE7); + addToMapW(0x43, 0x00, 0xE8); + addToMapW(0x43, 0x01, 0x06); + addToMapW(0x43, 0x01, 0x07); + addToMapW(0x43, 0x01, 0x08); + addToMapW(0x43, 0x01, 0x09); + addToMapW(0x43, 0x01, 0x0A); + addToMapW(0x43, 0x01, 0x0B); + addToMapW(0x43, 0x01, 0x0C); + addToMapW(0x43, 0x01, 0x0D); + addToMapW(0x43, 0x21, 0x02); + addToMapW(0x43, 0x21, 0x2D); + addToMapW(0x43, 0xFF, 0x23); + addToMapW(0x43, 0xFF, 0x43); + addToMapW(0x44, 0x00, 0x44); + addToMapW(0x44, 0x00, 0x64); + addToMapW(0x44, 0x00, 0xD0); + addToMapW(0x44, 0x00, 0xF0); + addToMapW(0x44, 0x01, 0x0E); + addToMapW(0x44, 0x01, 0x0F); + addToMapW(0x44, 0x01, 0x10); + addToMapW(0x44, 0x01, 0x11); + addToMapW(0x44, 0x01, 0x89); + addToMapW(0x44, 0x03, 0xB4); + addToMapW(0x44, 0x26, 0x6A); + addToMapW(0x44, 0x26, 0x6B); + addToMapW(0x44, 0xFF, 0x24); + addToMapW(0x44, 0xFF, 0x44); + addToMapW(0x45, 0x00, 0x45); + addToMapW(0x45, 0x00, 0x65); + addToMapW(0x45, 0x00, 0xC8); + addToMapW(0x45, 0x00, 0xC9); + addToMapW(0x45, 0x00, 0xCA); + addToMapW(0x45, 0x00, 0xCB); + addToMapW(0x45, 0x00, 0xE8); + addToMapW(0x45, 0x00, 0xE9); + addToMapW(0x45, 0x00, 0xEA); + addToMapW(0x45, 0x00, 0xEB); + addToMapW(0x45, 0x01, 0x12); + addToMapW(0x45, 0x01, 0x13); + addToMapW(0x45, 0x01, 0x14); + addToMapW(0x45, 0x01, 0x15); + addToMapW(0x45, 0x01, 0x16); + addToMapW(0x45, 0x01, 0x17); + addToMapW(0x45, 0x01, 0x18); + addToMapW(0x45, 0x01, 0x19); + addToMapW(0x45, 0x01, 0x1A); + addToMapW(0x45, 0x01, 0x1B); + addToMapW(0x45, 0x03, 0xB5); + addToMapW(0x45, 0x21, 0x07); + addToMapW(0x45, 0x21, 0x2E); + addToMapW(0x45, 0x21, 0x2F); + addToMapW(0x45, 0x21, 0x30); + addToMapW(0x45, 0xFF, 0x25); + addToMapW(0x45, 0xFF, 0x45); + addToMapW(0x46, 0x00, 0x46); + addToMapW(0x46, 0x00, 0x66); + addToMapW(0x46, 0x01, 0x91); + addToMapW(0x46, 0x01, 0x92); + addToMapW(0x46, 0x03, 0xA6); + addToMapW(0x46, 0x03, 0xC6); + addToMapW(0x46, 0x21, 0x31); + addToMapW(0x46, 0xFF, 0x26); + addToMapW(0x46, 0xFF, 0x46); + addToMapW(0x47, 0x00, 0x47); + addToMapW(0x47, 0x00, 0x67); + addToMapW(0x47, 0x01, 0x1C); + addToMapW(0x47, 0x01, 0x1D); + addToMapW(0x47, 0x01, 0x1E); + addToMapW(0x47, 0x01, 0x1F); + addToMapW(0x47, 0x01, 0x20); + addToMapW(0x47, 0x01, 0x21); + addToMapW(0x47, 0x01, 0x22); + addToMapW(0x47, 0x01, 0x23); + addToMapW(0x47, 0x01, 0xE4); + addToMapW(0x47, 0x01, 0xE5); + addToMapW(0x47, 0x01, 0xE6); + addToMapW(0x47, 0x01, 0xE7); + addToMapW(0x47, 0x02, 0x61); + addToMapW(0x47, 0x03, 0x93); + addToMapW(0x47, 0x21, 0x0A); + addToMapW(0x47, 0xFF, 0x27); + addToMapW(0x47, 0xFF, 0x47); + addToMapW(0x48, 0x00, 0x48); + addToMapW(0x48, 0x00, 0x68); + addToMapW(0x48, 0x01, 0x24); + addToMapW(0x48, 0x01, 0x25); + addToMapW(0x48, 0x01, 0x26); + addToMapW(0x48, 0x01, 0x27); + addToMapW(0x48, 0x04, 0xBB); + addToMapW(0x48, 0x21, 0x0B); + addToMapW(0x48, 0x21, 0x0C); + addToMapW(0x48, 0x21, 0x0D); + addToMapW(0x48, 0x21, 0x0E); + addToMapW(0x48, 0xFF, 0x28); + addToMapW(0x48, 0xFF, 0x48); + addToMapW(0x49, 0x00, 0x49); + addToMapW(0x49, 0x00, 0x69); + addToMapW(0x49, 0x00, 0xCC); + addToMapW(0x49, 0x00, 0xCD); + addToMapW(0x49, 0x00, 0xCE); + addToMapW(0x49, 0x00, 0xCF); + addToMapW(0x49, 0x00, 0xEC); + addToMapW(0x49, 0x00, 0xED); + addToMapW(0x49, 0x00, 0xEE); + addToMapW(0x49, 0x00, 0xEF); + addToMapW(0x49, 0x01, 0x28); + addToMapW(0x49, 0x01, 0x29); + addToMapW(0x49, 0x01, 0x2A); + addToMapW(0x49, 0x01, 0x2B); + addToMapW(0x49, 0x01, 0x2C); + addToMapW(0x49, 0x01, 0x2D); + addToMapW(0x49, 0x01, 0x2E); + addToMapW(0x49, 0x01, 0x2F); + addToMapW(0x49, 0x01, 0x30); + addToMapW(0x49, 0x01, 0x31); + addToMapW(0x49, 0x01, 0x97); + addToMapW(0x49, 0x01, 0xCF); + addToMapW(0x49, 0x01, 0xD0); + addToMapW(0x49, 0x21, 0x10); + addToMapW(0x49, 0x21, 0x11); + addToMapW(0x49, 0xFF, 0x29); + addToMapW(0x49, 0xFF, 0x49); + addToMapW(0x4a, 0x00, 0x4A); + addToMapW(0x4a, 0x00, 0x6A); + addToMapW(0x4a, 0x01, 0x34); + addToMapW(0x4a, 0x01, 0x35); + addToMapW(0x4a, 0x01, 0xF0); + addToMapW(0x4a, 0xFF, 0x2A); + addToMapW(0x4a, 0xFF, 0x4A); + addToMapW(0x4b, 0x00, 0x4B); + addToMapW(0x4b, 0x00, 0x6B); + addToMapW(0x4b, 0x01, 0x36); + addToMapW(0x4b, 0x01, 0x37); + addToMapW(0x4b, 0x01, 0xE8); + addToMapW(0x4b, 0x01, 0xE9); + addToMapW(0x4b, 0x21, 0x2A); + addToMapW(0x4b, 0xFF, 0x2B); + addToMapW(0x4b, 0xFF, 0x4B); + addToMapW(0x4c, 0x00, 0x4C); + addToMapW(0x4c, 0x00, 0x6C); + addToMapW(0x4c, 0x00, 0xA3); + addToMapW(0x4c, 0x01, 0x39); + addToMapW(0x4c, 0x01, 0x3A); + addToMapW(0x4c, 0x01, 0x3B); + addToMapW(0x4c, 0x01, 0x3C); + addToMapW(0x4c, 0x01, 0x3D); + addToMapW(0x4c, 0x01, 0x3E); + addToMapW(0x4c, 0x01, 0x41); + addToMapW(0x4c, 0x01, 0x42); + addToMapW(0x4c, 0x01, 0x9A); + addToMapW(0x4c, 0x20, 0xA4); + addToMapW(0x4c, 0x21, 0x12); + addToMapW(0x4c, 0x21, 0x13); + addToMapW(0x4c, 0xFF, 0x2C); + addToMapW(0x4c, 0xFF, 0x4C); + addToMapW(0x4d, 0x00, 0x4D); + addToMapW(0x4d, 0x00, 0x6D); + addToMapW(0x4d, 0x21, 0x33); + addToMapW(0x4d, 0xFF, 0x2D); + addToMapW(0x4d, 0xFF, 0x4D); + addToMapW(0x4e, 0x00, 0x4E); + addToMapW(0x4e, 0x00, 0x6E); + addToMapW(0x4e, 0x00, 0xD1); + addToMapW(0x4e, 0x00, 0xF1); + addToMapW(0x4e, 0x01, 0x43); + addToMapW(0x4e, 0x01, 0x44); + addToMapW(0x4e, 0x01, 0x45); + addToMapW(0x4e, 0x01, 0x46); + addToMapW(0x4e, 0x01, 0x47); + addToMapW(0x4e, 0x01, 0x48); + addToMapW(0x4e, 0x20, 0x7F); + addToMapW(0x4e, 0x21, 0x15); + addToMapW(0x4e, 0x22, 0x29); + addToMapW(0x4e, 0xFF, 0x2E); + addToMapW(0x4e, 0xFF, 0x4E); + addToMapW(0x4f, 0x00, 0x4F); + addToMapW(0x4f, 0x00, 0x6F); + addToMapW(0x4f, 0x00, 0xB0); + addToMapW(0x4f, 0x00, 0xBA); + addToMapW(0x4f, 0x00, 0xD2); + addToMapW(0x4f, 0x00, 0xD3); + addToMapW(0x4f, 0x00, 0xD4); + addToMapW(0x4f, 0x00, 0xD5); + addToMapW(0x4f, 0x00, 0xD6); + addToMapW(0x4f, 0x00, 0xD8); + addToMapW(0x4f, 0x00, 0xF2); + addToMapW(0x4f, 0x00, 0xF3); + addToMapW(0x4f, 0x00, 0xF4); + addToMapW(0x4f, 0x00, 0xF5); + addToMapW(0x4f, 0x00, 0xF6); + addToMapW(0x4f, 0x00, 0xF8); + addToMapW(0x4f, 0x01, 0x4C); + addToMapW(0x4f, 0x01, 0x4D); + addToMapW(0x4f, 0x01, 0x4E); + addToMapW(0x4f, 0x01, 0x4F); + addToMapW(0x4f, 0x01, 0x50); + addToMapW(0x4f, 0x01, 0x51); + addToMapW(0x4f, 0x01, 0x52); + addToMapW(0x4f, 0x01, 0x53); + addToMapW(0x4f, 0x01, 0x9F); + addToMapW(0x4f, 0x01, 0xA0); + addToMapW(0x4f, 0x01, 0xA1); + addToMapW(0x4f, 0x01, 0xD1); + addToMapW(0x4f, 0x01, 0xD2); + addToMapW(0x4f, 0x01, 0xEA); + addToMapW(0x4f, 0x01, 0xEB); + addToMapW(0x4f, 0x01, 0xEC); + addToMapW(0x4f, 0x01, 0xED); + addToMapW(0x4f, 0x03, 0xA9); + addToMapW(0x4f, 0x20, 0xDD); + addToMapW(0x4f, 0x21, 0x26); + addToMapW(0x4f, 0x21, 0x34); + addToMapW(0x4f, 0x22, 0x05); + addToMapW(0x4f, 0x30, 0x07); + addToMapW(0x4f, 0xFF, 0x2F); + addToMapW(0x4f, 0xFF, 0x4F); + addToMapW(0x50, 0x00, 0x50); + addToMapW(0x50, 0x00, 0x70); + addToMapW(0x50, 0x03, 0xC0); + addToMapW(0x50, 0x20, 0xA7); + addToMapW(0x50, 0x21, 0x18); + addToMapW(0x50, 0x21, 0x19); + addToMapW(0x50, 0xFF, 0x30); + addToMapW(0x50, 0xFF, 0x50); + addToMapW(0x51, 0x00, 0x51); + addToMapW(0x51, 0x00, 0x71); + addToMapW(0x51, 0x21, 0x1A); + addToMapW(0x51, 0xFF, 0x31); + addToMapW(0x51, 0xFF, 0x51); + addToMapW(0x52, 0x00, 0x52); + addToMapW(0x52, 0x00, 0x72); + addToMapW(0x52, 0x00, 0xAE); + addToMapW(0x52, 0x01, 0x54); + addToMapW(0x52, 0x01, 0x55); + addToMapW(0x52, 0x01, 0x56); + addToMapW(0x52, 0x01, 0x57); + addToMapW(0x52, 0x01, 0x58); + addToMapW(0x52, 0x01, 0x59); + addToMapW(0x52, 0x21, 0x1B); + addToMapW(0x52, 0x21, 0x1C); + addToMapW(0x52, 0x21, 0x1D); + addToMapW(0x52, 0xFF, 0x32); + addToMapW(0x52, 0xFF, 0x52); + addToMapW(0x53, 0x00, 0x53); + addToMapW(0x53, 0x00, 0x73); + addToMapW(0x53, 0x00, 0xDF); + addToMapW(0x53, 0x01, 0x5A); + addToMapW(0x53, 0x01, 0x5B); + addToMapW(0x53, 0x01, 0x5C); + addToMapW(0x53, 0x01, 0x5D); + addToMapW(0x53, 0x01, 0x5E); + addToMapW(0x53, 0x01, 0x5F); + addToMapW(0x53, 0x01, 0x60); + addToMapW(0x53, 0x01, 0x61); + addToMapW(0x53, 0x01, 0xA9); + addToMapW(0x53, 0x03, 0xA3); + addToMapW(0x53, 0x03, 0xC3); + addToMapW(0x53, 0x22, 0x11); + addToMapW(0x53, 0xFF, 0x33); + addToMapW(0x53, 0xFF, 0x53); + addToMapW(0x54, 0x00, 0x54); + addToMapW(0x54, 0x00, 0x74); + addToMapW(0x54, 0x00, 0xDE); + addToMapW(0x54, 0x00, 0xFE); + addToMapW(0x54, 0x01, 0x62); + addToMapW(0x54, 0x01, 0x63); + addToMapW(0x54, 0x01, 0x64); + addToMapW(0x54, 0x01, 0x65); + addToMapW(0x54, 0x01, 0x66); + addToMapW(0x54, 0x01, 0x67); + addToMapW(0x54, 0x01, 0xAB); + addToMapW(0x54, 0x01, 0xAE); + addToMapW(0x54, 0x03, 0xC4); + addToMapW(0x54, 0x21, 0x22); + addToMapW(0x54, 0xFF, 0x34); + addToMapW(0x54, 0xFF, 0x54); + addToMapW(0x55, 0x00, 0x55); + addToMapW(0x55, 0x00, 0x75); + addToMapW(0x55, 0x00, 0xB5); + addToMapW(0x55, 0x00, 0xD9); + addToMapW(0x55, 0x00, 0xDA); + addToMapW(0x55, 0x00, 0xDB); + addToMapW(0x55, 0x00, 0xDC); + addToMapW(0x55, 0x00, 0xF9); + addToMapW(0x55, 0x00, 0xFA); + addToMapW(0x55, 0x00, 0xFB); + addToMapW(0x55, 0x00, 0xFC); + addToMapW(0x55, 0x01, 0x68); + addToMapW(0x55, 0x01, 0x69); + addToMapW(0x55, 0x01, 0x6A); + addToMapW(0x55, 0x01, 0x6B); + addToMapW(0x55, 0x01, 0x6C); + addToMapW(0x55, 0x01, 0x6D); + addToMapW(0x55, 0x01, 0x6E); + addToMapW(0x55, 0x01, 0x6F); + addToMapW(0x55, 0x01, 0x70); + addToMapW(0x55, 0x01, 0x71); + addToMapW(0x55, 0x01, 0x72); + addToMapW(0x55, 0x01, 0x73); + addToMapW(0x55, 0x01, 0xAF); + addToMapW(0x55, 0x01, 0xB0); + addToMapW(0x55, 0x01, 0xD3); + addToMapW(0x55, 0x01, 0xD4); + addToMapW(0x55, 0x01, 0xD5); + addToMapW(0x55, 0x01, 0xD6); + addToMapW(0x55, 0x01, 0xD7); + addToMapW(0x55, 0x01, 0xD8); + addToMapW(0x55, 0x01, 0xD9); + addToMapW(0x55, 0x01, 0xDA); + addToMapW(0x55, 0x01, 0xDB); + addToMapW(0x55, 0x01, 0xDC); + addToMapW(0x55, 0x03, 0xBC); + addToMapW(0x55, 0xFF, 0x35); + addToMapW(0x55, 0xFF, 0x55); + addToMapW(0x56, 0x00, 0x56); + addToMapW(0x56, 0x00, 0x76); + addToMapW(0x56, 0x22, 0x1A); + addToMapW(0x56, 0x27, 0x13); + addToMapW(0x56, 0xFF, 0x36); + addToMapW(0x56, 0xFF, 0x56); + addToMapW(0x57, 0x00, 0x57); + addToMapW(0x57, 0x00, 0x77); + addToMapW(0x57, 0x01, 0x74); + addToMapW(0x57, 0x01, 0x75); + addToMapW(0x57, 0xFF, 0x37); + addToMapW(0x57, 0xFF, 0x57); + addToMapW(0x58, 0x00, 0x58); + addToMapW(0x58, 0x00, 0x78); + addToMapW(0x58, 0x00, 0xD7); + addToMapW(0x58, 0xFF, 0x38); + addToMapW(0x58, 0xFF, 0x58); + addToMapW(0x59, 0x00, 0x59); + addToMapW(0x59, 0x00, 0x79); + addToMapW(0x59, 0x00, 0xA5); + addToMapW(0x59, 0x00, 0xDD); + addToMapW(0x59, 0x00, 0xFD); + addToMapW(0x59, 0x00, 0xFF); + addToMapW(0x59, 0x01, 0x76); + addToMapW(0x59, 0x01, 0x77); + addToMapW(0x59, 0x01, 0x78); + addToMapW(0x59, 0xFF, 0x39); + addToMapW(0x59, 0xFF, 0x59); + addToMapW(0x5a, 0x00, 0x5A); + addToMapW(0x5a, 0x00, 0x7A); + addToMapW(0x5a, 0x01, 0x79); + addToMapW(0x5a, 0x01, 0x7A); + addToMapW(0x5a, 0x01, 0x7B); + addToMapW(0x5a, 0x01, 0x7C); + addToMapW(0x5a, 0x01, 0x7D); + addToMapW(0x5a, 0x01, 0x7E); + addToMapW(0x5a, 0x01, 0xB6); + addToMapW(0x5a, 0x21, 0x24); + addToMapW(0x5a, 0x21, 0x28); + addToMapW(0x5a, 0xFF, 0x3A); + addToMapW(0x5a, 0xFF, 0x5A); + addToMapW(0x5b, 0x00, 0x5B); + addToMapW(0x5b, 0x30, 0x1A); + addToMapW(0x5b, 0xFF, 0x3B); + addToMapW(0x5c, 0x00, 0x5C); + addToMapW(0x5c, 0x00, 0xA5); + addToMapW(0x5c, 0x22, 0x16); + addToMapW(0x5c, 0xFF, 0x3C); + addToMapW(0x5d, 0x00, 0x5D); + addToMapW(0x5d, 0x30, 0x1B); + addToMapW(0x5d, 0xFF, 0x3D); + addToMapW(0x5e, 0x00, 0x5E); + addToMapW(0x5e, 0x02, 0xC4); + addToMapW(0x5e, 0x02, 0xC6); + addToMapW(0x5e, 0x02, 0xC7); + addToMapW(0x5e, 0x02, 0xD8); + addToMapW(0x5e, 0x03, 0x02); + addToMapW(0x5e, 0x03, 0x06); + addToMapW(0x5e, 0x03, 0x0C); + addToMapW(0x5e, 0x23, 0x03); + addToMapW(0x5e, 0xFF, 0x3E); + addToMapW(0x5f, 0x00, 0x5F); + addToMapW(0x5f, 0x00, 0xAF); + addToMapW(0x5f, 0x00, 0xBE); + addToMapW(0x5f, 0x00, 0xDE); + addToMapW(0x5f, 0x00, 0xFE); + addToMapW(0x5f, 0x02, 0xCD); + addToMapW(0x5f, 0x03, 0x31); + addToMapW(0x5f, 0x03, 0x32); + addToMapW(0x5f, 0x20, 0x17); + addToMapW(0x5f, 0x30, 0xFC); + addToMapW(0x5f, 0xFF, 0x3F); + addToMapW(0x60, 0x00, 0x60); + addToMapW(0x60, 0x02, 0xCB); + addToMapW(0x60, 0x03, 0x00); + addToMapW(0x60, 0x20, 0x18); + addToMapW(0x60, 0x20, 0x35); + addToMapW(0x60, 0xFF, 0x40); + addToMapW(0x7b, 0x00, 0x7B); + addToMapW(0x7b, 0xFF, 0x5B); + addToMapW(0x7c, 0x00, 0x7C); + addToMapW(0x7c, 0x00, 0xA6); + addToMapW(0x7c, 0x01, 0xC0); + addToMapW(0x7c, 0x22, 0x23); + addToMapW(0x7c, 0x27, 0x58); + addToMapW(0x7c, 0xFF, 0x5C); + addToMapW(0x7d, 0x00, 0x7D); + addToMapW(0x7d, 0x30, 0x1B); + addToMapW(0x7d, 0xFF, 0x5D); + addToMapW(0x7e, 0x00, 0x7E); + addToMapW(0x7e, 0x02, 0xDC); + addToMapW(0x7e, 0x03, 0x03); + addToMapW(0x7e, 0x22, 0x3C); + addToMapW(0x7e, 0x22, 0x48); + addToMapW(0x7e, 0xFF, 0x5E); + addToMapW(0x7f, 0x00, 0x7F); + addToMapW(0x7f, 0x23, 0x02); + addToMapW(0x7f, 0x26, 0x60); + addToMapW(0x7f, 0x26, 0x63); + addToMapW(0x7f, 0x26, 0x65); + addToMapW(0x7f, 0x26, 0x66); + addToMapW(0x80, 0x00, 0x80); + addToMapW(0x80, 0x00, 0xC7); + addToMapW(0x80, 0x00, 0xE7); + addToMapW(0x80, 0x01, 0x06); + addToMapW(0x80, 0x01, 0x07); + addToMapW(0x80, 0x03, 0x91); + addToMapW(0x80, 0x03, 0xB1); + addToMapW(0x80, 0x04, 0x10); + addToMapW(0x80, 0x04, 0x30); + addToMapW(0x80, 0x05, 0xD0); + addToMapW(0x80, 0x20, 0xAC); + addToMapW(0x81, 0x00, 0x81); + addToMapW(0x81, 0x03, 0x92); + addToMapW(0x81, 0x03, 0xB2); + addToMapW(0x81, 0x04, 0x02); + addToMapW(0x81, 0x04, 0x11); + addToMapW(0x81, 0x04, 0x31); + addToMapW(0x81, 0x04, 0x52); + addToMapW(0x81, 0x05, 0xD1); + addToMapW(0x82, 0x00, 0x82); + addToMapW(0x82, 0x03, 0x93); + addToMapW(0x82, 0x03, 0xB3); + addToMapW(0x82, 0x04, 0x12); + addToMapW(0x82, 0x04, 0x32); + addToMapW(0x82, 0x05, 0xD2); + addToMapW(0x82, 0x20, 0x1A); + addToMapW(0x83, 0x00, 0x83); + addToMapW(0x83, 0x03, 0x94); + addToMapW(0x83, 0x03, 0xB4); + addToMapW(0x83, 0x04, 0x03); + addToMapW(0x83, 0x04, 0x13); + addToMapW(0x83, 0x04, 0x33); + addToMapW(0x83, 0x04, 0x53); + addToMapW(0x83, 0x05, 0xD3); + addToMapW(0x84, 0x00, 0x84); + addToMapW(0x84, 0x03, 0x95); + addToMapW(0x84, 0x03, 0xB5); + addToMapW(0x84, 0x04, 0x14); + addToMapW(0x84, 0x04, 0x34); + addToMapW(0x84, 0x05, 0xD4); + addToMapW(0x84, 0x20, 0x1E); + addToMapW(0x85, 0x03, 0x96); + addToMapW(0x85, 0x03, 0xB6); + addToMapW(0x85, 0x04, 0x01); + addToMapW(0x85, 0x04, 0x15); + addToMapW(0x85, 0x04, 0x35); + addToMapW(0x85, 0x04, 0x51); + addToMapW(0x85, 0x05, 0xD5); + addToMapW(0x85, 0x20, 0x26); + addToMapW(0x86, 0x00, 0x86); + addToMapW(0x86, 0x03, 0x97); + addToMapW(0x86, 0x03, 0xB7); + addToMapW(0x86, 0x04, 0x16); + addToMapW(0x86, 0x04, 0x36); + addToMapW(0x86, 0x05, 0xD6); + addToMapW(0x86, 0x20, 0x20); + addToMapW(0x87, 0x00, 0x87); + addToMapW(0x87, 0x03, 0x98); + addToMapW(0x87, 0x03, 0xB8); + addToMapW(0x87, 0x04, 0x04); + addToMapW(0x87, 0x04, 0x17); + addToMapW(0x87, 0x04, 0x37); + addToMapW(0x87, 0x04, 0x54); + addToMapW(0x87, 0x05, 0xD7); + addToMapW(0x87, 0x20, 0x21); + addToMapW(0x88, 0x00, 0x88); + addToMapW(0x88, 0x02, 0xC6); + addToMapW(0x88, 0x03, 0x99); + addToMapW(0x88, 0x03, 0xB9); + addToMapW(0x88, 0x04, 0x18); + addToMapW(0x88, 0x04, 0x38); + addToMapW(0x88, 0x05, 0xD8); + addToMapW(0x89, 0x00, 0x89); + addToMapW(0x89, 0x03, 0x9A); + addToMapW(0x89, 0x03, 0xBA); + addToMapW(0x89, 0x04, 0x05); + addToMapW(0x89, 0x04, 0x19); + addToMapW(0x89, 0x04, 0x39); + addToMapW(0x89, 0x04, 0x55); + addToMapW(0x89, 0x05, 0xD9); + addToMapW(0x89, 0x20, 0x30); + addToMapW(0x8a, 0x00, 0x8A); + addToMapW(0x8a, 0x01, 0x50); + addToMapW(0x8a, 0x01, 0x51); + addToMapW(0x8a, 0x01, 0x56); + addToMapW(0x8a, 0x01, 0x57); + addToMapW(0x8a, 0x03, 0x9B); + addToMapW(0x8a, 0x03, 0xBB); + addToMapW(0x8a, 0x04, 0x1A); + addToMapW(0x8a, 0x04, 0x3A); + addToMapW(0x8a, 0x05, 0xDA); + addToMapW(0x8b, 0x00, 0x8B); + addToMapW(0x8b, 0x03, 0x9C); + addToMapW(0x8b, 0x03, 0xBC); + addToMapW(0x8b, 0x04, 0x06); + addToMapW(0x8b, 0x04, 0x1B); + addToMapW(0x8b, 0x04, 0x3B); + addToMapW(0x8b, 0x04, 0x56); + addToMapW(0x8b, 0x05, 0xDB); + addToMapW(0x8b, 0x20, 0x39); + addToMapW(0x8c, 0x00, 0x8C); + addToMapW(0x8c, 0x01, 0x52); + addToMapW(0x8c, 0x01, 0x53); + addToMapW(0x8c, 0x03, 0x9D); + addToMapW(0x8c, 0x03, 0xBD); + addToMapW(0x8c, 0x04, 0x1C); + addToMapW(0x8c, 0x04, 0x3C); + addToMapW(0x8c, 0x05, 0xDC); + addToMapW(0x8d, 0x00, 0x8D); + addToMapW(0x8d, 0x01, 0x31); + addToMapW(0x8d, 0x01, 0x79); + addToMapW(0x8d, 0x01, 0x7A); + addToMapW(0x8d, 0x03, 0x9E); + addToMapW(0x8d, 0x03, 0xBE); + addToMapW(0x8d, 0x04, 0x07); + addToMapW(0x8d, 0x04, 0x1D); + addToMapW(0x8d, 0x04, 0x3D); + addToMapW(0x8d, 0x04, 0x57); + addToMapW(0x8d, 0x05, 0xDD); + addToMapW(0x8e, 0x00, 0x8E); + addToMapW(0x8e, 0x00, 0xC4); + addToMapW(0x8e, 0x00, 0xE4); + addToMapW(0x8e, 0x03, 0x9F); + addToMapW(0x8e, 0x03, 0xBF); + addToMapW(0x8e, 0x04, 0x1E); + addToMapW(0x8e, 0x04, 0x3E); + addToMapW(0x8e, 0x05, 0xDE); + addToMapW(0x8f, 0x00, 0x8F); + addToMapW(0x8f, 0x00, 0xC5); + addToMapW(0x8f, 0x00, 0xE5); + addToMapW(0x8f, 0x01, 0x06); + addToMapW(0x8f, 0x01, 0x07); + addToMapW(0x8f, 0x03, 0xA0); + addToMapW(0x8f, 0x03, 0xC0); + addToMapW(0x8f, 0x04, 0x08); + addToMapW(0x8f, 0x04, 0x1F); + addToMapW(0x8f, 0x04, 0x3F); + addToMapW(0x8f, 0x04, 0x58); + addToMapW(0x8f, 0x05, 0xDF); + addToMapW(0x8f, 0x21, 0x2B); + addToMapW(0x90, 0x00, 0x90); + addToMapW(0x90, 0x00, 0xC9); + addToMapW(0x90, 0x00, 0xE9); + addToMapW(0x90, 0x03, 0xA1); + addToMapW(0x90, 0x03, 0xC1); + addToMapW(0x90, 0x04, 0x20); + addToMapW(0x90, 0x04, 0x40); + addToMapW(0x90, 0x05, 0xE0); + addToMapW(0x91, 0x01, 0x39); + addToMapW(0x91, 0x01, 0x3A); + addToMapW(0x91, 0x03, 0xA3); + addToMapW(0x91, 0x03, 0xC2); + addToMapW(0x91, 0x03, 0xC3); + addToMapW(0x91, 0x04, 0x09); + addToMapW(0x91, 0x04, 0x21); + addToMapW(0x91, 0x04, 0x41); + addToMapW(0x91, 0x04, 0x59); + addToMapW(0x91, 0x05, 0xE1); + addToMapW(0x91, 0x06, 0x51); + addToMapW(0x91, 0x20, 0x18); + addToMapW(0x91, 0xFE, 0x7C); + addToMapW(0x91, 0xFE, 0x7D); + addToMapW(0x92, 0x00, 0xC6); + addToMapW(0x92, 0x00, 0xE6); + addToMapW(0x92, 0x03, 0xA4); + addToMapW(0x92, 0x03, 0xC4); + addToMapW(0x92, 0x04, 0x22); + addToMapW(0x92, 0x04, 0x42); + addToMapW(0x92, 0x05, 0xE2); + addToMapW(0x92, 0x06, 0x52); + addToMapW(0x92, 0x20, 0x19); + addToMapW(0x92, 0xFE, 0x7E); + addToMapW(0x92, 0xFE, 0x7F); + addToMapW(0x93, 0x03, 0xA5); + addToMapW(0x93, 0x03, 0xC5); + addToMapW(0x93, 0x04, 0x0A); + addToMapW(0x93, 0x04, 0x23); + addToMapW(0x93, 0x04, 0x43); + addToMapW(0x93, 0x04, 0x5A); + addToMapW(0x93, 0x05, 0xE3); + addToMapW(0x93, 0x20, 0x1C); + addToMapW(0x94, 0x00, 0xA4); + addToMapW(0x94, 0x03, 0xA6); + addToMapW(0x94, 0x03, 0xC6); + addToMapW(0x94, 0x04, 0x24); + addToMapW(0x94, 0x04, 0x44); + addToMapW(0x94, 0x05, 0xE4); + addToMapW(0x94, 0x20, 0x1D); + addToMapW(0x95, 0x01, 0x22); + addToMapW(0x95, 0x01, 0x23); + addToMapW(0x95, 0x01, 0x3D); + addToMapW(0x95, 0x01, 0x3E); + addToMapW(0x95, 0x03, 0xA7); + addToMapW(0x95, 0x03, 0xC7); + addToMapW(0x95, 0x04, 0x0B); + addToMapW(0x95, 0x04, 0x25); + addToMapW(0x95, 0x04, 0x45); + addToMapW(0x95, 0x04, 0x5B); + addToMapW(0x95, 0x05, 0xE5); + addToMapW(0x95, 0x06, 0x40); + addToMapW(0x95, 0x20, 0x22); + addToMapW(0x96, 0x00, 0xA2); + addToMapW(0x96, 0x03, 0xA8); + addToMapW(0x96, 0x03, 0xC8); + addToMapW(0x96, 0x04, 0x26); + addToMapW(0x96, 0x04, 0x46); + addToMapW(0x96, 0x05, 0xE6); + addToMapW(0x96, 0x20, 0x13); + addToMapW(0x97, 0x00, 0xB5); + addToMapW(0x97, 0x01, 0x5A); + addToMapW(0x97, 0x01, 0x5B); + addToMapW(0x97, 0x03, 0xA9); + addToMapW(0x97, 0x03, 0xC9); + addToMapW(0x97, 0x04, 0x0C); + addToMapW(0x97, 0x04, 0x27); + addToMapW(0x97, 0x04, 0x47); + addToMapW(0x97, 0x04, 0x5C); + addToMapW(0x97, 0x05, 0xE7); + addToMapW(0x97, 0x20, 0x14); + addToMapW(0x98, 0x00, 0x98); + addToMapW(0x98, 0x01, 0x30); + addToMapW(0x98, 0x02, 0xDC); + addToMapW(0x98, 0x04, 0x28); + addToMapW(0x98, 0x04, 0x48); + addToMapW(0x98, 0x05, 0xE8); + addToMapW(0x98, 0x06, 0x21); + addToMapW(0x98, 0xFE, 0x80); + addToMapW(0x99, 0x00, 0x99); + addToMapW(0x99, 0x00, 0xD6); + addToMapW(0x99, 0x00, 0xF6); + addToMapW(0x99, 0x04, 0x0E); + addToMapW(0x99, 0x04, 0x29); + addToMapW(0x99, 0x04, 0x49); + addToMapW(0x99, 0x04, 0x5E); + addToMapW(0x99, 0x05, 0xE9); + addToMapW(0x99, 0x06, 0x22); + addToMapW(0x99, 0x21, 0x22); + addToMapW(0x99, 0xFE, 0x81); + addToMapW(0x99, 0xFE, 0x82); + addToMapW(0x9a, 0x00, 0x9A); + addToMapW(0x9a, 0x00, 0xDC); + addToMapW(0x9a, 0x00, 0xFC); + addToMapW(0x9a, 0x04, 0x2A); + addToMapW(0x9a, 0x04, 0x4A); + addToMapW(0x9a, 0x05, 0xEA); + addToMapW(0x9a, 0x06, 0x23); + addToMapW(0x9a, 0xFE, 0x83); + addToMapW(0x9a, 0xFE, 0x84); + addToMapW(0x9b, 0x00, 0x9B); + addToMapW(0x9b, 0x00, 0xA2); + addToMapW(0x9b, 0x01, 0x64); + addToMapW(0x9b, 0x01, 0x65); + addToMapW(0x9b, 0x04, 0x0F); + addToMapW(0x9b, 0x04, 0x2B); + addToMapW(0x9b, 0x04, 0x4B); + addToMapW(0x9b, 0x04, 0x5F); + addToMapW(0x9b, 0x06, 0x24); + addToMapW(0x9b, 0x20, 0x3A); + addToMapW(0x9b, 0xFE, 0x85); + addToMapW(0x9b, 0xFE, 0x86); + addToMapW(0x9c, 0x00, 0x9C); + addToMapW(0x9c, 0x00, 0xA3); + addToMapW(0x9c, 0x04, 0x2C); + addToMapW(0x9c, 0x04, 0x4C); + addToMapW(0x9c, 0x20, 0xA4); + addToMapW(0x9d, 0x00, 0x9D); + addToMapW(0x9d, 0x00, 0xA5); + addToMapW(0x9d, 0x00, 0xD8); + addToMapW(0x9d, 0x00, 0xF8); + addToMapW(0x9d, 0x01, 0x41); + addToMapW(0x9d, 0x01, 0x42); + addToMapW(0x9d, 0x02, 0x78); + addToMapW(0x9d, 0x03, 0x98); + addToMapW(0x9d, 0x04, 0x2D); + addToMapW(0x9d, 0x04, 0x2E); + addToMapW(0x9d, 0x04, 0x4D); + addToMapW(0x9d, 0x04, 0x4E); + addToMapW(0x9d, 0x06, 0x25); + addToMapW(0x9d, 0x22, 0x05); + addToMapW(0x9d, 0xFE, 0x87); + addToMapW(0x9d, 0xFE, 0x88); + addToMapW(0x9e, 0x00, 0x9E); + addToMapW(0x9e, 0x00, 0xD7); + addToMapW(0x9e, 0x01, 0x5E); + addToMapW(0x9e, 0x01, 0x5F); + addToMapW(0x9e, 0x04, 0x2E); + addToMapW(0x9e, 0x04, 0x4E); + addToMapW(0x9e, 0x06, 0x26); + addToMapW(0x9e, 0x20, 0xA7); + addToMapW(0x9e, 0xFE, 0x89); + addToMapW(0x9e, 0xFE, 0x8A); + addToMapW(0x9e, 0xFE, 0x8B); + addToMapW(0x9e, 0xFE, 0x8C); + addToMapW(0x9f, 0x00, 0x9F); + addToMapW(0x9f, 0x00, 0xA4); + addToMapW(0x9f, 0x00, 0xFF); + addToMapW(0x9f, 0x01, 0x78); + addToMapW(0x9f, 0x01, 0x91); + addToMapW(0x9f, 0x01, 0x92); + addToMapW(0x9f, 0x04, 0x2A); + addToMapW(0x9f, 0x04, 0x2F); + addToMapW(0x9f, 0x04, 0x4A); + addToMapW(0x9f, 0x04, 0x4F); + addToMapW(0x9f, 0x06, 0x27); + addToMapW(0x9f, 0xFE, 0x8D); + addToMapW(0x9f, 0xFE, 0x8E); + addToMapW(0xa0, 0x00, 0xA0); + addToMapW(0xa0, 0x01, 0x00); + addToMapW(0xa0, 0x01, 0x01); + addToMapW(0xa0, 0x06, 0x28); + addToMapW(0xa0, 0xF8, 0xF0); + addToMapW(0xa0, 0xFE, 0x8F); + addToMapW(0xa0, 0xFE, 0x90); + addToMapW(0xa0, 0xFE, 0x91); + addToMapW(0xa0, 0xFE, 0x92); + addToMapW(0xa1, 0x00, 0xA1); + addToMapW(0xa1, 0x01, 0x2A); + addToMapW(0xa1, 0x01, 0x2B); + addToMapW(0xa1, 0x04, 0x10); + addToMapW(0xa1, 0x04, 0x30); + addToMapW(0xa1, 0x06, 0x29); + addToMapW(0xa1, 0x0E, 0x01); + addToMapW(0xa1, 0xFE, 0x93); + addToMapW(0xa1, 0xFE, 0x94); + addToMapW(0xa1, 0xFF, 0x61); + addToMapW(0xa2, 0x00, 0xA2); + addToMapW(0xa2, 0x06, 0x2A); + addToMapW(0xa2, 0x0E, 0x02); + addToMapW(0xa2, 0xFE, 0x95); + addToMapW(0xa2, 0xFE, 0x96); + addToMapW(0xa2, 0xFE, 0x97); + addToMapW(0xa2, 0xFE, 0x98); + addToMapW(0xa2, 0xFF, 0x62); + addToMapW(0xa3, 0x00, 0xA3); + addToMapW(0xa3, 0x01, 0x7B); + addToMapW(0xa3, 0x01, 0x7C); + addToMapW(0xa3, 0x04, 0x11); + addToMapW(0xa3, 0x04, 0x31); + addToMapW(0xa3, 0x06, 0x2B); + addToMapW(0xa3, 0x0E, 0x03); + addToMapW(0xa3, 0xFE, 0x99); + addToMapW(0xa3, 0xFE, 0x9A); + addToMapW(0xa3, 0xFE, 0x9B); + addToMapW(0xa3, 0xFE, 0x9C); + addToMapW(0xa3, 0xFF, 0x63); + addToMapW(0xa4, 0x00, 0xA4); + addToMapW(0xa4, 0x01, 0x04); + addToMapW(0xa4, 0x01, 0x05); + addToMapW(0xa4, 0x06, 0x2C); + addToMapW(0xa4, 0x0E, 0x04); + addToMapW(0xa4, 0xFE, 0x9D); + addToMapW(0xa4, 0xFE, 0x9E); + addToMapW(0xa4, 0xFE, 0x9F); + addToMapW(0xa4, 0xFE, 0xA0); + addToMapW(0xa4, 0xFF, 0x64); + addToMapW(0xa5, 0x00, 0xA5); + addToMapW(0xa5, 0x00, 0xD1); + addToMapW(0xa5, 0x00, 0xF1); + addToMapW(0xa5, 0x04, 0x26); + addToMapW(0xa5, 0x04, 0x46); + addToMapW(0xa5, 0x06, 0x2D); + addToMapW(0xa5, 0x0E, 0x05); + addToMapW(0xa5, 0xFE, 0xA1); + addToMapW(0xa5, 0xFE, 0xA2); + addToMapW(0xa5, 0xFE, 0xA3); + addToMapW(0xa5, 0xFE, 0xA4); + addToMapW(0xa5, 0xFF, 0x65); + addToMapW(0xa6, 0x00, 0xA6); + addToMapW(0xa6, 0x00, 0xAA); + addToMapW(0xa6, 0x01, 0x1E); + addToMapW(0xa6, 0x01, 0x1F); + addToMapW(0xa6, 0x01, 0x7D); + addToMapW(0xa6, 0x01, 0x7E); + addToMapW(0xa6, 0x06, 0x2E); + addToMapW(0xa6, 0x0E, 0x06); + addToMapW(0xa6, 0x20, 0x1D); + addToMapW(0xa6, 0xFE, 0xA5); + addToMapW(0xa6, 0xFE, 0xA6); + addToMapW(0xa6, 0xFE, 0xA7); + addToMapW(0xa6, 0xFE, 0xA8); + addToMapW(0xa6, 0xFF, 0x66); + addToMapW(0xa7, 0x00, 0xA6); + addToMapW(0xa7, 0x00, 0xA7); + addToMapW(0xa7, 0x00, 0xBA); + addToMapW(0xa7, 0x04, 0x14); + addToMapW(0xa7, 0x04, 0x34); + addToMapW(0xa7, 0x06, 0x2F); + addToMapW(0xa7, 0x0E, 0x07); + addToMapW(0xa7, 0xFE, 0xA9); + addToMapW(0xa7, 0xFE, 0xAA); + addToMapW(0xa7, 0xFF, 0x67); + addToMapW(0xa8, 0x00, 0xA8); + addToMapW(0xa8, 0x00, 0xA9); + addToMapW(0xa8, 0x00, 0xBF); + addToMapW(0xa8, 0x01, 0x18); + addToMapW(0xa8, 0x01, 0x19); + addToMapW(0xa8, 0x06, 0x30); + addToMapW(0xa8, 0x0E, 0x08); + addToMapW(0xa8, 0xFE, 0xAB); + addToMapW(0xa8, 0xFE, 0xAC); + addToMapW(0xa8, 0xFF, 0x68); + addToMapW(0xa9, 0x00, 0xA9); + addToMapW(0xa9, 0x00, 0xAE); + addToMapW(0xa9, 0x04, 0x15); + addToMapW(0xa9, 0x04, 0x35); + addToMapW(0xa9, 0x06, 0x31); + addToMapW(0xa9, 0x0E, 0x09); + addToMapW(0xa9, 0x23, 0x10); + addToMapW(0xa9, 0xFE, 0xAD); + addToMapW(0xa9, 0xFE, 0xAE); + addToMapW(0xa9, 0xFF, 0x69); + addToMapW(0xaa, 0x00, 0xAA); + addToMapW(0xaa, 0x00, 0xAC); + addToMapW(0xaa, 0x06, 0x32); + addToMapW(0xaa, 0x0E, 0x0A); + addToMapW(0xaa, 0x23, 0x10); + addToMapW(0xaa, 0xFE, 0xAF); + addToMapW(0xaa, 0xFE, 0xB0); + addToMapW(0xaa, 0xFF, 0x6A); + addToMapW(0xab, 0x00, 0xAB); + addToMapW(0xab, 0x00, 0xBD); + addToMapW(0xab, 0x04, 0x24); + addToMapW(0xab, 0x04, 0x44); + addToMapW(0xab, 0x06, 0x33); + addToMapW(0xab, 0x0E, 0x0B); + addToMapW(0xab, 0xFE, 0xB1); + addToMapW(0xab, 0xFE, 0xB2); + addToMapW(0xab, 0xFE, 0xB3); + addToMapW(0xab, 0xFE, 0xB4); + addToMapW(0xab, 0xFF, 0x6B); + addToMapW(0xac, 0x00, 0xAC); + addToMapW(0xac, 0x00, 0xBC); + addToMapW(0xac, 0x01, 0x0C); + addToMapW(0xac, 0x01, 0x0D); + addToMapW(0xac, 0x06, 0x34); + addToMapW(0xac, 0x0E, 0x0C); + addToMapW(0xac, 0xFE, 0xB5); + addToMapW(0xac, 0xFE, 0xB6); + addToMapW(0xac, 0xFE, 0xB7); + addToMapW(0xac, 0xFE, 0xB8); + addToMapW(0xac, 0xFF, 0x6C); + addToMapW(0xad, 0x00, 0xA1); + addToMapW(0xad, 0x00, 0xAD); + addToMapW(0xad, 0x01, 0x41); + addToMapW(0xad, 0x01, 0x42); + addToMapW(0xad, 0x04, 0x13); + addToMapW(0xad, 0x04, 0x33); + addToMapW(0xad, 0x06, 0x35); + addToMapW(0xad, 0x0E, 0x0D); + addToMapW(0xad, 0xFE, 0xB9); + addToMapW(0xad, 0xFE, 0xBA); + addToMapW(0xad, 0xFE, 0xBB); + addToMapW(0xad, 0xFE, 0xBC); + addToMapW(0xad, 0xFF, 0x6D); + addToMapW(0xae, 0x00, 0xAB); + addToMapW(0xae, 0x00, 0xAE); + addToMapW(0xae, 0x0E, 0x0E); + addToMapW(0xae, 0x22, 0x6A); + addToMapW(0xae, 0x30, 0x0A); + addToMapW(0xae, 0xFF, 0x6E); + addToMapW(0xaf, 0x00, 0xAF); + addToMapW(0xaf, 0x00, 0xBB); + addToMapW(0xaf, 0x0E, 0x0F); + addToMapW(0xaf, 0x22, 0x6B); + addToMapW(0xaf, 0x30, 0x0B); + addToMapW(0xaf, 0xFF, 0x6F); + addToMapW(0xb0, 0x00, 0xB0); + addToMapW(0xb0, 0x0E, 0x10); + addToMapW(0xb0, 0x25, 0x91); + addToMapW(0xb0, 0xFF, 0x70); + addToMapW(0xb1, 0x00, 0xB1); + addToMapW(0xb1, 0x0E, 0x11); + addToMapW(0xb1, 0x25, 0x92); + addToMapW(0xb1, 0xFF, 0x71); + addToMapW(0xb2, 0x00, 0xB2); + addToMapW(0xb2, 0x0E, 0x12); + addToMapW(0xb2, 0x25, 0x93); + addToMapW(0xb2, 0xFF, 0x72); + addToMapW(0xb3, 0x00, 0xA6); + addToMapW(0xb3, 0x00, 0xB3); + addToMapW(0xb3, 0x01, 0xC0); + addToMapW(0xb3, 0x0E, 0x13); + addToMapW(0xb3, 0x22, 0x23); + addToMapW(0xb3, 0x25, 0x02); + addToMapW(0xb3, 0x27, 0x58); + addToMapW(0xb3, 0xFF, 0x73); + addToMapW(0xb4, 0x00, 0xB4); + addToMapW(0xb4, 0x0E, 0x14); + addToMapW(0xb4, 0x25, 0x24); + addToMapW(0xb4, 0xFF, 0x74); + addToMapW(0xb5, 0x00, 0xB5); + addToMapW(0xb5, 0x00, 0xC1); + addToMapW(0xb5, 0x00, 0xE1); + addToMapW(0xb5, 0x01, 0x04); + addToMapW(0xb5, 0x01, 0x05); + addToMapW(0xb5, 0x0E, 0x15); + addToMapW(0xb5, 0x25, 0x61); + addToMapW(0xb5, 0xFF, 0x75); + addToMapW(0xb6, 0x00, 0xB6); + addToMapW(0xb6, 0x00, 0xC2); + addToMapW(0xb6, 0x00, 0xE2); + addToMapW(0xb6, 0x01, 0x0C); + addToMapW(0xb6, 0x01, 0x0D); + addToMapW(0xb6, 0x04, 0x25); + addToMapW(0xb6, 0x04, 0x45); + addToMapW(0xb6, 0x0E, 0x16); + addToMapW(0xb6, 0x25, 0x62); + addToMapW(0xb6, 0xFF, 0x76); + addToMapW(0xb7, 0x00, 0xB7); + addToMapW(0xb7, 0x00, 0xC0); + addToMapW(0xb7, 0x00, 0xE0); + addToMapW(0xb7, 0x01, 0x18); + addToMapW(0xb7, 0x01, 0x19); + addToMapW(0xb7, 0x01, 0x1A); + addToMapW(0xb7, 0x01, 0x1B); + addToMapW(0xb7, 0x0E, 0x17); + addToMapW(0xb7, 0x25, 0x56); + addToMapW(0xb7, 0xFF, 0x77); + addToMapW(0xb8, 0x00, 0xA9); + addToMapW(0xb8, 0x00, 0xB8); + addToMapW(0xb8, 0x01, 0x16); + addToMapW(0xb8, 0x01, 0x17); + addToMapW(0xb8, 0x01, 0x5E); + addToMapW(0xb8, 0x01, 0x5F); + addToMapW(0xb8, 0x04, 0x18); + addToMapW(0xb8, 0x04, 0x38); + addToMapW(0xb8, 0x0E, 0x18); + addToMapW(0xb8, 0x25, 0x55); + addToMapW(0xb8, 0xFF, 0x78); + addToMapW(0xb9, 0x00, 0xB9); + addToMapW(0xb9, 0x0E, 0x19); + addToMapW(0xb9, 0x25, 0x61); + addToMapW(0xb9, 0x25, 0x62); + addToMapW(0xb9, 0x25, 0x63); + addToMapW(0xb9, 0xFF, 0x79); + addToMapW(0xba, 0x00, 0xBA); + addToMapW(0xba, 0x0E, 0x1A); + addToMapW(0xba, 0x25, 0x51); + addToMapW(0xba, 0xFF, 0x7A); + addToMapW(0xbb, 0x00, 0xBB); + addToMapW(0xbb, 0x0E, 0x1B); + addToMapW(0xbb, 0x25, 0x55); + addToMapW(0xbb, 0x25, 0x56); + addToMapW(0xbb, 0x25, 0x57); + addToMapW(0xbb, 0xFF, 0x7B); + addToMapW(0xbc, 0x00, 0xBC); + addToMapW(0xbc, 0x0E, 0x1C); + addToMapW(0xbc, 0x25, 0x5B); + addToMapW(0xbc, 0x25, 0x5C); + addToMapW(0xbc, 0x25, 0x5D); + addToMapW(0xbc, 0xFF, 0x7C); + addToMapW(0xbd, 0x00, 0xA2); + addToMapW(0xbd, 0x00, 0xBD); + addToMapW(0xbd, 0x01, 0x2E); + addToMapW(0xbd, 0x01, 0x2F); + addToMapW(0xbd, 0x01, 0x7B); + addToMapW(0xbd, 0x01, 0x7C); + addToMapW(0xbd, 0x0E, 0x1D); + addToMapW(0xbd, 0x25, 0x5C); + addToMapW(0xbd, 0xFF, 0x7D); + addToMapW(0xbe, 0x00, 0xA5); + addToMapW(0xbe, 0x00, 0xBE); + addToMapW(0xbe, 0x01, 0x60); + addToMapW(0xbe, 0x01, 0x61); + addToMapW(0xbe, 0x04, 0x19); + addToMapW(0xbe, 0x04, 0x39); + addToMapW(0xbe, 0x0E, 0x1E); + addToMapW(0xbe, 0x25, 0x5B); + addToMapW(0xbe, 0xFF, 0x7E); + addToMapW(0xbf, 0x00, 0xAC); + addToMapW(0xbf, 0x00, 0xBF); + addToMapW(0xbf, 0x0E, 0x1F); + addToMapW(0xbf, 0x25, 0x10); + addToMapW(0xbf, 0xFF, 0x7F); + addToMapW(0xc0, 0x00, 0xC0); + addToMapW(0xc0, 0x00, 0xE0); + addToMapW(0xc0, 0x0E, 0x20); + addToMapW(0xc0, 0x25, 0x14); + addToMapW(0xc0, 0xFF, 0x80); + addToMapW(0xc1, 0x00, 0xC1); + addToMapW(0xc1, 0x00, 0xE1); + addToMapW(0xc1, 0x0E, 0x21); + addToMapW(0xc1, 0x25, 0x34); + addToMapW(0xc1, 0xFF, 0x81); + addToMapW(0xc2, 0x00, 0xC2); + addToMapW(0xc2, 0x00, 0xE2); + addToMapW(0xc2, 0x0E, 0x22); + addToMapW(0xc2, 0x25, 0x2C); + addToMapW(0xc2, 0xFF, 0x82); + addToMapW(0xc3, 0x01, 0x02); + addToMapW(0xc3, 0x01, 0x03); + addToMapW(0xc3, 0x0E, 0x23); + addToMapW(0xc3, 0x25, 0x1C); + addToMapW(0xc3, 0xFF, 0x83); + addToMapW(0xc4, 0x00, 0xAF); + addToMapW(0xc4, 0x00, 0xC4); + addToMapW(0xc4, 0x00, 0xE4); + addToMapW(0xc4, 0x02, 0xC9); + addToMapW(0xc4, 0x03, 0x04); + addToMapW(0xc4, 0x03, 0x05); + addToMapW(0xc4, 0x0E, 0x24); + addToMapW(0xc4, 0x25, 0x00); + addToMapW(0xc4, 0xFF, 0x84); + addToMapW(0xc5, 0x00, 0xC5); + addToMapW(0xc5, 0x00, 0xE5); + addToMapW(0xc5, 0x0E, 0x25); + addToMapW(0xc5, 0x20, 0x20); + addToMapW(0xc5, 0x20, 0x21); + addToMapW(0xc5, 0x25, 0x3C); + addToMapW(0xc5, 0xFF, 0x85); + addToMapW(0xc6, 0x00, 0xC6); + addToMapW(0xc6, 0x00, 0xE6); + addToMapW(0xc6, 0x01, 0x02); + addToMapW(0xc6, 0x01, 0x03); + addToMapW(0xc6, 0x01, 0x72); + addToMapW(0xc6, 0x01, 0x73); + addToMapW(0xc6, 0x0E, 0x26); + addToMapW(0xc6, 0x25, 0x5E); + addToMapW(0xc6, 0xFF, 0x86); + addToMapW(0xc7, 0x00, 0xC3); + addToMapW(0xc7, 0x00, 0xC7); + addToMapW(0xc7, 0x00, 0xE3); + addToMapW(0xc7, 0x00, 0xE7); + addToMapW(0xc7, 0x01, 0x6A); + addToMapW(0xc7, 0x01, 0x6B); + addToMapW(0xc7, 0x04, 0x1A); + addToMapW(0xc7, 0x04, 0x3A); + addToMapW(0xc7, 0x0E, 0x27); + addToMapW(0xc7, 0x25, 0x5F); + addToMapW(0xc7, 0xFF, 0x87); + addToMapW(0xc8, 0x00, 0xC8); + addToMapW(0xc8, 0x00, 0xE8); + addToMapW(0xc8, 0x0E, 0x28); + addToMapW(0xc8, 0x25, 0x58); + addToMapW(0xc8, 0x25, 0x59); + addToMapW(0xc8, 0x25, 0x5A); + addToMapW(0xc8, 0xFF, 0x88); + addToMapW(0xc9, 0x00, 0xC9); + addToMapW(0xc9, 0x00, 0xE9); + addToMapW(0xc9, 0x0E, 0x29); + addToMapW(0xc9, 0x25, 0x52); + addToMapW(0xc9, 0x25, 0x53); + addToMapW(0xc9, 0x25, 0x54); + addToMapW(0xc9, 0xFF, 0x89); + addToMapW(0xca, 0x00, 0xCA); + addToMapW(0xca, 0x00, 0xEA); + addToMapW(0xca, 0x0E, 0x2A); + addToMapW(0xca, 0x25, 0x67); + addToMapW(0xca, 0x25, 0x68); + addToMapW(0xca, 0x25, 0x69); + addToMapW(0xca, 0xFF, 0x8A); + addToMapW(0xcb, 0x00, 0xCB); + addToMapW(0xcb, 0x00, 0xEB); + addToMapW(0xcb, 0x0E, 0x2B); + addToMapW(0xcb, 0x25, 0x64); + addToMapW(0xcb, 0x25, 0x65); + addToMapW(0xcb, 0x25, 0x66); + addToMapW(0xcb, 0xFF, 0x8B); + addToMapW(0xcc, 0x03, 0x00); + addToMapW(0xcc, 0x0E, 0x2C); + addToMapW(0xcc, 0x25, 0x5E); + addToMapW(0xcc, 0x25, 0x5F); + addToMapW(0xcc, 0x25, 0x60); + addToMapW(0xcc, 0xFF, 0x8C); + addToMapW(0xcd, 0x00, 0xCD); + addToMapW(0xcd, 0x00, 0xED); + addToMapW(0xcd, 0x0E, 0x2D); + addToMapW(0xcd, 0x25, 0x50); + addToMapW(0xcd, 0xFF, 0x8D); + addToMapW(0xce, 0x00, 0xCE); + addToMapW(0xce, 0x00, 0xEE); + addToMapW(0xce, 0x0E, 0x2E); + addToMapW(0xce, 0x20, 0x21); + addToMapW(0xce, 0x25, 0x6A); + addToMapW(0xce, 0x25, 0x6B); + addToMapW(0xce, 0x25, 0x6C); + addToMapW(0xce, 0xFF, 0x8E); + addToMapW(0xcf, 0x00, 0xA4); + addToMapW(0xcf, 0x00, 0xCF); + addToMapW(0xcf, 0x00, 0xEF); + addToMapW(0xcf, 0x01, 0x7D); + addToMapW(0xcf, 0x01, 0x7E); + addToMapW(0xcf, 0x0E, 0x2F); + addToMapW(0xcf, 0x25, 0x67); + addToMapW(0xcf, 0xFF, 0x8F); + addToMapW(0xd0, 0x00, 0xBA); + addToMapW(0xd0, 0x01, 0x10); + addToMapW(0xd0, 0x01, 0x11); + addToMapW(0xd0, 0x0E, 0x30); + addToMapW(0xd0, 0x25, 0x68); + addToMapW(0xd0, 0xFF, 0x90); + addToMapW(0xd1, 0x00, 0xAA); + addToMapW(0xd1, 0x00, 0xD0); + addToMapW(0xd1, 0x00, 0xD1); + addToMapW(0xd1, 0x00, 0xF0); + addToMapW(0xd1, 0x00, 0xF1); + addToMapW(0xd1, 0x01, 0x10); + addToMapW(0xd1, 0x01, 0x11); + addToMapW(0xd1, 0x01, 0x89); + addToMapW(0xd1, 0x04, 0x1B); + addToMapW(0xd1, 0x04, 0x3B); + addToMapW(0xd1, 0x0E, 0x31); + addToMapW(0xd1, 0x25, 0x64); + addToMapW(0xd1, 0xFF, 0x91); + addToMapW(0xd2, 0x00, 0xCA); + addToMapW(0xd2, 0x00, 0xEA); + addToMapW(0xd2, 0x01, 0x0E); + addToMapW(0xd2, 0x01, 0x0F); + addToMapW(0xd2, 0x03, 0x09); + addToMapW(0xd2, 0x0E, 0x32); + addToMapW(0xd2, 0x25, 0x65); + addToMapW(0xd2, 0xFF, 0x92); + addToMapW(0xd3, 0x00, 0xCB); + addToMapW(0xd3, 0x00, 0xD3); + addToMapW(0xd3, 0x00, 0xEB); + addToMapW(0xd3, 0x00, 0xF3); + addToMapW(0xd3, 0x04, 0x1C); + addToMapW(0xd3, 0x04, 0x3C); + addToMapW(0xd3, 0x0E, 0x33); + addToMapW(0xd3, 0x25, 0x59); + addToMapW(0xd3, 0xFF, 0x93); + addToMapW(0xd4, 0x00, 0xC8); + addToMapW(0xd4, 0x00, 0xD4); + addToMapW(0xd4, 0x00, 0xE8); + addToMapW(0xd4, 0x00, 0xF4); + addToMapW(0xd4, 0x0E, 0x34); + addToMapW(0xd4, 0x25, 0x58); + addToMapW(0xd4, 0xFF, 0x94); + addToMapW(0xd5, 0x01, 0x31); + addToMapW(0xd5, 0x01, 0x47); + addToMapW(0xd5, 0x01, 0x48); + addToMapW(0xd5, 0x01, 0xA0); + addToMapW(0xd5, 0x01, 0xA1); + addToMapW(0xd5, 0x04, 0x1D); + addToMapW(0xd5, 0x04, 0x3D); + addToMapW(0xd5, 0x0E, 0x35); + addToMapW(0xd5, 0x25, 0x52); + addToMapW(0xd5, 0xF8, 0xBB); + addToMapW(0xd5, 0xFF, 0x95); + addToMapW(0xd6, 0x00, 0xCD); + addToMapW(0xd6, 0x00, 0xD6); + addToMapW(0xd6, 0x00, 0xED); + addToMapW(0xd6, 0x00, 0xF6); + addToMapW(0xd6, 0x0E, 0x36); + addToMapW(0xd6, 0x25, 0x53); + addToMapW(0xd6, 0xFF, 0x96); + addToMapW(0xd7, 0x00, 0xCE); + addToMapW(0xd7, 0x00, 0xD7); + addToMapW(0xd7, 0x00, 0xEE); + addToMapW(0xd7, 0x04, 0x1E); + addToMapW(0xd7, 0x04, 0x3E); + addToMapW(0xd7, 0x0E, 0x37); + addToMapW(0xd7, 0x25, 0x6B); + addToMapW(0xd7, 0xFF, 0x97); + addToMapW(0xd8, 0x00, 0xCF); + addToMapW(0xd8, 0x00, 0xD8); + addToMapW(0xd8, 0x00, 0xEF); + addToMapW(0xd8, 0x00, 0xF8); + addToMapW(0xd8, 0x0E, 0x38); + addToMapW(0xd8, 0x20, 0x21); + addToMapW(0xd8, 0x25, 0x6A); + addToMapW(0xd8, 0xFF, 0x98); + addToMapW(0xd9, 0x00, 0xD9); + addToMapW(0xd9, 0x00, 0xF9); + addToMapW(0xd9, 0x0E, 0x39); + addToMapW(0xd9, 0x25, 0x18); + addToMapW(0xd9, 0xFF, 0x99); + addToMapW(0xda, 0x00, 0xDA); + addToMapW(0xda, 0x00, 0xFA); + addToMapW(0xda, 0x0E, 0x3A); + addToMapW(0xda, 0x25, 0x0C); + addToMapW(0xda, 0xFF, 0x9A); + addToMapW(0xdb, 0x00, 0xDB); + addToMapW(0xdb, 0x00, 0xFB); + addToMapW(0xdb, 0x25, 0x88); + addToMapW(0xdb, 0x25, 0x8C); + addToMapW(0xdb, 0x25, 0x90); + addToMapW(0xdb, 0xF8, 0xC1); + addToMapW(0xdb, 0xFF, 0x9B); + addToMapW(0xdc, 0x00, 0xDC); + addToMapW(0xdc, 0x00, 0xFC); + addToMapW(0xdc, 0x25, 0x84); + addToMapW(0xdc, 0xF8, 0xC2); + addToMapW(0xdc, 0xFF, 0x9C); + addToMapW(0xdd, 0x00, 0xA6); + addToMapW(0xdd, 0x01, 0x62); + addToMapW(0xdd, 0x01, 0x63); + addToMapW(0xdd, 0x01, 0xAF); + addToMapW(0xdd, 0x01, 0xB0); + addToMapW(0xdd, 0x04, 0x1F); + addToMapW(0xdd, 0x04, 0x3F); + addToMapW(0xdd, 0x25, 0x8C); + addToMapW(0xdd, 0xF8, 0xC3); + addToMapW(0xdd, 0xFF, 0x9D); + addToMapW(0xde, 0x00, 0xCC); + addToMapW(0xde, 0x00, 0xEC); + addToMapW(0xde, 0x01, 0x6E); + addToMapW(0xde, 0x01, 0x6F); + addToMapW(0xde, 0x03, 0x03); + addToMapW(0xde, 0x25, 0x90); + addToMapW(0xde, 0xF8, 0xC4); + addToMapW(0xde, 0xFF, 0x9E); + addToMapW(0xdf, 0x00, 0xDF); + addToMapW(0xdf, 0x0E, 0x3F); + addToMapW(0xdf, 0x25, 0x80); + addToMapW(0xdf, 0xFF, 0x9F); + addToMapW(0xe0, 0x00, 0xD3); + addToMapW(0xe0, 0x00, 0xF3); + addToMapW(0xe0, 0x03, 0x91); + addToMapW(0xe0, 0x03, 0xB1); + addToMapW(0xe0, 0x04, 0x2F); + addToMapW(0xe0, 0x04, 0x4F); + addToMapW(0xe0, 0x06, 0x36); + addToMapW(0xe0, 0x0E, 0x40); + addToMapW(0xe0, 0xFE, 0xBD); + addToMapW(0xe0, 0xFE, 0xBE); + addToMapW(0xe0, 0xFE, 0xBF); + addToMapW(0xe0, 0xFE, 0xC0); + addToMapW(0xe1, 0x00, 0xDF); + addToMapW(0xe1, 0x03, 0xB2); + addToMapW(0xe1, 0x06, 0x37); + addToMapW(0xe1, 0x0E, 0x41); + addToMapW(0xe1, 0xFE, 0xC1); + addToMapW(0xe1, 0xFE, 0xC2); + addToMapW(0xe1, 0xFE, 0xC3); + addToMapW(0xe1, 0xFE, 0xC4); + addToMapW(0xe2, 0x00, 0xD4); + addToMapW(0xe2, 0x00, 0xF4); + addToMapW(0xe2, 0x01, 0x4C); + addToMapW(0xe2, 0x01, 0x4D); + addToMapW(0xe2, 0x03, 0x93); + addToMapW(0xe2, 0x04, 0x20); + addToMapW(0xe2, 0x04, 0x40); + addToMapW(0xe2, 0x06, 0x38); + addToMapW(0xe2, 0x0E, 0x42); + addToMapW(0xe2, 0xFE, 0xC5); + addToMapW(0xe2, 0xFE, 0xC6); + addToMapW(0xe2, 0xFE, 0xC7); + addToMapW(0xe2, 0xFE, 0xC8); + addToMapW(0xe3, 0x00, 0xD2); + addToMapW(0xe3, 0x00, 0xF2); + addToMapW(0xe3, 0x01, 0x43); + addToMapW(0xe3, 0x01, 0x44); + addToMapW(0xe3, 0x03, 0xA0); + addToMapW(0xe3, 0x03, 0xC0); + addToMapW(0xe3, 0x06, 0x39); + addToMapW(0xe3, 0x0E, 0x43); + addToMapW(0xe3, 0xFE, 0xC9); + addToMapW(0xe3, 0xFE, 0xCA); + addToMapW(0xe3, 0xFE, 0xCB); + addToMapW(0xe3, 0xFE, 0xCC); + addToMapW(0xe4, 0x01, 0xA9); + addToMapW(0xe4, 0x03, 0xA3); + addToMapW(0xe4, 0x03, 0xC3); + addToMapW(0xe4, 0x04, 0x21); + addToMapW(0xe4, 0x04, 0x41); + addToMapW(0xe4, 0x06, 0x3A); + addToMapW(0xe4, 0x0E, 0x44); + addToMapW(0xe4, 0x22, 0x11); + addToMapW(0xe4, 0xFE, 0xCD); + addToMapW(0xe4, 0xFE, 0xCE); + addToMapW(0xe4, 0xFE, 0xCF); + addToMapW(0xe4, 0xFE, 0xD0); + addToMapW(0xe5, 0x00, 0xD5); + addToMapW(0xe5, 0x00, 0xF5); + addToMapW(0xe5, 0x06, 0x41); + addToMapW(0xe5, 0x0E, 0x45); + addToMapW(0xe5, 0xFE, 0xD1); + addToMapW(0xe5, 0xFE, 0xD2); + addToMapW(0xe5, 0xFE, 0xD3); + addToMapW(0xe5, 0xFE, 0xD4); + addToMapW(0xe6, 0x00, 0xB5); + addToMapW(0xe6, 0x01, 0x60); + addToMapW(0xe6, 0x01, 0x61); + addToMapW(0xe6, 0x03, 0xBC); + addToMapW(0xe6, 0x04, 0x22); + addToMapW(0xe6, 0x04, 0x42); + addToMapW(0xe6, 0x0E, 0x46); + addToMapW(0xe7, 0x03, 0xA4); + addToMapW(0xe7, 0x03, 0xC4); + addToMapW(0xe7, 0x06, 0x42); + addToMapW(0xe7, 0x0E, 0x47); + addToMapW(0xe7, 0xF8, 0xBC); + addToMapW(0xe7, 0xFE, 0xD5); + addToMapW(0xe7, 0xFE, 0xD6); + addToMapW(0xe7, 0xFE, 0xD7); + addToMapW(0xe7, 0xFE, 0xD8); + addToMapW(0xe8, 0x00, 0xD7); + addToMapW(0xe8, 0x00, 0xDE); + addToMapW(0xe8, 0x00, 0xFE); + addToMapW(0xe8, 0x01, 0x36); + addToMapW(0xe8, 0x01, 0x37); + addToMapW(0xe8, 0x01, 0x54); + addToMapW(0xe8, 0x01, 0x55); + addToMapW(0xe8, 0x02, 0x78); + addToMapW(0xe8, 0x03, 0xA6); + addToMapW(0xe8, 0x03, 0xC6); + addToMapW(0xe8, 0x04, 0x23); + addToMapW(0xe8, 0x04, 0x43); + addToMapW(0xe8, 0x06, 0x43); + addToMapW(0xe8, 0x0E, 0x48); + addToMapW(0xe8, 0x22, 0x05); + addToMapW(0xe8, 0xFE, 0xD9); + addToMapW(0xe8, 0xFE, 0xDA); + addToMapW(0xe8, 0xFE, 0xDB); + addToMapW(0xe8, 0xFE, 0xDC); + addToMapW(0xe9, 0x00, 0xDA); + addToMapW(0xe9, 0x00, 0xFA); + addToMapW(0xe9, 0x03, 0x98); + addToMapW(0xe9, 0x06, 0x44); + addToMapW(0xe9, 0x0E, 0x49); + addToMapW(0xe9, 0xFE, 0xDD); + addToMapW(0xe9, 0xFE, 0xDE); + addToMapW(0xe9, 0xFE, 0xDF); + addToMapW(0xe9, 0xFE, 0xE0); + addToMapW(0xea, 0x00, 0xDB); + addToMapW(0xea, 0x00, 0xFB); + addToMapW(0xea, 0x01, 0x3B); + addToMapW(0xea, 0x01, 0x3C); + addToMapW(0xea, 0x03, 0x86); + addToMapW(0xea, 0x03, 0xA9); + addToMapW(0xea, 0x03, 0xAC); + addToMapW(0xea, 0x04, 0x16); + addToMapW(0xea, 0x04, 0x36); + addToMapW(0xea, 0x06, 0x45); + addToMapW(0xea, 0x0E, 0x4A); + addToMapW(0xea, 0x21, 0x26); + addToMapW(0xea, 0xFE, 0xE1); + addToMapW(0xea, 0xFE, 0xE2); + addToMapW(0xea, 0xFE, 0xE3); + addToMapW(0xea, 0xFE, 0xE4); + addToMapW(0xeb, 0x00, 0xD9); + addToMapW(0xeb, 0x00, 0xF9); + addToMapW(0xeb, 0x01, 0x70); + addToMapW(0xeb, 0x01, 0x71); + addToMapW(0xeb, 0x03, 0x88); + addToMapW(0xeb, 0x03, 0x94); + addToMapW(0xeb, 0x03, 0xAD); + addToMapW(0xeb, 0x03, 0xB4); + addToMapW(0xeb, 0x06, 0x46); + addToMapW(0xeb, 0x0E, 0x4B); + addToMapW(0xeb, 0xFE, 0xE5); + addToMapW(0xeb, 0xFE, 0xE6); + addToMapW(0xeb, 0xFE, 0xE7); + addToMapW(0xeb, 0xFE, 0xE8); + addToMapW(0xec, 0x03, 0x01); + addToMapW(0xec, 0x03, 0x89); + addToMapW(0xec, 0x03, 0xAE); + addToMapW(0xec, 0x04, 0x12); + addToMapW(0xec, 0x04, 0x32); + addToMapW(0xec, 0x06, 0x47); + addToMapW(0xec, 0x0E, 0x4C); + addToMapW(0xec, 0x22, 0x1E); + addToMapW(0xec, 0xFE, 0xE9); + addToMapW(0xec, 0xFE, 0xEA); + addToMapW(0xec, 0xFE, 0xEB); + addToMapW(0xec, 0xFE, 0xEC); + addToMapW(0xed, 0x00, 0xDD); + addToMapW(0xed, 0x00, 0xFD); + addToMapW(0xed, 0x01, 0x12); + addToMapW(0xed, 0x01, 0x13); + addToMapW(0xed, 0x03, 0x8A); + addToMapW(0xed, 0x03, 0xAF); + addToMapW(0xed, 0x06, 0x48); + addToMapW(0xed, 0x0E, 0x4D); + addToMapW(0xed, 0xFE, 0xED); + addToMapW(0xed, 0xFE, 0xEE); + addToMapW(0xee, 0x00, 0xAF); + addToMapW(0xee, 0x01, 0x45); + addToMapW(0xee, 0x01, 0x46); + addToMapW(0xee, 0x03, 0x04); + addToMapW(0xee, 0x03, 0x05); + addToMapW(0xee, 0x03, 0x8C); + addToMapW(0xee, 0x03, 0x95); + addToMapW(0xee, 0x03, 0xB5); + addToMapW(0xee, 0x03, 0xCC); + addToMapW(0xee, 0x04, 0x2C); + addToMapW(0xee, 0x04, 0x4C); + addToMapW(0xee, 0x06, 0x49); + addToMapW(0xee, 0x0E, 0x4E); + addToMapW(0xee, 0xFE, 0xEF); + addToMapW(0xee, 0xFE, 0xF0); + addToMapW(0xef, 0x00, 0xB4); + addToMapW(0xef, 0x02, 0xB9); + addToMapW(0xef, 0x02, 0xCA); + addToMapW(0xef, 0x03, 0x01); + addToMapW(0xef, 0x03, 0x8E); + addToMapW(0xef, 0x03, 0xCD); + addToMapW(0xef, 0x06, 0x4A); + addToMapW(0xef, 0x0E, 0x4F); + addToMapW(0xef, 0x20, 0x19); + addToMapW(0xef, 0x20, 0x32); + addToMapW(0xef, 0x20, 0x35); + addToMapW(0xef, 0x21, 0x16); + addToMapW(0xef, 0x22, 0x29); + addToMapW(0xef, 0xFE, 0xF1); + addToMapW(0xef, 0xFE, 0xF2); + addToMapW(0xef, 0xFE, 0xF3); + addToMapW(0xef, 0xFE, 0xF4); + addToMapW(0xf0, 0x00, 0xAD); + addToMapW(0xf0, 0x03, 0x8F); + addToMapW(0xf0, 0x03, 0xCE); + addToMapW(0xf0, 0x04, 0x01); + addToMapW(0xf0, 0x04, 0x51); + addToMapW(0xf0, 0x0E, 0x50); + addToMapW(0xf0, 0x22, 0x61); + addToMapW(0xf1, 0x00, 0xB1); + addToMapW(0xf1, 0x02, 0xDD); + addToMapW(0xf1, 0x06, 0x4B); + addToMapW(0xf1, 0x0E, 0x51); + addToMapW(0xf1, 0x22, 0x13); + addToMapW(0xf1, 0xFE, 0x70); + addToMapW(0xf1, 0xFE, 0x71); + addToMapW(0xf2, 0x02, 0xDB); + addToMapW(0xf2, 0x03, 0x23); + addToMapW(0xf2, 0x04, 0x04); + addToMapW(0xf2, 0x04, 0x2B); + addToMapW(0xf2, 0x04, 0x4B); + addToMapW(0xf2, 0x04, 0x54); + addToMapW(0xf2, 0x06, 0x4C); + addToMapW(0xf2, 0x0E, 0x52); + addToMapW(0xf2, 0x20, 0x17); + addToMapW(0xf2, 0x20, 0x1C); + addToMapW(0xf2, 0x22, 0x65); + addToMapW(0xf2, 0xF8, 0xBD); + addToMapW(0xf2, 0xFE, 0x72); + addToMapW(0xf3, 0x00, 0xBE); + addToMapW(0xf3, 0x02, 0xC7); + addToMapW(0xf3, 0x03, 0x0C); + addToMapW(0xf3, 0x06, 0x4D); + addToMapW(0xf3, 0x0E, 0x53); + addToMapW(0xf3, 0x22, 0x64); + addToMapW(0xf3, 0xFE, 0x74); + addToMapW(0xf4, 0x00, 0xB6); + addToMapW(0xf4, 0x02, 0xD8); + addToMapW(0xf4, 0x03, 0x06); + addToMapW(0xf4, 0x03, 0xAA); + addToMapW(0xf4, 0x03, 0xCA); + addToMapW(0xf4, 0x04, 0x07); + addToMapW(0xf4, 0x04, 0x17); + addToMapW(0xf4, 0x04, 0x37); + addToMapW(0xf4, 0x04, 0x57); + addToMapW(0xf4, 0x06, 0x4E); + addToMapW(0xf4, 0x0E, 0x54); + addToMapW(0xf4, 0x23, 0x20); + addToMapW(0xf4, 0xFE, 0x76); + addToMapW(0xf4, 0xFE, 0x77); + addToMapW(0xf5, 0x00, 0xA7); + addToMapW(0xf5, 0x03, 0xAB); + addToMapW(0xf5, 0x03, 0xCB); + addToMapW(0xf5, 0x06, 0x4F); + addToMapW(0xf5, 0x0E, 0x55); + addToMapW(0xf5, 0x23, 0x21); + addToMapW(0xf5, 0xFE, 0x78); + addToMapW(0xf5, 0xFE, 0x79); + addToMapW(0xf6, 0x00, 0xF7); + addToMapW(0xf6, 0x04, 0x0E); + addToMapW(0xf6, 0x04, 0x28); + addToMapW(0xf6, 0x04, 0x48); + addToMapW(0xf6, 0x04, 0x5E); + addToMapW(0xf6, 0x06, 0x50); + addToMapW(0xf6, 0x0E, 0x56); + addToMapW(0xf6, 0xFE, 0x7A); + addToMapW(0xf6, 0xFE, 0x7B); + addToMapW(0xf7, 0x00, 0xB8); + addToMapW(0xf7, 0x00, 0xF7); + addToMapW(0xf7, 0x02, 0xDB); + addToMapW(0xf7, 0x03, 0x27); + addToMapW(0xf7, 0x0E, 0x57); + addToMapW(0xf7, 0x20, 0x1E); + addToMapW(0xf7, 0x22, 0x48); + addToMapW(0xf8, 0x00, 0xB0); + addToMapW(0xf8, 0x02, 0xDA); + addToMapW(0xf8, 0x03, 0x0A); + addToMapW(0xf8, 0x04, 0x2D); + addToMapW(0xf8, 0x04, 0x4D); + addToMapW(0xf8, 0x0E, 0x58); + addToMapW(0xf8, 0x20, 0x70); + addToMapW(0xf8, 0x22, 0x18); + addToMapW(0xf9, 0x00, 0xA8); + addToMapW(0xf9, 0x02, 0xDD); + addToMapW(0xf9, 0x03, 0x08); + addToMapW(0xf9, 0x0E, 0x59); + addToMapW(0xf9, 0x22, 0x19); + addToMapW(0xfa, 0x00, 0xB7); + addToMapW(0xfa, 0x02, 0xD9); + addToMapW(0xfa, 0x03, 0x07); + addToMapW(0xfa, 0x04, 0x29); + addToMapW(0xfa, 0x04, 0x49); + addToMapW(0xfa, 0x0E, 0x5A); + addToMapW(0xfa, 0x20, 0x24); + addToMapW(0xfa, 0x22, 0xC5); + addToMapW(0xfa, 0x30, 0xFB); + addToMapW(0xfb, 0x00, 0xB9); + addToMapW(0xfb, 0x0E, 0x5B); + addToMapW(0xfb, 0x20, 0x81); + addToMapW(0xfb, 0x22, 0x1A); + addToMapW(0xfb, 0x27, 0x13); + addToMapW(0xfc, 0x00, 0xB3); + addToMapW(0xfc, 0x01, 0x58); + addToMapW(0xfc, 0x01, 0x59); + addToMapW(0xfc, 0x04, 0x27); + addToMapW(0xfc, 0x04, 0x47); + addToMapW(0xfc, 0x20, 0x7F); + addToMapW(0xfc, 0x20, 0x83); + addToMapW(0xfc, 0x21, 0x16); + addToMapW(0xfc, 0xF8, 0xC5); + addToMapW(0xfd, 0x00, 0xA4); + addToMapW(0xfd, 0x00, 0xA7); + addToMapW(0xfd, 0x00, 0xB2); + addToMapW(0xfd, 0x20, 0x82); + addToMapW(0xfd, 0xF8, 0xC6); + addToMapW(0xfd, 0xF8, 0xF1); + addToMapW(0xfe, 0x20, 0xAB); + addToMapW(0xfe, 0x25, 0xA0); + addToMapW(0xfe, 0xF8, 0xC7); + addToMapW(0xfe, 0xF8, 0xF2); + addToMapW(0xff, 0x00, 0xA0); + addToMapW(0xff, 0xF8, 0xC8); + addToMapW(0xff, 0xF8, 0xF3); +} diff --git a/Client Applications/rcracki_mt/lm2ntlm.h b/Client Applications/rcracki_mt/lm2ntlm.h index ff2e42d..e608040 100644 --- a/Client Applications/rcracki_mt/lm2ntlm.h +++ b/Client Applications/rcracki_mt/lm2ntlm.h @@ -1,55 +1,80 @@ -#include -#include -#include -#ifdef _WIN32 -#include -#endif -#include "openssl/md4.h" -#include "time.h" -#include "signal.h" -#include "Public.h" -using namespace std; - -class LM2NTLMcorrector -{ -public: - LM2NTLMcorrector(); - -private: - map > m_mapChar; - uint64 progressCurrentCombination; - uint64 totalCurrentCombination; - uint64 counterOverall; - unsigned char NTLMHash[16]; - clock_t startClock; - int countCombinations; - int countTotalCombinations; - int counter; - clock_t previousClock; - unsigned char currentCharmap[16][128]; - bool aborting; - string sBinary; - -private: - bool checkNTLMPassword(unsigned char* pLMPassword, int nLMPasswordLen, string& sNTLMPassword); - bool startCorrecting(string sLMPassword, unsigned char* pNTLMHash, string& sNTLMPassword, unsigned char* pLMPassword); - void printString(unsigned char* muteThis, int length); - void setupCombinationAtPositions(int length, unsigned char* pMuteMe, unsigned char* pTempMute, int* jAtPos, bool* fullAtPos, int* sizeAtPos); - bool checkPermutations(int length, unsigned char* pMuteMe, unsigned char* pTempMute, int* jAtPos, int* sizeAtPos, unsigned char* pLMPassword, string& sNTLMPassword); - - int calculateTotalCombinations(int length, int setSize); - int factorial (int num); - - bool parseHexPassword(string hexPassword, string& sPlain); - bool NormalizeHexString(string& sHash); - void ParseHash(string sHash, unsigned char* pHash, int& nHashLen); - string ByteToStr(const unsigned char* pData, int nLen); - void addToMapW(unsigned char key, unsigned char value1, unsigned char value2); - void fillMapW(); - void checkAbort(); - void writeEndStats(); -public: - bool LMPasswordCorrectUnicode(string hexPassword, unsigned char* NTLMHash, string& sNTLMPassword); - string getBinary(); -}; - +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#include +#include +#include +#ifdef _WIN32 + #include +#endif +//#include "openssl/md4.h" +#include +#include "signal.h" +#include "Public.h" +#include "md4.h" + +using namespace std; + +class LM2NTLMcorrector +{ +public: + LM2NTLMcorrector(); + +private: + map > m_mapChar; + uint64 progressCurrentCombination; + uint64 totalCurrentCombination; + uint64 counterOverall; + unsigned char NTLMHash[16]; + clock_t startClock; + int countCombinations; + int countTotalCombinations; + int counter; + clock_t previousClock; + unsigned char currentCharmap[16][128]; + bool aborting; + string sBinary; + +private: + bool checkNTLMPassword(unsigned char* pLMPassword, int nLMPasswordLen, string& sNTLMPassword); + bool startCorrecting(string sLMPassword, string& sNTLMPassword, unsigned char* pLMPassword); + void printString(unsigned char* muteThis, int length); + void setupCombinationAtPositions(int length, unsigned char* pMuteMe, unsigned char* pTempMute, int* jAtPos, bool* fullAtPos, int* sizeAtPos); + bool checkPermutations(int length, unsigned char* pTempMute, int* jAtPos, int* sizeAtPos, unsigned char* pLMPassword, string& sNTLMPassword); + + int calculateTotalCombinations(int length, int setSize); + int factorial (int num); + + bool parseHexPassword(string hexPassword, string& sPlain); + bool NormalizeHexString(string& sHash); + void ParseHash(string sHash, unsigned char* pHash, int& nHashLen); + string ByteToStr(const unsigned char* pData, int nLen); + void addToMapW(unsigned char key, unsigned char value1, unsigned char value2); + void fillMapW(); + void checkAbort(); + void writeEndStats(); +public: + bool LMPasswordCorrectUnicode(string hexPassword, unsigned char* NTLMHash, string& sNTLMPassword); + string getBinary(); +}; + diff --git a/Client Applications/rcracki_mt/md4.cpp b/Client Applications/rcracki_mt/md4.cpp index 4e402fd..a35cdb5 100644 --- a/Client Applications/rcracki_mt/md4.cpp +++ b/Client Applications/rcracki_mt/md4.cpp @@ -1,485 +1,334 @@ -/* - * This code implements the MD4 message-digest algorithm. - * "Just the reference implementation, single stage. Hardly "optimized." Though a good bit faster than libssl's MD4, as it isn't doing nearly the same amount of work." - Bitweasil - * - * little bit optimized (or at least attempted) for NTLM (unicode) by neinbrucke - */ - - -//#include -#include -#include "md4.h" - -/* MD4 Defines as per RFC reference implementation */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) -#define FF(a, b, c, d, x, s) { \ - (a) += F ((b), (c), (d)) + (x); \ - (a) = ROTATE_LEFT ((a), (s)); \ - } -#define GG(a, b, c, d, x, s) { \ - (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \ - (a) = ROTATE_LEFT ((a), (s)); \ - } -#define HH(a, b, c, d, x, s) { \ - (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \ - (a) = ROTATE_LEFT ((a), (s)); \ - } -#define S11 3 -#define S12 7 -#define S13 11 -#define S14 19 -#define S21 3 -#define S22 5 -#define S23 9 -#define S24 13 -#define S31 3 -#define S32 9 -#define S33 11 -#define S34 15 -/* End MD4 Defines */ - -void MD4_NEW( unsigned char * pData, int length, unsigned char * pDigest) -{ - // access data as 4-byte word - #define uData ((UINT4 *)pData) - #define uDigest ((UINT4 *)pDigest) - - // pad word and append bit at appropriate location - #define MD4_pad_w0() (0x00000080) - #define MD4_pad_w1(data) (((data) & 0x000000FF) | 0x00008000) - #define MD4_pad_w2(data) (((data) & 0x0000FFFF) | 0x00800000) - #define MD4_pad_w3(data) (((data) & 0x00FFFFFF) | 0x80000000) - - // For the hash working space - //__attribute__((aligned(16))) UINT4 data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - //__declspec(align(16)) UINT4 data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - UINT4 data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - - // For the output result - UINT4 a,b,c,d; - - switch (length) - { - case 0: - { - data[ 0] = MD4_pad_w0(); - - data[14] = 0; - } - break; - case 1: - { - data[ 0] = MD4_pad_w1(uData[0]); - - data[14] = 1 << 3; - } - break; - case 2: - { - data[ 0] = MD4_pad_w2(uData[0]); - - data[14] = 2 << 3; - } - break; - case 3: - { - data[ 0] = MD4_pad_w3(uData[0]); - - data[14] = 3 << 3; - } - break; - case 4: - { - data[ 0] = uData[0]; - data[ 1] = MD4_pad_w0(); - - data[14] = 4 << 3; - } - break; - case 5: - { - data[ 0] = uData[0]; - data[ 1] = MD4_pad_w1(uData[1]); - - data[14] = 5 << 3; - } - break; - case 6: - { - data[ 0] = uData[0]; - data[ 1] = MD4_pad_w2(uData[1]); - - data[14] = 6 << 3; - } - break; - case 7: - { - data[ 0] = uData[0]; - data[ 1] = MD4_pad_w3(uData[1]); - - data[14] = 7 << 3; - } - break; - case 8: - { - data[ 0] = uData[0]; - data[ 1] = uData[1]; - data[ 2] = MD4_pad_w0(); - - data[14] = 8 << 3; - } - break; - case 9: - { - data[ 0] = uData[0]; - data[ 1] = uData[1]; - data[ 2] = MD4_pad_w1(uData[2]); - - data[14] = 9 << 3; - } - break; - case 10: - { - data[ 0] = uData[0]; - data[ 1] = uData[1]; - data[ 2] = MD4_pad_w2(uData[2]); - - data[14] = 10 << 3; - } - break; - case 11: - { - data[ 0] = uData[0]; - data[ 1] = uData[1]; - data[ 2] = MD4_pad_w3(uData[2]); - - data[14] = 11 << 3; - } - break; - case 12: - { - data[ 0] = uData[0]; - data[ 1] = uData[1]; - data[ 2] = uData[2]; - data[ 3] = MD4_pad_w0(); - - data[14] = 12 << 3; - } - break; - case 13: - { - data[ 0] = uData[0]; - data[ 1] = uData[1]; - data[ 2] = uData[2]; - data[ 3] = MD4_pad_w1(uData[3]); - - data[14] = 13 << 3; - } - break; - case 14: - { - data[ 0] = uData[0]; - data[ 1] = uData[1]; - data[ 2] = uData[2]; - data[ 3] = MD4_pad_w2(uData[3]); - - data[14] = 14 << 3; - } - break; - case 15: - { - data[ 0] = uData[0]; - data[ 1] = uData[1]; - data[ 2] = uData[2]; - data[ 3] = MD4_pad_w3(uData[3]); - - data[14] = 15 << 3; - } - break; - - default: - { - length = length % 32; // lenght >= 32 not suported - - int word = length >> 2; - - int i = 0; - while (i < word) { - data[i] = uData[i]; - i++; - } - - switch (length & 0x3) { - case 0: - { - data[word] = MD4_pad_w0(); - } - break; - case 1: - { - data[word] = MD4_pad_w1(uData[word]); - } - break; - case 2: - { - data[word] = MD4_pad_w2(uData[word]); - } - break; - case 3: - { - data[word] = MD4_pad_w3(uData[word]); - } - break; - } - - data[14] = length << 3; - } - break; - } - - a = 0x67452301; - b = 0xefcdab89; - c = 0x98badcfe; - d = 0x10325476; - - /* Round 1 */ - FF (a, b, c, d, data[ 0], S11); /* 1 */ - FF (d, a, b, c, data[ 1], S12); /* 2 */ - FF (c, d, a, b, data[ 2], S13); /* 3 */ - FF (b, c, d, a, data[ 3], S14); /* 4 */ - FF (a, b, c, d, data[ 4], S11); /* 5 */ - FF (d, a, b, c, data[ 5], S12); /* 6 */ - FF (c, d, a, b, data[ 6], S13); /* 7 */ - FF (b, c, d, a, data[ 7], S14); /* 8 */ - FF (a, b, c, d, 0, S11); /* 9 */ - FF (d, a, b, c, 0, S12); /* 10 */ - FF (c, d, a, b, 0, S13); /* 11 */ - FF (b, c, d, a, 0, S14); /* 12 */ - FF (a, b, c, d, 0, S11); /* 13 */ - FF (d, a, b, c, 0, S12); /* 14 */ - FF (c, d, a, b, data[14], S13); /* 15 */ - FF (b, c, d, a, 0, S14); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, data[ 0], S21); /* 17 */ - GG (d, a, b, c, data[ 4], S22); /* 18 */ - GG (c, d, a, b, 0, S23); /* 19 */ - GG (b, c, d, a, 0, S24); /* 20 */ - GG (a, b, c, d, data[ 1], S21); /* 21 */ - GG (d, a, b, c, data[ 5], S22); /* 22 */ - GG (c, d, a, b, 0, S23); /* 23 */ - GG (b, c, d, a, 0, S24); /* 24 */ - GG (a, b, c, d, data[ 2], S21); /* 25 */ - GG (d, a, b, c, data[ 6], S22); /* 26 */ - GG (c, d, a, b, 0, S23); /* 27 */ - GG (b, c, d, a, data[14], S24); /* 28 */ - GG (a, b, c, d, data[ 3], S21); /* 29 */ - GG (d, a, b, c, data[ 7], S22); /* 30 */ - GG (c, d, a, b, 0, S23); /* 31 */ - GG (b, c, d, a, 0, S24); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, data[ 0], S31); /* 33 */ - HH (d, a, b, c, 0, S32); /* 34 */ - HH (c, d, a, b, data[ 4], S33); /* 35 */ - HH (b, c, d, a, 0, S34); /* 36 */ - HH (a, b, c, d, data[ 2], S31); /* 37 */ - HH (d, a, b, c, 0, S32); /* 38 */ - HH (c, d, a, b, data[ 6], S33); /* 39 */ - HH (b, c, d, a, data[14], S34); /* 40 */ - HH (a, b, c, d, data[ 1], S31); /* 41 */ - HH (d, a, b, c, 0, S32); /* 42 */ - HH (c, d, a, b, data[ 5], S33); /* 43 */ - HH (b, c, d, a, 0, S34); /* 44 */ - HH (a, b, c, d, data[ 3], S31); /* 45 */ - HH (d, a, b, c, 0, S32); /* 46 */ - HH (c, d, a, b, data[ 7], S33); /* 47 */ - HH (b, c, d, a, 0, S34); /* 48 */ - - // Finally, add initial values, as this is the only pass we make. - a += 0x67452301; - b += 0xefcdab89; - c += 0x98badcfe; - d += 0x10325476; - - uDigest[0] = a; - uDigest[1] = b; - uDigest[2] = c; - uDigest[3] = d; -} - -//void MD4_NEW( unsigned char * pData, int length, unsigned char * pDigest) -//{ -// // For the hash working space -// UINT4 b0,b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15; -// -// // For the output result -// UINT4 a,b,c,d; -// -// b0 = 0x00000000; -// b1 = 0x00000000; -// b2 = 0x00000000; -// b3 = 0x00000000; -// b4 = 0x00000000; -// b5 = 0x00000000; -// b6 = 0x00000000; -// b7 = 0x00000000; -// b8 = 0x00000000; -// b9 = 0x00000000; -// b10 = 0x00000000; -// b11 = 0x00000000; -// b12 = 0x00000000; -// b13 = 0x00000000; -// b14 = 0x00000000; -// b15 = 0x00000000; -// -// // LOAD DATA INTO b0 ... whatever here. -// switch (length) -// { -// case 2: -// { -// unsigned char in[4]; -// memcpy(in, pData, length); -// in[2] = 0x80; -// in[3] = 0x00; -// UINT4 * pUiIn = (UINT4 *) in; -// b0 = pUiIn[0]; -// } -// break; -// case 4: -// { -// unsigned char in[4]; -// memcpy(in, pData, length); -// UINT4 * pUiIn = (UINT4 *) in; -// b0 = pUiIn[0]; -// b1 = 0x00000080; -// } -// break; -// case 6: -// { -// unsigned char in[8]; -// memcpy(in, pData, length); -// in[6] = 0x80; -// in[7] = 0x00; -// UINT4 * pUiIn = (UINT4 *) in; -// b0 = pUiIn[0]; -// b1 = pUiIn[1]; -// } -// break; -// case 8: -// { -// unsigned char in[8]; -// memcpy(in, pData, length); -// UINT4 * pUiIn = (UINT4 *) in; -// b0 = pUiIn[0]; -// b1 = pUiIn[1]; -// b2 = 0x00000080; -// } -// break; -// case 10: -// { -// unsigned char in[12]; -// memcpy(in, pData, length); -// in[10] = 0x80; -// in[11] = 0x00; -// UINT4 * pUiIn = (UINT4 *) in; -// b0 = pUiIn[0]; -// b1 = pUiIn[1]; -// b2 = pUiIn[2]; -// } -// break; -// default: -// { -// unsigned char in[32]; -// memcpy(in, pData, length); -// in[length] = 0x80; -// memset(in + length + 1, 0, 32 - length - 1); -// UINT4 * pUiIn = (UINT4 *) in; -// b0 = pUiIn[0]; -// b1 = pUiIn[1]; -// b2 = pUiIn[2]; -// b3 = pUiIn[3]; -// b4 = pUiIn[4]; -// b5 = pUiIn[5]; -// b6 = pUiIn[6]; -// b7 = pUiIn[7]; // max 14 2byte chars (ntlm) -// b8 = pUiIn[8]; -// } -// break; -// } -// -// b14 = length << 3; -// -// a = 0x67452301; -// b = 0xefcdab89; -// c = 0x98badcfe; -// d = 0x10325476; -// -// /* Round 1 */ -// FF (a, b, c, d, b0, S11); /* 1 */ -// FF (d, a, b, c, b1, S12); /* 2 */ -// FF (c, d, a, b, b2, S13); /* 3 */ -// FF (b, c, d, a, b3, S14); /* 4 */ -// FF (a, b, c, d, b4, S11); /* 5 */ -// FF (d, a, b, c, b5, S12); /* 6 */ -// FF (c, d, a, b, b6, S13); /* 7 */ -// FF (b, c, d, a, b7, S14); /* 8 */ -// FF (a, b, c, d, 0, S11); /* 9 */ -// FF (d, a, b, c, 0, S12); /* 10 */ -// FF (c, d, a, b, 0, S13); /* 11 */ -// FF (b, c, d, a, 0, S14); /* 12 */ -// FF (a, b, c, d, 0, S11); /* 13 */ -// FF (d, a, b, c, 0, S12); /* 14 */ -// FF (c, d, a, b, b14, S13); /* 15 */ -// FF (b, c, d, a, 0, S14); /* 16 */ -// -// /* Round 2 */ -// GG (a, b, c, d, b0, S21); /* 17 */ -// GG (d, a, b, c, b4, S22); /* 18 */ -// GG (c, d, a, b, 0, S23); /* 19 */ -// GG (b, c, d, a, 0, S24); /* 20 */ -// GG (a, b, c, d, b1, S21); /* 21 */ -// GG (d, a, b, c, b5, S22); /* 22 */ -// GG (c, d, a, b, 0, S23); /* 23 */ -// GG (b, c, d, a, 0, S24); /* 24 */ -// GG (a, b, c, d, b2, S21); /* 25 */ -// GG (d, a, b, c, b6, S22); /* 26 */ -// GG (c, d, a, b, 0, S23); /* 27 */ -// GG (b, c, d, a, b14, S24); /* 28 */ -// GG (a, b, c, d, b3, S21); /* 29 */ -// GG (d, a, b, c, b7, S22); /* 30 */ -// GG (c, d, a, b, 0, S23); /* 31 */ -// GG (b, c, d, a, 0, S24); /* 32 */ -// -// /* Round 3 */ -// HH (a, b, c, d, b0, S31); /* 33 */ -// HH (d, a, b, c, 0, S32); /* 34 */ -// HH (c, d, a, b, b4, S33); /* 35 */ -// HH (b, c, d, a, 0, S34); /* 36 */ -// HH (a, b, c, d, b2, S31); /* 37 */ -// HH (d, a, b, c, 0, S32); /* 38 */ -// HH (c, d, a, b, b6, S33); /* 39 */ -// HH (b, c, d, a, b14, S34); /* 40 */ -// HH (a, b, c, d, b1, S31); /* 41 */ -// HH (d, a, b, c, 0, S32); /* 42 */ -// HH (c, d, a, b, b5, S33); /* 43 */ -// HH (b, c, d, a, 0, S34); /* 44 */ -// HH (a, b, c, d, b3, S31); /* 45 */ -// HH (d, a, b, c, 0, S32); /* 46 */ -// HH (c, d, a, b, b7, S33); /* 47 */ -// HH (b, c, d, a, 0, S34); /* 48 */ -// -// // Finally, add initial values, as this is the only pass we make. -// a += 0x67452301; -// b += 0xefcdab89; -// c += 0x98badcfe; -// d += 0x10325476; -// -// UINT4 buf[4] = { a, b, c, d}; -// memcpy(pDigest, buf, 16); -// -// return; -//} +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright Bitweasil + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + * + * This code implements the MD4 message-digest algorithm. + * "Just the reference implementation, single stage. Hardly "optimized." Though a good bit faster than libssl's MD4, as it isn't doing nearly the same amount of work." - Bitweasil + * + * little bit optimized (or at least attempted) for NTLM (unicode) by neinbrucke + */ + + +//#include +//#include +#include "md4.h" + +/* MD4 Defines as per RFC reference implementation */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) +#define FF(a, b, c, d, x, s) { \ + (a) += F ((b), (c), (d)) + (x); \ + (a) = ROTATE_LEFT ((a), (s)); \ + } +#define GG(a, b, c, d, x, s) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \ + (a) = ROTATE_LEFT ((a), (s)); \ + } +#define HH(a, b, c, d, x, s) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \ + (a) = ROTATE_LEFT ((a), (s)); \ + } +#define S11 3 +#define S12 7 +#define S13 11 +#define S14 19 +#define S21 3 +#define S22 5 +#define S23 9 +#define S24 13 +#define S31 3 +#define S32 9 +#define S33 11 +#define S34 15 +/* End MD4 Defines */ + +void MD4_NEW( unsigned char * pData, int length, unsigned char * pDigest) +{ + // access data as 4-byte word + #define uData ((UINT4 *)pData) + #define uDigest ((UINT4 *)pDigest) + + // pad word and append bit at appropriate location + #define MD4_pad_w0() (0x00000080) + #define MD4_pad_w1(data) (((data) & 0x000000FF) | 0x00008000) + #define MD4_pad_w2(data) (((data) & 0x0000FFFF) | 0x00800000) + #define MD4_pad_w3(data) (((data) & 0x00FFFFFF) | 0x80000000) + + // For the hash working space + //__attribute__((aligned(16))) UINT4 data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + //__declspec(align(16)) UINT4 data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + UINT4 data[MD4_DIGEST_LENGTH] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + // For the output result + UINT4 a,b,c,d; + + switch (length) + { + case 0: + { + data[ 0] = MD4_pad_w0(); + + data[14] = 0; + } + break; + case 1: + { + data[ 0] = MD4_pad_w1(uData[0]); + + data[14] = 1 << 3; + } + break; + case 2: + { + data[ 0] = MD4_pad_w2(uData[0]); + + data[14] = 2 << 3; + } + break; + case 3: + { + data[ 0] = MD4_pad_w3(uData[0]); + + data[14] = 3 << 3; + } + break; + case 4: + { + data[ 0] = uData[0]; + data[ 1] = MD4_pad_w0(); + + data[14] = 4 << 3; + } + break; + case 5: + { + data[ 0] = uData[0]; + data[ 1] = MD4_pad_w1(uData[1]); + + data[14] = 5 << 3; + } + break; + case 6: + { + data[ 0] = uData[0]; + data[ 1] = MD4_pad_w2(uData[1]); + + data[14] = 6 << 3; + } + break; + case 7: + { + data[ 0] = uData[0]; + data[ 1] = MD4_pad_w3(uData[1]); + + data[14] = 7 << 3; + } + break; + case 8: + { + data[ 0] = uData[0]; + data[ 1] = uData[1]; + data[ 2] = MD4_pad_w0(); + + data[14] = 8 << 3; + } + break; + case 9: + { + data[ 0] = uData[0]; + data[ 1] = uData[1]; + data[ 2] = MD4_pad_w1(uData[2]); + + data[14] = 9 << 3; + } + break; + case 10: + { + data[ 0] = uData[0]; + data[ 1] = uData[1]; + data[ 2] = MD4_pad_w2(uData[2]); + + data[14] = 10 << 3; + } + break; + case 11: + { + data[ 0] = uData[0]; + data[ 1] = uData[1]; + data[ 2] = MD4_pad_w3(uData[2]); + + data[14] = 11 << 3; + } + break; + case 12: + { + data[ 0] = uData[0]; + data[ 1] = uData[1]; + data[ 2] = uData[2]; + data[ 3] = MD4_pad_w0(); + + data[14] = 12 << 3; + } + break; + case 13: + { + data[ 0] = uData[0]; + data[ 1] = uData[1]; + data[ 2] = uData[2]; + data[ 3] = MD4_pad_w1(uData[3]); + + data[14] = 13 << 3; + } + break; + case 14: + { + data[ 0] = uData[0]; + data[ 1] = uData[1]; + data[ 2] = uData[2]; + data[ 3] = MD4_pad_w2(uData[3]); + + data[14] = 14 << 3; + } + break; + case 15: + { + data[ 0] = uData[0]; + data[ 1] = uData[1]; + data[ 2] = uData[2]; + data[ 3] = MD4_pad_w3(uData[3]); + + data[14] = 15 << 3; + } + break; + + default: + { + length = length % 32; // lenght >= 32 not suported + + int word = length >> 2; + + int i = 0; + while (i < word) { + data[i] = uData[i]; + i++; + } + + switch (length & 0x3) { + case 0: + { + data[word] = MD4_pad_w0(); + } + break; + case 1: + { + data[word] = MD4_pad_w1(uData[word]); + } + break; + case 2: + { + data[word] = MD4_pad_w2(uData[word]); + } + break; + case 3: + { + data[word] = MD4_pad_w3(uData[word]); + } + break; + } + + data[14] = length << 3; + } + break; + } + + a = 0x67452301; + b = 0xefcdab89; + c = 0x98badcfe; + d = 0x10325476; + + /* Round 1 */ + FF (a, b, c, d, data[ 0], S11); /* 1 */ + FF (d, a, b, c, data[ 1], S12); /* 2 */ + FF (c, d, a, b, data[ 2], S13); /* 3 */ + FF (b, c, d, a, data[ 3], S14); /* 4 */ + FF (a, b, c, d, data[ 4], S11); /* 5 */ + FF (d, a, b, c, data[ 5], S12); /* 6 */ + FF (c, d, a, b, data[ 6], S13); /* 7 */ + FF (b, c, d, a, data[ 7], S14); /* 8 */ + FF (a, b, c, d, 0, S11); /* 9 */ + FF (d, a, b, c, 0, S12); /* 10 */ + FF (c, d, a, b, 0, S13); /* 11 */ + FF (b, c, d, a, 0, S14); /* 12 */ + FF (a, b, c, d, 0, S11); /* 13 */ + FF (d, a, b, c, 0, S12); /* 14 */ + FF (c, d, a, b, data[14], S13); /* 15 */ + FF (b, c, d, a, 0, S14); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, data[ 0], S21); /* 17 */ + GG (d, a, b, c, data[ 4], S22); /* 18 */ + GG (c, d, a, b, 0, S23); /* 19 */ + GG (b, c, d, a, 0, S24); /* 20 */ + GG (a, b, c, d, data[ 1], S21); /* 21 */ + GG (d, a, b, c, data[ 5], S22); /* 22 */ + GG (c, d, a, b, 0, S23); /* 23 */ + GG (b, c, d, a, 0, S24); /* 24 */ + GG (a, b, c, d, data[ 2], S21); /* 25 */ + GG (d, a, b, c, data[ 6], S22); /* 26 */ + GG (c, d, a, b, 0, S23); /* 27 */ + GG (b, c, d, a, data[14], S24); /* 28 */ + GG (a, b, c, d, data[ 3], S21); /* 29 */ + GG (d, a, b, c, data[ 7], S22); /* 30 */ + GG (c, d, a, b, 0, S23); /* 31 */ + GG (b, c, d, a, 0, S24); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, data[ 0], S31); /* 33 */ + HH (d, a, b, c, 0, S32); /* 34 */ + HH (c, d, a, b, data[ 4], S33); /* 35 */ + HH (b, c, d, a, 0, S34); /* 36 */ + HH (a, b, c, d, data[ 2], S31); /* 37 */ + HH (d, a, b, c, 0, S32); /* 38 */ + HH (c, d, a, b, data[ 6], S33); /* 39 */ + HH (b, c, d, a, data[14], S34); /* 40 */ + HH (a, b, c, d, data[ 1], S31); /* 41 */ + HH (d, a, b, c, 0, S32); /* 42 */ + HH (c, d, a, b, data[ 5], S33); /* 43 */ + HH (b, c, d, a, 0, S34); /* 44 */ + HH (a, b, c, d, data[ 3], S31); /* 45 */ + HH (d, a, b, c, 0, S32); /* 46 */ + HH (c, d, a, b, data[ 7], S33); /* 47 */ + HH (b, c, d, a, 0, S34); /* 48 */ + + // Finally, add initial values, as this is the only pass we make. + a += 0x67452301; + b += 0xefcdab89; + c += 0x98badcfe; + d += 0x10325476; + + uDigest[0] = a; + uDigest[1] = b; + uDigest[2] = c; + uDigest[3] = d; +} diff --git a/Client Applications/rcracki_mt/md4.h b/Client Applications/rcracki_mt/md4.h index 8a5c428..fd11278 100644 --- a/Client Applications/rcracki_mt/md4.h +++ b/Client Applications/rcracki_mt/md4.h @@ -1,10 +1,35 @@ -#ifndef MD4_H -#define MD4_H - -//typedef unsigned long uint32; -typedef unsigned long UINT4; - -//Main function -void MD4_NEW( unsigned char * buf, int len, unsigned char * pDigest); - -#endif /* !MD4_H */ +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright Bitweasil + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifndef MD4_H +#define MD4_H + +#include "global.h" + +#define MD4_DIGEST_LENGTH 16 + +//Main function +void MD4_NEW( unsigned char * buf, int len, unsigned char * pDigest); + +#endif /* !MD4_H */ diff --git a/Client Applications/rcracki_mt/rcrackiThread.cpp b/Client Applications/rcracki_mt/rcrackiThread.cpp index 9dc1f14..8123ba5 100644 --- a/Client Applications/rcracki_mt/rcrackiThread.cpp +++ b/Client Applications/rcracki_mt/rcrackiThread.cpp @@ -1,239 +1,247 @@ -#ifdef _WIN32 - #pragma warning(disable : 4786 4267 4018) -#endif - -#include "rcrackiThread.h" - -// create job for pre-calculation -rcrackiThread::rcrackiThread(unsigned char* TargetHash, int thread_id, int nRainbowChainLen, int thread_count, uint64* pStartPosIndexE) -{ - t_TargetHash = TargetHash; - t_nRainbowChainLen = nRainbowChainLen; - t_ID = thread_id; - t_count = thread_count; - t_pStartPosIndexE = pStartPosIndexE; - t_nChainWalkStep = 0; - falseAlarmChecker = false; - falseAlarmCheckerO = false; -} - -// create job for false alarm checking -rcrackiThread::rcrackiThread(unsigned char* pHash) -{ - falseAlarmChecker = true; - falseAlarmCheckerO = false; - t_pChainsFound.clear(); - t_nGuessedPoss.clear(); - t_pHash = pHash; - t_nChainWalkStepDueToFalseAlarm = 0; - t_nFalseAlarm = 0; - foundHash = false; -} - -// create job for false alarm checking OLD format -rcrackiThread::rcrackiThread(unsigned char* pHash, bool oldFormat) -{ - falseAlarmChecker = true; - falseAlarmCheckerO = true; - t_pChainsFoundO.clear(); - t_nGuessedPoss.clear(); - t_pHash = pHash; - t_nChainWalkStepDueToFalseAlarm = 0; - t_nFalseAlarm = 0; - foundHash = false; -} - - -void rcrackiThread::AddAlarmCheck(RainbowChain* pChain, int nGuessedPos) -{ - t_pChainsFound.push_back(pChain); - t_nGuessedPoss.push_back(nGuessedPos); -} - -void rcrackiThread::AddAlarmCheckO(RainbowChainO* pChain, int nGuessedPos) -{ - t_pChainsFoundO.push_back(pChain); - t_nGuessedPoss.push_back(nGuessedPos); -} - -// Windows (beginthreadex) way of threads -//unsigned __stdcall rcrackiThread::rcrackiThreadStaticEntryPoint(void * pThis) -//{ -// rcrackiThread* pTT = (rcrackiThread*)pThis; -// pTT->rcrackiThreadEntryPoint(); -// _endthreadex( 2 ); -// return 2; -//} - -// entry point for the posix thread -void * rcrackiThread::rcrackiThreadStaticEntryPointPthread(void * pThis) -{ - rcrackiThread* pTT = (rcrackiThread*)pThis; - pTT->rcrackiThreadEntryPoint(); - pthread_exit(NULL); - return NULL; -} - -// start processing of jobs -void rcrackiThread::rcrackiThreadEntryPoint() -{ - if (falseAlarmChecker) { - if (falseAlarmCheckerO) { - CheckAlarmO(); - } - else { - CheckAlarm(); - } - } - else { - PreCalculate(); - } -} - -uint64 rcrackiThread::GetIndex(int nPos) -{ - uint64 t_index = t_vStartPosIndexE[nPos - t_ID]; - return t_index; -} - -int rcrackiThread::GetChainWalkStep() -{ - return t_nChainWalkStep; -} - -int rcrackiThread::GetIndexCount() -{ - return t_vStartPosIndexE.size(); -} - -rcrackiThread::~rcrackiThread(void) -{ -} - -void rcrackiThread::PreCalculate() -{ - for (t_nPos = t_nRainbowChainLen - 2 - t_ID; t_nPos >= 0; t_nPos -= t_count) - { - t_cwc.SetHash(t_TargetHash); - t_cwc.HashToIndex(t_nPos); - int i; - for (i = t_nPos + 1; i <= t_nRainbowChainLen - 2; i++) - //for (i = t_nPos + 1; i <= 10; i++) - { - t_cwc.IndexToPlain(); - t_cwc.PlainToHash(); - t_cwc.HashToIndex(i); - } - t_pStartPosIndexE[t_nPos] = t_cwc.GetIndex(); - t_nChainWalkStep += t_nRainbowChainLen - 2 - t_nPos; - } -} - -void rcrackiThread::CheckAlarm() -{ - int i; - for (i = 0; i < t_pChainsFound.size(); i++) - { - RainbowChain* t_pChain = t_pChainsFound[i]; - int t_nGuessedPos = t_nGuessedPoss[i]; - - CChainWalkContext cwc; - //uint64 nIndexS = t_pChain->nIndexS & 0x0000FFFFFFFFFFFF; // for first 6 bytes - //uint64 nIndexS = t_pChain->nIndexS >> 16; - uint64 nIndexS = t_pChain->nIndexS & 0x0000FFFFFFFFFFFFULL; // for first 6 bytes - cwc.SetIndex(nIndexS); - //cwc.SetIndex(t_pChain->nIndexS); - int nPos; - for (nPos = 0; nPos < t_nGuessedPos; nPos++) - { - cwc.IndexToPlain(); - cwc.PlainToHash(); - cwc.HashToIndex(nPos); - } - cwc.IndexToPlain(); - cwc.PlainToHash(); - if (cwc.CheckHash(t_pHash)) - { - t_Hash = cwc.GetHash(); - t_Plain = cwc.GetPlain(); - t_Binary = cwc.GetBinary(); - - foundHash = true; - break; - } - else { - foundHash = false; - t_nChainWalkStepDueToFalseAlarm += t_nGuessedPos + 1; - t_nFalseAlarm++; - } - } -} - -void rcrackiThread::CheckAlarmO() -{ - int i; - for (i = 0; i < t_pChainsFoundO.size(); i++) - { - RainbowChainO* t_pChain = t_pChainsFoundO[i]; - int t_nGuessedPos = t_nGuessedPoss[i]; - - CChainWalkContext cwc; - - uint64 nIndexS = t_pChain->nIndexS; - cwc.SetIndex(nIndexS); - - int nPos; - for (nPos = 0; nPos < t_nGuessedPos; nPos++) - { - cwc.IndexToPlain(); - cwc.PlainToHash(); - cwc.HashToIndex(nPos); - } - cwc.IndexToPlain(); - cwc.PlainToHash(); - if (cwc.CheckHash(t_pHash)) - { - t_Hash = cwc.GetHash(); - t_Plain = cwc.GetPlain(); - t_Binary = cwc.GetBinary(); - - foundHash = true; - break; - } - else { - foundHash = false; - t_nChainWalkStepDueToFalseAlarm += t_nGuessedPos + 1; - t_nFalseAlarm++; - } - } -} - -bool rcrackiThread::FoundHash() -{ - return foundHash; -} - -int rcrackiThread::GetChainWalkStepDueToFalseAlarm() -{ - return t_nChainWalkStepDueToFalseAlarm; -} - -int rcrackiThread::GetnFalseAlarm() -{ - return t_nFalseAlarm; -} - -string rcrackiThread::GetHash() -{ - return t_Hash; -} - -string rcrackiThread::GetPlain() -{ - return t_Plain; -} - -string rcrackiThread::GetBinary() -{ - return t_Binary; -} +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifdef _WIN32 + #pragma warning(disable : 4786 4267 4018) +#endif + +#include "rcrackiThread.h" + +// create job for pre-calculation +rcrackiThread::rcrackiThread(unsigned char* TargetHash, int thread_id, int nRainbowChainLen, int thread_count, uint64* pStartPosIndexE) +{ + t_TargetHash = TargetHash; + t_nRainbowChainLen = nRainbowChainLen; + t_ID = thread_id; + t_count = thread_count; + t_pStartPosIndexE = pStartPosIndexE; + t_nChainWalkStep = 0; + falseAlarmChecker = false; + falseAlarmCheckerO = false; +} + +// create job for false alarm checking +rcrackiThread::rcrackiThread(unsigned char* pHash, bool oldFormat) +{ + falseAlarmChecker = true; + falseAlarmCheckerO = oldFormat; + t_pChainsFound.clear(); + t_nGuessedPoss.clear(); + t_pHash = pHash; + t_nChainWalkStepDueToFalseAlarm = 0; + t_nFalseAlarm = 0; + foundHash = false; +} + +void rcrackiThread::AddAlarmCheck(RainbowChain* pChain, int nGuessedPos) +{ + t_pChainsFound.push_back(pChain); + t_nGuessedPoss.push_back(nGuessedPos); +} + +void rcrackiThread::AddAlarmCheckO(RainbowChainO* pChain, int nGuessedPos) +{ + t_pChainsFoundO.push_back(pChain); + t_nGuessedPoss.push_back(nGuessedPos); +} + +// Windows (beginthreadex) way of threads +//unsigned __stdcall rcrackiThread::rcrackiThreadStaticEntryPoint(void * pThis) +//{ +// rcrackiThread* pTT = (rcrackiThread*)pThis; +// pTT->rcrackiThreadEntryPoint(); +// _endthreadex( 2 ); +// return 2; +//} + +// entry point for the posix thread +void * rcrackiThread::rcrackiThreadStaticEntryPointPthread(void * pThis) +{ + rcrackiThread* pTT = (rcrackiThread*)pThis; + pTT->rcrackiThreadEntryPoint(); + pthread_exit(NULL); + return NULL; +} + +// start processing of jobs +void rcrackiThread::rcrackiThreadEntryPoint() +{ + if (falseAlarmChecker) { + if (falseAlarmCheckerO) { + CheckAlarmO(); + } + else { + CheckAlarm(); + } + } + else { + PreCalculate(); + } +} + +uint64 rcrackiThread::GetIndex(int nPos) +{ + uint64 t_index = t_vStartPosIndexE[nPos - t_ID]; + return t_index; +} + +int rcrackiThread::GetChainWalkStep() +{ + return t_nChainWalkStep; +} + +int rcrackiThread::GetIndexCount() +{ + return t_vStartPosIndexE.size(); +} + +rcrackiThread::~rcrackiThread(void) +{ +} + +void rcrackiThread::PreCalculate() +{ + for (t_nPos = t_nRainbowChainLen - 2 - t_ID; t_nPos >= 0; t_nPos -= t_count) + { + t_cwc.SetHash(t_TargetHash); + t_cwc.HashToIndex(t_nPos); + int i; + for (i = t_nPos + 1; i <= t_nRainbowChainLen - 2; i++) + { + t_cwc.IndexToPlain(); + t_cwc.PlainToHash(); + t_cwc.HashToIndex(i); + } + t_pStartPosIndexE[t_nPos] = t_cwc.GetIndex(); + t_nChainWalkStep += t_nRainbowChainLen - 2 - t_nPos; + } +} + +void rcrackiThread::CheckAlarm() +{ + UINT4 i; + for (i = 0; i < t_pChainsFound.size(); i++) + { + RainbowChain* t_pChain = t_pChainsFound[i]; + int t_nGuessedPos = t_nGuessedPoss[i]; + + CChainWalkContext cwc; + //uint64 nIndexS = t_pChain->nIndexS & 0x0000FFFFFFFFFFFF; // for first 6 bytes + //uint64 nIndexS = t_pChain->nIndexS >> 16; + uint64 nIndexS = t_pChain->nIndexS & 0x0000FFFFFFFFFFFFULL; // for first 6 bytes + cwc.SetIndex(nIndexS); + //cwc.SetIndex(t_pChain->nIndexS); + int nPos; + for (nPos = 0; nPos < t_nGuessedPos; nPos++) + { + cwc.IndexToPlain(); + cwc.PlainToHash(); + cwc.HashToIndex(nPos); + } + cwc.IndexToPlain(); + cwc.PlainToHash(); + if (cwc.CheckHash(t_pHash)) + { + t_Hash = cwc.GetHash(); + t_Plain = cwc.GetPlain(); + t_Binary = cwc.GetBinary(); + + foundHash = true; + break; + } + else { + foundHash = false; + t_nChainWalkStepDueToFalseAlarm += t_nGuessedPos + 1; + t_nFalseAlarm++; + } + } +} + +void rcrackiThread::CheckAlarmO() +{ + UINT4 i; + for (i = 0; i < t_pChainsFoundO.size(); i++) + { + RainbowChainO* t_pChain = t_pChainsFoundO[i]; + int t_nGuessedPos = t_nGuessedPoss[i]; + + CChainWalkContext cwc; + + uint64 nIndexS = t_pChain->nIndexS; + cwc.SetIndex(nIndexS); + + int nPos; + for (nPos = 0; nPos < t_nGuessedPos; nPos++) + { + cwc.IndexToPlain(); + cwc.PlainToHash(); + cwc.HashToIndex(nPos); + } + cwc.IndexToPlain(); + cwc.PlainToHash(); + if (cwc.CheckHash(t_pHash)) + { + t_Hash = cwc.GetHash(); + t_Plain = cwc.GetPlain(); + t_Binary = cwc.GetBinary(); + + foundHash = true; + break; + } + else { + foundHash = false; + t_nChainWalkStepDueToFalseAlarm += t_nGuessedPos + 1; + t_nFalseAlarm++; + } + } +} + +bool rcrackiThread::FoundHash() +{ + return foundHash; +} + +int rcrackiThread::GetChainWalkStepDueToFalseAlarm() +{ + return t_nChainWalkStepDueToFalseAlarm; +} + +int rcrackiThread::GetnFalseAlarm() +{ + return t_nFalseAlarm; +} + +string rcrackiThread::GetHash() +{ + return t_Hash; +} + +string rcrackiThread::GetPlain() +{ + return t_Plain; +} + +string rcrackiThread::GetBinary() +{ + return t_Binary; +} diff --git a/Client Applications/rcracki_mt/rcrackiThread.h b/Client Applications/rcracki_mt/rcrackiThread.h index 74b96dc..c5abeb4 100644 --- a/Client Applications/rcracki_mt/rcrackiThread.h +++ b/Client Applications/rcracki_mt/rcrackiThread.h @@ -1,60 +1,85 @@ -#pragma once -#include "ChainWalkContext.h" -#include "Public.h" -#include "HashSet.h" -//#include -#include - -class rcrackiThread -{ -private: - unsigned char* t_TargetHash; - int t_nPos; - int t_nRainbowChainLen; - CChainWalkContext t_cwc; - vector t_vStartPosIndexE; - int t_ID; - int t_count; - uint64* t_pStartPosIndexE; - int t_nChainWalkStep; - bool falseAlarmChecker; - bool falseAlarmCheckerO; - vector t_pChainsFound; - vector t_pChainsFoundO; - vector t_nGuessedPoss; - unsigned char* t_pHash; - bool foundHash; - int t_nChainWalkStepDueToFalseAlarm; - int t_nFalseAlarm; - string t_Hash; - string t_Plain; - string t_Binary; - -public: - rcrackiThread(unsigned char* TargetHash, int thread_id, int nRainbowChainLen, int thread_count, uint64* pStartPosIndexE); - rcrackiThread(unsigned char* pHash); - rcrackiThread(unsigned char* pHash, bool oldFormat); - rcrackiThread(void); - ~rcrackiThread(void); - - //void SetWork(unsigned char* TargetHash, int nPos, int nRainbowChainLen); - //static unsigned __stdcall rcrackiThread::rcrackiThreadStaticEntryPoint(void * pThis); - static void * rcrackiThreadStaticEntryPointPthread(void * pThis); - int GetIndexCount(); - int GetChainWalkStep(); - uint64 GetIndex(int nPos); - bool FoundHash(); - void AddAlarmCheck(RainbowChain* pChain, int nGuessedPos); - void AddAlarmCheckO(RainbowChainO* pChain, int nGuessedPos); - int GetChainWalkStepDueToFalseAlarm(); - int GetnFalseAlarm(); - string GetHash(); - string GetPlain(); - string GetBinary(); - -private: - void rcrackiThreadEntryPoint(); - void PreCalculate(); - void CheckAlarm(); - void CheckAlarmO(); -}; +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifdef _WIN32 + #pragma once +#endif + +#include "ChainWalkContext.h" +#include "Public.h" +#include "HashSet.h" +//#include +#include + +class rcrackiThread +{ +private: + unsigned char* t_TargetHash; + int t_nPos; + int t_nRainbowChainLen; + CChainWalkContext t_cwc; + vector t_vStartPosIndexE; + int t_ID; + int t_count; + uint64* t_pStartPosIndexE; + int t_nChainWalkStep; + bool falseAlarmChecker; + bool falseAlarmCheckerO; + vector t_pChainsFound; + vector t_pChainsFoundO; + vector t_nGuessedPoss; + unsigned char* t_pHash; + bool foundHash; + int t_nChainWalkStepDueToFalseAlarm; + int t_nFalseAlarm; + string t_Hash; + string t_Plain; + string t_Binary; + +public: + rcrackiThread(unsigned char* TargetHash, int thread_id, int nRainbowChainLen, int thread_count, uint64* pStartPosIndexE); + rcrackiThread(unsigned char* pHash, bool oldFormat = false); + rcrackiThread(void); + ~rcrackiThread(void); + + //void SetWork(unsigned char* TargetHash, int nPos, int nRainbowChainLen); + //static unsigned __stdcall rcrackiThread::rcrackiThreadStaticEntryPoint(void * pThis); + static void * rcrackiThreadStaticEntryPointPthread(void * pThis); + int GetIndexCount(); + int GetChainWalkStep(); + uint64 GetIndex(int nPos); + bool FoundHash(); + void AddAlarmCheck(RainbowChain* pChain, int nGuessedPos); + void AddAlarmCheckO(RainbowChainO* pChain, int nGuessedPos); + int GetChainWalkStepDueToFalseAlarm(); + int GetnFalseAlarm(); + string GetHash(); + string GetPlain(); + string GetBinary(); + +private: + void rcrackiThreadEntryPoint(); + void PreCalculate(); + void CheckAlarm(); + void CheckAlarmO(); +}; diff --git a/Client Applications/rcracki_mt/rcracki_mt.exe b/Client Applications/rcracki_mt/rcracki_mt.exe deleted file mode 100644 index 2de1ad6dab4e427c51b94e67f30e065d19061c19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 289280 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%0AF+s)fM~OC%&bm~C&}4y_ z`+m>N?j|7i>GSFD{k$)HviIJZGc#w-oH=vm%$XUnHwMsK1T^6X=*YZ$Gnb{a*xVTO zcO^EUNw#xz$OwS*&tlNu75ad_WVGzXnj3^dgnx5^(Rar%q@vLdWkd}Nevt|SV8EcM zVLtGkaaK(6C(;zZDaGXn2PqM zks_jib96`}D`8K^ph+_abf$o&SU|sq2dC}}c$vj2W6%@|1%Bsx0|9&@Xs{V zTQTT=rlEcogQgZlvu5CdW~F9Cvs&=rEOQogFJV85K~n^v%>tTY0lfte&N8&}HH%G( zK~n^vH{*f4DHhPH@c?wx=_WA-O%Z@zC7>$>bZ!(ATJFPj!LSMgME{vBbYu(d!p5Nind2}7_8+*3o!gc_#ddC&3_SeX8rj@0 z;WC=Nt1K8zmfN&-i||s+@&!^jgMJ;waP4cIgtvMQ0F@(!UHvH5z7mRAnt;`CoM{xJ z`HqOFot>040 ztoOVr^^uhB6gm(Ur~j%hmXk-9&s1iCn2%eXkJE9?575UPi&lWMg-_v#(^GV0vy0*6 zOm+l0p~@)!GADt85U3cN1yz6_h5I6w&?2WV@becc2Oi#cKslo0 zwr~g+wntCojRGYci~bpGHsk?Ks~YCv<{2r|&i-^9oU$Lv?|msHkt5LS?=us!*8>z< z_IPnr1I|1vL&Y#i@$=ay#F`LC&EhOG(SC0aMDcsim%a47)uPDxvT4A)f6oUU$&2%4 zw*x=LpUw}I#Tl#Vp?CS2vf_-@b(k_EGIplyqb`u%f4Ng-!QXN}bCp~~J$sD7GXe$n zr8tDayN=`zouGy@G1;DpfC8Q-i9L$~;@C_ISVz6Qm;y{}Dgwah zv!9DQCwRy~9@2|n4Tc$YD~%xaTN#BWBcp89O$RSd zRM+KF(8hfFeY%i-x6)WwzqOeEmf)A)iDw_!KK7=xeohiB1$vC@r-wMbeBi0^84;SS z&dd(X5HwlePm>93-VoII4Wfw~HJ)H(_fWu-L=z&?ggACP1^j_%GKT_8?0N*K&-c?} zJ*UOC)(K z4jdXniEuZ^4=5R^k4rY!zes4U-+|xy^)Jx-FQ3IP?-LnOnv3I0(wYXG3H`6q`_y0} zZ05_m%Y_@KF-

>0n1}Q2Bcj0<}%Z(EBH1ZO?ujUEhdY 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 J0B2_g{sz#*N234$ diff --git a/Client Applications/rcracki_mt/rcracki_mt.vcproj b/Client Applications/rcracki_mt/rcracki_mt.vcproj index 53f7498..b377639 100644 --- a/Client Applications/rcracki_mt/rcracki_mt.vcproj +++ b/Client Applications/rcracki_mt/rcracki_mt.vcproj @@ -3,10 +3,10 @@ ProjectType="Visual C++" Version="9,00" Name="rcracki_mt" - ProjectGUID="{D484ABA1-C117-4AB1-B361-22E5EA62FA00}" + ProjectGUID="{966DA4B4-E13C-449D-9A93-303C6FEA25C4}" RootNamespace="rcracki_mt" Keyword="Win32Proj" - TargetFrameworkVersion="196613" + TargetFrameworkVersion="131072" > + + + + @@ -245,15 +251,15 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + + diff --git a/Client Applications/rcracki_mt/sha1.cpp b/Client Applications/rcracki_mt/sha1.cpp index 339ebb9..c1af774 100644 --- a/Client Applications/rcracki_mt/sha1.cpp +++ b/Client Applications/rcracki_mt/sha1.cpp @@ -1,355 +1,348 @@ -//#include -#if defined(WIN32) -#include -#endif -#include - - - -#define SHA1CircularShift(bits,word) (((word) << (bits)) | ((word) >> (32-(bits)))) - -// this rotate isn't faster with me -#if defined(WIN32) -#define ROTATE(a,n) _lrotl(a,n) -#else -#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) -#endif - -/* A nice byte order reversal from Wei Dai */ -#if defined(WIN32) -/* 5 instructions with rotate instruction, else 9 */ -#define Endian_Reverse32(a) \ - { \ - unsigned long l=(a); \ - (a)=((ROTATE(l,8)&0x00FF00FF)|(ROTATE(l,24)&0xFF00FF00)); \ - } -#else -/* 6 instructions with rotate instruction, else 8 */ -#define Endian_Reverse32(a) \ - { \ - unsigned long l=(a); \ - l=(((l&0xFF00FF00)>>8L)|((l&0x00FF00FF)<<8L)); \ - (a)=ROTATE(l,16L); \ - } -#endif - -#define F_00_19(b,c,d) ((((c) ^ (d)) & (b)) ^ (d)) -#define F_20_39(b,c,d) ((b) ^ (c) ^ (d)) -#define F_40_59(b,c,d) (((b) & (c)) | (((b)|(c)) & (d))) -#define F_60_79(b,c,d) F_20_39(b,c,d) - -#define K0 0x5A827999 -#define K1 0x6ED9EBA1 -#define K2 0x8F1BBCDC -#define K3 0xCA62C1D6 - -#define H0 0x67452301 -#define H1 0xEFCDAB89 -#define H2 0x98BADCFE -#define H3 0x10325476 -#define H4 0xC3D2E1F0 - -typedef unsigned int UINT4; -#define SHA1HashSize 20 - -void SHA1_NEW( unsigned char * pData, int length, unsigned char * pDigest) -{ - if (length > 16) - return; - - UINT4 Message_Block_Index = 0; - - union - { - unsigned char Message_Block[64]; - UINT4 Message_Block_W[16]; - }; - - Message_Block_W[0] = 0x00000000; - Message_Block_W[1] = 0x00000000; - Message_Block_W[2] = 0x00000000; - Message_Block_W[3] = 0x00000000; - Message_Block_W[4] = 0x00000000; - - UINT4 Intermediate_Hash[5] = { H0, H1, H2, H3, H4 }; - - memcpy(Message_Block, pData, length); - Message_Block_Index += length; - - //padMessage - Message_Block[length] = 0x80; - - UINT4 W_15 = length << 3; - - int t; /* Loop counter */ - UINT4 temp; /* Temporary word value */ - UINT4 W[80]; /* Word sequence */ - UINT4 A, B, C, D, E; /* Word buffers */ - - /* - * Initialize the first 16 words in the array W - */ - - #define INIT_OLD(x) \ - W[x] = (Message_Block[(x) * 4] << 24) | \ - (Message_Block[(x) * 4 + 1] << 16) | \ - (Message_Block[(x) * 4 +2] << 8) | \ - (Message_Block[(x) * 4 +3]) - - #define INIT(x) W[x] = Message_Block_W[x]; - - #define INIT_NULL(x) W[x] = 0; - - - Endian_Reverse32(Message_Block_W[0]); - INIT(0); - - #define INIT_NULL_1_14 \ - INIT_NULL(1); INIT_NULL_2_14; - - #define INIT_NULL_2_14 \ - INIT_NULL(2); INIT_NULL_3_14; - - #define INIT_NULL_3_14 \ - INIT_NULL(3); INIT_NULL_4_14; - - #define INIT_NULL_4_14 \ - INIT_NULL(4); INIT_NULL_5_14; - - #define INIT_NULL_5_14 \ - INIT_NULL(5); INIT_NULL(6); INIT_NULL(7); \ - INIT_NULL(8); INIT_NULL(9); INIT_NULL(10); INIT_NULL(11); \ - INIT_NULL(12); INIT_NULL(13); INIT_NULL(14); - - #define ROTATE1_NULL_1_14 \ - ROTATE1_NULL; ROTATE1_NULL_2_14; - - #define ROTATE1_NULL_2_14 \ - ROTATE1_NULL; ROTATE1_NULL_3_14; - - #define ROTATE1_NULL_3_14 \ - ROTATE1_NULL; ROTATE1_NULL_4_14; - - #define ROTATE1_NULL_4_14 \ - ROTATE1_NULL; ROTATE1_NULL_5_14; - - #define ROTATE1_NULL_5_14 \ - ROTATE1_NULL; ROTATE1_NULL; ROTATE1_NULL; \ - ROTATE1_NULL; ROTATE1_NULL; ROTATE1_NULL; ROTATE1_NULL; \ - ROTATE1_NULL; ROTATE1_NULL; ROTATE1_NULL; - - - #define EXPAND(t) \ - W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); \ - - #define EXPAND_3(t) W[t] = SHA1CircularShift(1,W[t-3]); - #define EXPAND_16(t) W[t] = SHA1CircularShift(1,W[t-16]); - #define EXPAND_3_8(t) W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8]); - - - if (length < 4) { - INIT_NULL_1_14; - W[15] = W_15; - EXPAND_16(16); - W[17] = 0; - W[18] = W_15<<1; - } - else if (length < 8) { - Endian_Reverse32(Message_Block_W[1]); - INIT(1); - INIT_NULL_2_14; - W[15] = W_15; - EXPAND_16(16); - EXPAND_16(17); - W[18] = W_15<<1; - } - else { - Endian_Reverse32(Message_Block_W[1]); - Endian_Reverse32(Message_Block_W[2]); - Endian_Reverse32(Message_Block_W[3]); - Endian_Reverse32(Message_Block_W[4]); - INIT(1); INIT(2); INIT(3); INIT(4); - INIT_NULL_5_14; - W[15] = W_15; - EXPAND(16); - EXPAND(17); - EXPAND(18); - } - - - if (length < 12) { - EXPAND_3(19); - } - else { - EXPAND(19); - } - - if (length < 16) { - EXPAND_3(20); - } - else { - EXPAND(20); - } - EXPAND_3(21); EXPAND_3(22); - EXPAND(23); - - EXPAND(24); EXPAND(25); EXPAND_3_8(26); EXPAND_3_8(27); - EXPAND(28); EXPAND(29); EXPAND(30); EXPAND(31); - EXPAND(32); EXPAND(33); EXPAND(34); EXPAND(35); - EXPAND(36); EXPAND(37); EXPAND(38); EXPAND(39); - EXPAND(40); EXPAND(41); EXPAND(42); EXPAND(43); - EXPAND(44); EXPAND(45); EXPAND(46); EXPAND(47); - EXPAND(48); EXPAND(49); EXPAND(50); EXPAND(51); - EXPAND(52); EXPAND(53); EXPAND(54); EXPAND(55); - EXPAND(56); EXPAND(57); EXPAND(58); EXPAND(59); - EXPAND(60); EXPAND(61); EXPAND(62); EXPAND(63); - EXPAND(64); EXPAND(65); EXPAND(66); EXPAND(67); - EXPAND(68); EXPAND(69); EXPAND(70); EXPAND(71); - EXPAND(72); EXPAND(73); EXPAND(74); EXPAND(75); - EXPAND(76); EXPAND(77); EXPAND(78); EXPAND(79); - - - #define ROTATE1(t) \ - temp = SHA1CircularShift(5,A) + F_00_19(B,C,D) + E + W[t] + K0; \ - E = D; D = C; \ - C = SHA1CircularShift(30,B); \ - B = A; A = temp; \ - - #define ROTATE1_NEW(a, b, c, d, e, x) \ - e += SHA1CircularShift(5,a) + F_00_19(b,c,d) + x + K0; \ - b = SHA1CircularShift(30,b); - - #define ROTATE1_W(w) \ - temp = SHA1CircularShift(5,A) + F_00_19(B,C,D) + E + w + K0; \ - E = D; D = C; \ - C = SHA1CircularShift(30,B); \ - B = A; A = temp; - - #define ROTATE1_NULL \ - temp = SHA1CircularShift(5,A) + F_00_19(B,C,D) + E + K0; \ - E = D; D = C; \ - C = SHA1CircularShift(30,B); \ - B = A; A = temp; \ - - #define ROTATE2_NEW(a, b, c, d, e, x) \ - e += SHA1CircularShift(5,a) + F_20_39(b,c,d) + x + K1; \ - b = SHA1CircularShift(30,b); - - #define ROTATE2(t) \ - temp = SHA1CircularShift(5,A) + F_20_39(B,C,D) + E + W[t] + K1; \ - E = D; D = C; \ - C = SHA1CircularShift(30,B); \ - B = A; A = temp; - - #define ROTATE2_W(w) \ - temp = SHA1CircularShift(5,A) + F_20_39(B,C,D) + E + w + K1; \ - E = D; D = C; \ - C = SHA1CircularShift(30,B); \ - B = A; A = temp; - - #define ROTATE3(t) \ - temp = SHA1CircularShift(5,A) + F_40_59(B,C,D) + E + W[t] + K2; \ - E = D; D = C; \ - C = SHA1CircularShift(30,B); \ - B = A; A = temp; - - #define ROTATE4(t) \ - temp = SHA1CircularShift(5,A) + F_60_79(B,C,D) + E + W[t] + K3; \ - E = D; D = C; \ - C = SHA1CircularShift(30,B); \ - B = A; A = temp; - - A = H0; - B = H1; - C = H2; - D = H3; - E = H4; - - - E = H2; - //D = 2079550178; - //C = 1506887872; - B = 2679412915 + W[0]; - if (length < 4) { - A = SHA1CircularShift(5,B) + 1722862861; - } - else { - A = SHA1CircularShift(5,B) + 1722862861 + W[1]; - } - - if (length < 8) { - temp = SHA1CircularShift(5,A) + ((((1506887872) ^ (2079550178)) & (B)) ^ (2079550178)) + H2 + K0; - } - else { - temp = SHA1CircularShift(5,A) + (((572662306) & (B)) ^ (2079550178)) + H2 + K0 + W[2]; - } - C = SHA1CircularShift(30,B); //SHA1CircularShift(30,(2679412915 + W[0])); - B = A; - A = temp; - - if (length < 12) { - temp = SHA1CircularShift(5,A) + ((((C) ^ (1506887872)) & (B)) ^ (1506887872)) + 2079550178 + K0; - } - else { - temp = SHA1CircularShift(5,A) + ((((C) ^ (1506887872)) & (B)) ^ (1506887872)) + 2079550178 + K0 + W[3]; - } - E = 1506887872; - D = C; - C = SHA1CircularShift(30,B); - B = A; - A = temp; - - if (length < 16) { - temp = SHA1CircularShift(5,A) + F_00_19(B,C,D) + 1506887872 + K0; - } - else { - temp = SHA1CircularShift(5,A) + F_00_19(B,C,D) + 1506887872 + K0 + W[4]; - } - E = D; - D = C; - C = SHA1CircularShift(30,B); - B = A; - A = temp; - - ROTATE1_NULL_5_14; - - ROTATE1_NEW( A, B, C, D, E, W_15 ); - ROTATE1_NEW( E, A, B, C, D, W[16] ); - ROTATE1_NEW( D, E, A, B, C, W[17] ); - ROTATE1_NEW( C, D, E, A, B, W[18] ); - ROTATE1_NEW( B, C, D, E, A, W[19] ); - - for(t = 20; t < 40; t++) - { - if (t == 21 && length < 8) { - ROTATE2_W((length<<5)); // *32 - } - else { - ROTATE2(t); - } - } - - for(t = 40; t < 60; t++) - { - ROTATE3(t); - } - - for(t = 60; t < 80; t++) - { - ROTATE4(t); - } - - Intermediate_Hash[0] += A; - Intermediate_Hash[1] += B; - Intermediate_Hash[2] += C; - Intermediate_Hash[3] += D; - Intermediate_Hash[4] += E; - - Endian_Reverse32(Intermediate_Hash[0]); - Endian_Reverse32(Intermediate_Hash[1]); - Endian_Reverse32(Intermediate_Hash[2]); - Endian_Reverse32(Intermediate_Hash[3]); - Endian_Reverse32(Intermediate_Hash[4]); - - memcpy(pDigest, Intermediate_Hash, 20); -} +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright Martin Westergaard Jørgensen + * Copyright Wei Dai + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +//#include +#if defined(_WIN32) + #include +#endif + +#include + +#include "sha1.h" + +#define SHA1CircularShift(bits,word) (((word) << (bits)) | ((word) >> (32-(bits)))) + +// this rotate isn't faster with me +#if defined(_WIN32) + #define ROTATE(a,n) _lrotl(a,n) +#else + #define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) +#endif + +/* A nice byte order reversal from Wei Dai */ +#if defined(_WIN32) +/* 5 instructions with rotate instruction, else 9 */ +#define Endian_Reverse32(a) \ + { \ + unsigned long l=(a); \ + (a)=((ROTATE(l,8)&0x00FF00FF)|(ROTATE(l,24)&0xFF00FF00)); \ + } +#else +/* 6 instructions with rotate instruction, else 8 */ +#define Endian_Reverse32(a) \ + { \ + unsigned long l=(a); \ + l=(((l&0xFF00FF00)>>8L)|((l&0x00FF00FF)<<8L)); \ + (a)=ROTATE(l,16L); \ + } +#endif + +#define F_00_19(b,c,d) ((((c) ^ (d)) & (b)) ^ (d)) +#define F_20_39(b,c,d) ((b) ^ (c) ^ (d)) +#define F_40_59(b,c,d) (((b) & (c)) | (((b)|(c)) & (d))) +#define F_60_79(b,c,d) F_20_39(b,c,d) + +#define K0 0x5A827999 +#define K1 0x6ED9EBA1 +#define K2 0x8F1BBCDC +#define K3 0xCA62C1D6 + +#define H0 0x67452301 +#define H1 0xEFCDAB89 +#define H2 0x98BADCFE +#define H3 0x10325476 +#define H4 0xC3D2E1F0 + +#define SHA1HashSize 20 + +void SHA1_NEW( unsigned char * pData, int length, unsigned char * pDigest) +{ + if (length > 16) + return; + + UINT4 Message_Block_Index = 0; + + union + { + unsigned char Message_Block[64]; + UINT4 Message_Block_W[16]; + }; + + Message_Block_W[0] = 0x00000000; + Message_Block_W[1] = 0x00000000; + Message_Block_W[2] = 0x00000000; + Message_Block_W[3] = 0x00000000; + Message_Block_W[4] = 0x00000000; + + UINT4 Intermediate_Hash[5] = { H0, H1, H2, H3, H4 }; + + memcpy(Message_Block, pData, length); + Message_Block_Index += length; + + //padMessage + Message_Block[length] = 0x80; + + UINT4 W_15 = length << 3; + + int t; /* Loop counter */ + UINT4 temp; /* Temporary word value */ + UINT4 W[80]; /* Word sequence */ + UINT4 A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + + #define INIT(x) W[x] = Message_Block_W[x]; + + #define INIT_NULL(x) W[x] = 0; + + + Endian_Reverse32(Message_Block_W[0]); + INIT(0); + + #define INIT_NULL_1_14 \ + INIT_NULL(1); INIT_NULL_2_14; + + #define INIT_NULL_2_14 \ + INIT_NULL(2); INIT_NULL_3_14; + + #define INIT_NULL_3_14 \ + INIT_NULL(3); INIT_NULL_4_14; + + #define INIT_NULL_4_14 \ + INIT_NULL(4); INIT_NULL_5_14; + + #define INIT_NULL_5_14 \ + INIT_NULL(5); INIT_NULL(6); INIT_NULL(7); \ + INIT_NULL(8); INIT_NULL(9); INIT_NULL(10); INIT_NULL(11); \ + INIT_NULL(12); INIT_NULL(13); INIT_NULL(14); + + #define ROTATE1_NULL_5_14 \ + ROTATE1_NULL; ROTATE1_NULL; ROTATE1_NULL; \ + ROTATE1_NULL; ROTATE1_NULL; ROTATE1_NULL; ROTATE1_NULL; \ + ROTATE1_NULL; ROTATE1_NULL; ROTATE1_NULL; + + + #define EXPAND(t) \ + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); \ + + #define EXPAND_3(t) W[t] = SHA1CircularShift(1,W[t-3]); + #define EXPAND_16(t) W[t] = SHA1CircularShift(1,W[t-16]); + #define EXPAND_3_8(t) W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8]); + + if (length < 4) { + INIT_NULL_1_14; + W[15] = W_15; + EXPAND_16(16); + W[17] = 0; + W[18] = W_15<<1; + } + else if (length < 8) { + Endian_Reverse32(Message_Block_W[1]); + INIT(1); + INIT_NULL_2_14; + W[15] = W_15; + EXPAND_16(16); + EXPAND_16(17); + W[18] = W_15<<1; + } + else { + Endian_Reverse32(Message_Block_W[1]); + Endian_Reverse32(Message_Block_W[2]); + Endian_Reverse32(Message_Block_W[3]); + Endian_Reverse32(Message_Block_W[4]); + INIT(1); INIT(2); INIT(3); INIT(4); + INIT_NULL_5_14; + W[15] = W_15; + EXPAND(16); + EXPAND(17); + EXPAND(18); + } + + if (length < 12) { + EXPAND_3(19); + } + else { + EXPAND(19); + } + + if (length < 16) { + EXPAND_3(20); + } + else { + EXPAND(20); + } + EXPAND_3(21); EXPAND_3(22); + EXPAND(23); + + EXPAND(24); EXPAND(25); EXPAND_3_8(26); EXPAND_3_8(27); + EXPAND(28); EXPAND(29); EXPAND(30); EXPAND(31); + EXPAND(32); EXPAND(33); EXPAND(34); EXPAND(35); + EXPAND(36); EXPAND(37); EXPAND(38); EXPAND(39); + EXPAND(40); EXPAND(41); EXPAND(42); EXPAND(43); + EXPAND(44); EXPAND(45); EXPAND(46); EXPAND(47); + EXPAND(48); EXPAND(49); EXPAND(50); EXPAND(51); + EXPAND(52); EXPAND(53); EXPAND(54); EXPAND(55); + EXPAND(56); EXPAND(57); EXPAND(58); EXPAND(59); + EXPAND(60); EXPAND(61); EXPAND(62); EXPAND(63); + EXPAND(64); EXPAND(65); EXPAND(66); EXPAND(67); + EXPAND(68); EXPAND(69); EXPAND(70); EXPAND(71); + EXPAND(72); EXPAND(73); EXPAND(74); EXPAND(75); + EXPAND(76); EXPAND(77); EXPAND(78); EXPAND(79); + + + #define ROTATE1_NEW(a, b, c, d, e, x) \ + e += SHA1CircularShift(5,a) + F_00_19(b,c,d) + x + K0; \ + b = SHA1CircularShift(30,b); + + #define ROTATE1_NULL \ + temp = SHA1CircularShift(5,A) + F_00_19(B,C,D) + E + K0; \ + E = D; D = C; \ + C = SHA1CircularShift(30,B); \ + B = A; A = temp; \ + + #define ROTATE2_NEW(a, b, c, d, e, x) \ + e += SHA1CircularShift(5,a) + F_20_39(b,c,d) + x + K1; \ + b = SHA1CircularShift(30,b); + + #define ROTATE2(t) \ + temp = SHA1CircularShift(5,A) + F_20_39(B,C,D) + E + W[t] + K1; \ + E = D; D = C; \ + C = SHA1CircularShift(30,B); \ + B = A; A = temp; + + #define ROTATE2_W(w) \ + temp = SHA1CircularShift(5,A) + F_20_39(B,C,D) + E + w + K1; \ + E = D; D = C; \ + C = SHA1CircularShift(30,B); \ + B = A; A = temp; + + #define ROTATE3(t) \ + temp = SHA1CircularShift(5,A) + F_40_59(B,C,D) + E + W[t] + K2; \ + E = D; D = C; \ + C = SHA1CircularShift(30,B); \ + B = A; A = temp; + + #define ROTATE4(t) \ + temp = SHA1CircularShift(5,A) + F_60_79(B,C,D) + E + W[t] + K3; \ + E = D; D = C; \ + C = SHA1CircularShift(30,B); \ + B = A; A = temp; + + A = H0; + B = H1; + C = H2; + D = H3; + E = H4; + + + E = H2; + //D = 2079550178; + //C = 1506887872; + B = 2679412915u + W[0]; + if (length < 4) { + A = SHA1CircularShift(5,B) + 1722862861; + } + else { + A = SHA1CircularShift(5,B) + 1722862861 + W[1]; + } + + if (length < 8) { + temp = SHA1CircularShift(5,A) + ((((1506887872) ^ (2079550178)) & (B)) ^ (2079550178)) + H2 + K0; + } + else { + temp = SHA1CircularShift(5,A) + (((572662306) & (B)) ^ (2079550178)) + H2 + K0 + W[2]; + } + C = SHA1CircularShift(30,B); //SHA1CircularShift(30,(2679412915 + W[0])); + B = A; + A = temp; + + if (length < 12) { + temp = SHA1CircularShift(5,A) + ((((C) ^ (1506887872)) & (B)) ^ (1506887872)) + 2079550178 + K0; + } + else { + temp = SHA1CircularShift(5,A) + ((((C) ^ (1506887872)) & (B)) ^ (1506887872)) + 2079550178 + K0 + W[3]; + } + E = 1506887872; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + + if (length < 16) { + temp = SHA1CircularShift(5,A) + F_00_19(B,C,D) + 1506887872 + K0; + } + else { + temp = SHA1CircularShift(5,A) + F_00_19(B,C,D) + 1506887872 + K0 + W[4]; + } + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + + ROTATE1_NULL_5_14; + + ROTATE1_NEW( A, B, C, D, E, W_15 ); + ROTATE1_NEW( E, A, B, C, D, W[16] ); + ROTATE1_NEW( D, E, A, B, C, W[17] ); + ROTATE1_NEW( C, D, E, A, B, W[18] ); + ROTATE1_NEW( B, C, D, E, A, W[19] ); + + for(t = 20; t < 40; t++) + { + if (t == 21 && length < 8) { + ROTATE2_W((length<<5)); // *32 + } + else { + ROTATE2(t); + } + } + + for(t = 40; t < 60; t++) + { + ROTATE3(t); + } + + for(t = 60; t < 80; t++) + { + ROTATE4(t); + } + + Intermediate_Hash[0] += A; + Intermediate_Hash[1] += B; + Intermediate_Hash[2] += C; + Intermediate_Hash[3] += D; + Intermediate_Hash[4] += E; + + Endian_Reverse32(Intermediate_Hash[0]); + Endian_Reverse32(Intermediate_Hash[1]); + Endian_Reverse32(Intermediate_Hash[2]); + Endian_Reverse32(Intermediate_Hash[3]); + Endian_Reverse32(Intermediate_Hash[4]); + + memcpy(pDigest, Intermediate_Hash, 20); +} diff --git a/Client Applications/rcracki_mt/sha1.h b/Client Applications/rcracki_mt/sha1.h index e10a64b..0e59270 100644 --- a/Client Applications/rcracki_mt/sha1.h +++ b/Client Applications/rcracki_mt/sha1.h @@ -1 +1,33 @@ -void SHA1_NEW( unsigned char * pData, int length, unsigned char * pDigest); +/* + * rcracki_mt is a multithreaded implementation and fork of the original + * RainbowCrack + * + * Copyright 2009, 2010 Daniël Niggebrugge + * Copyright 2009, 2010 James Nobis + * + * This file is part of racrcki_mt. + * + * rcracki_mt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * rcracki_mt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rcracki_mt. If not, see . + */ + +#ifndef SHA1_H +#define SHA1_H + +#include "global.h" + +#define SHA1_DIGEST_LENGTH 20 + +void SHA1_NEW( unsigned char * pData, int length, unsigned char * pDigest); + +#endif /* !SHA1_H */ -- 2.39.2