1
0
Fork 0

DBG: more robust debug directory parsing. Validate the RVA, type and size bounds for each debug directory entry, and do not stop after the one unrecognised (non-CV) entry

Protect against PDB paths that do not have a null terminator in the PE codeview info
This commit is contained in:
Mattiwatti 2018-03-24 07:18:25 +01:00 committed by Duncan Ogilvie
parent 14da6c4448
commit 1f485f313e
No known key found for this signature in database
GPG Key ID: FC89E0AAA0C1AAD8
2 changed files with 106 additions and 83 deletions

View File

@ -358,76 +358,15 @@ static void ReadBaseRelocationTable(MODINFO & Info, ULONG_PTR FileMapVA)
//Useful information: http://www.debuginfo.com/articles/debuginfomatch.html
void ReadDebugDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
{
//TODO: proper error handling and proper bounds checking
auto pnth = PIMAGE_NT_HEADERS(FileMapVA + GetPE32DataFromMappedFile(FileMapVA, 0, UE_PE_OFFSET));
auto & debugDataDir = pnth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
if(!debugDataDir.VirtualAddress || !debugDataDir.Size)
// Get the debug directory and its size
ULONG debugDirSize;
auto debugDir = (PIMAGE_DEBUG_DIRECTORY)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE,
IMAGE_DIRECTORY_ENTRY_DEBUG,
&debugDirSize);
if(debugDirSize == 0 || debugDir == nullptr)
return;
auto debugDirOffset = (duint)ConvertVAtoFileOffsetEx(FileMapVA, Info.loadedSize, 0, debugDataDir.VirtualAddress, true, false);
if(!debugDirOffset || debugDirOffset + debugDataDir.Size > Info.loadedSize)
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Invalid debug directory for module %s%s...\n"), Info.name, Info.extension);
return;
}
IMAGE_DEBUG_DIRECTORY debugDir = { 0 };
memcpy(&debugDir, (char*)FileMapVA + debugDirOffset, sizeof(debugDir));
auto typeName = [](DWORD type)
{
switch(type)
{
case IMAGE_DEBUG_TYPE_UNKNOWN:
return "IMAGE_DEBUG_TYPE_UNKNOWN";
case IMAGE_DEBUG_TYPE_COFF:
return "IMAGE_DEBUG_TYPE_COFF";
case IMAGE_DEBUG_TYPE_CODEVIEW:
return "IMAGE_DEBUG_TYPE_CODEVIEW";
case IMAGE_DEBUG_TYPE_FPO:
return "IMAGE_DEBUG_TYPE_FPO";
case IMAGE_DEBUG_TYPE_MISC:
return "IMAGE_DEBUG_TYPE_MISC";
case IMAGE_DEBUG_TYPE_EXCEPTION:
return "IMAGE_DEBUG_TYPE_EXCEPTION";
case IMAGE_DEBUG_TYPE_FIXUP:
return "IMAGE_DEBUG_TYPE_FIXUP";
case IMAGE_DEBUG_TYPE_OMAP_TO_SRC:
return "IMAGE_DEBUG_TYPE_OMAP_TO_SRC";
case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC:
return "IMAGE_DEBUG_TYPE_OMAP_FROM_SRC";
case IMAGE_DEBUG_TYPE_BORLAND:
return "IMAGE_DEBUG_TYPE_BORLAND";
case IMAGE_DEBUG_TYPE_RESERVED10:
return "IMAGE_DEBUG_TYPE_RESERVED10";
case IMAGE_DEBUG_TYPE_CLSID:
return "IMAGE_DEBUG_TYPE_CLSID";
// The following types aren't defined in older Windows SDKs, so just count up from here so we can still return the names for them
case(IMAGE_DEBUG_TYPE_CLSID + 1):
return "IMAGE_DEBUG_TYPE_VC_FEATURE";
case(IMAGE_DEBUG_TYPE_CLSID + 2):
return "IMAGE_DEBUG_TYPE_POGO"; // For anyone grepping this: /NOCOFFGRPINFO is the undocumented linker switch to get rid of this crap. You're welcome
case(IMAGE_DEBUG_TYPE_CLSID + 3):
return "IMAGE_DEBUG_TYPE_ILTCG";
case(IMAGE_DEBUG_TYPE_CLSID + 4):
return "IMAGE_DEBUG_TYPE_MPX";
case(IMAGE_DEBUG_TYPE_CLSID + 5):
return "IMAGE_DEBUG_TYPE_REPRO";
default:
return "unknown";
}
}(debugDir.Type);
/*dprintf("IMAGE_DEBUG_DIRECTORY:\nCharacteristics: %08X\nTimeDateStamp: %08X\nMajorVersion: %04X\nMinorVersion: %04X\nType: %s\nSizeOfData: %08X\nAddressOfRawData: %08X\nPointerToRawData: %08X\n",
debugDir.Characteristics, debugDir.TimeDateStamp, debugDir.MajorVersion, debugDir.MinorVersion, typeName, debugDir.SizeOfData, debugDir.AddressOfRawData, debugDir.PointerToRawData);*/
if(debugDir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) //TODO: support other types (DBG)?
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Unsupported debug type %s in module %s%s...\n"), typeName, Info.name, Info.extension);
return;
}
struct CV_HEADER
{
DWORD Signature;
@ -450,40 +389,123 @@ void ReadDebugDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
BYTE PdbFileName[1];
};
auto codeViewOffset = (duint)ConvertVAtoFileOffsetEx(FileMapVA, Info.loadedSize, 0, debugDir.AddressOfRawData, true, false);;
if(!codeViewOffset)
const auto supported = [&Info](PIMAGE_DEBUG_DIRECTORY entry)
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Invalid debug directory for module %s%s...\n"), Info.name, Info.extension);
// Check for valid RVA
const auto offset = RvaToVa(0, Info.headers, entry->AddressOfRawData);
if(!offset)
return false;
// Check size is sane and end of data lies within the image
if(entry->SizeOfData < sizeof(CV_INFO_PDB20) /*smallest supported type*/ ||
entry->AddressOfRawData + entry->SizeOfData > HEADER_FIELD(Info.headers, SizeOfImage))
return false;
// Choose from one of our many supported types such as codeview
if(entry->Type == IMAGE_DEBUG_TYPE_CODEVIEW) // TODO: support other types (DBG)?
{
// Get the CV signature and do a final size check if it is valid
auto signature = *(DWORD*)(Info.fileMapVA + offset);
if(signature == '01BN')
return entry->SizeOfData >= sizeof(CV_INFO_PDB20) && entry->SizeOfData < sizeof(CV_INFO_PDB20) + DOS_MAX_PATH_LENGTH;
else if(signature == 'SDSR')
return entry->SizeOfData >= sizeof(CV_INFO_PDB70) && entry->SizeOfData < sizeof(CV_INFO_PDB70) + DOS_MAX_PATH_LENGTH;
else
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Unknown CodeView signature %08X for module %s%s...\n"), signature, Info.name, Info.extension);
return false;
}
}
return false;
};
// Iterate over entries until we find a CV one or the end of the directory
PIMAGE_DEBUG_DIRECTORY entry = debugDir;
while(debugDirSize >= sizeof(IMAGE_DEBUG_DIRECTORY))
{
if(supported(entry))
break;
const auto typeName = [](DWORD type)
{
switch(type)
{
case IMAGE_DEBUG_TYPE_UNKNOWN:
return "IMAGE_DEBUG_TYPE_UNKNOWN";
case IMAGE_DEBUG_TYPE_COFF:
return "IMAGE_DEBUG_TYPE_COFF";
case IMAGE_DEBUG_TYPE_CODEVIEW:
return "IMAGE_DEBUG_TYPE_CODEVIEW";
case IMAGE_DEBUG_TYPE_FPO:
return "IMAGE_DEBUG_TYPE_FPO";
case IMAGE_DEBUG_TYPE_MISC:
return "IMAGE_DEBUG_TYPE_MISC";
case IMAGE_DEBUG_TYPE_EXCEPTION:
return "IMAGE_DEBUG_TYPE_EXCEPTION";
case IMAGE_DEBUG_TYPE_FIXUP:
return "IMAGE_DEBUG_TYPE_FIXUP";
case IMAGE_DEBUG_TYPE_OMAP_TO_SRC:
return "IMAGE_DEBUG_TYPE_OMAP_TO_SRC";
case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC:
return "IMAGE_DEBUG_TYPE_OMAP_FROM_SRC";
case IMAGE_DEBUG_TYPE_BORLAND:
return "IMAGE_DEBUG_TYPE_BORLAND";
case IMAGE_DEBUG_TYPE_RESERVED10:
return "IMAGE_DEBUG_TYPE_RESERVED10";
case IMAGE_DEBUG_TYPE_CLSID:
return "IMAGE_DEBUG_TYPE_CLSID";
// The following types aren't defined in older Windows SDKs, so just count up from here so we can still return the names for them
case(IMAGE_DEBUG_TYPE_CLSID + 1):
return "IMAGE_DEBUG_TYPE_VC_FEATURE"; // How to kill: /NOVCFEATURE linker switch
case(IMAGE_DEBUG_TYPE_CLSID + 2):
return "IMAGE_DEBUG_TYPE_POGO"; // How to kill: /NOCOFFGRPINFO linker switch
case(IMAGE_DEBUG_TYPE_CLSID + 3):
return "IMAGE_DEBUG_TYPE_ILTCG";
case(IMAGE_DEBUG_TYPE_CLSID + 4):
return "IMAGE_DEBUG_TYPE_MPX";
case(IMAGE_DEBUG_TYPE_CLSID + 5):
return "IMAGE_DEBUG_TYPE_REPRO";
default:
return "unknown";
}
}(entry->Type);
/*dprintf("IMAGE_DEBUG_DIRECTORY:\nCharacteristics: %08X\nTimeDateStamp: %08X\nMajorVersion: %04X\nMinorVersion: %04X\nType: %s\nSizeOfData: %08X\nAddressOfRawData: %08X\nPointerToRawData: %08X\n",
debugDir->Characteristics, debugDir->TimeDateStamp, debugDir->MajorVersion, debugDir->MinorVersion, typeName, debugDir->SizeOfData, debugDir->AddressOfRawData, debugDir->PointerToRawData);*/
dprintf(QT_TRANSLATE_NOOP("DBG", "Skipping unsupported debug type %s in module %s%s...\n"), typeName, Info.name, Info.extension);
entry++;
debugDirSize -= sizeof(IMAGE_DEBUG_DIRECTORY);
}
if(!supported(entry))
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Did not find any supported debug types in module %s%s!\n"), Info.name, Info.extension);
return;
}
//TODO: bounds checking with debugDir.SizeOfData
auto signature = *(DWORD*)(FileMapVA + codeViewOffset);
// At this point we know the entry is a valid CV one
auto cvData = (unsigned char*)(FileMapVA + RvaToVa(0, Info.headers, entry->AddressOfRawData));
auto signature = *(DWORD*)cvData;
if(signature == '01BN')
{
auto cv = (CV_INFO_PDB20*)(FileMapVA + codeViewOffset);
auto cv = (CV_INFO_PDB20*)cvData;
Info.pdbSignature = StringUtils::sprintf("%X%X", cv->Signature, cv->Age);
Info.pdbFile = String((const char*)cv->PdbFileName);
Info.pdbFile = String((const char*)cv->PdbFileName, entry->SizeOfData - FIELD_OFFSET(CV_INFO_PDB20, PdbFileName));
Info.pdbValidation.signature = cv->Signature;
Info.pdbValidation.age = cv->Age;
}
else if(signature == 'SDSR')
{
auto cv = (CV_INFO_PDB70*)(FileMapVA + codeViewOffset);
auto cv = (CV_INFO_PDB70*)cvData;
Info.pdbSignature = StringUtils::sprintf("%08X%04X%04X%s%X",
cv->Signature.Data1, cv->Signature.Data2, cv->Signature.Data3,
StringUtils::ToHex(cv->Signature.Data4, 8).c_str(),
cv->Age);
Info.pdbFile = String((const char*)cv->PdbFileName);
Info.pdbFile = String((const char*)cv->PdbFileName, entry->SizeOfData - FIELD_OFFSET(CV_INFO_PDB70, PdbFileName));
memcpy(&Info.pdbValidation.guid, &cv->Signature, sizeof(GUID));
Info.pdbValidation.age = cv->Age;
}
else
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Unknown debug directory signature %08X for module %s%s...\n"), signature, Info.name, Info.extension);
return;
}
//dprintf("%s%s pdbSignature: %s, pdbFile: \"%s\"\n", Info.name, Info.extension, Info.pdbSignature.c_str(), Info.pdbFile.c_str());

View File

@ -357,6 +357,7 @@ bool PDBDiaFile::open(const wchar_t* file, uint64_t loadAddress, DiaValidationDa
}
// NOTE: For some reason this never matches, commented for now.
// ^ 99% sure this should only be used for PDB v2.0 ('NB10' ones). v7.0 PDBs should be checked using (age+guid) only
/*
DWORD signature = 0;
hr = globalSym->get_signature(&signature);