From 2e7aa8133a8b3d03ee2105bad66f1081b30e3b54 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Wed, 24 Jul 2019 10:30:59 -0700 Subject: [PATCH] Initial ME parser, improved ucode parser, reset vector info --- UEFITool/uefitool.cpp | 36 +- UEFITool/uefitool.pro | 2 + common/Tiano/EfiTianoDecompress.c | 4 - common/basetypes.h | 13 + common/ffs.cpp | 30 +- common/ffs.h | 15 + common/ffsparser.cpp | 305 +++++--- common/ffsparser.h | 1 + common/ffsreport.cpp | 2 +- common/fit.h | 63 +- common/meparser.cpp | 1169 +++++++++++++++++++++++++++++ common/meparser.h | 230 +++++- common/nvram.h | 1 + common/nvramparser.cpp | 52 +- common/types.cpp | 34 + common/types.h | 38 + common/utility.cpp | 25 +- common/utility.h | 3 + common/zlib/gzread.c | 3 + common/zlib/gzwrite.c | 3 + version.h | 4 +- 21 files changed, 1867 insertions(+), 166 deletions(-) mode change 100644 => 100755 common/fit.h create mode 100755 common/meparser.cpp mode change 100644 => 100755 common/meparser.h mode change 100644 => 100755 common/nvram.h mode change 100644 => 100755 common/nvramparser.cpp mode change 100644 => 100755 common/types.cpp mode change 100644 => 100755 common/types.h mode change 100644 => 100755 common/utility.cpp mode change 100644 => 100755 common/utility.h diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index 354014f..c3d0ff9 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -192,11 +192,24 @@ void UEFITool::populateUi(const QModelIndex ¤t) ui->menuVolumeActions->setEnabled(type == Types::Volume); ui->menuFileActions->setEnabled(type == Types::File); ui->menuSectionActions->setEnabled(type == Types::Section); - ui->menuEntryActions->setEnabled(type == Types::NvarEntry + ui->menuEntryActions->setEnabled(type == Types::Microcode + || type == Types::SlicData + || type == Types::NvarEntry || type == Types::VssEntry || type == Types::FsysEntry || type == Types::EvsaEntry - || type == Types::FlashMapEntry); + || type == Types::FlashMapEntry + || type == Types::IfwiHeader + || type == Types::IfwiPartition + || type == Types::FptPartition + || type == Types::FptEntry + || type == Types::BpdtPartition + || type == Types::BpdtEntry + || type == Types::CpdPartition + || type == Types::CpdEntry + || type == Types::CpdExtension + || type == Types::CpdSpiEntry + ); ui->menuStoreActions->setEnabled(type == Types::VssStore || type == Types::Vss2Store || type == Types::FdcStore @@ -204,9 +217,11 @@ void UEFITool::populateUi(const QModelIndex ¤t) || type == Types::EvsaStore || type == Types::FtwStore || type == Types::FlashMapStore - || type == Types::CmdbStore - || type == Types::Microcode - || type == Types::SlicData); + || type == Types::CmdbStore + || type == Types::FptStore + || type == Types::BpdtStore + || type == Types::CpdStore + ); // Enable actions ui->actionHexView->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current) && model->hasEmptyTail(current)); @@ -481,7 +496,7 @@ void UEFITool::extract(const UINT8 mode) if (subtype == Subtypes::PubkeySlicData) path = QFileDialog::getSaveFileName(this, tr("Save SLIC pubkey to file"), name + ".spk", tr("SLIC pubkey files (*.spk *.bin);;All files (*)")); else path = QFileDialog::getSaveFileName(this, tr("Save SLIC marker to file"), name + ".smk", tr("SLIC marker files (*.smk *.bin);;All files (*)")); break; - default: path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", tr("Binary files (*.bin);;All files (*)")); + default: path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", tr("Binary files (*.bin);;All files (*)")); } } else if (mode == EXTRACT_MODE_BODY || mode == EXTRACT_MODE_BODY_UNCOMPRESSED) { @@ -553,7 +568,7 @@ void UEFITool::remove() void UEFITool::about() { QMessageBox::about(this, tr("About UEFITool"), tr( - "Copyright (c) 2018, LongSoft.
" + "Copyright (c) 2019, Nikolaj Schlej.
" "Program icon made by Alexander Zhidkov.
" "The program uses QHexEdit2 library made by Simsys.
" "Qt-less engine is using Bstrlib made by Paul Hsieh.

