From 1f488862c6e74e7170d30283e3a26dcc4f057706 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Wed, 13 Oct 2021 17:34:38 -0700 Subject: [PATCH] Fix #246 - fixed CPLD extension area parser hang - added some definitions for CSME types obtained from MEParser --- common/ffs.cpp | 7 +++ common/ffs.h | 20 ++++--- common/ffsparser.cpp | 129 ++++++++++++++++++++----------------------- common/meparser.cpp | 14 +---- common/utility.cpp | 17 +++++- 5 files changed, 94 insertions(+), 93 deletions(-) diff --git a/common/ffs.cpp b/common/ffs.cpp index 3549989..cfb4292 100644 --- a/common/ffs.cpp +++ b/common/ffs.cpp @@ -175,6 +175,8 @@ UString bpdtEntryTypeToUString(const UINT16 type) case BPDT_ENTRY_TYPE_TCSS_FW_IOM: return UString("TCSS FW IOM"); case BPDT_ENTRY_TYPE_TCSS_FW_PHY: return UString("TCSS FW PHY"); case BPDT_ENTRY_TYPE_TBT: return UString("TCSS TBT"); + case BPDT_ENTRY_TYPE_SAMF: return UString("SAMF"); + case BPDT_ENTRY_TYPE_PPHY: return UString("PPHY"); default: return usprintf("Unknown %u", type); } } @@ -209,6 +211,11 @@ UString cpdExtensionTypeToUstring(const UINT32 type) case CPD_EXT_TYPE_IOM_METADATA: return UString("IOM Metadata"); case CPD_EXT_TYPE_MGP_METADATA: return UString("MGP Metadata"); case CPD_EXT_TYPE_TBT_METADATA: return UString("TBT Metadata"); + case CPD_EXT_TYPE_GMF_CERTIFICATE: return UString("Golden Measurement File Certificate"); + case CPD_EXT_TYPE_GMF_BODY: return UString("Golden Measurement File Body"); + case CPD_EXT_TYPE_KEY_MANIFEST_EXT: return UString("Extended Key Manifest"); + case CPD_EXT_TYPE_SIGNED_PACKAGE_INFO_EXT: return UString("Extended Signed Package Info"); + case CPD_EXT_TYPE_SPS_PLATFORM_ID: return UString("SPS Platform ID"); default: return usprintf("Unknown %u", type); } } diff --git a/common/ffs.h b/common/ffs.h index 0788ff0..b5e4979 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -648,7 +648,8 @@ typedef struct BPDT_ENTRY_ { #define BPDT_ENTRY_TYPE_TCSS_FW_IOM 23 #define BPDT_ENTRY_TYPE_TCSS_FW_PHY 24 #define BPDT_ENTRY_TYPE_TBT 25 -#define BPDT_LAST_KNOWN_ENTRY_TYPE BPDT_ENTRY_TYPE_TBT +#define BPDT_ENTRY_TYPE_SAMF 41 +#define BPDT_ENTRY_TYPE_PPHY 42 // CPD #define CPD_SIGNATURE 0x44504324 //$CPD @@ -740,7 +741,11 @@ typedef struct CPD_EXTENTION_HEADER_ { #define CPD_EXT_TYPE_IOM_METADATA 24 #define CPD_EXT_TYPE_MGP_METADATA 25 #define CPD_EXT_TYPE_TBT_METADATA 26 -#define CPD_LAST_KNOWN_EXT_TYPE CPD_EXT_TYPE_TBT_METADATA +#define CPD_EXT_TYPE_GMF_CERTIFICATE 30 +#define CPD_EXT_TYPE_GMF_BODY 31 +#define CPD_EXT_TYPE_KEY_MANIFEST_EXT 34 +#define CPD_EXT_TYPE_SIGNED_PACKAGE_INFO_EXT 35 +#define CPD_EXT_TYPE_SPS_PLATFORM_ID 50 typedef struct CPD_EXT_SIGNED_PACKAGE_INFO_MODULE_ { UINT8 Name[12]; @@ -748,7 +753,7 @@ typedef struct CPD_EXT_SIGNED_PACKAGE_INFO_MODULE_ { UINT8 HashAlgorithm; UINT16 HashSize; UINT32 MetadataSize; - UINT8 MetadataHash[32]; + UINT8 MetadataHash[1]; // Can be 32 or 48 bit } CPD_EXT_SIGNED_PACKAGE_INFO_MODULE; typedef struct CPD_EXT_SIGNED_PACKAGE_INFO_ { @@ -765,12 +770,11 @@ typedef struct CPD_EXT_SIGNED_PACKAGE_INFO_ { typedef struct CPD_EXT_MODULE_ATTRIBUTES_ { UINT32 ExtensionType; UINT32 ExtensionLength; - UINT8 CompressionType; - UINT8 Reserved[3]; + UINT32 CompressionType; UINT32 UncompressedSize; UINT32 CompressedSize; UINT32 GlobalModuleId; - UINT8 ImageHash[32]; + UINT8 ImageHash[1]; // The actual hash size is 32 or 48 bytes } CPD_EXT_MODULE_ATTRIBUTES; #define CPD_EXT_MODULE_COMPRESSION_TYPE_UNCOMPRESSED 0 @@ -797,8 +801,8 @@ typedef struct CPD_EXT_IFWI_PARTITION_MANIFEST_ { UINT32 ReservedFlags : 23; UINT32 HashAlgorithm : 8; UINT32 HashSize : 24; - UINT8 CompletePartitionHash[32]; - UINT8 Reserved[20]; + UINT8 CompletePartitionHash[48]; + UINT8 Reserved[4]; } CPD_EXT_IFWI_PARTITION_MANIFEST; // Restore previous packing rules diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index bdc10cd..749a3ac 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -3871,10 +3871,10 @@ USTATUS FfsParser::parseFit(const UModelIndex & index) // Perform validation of BootGuard stuff if (bgAcmFound) { if (!bgKeyManifestFound) { - msg(usprintf("%s: ACM found, but KeyManifest isn't", __FUNCTION__), acmIndex); + msg(usprintf("%s: ACM found, but KeyManifest is not", __FUNCTION__), acmIndex); } else if (!bgBootPolicyFound) { - msg(usprintf("%s: ACM and KeyManifest found, BootPolicy isn't", __FUNCTION__), kmIndex); + msg(usprintf("%s: ACM and KeyManifest found, BootPolicy is not", __FUNCTION__), kmIndex); } else { // Check key hashes @@ -4693,10 +4693,13 @@ make_partition_table_consistent: if (readUnaligned((const UINT32*)partition.constData()) == CPD_SIGNATURE) { // Parse code partition contents UModelIndex cpdIndex; - parseCpdRegion(partition, localOffset, partitionIndex, cpdIndex); + parseCpdRegion(partition, 0, partitionIndex, cpdIndex); } - if (partitions[i].ptEntry.Type > BPDT_LAST_KNOWN_ENTRY_TYPE) { + // TODO: make this generic again + if (partitions[i].ptEntry.Type > BPDT_ENTRY_TYPE_TBT + && partitions[i].ptEntry.Type != BPDT_ENTRY_TYPE_SAMF + && partitions[i].ptEntry.Type != BPDT_ENTRY_TYPE_PPHY) { msg(usprintf("%s: BPDT entry of unknown type found", __FUNCTION__), partitionIndex); } } @@ -4765,11 +4768,11 @@ USTATUS FfsParser::parseCpdRegion(const UByteArray & region, const UINT32 localO // Get info UByteArray header = region.left(ptHeaderSize); - UByteArray body = region.mid(ptHeaderSize); + UByteArray body = region.mid(ptHeaderSize, ptBodySize); UString name = usprintf("CPD partition table"); UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nNumber of entries: %u\n" - "Header version: %02X\nEntry version: %02X", - region.size(), region.size(), + "Header version: %u\nEntry version: %u", + ptSize, ptSize, header.size(), header.size(), body.size(), body.size(), cpdHeader->NumEntries, @@ -4832,8 +4835,8 @@ USTATUS FfsParser::parseCpdRegion(const UByteArray & region, const UINT32 localO std::sort(partitions.begin(), partitions.end()); // Because lenghts for all Huffmann-compressed partitions mean nothing at all, we need to split all partitions into 2 classes: - // 1. CPD manifest (should be the first) - // 2. Metadata entries (should begin right after partition manifest and end before any code partition) + // 1. CPD manifest + // 2. Metadata entries UINT32 i = 1; // manifest is index 0, .met partitions start at index 1 while (i < partitions.size()) { name = usprintf("%.12s", partitions[i].ptEntry.EntryName); @@ -4866,7 +4869,7 @@ USTATUS FfsParser::parseCpdRegion(const UByteArray & region, const UINT32 localO // Search bool found = false; - UINT32 j = i + 1; + UINT32 j = 1; while (j < partitions.size()) { UString namej = usprintf("%.12s", partitions[j].ptEntry.EntryName); @@ -4991,28 +4994,31 @@ make_partition_table_consistent: name = usprintf("%.12s", partitions[i].ptEntry.EntryName); // It's a manifest - if (name.contains(".man")) { + if (name.endsWith(".man")) { if (!partitions[i].ptEntry.Offset.HuffmanCompressed && partitions[i].ptEntry.Length >= sizeof(CPD_MANIFEST_HEADER)) { const CPD_MANIFEST_HEADER* manifestHeader = (const CPD_MANIFEST_HEADER*) partition.constData(); if (manifestHeader->HeaderId == ME_MANIFEST_HEADER_ID) { UByteArray header = partition.left(manifestHeader->HeaderLength * sizeof(UINT32)); - UByteArray body = partition.mid(header.size()); + UByteArray body = partition.mid(manifestHeader->HeaderLength * sizeof(UINT32)); - info += usprintf( - "\nHeader type: %u\nHeader length: %lXh (%lu)\nHeader version: %Xh\nFlags: %08Xh\nVendor: %Xh\n" - "Date: %Xh\nSize: %lXh (%lu)\nVersion: %u.%u.%u.%u\nSecurity version number: %u\nModulus size: %lXh (%lu)\nExponent size: %lXh (%lu)", - manifestHeader->HeaderType, - manifestHeader->HeaderLength * sizeof(UINT32), manifestHeader->HeaderLength * sizeof(UINT32), - manifestHeader->HeaderVersion, - manifestHeader->Flags, - manifestHeader->Vendor, - manifestHeader->Date, - manifestHeader->Size * sizeof(UINT32), manifestHeader->Size * sizeof(UINT32), - manifestHeader->VersionMajor, manifestHeader->VersionMinor, manifestHeader->VersionBugfix, manifestHeader->VersionBuild, - manifestHeader->SecurityVersion, - manifestHeader->ModulusSize * sizeof(UINT32), manifestHeader->ModulusSize * sizeof(UINT32), - manifestHeader->ExponentSize * sizeof(UINT32), manifestHeader->ExponentSize * sizeof(UINT32)); + info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")" + "\nHeader type: %u\nHeader length: %lXh (%lu)\nHeader version: %Xh\nFlags: %08Xh\nVendor: %Xh\n" + "Date: %Xh\nSize: %lXh (%lu)\nVersion: %u.%u.%u.%u\nSecurity version number: %u\nModulus size: %lXh (%lu)\nExponent size: %lXh (%lu)", + partition.size(), partition.size(), + header.size(), header.size(), + body.size(), body.size(), + manifestHeader->HeaderType, + manifestHeader->HeaderLength * sizeof(UINT32), manifestHeader->HeaderLength * sizeof(UINT32), + manifestHeader->HeaderVersion, + manifestHeader->Flags, + manifestHeader->Vendor, + manifestHeader->Date, + manifestHeader->Size * sizeof(UINT32), manifestHeader->Size * sizeof(UINT32), + manifestHeader->VersionMajor, manifestHeader->VersionMinor, manifestHeader->VersionBugfix, manifestHeader->VersionBuild, + manifestHeader->SecurityVersion, + manifestHeader->ModulusSize * sizeof(UINT32), manifestHeader->ModulusSize * sizeof(UINT32), + manifestHeader->ExponentSize * sizeof(UINT32), manifestHeader->ExponentSize * sizeof(UINT32)); // Add tree item UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::ManifestCpdPartition, name, UString(), info, header, body, UByteArray(), Fixed, parent); @@ -5023,11 +5029,9 @@ make_partition_table_consistent: } } // It's a metadata - else if (name.contains(".met")) { - info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", - partition.size(), partition.size(), - partitions[i].ptEntry.Offset.Offset, - partitions[i].ptEntry.Length) + else if (name.endsWith(".met")) { + info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nHuffman compressed: ", + partition.size(), partition.size()) + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No"); // Calculate SHA256 hash over the metadata and add it to its info @@ -5041,31 +5045,10 @@ make_partition_table_consistent: // Parse data as extensions area parseCpdExtensionsArea(partitionIndex); } - // It's a key - else if (name.contains(".key")) { - info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", - partition.size(), partition.size(), - partitions[i].ptEntry.Offset.Offset, - partitions[i].ptEntry.Length) - + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No"); - - // Calculate SHA256 hash over the key and add it to its info - UByteArray hash(SHA256_DIGEST_SIZE, '\x00'); - sha256(partition.constData(), partition.size(), hash.data()); - info += UString("\nHash: ") + UString(hash.toHex().constData()); - - // Add three item - UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::KeyCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); - - // Parse data as extensions area - parseCpdExtensionsArea(partitionIndex); - } // It's a code else { - info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", - partition.size(), partition.size(), - partitions[i].ptEntry.Offset.Offset, - partitions[i].ptEntry.Length) + info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nHuffman compressed: ", + partition.size(), partition.size()) + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No"); // Calculate SHA256 hash over the code and add it to its info @@ -5106,7 +5089,7 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index) UINT32 offset = 0; while (offset < (UINT32)body.size()) { const CPD_EXTENTION_HEADER* extHeader = (const CPD_EXTENTION_HEADER*) (body.constData() + offset); - if (extHeader->Length <= ((UINT32)body.size() - offset)) { + if (extHeader->Length > 0 && extHeader->Length <= ((UINT32)body.size() - offset)) { UByteArray partition = body.mid(offset, extHeader->Length); UString name = cpdExtensionTypeToUstring(extHeader->Type); @@ -5142,10 +5125,18 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index) // Parse IFWI Partition Manifest a bit further else if (extHeader->Type == CPD_EXT_TYPE_IFWI_PARTITION_MANIFEST) { const CPD_EXT_IFWI_PARTITION_MANIFEST* attrHeader = (const CPD_EXT_IFWI_PARTITION_MANIFEST*)partition.constData(); - + + // Check HashSize to be sane. + UINT32 hashSize = attrHeader->HashSize; + bool msgHashSizeMismatch = false; + if (hashSize > sizeof(attrHeader->CompletePartitionHash)) { + hashSize = sizeof(attrHeader->CompletePartitionHash); + msgHashSizeMismatch = true; + } + // This hash is stored reversed // Need to reverse it back to normal - UByteArray hash((const char*)&attrHeader->CompletePartitionHash, attrHeader->HashSize); + UByteArray hash((const char*)&attrHeader->CompletePartitionHash, hashSize); std::reverse(hash.begin(), hash.end()); info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nType: %Xh\n" @@ -5172,14 +5163,13 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index) // Add tree item extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); - if (sizeof (attrHeader->CompletePartitionHash) != attrHeader->HashSize) { - msg(usprintf("%s: IFWI Partition Manifest hash size is %d, expected %lu", __FUNCTION__, attrHeader->HashSize, sizeof (attrHeader->CompletePartitionHash)), extIndex); + if (msgHashSizeMismatch) { + msg(usprintf("%s: IFWI Partition Manifest hash size is %u, maximum allowed is %lu, truncated", __FUNCTION__, attrHeader->HashSize, sizeof(attrHeader->CompletePartitionHash)), extIndex); } } // Parse Module Attributes a bit further else if (extHeader->Type == CPD_EXT_TYPE_MODULE_ATTRIBUTES) { const CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const CPD_EXT_MODULE_ATTRIBUTES*)partition.constData(); - int hashSize = partition.size() - offsetof(CPD_EXT_MODULE_ATTRIBUTES, ImageHash); // This hash is stored reversed @@ -5198,9 +5188,6 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index) // Add tree item extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); - if (hashSize != sizeof (attrHeader->ImageHash)) { - msg(usprintf("%s: Module Attributes hash size is %d, expected %lu", __FUNCTION__, hashSize, sizeof (attrHeader->ImageHash)), extIndex); - } } // Parse everything else else { @@ -5208,7 +5195,13 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index) extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); } - if (extHeader->Type > CPD_LAST_KNOWN_EXT_TYPE) { + // TODO: make this generic again + if (extHeader->Type > CPD_EXT_TYPE_TBT_METADATA + && extHeader->Type != CPD_EXT_TYPE_GMF_CERTIFICATE + && extHeader->Type != CPD_EXT_TYPE_GMF_BODY + && extHeader->Type != CPD_EXT_TYPE_KEY_MANIFEST_EXT + && extHeader->Type != CPD_EXT_TYPE_SIGNED_PACKAGE_INFO_EXT + && extHeader->Type != CPD_EXT_TYPE_SPS_PLATFORM_ID) { msg(usprintf("%s: CPD extention of unknown type found", __FUNCTION__), extIndex); } @@ -5232,8 +5225,8 @@ USTATUS FfsParser::parseSignedPackageInfoData(const UModelIndex & index) while (offset < (UINT32)body.size()) { const CPD_EXT_SIGNED_PACKAGE_INFO_MODULE* moduleHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO_MODULE*)(body.constData() + offset); if (sizeof(CPD_EXT_SIGNED_PACKAGE_INFO_MODULE) <= ((UINT32)body.size() - offset)) { - UByteArray module((const char*)moduleHeader, sizeof(CPD_EXT_SIGNED_PACKAGE_INFO_MODULE) - sizeof (moduleHeader->MetadataHash) + moduleHeader->HashSize); - + // TODO: check sanity of moduleHeader->HashSize + UByteArray module((const char*)moduleHeader, sizeof(CPD_EXT_SIGNED_PACKAGE_INFO_MODULE) - sizeof(moduleHeader->MetadataHash) + moduleHeader->HashSize); UString name = usprintf("%.12s", moduleHeader->Name); // This hash is stored reversed @@ -5249,10 +5242,6 @@ USTATUS FfsParser::parseSignedPackageInfoData(const UModelIndex & index) moduleHeader->MetadataSize, moduleHeader->MetadataSize) + UString(hash.toHex().constData()); // Add tree otem UModelIndex extIndex = model->addItem(offset, Types::CpdSpiEntry, 0, name, UString(), info, UByteArray(), module, UByteArray(), Fixed, index); - if (sizeof (moduleHeader->MetadataHash) != moduleHeader->HashSize) { - msg(usprintf("%s: CPD Signed Package Info hash size is %d, expected %lu", __FUNCTION__, moduleHeader->HashSize, sizeof (moduleHeader->MetadataHash)), extIndex); - } - offset += module.size(); } else break; diff --git a/common/meparser.cpp b/common/meparser.cpp index 54aa436..1b3dc1f 100755 --- a/common/meparser.cpp +++ b/common/meparser.cpp @@ -126,13 +126,6 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & return U_INVALID_ME_PARTITION_TABLE; } - // Recalculate checksum - UByteArray tempHeader = UByteArray((const char*)region.constData(), romBypassVectorSize + sizeof(FPT_HEADER)); - FPT_HEADER* tempPtHeader = (FPT_HEADER*)(tempHeader.data() + romBypassVectorSize); - tempPtHeader->Checksum = 0; - UINT8 calculated = calculateChecksum8((const UINT8*)tempHeader.data(), romBypassVectorSize + sizeof(FPT_HEADER)); - bool msgInvalidPtHeaderChecksum = (calculated != ptHeader->Checksum); - // Get info UByteArray header = region.left(romBypassVectorSize + sizeof(FPT_HEADER)); UByteArray body = region.mid(header.size(), ptBodySize); @@ -153,16 +146,11 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & ptHeader->UmaSize, ptHeader->FlashLayout, ptHeader->FitcMajor, ptHeader->FitcMinor, ptHeader->FitcHotfix, ptHeader->FitcBuild, - ptHeader->Checksum) + (ptHeader->Checksum == calculated ? UString("valid\n") : usprintf("invalid, should be %02Xh\n", calculated)); + ptHeader->Checksum); // Add tree item index = model->addItem(0, Types::FptStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); - // Show messages - if (msgInvalidPtHeaderChecksum) { - msg(usprintf("%s: FPT partition table header checksum is invalid", __FUNCTION__), index); - } - // Add partition table entries std::vector partitions; UINT32 offset = (UINT32)header.size(); diff --git a/common/utility.cpp b/common/utility.cpp index a2f77f2..f1e623e 100755 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -33,10 +33,23 @@ UString visibleAsciiOrHex(UINT8* bytes, UINT32 length) for (UINT32 i = 0; i < length; i++) { hexString += usprintf("%02X", bytes[i]); - if (bytes[i] < '\x20' || bytes[i] > '\x7E') { // Explicit ascii codes to avoid locale dependency + if (bytes[i] == '\x00') { // Check for the rest of the buffer being zeroes, and make the whole previous string visible, if so + for (UINT32 j = i + 1; j < length; j++) { + if (bytes[j] != '\x00') { + ascii = false; + break; + } + } + + if (ascii) { + // No need to continue iterating over every symbol, we did it already + break; + } + } + else if (bytes[i] < '\x20' || bytes[i] > '\x7E') { // Explicit ascii codes to avoid locale dependency ascii = false; } - + if (ascii) { asciiString += usprintf("%c", bytes[i]); }