change DLL integrity self-test to allow DLL to be Authenticode signed

pull/2/head
weidai 2006-07-30 17:15:01 +00:00
parent a0c89ccaae
commit 1b6b327200
6 changed files with 181 additions and 64 deletions

View File

@ -141,46 +141,86 @@ bool Filter::OutputMessageSeriesEnd(int outputSite, int propagation, bool blocki
// ************************************************************* // *************************************************************
void MeterFilter::ResetMeter()
{
m_currentMessageBytes = m_totalBytes = m_currentSeriesMessages = m_totalMessages = m_totalMessageSeries = 0;
m_rangesToSkip.clear();
}
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::PutMaybeModifiable(byte *begin, size_t length, int messageEnd, bool blocking, bool modifiable)
{
if (!m_transparent)
return 0;
size_t t;
FILTER_BEGIN;
m_begin = begin;
m_length = length;
while (m_length > 0 || messageEnd)
{
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 (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
{
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_END_NO_MESSAGE_END;
}
size_t MeterFilter::Put2(const byte *begin, size_t length, int messageEnd, bool blocking) size_t MeterFilter::Put2(const byte *begin, size_t length, int messageEnd, bool blocking)
{ {
if (m_transparent) return PutMaybeModifiable(const_cast<byte *>(begin), length, messageEnd, blocking, false);
{
FILTER_BEGIN;
m_currentMessageBytes += length;
m_totalBytes += length;
if (messageEnd)
{
m_currentMessageBytes = 0;
m_currentSeriesMessages++;
m_totalMessages++;
}
FILTER_OUTPUT(1, begin, length, messageEnd);
FILTER_END_NO_MESSAGE_END;
}
return 0;
} }
size_t MeterFilter::PutModifiable2(byte *begin, size_t length, int messageEnd, bool blocking) size_t MeterFilter::PutModifiable2(byte *begin, size_t length, int messageEnd, bool blocking)
{ {
if (m_transparent) return PutMaybeModifiable(begin, length, messageEnd, blocking, true);
{
FILTER_BEGIN;
m_currentMessageBytes += length;
m_totalBytes += length;
if (messageEnd)
{
m_currentMessageBytes = 0;
m_currentSeriesMessages++;
m_totalMessages++;
}
FILTER_OUTPUT_MODIFIABLE(1, begin, length, messageEnd);
FILTER_END_NO_MESSAGE_END;
}
return 0;
} }
bool MeterFilter::IsolatedMessageSeriesEnd(bool blocking) bool MeterFilter::IsolatedMessageSeriesEnd(bool blocking)

View File

@ -7,6 +7,7 @@
#include "smartptr.h" #include "smartptr.h"
#include "queue.h" #include "queue.h"
#include "algparam.h" #include "algparam.h"
#include <deque>
NAMESPACE_BEGIN(CryptoPP) NAMESPACE_BEGIN(CryptoPP)
@ -86,7 +87,9 @@ public:
: m_transparent(transparent) {Detach(attachment); ResetMeter();} : m_transparent(transparent) {Detach(attachment); ResetMeter();}
void SetTransparent(bool transparent) {m_transparent = transparent;} 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 &parameters) {ResetMeter();}
lword GetCurrentMessageBytes() const {return m_currentMessageBytes;} lword GetCurrentMessageBytes() const {return m_currentMessageBytes;}
lword GetTotalBytes() {return m_totalBytes;} lword GetTotalBytes() {return m_totalBytes;}
@ -101,12 +104,20 @@ public:
bool IsolatedMessageSeriesEnd(bool blocking); bool IsolatedMessageSeriesEnd(bool blocking);
private: private:
size_t PutMaybeModifiable(byte *inString, size_t length, int messageEnd, bool blocking, bool modifiable);
bool ShouldPropagateMessageEnd() const {return m_transparent;} bool ShouldPropagateMessageEnd() const {return m_transparent;}
bool ShouldPropagateMessageSeriesEnd() 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; bool m_transparent;
lword m_currentMessageBytes, m_totalBytes; lword m_currentMessageBytes, m_totalBytes;
unsigned int m_currentSeriesMessages, m_totalMessages, m_totalMessageSeries; unsigned int m_currentSeriesMessages, m_totalMessages, m_totalMessageSeries;
std::deque<MessageRange> m_rangesToSkip;
byte *m_begin;
size_t m_length;
}; };
//! _ //! _

View File

@ -8,7 +8,17 @@
#include "dll.h" #include "dll.h"
#ifdef CRYPTOPP_WIN32_AVAILABLE #ifdef CRYPTOPP_WIN32_AVAILABLE
#define _WIN32_WINNT 0x0400
#include <windows.h> #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 #endif
NAMESPACE_BEGIN(CryptoPP) NAMESPACE_BEGIN(CryptoPP)
@ -249,20 +259,31 @@ bool IntegrityCheckModule(const char *moduleFilename, const byte *expectedModule
unsigned long &macFileLocation = pMacFileLocation ? *pMacFileLocation : tempLocation; unsigned long &macFileLocation = pMacFileLocation ? *pMacFileLocation : tempLocation;
macFileLocation = 0; macFileLocation = 0;
HashFilter verifier(*mac, new ArraySink(actualMac, actualMac.size())); MeterFilter verifier(new HashFilter(*mac, new ArraySink(actualMac, actualMac.size())));
// FileSink verifier("c:\\dt.tmp"); // MeterFilter verifier(new FileSink("c:\\dt.tmp"));
FileStore file(moduleFilename); FileStore file(moduleFilename);
#ifdef CRYPTOPP_WIN32_AVAILABLE #ifdef CRYPTOPP_WIN32_AVAILABLE
// try to hash from memory first // try to hash from memory first
HMODULE h = GetModuleHandle(moduleFilename); HMODULE h = GetModuleHandle(moduleFilename);
const byte *memBase = (const byte *)h; const byte *memBase = (const byte *)h;
IMAGE_DOS_HEADER *ph = (IMAGE_DOS_HEADER *)h; const IMAGE_DOS_HEADER *ph = (IMAGE_DOS_HEADER *)memBase;
IMAGE_NT_HEADERS *phnt = (IMAGE_NT_HEADERS *)((byte *)h + ph->e_lfanew); const IMAGE_NT_HEADERS *phnt = (IMAGE_NT_HEADERS *)(memBase + ph->e_lfanew);
IMAGE_SECTION_HEADER *phs = IMAGE_FIRST_SECTION(phnt); const IMAGE_SECTION_HEADER *phs = IMAGE_FIRST_SECTION(phnt);
DWORD nSections = phnt->FileHeader.NumberOfSections; DWORD nSections = phnt->FileHeader.NumberOfSections;
size_t currentFilePos = 0; 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--) while (nSections--)
{ {
switch (phs->Characteristics) 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) if (subSectionMemStart <= expectedModuleMac && expectedModuleMac < subSectionMemStart + subSectionSize)
{ {
// skip over the MAC // found stored MAC
verifier.Put(subSectionMemStart, expectedModuleMac - subSectionMemStart);
verifier.Put(expectedModuleMac + macSize, subSectionSize - macSize - (expectedModuleMac - subSectionMemStart));
macFileLocation = (unsigned long)(subSectionFileStart + (expectedModuleMac - subSectionMemStart)); 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); file.Skip(subSectionSize);
currentFilePos = subSectionFileStart + subSectionSize; currentFilePos = subSectionFileStart + subSectionSize;
subSectionStart = nextSubSectionStart; subSectionStart = nextSubSectionStart;
@ -321,13 +353,13 @@ bool IntegrityCheckModule(const char *moduleFilename, const byte *expectedModule
if (memcmp(expectedModuleMac, actualMac, macSize) != 0) if (memcmp(expectedModuleMac, actualMac, macSize) != 0)
{ {
OutputDebugString("In memory integrity check failed. This may be caused by debug breakpoints or DLL relocation.\n"); OutputDebugString("In memory integrity check failed. This may be caused by debug breakpoints or DLL relocation.\n");
file.Initialize(MakeParameters("InputFileName", moduleFilename)); file.Initialize(MakeParameters(Name::InputFileName(), moduleFilename));
verifier.Detach(new ArraySink(actualMac, actualMac.size())); verifier.Initialize(MakeParameters(Name::OutputBuffer(), ByteArrayParameter(actualMac, (unsigned int)actualMac.size())));
if (macFileLocation) // verifier.Initialize(MakeParameters(Name::OutputFileName(), (const char *)"c:\\dt2.tmp"));
{ verifier.AddRangeToSkip(0, checksumPos, checksumSize);
file.TransferTo(verifier, macFileLocation); verifier.AddRangeToSkip(0, certificateTableDirectoryPos, certificateTableDirectorySize);
file.Skip(macSize); verifier.AddRangeToSkip(0, certificateTablePos, certificateTableSize);
} verifier.AddRangeToSkip(0, macFileLocation, macSize);
file.TransferAllTo(verifier); file.TransferAllTo(verifier);
} }
#endif #endif

View File

@ -50,4 +50,15 @@
#define FILTER_OUTPUT_MODIFIABLE(site, output, length, messageEnd) \ #define FILTER_OUTPUT_MODIFIABLE(site, output, length, messageEnd) \
FILTER_OUTPUT2_MODIFIABLE(site, 0, 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 #endif

2
misc.h
View File

@ -224,7 +224,7 @@ template <class T1, class T2>
inline bool SafeConvert(T1 from, T2 &to) inline bool SafeConvert(T1 from, T2 &to)
{ {
to = (T2)from; to = (T2)from;
if (from != to || (from > 0 && to < 0)) if (from != to || (from > 0) != (to > 0))
return false; return false;
return true; return true;
} }

View File

@ -170,38 +170,61 @@ int __cdecl main(int argc, char *argv[])
} }
else if (command == "mac_dll") else if (command == "mac_dll")
{ {
// sanity check on file size
std::fstream dllFile(argv[2], ios::in | ios::out | ios::binary); std::fstream dllFile(argv[2], ios::in | ios::out | ios::binary);
std::ifstream::pos_type fileEnd = dllFile.seekg(0, std::ios_base::end).tellg(); 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"; cerr << "Input file too large (more than 20 MB).\n";
return 1; return 1;
} }
// read file into memory
unsigned int fileSize = (unsigned int)fileEnd; unsigned int fileSize = (unsigned int)fileEnd;
SecByteBlock buf(fileSize); SecByteBlock buf(fileSize);
dllFile.seekg(0, std::ios_base::beg); dllFile.seekg(0, std::ios_base::beg);
dllFile.read((char *)buf.begin(), fileSize); dllFile.read((char *)buf.begin(), fileSize);
byte dummyMac[] = CRYPTOPP_DUMMY_DLL_MAC; // 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";
byte *found = std::search(buf.begin(), buf.end(), dummyMac+0, dummyMac+sizeof(dummyMac)); // 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()) if (found == buf.end())
{ {
cerr << "MAC placeholder not found. Possibly the actual MAC was already placed.\n"; 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()); member_ptr<MessageAuthenticationCode> pMac(NewIntegrityCheckingMAC());
pMac->Update(buf.begin(), macPos); assert(pMac->DigestSize() == sizeof(mac));
pMac->Update(buf.begin() + macPos + sizeof(dummyMac), fileSize - sizeof(dummyMac) - macPos); MeterFilter f(new HashFilter(*pMac, new ArraySink(mac, sizeof(mac))));
assert(pMac->DigestSize() == sizeof(dummyMac)); f.AddRangeToSkip(0, checksumPos, 4);
pMac->Final(dummyMac); 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"; cout << "Placing MAC in file " << argv[2] << ", location " << macPos << ".\n";
dllFile.seekg(macPos, std::ios_base::beg); dllFile.seekg(macPos, std::ios_base::beg);
dllFile.write((char *)dummyMac, sizeof(dummyMac)); dllFile.write((char *)mac, sizeof(mac));
} }
else if (command == "m") else if (command == "m")
DigestFile(argv[2]); DigestFile(argv[2]);