diff options
-rw-r--r-- | filters.cpp | 100 | ||||
-rw-r--r-- | filters.h | 13 | ||||
-rw-r--r-- | fipstest.cpp | 68 | ||||
-rw-r--r-- | fltrimpl.h | 11 | ||||
-rw-r--r-- | misc.h | 2 | ||||
-rw-r--r-- | test.cpp | 47 |
6 files changed, 179 insertions, 62 deletions
diff --git a/filters.cpp b/filters.cpp index f2d78d1..8d22555 100644 --- a/filters.cpp +++ b/filters.cpp @@ -141,46 +141,86 @@ bool Filter::OutputMessageSeriesEnd(int outputSite, int propagation, bool blocki // ************************************************************* -size_t MeterFilter::Put2(const byte *begin, size_t length, int messageEnd, bool blocking) +void MeterFilter::ResetMeter() { - if (m_transparent) - { - FILTER_BEGIN; - m_currentMessageBytes += length; - m_totalBytes += length; - - if (messageEnd) - { - m_currentMessageBytes = 0; - m_currentSeriesMessages++; - m_totalMessages++; - } + m_currentMessageBytes = m_totalBytes = m_currentSeriesMessages = m_totalMessages = m_totalMessageSeries = 0; + m_rangesToSkip.clear(); +} - FILTER_OUTPUT(1, begin, length, messageEnd); - FILTER_END_NO_MESSAGE_END; - } - return 0; +void MeterFilter::AddRangeToSkip(unsigned int message, lword position, lword size, bool sortNow) +{ + MessageRange r = {message, position, size}; + m_rangesToSkip.push_back(r); + if (sortNow) + std::sort(m_rangesToSkip.begin(), m_rangesToSkip.end()); } -size_t MeterFilter::PutModifiable2(byte *begin, size_t length, int messageEnd, bool blocking) +size_t MeterFilter::PutMaybeModifiable(byte *begin, size_t length, int messageEnd, bool blocking, bool modifiable) { - if (m_transparent) + if (!m_transparent) + return 0; + + size_t t; + FILTER_BEGIN; + + m_begin = begin; + m_length = length; + + while (m_length > 0 || messageEnd) { - FILTER_BEGIN; - m_currentMessageBytes += length; - m_totalBytes += length; + if (!m_rangesToSkip.empty() && m_rangesToSkip.front().message == m_totalMessages && m_currentMessageBytes + m_length > m_rangesToSkip.front().position) + { + FILTER_OUTPUT_MAYBE_MODIFIABLE(1, m_begin, t = (size_t)SaturatingSubtract(m_rangesToSkip.front().position, m_currentMessageBytes), false, modifiable); + + assert(t < m_length); + m_begin += t; + m_length -= t; + m_currentMessageBytes += t; + m_totalBytes += t; - if (messageEnd) + if (m_currentMessageBytes + m_length < m_rangesToSkip.front().position + m_rangesToSkip.front().size) + t = m_length; + else + { + t = (size_t)SaturatingSubtract(m_rangesToSkip.front().position + m_rangesToSkip.front().size, m_currentMessageBytes); + assert(t <= m_length); + m_rangesToSkip.pop_front(); + } + + m_begin += t; + m_length -= t; + m_currentMessageBytes += t; + m_totalBytes += t; + } + else { - m_currentMessageBytes = 0; - m_currentSeriesMessages++; - m_totalMessages++; + FILTER_OUTPUT_MAYBE_MODIFIABLE(2, m_begin, m_length, messageEnd, modifiable); + + m_currentMessageBytes += m_length; + m_totalBytes += m_length; + m_length = 0; + + if (messageEnd) + { + m_currentMessageBytes = 0; + m_currentSeriesMessages++; + m_totalMessages++; + messageEnd = false; + } } - - FILTER_OUTPUT_MODIFIABLE(1, begin, length, messageEnd); - FILTER_END_NO_MESSAGE_END; } - return 0; + + FILTER_END_NO_MESSAGE_END; +} + +size_t MeterFilter::Put2(const byte *begin, size_t length, int messageEnd, bool blocking) +{ + return PutMaybeModifiable(const_cast<byte *>(begin), length, messageEnd, blocking, false); +} + +size_t MeterFilter::PutModifiable2(byte *begin, size_t length, int messageEnd, bool blocking) +{ + return PutMaybeModifiable(begin, length, messageEnd, blocking, true); } bool MeterFilter::IsolatedMessageSeriesEnd(bool blocking) @@ -7,6 +7,7 @@ #include "smartptr.h" #include "queue.h" #include "algparam.h" +#include <deque> NAMESPACE_BEGIN(CryptoPP) @@ -86,7 +87,9 @@ public: : m_transparent(transparent) {Detach(attachment); ResetMeter();} void SetTransparent(bool transparent) {m_transparent = transparent;} - void ResetMeter() {m_currentMessageBytes = m_totalBytes = m_currentSeriesMessages = m_totalMessages = m_totalMessageSeries = 0;} + void AddRangeToSkip(unsigned int message, lword position, lword size, bool sortNow = true); + void ResetMeter(); + void IsolatedInitialize(const NameValuePairs ¶meters) {ResetMeter();} lword GetCurrentMessageBytes() const {return m_currentMessageBytes;} lword GetTotalBytes() {return m_totalBytes;} @@ -101,12 +104,20 @@ public: bool IsolatedMessageSeriesEnd(bool blocking); private: + size_t PutMaybeModifiable(byte *inString, size_t length, int messageEnd, bool blocking, bool modifiable); bool ShouldPropagateMessageEnd() const {return m_transparent;} bool ShouldPropagateMessageSeriesEnd() const {return m_transparent;} + struct MessageRange {unsigned int message; lword position; lword size;}; + friend inline bool operator<(const MessageRange &a, const MessageRange &b) + {return a.message < b.message || (a.message == b.message && a.position < b.position);} + bool m_transparent; lword m_currentMessageBytes, m_totalBytes; unsigned int m_currentSeriesMessages, m_totalMessages, m_totalMessageSeries; + std::deque<MessageRange> m_rangesToSkip; + byte *m_begin; + size_t m_length; }; //! _ diff --git a/fipstest.cpp b/fipstest.cpp index bc86fe2..78d3867 100644 --- a/fipstest.cpp +++ b/fipstest.cpp @@ -8,7 +8,17 @@ #include "dll.h" #ifdef CRYPTOPP_WIN32_AVAILABLE +#define _WIN32_WINNT 0x0400 #include <windows.h> + +#if defined(_MSC_VER) && _MSC_VER >= 14 +#ifdef _M_IX86 +#define _CRT_DEBUGGER_HOOK _crt_debugger_hook +#else +#define _CRT_DEBUGGER_HOOK __crt_debugger_hook +#endif +extern "C" {_CRTIMP void __cdecl _CRT_DEBUGGER_HOOK(int);} +#endif #endif NAMESPACE_BEGIN(CryptoPP) @@ -249,20 +259,31 @@ bool IntegrityCheckModule(const char *moduleFilename, const byte *expectedModule unsigned long &macFileLocation = pMacFileLocation ? *pMacFileLocation : tempLocation; macFileLocation = 0; - HashFilter verifier(*mac, new ArraySink(actualMac, actualMac.size())); -// FileSink verifier("c:\\dt.tmp"); + MeterFilter verifier(new HashFilter(*mac, new ArraySink(actualMac, actualMac.size()))); +// MeterFilter verifier(new FileSink("c:\\dt.tmp")); FileStore file(moduleFilename); #ifdef CRYPTOPP_WIN32_AVAILABLE // try to hash from memory first HMODULE h = GetModuleHandle(moduleFilename); const byte *memBase = (const byte *)h; - IMAGE_DOS_HEADER *ph = (IMAGE_DOS_HEADER *)h; - IMAGE_NT_HEADERS *phnt = (IMAGE_NT_HEADERS *)((byte *)h + ph->e_lfanew); - IMAGE_SECTION_HEADER *phs = IMAGE_FIRST_SECTION(phnt); + const IMAGE_DOS_HEADER *ph = (IMAGE_DOS_HEADER *)memBase; + const IMAGE_NT_HEADERS *phnt = (IMAGE_NT_HEADERS *)(memBase + ph->e_lfanew); + const IMAGE_SECTION_HEADER *phs = IMAGE_FIRST_SECTION(phnt); DWORD nSections = phnt->FileHeader.NumberOfSections; size_t currentFilePos = 0; + size_t checksumPos = (byte *)&phnt->OptionalHeader.CheckSum - memBase; + size_t checksumSize = sizeof(phnt->OptionalHeader.CheckSum); + size_t certificateTableDirectoryPos = (byte *)&phnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY] - memBase; + size_t certificateTableDirectorySize = sizeof(phnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]); + size_t certificateTablePos = phnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress; + size_t certificateTableSize = phnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size; + + verifier.AddRangeToSkip(0, checksumPos, checksumSize); + verifier.AddRangeToSkip(0, certificateTableDirectoryPos, certificateTableDirectorySize); + verifier.AddRangeToSkip(0, certificateTablePos, certificateTableSize); + while (nSections--) { switch (phs->Characteristics) @@ -295,16 +316,27 @@ bool IntegrityCheckModule(const char *moduleFilename, const byte *expectedModule } } - file.TransferTo(verifier, subSectionFileStart - currentFilePos); +#if defined(_MSC_VER) && _MSC_VER >= 14 + // first byte of _CRT_DEBUGGER_HOOK gets modified in memory by the debugger invisibly, so read it from file + if (IsDebuggerPresent()) + { + if (subSectionMemStart <= (byte *)&_CRT_DEBUGGER_HOOK && (byte *)&_CRT_DEBUGGER_HOOK < subSectionMemStart + subSectionSize) + { + subSectionSize = (byte *)&_CRT_DEBUGGER_HOOK - subSectionMemStart; + nextSubSectionStart = (byte *)&_CRT_DEBUGGER_HOOK - sectionMemStart + 1; + } + } +#endif + if (subSectionMemStart <= expectedModuleMac && expectedModuleMac < subSectionMemStart + subSectionSize) { - // skip over the MAC - verifier.Put(subSectionMemStart, expectedModuleMac - subSectionMemStart); - verifier.Put(expectedModuleMac + macSize, subSectionSize - macSize - (expectedModuleMac - subSectionMemStart)); + // found stored MAC macFileLocation = (unsigned long)(subSectionFileStart + (expectedModuleMac - subSectionMemStart)); + verifier.AddRangeToSkip(0, macFileLocation, macSize); } - else - verifier.Put(subSectionMemStart, subSectionSize); + + file.TransferTo(verifier, subSectionFileStart - currentFilePos); + verifier.Put(subSectionMemStart, subSectionSize); file.Skip(subSectionSize); currentFilePos = subSectionFileStart + subSectionSize; subSectionStart = nextSubSectionStart; @@ -321,13 +353,13 @@ bool IntegrityCheckModule(const char *moduleFilename, const byte *expectedModule if (memcmp(expectedModuleMac, actualMac, macSize) != 0) { OutputDebugString("In memory integrity check failed. This may be caused by debug breakpoints or DLL relocation.\n"); - file.Initialize(MakeParameters("InputFileName", moduleFilename)); - verifier.Detach(new ArraySink(actualMac, actualMac.size())); - if (macFileLocation) - { - file.TransferTo(verifier, macFileLocation); - file.Skip(macSize); - } + file.Initialize(MakeParameters(Name::InputFileName(), moduleFilename)); + verifier.Initialize(MakeParameters(Name::OutputBuffer(), ByteArrayParameter(actualMac, (unsigned int)actualMac.size()))); +// verifier.Initialize(MakeParameters(Name::OutputFileName(), (const char *)"c:\\dt2.tmp")); + verifier.AddRangeToSkip(0, checksumPos, checksumSize); + verifier.AddRangeToSkip(0, certificateTableDirectoryPos, certificateTableDirectorySize); + verifier.AddRangeToSkip(0, certificateTablePos, certificateTableSize); + verifier.AddRangeToSkip(0, macFileLocation, macSize); file.TransferAllTo(verifier); } #endif @@ -50,4 +50,15 @@ #define FILTER_OUTPUT_MODIFIABLE(site, output, length, messageEnd) \ FILTER_OUTPUT2_MODIFIABLE(site, 0, output, length, messageEnd) +#define FILTER_OUTPUT2_MAYBE_MODIFIABLE(site, statement, output, length, messageEnd, modifiable) \ + {\ + case site: \ + statement; \ + if (modifiable ? OutputModifiable(site, output, length, messageEnd, blocking) : Output(site, output, length, messageEnd, blocking)) \ + return STDMAX(size_t(1), length-m_inputPosition);\ + } + +#define FILTER_OUTPUT_MAYBE_MODIFIABLE(site, output, length, messageEnd, modifiable) \ + FILTER_OUTPUT2_MAYBE_MODIFIABLE(site, 0, output, length, messageEnd, modifiable) + #endif @@ -224,7 +224,7 @@ template <class T1, class T2> inline bool SafeConvert(T1 from, T2 &to) { to = (T2)from; - if (from != to || (from > 0 && to < 0)) + if (from != to || (from > 0) != (to > 0)) return false; return true; } @@ -170,38 +170,61 @@ int __cdecl main(int argc, char *argv[]) } else if (command == "mac_dll") { + // sanity check on file size std::fstream dllFile(argv[2], ios::in | ios::out | ios::binary); std::ifstream::pos_type fileEnd = dllFile.seekg(0, std::ios_base::end).tellg(); - if (fileEnd > 20*1000*1000) // sanity check on file size + if (fileEnd > 20*1000*1000) { cerr << "Input file too large (more than 20 MB).\n"; return 1; } + // read file into memory unsigned int fileSize = (unsigned int)fileEnd; SecByteBlock buf(fileSize); dllFile.seekg(0, std::ios_base::beg); dllFile.read((char *)buf.begin(), fileSize); - byte dummyMac[] = CRYPTOPP_DUMMY_DLL_MAC; - - byte *found = std::search(buf.begin(), buf.end(), dummyMac+0, dummyMac+sizeof(dummyMac)); + // find positions of relevant sections in the file, based on version 8 of documentation from http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx + word32 coffPos = *(word16 *)(buf+0x3c); + word32 optionalHeaderPos = coffPos + 24; + word16 optionalHeaderMagic = *(word16 *)(buf+optionalHeaderPos); + if (optionalHeaderMagic != 0x10b && optionalHeaderMagic != 0x20b) + { + cerr << "Target file is not a PE32 or PE32+ image.\n"; + return 3; + } + word32 checksumPos = optionalHeaderPos + 64; + word32 certificateTableDirectoryPos = optionalHeaderPos + (optionalHeaderMagic == 0x10b ? 128 : 144); + word32 certificateTablePos = *(word32 *)(buf+certificateTableDirectoryPos); + word32 certificateTableSize = *(word32 *)(buf+certificateTableDirectoryPos+4); + if (certificateTableSize != 0) + cerr << "Warning: certificate table (IMAGE_DIRECTORY_ENTRY_SECURITY) of target image is not empty.\n"; + + // find where to place computed MAC + byte mac[] = CRYPTOPP_DUMMY_DLL_MAC; + byte *found = std::search(buf.begin(), buf.end(), mac+0, mac+sizeof(mac)); if (found == buf.end()) { cerr << "MAC placeholder not found. Possibly the actual MAC was already placed.\n"; - return 1; + return 2; } + word32 macPos = (unsigned int)(found-buf.begin()); - unsigned int macPos = (unsigned int)(found-buf.begin()); + // compute MAC member_ptr<MessageAuthenticationCode> pMac(NewIntegrityCheckingMAC()); - pMac->Update(buf.begin(), macPos); - pMac->Update(buf.begin() + macPos + sizeof(dummyMac), fileSize - sizeof(dummyMac) - macPos); - assert(pMac->DigestSize() == sizeof(dummyMac)); - pMac->Final(dummyMac); - + assert(pMac->DigestSize() == sizeof(mac)); + MeterFilter f(new HashFilter(*pMac, new ArraySink(mac, sizeof(mac)))); + f.AddRangeToSkip(0, checksumPos, 4); + f.AddRangeToSkip(0, certificateTableDirectoryPos, 8); + f.AddRangeToSkip(0, macPos, sizeof(mac)); + f.AddRangeToSkip(0, certificateTablePos, certificateTableSize); + f.PutMessageEnd(buf.begin(), buf.size()); + + // place MAC cout << "Placing MAC in file " << argv[2] << ", location " << macPos << ".\n"; dllFile.seekg(macPos, std::ios_base::beg); - dllFile.write((char *)dummyMac, sizeof(dummyMac)); + dllFile.write((char *)mac, sizeof(mac)); } else if (command == "m") DigestFile(argv[2]); |