" @@ -858,8 +873,11 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event) case Types::EvsaStore: case Types::FtwStore: case Types::FlashMapStore: - case Types::CmdbStore: ui->menuStoreActions->exec(event->globalPos()); break; - case Types::FreeSpace: break; + case Types::CmdbStore: + case Types::FptStore: + case Types::CpdStore: + case Types::BpdtStore: ui->menuStoreActions->exec(event->globalPos()); break; + case Types::FreeSpace: break; // No menu needed for FreeSpace item default: ui->menuEntryActions->exec(event->globalPos()); break; } } diff --git a/UEFITool/uefitool.pro b/UEFITool/uefitool.pro index 4225e51..64cfc18 100644 --- a/UEFITool/uefitool.pro +++ b/UEFITool/uefitool.pro @@ -5,6 +5,7 @@ TARGET = UEFITool TEMPLATE = app DEFINES += "U_ENABLE_FIT_PARSING_SUPPORT" DEFINES += "U_ENABLE_NVRAM_PARSING_SUPPORT" +DEFINES += "U_ENABLE_ME_PARSING_SUPPORT" DEFINES += "U_ENABLE_GUID_DATABASE_SUPPORT" HEADERS += uefitool.h \ @@ -70,6 +71,7 @@ SOURCES += uefitool_main.cpp \ ../common/guiddatabase.cpp \ ../common/nvram.cpp \ ../common/nvramparser.cpp \ + ../common/meparser.cpp \ ../common/ffsops.cpp \ ../common/types.cpp \ ../common/descriptor.cpp \ diff --git a/common/Tiano/EfiTianoDecompress.c b/common/Tiano/EfiTianoDecompress.c index 41f38ec..fbcd8ca 100644 --- a/common/Tiano/EfiTianoDecompress.c +++ b/common/Tiano/EfiTianoDecompress.c @@ -711,10 +711,6 @@ Decode ( UINT32 DataIdx; UINT16 CharC; - BytesRemain = (UINT16)(-1); - - DataIdx = 0; - for (;;) { // // Get one code from mBitBuf diff --git a/common/basetypes.h b/common/basetypes.h index de4ae3b..a16030f 100644 --- a/common/basetypes.h +++ b/common/basetypes.h @@ -68,6 +68,19 @@ typedef size_t USTATUS; #define U_INVALID_BG_BOOT_POLICY 46 #define U_INVALID_TXT_CONF 47 #define U_ELEMENTS_NOT_FOUND 48 +#define U_PEI_CORE_ENTRY_POINT_NOT_FOUND 49 +#define U_INVALID_STORE_SIZE 50 +#define U_UNKNOWN_COMPRESSION_ALGORITHM 51 +#define U_NOTHING_TO_PATCH 52 +#define U_UNKNOWN_PATCH_TYPE 53 +#define U_PATCH_OFFSET_OUT_OF_BOUNDS 54 +#define U_INVALID_SYMBOL 55 + +#define U_INVALID_MANIFEST 251 +#define U_UNKNOWN_MANIFEST_HEADER_VERSION 252 +#define U_INVALID_ME_PARTITION_TABLE 253 +#define U_INVALID_ME_PARTITION 254 + #define U_NOT_IMPLEMENTED 0xFF // EDK2 porting definitions diff --git a/common/ffs.cpp b/common/ffs.cpp index ed414a8..de9a274 100644 --- a/common/ffs.cpp +++ b/common/ffs.cpp @@ -39,16 +39,16 @@ const UINT8 ffsAlignment2Table[] = VOID uint32ToUint24(UINT32 size, UINT8* ffsSize) { - ffsSize[2] = (UINT8)((size) >> 16); - ffsSize[1] = (UINT8)((size) >> 8); + ffsSize[2] = (UINT8)((size) >> 16U); + ffsSize[1] = (UINT8)((size) >> 8U); ffsSize[0] = (UINT8)((size)); } UINT32 uint24ToUint32(const UINT8* ffsSize) { return (UINT32) ffsSize[0] - + ((UINT32) ffsSize[1] << 8U) - + ((UINT32) ffsSize[2] << 16U); + + ((UINT32) ffsSize[1] << 8U) + + ((UINT32) ffsSize[2] << 16U); } UString guidToUString(const EFI_GUID & guid, bool convertToString) @@ -84,17 +84,17 @@ bool ustringToGuid(const UString & str, EFI_GUID & guid) if (err == 0) return false; - guid.Data1 = p0; - guid.Data2 = p1; - guid.Data3 = p2; - guid.Data4[0] = p3; - guid.Data4[1] = p4; - guid.Data4[2] = p5; - guid.Data4[3] = p6; - guid.Data4[4] = p7; - guid.Data4[5] = p8; - guid.Data4[6] = p9; - guid.Data4[7] = p10; + guid.Data1 = (UINT32)p0; + guid.Data2 = (UINT16)p1; + guid.Data3 = (UINT16)p2; + guid.Data4[0] = (UINT8)p3; + guid.Data4[1] = (UINT8)p4; + guid.Data4[2] = (UINT8)p5; + guid.Data4[3] = (UINT8)p6; + guid.Data4[4] = (UINT8)p7; + guid.Data4[5] = (UINT8)p8; + guid.Data4[6] = (UINT8)p9; + guid.Data4[7] = (UINT8)p10; return true; } diff --git a/common/ffs.h b/common/ffs.h index 4103961..4a7ccae 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -571,6 +571,21 @@ typedef struct POSTCODE_SECTION_ { /// #define EFI_DEP_SOR 0x09 +//***************************************************************************** +// X86 Reset Vector Data +//***************************************************************************** +typedef struct X86_RESET_VECTOR_DATA_ { + UINT8 ApEntryVector[8]; // Base: 0xffffffd0 + UINT8 Reserved0[8]; + UINT32 PeiCoreEntryPoint; // Base: 0xffffffe0 + UINT8 Reserved1[12]; + UINT8 ResetVector[8]; // Base: 0xfffffff0 + UINT32 ApStartupSegment; // Base: 0xfffffff8 + UINT32 BootFvBaseAddress; // Base: 0xfffffffc +} X86_RESET_VECTOR_DATA; + +#define X86_RESET_VECTOR_DATA_UNPOPULATED 0x12345678 + // Restore previous packing rules #pragma pack(pop) diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 0336026..7432f0e 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -120,7 +120,7 @@ USTATUS FfsParser::parse(const UByteArray & buffer) USTATUS FfsParser::performFirstPass(const UByteArray & buffer, UModelIndex & index) { - // Sanity check + // Sanity check if (buffer.isEmpty()) { return EFI_INVALID_PARAMETER; } @@ -756,7 +756,7 @@ USTATUS FfsParser::parsePdrRegion(const UByteArray & pdr, const UINT32 localOffs // Parse PDR region as BIOS space USTATUS result = parseRawArea(index); - if (result && result != U_VOLUMES_NOT_FOUND && result != U_INVALID_VOLUME) + if (result && result != U_VOLUMES_NOT_FOUND && result != U_INVALID_VOLUME && result != U_STORES_NOT_FOUND) return result; return U_SUCCESS; @@ -910,8 +910,8 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) prevItemType = itemType; result = findNextRawAreaItem(index, itemOffset + prevItemSize, itemType, itemOffset, itemSize, itemAltSize); - // Silence value not used after assignment warning - (void)prevItemType; + // Silence value not used after assignment warning + (void)prevItemType; } // Padding at the end of RAW area @@ -1058,7 +1058,7 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc UINT32 usedSpace = *(UINT32*)(volume.constData() + 12); if (appleCrc32 != 0) { // Calculate CRC32 of the volume body - UINT32 crc = crc32(0, (const UINT8*)(volume.constData() + volumeHeader->HeaderLength), volumeSize - volumeHeader->HeaderLength); + UINT32 crc = (UINT32)crc32(0, (const UINT8*)(volume.constData() + volumeHeader->HeaderLength), volumeSize - volumeHeader->HeaderLength); if (crc == appleCrc32) { hasAppleCrc32 = true; } @@ -1161,26 +1161,34 @@ USTATUS FfsParser::findNextRawAreaItem(const UModelIndex & index, const UINT32 l for (; offset < dataSize - sizeof(UINT32); offset++) { const UINT32* currentPos = (const UINT32*)(data.constData() + offset); const UINT32 restSize = dataSize - offset; - if (readUnaligned(currentPos) == INTEL_MICROCODE_HEADER_VERSION) {// Intel microcode + if (readUnaligned(currentPos) == INTEL_MICROCODE_HEADER_VERSION_1) {// Intel microcode // Check data size if (restSize < sizeof(INTEL_MICROCODE_HEADER)) continue; // Check microcode size const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos; - if (!INTEL_MICROCODE_HEADER_SIZES_VALID(currentPos) || restSize < ucodeHeader->TotalSize) //TODO: needs a separate checking function - continue; // Check reserved bytes bool reservedBytesValid = true; for (UINT32 i = 0; i < sizeof(ucodeHeader->Reserved); i++) - if (ucodeHeader->Reserved[i] != INTEL_MICROCODE_HEADER_RESERVED_BYTE) { + if (ucodeHeader->Reserved[i] != 0x00) { reservedBytesValid = false; break; } if (!reservedBytesValid) continue; + // Data size is multiple of 4 + if (ucodeHeader->DataSize % 4 != 0) { + continue; + } + + // TotalSize is greater then DataSize and is multiple of 1024 + if (ucodeHeader->TotalSize <= ucodeHeader->DataSize || ucodeHeader->TotalSize % 1024 != 0) { + continue; + } + // All checks passed, microcode found nextItemType = Types::Microcode; nextItemSize = ucodeHeader->TotalSize; @@ -1442,7 +1450,6 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf if (file.isEmpty()) { return U_INVALID_PARAMETER; } - if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER)) { return U_INVALID_FILE; } @@ -1480,14 +1487,16 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf } UINT32 alignment = (UINT32)(1UL << alignmentPower); - if ((localOffset + header.size()) % alignment) + if ((localOffset + header.size()) % alignment) { msgUnalignedFile = true; - + } + // Check file alignment agains volume alignment bool msgFileAlignmentIsGreaterThanVolumeAlignment = false; - if (!isWeakAligned && volumeAlignment < alignment) + if (!isWeakAligned && volumeAlignment < alignment) { msgFileAlignmentIsGreaterThanVolumeAlignment = true; - + } + // Get file body UByteArray body = file.mid(header.size()); @@ -1508,9 +1517,10 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf // Check header checksum UINT8 calculatedHeader = 0x100 - (calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->IntegrityCheck.Checksum.Header - fileHeader->IntegrityCheck.Checksum.File - fileHeader->State); bool msgInvalidHeaderChecksum = false; - if (fileHeader->IntegrityCheck.Checksum.Header != calculatedHeader) + if (fileHeader->IntegrityCheck.Checksum.Header != calculatedHeader) { msgInvalidHeaderChecksum = true; - + } + // Check data checksum // Data checksum must be calculated bool msgInvalidDataChecksum = false; @@ -1526,9 +1536,10 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf calculatedData = FFS_FIXED_CHECKSUM2; } - if (fileHeader->IntegrityCheck.Checksum.File != calculatedData) + if (fileHeader->IntegrityCheck.Checksum.File != calculatedData) { msgInvalidDataChecksum = true; - + } + // Check file type bool msgUnknownType = false; if (fileHeader->Type > EFI_FV_FILETYPE_MM_CORE_STANDALONE && fileHeader->Type != EFI_FV_FILETYPE_PAD) { @@ -1663,19 +1674,16 @@ USTATUS FfsParser::parseFileBody(const UModelIndex & index) model->setText(index, UString("NVAR store")); return nvramParser->parseNvarStore(index); } - - if (fileGuid == NVRAM_NVAR_PEI_EXTERNAL_DEFAULTS_FILE_GUID) { + else if (fileGuid == NVRAM_NVAR_PEI_EXTERNAL_DEFAULTS_FILE_GUID) { model->setText(index, UString("NVRAM external defaults")); return nvramParser->parseNvarStore(index); } - - if (fileGuid == NVRAM_NVAR_BB_DEFAULTS_FILE_GUID) { + else if (fileGuid == NVRAM_NVAR_BB_DEFAULTS_FILE_GUID) { model->setText(index, UString("NVAR bb defaults")); return nvramParser->parseNvarStore(index); } - // Parse vendor hash file - if (fileGuid == BG_VENDOR_HASH_FILE_GUID_PHOENIX) { + else if (fileGuid == BG_VENDOR_HASH_FILE_GUID_PHOENIX) { return parseVendorHashFile(fileGuid, index); } @@ -2081,7 +2089,7 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI UINT32 crc = *(UINT32*)(section.constData() + headerSize); additionalInfo += UString("\nChecksum type: CRC32"); // Calculate CRC32 of section data - UINT32 calculated = crc32(0, (const UINT8*)section.constData() + dataOffset, section.size() - dataOffset); + UINT32 calculated = (UINT32)crc32(0, (const UINT8*)section.constData() + dataOffset, section.size() - dataOffset); if (crc == calculated) { additionalInfo += usprintf("\nChecksum: %08Xh, valid", crc); } @@ -2995,6 +3003,9 @@ USTATUS FfsParser::performSecondPass(const UModelIndex & index) const UINT32 vtfSize = model->header(lastVtf).size() + model->body(lastVtf).size() + model->tail(lastVtf).size(); addressDiff = 0xFFFFFFFFULL - model->base(lastVtf) - vtfSize + 1; + // Parse reset vector data + parseResetVectorData(); + // Find and parse FIT parseFit(index); @@ -3007,6 +3018,37 @@ USTATUS FfsParser::performSecondPass(const UModelIndex & index) return U_SUCCESS; } +USTATUS FfsParser::parseResetVectorData() +{ + // Sanity check + if (!lastVtf.isValid()) + return U_SUCCESS; + + // Check VTF to have enough space at the end to fit Reset Vector Data + UByteArray vtf = model->header(lastVtf) + model->body(lastVtf) + model->tail(lastVtf); + if ((UINT32)vtf.size() < sizeof(X86_RESET_VECTOR_DATA)) + return U_SUCCESS; + + const X86_RESET_VECTOR_DATA* resetVectorData = (const X86_RESET_VECTOR_DATA*)(vtf.constData() + vtf.size() - sizeof(X86_RESET_VECTOR_DATA)); + + // Add info + UString info = usprintf("\nAP entry vector: %02X %02X %02X %02X %02X %02X %02X %02X\n" + "Reset vector: %02X %02X %02X %02X %02X %02X %02X %02X\n" + "PEI core entry point: %08Xh\n" + "AP startup segment: %08X\n" + "BootFV base address: %08X\n", + resetVectorData->ApEntryVector[0], resetVectorData->ApEntryVector[1], resetVectorData->ApEntryVector[2], resetVectorData->ApEntryVector[3], + resetVectorData->ApEntryVector[4], resetVectorData->ApEntryVector[5], resetVectorData->ApEntryVector[6], resetVectorData->ApEntryVector[7], + resetVectorData->ResetVector[0], resetVectorData->ResetVector[1], resetVectorData->ResetVector[2], resetVectorData->ResetVector[3], + resetVectorData->ResetVector[4], resetVectorData->ResetVector[5], resetVectorData->ResetVector[6], resetVectorData->ResetVector[7], + resetVectorData->PeiCoreEntryPoint, + resetVectorData->ApStartupSegment, + resetVectorData->BootFvBaseAddress); + + model->addInfo(lastVtf, info); + return U_SUCCESS; +} + USTATUS FfsParser::checkTeImageBase(const UModelIndex & index) { // Sanity check @@ -3618,13 +3660,13 @@ USTATUS FfsParser::parseFitEntryMicrocode(const UByteArray & microcode, const UI } const INTEL_MICROCODE_HEADER* header = (const INTEL_MICROCODE_HEADER*)(microcode.constData() + localOffset); - if (header->Version != INTEL_MICROCODE_HEADER_VERSION) { + if (header->Version != INTEL_MICROCODE_HEADER_VERSION_1) { return U_INVALID_MICROCODE; } bool reservedBytesValid = true; for (UINT8 i = 0; i < sizeof(header->Reserved); i++) - if (header->Reserved[i] != INTEL_MICROCODE_HEADER_RESERVED_BYTE) { + if (header->Reserved[i] != 0x00) { reservedBytesValid = false; break; } @@ -3632,13 +3674,21 @@ USTATUS FfsParser::parseFitEntryMicrocode(const UByteArray & microcode, const UI return U_INVALID_MICROCODE; } + if (header->DataSize % 4 != 0) { + return U_INVALID_MICROCODE; + } + + if (header->TotalSize <= header->DataSize || header->TotalSize % 1024 != 0) { + return U_INVALID_MICROCODE; + } + UINT32 mcSize = header->TotalSize; if ((UINT32)microcode.size() < localOffset + mcSize) { return U_INVALID_MICROCODE; } // Valid microcode found - info = usprintf("CPUID: %08Xh, Revision: %08Xh, Date: %02X.%02X.%04X", + info = usprintf("CpuSignature: %08Xh, Revision: %08Xh, Date: %02X.%02X.%04X", header->CpuSignature, header->Revision, header->DateDay, @@ -3678,34 +3728,33 @@ USTATUS FfsParser::parseFitEntryAcm(const UByteArray & acm, const UINT32 localOf // Add ACM header info UString acmInfo; - acmInfo += usprintf( - " found at base %Xh\n" - "ModuleType: %04Xh ModuleSubtype: %04Xh HeaderLength: %08Xh\n" - "HeaderVersion: %08Xh ChipsetId: %04Xh Flags: %04Xh\n" - "ModuleVendor: %04Xh Date: %02X.%02X.%04X ModuleSize: %08Xh\n" - "EntryPoint: %08Xh AcmSvn: %04Xh Unknown1: %08Xh\n" - "Unknown2: %08Xh GdtBase: %08Xh GdtMax: %08Xh\n" - "SegSel: %08Xh KeySize: %08Xh Unknown3: %08Xh", - model->base(parent) + localOffset, - header->ModuleType, - header->ModuleSubtype, - header->ModuleSize * sizeof(UINT32), - header->HeaderVersion, - header->ChipsetId, - header->Flags, - header->ModuleVendor, - header->DateDay, header->DateMonth, header->DateYear, - header->ModuleSize * sizeof(UINT32), - header->EntryPoint, - header->AcmSvn, - header->Unknown1, - header->Unknown2, - header->GdtBase, - header->GdtMax, - header->SegmentSel, - header->KeySize * sizeof(UINT32), - header->Unknown4 * sizeof(UINT32) - ); + acmInfo += usprintf(" found at base %Xh\n" + "ModuleType: %04Xh ModuleSubtype: %04Xh HeaderLength: %08Xh\n" + "HeaderVersion: %08Xh ChipsetId: %04Xh Flags: %04Xh\n" + "ModuleVendor: %04Xh Date: %02X.%02X.%04X ModuleSize: %08Xh\n" + "EntryPoint: %08Xh AcmSvn: %04Xh Unknown1: %08Xh\n" + "Unknown2: %08Xh GdtBase: %08Xh GdtMax: %08Xh\n" + "SegSel: %08Xh KeySize: %08Xh Unknown3: %08Xh", + model->base(parent) + localOffset, + header->ModuleType, + header->ModuleSubtype, + header->ModuleSize * sizeof(UINT32), + header->HeaderVersion, + header->ChipsetId, + header->Flags, + header->ModuleVendor, + header->DateDay, header->DateMonth, header->DateYear, + header->ModuleSize * sizeof(UINT32), + header->EntryPoint, + header->AcmSvn, + header->Unknown1, + header->Unknown2, + header->GdtBase, + header->GdtMax, + header->SegmentSel, + header->KeySize * sizeof(UINT32), + header->Unknown4 * sizeof(UINT32) + ); // Add PubKey acmInfo += usprintf("\n\nACM RSA Public Key (Exponent: %Xh):", header->RsaPubExp); for (UINT16 i = 0; i < sizeof(header->RsaPubKey); i++) { @@ -4071,66 +4120,134 @@ USTATUS FfsParser::parseMicrocodeVolumeBody(const UModelIndex & index) USTATUS FfsParser::parseIntelMicrocodeHeader(const UByteArray & microcode, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) { - const UINT32 dataSize = (const UINT32)microcode.size(); - - if (dataSize < sizeof(INTEL_MICROCODE_HEADER)) { - //msg(usprintf("%s: input is too small even for Intel microcode header", __FUNCTION__), parent); + // We have enough data to fit the header + if ((UINT32)microcode.size() < sizeof(INTEL_MICROCODE_HEADER)) { return U_INVALID_MICROCODE; } const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)microcode.constData(); - if (ucodeHeader->Version != INTEL_MICROCODE_HEADER_VERSION) { - //msg(usprintf("%s: input has invalid Intel microcode header", __FUNCTION__), parent); - return U_INVALID_MICROCODE; - } - - if (!INTEL_MICROCODE_HEADER_SIZES_VALID(ucodeHeader)) { - //msg(usprintf("%s: input has invalid Intel microcode header", __FUNCTION__), parent); + + // Header version is 1 + if (ucodeHeader->Version != INTEL_MICROCODE_HEADER_VERSION_1) { return U_INVALID_MICROCODE; } + // Reserved bytes are all zeroes bool reservedBytesValid = true; - for (UINT8 i = 0; i < sizeof(ucodeHeader->Reserved); i++) - if (ucodeHeader->Reserved[i] != INTEL_MICROCODE_HEADER_RESERVED_BYTE) { + for (UINT8 i = 0; i < sizeof(ucodeHeader->Reserved); i++) { + if (ucodeHeader->Reserved[i] != 0x00) { reservedBytesValid = false; break; } + } if (!reservedBytesValid) { - //msg(usprintf("%s: input has invalid Intel microcode header", __FUNCTION__), parent); return U_INVALID_MICROCODE; } - if (dataSize < ucodeHeader->TotalSize) { - //msg(usprintf("%s: input is too small for the whole Intel microcode", __FUNCTION__), parent); + // Data size is multiple of 4 + if (ucodeHeader->DataSize % 4 != 0) { + return U_INVALID_MICROCODE; + } + + // TotalSize is greater then DataSize and is multiple of 1024 + if (ucodeHeader->TotalSize <= ucodeHeader->DataSize || ucodeHeader->TotalSize % 1024 != 0) { + return U_INVALID_MICROCODE; + } + + // We have enough data to fit the whole TotalSize + if ((UINT32)microcode.size() < ucodeHeader->TotalSize) { return U_INVALID_MICROCODE; } // Valid microcode found - // Construct header and body + UINT32 dataSize = ucodeHeader->DataSize; + if (dataSize == 0) + dataSize = INTEL_MICROCODE_REAL_DATA_SIZE_ON_ZERO; + + // Recalculate the whole microcode checksum + UByteArray tempMicrocode = microcode; + INTEL_MICROCODE_HEADER* tempUcodeHeader = (INTEL_MICROCODE_HEADER*)(tempMicrocode.data()); + tempUcodeHeader->Checksum = 0; + UINT32 calculated = calculateChecksum32((const UINT32*)tempMicrocode.constData(), tempUcodeHeader->TotalSize); + bool msgInvalidChecksum = (ucodeHeader->Checksum != calculated); + + // Construct header, body and tail UByteArray header = microcode.left(sizeof(INTEL_MICROCODE_HEADER)); - UByteArray body = microcode.mid(sizeof(INTEL_MICROCODE_HEADER), ucodeHeader->DataSize); - - //TODO: recalculate microcode checksum - + UByteArray body = microcode.mid(sizeof(INTEL_MICROCODE_HEADER), dataSize); + UByteArray tail = microcode.mid(sizeof(INTEL_MICROCODE_HEADER) + dataSize); + + // Check if we have extended header in the tail + UString extendedHeaderInfo; + if ((UINT32)tail.size() >= sizeof(INTEL_MICROCODE_EXTENDED_HEADER)) { + const INTEL_MICROCODE_EXTENDED_HEADER* extendedHeader = (const INTEL_MICROCODE_EXTENDED_HEADER*)tail.constData(); + + // Reserved bytes are all zeroes + bool extendedReservedBytesValid = true; + for (UINT8 i = 0; i < sizeof(extendedHeader->Reserved); i++) { + if (extendedHeader->Reserved[i] != 0x00) { + extendedReservedBytesValid = false; + break; + } + } + + // We have more than 0 entries and they are all in the tail + if (extendedReservedBytesValid + && extendedHeader->EntryCount > 0 + && (UINT32)tail.size() >= sizeof(INTEL_MICROCODE_EXTENDED_HEADER) + extendedHeader->EntryCount * sizeof(INTEL_MICROCODE_EXTENDED_HEADER_ENTRY)) { + // Recalculate extended header checksum + INTEL_MICROCODE_EXTENDED_HEADER* tempExtendedHeader = (INTEL_MICROCODE_EXTENDED_HEADER*)(tempMicrocode.data() + sizeof(INTEL_MICROCODE_HEADER) + dataSize); + tempExtendedHeader->Checksum = 0; + UINT32 extendedCalculated = calculateChecksum32((const UINT32*)tempExtendedHeader, sizeof(INTEL_MICROCODE_EXTENDED_HEADER) + extendedHeader->EntryCount * sizeof(INTEL_MICROCODE_EXTENDED_HEADER_ENTRY)); + + extendedHeaderInfo = usprintf("\nExtended header entries: %u\nExtended header checksum: %08Xh, ", + extendedHeader->EntryCount, + extendedHeader->Checksum) + + (extendedHeader->Checksum == extendedCalculated ? UString("valid") : usprintf("invalid, should be %08Xh", extendedCalculated)); + + const INTEL_MICROCODE_EXTENDED_HEADER_ENTRY* firstEntry = (const INTEL_MICROCODE_EXTENDED_HEADER_ENTRY*)(extendedHeader + 1); + for (UINT8 i = 0; i < extendedHeader->EntryCount; i++) { + const INTEL_MICROCODE_EXTENDED_HEADER_ENTRY* entry = (const INTEL_MICROCODE_EXTENDED_HEADER_ENTRY*)(firstEntry + i); + + // Recalculate checksum after patching + tempUcodeHeader->Checksum = 0; + tempUcodeHeader->CpuFlags = entry->CpuFlags; + tempUcodeHeader->CpuSignature = entry->CpuSignature; + UINT32 entryCalculated = calculateChecksum32((const UINT32*)tempMicrocode.constData(), sizeof(INTEL_MICROCODE_HEADER) + dataSize); + + + extendedHeaderInfo += usprintf("\nCPU signature #%u: %08Xh\nCPU flags #%u: %08Xh\nChecksum #%u: %08Xh, ", + i + 1, entry->CpuSignature, + i + 1, entry->CpuFlags, + i + 1, entry->Checksum) + + (entry->Checksum == entryCalculated ? UString("valid") : usprintf("invalid, should be %08Xh", entryCalculated)); + } + } + } + // Add info UString name("Intel microcode"); - UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\n" - "Date: %02X.%02X.%04x\nCPU signature: %08Xh\nRevision: %08Xh\nChecksum: %08Xh\nLoader revision: %08Xh\nCPU flags: %08Xh", - ucodeHeader->TotalSize, ucodeHeader->TotalSize, - header.size(), header.size(), - body.size(), body.size(), - ucodeHeader->DateDay, - ucodeHeader->DateMonth, - ucodeHeader->DateYear, - ucodeHeader->CpuSignature, - ucodeHeader->Revision, - ucodeHeader->Checksum, - ucodeHeader->LoaderRevision, - ucodeHeader->CpuFlags); - + UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nTail size: %Xh (%u)\n" + "Date: %02X.%02X.%04x\nCPU signature: %08Xh\nRevision: %08Xh\nLoader revision: %08Xh\nCPU flags: %08Xh\nChecksum: %08Xh, ", + dataSize, dataSize, + header.size(), header.size(), + body.size(), body.size(), + tail.size(), tail.size(), + ucodeHeader->DateDay, + ucodeHeader->DateMonth, + ucodeHeader->DateYear, + ucodeHeader->CpuSignature, + ucodeHeader->Revision, + ucodeHeader->LoaderRevision, + ucodeHeader->CpuFlags, + ucodeHeader->Checksum) + + (ucodeHeader->Checksum == calculated ? UString("valid") : usprintf("invalid, should be %08Xh", calculated)) + + extendedHeaderInfo; + // Add tree item - index = model->addItem(localOffset, Types::Microcode, Subtypes::IntelMicrocode, name, UString(), info, header, body, UByteArray(), Fixed, parent); - - // No need to parse body further for now + index = model->addItem(localOffset, Types::Microcode, Subtypes::IntelMicrocode, name, UString(), info, header, body, tail, Fixed, parent); + if (msgInvalidChecksum) + msg(usprintf("%s: invalid microcode checksum %08Xh, should be %08Xh", __FUNCTION__, ucodeHeader->Checksum, calculated), index); + + // No need to parse the body further for now return U_SUCCESS; } diff --git a/common/ffsparser.h b/common/ffsparser.h index 0ab735a..74febfc 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -144,6 +144,7 @@ private: USTATUS checkProtectedRanges(const UModelIndex & index); USTATUS markProtectedRangeRecursive(const UModelIndex & index, const BG_PROTECTED_RANGE & range); + USTATUS parseResetVectorData(); USTATUS parseFit(const UModelIndex & index); USTATUS parseVendorHashFile(const UByteArray & fileGuid, const UModelIndex & index); USTATUS parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); diff --git a/common/ffsreport.cpp b/common/ffsreport.cpp index cf94860..6f06745 100644 --- a/common/ffsreport.cpp +++ b/common/ffsreport.cpp @@ -49,7 +49,7 @@ USTATUS FfsReport::generateRecursive(std::vector & report, const UModel // Calculate item CRC32 UByteArray data = model->header(index) + model->body(index) + model->tail(index); - UINT32 crc = crc32(0, (const UINT8*)data.constData(), data.size()); + UINT32 crc = (UINT32)crc32(0, (const UINT8*)data.constData(), data.size()); // Information on current item UString text = model->text(index); diff --git a/common/fit.h b/common/fit.h old mode 100644 new mode 100755 index 24a79ff..afa98bd --- a/common/fit.h +++ b/common/fit.h @@ -50,21 +50,6 @@ typedef struct FIT_ENTRY_ { UINT8 Checksum; } FIT_ENTRY; -typedef struct INTEL_MICROCODE_HEADER_ { - UINT32 Version; - UINT32 Revision; - UINT16 DateYear; - UINT8 DateDay; - UINT8 DateMonth; - UINT32 CpuSignature; - UINT32 Checksum; - UINT32 LoaderRevision; - UINT32 CpuFlags; - UINT32 DataSize; - UINT32 TotalSize; - UINT8 Reserved[12]; -} INTEL_MICROCODE_HEADER; - typedef struct { UINT16 IndexRegisterAddress; UINT16 DataRegisterAddress; @@ -73,9 +58,51 @@ typedef struct { UINT16 Index; } FIT_ENTRY_VERSION_0_CONFIG_POLICY; -#define INTEL_MICROCODE_HEADER_VERSION 0x00000001 -#define INTEL_MICROCODE_HEADER_RESERVED_BYTE 0x00 -#define INTEL_MICROCODE_HEADER_SIZES_VALID(ptr) (((INTEL_MICROCODE_HEADER*)ptr)->TotalSize - ((INTEL_MICROCODE_HEADER*)ptr)->DataSize == sizeof(INTEL_MICROCODE_HEADER)) +// This scructure is described in Section 9.11.1 of the Intel Software Developer manual Volume 3A Part 1 +typedef struct INTEL_MICROCODE_HEADER_ { + UINT32 Version; + UINT32 Revision; + UINT16 DateYear; + UINT8 DateDay; + UINT8 DateMonth; + UINT32 CpuSignature; + UINT32 Checksum; // Checksum of Update Data and Header. Used to verify the integrity of the update header and data. + // Checksum is correct when the summation of all the DWORDs (including the extended Processor Signature Table) + // that comprise the microcode update result in 00000000H. + + UINT32 LoaderRevision; + UINT32 CpuFlags; + UINT32 DataSize; // Specifies the size of the encrypted data in bytes, and must be a multiple of DWORDs. + // If this value is 00000000H, then the microcode update encrypted data is 2000 bytes (or 500 DWORDs). + + UINT32 TotalSize;// Specifies the total size of the microcode update in bytes. + // It is the summation of the header size, the encrypted data size and the size of the optional extended signature table. + // This value is always a multiple of 1024. + + UINT8 Reserved[12]; +} INTEL_MICROCODE_HEADER; + +#define INTEL_MICROCODE_REAL_DATA_SIZE_ON_ZERO 2000 + +typedef struct INTEL_MICROCODE_EXTENDED_HEADER_ { + UINT32 EntryCount; + UINT32 Checksum; // Checksum of extended processor signature table. + // Used to verify the integrity of the extended processor signature table. + // Checksum is correct when the summation of the DWORDs that comprise the extended processor signature table results in 00000000H. + + UINT8 Reserved[12]; + // INTEL_MICROCODE_EXTENDED_HEADER_ENTRY Entries[EntryCount]; +} INTEL_MICROCODE_EXTENDED_HEADER; + +typedef struct INTEL_MICROCODE_EXTENDED_HEADER_ENTRY_ { + UINT32 CpuSignature; + UINT32 CpuFlags; + UINT32 Checksum; // To calculate the Checksum, substitute the Primary Processor Signature entry and the Processor Flags entry with the corresponding Extended Patch entry. + // Delete the Extended Processor Signature Table entries. + // Checksum is correct when the summation of all DWORDs that comprise the created Extended Processor Patch results in 00000000H. +} INTEL_MICROCODE_EXTENDED_HEADER_ENTRY; + +#define INTEL_MICROCODE_HEADER_VERSION_1 0x00000001 #pragma pack(pop) diff --git a/common/meparser.cpp b/common/meparser.cpp new file mode 100755 index 0000000..b7ae12f --- /dev/null +++ b/common/meparser.cpp @@ -0,0 +1,1169 @@ +/* meparser.cpp + +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#include +#include + +#include "meparser.h" +#include "parsingdata.h" +#include "utility.h" + +#ifdef U_ENABLE_ME_PARSING_SUPPORT + +UString meBpdtEntryTypeToUString(UINT16 type) { + switch (type) { + case 0: return UString("OEM SMIP"); + case 1: return UString("CSE RBE"); + case 2: return UString("CSE BUP"); + case 3: return UString("uCode"); + case 4: return UString("IBB"); + case 5: return UString("S-BPDT"); + case 6: return UString("OBB"); + case 7: return UString("CSE Main"); + case 8: return UString("ISH"); + case 9: return UString("CSE IDLM"); + case 10: return UString("IFP Override"); + case 11: return UString("Debug Tokens"); + case 12: return UString("USF Phy Config"); + case 13: return UString("USF GPP LUN ID"); + case 14: return UString("PMC"); + case 15: return UString("iUnit"); + case 16: return UString("NVM Config"); + case 17: return UString("UEP"); + case 18: return UString("WLAN uCode"); + case 19: return UString("LOCL Sprites"); + case 20: return UString("OEM Key Manifest"); + case 21: return UString("Defaults/FITC.cfg"); + case 22: return UString("PAVP"); + case 23: return UString("TCSS FW IOM"); + case 24: return UString("TCSS FW PHY"); + case 25: return UString("TCSS TBT"); + default: return usprintf("Unknown %u", type); + } +} + +UString meExtensionTypeToUstring(UINT32 type) { + switch (type) { + case 0: return UString("System Info"); + case 1: return UString("Init Script"); + case 2: return UString("Feature Permissions"); + case 3: return UString("Partition Info"); + case 4: return UString("Shared Lib Attributes"); + case 5: return UString("Process Attributes"); + case 6: return UString("Thread Attributes"); + case 7: return UString("Device Type"); + case 8: return UString("MMIO Range"); + case 9: return UString("Spec File Producer"); + case 10: return UString("Module Attributes"); + case 11: return UString("Locked Ranges"); + case 12: return UString("Client System Info"); + case 13: return UString("User Info"); + case 14: return UString("Key Manifest"); + case 15: return UString("Signed Package Info"); + case 16: return UString("Anto-cloning SKU ID"); + case 18: return UString("Intel IMR Info"); + case 20: return UString("RCIP Info"); + case 21: return UString("Secure Token"); + case 22: return UString("IFWI Partition Manifest"); + default: return usprintf("Unknown %u", type); + } +} + +struct ME_FPT_PARTITION_INFO { + ME_FPT_ENTRY ptEntry; + UINT8 type; + UModelIndex index; + friend bool operator< (const ME_FPT_PARTITION_INFO & lhs, const ME_FPT_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; } +}; + +struct ME_BPDT_PARTITION_INFO { + ME_BPDT_ENTRY ptEntry; + UINT8 type; + UModelIndex index; + friend bool operator< (const ME_BPDT_PARTITION_INFO & lhs, const ME_BPDT_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; } +}; + +struct ME_CPD_PARTITION_INFO { + ME_BPDT_CPD_ENTRY ptEntry; + UINT8 type; + UModelIndex index; + friend bool operator< (const ME_CPD_PARTITION_INFO & lhs, const ME_CPD_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset.Offset < rhs.ptEntry.Offset.Offset; } +}; + +USTATUS MeParser::parseMeRegionBody(const UModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return U_INVALID_PARAMETER; + + // Obtain ME region + UByteArray meRegion = model->body(index); + + // Check region size + if ((UINT32)meRegion.size() < ME_ROM_BYPASS_VECTOR_SIZE + sizeof(UINT32)) { + msg(usprintf("%s: ME region too small to fit ROM bypass vector", __FUNCTION__), index); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Check ME signature to determine it's version + // ME v11 and older layout + if (meRegion.left(sizeof(UINT32)) == ME_FPT_HEADER_SIGNATURE || meRegion.mid(ME_ROM_BYPASS_VECTOR_SIZE, sizeof(UINT32)) == ME_FPT_HEADER_SIGNATURE) { + UModelIndex ptIndex; + return parseFptRegion(meRegion, index, ptIndex); + } + + // CannonLake 1.6+ layout (IFWI) + // Check region size + if ((UINT32)meRegion.size() < sizeof(ME_IFWI_LAYOUT_HEADER)) { + msg(usprintf("%s: ME region too small to fit IFWI layout header", __FUNCTION__), index); + return U_INVALID_ME_PARTITION_TABLE; + } + + const ME_IFWI_LAYOUT_HEADER* regionHeader = (const ME_IFWI_LAYOUT_HEADER*)meRegion.constData(); + // Check region size + if ((UINT32)meRegion.size() < regionHeader->DataPartitionOffset + sizeof(UINT32)) { + msg(usprintf("%s: ME region too small to fit IFWI layout header", __FUNCTION__), index); + return U_INVALID_ME_PARTITION_TABLE; + } + // Data partition always points to FPT header + if (meRegion.mid(regionHeader->DataPartitionOffset, sizeof(UINT32)) == ME_FPT_HEADER_SIGNATURE) { + UModelIndex ptIndex; + return parseIfwiRegion(meRegion, index, ptIndex); + } + + // Something else entirely + msg(usprintf("%s: unknown ME region format", __FUNCTION__), index); + return U_INVALID_ME_PARTITION_TABLE; +} + +USTATUS MeParser::parseIfwiRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index) +{ + // Add header + UByteArray header = region.left(sizeof(ME_IFWI_LAYOUT_HEADER)); + const ME_IFWI_LAYOUT_HEADER* ifwiHeader = (const ME_IFWI_LAYOUT_HEADER*)region.constData(); + + UString name = UString("IFWI header"); + UString info = usprintf("Full size: %Xh (%u)\n" + "Data partition offset: %Xh\nData partition length: %Xh\n" + "Boot1 partition offset: %Xh\nBoot1 partition length: %Xh\n" + "Boot2 partition offset: %Xh\nBoot2 partition length: %Xh\n" + "Boot3 partition offset: %Xh\nBoot3 partition length: %Xh", + header.size(), header.size(), + ifwiHeader->DataPartitionOffset, ifwiHeader->DataPartitionSize, + ifwiHeader->Boot1Offset, ifwiHeader->Boot1Size, + ifwiHeader->Boot2Offset, ifwiHeader->Boot2Size, + ifwiHeader->Boot3Offset, ifwiHeader->Boot3Size); + // Add tree item + index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent); + + // TODO: this requires better parsing using a similar approach as in other things: get all, sort, check for paddings/intersections + + // Add padding after header + if (ifwiHeader->DataPartitionOffset > sizeof(ME_IFWI_LAYOUT_HEADER)) { + UByteArray padding = region.mid(sizeof(ME_IFWI_LAYOUT_HEADER), ifwiHeader->DataPartitionOffset - sizeof(ME_IFWI_LAYOUT_HEADER)); + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); + // Add tree item + model->addItem(sizeof(ME_IFWI_LAYOUT_HEADER), Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent); + } + + // Add data partition + UByteArray dataPartition = region.mid(ifwiHeader->DataPartitionOffset, ifwiHeader->DataPartitionSize); + name = UString("Data partition"); + info = usprintf("Full size: %Xh (%u)", dataPartition.size(), dataPartition.size()); + UModelIndex dataPartitionIndex = model->addItem(ifwiHeader->DataPartitionOffset, Types::IfwiPartition, Subtypes::DataIfwiPartition, name, UString(), info, UByteArray(), dataPartition, UByteArray(), Fixed, parent); + UModelIndex dataPartitionFptRegionIndex; + parseFptRegion(dataPartition, dataPartitionIndex, dataPartitionFptRegionIndex); + + // Add padding after data partition + if (ifwiHeader->Boot1Offset > ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize) { + UByteArray padding = region.mid(ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize, ifwiHeader->Boot1Offset - ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize); + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); + // Add tree item + model->addItem(ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent); + } + else if (ifwiHeader->Boot1Offset < ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize) { + msg(usprintf("%s: invalid Boot1 partition offset", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Add Boot1 partition + UByteArray bpdt1Partition = region.mid(ifwiHeader->Boot1Offset, ifwiHeader->Boot1Size); + name = UString("Boot1 partition"); + info = usprintf("Full size: %Xh (%u)", bpdt1Partition.size(), bpdt1Partition.size()); + UModelIndex bpdt1PartitionIndex = model->addItem(ifwiHeader->Boot1Offset, Types::IfwiPartition, Subtypes::BootIfwiPartition, name, UString(), info, UByteArray(), bpdt1Partition, UByteArray(), Fixed, parent); + UModelIndex bpdt1PartitionBpdtRegionIndex; + parseBpdtRegion(bpdt1Partition, bpdt1PartitionIndex, bpdt1PartitionBpdtRegionIndex); + + // Add padding after Boot1 partition + if (ifwiHeader->Boot2Offset > ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size) { + UByteArray padding = region.mid(ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size, ifwiHeader->Boot2Offset - ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size); + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); + // Add tree item + model->addItem(ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent); + } + else if (ifwiHeader->Boot2Offset < ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size) { + msg(usprintf("%s: invalid Boot2 partition offset", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Add Boot2 partition + UByteArray bpdt2Partition = region.mid(ifwiHeader->Boot2Offset, ifwiHeader->Boot2Size); + name = UString("Boot2 partition"); + info = usprintf("Full size: %Xh (%u)", bpdt2Partition.size(), bpdt2Partition.size()); + UModelIndex bpdt2PartitionIndex = model->addItem(ifwiHeader->Boot2Offset, Types::IfwiPartition, Subtypes::BootIfwiPartition, name, UString(), info, UByteArray(), bpdt2Partition, UByteArray(), Fixed, parent); + UModelIndex bpdt2PartitionBpdtRegionIndex; + parseBpdtRegion(bpdt2Partition, bpdt2PartitionIndex, bpdt2PartitionBpdtRegionIndex); + + // TODO: add Boot3 if needed + // Add padding at the end + if ((UINT32)region.size() > ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size) { + UByteArray padding = region.mid(ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size, (UINT32)region.size() - ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size); + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); + // Add tree item + model->addItem(ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent); + } + else if ((UINT32)region.size() < ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size) { + msg(usprintf("%s: Boot2 partition is located outside of the region", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + return U_SUCCESS; +} + +USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index) +{ + // Check region size + if ((UINT32)region.size() < sizeof(ME_FPT_HEADER)) { + msg(usprintf("%s: region too small to fit FPT header", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Populate partition table header + const ME_FPT_HEADER* ptHeader = (const ME_FPT_HEADER*)region.constData(); + UINT32 romBypassVectorSize = 0; + if (region.left(sizeof(UINT32)) != ME_FPT_HEADER_SIGNATURE) { + // Adjust the header to skip ROM bypass vector + romBypassVectorSize = ME_ROM_BYPASS_VECTOR_SIZE; + ptHeader = (const ME_FPT_HEADER*)(region.constData() + romBypassVectorSize); + } + + // Check region size again + UINT32 ptBodySize = ptHeader->NumEntries * sizeof(ME_FPT_ENTRY); + UINT32 ptSize = romBypassVectorSize + sizeof(ME_FPT_HEADER) + ptBodySize; + if ((UINT32)region.size() < ptSize) { + msg(usprintf("%s: ME region too small to fit partition table", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Recalculate checksum + UByteArray tempHeader = UByteArray((const char*)ptHeader, sizeof(ME_FPT_HEADER)); + ME_FPT_HEADER* tempPtHeader = (ME_FPT_HEADER*)tempHeader.data(); + tempPtHeader->Checksum = 0; + UINT8 calculated = calculateChecksum8((const UINT8*)tempPtHeader, sizeof(ME_FPT_HEADER)); + bool msgInvalidPtHeaderChecksum = (calculated != ptHeader->Checksum); + + // Get info + UByteArray header = region.left(romBypassVectorSize + sizeof(ME_FPT_HEADER)); + UByteArray body = region.mid(header.size(), ptBodySize); + + UString name = UString("FPT partition table"); + UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\nHeader version: %02Xh\nEntry version: %02Xh\n" + "Header length: %02Xh\nTicks to add: %04Xh\nTokens to add: %04Xh\nUMA size: %Xh\nFlash layout: %Xh\nFITC version: %u.%u.%u.%u\nChecksum: %02Xh, ", + ptSize, ptSize, + header.size(), header.size(), + ptBodySize, ptBodySize, + ptHeader->NumEntries, + ptHeader->HeaderVersion, + ptHeader->EntryVersion, + ptHeader->HeaderLength, + ptHeader->TicksToAdd, + ptHeader->TokensToAdd, + ptHeader->UmaSize, + ptHeader->FlashLayout, + ptHeader->FitcMajor, ptHeader->FitcMinor, ptHeader->FitcHotfix, ptHeader->FitcBuild, + ptHeader->Checksum) + (ptHeader->Checksum == calculated ? UString("valid") : usprintf("invalid, should be %02Xh", calculated)); + + // 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 = header.size(); + const ME_FPT_ENTRY* firstPtEntry = (const ME_FPT_ENTRY*)(region.constData() + offset); + for (UINT8 i = 0; i < ptHeader->NumEntries; i++) { + // Populate entry header + const ME_FPT_ENTRY* ptEntry = firstPtEntry + i; + + // Get info + name = usprintf("%c%c%c%c", ptEntry->PartitionName[0], ptEntry->PartitionName[1], ptEntry->PartitionName[2], ptEntry->PartitionName[3]); + info = usprintf("Full size: %Xh (%u)\nPartition offset: %Xh\nPartition length: %Xh\nPartition type: %02Xh", + sizeof(ME_FPT_ENTRY), sizeof(ME_FPT_ENTRY), + ptEntry->Offset, + ptEntry->Length, + ptEntry->PartitionType); + + // Add tree item + const UINT8 type = (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Length != 0 && ptEntry->EntryValid != 0xFF) ? Subtypes::ValidFptEntry : Subtypes::InvalidFptEntry; + UModelIndex entryIndex = model->addItem(offset, Types::FptEntry, type, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(ME_FPT_ENTRY)), UByteArray(), Fixed, index); + + // Adjust offset + offset += sizeof(ME_FPT_ENTRY); + + // Add valid partitions + if (type == Subtypes::ValidFptEntry) { // Skip absent and invalid partitions + // Add to partitions vector + ME_FPT_PARTITION_INFO partition; + partition.type = Types::FptPartition; + partition.ptEntry = *ptEntry; + partition.index = entryIndex; + partitions.push_back(partition); + } + } + +make_partition_table_consistent: + // Sort partitions by offset + std::sort(partitions.begin(), partitions.end()); + + // Check for intersections and paddings between partitions + ME_FPT_PARTITION_INFO padding; + + // Check intersection with the partition table header + if (partitions.front().ptEntry.Offset < ptSize) { + msg(usprintf("%s: ME partition has intersection with ME partition table, skipped", __FUNCTION__), + partitions.front().index); + partitions.erase(partitions.begin()); + goto make_partition_table_consistent; + } + // Check for padding between partition table and the first partition + else if (partitions.front().ptEntry.Offset > ptSize) { + padding.ptEntry.Offset = ptSize; + padding.ptEntry.Length = partitions.front().ptEntry.Offset - ptSize; + padding.type = Types::Padding; + partitions.insert(partitions.begin(), padding); + } + // Check for intersections/paddings between partitions + for (size_t i = 1; i < partitions.size(); i++) { + UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Length; + + // Check that current region is fully present in the image + if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Length > (UINT32)region.size()) { + if ((UINT32)partitions[i].ptEntry.Offset >= (UINT32)region.size()) { + msg(usprintf("%s: FPT partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: FPT partition can't fit into the region, truncated", __FUNCTION__), partitions[i].index); + partitions[i].ptEntry.Length = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset; + } + } + + // Check for intersection with previous partition + if (partitions[i].ptEntry.Offset < previousPartitionEnd) { + // Check if current partition is located inside previous one + if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) { + msg(usprintf("%s: FPT partition is located inside another FPT partition, skipped", __FUNCTION__), + partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: FPT partition intersects with prevous one, skipped", __FUNCTION__), + partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + } + + // Check for padding between current and previous partitions + else if (partitions[i].ptEntry.Offset > previousPartitionEnd) { + padding.ptEntry.Offset = previousPartitionEnd; + padding.ptEntry.Length = partitions[i].ptEntry.Offset - previousPartitionEnd; + padding.type = Types::Padding; + std::vector::iterator iter = partitions.begin(); + std::advance(iter, i); + partitions.insert(iter, padding); + } + } + // Check for padding after the last region + if ((UINT32)partitions.back().ptEntry.Offset + (UINT32)partitions.back().ptEntry.Length < (UINT32)region.size()) { + padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Length; + padding.ptEntry.Length = region.size() - padding.ptEntry.Offset; + padding.type = Types::Padding; + partitions.push_back(padding); + } + + // Partition map is consistent + for (size_t i = 0; i < partitions.size(); i++) { + UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Length); + if (partitions[i].type == Types::FptPartition) { + UModelIndex partitionIndex; + // Get info + name = usprintf("%c%c%c%c", partitions[i].ptEntry.PartitionName[0], partitions[i].ptEntry.PartitionName[1], partitions[i].ptEntry.PartitionName[2], partitions[i].ptEntry.PartitionName[3]); + info = usprintf("Full size: %Xh (%u)\nPartition type: %02Xh\n", + partition.size(), partition.size(), + partitions[i].ptEntry.PartitionType); + + // Add tree item + UINT8 type = Subtypes::CodeFptPartition + partitions[i].ptEntry.PartitionType; + partitionIndex = model->addItem(partitions[i].ptEntry.Offset, Types::FptPartition, type, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + if (type == Subtypes::CodeFptPartition && partition.left(sizeof(UINT32)) == ME_CPD_SIGNATURE) { + // Parse conde partition contents + UModelIndex ptIndex; + parseCodePartitionDirectory(partition, partitions[i].ptEntry.Offset, partitionIndex, ptIndex); + } + } + else if (partitions[i].type == Types::Padding) { + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", partition.size(), partition.size()); + + // Add tree item + model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + } + } + + return U_SUCCESS; +} + +USTATUS MeParser::parseBpdtRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index) +{ + // Check region size + if ((UINT32)region.size() < sizeof(ME_BPDT_HEADER)) { + msg(usprintf("%s: BPDT region too small to fit partition table header", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Populate partition table header + const ME_BPDT_HEADER* ptHeader = (const ME_BPDT_HEADER*)region.constData(); + + // Check region size again + UINT32 ptBodySize = ptHeader->NumEntries * sizeof(ME_BPDT_ENTRY); + UINT32 ptSize = sizeof(ME_BPDT_HEADER) + ptBodySize; + if ((UINT32)region.size() < ptSize) { + msg(usprintf("%s: BPDT region too small to fit BPDT partition table", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Get info + UByteArray header = region.left(sizeof(ME_BPDT_HEADER)); + UByteArray body = region.mid(sizeof(ME_BPDT_HEADER), ptBodySize); + + UString name = UString("BPDT partition table"); + UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\nVersion: %Xh\n" + "IFWI version: %Xh\nFITC version: %u.%u.%u.%u", + ptSize, ptSize, + header.size(), header.size(), + ptBodySize, ptBodySize, + ptHeader->NumEntries, + ptHeader->Version, + ptHeader->IfwiVersion, + ptHeader->FitcMajor, ptHeader->FitcMinor, ptHeader->FitcHotfix, ptHeader->FitcBuild); + + // Add tree item + index = model->addItem(0, Types::BpdtStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + + // Adjust offset + UINT32 offset = sizeof(ME_FPT_HEADER); + + // Add partition table entries + std::vector partitions; + const ME_BPDT_ENTRY* firstPtEntry = (const ME_BPDT_ENTRY*)(region.constData() + sizeof(ME_BPDT_HEADER)); + for (UINT8 i = 0; i < ptHeader->NumEntries; i++) { + // Populate entry header + const ME_BPDT_ENTRY* ptEntry = firstPtEntry + i; + + // Get info + name = meBpdtEntryTypeToUString(ptEntry->Type); + info = usprintf("Full size: %Xh (%u)\nType: %Xh\nPartition offset: %Xh\nPartition length: %Xh", + sizeof(ME_BPDT_ENTRY), sizeof(ME_BPDT_ENTRY), + ptEntry->Type, + ptEntry->Offset, + ptEntry->Length) + + UString("\nSplit sub-partition first part: ") + (ptEntry->SplitSubPartitionFirstPart ? "Yes" : "No") + + UString("\nSplit sub-partition second part: ") + (ptEntry->SplitSubPartitionSecondPart ? "Yes" : "No") + + UString("\nCode sub-partition: ") + (ptEntry->CodeSubPartition ? "Yes" : "No") + + UString("\nUMA cachable: ") + (ptEntry->UmaCachable ? "Yes" : "No"); + + // Add tree item + UModelIndex entryIndex = model->addItem(offset, Types::BpdtEntry, 0, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(ME_BPDT_ENTRY)), UByteArray(), Fixed, index); + + // Adjust offset + offset += sizeof(ME_BPDT_ENTRY); + + if (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Length != 0) { + // Add to partitions vector + ME_BPDT_PARTITION_INFO partition; + partition.type = Types::FptPartition; + partition.ptEntry = *ptEntry; + partition.index = entryIndex; + partitions.push_back(partition); + } + } + + // Add padding if there's no partions to add + if (partitions.size() == 0) { + UByteArray partition = region.mid(ptSize); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", + partition.size(), partition.size()); + + // Add tree item + model->addItem(ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + + return U_SUCCESS; + } + +make_partition_table_consistent: + // Sort partitions by offset + std::sort(partitions.begin(), partitions.end()); + + // Check for intersections and paddings between partitions + ME_BPDT_PARTITION_INFO padding; + + // Check intersection with the partition table header + if (partitions.front().ptEntry.Offset < ptSize) { + msg(usprintf("%s: BPDT partition has intersection with BPDT partition table, skipped", __FUNCTION__), + partitions.front().index); + partitions.erase(partitions.begin()); + goto make_partition_table_consistent; + } + // Check for padding between partition table and the first partition + else if (partitions.front().ptEntry.Offset > ptSize) { + padding.ptEntry.Offset = ptSize; + padding.ptEntry.Length = partitions.front().ptEntry.Offset - padding.ptEntry.Offset; + padding.type = Types::Padding; + partitions.insert(partitions.begin(), padding); + } + // Check for intersections/paddings between partitions + for (size_t i = 1; i < partitions.size(); i++) { + UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Length; + + // Check that partition is fully present in the image + if ((UINT64)partitions[i].ptEntry.Offset + (UINT64)partitions[i].ptEntry.Length > (UINT64)region.size()) { + if ((UINT64)partitions[i].ptEntry.Offset >= (UINT64)region.size()) { + msg(usprintf("%s: BPDT partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: BPDT partition can't fit into it's region, truncated", __FUNCTION__), partitions[i].index); + partitions[i].ptEntry.Length = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset; + } + } + + // Check for intersection with previous partition + if (partitions[i].ptEntry.Offset < previousPartitionEnd) { + // Check if current partition is located inside previous one + if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) { + msg(usprintf("%s: BPDT partition is located inside another BPDT partition, skipped", __FUNCTION__), + partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: BPDT partition intersects with prevous one, skipped", __FUNCTION__), + partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + } + + // Check for padding between current and previous partitions + else if (partitions[i].ptEntry.Offset > previousPartitionEnd) { + padding.ptEntry.Offset = previousPartitionEnd; + padding.ptEntry.Length = partitions[i].ptEntry.Offset - previousPartitionEnd; + padding.type = Types::Padding; + std::vector::iterator iter = partitions.begin(); + std::advance(iter, i); + partitions.insert(iter, padding); + } + } + + // Check for padding after the last region + if ((UINT64)partitions.back().ptEntry.Offset + (UINT64)partitions.back().ptEntry.Length < (UINT64)region.size()) { + padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Length; + padding.ptEntry.Length = region.size() - padding.ptEntry.Offset; + padding.type = Types::Padding; + partitions.push_back(padding); + } + + // Partition map is consistent + for (size_t i = 0; i < partitions.size(); i++) { + if (partitions[i].type == Types::FptPartition) { + // Get info + UString name = meBpdtEntryTypeToUString(partitions[i].ptEntry.Type); + UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Length); + UByteArray signature = partition.left(sizeof(UINT32)); + + UString info = usprintf("Full size: %Xh (%u)\nType: %Xh", + partition.size(), partition.size(), + partitions[i].ptEntry.Type) + + UString("\nSplit sub-partition first part: ") + (partitions[i].ptEntry.SplitSubPartitionFirstPart ? "Yes" : "No") + + UString("\nSplit sub-partition second part: ") + (partitions[i].ptEntry.SplitSubPartitionSecondPart ? "Yes" : "No") + + UString("\nCode sub-partition: ") + (partitions[i].ptEntry.CodeSubPartition ? "Yes" : "No") + + UString("\nUMA cachable: ") + (partitions[i].ptEntry.UmaCachable ? "Yes" : "No"); + + if (signature == ME_CPD_SIGNATURE) { + const ME_CPD_HEADER* cpdHeader = (const ME_CPD_HEADER*)partition.constData(); + name = usprintf("%c%c%c%c", cpdHeader->ShortName[0], cpdHeader->ShortName[1], cpdHeader->ShortName[2], cpdHeader->ShortName[3]); + UString text = meBpdtEntryTypeToUString(partitions[i].ptEntry.Type); + + // Add tree item + UModelIndex ptIndex = model->addItem(partitions[i].ptEntry.Offset, Types::BpdtPartition, 0, name, text, info, UByteArray(), partition, UByteArray(), Fixed, parent); + + // Parse contents + UModelIndex cpdIndex; + parseCodePartitionDirectory(partition, partitions[i].ptEntry.Offset, ptIndex, cpdIndex); + } + else { + // Add tree item + model->addItem(partitions[i].ptEntry.Offset, Types::BpdtEntry, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + } + } + else if (partitions[i].type == Types::Padding) { + UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Length); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", + partition.size(), partition.size()); + + // Add tree item + model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + } + } + + return U_SUCCESS; +} + + +USTATUS MeParser::parseCodePartitionDirectory(const UByteArray & directory, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +{ + // Check directory size + if ((UINT32)directory.size() < sizeof(ME_CPD_HEADER)) { + msg(usprintf("%s: CPD too small to fit partition table header", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Populate partition table header + const ME_CPD_HEADER* cpdHeader = (const ME_CPD_HEADER*)directory.constData(); + + // Check directory size again + UINT32 ptBodySize = cpdHeader->NumEntries * sizeof(ME_BPDT_CPD_ENTRY); + UINT32 ptSize = sizeof(ME_CPD_HEADER) + ptBodySize; + if ((UINT32)directory.size() < ptSize) { + msg(usprintf("%s: CPD too small to fit partition table", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Get info + UByteArray header = directory.left(sizeof(ME_CPD_HEADER)); + UByteArray body = directory.mid(sizeof(ME_CPD_HEADER)); + UString name = usprintf("CPD partition table"); + UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\n" + "Header version: %u\nEntry version: %u\nHeader checksum: %02Xh", + directory.size(), directory.size(), + header.size(), header.size(), + body.size(), body.size(), + cpdHeader->NumEntries, + cpdHeader->HeaderVersion, + cpdHeader->EntryVersion, + cpdHeader->HeaderChecksum); + + // Add tree item + index = model->addItem(localOffset, Types::CpdStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + + // Add partition table entries + std::vector partitions; + UINT32 offset = sizeof(ME_CPD_HEADER); + const ME_BPDT_CPD_ENTRY* firstCpdEntry = (const ME_BPDT_CPD_ENTRY*)(body.constData()); + for (UINT32 i = 0; i < cpdHeader->NumEntries; i++) { + // Populate entry header + const ME_BPDT_CPD_ENTRY* cpdEntry = firstCpdEntry + i; + UByteArray entry((const char*)cpdEntry, sizeof(ME_BPDT_CPD_ENTRY)); + + // Get info + name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", + cpdEntry->EntryName[0], cpdEntry->EntryName[1], cpdEntry->EntryName[2], cpdEntry->EntryName[3], + cpdEntry->EntryName[4], cpdEntry->EntryName[5], cpdEntry->EntryName[6], cpdEntry->EntryName[7], + cpdEntry->EntryName[8], cpdEntry->EntryName[9], cpdEntry->EntryName[10], cpdEntry->EntryName[11]); + info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", + entry.size(), entry.size(), + cpdEntry->Offset.Offset, + cpdEntry->Length) + + (cpdEntry->Offset.HuffmanCompressed ? "Yes" : "No"); + + // Add tree item + UModelIndex entryIndex = model->addItem(offset, Types::CpdEntry, 0, name, UString(), info, UByteArray(), entry, UByteArray(), Fixed, index); + + // Adjust offset + offset += sizeof(ME_BPDT_CPD_ENTRY); + + if (cpdEntry->Offset.Offset != 0 && cpdEntry->Length != 0) { + // Add to partitions vector + ME_CPD_PARTITION_INFO partition; + partition.type = Types::CpdPartition; + partition.ptEntry = *cpdEntry; + partition.index = entryIndex; + partitions.push_back(partition); + } + } + + // Add padding if there's no partions to add + if (partitions.size() == 0) { + UByteArray partition = directory.mid(ptSize); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", + partition.size(), partition.size()); + + // Add tree item + model->addItem(localOffset + ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + + return U_SUCCESS; + } + + // Sort partitions by offset + 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) + UINT32 i = 1; + while (i < partitions.size()) { + name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", + partitions[i].ptEntry.EntryName[0], partitions[i].ptEntry.EntryName[1], partitions[i].ptEntry.EntryName[2], partitions[i].ptEntry.EntryName[3], + partitions[i].ptEntry.EntryName[4], partitions[i].ptEntry.EntryName[5], partitions[i].ptEntry.EntryName[6], partitions[i].ptEntry.EntryName[7], + partitions[i].ptEntry.EntryName[8], partitions[i].ptEntry.EntryName[9], partitions[i].ptEntry.EntryName[10], partitions[i].ptEntry.EntryName[11]); + + // Check if the current entry is metadata entry + if (!name.contains(".met")) { + // No need to parse further, all metadata partitions are parsed + break; + } + + // Parse into data block, find Module Attributes extension, and get compressed size from there + UINT32 offset = 0; + UINT32 length = 0xFFFFFFFF; // Special guardian value + UByteArray partition = directory.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length); + while (offset < (UINT32)partition.size()) { + const ME_CPD_EXTENTION_HEADER* extHeader = (const ME_CPD_EXTENTION_HEADER*) (partition.constData() + offset); + if (extHeader->Length <= ((UINT32)partition.size() - offset)) { + if (extHeader->Type == 10) { //TODO: replace with defines + const ME_CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const ME_CPD_EXT_MODULE_ATTRIBUTES*)(partition.constData() + offset); + length = attrHeader->CompressedSize; + } + offset += extHeader->Length; + } + else break; + } + + // Search down for corresponding code partition + // Construct it's name by replacing last 4 non-zero butes of the name with zeros + UINT32 j = 0; + for (UINT32 k = 11; k > 0 && j < 4; k--) { + if (name[k] != '\x00') { + name[k] = '\x00'; + j++; + } + } + + // Search + j = i + 1; + while (j < partitions.size()) { + if (name == usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", + partitions[j].ptEntry.EntryName[0], partitions[j].ptEntry.EntryName[1], partitions[j].ptEntry.EntryName[2], partitions[j].ptEntry.EntryName[3], + partitions[j].ptEntry.EntryName[4], partitions[j].ptEntry.EntryName[5], partitions[j].ptEntry.EntryName[6], partitions[j].ptEntry.EntryName[7], + partitions[j].ptEntry.EntryName[8], partitions[j].ptEntry.EntryName[9], partitions[j].ptEntry.EntryName[10], partitions[j].ptEntry.EntryName[11])) { + // Found it, update it's Length if needed + if (partitions[j].ptEntry.Offset.HuffmanCompressed) { + partitions[j].ptEntry.Length = length; + } + else if (length != 0xFFFFFFFF && partitions[j].ptEntry.Length != length) { + msg(usprintf("%s: partition size mismatch between partition table (%Xh) and partition metadata (%Xh)", __FUNCTION__, + partitions[j].ptEntry.Length, length), partitions[j].index); + partitions[j].ptEntry.Length = length; // Believe metadata + } + // No need to search further + break; + } + // Check the next partition + j++; + } + // Check the next partition + i++; + } + +make_partition_table_consistent: + // Sort partitions by offset + std::sort(partitions.begin(), partitions.end()); + + // Check for intersections and paddings between partitions + ME_CPD_PARTITION_INFO padding; + + // Check intersection with the partition table header + if (partitions.front().ptEntry.Offset.Offset < ptSize) { + msg(usprintf("%s: CPD partition has intersection with CPD partition table, skipped", __FUNCTION__), + partitions.front().index); + partitions.erase(partitions.begin()); + goto make_partition_table_consistent; + } + // Check for padding between partition table and the first partition + else if (partitions.front().ptEntry.Offset.Offset > ptSize) { + padding.ptEntry.Offset.Offset = ptSize; + padding.ptEntry.Length = partitions.front().ptEntry.Offset.Offset - padding.ptEntry.Offset.Offset; + padding.type = Types::Padding; + partitions.insert(partitions.begin(), padding); + } + // Check for intersections/paddings between partitions + for (size_t i = 1; i < partitions.size(); i++) { + UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset.Offset + partitions[i - 1].ptEntry.Length; + + // Check that current region is fully present in the image + if ((UINT64)partitions[i].ptEntry.Offset.Offset + (UINT64)partitions[i].ptEntry.Length > (UINT64)directory.size()) { + if ((UINT64)partitions[i].ptEntry.Offset.Offset >= (UINT64)directory.size()) { + msg(usprintf("%s: CPD partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: CPD partition can't fit into it's region, truncated", __FUNCTION__), partitions[i].index); + partitions[i].ptEntry.Length = (UINT32)directory.size() - (UINT32)partitions[i].ptEntry.Offset.Offset; + } + } + + // Check for intersection with previous partition + if (partitions[i].ptEntry.Offset.Offset < previousPartitionEnd) { + // Check if current partition is located inside previous one + if (partitions[i].ptEntry.Offset.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) { + msg(usprintf("%s: CPD partition is located inside another CPD partition, skipped", __FUNCTION__), + partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: CPD partition intersects with prevous one, skipped", __FUNCTION__), + partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + } + // Check for padding between current and previous partitions + else if (partitions[i].ptEntry.Offset.Offset > previousPartitionEnd) { + padding.ptEntry.Offset.Offset = previousPartitionEnd; + padding.ptEntry.Length = partitions[i].ptEntry.Offset.Offset - previousPartitionEnd; + padding.type = Types::Padding; + std::vector::iterator iter = partitions.begin(); + std::advance(iter, i); + partitions.insert(iter, padding); + } + } + // Check for padding after the last region + if ((UINT64)partitions.back().ptEntry.Offset.Offset + (UINT64)partitions.back().ptEntry.Length < (UINT64)directory.size()) { + padding.ptEntry.Offset.Offset = partitions.back().ptEntry.Offset.Offset + partitions.back().ptEntry.Length; + padding.ptEntry.Length = (UINT32)directory.size() - padding.ptEntry.Offset.Offset; + padding.type = Types::Padding; + partitions.push_back(padding); + } + + // Partition map is consistent + for (size_t i = 0; i < partitions.size(); i++) { + if (partitions[i].type == Types::CpdPartition) { + UByteArray partition = directory.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length); + + // Get info + name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", + partitions[i].ptEntry.EntryName[0], partitions[i].ptEntry.EntryName[1], partitions[i].ptEntry.EntryName[2], partitions[i].ptEntry.EntryName[3], + partitions[i].ptEntry.EntryName[4], partitions[i].ptEntry.EntryName[5], partitions[i].ptEntry.EntryName[6], partitions[i].ptEntry.EntryName[7], + partitions[i].ptEntry.EntryName[8], partitions[i].ptEntry.EntryName[9], partitions[i].ptEntry.EntryName[10], partitions[i].ptEntry.EntryName[11]); + + // It's a manifest + if (name.contains(".man")) { + if (!partitions[i].ptEntry.Offset.HuffmanCompressed + && partitions[i].ptEntry.Length >= sizeof(ME_CPD_MANIFEST_HEADER)) { + const ME_CPD_MANIFEST_HEADER* manifestHeader = (const ME_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()); + + info += usprintf( + "\nHeader type: %u\nHeader length: %Xh (%u)\nHeader version: %Xh\nFlags: %08Xh\nVendor: %Xh\n" + "Date: %Xh\nSize: %Xh (%u)\nVersion: %u.%u.%u.%u\nSecurity version number: %u\nModulus size: %Xh (%u)\nExponent size: %Xh (%u)", + 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); + + // Parse data as extensions area + parseExtensionsArea(partitionIndex); + } + } + } + // It's a metadata + else if (name.contains(".met")) { + info = usprintf("Full size: %Xh (%u)\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 metadata and add it to it's info + UByteArray hash(SHA256_DIGEST_SIZE, '\x00'); + sha256(partition.constData(), partition.size(), hash.data()); + info += UString("\nMetadata hash: ") + UString(hash.toHex().constData()); + + // Add three item + UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::MetadataCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + + // Parse data as extensions area + parseExtensionsArea(partitionIndex); + } + // It's a key + else if (name.contains(".key")) { + info = usprintf("Full size: %Xh (%u)\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 it's 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 + parseExtensionsArea(partitionIndex); + } + // It's a code + else { + info = usprintf("Full size: %Xh (%u)\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 code and add it to it's info + UByteArray hash(SHA256_DIGEST_SIZE, '\x00'); + sha256(partition.constData(), partition.size(), hash.data()); + info += UString("\nHash: ") + UString(hash.toHex().constData()); + + model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::CodeCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + } + } + else if (partitions[i].type == Types::Padding) { + UByteArray partition = directory.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", partition.size(), partition.size()); + + // Add tree item + model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + } + else { + msg(usprintf("%s: CPD partition of unknown type found", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + } + + return U_SUCCESS; +} + +USTATUS MeParser::parseExtensionsArea(const UModelIndex & index) +{ + if (!index.isValid()) { + return U_INVALID_PARAMETER; + } + + UByteArray body = model->body(index); + UINT32 offset = 0; + while (offset < (UINT32)body.size()) { + const ME_CPD_EXTENTION_HEADER* extHeader = (const ME_CPD_EXTENTION_HEADER*) (body.constData() + offset); + if (extHeader->Length <= ((UINT32)body.size() - offset)) { + UByteArray partition = body.mid(offset, extHeader->Length); + + UString name = meExtensionTypeToUstring(extHeader->Type); + UString info = usprintf("Full size: %Xh (%u)\nType: %Xh", partition.size(), partition.size(), extHeader->Type); + + // Parse Signed Package Info a bit further + bool parsed = false; + if (extHeader->Type == 15) { + UByteArray header = partition.left(sizeof(ME_CPD_EXT_SIGNED_PACKAGE_INFO)); + UByteArray data = partition.mid(header.size()); + + const ME_CPD_EXT_SIGNED_PACKAGE_INFO* infoHeader = (const ME_CPD_EXT_SIGNED_PACKAGE_INFO*)header.constData(); + + info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %Xh\n" + "Package name: %c%c%c%c\nVersion control number: %Xh\nSecurity version number: %Xh\n" + "Usage bitmap: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + partition.size(), partition.size(), + header.size(), header.size(), + body.size(), body.size(), + infoHeader->ExtensionType, + infoHeader->PackageName[0], infoHeader->PackageName[1], infoHeader->PackageName[2], infoHeader->PackageName[3], + infoHeader->Vcn, + infoHeader->Svn, + infoHeader->UsageBitmap[0], infoHeader->UsageBitmap[1], infoHeader->UsageBitmap[2], infoHeader->UsageBitmap[3], + infoHeader->UsageBitmap[4], infoHeader->UsageBitmap[5], infoHeader->UsageBitmap[6], infoHeader->UsageBitmap[7], + infoHeader->UsageBitmap[8], infoHeader->UsageBitmap[9], infoHeader->UsageBitmap[10], infoHeader->UsageBitmap[11], + infoHeader->UsageBitmap[12], infoHeader->UsageBitmap[13], infoHeader->UsageBitmap[14], infoHeader->UsageBitmap[15]); + + // Add tree item + UModelIndex infoIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, header, data, UByteArray(), Fixed, index); + parseSignedPackageInfoData(infoIndex); + parsed = true; + } + + // Parse IFWI Partition Manifest a bit further + else if (extHeader->Type == 22) { + const ME_CPD_EXT_IFWI_PARTITION_MANIFEST* attrHeader = (const ME_CPD_EXT_IFWI_PARTITION_MANIFEST*)partition.constData(); + + // This hash is stored reversed, because why the hell not + // Need to reverse it back to normal + UByteArray hash((const char*)&attrHeader->CompletePartitionHash, sizeof(attrHeader->CompletePartitionHash)); + std::reverse(hash.begin(), hash.end()); + + info = usprintf("Full size: %Xh (%u)\nType: %Xh\n" + "Partition name: %c%c%c%c\nPartition length: %Xh\nPartition version major: %Xh\nPartition version minor: %Xh\n" + "Data format version: %Xh\nInstance ID: %Xh\nHash algorithm: %Xh\nHash size: %Xh\nAction on update: %Xh", + partition.size(), partition.size(), + attrHeader->ExtensionType, + attrHeader->PartitionName[0], attrHeader->PartitionName[1], attrHeader->PartitionName[2], attrHeader->PartitionName[3], + attrHeader->CompletePartitionLength, + attrHeader->PartitionVersionMajor, attrHeader->PartitionVersionMinor, + attrHeader->DataFormatVersion, + attrHeader->InstanceId, + attrHeader->HashAlgorithm, + attrHeader->HashSize, + attrHeader->ActionOnUpdate) + + UString("\nSupport multiple instances: ") + (attrHeader->SupportMultipleInstances ? "Yes" : "No") + + UString("\nSupport API version based update: ") + (attrHeader->SupportApiVersionBasedUpdate ? "Yes" : "No") + + UString("\nObey full update rules: ") + (attrHeader->ObeyFullUpdateRules ? "Yes" : "No") + + UString("\nIFR enable only: ") + (attrHeader->IfrEnableOnly ? "Yes" : "No") + + UString("\nAllow cross point update: ") + (attrHeader->AllowCrossPointUpdate ? "Yes" : "No") + + UString("\nAllow cross hotfix update: ") + (attrHeader->AllowCrossHotfixUpdate ? "Yes" : "No") + + UString("\nPartial update only: ") + (attrHeader->PartialUpdateOnly ? "Yes" : "No") + + UString("\nPartition hash: ") + UString(hash.toHex().constData()); + + // Add tree item + model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); + parsed = true; + } + + // Parse Module Attributes a bit further + else if (extHeader->Type == 10) { + const ME_CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const ME_CPD_EXT_MODULE_ATTRIBUTES*)partition.constData(); + + // This hash is stored reversed, because why the hell not + // Need to reverse it back to normal + UByteArray hash((const char*)&attrHeader->ImageHash, sizeof(attrHeader->ImageHash)); + std::reverse(hash.begin(), hash.end()); + + info = usprintf("Full size: %Xh (%u)\nType: %Xh\n" + "Compression type: %Xh\nUncompressed size: %Xh (%u)\nCompressed size: %Xh (%u)\nGlobal module ID: %Xh\nImage hash: ", + partition.size(), partition.size(), + attrHeader->ExtensionType, + attrHeader->CompressionType, + attrHeader->UncompressedSize, attrHeader->UncompressedSize, + attrHeader->CompressedSize, attrHeader->CompressedSize, + attrHeader->GlobalModuleId) + UString(hash.toHex().constData()); + + // Add tree item + model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); + parsed = true; + } + + if (!parsed) { + // Add tree item, if needed + model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); + } + + offset += extHeader->Length; + } + else break; + // TODO: add padding at the end + } + + return U_SUCCESS; +} + +USTATUS MeParser::parseSignedPackageInfoData(const UModelIndex & index) +{ + if (!index.isValid()) { + return U_INVALID_PARAMETER; + } + + UByteArray body = model->body(index); + UINT32 offset = 0; + while (offset < (UINT32)body.size()) { + const ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES* moduleHeader = (const ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES*)(body.constData() + offset); + if (sizeof(ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES) <= ((UINT32)body.size() - offset)) { + UByteArray module((const char*)moduleHeader,sizeof(ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES)); + + UString name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", + moduleHeader->Name[0], moduleHeader->Name[1], moduleHeader->Name[2], moduleHeader->Name[3], + moduleHeader->Name[4], moduleHeader->Name[5], moduleHeader->Name[6], moduleHeader->Name[7], + moduleHeader->Name[8], moduleHeader->Name[9], moduleHeader->Name[10],moduleHeader->Name[11]); + + // This hash is stored reversed, because why the hell not + // Need to reverse it back to normal + UByteArray hash((const char*)&moduleHeader->MetadataHash, sizeof(moduleHeader->MetadataHash)); + std::reverse(hash.begin(), hash.end()); + + UString info = usprintf("Full size: %X (%u)\nType: %Xh\nHash algorithm: %Xh\nHash size: %Xh (%u)\nMetadata size: %Xh (%u)\nMetadata hash: ", + module.size(), module.size(), + moduleHeader->Type, + moduleHeader->HashAlgorithm, + moduleHeader->HashSize, moduleHeader->HashSize, + moduleHeader->MetadataSize, moduleHeader->MetadataSize) + UString(hash.toHex().constData()); + // Add tree otem + model->addItem(offset, Types::CpdSpiEntry, 0, name, UString(), info, UByteArray(), module, UByteArray(), Fixed, index); + + offset += module.size(); + } + else break; + // TODO: add padding at the end + } + + return U_SUCCESS; +} + +#endif // U_ENABLE_ME_PARSING_SUPPORT + diff --git a/common/meparser.h b/common/meparser.h old mode 100644 new mode 100755 index 45b66dd..cc69c64 --- a/common/meparser.h +++ b/common/meparser.h @@ -21,10 +21,235 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "ustring.h" #include "ubytearray.h" #include "treemodel.h" -#include "me.h" +#include "sha256.h" -// TODO: implement ME region parser +#ifdef U_ENABLE_ME_PARSING_SUPPORT +// FPT +#define ME_ROM_BYPASS_VECTOR_SIZE 0x10 + +const UByteArray ME_FPT_HEADER_SIGNATURE("\x24\x46\x50\x54", 4); //$FPT + +typedef struct ME_FPT_HEADER_ { + UINT32 Signature; + UINT32 NumEntries; + UINT8 HeaderVersion; + UINT8 EntryVersion; + UINT8 HeaderLength; + UINT8 Checksum; // One bit for Redundant before IFWI + UINT16 TicksToAdd; + UINT16 TokensToAdd; + UINT32 UmaSize; // Flags in SPS + UINT32 FlashLayout; // Crc32 before IFWI + UINT16 FitcMajor; + UINT16 FitcMinor; + UINT16 FitcHotfix; + UINT16 FitcBuild; +} ME_FPT_HEADER; + +typedef struct ME_FPT_ENTRY_{ + CHAR8 PartitionName[4]; + UINT8 Reserved1; + UINT32 Offset; + UINT32 Length; + UINT8 Reserved2[12]; + UINT32 PartitionType : 7; + UINT32 CopyToDramCache : 1; + UINT32 Reserved3 : 7; + UINT32 BuiltWithLength1 : 1; + UINT32 BuiltWithLength2 : 1; + UINT32 Reserved4 : 7; + UINT32 EntryValid : 8; +} ME_FPT_ENTRY; + + +// IFWI +typedef struct ME_IFWI_LAYOUT_HEADER_ { + UINT8 RomBypassVector[16]; + UINT32 DataPartitionOffset; + UINT32 DataPartitionSize; + UINT32 Boot1Offset; + UINT32 Boot1Size; + UINT32 Boot2Offset; + UINT32 Boot2Size; + UINT32 Boot3Offset; + UINT32 Boot3Size; +} ME_IFWI_LAYOUT_HEADER; + + +// BPDT +const UByteArray ME_BPDT_GREEN_SIGNATURE("\xAA\x55\x00\x00", 4); //0x000055AA +const UByteArray ME_BPDT_YELLOW_SIGNATURE("\xAA\x55\xAA\x00", 4); //0x00AA55AA + +typedef struct ME_BPDT_HEADER_ { + UINT32 Signature; + UINT16 NumEntries; + UINT16 Version; + UINT32 Checksum; + UINT32 IfwiVersion; + UINT16 FitcMajor; + UINT16 FitcMinor; + UINT16 FitcHotfix; + UINT16 FitcBuild; +} ME_BPDT_HEADER ; + +typedef struct ME_BPDT_ENTRY_ { + UINT32 Type : 16; + UINT32 SplitSubPartitionFirstPart : 1; + UINT32 SplitSubPartitionSecondPart : 1; + UINT32 CodeSubPartition : 1; + UINT32 UmaCachable : 1; + UINT32 Reserved: 12; + UINT32 Offset; + UINT32 Length; +} ME_BPDT_ENTRY; + +// CPD +const UByteArray ME_CPD_SIGNATURE("\x24\x43\x50\x44", 4); //$CPD + +typedef struct ME_CPD_HEADER_ { + UINT32 Signature; + UINT32 NumEntries; + UINT8 HeaderVersion; + UINT8 EntryVersion; + UINT8 HeaderLength; + UINT8 HeaderChecksum; + UINT8 ShortName[4]; +} ME_CPD_HEADER; + +typedef struct ME_BPDT_CPD_ENTRY_ { + UINT8 EntryName[12]; + struct { + UINT32 Offset : 25; + UINT32 HuffmanCompressed : 1; + UINT32 Reserved : 6; + } Offset; + UINT32 Length; + UINT32 Reserved; +} ME_BPDT_CPD_ENTRY; + +typedef struct ME_CPD_MANIFEST_HEADER_ { + UINT32 HeaderType; + UINT32 HeaderLength; + UINT32 HeaderVersion; + UINT32 Flags; + UINT32 Vendor; + UINT32 Date; + UINT32 Size; + UINT32 HeaderId; + UINT32 Reserved1; + UINT16 VersionMajor; + UINT16 VersionMinor; + UINT16 VersionBugfix; + UINT16 VersionBuild; + UINT32 SecurityVersion; + UINT8 Reserved2[8]; + UINT8 Reserved3[64]; + UINT32 ModulusSize; + UINT32 ExponentSize; + //manifest_rsa_key_t public_key; + //manifest_signature_t signature; +} ME_CPD_MANIFEST_HEADER; + +typedef struct ME_CPD_EXTENTION_HEADER_ { + UINT32 Type; + UINT32 Length; +} ME_CPD_EXTENTION_HEADER; + +typedef struct ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES_ { + UINT8 Name[12]; + UINT8 Type; + UINT8 HashAlgorithm; + UINT16 HashSize; + UINT32 MetadataSize; + UINT8 MetadataHash[32]; +} ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES; + +typedef struct ME_CPD_EXT_SIGNED_PACKAGE_INFO_ { + UINT32 ExtensionType; + UINT32 ExtensionLength; + UINT8 PackageName[4]; + UINT32 Vcn; + UINT8 UsageBitmap[16]; + UINT32 Svn; + UINT8 Reserved[16]; + // ME_EXT_SIGNED_PACKAGE_INFO_MODULES Modules[]; +} ME_CPD_EXT_SIGNED_PACKAGE_INFO; + +typedef struct ME_CPD_EXT_MODULE_ATTRIBUTES_ { + UINT32 ExtensionType; + UINT32 ExtensionLength; + UINT8 CompressionType; + UINT8 Reserved[3]; + UINT32 UncompressedSize; + UINT32 CompressedSize; + UINT32 GlobalModuleId; + UINT8 ImageHash[32]; +} ME_CPD_EXT_MODULE_ATTRIBUTES; + +typedef struct ME_CPD_EXT_IFWI_PARTITION_MANIFEST_ { + UINT32 ExtensionType; + UINT32 ExtensionLength; + UINT8 PartitionName[4]; + UINT32 CompletePartitionLength; + UINT16 PartitionVersionMinor; + UINT16 PartitionVersionMajor; + UINT32 DataFormatVersion; + UINT32 InstanceId; + UINT32 SupportMultipleInstances : 1; + UINT32 SupportApiVersionBasedUpdate : 1; + UINT32 ActionOnUpdate : 2; + UINT32 ObeyFullUpdateRules : 1; + UINT32 IfrEnableOnly : 1; + UINT32 AllowCrossPointUpdate : 1; + UINT32 AllowCrossHotfixUpdate : 1; + UINT32 PartialUpdateOnly : 1; + UINT32 ReservedFlags : 23; + UINT32 HashAlgorithm : 8; + UINT32 HashSize : 24; + UINT8 CompletePartitionHash[32]; + UINT8 Reserved[20]; +} ME_CPD_EXT_IFWI_PARTITION_MANIFEST; + +#define ME_MODULE_COMPRESSION_TYPE_UNCOMPRESSED 0 +#define ME_MODULE_COMPRESSION_TYPE_HUFFMAN 1 +#define ME_MODULE_COMPRESSION_TYPE_LZMA 2 + +#define ME_MANIFEST_HEADER_ID 0x324E4D24 //$MN2 + + +class MeParser +{ +public: + // Default constructor and destructor + MeParser(TreeModel* treeModel) : model(treeModel) {} + ~MeParser() {} + + // Returns messages + std::vector > getMessages() const { return messagesVector; } + // Clears messages + void clearMessages() { messagesVector.clear(); } + + // ME parsing + USTATUS parseMeRegionBody(const UModelIndex & index); + +private: + TreeModel *model; + std::vector > messagesVector; + + void msg(const UString message, const UModelIndex index = UModelIndex()) { + messagesVector.push_back(std::pair(message, index)); + } + + USTATUS parseFptRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index); + USTATUS parseIfwiRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index); + USTATUS parseBpdtRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index); + + USTATUS parseCodePartitionDirectory(const UByteArray & directory, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseExtensionsArea(const UModelIndex & index); + USTATUS parseSignedPackageInfoData(const UModelIndex & index); +}; +#else class MeParser { public: @@ -40,4 +265,5 @@ public: // ME parsing USTATUS parseMeRegionBody(const UModelIndex & index) { U_UNUSED_PARAMETER(index); return U_SUCCESS; } }; +#endif // U_ENABLE_ME_PARSING_SUPPORT #endif // MEPARSER_H diff --git a/common/nvram.h b/common/nvram.h old mode 100644 new mode 100755 index eb03516..74d9126 --- a/common/nvram.h +++ b/common/nvram.h @@ -86,6 +86,7 @@ const UByteArray NVRAM_ADDITIONAL_STORE_VOLUME_GUID #define NVRAM_VSS_STORE_SIGNATURE 0x53535624 // $VSS #define NVRAM_APPLE_SVS_STORE_SIGNATURE 0x53565324 // $SVS +#define NVRAM_APPLE_NSS_STORE_SIGNATURE 0x53534E24 // $NSS #define NVRAM_APPLE_FSYS_STORE_SIGNATURE 0x73797346 // Fsys #define NVRAM_APPLE_GAID_STORE_SIGNATURE 0x64696147 // Gaid #define NVRAM_VSS_VARIABLE_START_ID 0x55AA diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp old mode 100644 new mode 100755 index abaf6d7..6a78b4f --- a/common/nvramparser.cpp +++ b/common/nvramparser.cpp @@ -536,7 +536,7 @@ USTATUS NvramParser::findNextStore(const UModelIndex & index, const UByteArray & UINT32 offset = storeOffset; for (; offset < dataSize - sizeof(UINT32); offset++) { const UINT32* currentPos = (const UINT32*)(volume.constData() + offset); - if (*currentPos == NVRAM_VSS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_SVS_STORE_SIGNATURE) { // $VSS or $SVS signatures found, perform checks + if (*currentPos == NVRAM_VSS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_SVS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_NSS_STORE_SIGNATURE) { // $VSS, $SVS or $NSS signatures found, perform checks const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)currentPos; if (vssHeader->Format != NVRAM_VSS_VARIABLE_STORE_FORMATTED) { msg(usprintf("%s: VSS store candidate at offset %Xh skipped, has invalid format %02Xh", __FUNCTION__, localOffset + offset, vssHeader->Format), index); @@ -645,20 +645,27 @@ USTATUS NvramParser::findNextStore(const UModelIndex & index, const UByteArray & // All checks passed, store found break; } - else if (*currentPos == INTEL_MICROCODE_HEADER_VERSION) {// Intel microcode - if (!INTEL_MICROCODE_HEADER_SIZES_VALID(currentPos)) // Check header sizes - continue; - + else if (*currentPos == INTEL_MICROCODE_HEADER_VERSION_1) {// Intel microcode // Check reserved bytes const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos; bool reservedBytesValid = true; for (UINT32 i = 0; i < sizeof(ucodeHeader->Reserved); i++) - if (ucodeHeader->Reserved[i] != INTEL_MICROCODE_HEADER_RESERVED_BYTE) { + if (ucodeHeader->Reserved[i] != 0x00) { reservedBytesValid = false; break; } if (!reservedBytesValid) continue; + + // Data size is multiple of 4 + if (ucodeHeader->DataSize % 4 != 0) { + continue; + } + + // TotalSize is greater then DataSize and is multiple of 1024 + if (ucodeHeader->TotalSize <= ucodeHeader->DataSize || ucodeHeader->TotalSize % 1024 != 0) { + continue; + } // All checks passed, store found break; @@ -710,7 +717,7 @@ USTATUS NvramParser::findNextStore(const UModelIndex & index, const UByteArray & USTATUS NvramParser::getStoreSize(const UByteArray & data, const UINT32 storeOffset, UINT32 & storeSize) { const UINT32* signature = (const UINT32*)(data.constData() + storeOffset); - if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) { + if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE || *signature == NVRAM_APPLE_NSS_STORE_SIGNATURE) { const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)signature; storeSize = vssHeader->Size; } @@ -755,7 +762,7 @@ USTATUS NvramParser::getStoreSize(const UByteArray & data, const UINT32 storeOff const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)signature; storeSize = markerHeader->Size; } - else if (*signature == INTEL_MICROCODE_HEADER_VERSION) { // Intel microcode, must be checked after SLIC marker because of the same *signature values + else if (*signature == INTEL_MICROCODE_HEADER_VERSION_1) { // Intel microcode, must be checked after SLIC marker because of the same *signature values const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)signature; storeSize = ucodeHeader->TotalSize; } else { @@ -796,10 +803,19 @@ USTATUS NvramParser::parseVssStoreHeader(const UByteArray & store, const UINT32 UByteArray body = store.mid(sizeof(VSS_VARIABLE_STORE_HEADER), storeSize - sizeof(VSS_VARIABLE_STORE_HEADER)); // Add info - bool isSvsStore = (vssStoreHeader->Signature == NVRAM_APPLE_SVS_STORE_SIGNATURE); - UString name = isSvsStore ? UString("SVS store") : UString("VSS store"); - UString info = usprintf("Signature: %s\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nFormat: %02Xh\nState: %02Xh\nUnknown: %04Xh", - isSvsStore ? "$SVS" : "$VSS", + UString name; + if (vssStoreHeader->Signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) { + name = UString("SVS store"); + } + else if (vssStoreHeader->Signature == NVRAM_APPLE_NSS_STORE_SIGNATURE) { + name = UString("NSS store"); + } + else { + name = UString("VSS store"); + } + + UString info = usprintf("Signature: %Xh\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nFormat: %02Xh\nState: %02Xh\nUnknown: %04Xh", + vssStoreHeader->Signature, storeSize, storeSize, header.size(), header.size(), body.size(), body.size(), @@ -912,7 +928,7 @@ USTATUS NvramParser::parseFtwStoreHeader(const UByteArray & store, const UINT32 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)header.data(); crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0; crcFtwBlockHeader->State = emptyByte ? 0xFF : 0; - UINT32 calculatedCrc = crc32(0, (const UINT8*)crcFtwBlockHeader, headerSize); + UINT32 calculatedCrc = (UINT32)crc32(0, (const UINT8*)crcFtwBlockHeader, headerSize); // Add info UString name("FTW store"); @@ -996,7 +1012,7 @@ USTATUS NvramParser::parseFsysStoreHeader(const UByteArray & store, const UINT32 // Check store checksum UINT32 storedCrc = *(UINT32*)store.right(sizeof(UINT32)).constData(); - UINT32 calculatedCrc = crc32(0, (const UINT8*)store.constData(), (const UINT32)store.size() - sizeof(UINT32)); + UINT32 calculatedCrc = (UINT32)crc32(0, (const UINT8*)store.constData(), (const UINT32)store.size() - sizeof(UINT32)); // Add info bool isGaidStore = (fsysStoreHeader->Signature == NVRAM_APPLE_GAID_STORE_SIGNATURE); @@ -1234,8 +1250,8 @@ USTATUS NvramParser::parseStoreHeader(const UByteArray & store, const UINT32 loc } // Check signature and run parser function needed - // VSS/SVS store - if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) + // VSS/SVS/NSS store + if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE || *signature == NVRAM_APPLE_NSS_STORE_SIGNATURE) return parseVssStoreHeader(store, localOffset, false, parent, index); // VSS2 store if (*signature == NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID_PART1 || *signature == NVRAM_VSS2_STORE_GUID_PART1) @@ -1266,7 +1282,7 @@ USTATUS NvramParser::parseStoreHeader(const UByteArray & store, const UINT32 loc return parseSlicMarkerHeader(store, localOffset, parent, index); // Intel microcode // Must be checked after SLIC marker because of the same *signature values - else if (*signature == INTEL_MICROCODE_HEADER_VERSION) + else if (*signature == INTEL_MICROCODE_HEADER_VERSION_1) return ffsParser->parseIntelMicrocodeHeader(store, localOffset, parent, index); msg(usprintf("parseStoreHeader: don't know how to parse a header with signature %08Xh", *signature), parent); @@ -1393,7 +1409,7 @@ USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index, UINT8 alignmen // Calculate CRC32 of the variable data storedCrc32 = appleVariableHeader->DataCrc32; - calculatedCrc32 = crc32(0, (const UINT8*)body.constData(), body.size()); + calculatedCrc32 = (UINT32)crc32(0, (const UINT8*)body.constData(), body.size()); } } diff --git a/common/types.cpp b/common/types.cpp old mode 100644 new mode 100755 index cc7af25..ca365ba --- a/common/types.cpp +++ b/common/types.cpp @@ -66,6 +66,20 @@ UString itemTypeToUString(const UINT8 type) case Types::FlashMapEntry: return UString("FlashMap entry"); case Types::Microcode: return UString("Microcode"); case Types::SlicData: return UString("SLIC data"); + // ME-specific + case Types::FptStore: return UString("FPT store"); + case Types::FptEntry: return UString("FPT entry"); + case Types::IfwiHeader: return UString("IFWI header"); + case Types::IfwiPartition: return UString("IFWI partition"); + case Types::FptPartition: return UString("FPT partition"); + case Types::BpdtStore: return UString("BPDT store"); + case Types::BpdtEntry: return UString("BPDT entry"); + case Types::BpdtPartition: return UString("BPDT partition"); + case Types::CpdStore: return UString("CPD store"); + case Types::CpdEntry: return UString("CPD entry"); + case Types::CpdPartition: return UString("CPD partition"); + case Types::CpdExtension: return UString("CPD extension"); + case Types::CpdSpiEntry: return UString("CPD SPI entry"); } return UString("Unknown"); @@ -132,6 +146,26 @@ UString itemSubtypeToUString(const UINT8 type, const UINT8 subtype) if (subtype == Subtypes::IntelMicrocode) return UString("Intel"); if (subtype == Subtypes::AmdMicrocode) return UString("AMD"); break; + // ME-specific + case Types::FptEntry: + if (subtype == Subtypes::ValidFptEntry) return UString("Valid"); + if (subtype == Subtypes::InvalidFptEntry) return UString("Invalid"); + break; + case Types::FptPartition: + if (subtype == Subtypes::CodeFptPartition) return UString("Code"); + if (subtype == Subtypes::DataFptPartition) return UString("Data"); + if (subtype == Subtypes::GlutFptPartition) return UString("GLUT"); + break; + case Types::IfwiPartition: + if (subtype == Subtypes::BootIfwiPartition) return UString("Boot"); + if (subtype == Subtypes::DataIfwiPartition) return UString("Data"); + break; + case Types::CpdPartition: + if (subtype == Subtypes::ManifestCpdPartition) return UString("Manifest"); + if (subtype == Subtypes::MetadataCpdPartition) return UString("Metadata"); + if (subtype == Subtypes::KeyCpdPartition) return UString("Key"); + if (subtype == Subtypes::CodeCpdPartition) return UString("Code"); + break; } return UString(); diff --git a/common/types.h b/common/types.h old mode 100644 new mode 100755 index 363c3a6..edab19f --- a/common/types.h +++ b/common/types.h @@ -58,6 +58,20 @@ namespace Types { FlashMapEntry, Microcode, SlicData, + // ME-specific + IfwiHeader, + IfwiPartition, + FptStore, + FptEntry, + FptPartition, + BpdtStore, + BpdtEntry, + BpdtPartition, + CpdStore, + CpdEntry, + CpdPartition, + CpdExtension, + CpdSpiEntry }; } @@ -150,6 +164,30 @@ namespace Subtypes { PubkeySlicData = 190, MarkerSlicData }; + + // ME-specific + enum IfwiPartitionSubtypes { + DataIfwiPartition = 200, + BootIfwiPartition + }; + + enum FptEntrySubtypes { + ValidFptEntry = 210, + InvalidFptEntry + }; + + enum FptPartitionSubtypes { + CodeFptPartition = 220, + DataFptPartition, + GlutFptPartition + }; + + enum CpdPartitionSubtypes { + ManifestCpdPartition = 230, + MetadataCpdPartition, + KeyCpdPartition, + CodeCpdPartition + }; } // *ToUString conversion routines diff --git a/common/utility.cpp b/common/utility.cpp old mode 100644 new mode 100755 index c59906e..929e16d --- a/common/utility.cpp +++ b/common/utility.cpp @@ -141,6 +141,7 @@ UString errorCodeToUString(USTATUS errorCode) case U_TRUNCATED_IMAGE: return UString("Image is truncated"); case U_INVALID_CAPSULE: return UString("Invalid capsule"); case U_STORES_NOT_FOUND: return UString("Stores not found"); + case U_INVALID_STORE_SIZE: return UString("Invalid store size"); default: return usprintf("Unknown error %02X", errorCode); } } @@ -149,12 +150,12 @@ UString errorCodeToUString(USTATUS errorCode) USTATUS decompress(const UByteArray & compressedData, const UINT8 compressionType, UINT8 & algorithm, UINT32 & dictionarySize, UByteArray & decompressedData, UByteArray & efiDecompressedData) { const UINT8* data; - UINT32 dataSize; + UINT32 dataSize; UINT8* decompressed; UINT8* efiDecompressed; - UINT32 decompressedSize = 0; + UINT32 decompressedSize = 0; UINT8* scratch; - UINT32 scratchSize = 0; + UINT32 scratchSize = 0; const EFI_TIANO_HEADER* header; // For all but LZMA dictionary size is 0 @@ -333,6 +334,24 @@ UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize) return (UINT16)(0x10000 - counter); } +// 32bit checksum calculation routine +UINT32 calculateChecksum32(const UINT32* buffer, UINT32 bufferSize) +{ + if (!buffer) + return 0; + + UINT32 counter = 0; + UINT32 index = 0; + + bufferSize /= sizeof(UINT32); + + for (; index < bufferSize; index++) { + counter = (UINT32)(counter + buffer[index]); + } + + return (UINT32)(0x100000000ULL - counter); +} + // Get padding type for a given padding UINT8 getPaddingType(const UByteArray & padding) { diff --git a/common/utility.h b/common/utility.h old mode 100644 new mode 100755 index 7bad658..863fb0a --- a/common/utility.h +++ b/common/utility.h @@ -44,6 +44,9 @@ UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize); // 16bit checksum calculation routine UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); +// 32bit checksum calculation routine +UINT32 calculateChecksum32(const UINT32* buffer, UINT32 bufferSize); + // Return padding type from it's contents UINT8 getPaddingType(const UByteArray & padding); diff --git a/common/zlib/gzread.c b/common/zlib/gzread.c index 956b91e..f40e34b 100755 --- a/common/zlib/gzread.c +++ b/common/zlib/gzread.c @@ -417,6 +417,9 @@ z_size_t ZEXPORT gzfread(buf, size, nitems, file) z_size_t len; gz_statep state; + if (nitems == 0 || size == 0) + return 0; + /* get internal structure */ if (file == NULL) return 0; diff --git a/common/zlib/gzwrite.c b/common/zlib/gzwrite.c index c7b5651..6c21870 100755 --- a/common/zlib/gzwrite.c +++ b/common/zlib/gzwrite.c @@ -280,6 +280,9 @@ z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) z_size_t len; gz_statep state; + if (nitems == 0 || size == 0) + return 0; + /* get internal structure */ if (file == NULL) return 0; diff --git a/version.h b/version.h index 3be6074..2cc36fc 100644 --- a/version.h +++ b/version.h @@ -1,6 +1,6 @@ /* version.h -Copyright (c) 2018, LongSoft. All rights reserved. +Copyright (c) 2019, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -14,6 +14,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef VERSION_H #define VERSION_H -#define PROGRAM_VERSION "NE alpha 55" " (" __DATE__ ")" +#define PROGRAM_VERSION "NE alpha 56" " (" __DATE__ ")" #endif // VERSION_H