diff --git a/src/dbg/module.cpp b/src/dbg/module.cpp index f660e59b..7e68a4c4 100644 --- a/src/dbg/module.cpp +++ b/src/dbg/module.cpp @@ -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()); diff --git a/src/dbg/pdbdiafile.cpp b/src/dbg/pdbdiafile.cpp index ad0523c9..5f83df0f 100644 --- a/src/dbg/pdbdiafile.cpp +++ b/src/dbg/pdbdiafile.cpp @@ -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);