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

This commit is contained in:
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)
{
if (m_transparent)
{
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;
return PutMaybeModifiable(const_cast<byte *>(begin), length, messageEnd, blocking, false);
}
size_t MeterFilter::PutModifiable2(byte *begin, size_t length, int messageEnd, bool blocking)
{
if (m_transparent)
{
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;
return PutMaybeModifiable(begin, length, messageEnd, blocking, true);
}
bool MeterFilter::IsolatedMessageSeriesEnd(bool blocking)

View File

@ -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 &parameters) {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;
};
//! _

View File

@ -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

View File

@ -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

2
misc.h
View File

@ -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;
}

View File

@ -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;
// 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())
{
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]);