diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bd25b50 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: cpp + +compiler: + - clang + - gcc + +before_install: + - sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa + - sudo apt-get update -qq + - sudo apt-get install -qq qt5-qmake qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev + +script: + - qmake -qt=qt5 uefitool.pro + - make diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3f16459 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,23 @@ +Copyright (c) 2015, Nikolaj Schlej +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/UEFIPatch/uefipatch_main.cpp b/UEFIPatch/uefipatch_main.cpp index 2ff15c9..e14e59f 100644 --- a/UEFIPatch/uefipatch_main.cpp +++ b/UEFIPatch/uefipatch_main.cpp @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) result = w.patchFromFile(a.arguments().at(1)); } else { - std::cout << "UEFIPatch 0.3.4 - UEFI image file patching utility" << std::endl << std::endl << + std::cout << "UEFIPatch 0.3.9 - UEFI image file patching utility" << std::endl << std::endl << "Usage: UEFIPatch image_file" << std::endl << std::endl << "Patches will be read from patches.txt file\n"; return ERR_SUCCESS; diff --git a/basetypes.h b/basetypes.h index 083267b..3c07309 100644 --- a/basetypes.h +++ b/basetypes.h @@ -85,6 +85,8 @@ typedef unsigned int UINTN; #define ERR_INVALID_SYMBOL 40 #define ERR_NOTHING_TO_PATCH 41 #define ERR_DEPEX_PARSE_FAILED 42 +#define ERR_TRUNCATED_IMAGE 43 +#define ERR_BAD_RELOCATION_ENTRY 44 #define ERR_NOT_IMPLEMENTED 0xFF // UDK porting definitions diff --git a/descriptor.h b/descriptor.h index 82ff6da..5bdefd5 100644 --- a/descriptor.h +++ b/descriptor.h @@ -17,11 +17,11 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "basetypes.h" // Make sure we use right packing rules -#pragma pack(push,1) +#pragma pack(push, 1) // Flash descriptor header typedef struct _FLASH_DESCRIPTOR_HEADER { - UINT8 FfVector[16]; // Must be 16 0xFFs + UINT8 FfVector[16]; // Must be 16 0xFFs UINT32 Signature; // 0x0FF0A55A } FLASH_DESCRIPTOR_HEADER; @@ -34,37 +34,41 @@ typedef struct _FLASH_DESCRIPTOR_HEADER { // Descriptor map // Base fields are storing bits [11:4] of actual base addresses, all other bits are 0 typedef struct _FLASH_DESCRIPTOR_MAP { - UINT8 ComponentBase; // 0x03 on most machines - UINT8 NumberOfFlashChips; // Zero-based number of flash chips installed on board - UINT8 RegionBase; // 0x04 on most machines - UINT8 NumberOfRegions; // Zero-based number of flash regions (descriptor is always included) - UINT8 MasterBase; // 0x06 on most machines - UINT8 NumberOfMasters; // Zero-based number of flash masters - UINT8 PchStrapsBase; // 0x10 on most machines - UINT8 NumberOfPchStraps; // One-based number of UINT32s to read as PCH Straps, min=0, max=255 (1 Kb) - UINT8 ProcStrapsBase; // 0x20 on most machines - UINT8 NumberOfProcStraps; // Number of PROC straps to be read, can be 0 or 1 - UINT8 IccTableBase; // 0x21 on most machines - UINT8 NumberOfIccTableEntries; // 0x00 on most machines - UINT8 DmiTableBase; // 0x25 on most machines - UINT8 NumberOfDmiTableEntries; // 0x00 on most machines - UINT16 ReservedZero; // Still unknown, zeros in all descriptors I have seen + // FLMAP0 + UINT32 ComponentBase : 8; + UINT32 NumberOfFlashChips : 2; // Zero-based number of flash chips installed on board + UINT32 : 6; + UINT32 RegionBase : 8; + UINT32 NumberOfRegions : 3; // Reserved in v2 descriptor + UINT32 : 5; + // FLMAP 1 + UINT32 MasterBase : 8; + UINT32 NumberOfMasters : 2; // Zero-based number of flash masters + UINT32 : 6; + UINT32 PchStrapsBase : 8; + UINT32 NumberOfPchStraps : 8; // One-based number of UINT32s to read as PCH straps, min=0, max=255 (1 Kb) + // FLMAP 2 + UINT32 ProcStrapsBase : 8; + UINT32 NumberOfProcStraps : 8; // One-based number of UINT32s to read as processor straps, min=0, max=255 (1 Kb) + UINT32: 16; } FLASH_DESCRIPTOR_MAP; +#define FLASH_DESCRIPTOR_MAX_BASE 0xE0 + // Component section // Flash parameters DWORD structure typedef struct _FLASH_PARAMETERS { - UINT8 FirstChipDensity : 3; - UINT8 SecondChipDensity : 3; - UINT8 ReservedZero0 : 2; // Still unknown, zeros in all descriptors I have seen - UINT8 ReservedZero1 : 8; // Still unknown, zeros in all descriptors I have seen - UINT8 ReservedZero2 : 4; // Still unknown, zeros in all descriptors I have seen + UINT8 FirstChipDensity : 4; + UINT8 SecondChipDensity : 4; + UINT8 : 8; + UINT8 : 1; + UINT8 ReadClockFrequency : 3; // Hardcoded value of 20 Mhz (000b) in v1 descriptors and 17 Mhz (110b) in v2 ones UINT8 FastReadEnabled : 1; - UINT8 FastReadFreqency : 3; - UINT8 FlashReadStatusFrequency : 3; + UINT8 FastReadFrequency : 3; UINT8 FlashWriteFrequency : 3; + UINT8 FlashReadStatusFrequency : 3; UINT8 DualOutputFastReadSupported : 1; - UINT8 ReservedZero3 : 1; // Still unknown, zero in all descriptors I have seen + UINT8 : 1; } FLASH_PARAMETERS; // Flash densities @@ -74,11 +78,16 @@ typedef struct _FLASH_PARAMETERS { #define FLASH_DENSITY_4MB 0x03 #define FLASH_DENSITY_8MB 0x04 #define FLASH_DENSITY_16MB 0x05 +#define FLASH_DENSITY_32MB 0x06 +#define FLASH_DENSITY_64MB 0x07 +#define FLASH_DENSITY_UNUSED 0x0F // Flash frequencies -#define FLASH_FREQUENCY_20MHZ 0x00 -#define FLASH_FREQUENCY_33MHZ 0x01 -#define FLASH_FREQUENCY_50MHZ 0x04 +#define FLASH_FREQUENCY_20MHZ 0x00 +#define FLASH_FREQUENCY_33MHZ 0x01 +#define FLASH_FREQUENCY_48MHZ 0x02 +#define FLASH_FREQUENCY_50MHZ_30MHZ 0x04 +#define FLASH_FREQUENCY_17MHZ 0x06 // Component section structure typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION { @@ -87,52 +96,82 @@ typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION { UINT8 InvalidInstruction1; // UINT8 InvalidInstruction2; // UINT8 InvalidInstruction3; // - UINT16 PartitionBoundary; // Upper 16 bit of partition boundary address. Default is 0x0000, which makes the boundary to be 0x00001000 - UINT16 ReservedZero; // Still unknown, zero in all descriptors I have seen } FLASH_DESCRIPTOR_COMPONENT_SECTION; +// Component section structure +typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION_V2 { + FLASH_PARAMETERS FlashParameters; + UINT8 InvalidInstruction0; // Instructions for SPI chip, that must not be executed, like FLASH ERASE + UINT8 InvalidInstruction1; // + UINT8 InvalidInstruction2; // + UINT8 InvalidInstruction3; // + UINT8 InvalidInstruction4; // + UINT8 InvalidInstruction5; // + UINT8 InvalidInstruction6; // + UINT8 InvalidInstruction7; // +} FLASH_DESCRIPTOR_COMPONENT_SECTION_V2; + // Region section // All base and limit register are storing upper part of actual UINT32 base and limit // If limit is zero - region is not present typedef struct _FLASH_DESCRIPTOR_REGION_SECTION { - UINT16 ReservedZero; // Still unknown, zero in all descriptors I have seen - UINT16 FlashBlockEraseSize; // Size of block erased by single BLOCK ERASE command - UINT16 BiosBase; - UINT16 BiosLimit; - UINT16 MeBase; - UINT16 MeLimit; - UINT16 GbeBase; - UINT16 GbeLimit; - UINT16 PdrBase; - UINT16 PdrLimit; + UINT16 DescriptorBase; // Descriptor + UINT16 DescriptorLimit; // + UINT16 BiosBase; // BIOS + UINT16 BiosLimit; // + UINT16 MeBase; // ME + UINT16 MeLimit; // + UINT16 GbeBase; // GbE + UINT16 GbeLimit; // + UINT16 PdrBase; // PDR + UINT16 PdrLimit; // + UINT16 Region5Base; // Reserved region + UINT16 Region5Limit; // + UINT16 Region6Base; // Reserved region + UINT16 Region6Limit; // + UINT16 Region7Base; // Reserved region + UINT16 Region7Limit; // + UINT16 EcBase; // EC + UINT16 EcLimit; // } FLASH_DESCRIPTOR_REGION_SECTION; -// Flash block erase sizes -#define FLASH_BLOCK_ERASE_SIZE_4KB 0x0000 -#define FLASH_BLOCK_ERASE_SIZE_8KB 0x0001 -#define FLASH_BLOCK_ERASE_SIZE_64KB 0x000F - // Master section typedef struct _FLASH_DESCRIPTOR_MASTER_SECTION { UINT16 BiosId; - UINT8 BiosRead; - UINT8 BiosWrite; + UINT8 BiosRead; + UINT8 BiosWrite; UINT16 MeId; - UINT8 MeRead; - UINT8 MeWrite; + UINT8 MeRead; + UINT8 MeWrite; UINT16 GbeId; - UINT8 GbeRead; - UINT8 GbeWrite; + UINT8 GbeRead; + UINT8 GbeWrite; } FLASH_DESCRIPTOR_MASTER_SECTION; +// Master section v2 (Skylake+) +typedef struct _FLASH_DESCRIPTOR_MASTER_SECTION_V2 { + UINT32 : 8; + UINT32 BiosRead : 12; + UINT32 BiosWrite : 12; + UINT32 : 8; + UINT32 MeRead : 12; + UINT32 MeWrite : 12; + UINT32 : 8; + UINT32 GbeRead : 12; + UINT32 GbeWrite : 12; + UINT32 :32; + UINT32 : 8; + UINT32 EcRead : 12; + UINT32 EcWrite : 12; +} FLASH_DESCRIPTOR_MASTER_SECTION_V2; + // Region access bits in master section #define FLASH_DESCRIPTOR_REGION_ACCESS_DESC 0x01 #define FLASH_DESCRIPTOR_REGION_ACCESS_BIOS 0x02 #define FLASH_DESCRIPTOR_REGION_ACCESS_ME 0x04 #define FLASH_DESCRIPTOR_REGION_ACCESS_GBE 0x08 #define FLASH_DESCRIPTOR_REGION_ACCESS_PDR 0x10 - -//!TODO: Describe PCH and PROC straps sections, as well as ICC and DMI tables +#define FLASH_DESCRIPTOR_REGION_ACCESS_EC 0x20 // Base address of descriptor upper map #define FLASH_DESCRIPTOR_UPPER_MAP_BASE 0x0EFC @@ -155,7 +194,7 @@ typedef struct _VSCC_TABLE_ENTRY { // Base address and size of OEM section #define FLASH_DESCRIPTOR_OEM_SECTION_BASE 0x0F00 -#define FLASH_DESCRIPTOR_OEM_SECTION_SIZE 0xFF +#define FLASH_DESCRIPTOR_OEM_SECTION_SIZE 0x100 // Restore previous packing rules #pragma pack(pop) diff --git a/ffs.cpp b/ffs.cpp index fcdf03f..33410c3 100644 --- a/ffs.cpp +++ b/ffs.cpp @@ -155,7 +155,7 @@ UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header) if (!header) return 0; - bool extended = false; + const bool extended = false; /*if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) { extended = true; }*/ diff --git a/ffs.h b/ffs.h index 8b7da95..946a141 100644 --- a/ffs.h +++ b/ffs.h @@ -32,7 +32,7 @@ extern QString sectionTypeToQString(const UINT8 type); //***************************************************************************** // EFI Capsule //***************************************************************************** -// Capsule header +// Standard EFI Capsule header typedef struct _EFI_CAPSULE_HEADER { EFI_GUID CapsuleGuid; UINT32 HeaderSize; @@ -49,16 +49,32 @@ typedef struct _EFI_CAPSULE_HEADER { const QByteArray EFI_CAPSULE_GUID ("\xBD\x86\x66\x3B\x76\x0D\x30\x40\xB7\x0E\xB5\x51\x9E\x2F\xC5\xA0", 16); +// Intel capsule GUID +const QByteArray INTEL_CAPSULE_GUID +("\xB9\x82\x91\x53\xB5\xAB\x91\x43\xB6\x9A\xE3\xA9\x43\xF7\x2F\xCC", 16); + +// Toshiba EFI Capsule header +typedef struct _TOSHIBA_CAPSULE_HEADER { + EFI_GUID CapsuleGuid; + UINT32 HeaderSize; + UINT32 FullSize; + UINT32 Flags; +} TOSHIBA_CAPSULE_HEADER; + +// Toshiba capsule GUID +const QByteArray TOSHIBA_CAPSULE_GUID +("\x62\x70\xE0\x3B\x51\x1D\xD2\x45\x83\x2B\xF0\x93\x25\x7E\xD4\x61", 16); + // AMI Aptio extended capsule header typedef struct _APTIO_CAPSULE_HEADER { EFI_CAPSULE_HEADER CapsuleHeader; - UINT16 RomImageOffset; // offset in bytes from the beginning of the capsule header to the start of + UINT16 RomImageOffset; // offset in bytes from the beginning of the capsule header to the start of // the capsule volume //!TODO: Enable certificate and ROM layout reading - //UINT16 RomLayoutOffset; // offset to the table of the module descriptors in the capsule's volume + //UINT16 RomLayoutOffset; // offset to the table of the module descriptors in the capsule's volume // that are included in the signature calculation //FW_CERTIFICATE FWCert; - //ROM_AREA RomAreaMap[1]; + //ROM_AREA RomAreaMap[1]; } APTIO_CAPSULE_HEADER; // AMI Aptio signed extended capsule GUID @@ -93,7 +109,7 @@ typedef struct _EFI_FIRMWARE_VOLUME_HEADER { UINT16 ExtHeaderOffset; //Reserved in Revision 1 UINT8 Reserved; UINT8 Revision; - //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[1]; + //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[2]; } EFI_FIRMWARE_VOLUME_HEADER; // Standard file system GUIDs @@ -252,8 +268,8 @@ extern UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); // Integrity check typedef union { struct { - UINT8 Header; - UINT8 File; + UINT8 Header; + UINT8 File; } Checksum; UINT16 TailReference; // Revision 1 UINT16 Checksum16; // Revision 2 diff --git a/ffsengine.cpp b/ffsengine.cpp index 2e0a8fe..7909f10 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -75,6 +75,8 @@ QString errorMessage(UINT8 errorCode) case ERR_INVALID_SYMBOL: return QObject::tr("Invalid symbol"); case ERR_NOTHING_TO_PATCH: return QObject::tr("Nothing to patch"); case ERR_DEPEX_PARSE_FAILED: return QObject::tr("Dependency expression parsing failed"); + case ERR_TRUNCATED_IMAGE: return QObject::tr("Image is truncated"); + case ERR_BAD_RELOCATION_ENTRY: return QObject::tr("Bad image relocation entry"); default: return QObject::tr("Unknown error %1").arg(errorCode); } } @@ -146,14 +148,15 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) // Check buffer size to be more then or equal to size of EFI_CAPSULE_HEADER if ((UINT32)buffer.size() <= sizeof(EFI_CAPSULE_HEADER)) { - msg(tr("parseImageFile: image file is smaller then minimum size of %1h (%2) bytes").hexarg(sizeof(EFI_CAPSULE_HEADER)).arg(sizeof(EFI_CAPSULE_HEADER))); + msg(tr("parseImageFile: image file is smaller then minimum size of %1h (%2) bytes").hexarg(sizeof(EFI_CAPSULE_HEADER)).arg(sizeof(EFI_CAPSULE_HEADER))); return ERR_INVALID_PARAMETER; } // Check buffer for being normal EFI capsule header UINT32 capsuleHeaderSize = 0; QModelIndex index; - if (buffer.startsWith(EFI_CAPSULE_GUID)) { + if (buffer.startsWith(EFI_CAPSULE_GUID) + || buffer.startsWith(INTEL_CAPSULE_GUID)) { // Get info const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_CAPSULE_HEADER*)buffer.constData(); capsuleHeaderSize = capsuleHeader->HeaderSize; @@ -170,7 +173,24 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) // Add tree item index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); } + // Check buffer for being Toshiba capsule header + else if (buffer.startsWith(TOSHIBA_CAPSULE_GUID)) { + // Get info + const TOSHIBA_CAPSULE_HEADER* capsuleHeader = (const TOSHIBA_CAPSULE_HEADER*)buffer.constData(); + capsuleHeaderSize = capsuleHeader->HeaderSize; + QByteArray header = buffer.left(capsuleHeaderSize); + QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize); + QString name = tr("UEFI capsule"); + QString info = tr("Capsule GUID: %1\nFull size: %2h (%3)\nHeader size: %4h (%5)\nImage size: %6h (%7)\nFlags: %8h") + .arg(guidToQString(capsuleHeader->CapsuleGuid)) + .hexarg(buffer.size()).arg(buffer.size()) + .hexarg(capsuleHeader->HeaderSize).arg(capsuleHeader->HeaderSize) + .hexarg(capsuleHeader->FullSize - capsuleHeader->HeaderSize).arg(capsuleHeader->FullSize - capsuleHeader->HeaderSize) + .hexarg2(capsuleHeader->Flags, 8); + // Add tree item + index = model->addItem(Types::Capsule, Subtypes::ToshibaCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); + } // Check buffer for being extended Aptio signed capsule header else if (buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID) || buffer.startsWith(APTIO_UNSIGNED_CAPSULE_GUID)) { // Get info @@ -192,7 +212,7 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) // Add tree item index = model->addItem(Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); - + // Show message about possible Aptio signature break if (signedCapsule) { msg(tr("parseImageFile: Aptio capsule signature may become invalid after image modifications"), index); @@ -221,7 +241,7 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) .hexarg(flashImage.size()).arg(flashImage.size()); // Add tree item - index = model->addItem(Types::Image, Subtypes::UefiImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), flashImage, QByteArray(), index); + index = model->addItem(Types::Image, Subtypes::UefiImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), flashImage, index); return parseBios(flashImage, index); } @@ -238,26 +258,45 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in // Check for buffer size to be greater or equal to descriptor region size if (intelImage.size() < FLASH_DESCRIPTOR_SIZE) { - msg(tr("parseIntelImage: input file is smaller then minimum descriptor size of %1h (%2) bytes").hexarg(FLASH_DESCRIPTOR_SIZE).arg(FLASH_DESCRIPTOR_SIZE)); + msg(tr("parseIntelImage: input file is smaller than minimum descriptor size of 1000h (4096) bytes")); return ERR_INVALID_FLASH_DESCRIPTOR; } // Parse descriptor map const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor + sizeof(FLASH_DESCRIPTOR_HEADER)); const FLASH_DESCRIPTOR_UPPER_MAP* upperMap = (const FLASH_DESCRIPTOR_UPPER_MAP*)(descriptor + FLASH_DESCRIPTOR_UPPER_MAP_BASE); - const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8(descriptor, descriptorMap->RegionBase); - const FLASH_DESCRIPTOR_MASTER_SECTION* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION*)calculateAddress8(descriptor, descriptorMap->MasterBase); - // GbE region - QByteArray gbe; - UINT32 gbeBegin = 0; - UINT32 gbeEnd = 0; - if (regionSection->GbeLimit) { - gbeBegin = calculateRegionOffset(regionSection->GbeBase); - gbeEnd = calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); - gbe = intelImage.mid(gbeBegin, gbeEnd); - gbeEnd += gbeBegin; + // Check sanity of base values + if (descriptorMap->MasterBase > FLASH_DESCRIPTOR_MAX_BASE + || descriptorMap->MasterBase == descriptorMap->RegionBase + || descriptorMap->MasterBase == descriptorMap->ComponentBase) { + msg(tr("parseIntelImage: invalid descriptor master base %1h").hexarg2(descriptorMap->MasterBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; } + if (descriptorMap->RegionBase > FLASH_DESCRIPTOR_MAX_BASE + || descriptorMap->RegionBase == descriptorMap->ComponentBase) { + msg(tr("parseIntelImage: invalid descriptor region base %1h").hexarg2(descriptorMap->RegionBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + if (descriptorMap->ComponentBase > FLASH_DESCRIPTOR_MAX_BASE) { + msg(tr("parseIntelImage: invalid descriptor component base %1h").hexarg2(descriptorMap->ComponentBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + + const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8(descriptor, descriptorMap->RegionBase); + const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8(descriptor, descriptorMap->ComponentBase); + + // Check descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency + UINT8 descriptorVersion = 0; + if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Old descriptor + descriptorVersion = 1; + else if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_17MHZ) // Skylake+ descriptor + descriptorVersion = 2; + else { + msg(tr("parseIntelImage: unknown descriptor version with ReadClockFrequency %1h").hexarg(componentSection->FlashParameters.ReadClockFrequency)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + // ME region QByteArray me; UINT32 meBegin = 0; @@ -268,16 +307,6 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in me = intelImage.mid(meBegin, meEnd); meEnd += meBegin; } - // PDR region - QByteArray pdr; - UINT32 pdrBegin = 0; - UINT32 pdrEnd = 0; - if (regionSection->PdrLimit) { - pdrBegin = calculateRegionOffset(regionSection->PdrBase); - pdrEnd = calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); - pdr = intelImage.mid(pdrBegin, pdrEnd); - pdrEnd += pdrBegin; - } // BIOS region QByteArray bios; UINT32 biosBegin = 0; @@ -293,17 +322,57 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in return ERR_INVALID_FLASH_DESCRIPTOR; } biosBegin = meEnd; + bios = intelImage.mid(biosBegin, biosEnd); + // biosEnd will point to the end of the image file + // it may be wrong, but it's pretty hard to detect a padding after BIOS region + // with malformed descriptor + } + // Normal descriptor map + else { + bios = intelImage.mid(biosBegin, biosEnd); + // Calculate biosEnd + biosEnd += biosBegin; } - - bios = intelImage.mid(biosBegin, biosEnd); - biosEnd += biosBegin; } else { msg(tr("parseIntelImage: descriptor parsing failed, BIOS region not found in descriptor")); return ERR_INVALID_FLASH_DESCRIPTOR; } + // GbE region + QByteArray gbe; + UINT32 gbeBegin = 0; + UINT32 gbeEnd = 0; + if (regionSection->GbeLimit) { + gbeBegin = calculateRegionOffset(regionSection->GbeBase); + gbeEnd = calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); + gbe = intelImage.mid(gbeBegin, gbeEnd); + gbeEnd += gbeBegin; + } + // PDR region + QByteArray pdr; + UINT32 pdrBegin = 0; + UINT32 pdrEnd = 0; + if (regionSection->PdrLimit) { + pdrBegin = calculateRegionOffset(regionSection->PdrBase); + pdrEnd = calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); + pdr = intelImage.mid(pdrBegin, pdrEnd); + pdrEnd += pdrBegin; + } + // EC region + QByteArray ec; + UINT32 ecBegin = 0; + UINT32 ecEnd = 0; + if (descriptorVersion == 2) { + if (regionSection->EcLimit) { + pdrBegin = calculateRegionOffset(regionSection->EcBase); + pdrEnd = calculateRegionSize(regionSection->EcBase, regionSection->EcLimit); + pdr = intelImage.mid(ecBegin, ecEnd); + ecEnd += ecBegin; + } + } // Check for intersections between regions + // Descriptor if (hasIntersection(descriptorBegin, descriptorEnd, gbeBegin, gbeEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with GbE region")); return ERR_INVALID_FLASH_DESCRIPTOR; @@ -320,6 +389,11 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with PDR region")); return ERR_INVALID_FLASH_DESCRIPTOR; } + if (descriptorVersion == 2 && hasIntersection(descriptorBegin, descriptorEnd, ecBegin, ecEnd)) { + msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with EC region")); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + // GbE if (hasIntersection(gbeBegin, gbeEnd, meBegin, meEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with ME region")); return ERR_INVALID_FLASH_DESCRIPTOR; @@ -332,6 +406,11 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with PDR region")); return ERR_INVALID_FLASH_DESCRIPTOR; } + if (descriptorVersion == 2 && hasIntersection(gbeBegin, gbeEnd, ecBegin, ecEnd)) { + msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with EC region")); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + // ME if (hasIntersection(meBegin, meEnd, biosBegin, biosEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, ME region has intersection with BIOS region")); return ERR_INVALID_FLASH_DESCRIPTOR; @@ -340,26 +419,38 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in msg(tr("parseIntelImage: descriptor parsing failed, ME region has intersection with PDR region")); return ERR_INVALID_FLASH_DESCRIPTOR; } + if (descriptorVersion == 2 && hasIntersection(meBegin, meEnd, ecBegin, ecEnd)) { + msg(tr("parseIntelImage: descriptor parsing failed, ME region has intersection with EC region")); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + // BIOS if (hasIntersection(biosBegin, biosEnd, pdrBegin, pdrEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, BIOS region has intersection with PDR region")); return ERR_INVALID_FLASH_DESCRIPTOR; } + if (descriptorVersion == 2 && hasIntersection(biosBegin, biosEnd, ecBegin, ecEnd)) { + msg(tr("parseIntelImage: descriptor parsing failed, BIOS region has intersection with EC region")); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + // PDR + if (descriptorVersion == 2 && hasIntersection(pdrBegin, pdrEnd, ecBegin, ecEnd)) { + msg(tr("parseIntelImage: descriptor parsing failed, PDR region has intersection with EC region")); + return ERR_INVALID_FLASH_DESCRIPTOR; + } // Region map is consistent // Intel image QString name = tr("Intel image"); - QString info = tr("Full size: %1h (%2)\nFlash chips: %3\nRegions: %4\nMasters: %5\nPCH straps: %6\nPROC straps: %7\nICC table entries: %8") + QString info = tr("Full size: %1h (%2)\nFlash chips: %3\nMasters: %4\nPCH straps: %5\nCPU straps: %6\n") .hexarg(intelImage.size()).arg(intelImage.size()) - .arg(descriptorMap->NumberOfFlashChips + 1) // - .arg(descriptorMap->NumberOfRegions + 1) // Zero-based numbers in storage - .arg(descriptorMap->NumberOfMasters + 1) // + .arg(descriptorMap->NumberOfFlashChips + 1) + .arg(descriptorMap->NumberOfMasters + 1) .arg(descriptorMap->NumberOfPchStraps) - .arg(descriptorMap->NumberOfProcStraps) - .arg(descriptorMap->NumberOfIccTableEntries); + .arg(descriptorMap->NumberOfProcStraps); // Add Intel image tree item - index = model->addItem(Types::Image, Subtypes::IntelImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), intelImage, QByteArray(), parent); + index = model->addItem(Types::Image, Subtypes::IntelImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), intelImage, parent); // Descriptor // Get descriptor info @@ -385,33 +476,73 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in offsets.append(pdrBegin); info += tr("\nPDR region offset: %1h").hexarg(pdrBegin); } + if (descriptorVersion == 2 && regionSection->EcLimit) { + offsets.append(ecBegin); + info += tr("\nEC region offset: %1h").hexarg(ecBegin); + } // Region access settings - info += tr("\nRegion access settings:"); - info += tr("\nBIOS:%1%2h ME:%3%4h GbE:%5%6h") - .hexarg2(masterSection->BiosRead, 2) - .hexarg2(masterSection->BiosWrite, 2) - .hexarg2(masterSection->MeRead, 2) - .hexarg2(masterSection->MeWrite, 2) - .hexarg2(masterSection->GbeRead, 2) - .hexarg2(masterSection->GbeWrite, 2); + if (descriptorVersion == 1) { + const FLASH_DESCRIPTOR_MASTER_SECTION* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION*)calculateAddress8(descriptor, descriptorMap->MasterBase); + info += tr("\nRegion access settings:"); + info += tr("\nBIOS:%1%2h ME:%3%4h GbE:%5%6h") + .hexarg2(masterSection->BiosRead, 2) + .hexarg2(masterSection->BiosWrite, 2) + .hexarg2(masterSection->MeRead, 2) + .hexarg2(masterSection->MeWrite, 2) + .hexarg2(masterSection->GbeRead, 2) + .hexarg2(masterSection->GbeWrite, 2); - // BIOS access table - info += tr("\nBIOS access table:"); - info += tr("\n Read Write"); - info += tr("\nDesc %1 %2") - .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ") - .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No "); - info += tr("\nBIOS Yes Yes"); - info += tr("\nME %1 %2") - .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ") - .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No "); - info += tr("\nGbE %1 %2") - .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ") - .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No "); - info += tr("\nPDR %1 %2") - .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ") - .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No "); + // BIOS access table + info += tr("\nBIOS access table:"); + info += tr("\n Read Write"); + info += tr("\nDesc %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No "); + info += tr("\nBIOS Yes Yes"); + info += tr("\nME %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No "); + info += tr("\nGbE %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No "); + info += tr("\nPDR %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No "); + } + else if (descriptorVersion == 2) { + const FLASH_DESCRIPTOR_MASTER_SECTION_V2* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION_V2*)calculateAddress8(descriptor, descriptorMap->MasterBase); + info += tr("\nRegion access settings:"); + info += tr("\nBIOS: %1h %2h ME: %3h %4h\nGbE: %5h %6h EC: %7h %8h") + .hexarg2(masterSection->BiosRead, 3) + .hexarg2(masterSection->BiosWrite, 3) + .hexarg2(masterSection->MeRead, 3) + .hexarg2(masterSection->MeWrite, 3) + .hexarg2(masterSection->GbeRead, 3) + .hexarg2(masterSection->GbeWrite, 3) + .hexarg2(masterSection->EcRead, 3) + .hexarg2(masterSection->EcWrite, 3); + + // BIOS access table + info += tr("\nBIOS access table:"); + info += tr("\n Read Write"); + info += tr("\nDesc %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No "); + info += tr("\nBIOS Yes Yes"); + info += tr("\nME %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No "); + info += tr("\nGbE %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No "); + info += tr("\nPDR %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No "); + info += tr("\nEC %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_EC ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_EC ? "Yes " : "No "); + } // VSCC table const VSCC_TABLE_ENTRY* vsccTableEntry = (const VSCC_TABLE_ENTRY*)(descriptor + ((UINT16)upperMap->VsccTableBase << 4)); @@ -426,7 +557,7 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in } // Add descriptor tree item - model->addItem(Types::Region, Subtypes::DescriptorRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, QByteArray(), index); + model->addItem(Types::Region, Subtypes::DescriptorRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, index); // Sort regions in ascending order qSort(offsets); @@ -454,10 +585,45 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in QModelIndex pdrIndex; result = parsePdrRegion(pdr, pdrIndex, index); } + // Parse EC region + else if (descriptorVersion == 2 && offsets.at(i) == ecBegin) { + QModelIndex ecIndex; + result = parseEcRegion(ec, ecIndex, index); + } if (result) return result; } + // Add the data after the last region as padding + UINT32 IntelDataEnd = 0; + UINT32 LastRegionOffset = offsets.last(); + if (LastRegionOffset == gbeBegin) + IntelDataEnd = gbeEnd; + else if (LastRegionOffset == meBegin) + IntelDataEnd = meEnd; + else if (LastRegionOffset == biosBegin) + IntelDataEnd = biosEnd; + else if (LastRegionOffset == pdrBegin) + IntelDataEnd = pdrEnd; + else if (descriptorVersion == 2 && LastRegionOffset == ecBegin) + IntelDataEnd = ecEnd; + + if (IntelDataEnd > (UINT32)intelImage.size()) { // Image file is truncated + msg(tr("parseIntelImage: image size %1 (%2) is smaller than the end of last region %3 (%4), may be damaged") + .hexarg(intelImage.size()).arg(intelImage.size()) + .hexarg(IntelDataEnd).arg(IntelDataEnd), index); + return ERR_TRUNCATED_IMAGE; + } + else if (IntelDataEnd < (UINT32)intelImage.size()) { // Insert padding + QByteArray padding = intelImage.mid(IntelDataEnd); + // Get info + name = tr("Padding"); + info = tr("Full size: %1h (%2)") + .hexarg(padding.size()).arg(padding.size()); + // Add tree item + model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, index); + } + return ERR_SUCCESS; } @@ -483,7 +649,7 @@ UINT8 FfsEngine::parseGbeRegion(const QByteArray & gbe, QModelIndex & index, con .arg(version->minor); // Add tree item - index = model->addItem(Types::Region, Subtypes::GbeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), gbe, QByteArray(), parent, mode); + index = model->addItem(Types::Region, Subtypes::GbeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), gbe, parent, mode); return ERR_SUCCESS; } @@ -495,7 +661,7 @@ UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const return ERR_EMPTY_REGION; // Get info - QString name = tr("ME/TXE region"); + QString name = tr("ME region"); QString info = tr("Full size: %1h (%2)"). hexarg(me.size()).arg(me.size()); @@ -532,14 +698,14 @@ UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const } // Add tree item - index = model->addItem(Types::Region, Subtypes::MeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), me, QByteArray(), parent, mode); + index = model->addItem(Types::Region, Subtypes::MeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), me, parent, mode); // Show messages if (emptyRegion) { - msg(tr("parseRegion: ME/TXE region is empty"), index); + msg(tr("parseRegion: ME region is empty"), index); } else if (!versionFound) { - msg(tr("parseRegion: ME/TXE region version is unknown, it can be damaged"), index); + msg(tr("parseRegion: ME region version is unknown, it can be damaged"), index); } return ERR_SUCCESS; @@ -557,7 +723,7 @@ UINT8 FfsEngine::parsePdrRegion(const QByteArray & pdr, QModelIndex & index, con hexarg(pdr.size()).arg(pdr.size()); // Add tree item - index = model->addItem(Types::Region, Subtypes::PdrRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), pdr, QByteArray(), parent, mode); + index = model->addItem(Types::Region, Subtypes::PdrRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), pdr, parent, mode); // Parse PDR region as BIOS space UINT8 result = parseBios(pdr, index); @@ -567,6 +733,23 @@ UINT8 FfsEngine::parsePdrRegion(const QByteArray & pdr, QModelIndex & index, con return ERR_SUCCESS; } +UINT8 FfsEngine::parseEcRegion(const QByteArray & ec, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) +{ + // Check sanity + if (ec.isEmpty()) + return ERR_EMPTY_REGION; + + // Get info + QString name = tr("EC region"); + QString info = tr("Full size: %1h (%2)"). + hexarg(ec.size()).arg(ec.size()); + + // Add tree item + index = model->addItem(Types::Region, Subtypes::EcRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), ec, parent, mode); + + return ERR_SUCCESS; +} + UINT8 FfsEngine::parseBiosRegion(const QByteArray & bios, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { if (bios.isEmpty()) @@ -578,7 +761,7 @@ UINT8 FfsEngine::parseBiosRegion(const QByteArray & bios, QModelIndex & index, c hexarg(bios.size()).arg(bios.size()); // Add tree item - index = model->addItem(Types::Region, Subtypes::BiosRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), bios, QByteArray(), parent, mode); + index = model->addItem(Types::Region, Subtypes::BiosRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), bios, parent, mode); return parseBios(bios, index); } @@ -611,9 +794,9 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) name = tr("Padding"); info = tr("Full size: %1h (%2)") .hexarg(padding.size()).arg(padding.size()); - + // Add tree item - model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, QByteArray(), parent); + model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, parent); } // Search for and parse all volumes @@ -638,24 +821,26 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) info = tr("Full size: %1h (%2)") .hexarg(padding.size()).arg(padding.size()); // Add tree item - model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, QByteArray(), parent); + model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, parent); } // Get volume size result = getVolumeSize(bios, volumeOffset, volumeSize, bmVolumeSize); - if (result) + if (result) { + msg(tr("parseBios: getVolumeSize failed with error \"%1\"").arg(errorMessage(result)), parent); return result; + } - // Check reported size - if (volumeSize != bmVolumeSize) - msgSizeMismach = true; - - //Check that volume is fully present in input - if (volumeOffset + volumeSize > (UINT32)bios.size()) { + // Check that volume is fully present in input + if (volumeSize > (UINT32)bios.size() || volumeOffset + volumeSize > (UINT32)bios.size()) { msg(tr("parseBios: one of volumes inside overlaps the end of data"), parent); return ERR_INVALID_VOLUME; } + // Check reported size against a size calculated using block map + if (volumeSize != bmVolumeSize) + msgSizeMismach = true; + // Check volume revision and alignment const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + volumeOffset); UINT32 alignment; @@ -712,7 +897,7 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) info = tr("Full size: %1h (%2)") .hexarg(padding.size()).arg(padding.size()); // Add tree item - model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, QByteArray(), parent); + model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, parent); } break; } @@ -734,6 +919,10 @@ UINT8 FfsEngine::findNextVolume(const QByteArray & bios, UINT32 volumeOffset, UI UINT8 FfsEngine::getVolumeSize(const QByteArray & bios, UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize) { + // Check that there is space for the volume header and at least two block map entries. + if ((UINT32)bios.size() < volumeOffset + sizeof(EFI_FIRMWARE_VOLUME_HEADER) + 2 * sizeof(EFI_FV_BLOCK_MAP_ENTRY)) + return ERR_INVALID_VOLUME; + // Populate volume header const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + volumeOffset); @@ -754,14 +943,37 @@ UINT8 FfsEngine::getVolumeSize(const QByteArray & bios, UINT32 volumeOffset, UIN volumeSize = volumeHeader->FvLength; bmVolumeSize = calcVolumeSize; + + if (volumeSize == 0) + return ERR_INVALID_VOLUME; + return ERR_SUCCESS; } UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { + // Check that there is space for the volume header + if ((UINT32)volume.size() < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) { + msg(tr("parseVolume: input volume size %1h (%2) is smaller than volume header size 40h (64)").hexarg(volume.size()).arg(volume.size())); + return ERR_INVALID_VOLUME; + } + // Populate volume header const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(volume.constData()); + // Check sanity of HeaderLength value + if (ALIGN8(volumeHeader->HeaderLength) > volume.size()) { + msg(tr("parseVolume: volume header overlaps the end of data")); + return ERR_INVALID_VOLUME; + } + + // Check sanity of ExtHeaderOffset value + if (volumeHeader->ExtHeaderOffset > 0 + && (UINT32)volume.size() < ALIGN8(volumeHeader->ExtHeaderOffset + sizeof(EFI_FIRMWARE_VOLUME_EXT_HEADER))) { + msg(tr("parseVolume: extended volume header overlaps the end of data")); + return ERR_INVALID_VOLUME; + } + // Calculate volume header size UINT32 headerSize; if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { @@ -776,20 +988,12 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Check for volume structure to be known bool volumeIsUnknown = true; - /*UINT8 volumeFfsVersion = 0;*/ - + // Check for FFS v2 volume if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { volumeIsUnknown = false; - /*volumeFfsVersion = 2;*/ } - // Check for FFS v3 volume - /*if (FFSv3Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { - volumeIsUnknown = false; - volumeFfsVersion = 3; - }*/ - // Check attributes // Determine value of empty byte char empty = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; @@ -804,13 +1008,20 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Check for Apple CRC32 in ZeroVector bool volumeHasZVCRC = false; + bool volumeHasZVFSO = false; UINT32 crc32FromZeroVector = *(UINT32*)(volume.constData() + 8); + UINT32 freeSpaceOffsetFromZeroVector = *(UINT32*)(volume.constData() + 12); if (crc32FromZeroVector != 0) { // Calculate CRC32 of the volume body UINT32 crc = crc32(0, (const UINT8*)(volume.constData() + volumeHeader->HeaderLength), volumeSize - volumeHeader->HeaderLength); if (crc == crc32FromZeroVector) { volumeHasZVCRC = true; } + + // Check for free space size in zero vector + if (freeSpaceOffsetFromZeroVector != 0) { + volumeHasZVFSO = true; + } } // Check header checksum by recalculating it @@ -833,11 +1044,6 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co .arg(volumeHeader->Revision) .hexarg2(volumeHeader->Attributes, 8) .arg(empty ? "1" : "0"); - - // Apple CRC32 volume - if (volumeHasZVCRC) { - info += tr("\nCRC32 in ZeroVector: valid"); - } // Extended header present if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { @@ -847,21 +1053,17 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co .arg(guidToQString(extendedHeader->FvName)); } - // Construct parsing data structure - QByteArray parsingData(sizeof(PARSING_DATA), 0); - PARSING_DATA* pdata = (PARSING_DATA*)parsingData.data(); - pdata->Type = VolumeParsingData; - pdata->Data.Volume.HasZeroVectorCRC = volumeHasZVCRC; - // Add text QString text; if (volumeHasZVCRC) - text += tr("ZeroVectorCRC "); + text += tr("AppleCRC32 "); + if (volumeHasZVFSO) + text += tr("AppleFSO "); // Add tree item QByteArray header = volume.left(headerSize); QByteArray body = volume.mid(headerSize, volumeSize - headerSize); - index = model->addItem(Types::Volume, volumeIsUnknown ? Subtypes::UnknownVolume : Subtypes::Ffs2Volume, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parsingData, parent, mode); + index = model->addItem(Types::Volume, volumeIsUnknown ? Subtypes::UnknownVolume : Subtypes::Ffs2Volume, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parent, mode); // Show messages if (volumeIsUnknown) { @@ -872,7 +1074,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co if (msgInvalidChecksum) { msg(tr("parseVolume: volume header checksum is invalid"), index); } - + // Search for and parse all files UINT32 fileOffset = headerSize; UINT32 fileSize; @@ -882,6 +1084,22 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co bool msgUnalignedFile = false; bool msgDuplicateGuid = false; + // Check if it's possibly the latest file in the volume + if (volumeSize - fileOffset < sizeof(EFI_FFS_FILE_HEADER)) { + // No files are possible after this point + // All the rest is either free space or non-UEFI data + QByteArray rest = volume.right(volumeSize - fileOffset); + if (rest.count(empty) == rest.size()) { // It's a free space + model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index); + } + else { //It's non-UEFI data + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index); + msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex); + } + // Exit from loop + break; + } + result = getFileSize(volume, fileOffset, fileSize); if (result) return result; @@ -915,18 +1133,18 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co i = ALIGN8(i) - 8; // Add all bytes before as free space... - if (i > 0) { + if (i > 0) { QByteArray free = freeSpace.left(i); - model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, QByteArray(), index, mode); + model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, index); } // ... and all bytes after as a padding QByteArray padding = freeSpace.mid(i); - QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, QByteArray(), index, mode); + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, index); msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex); } else { // Add free space element - model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(freeSpace.size()).arg(freeSpace.size()), QByteArray(), freeSpace, QByteArray(), index, mode); + model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(freeSpace.size()).arg(freeSpace.size()), QByteArray(), freeSpace, index); } break; // Exit from loop } @@ -951,14 +1169,6 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) msg(tr("parseVolume: FFS file parsing failed with error \"%1\"").arg(errorMessage(result)), index); - - // Construct parsing data structure - QByteArray parsingData(sizeof(PARSING_DATA), 0); - PARSING_DATA* pdata = (PARSING_DATA*)parsingData.data(); - pdata->Type = FileParsingData; - pdata->Data.File.Offset = fileOffset; - model->setParsingData(fileIndex, parsingData); - // Show messages if (msgUnalignedFile) msg(tr("parseVolume: unaligned file %1").arg(guidToQString(fileHeader->Name)), fileIndex); @@ -975,6 +1185,8 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co UINT8 FfsEngine::getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize) { + if ((UINT32)volume.size() < fileOffset + sizeof(EFI_FFS_FILE_HEADER)) + return ERR_INVALID_VOLUME; const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)(volume.constData() + fileOffset); fileSize = uint24ToUint32(fileHeader->Size); return ERR_SUCCESS; @@ -1095,20 +1307,20 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U QString info; if (fileHeader->Type != EFI_FV_FILETYPE_PAD) name = guidToQString(fileHeader->Name); - else + else name = parseAsNonEmptyPadFile ? tr("Non-empty pad-file") : tr("Pad-file"); info = tr("File GUID: %1\nType: %2h\nAttributes: %3h\nFull size: %4h (%5)\nHeader size: %6h (%7)\nBody size: %8h (%9)\nState: %10h") .arg(guidToQString(fileHeader->Name)) .hexarg2(fileHeader->Type, 2) .hexarg2(fileHeader->Attributes, 2) - .hexarg(header.size() + body.size()).arg(header.size() + body.size()) + .hexarg(header.size() + body.size() + tail.size()).arg(header.size() + body.size() + tail.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()) .hexarg2(fileHeader->State, 2); // Add tree item - index = model->addItem(Types::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgInvalidHeaderChecksum) @@ -1119,7 +1331,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U msg(tr("parseFile: invalid tail value"), index); if (msgInvalidType) msg(tr("parseFile: unknown file type %1h").arg(fileHeader->Type, 2), index); - + // No parsing needed if (!parseCurrentFile) return ERR_SUCCESS; @@ -1137,15 +1349,15 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U // Add all bytes before as free space... if (i > 0) { QByteArray free = body.left(i); - model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, QByteArray(), index, mode); + model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, index, mode); } // ... and all bytes after as a padding QByteArray padding = body.mid(i); - QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, QByteArray(), index, mode); - + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, index, mode); + // Show message msg(tr("parseFile: non-empty pad-file contents will be destroyed after volume modifications"), dataIndex); - + return ERR_SUCCESS; } @@ -1168,6 +1380,8 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U UINT8 FfsEngine::getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize) { + if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER)) + return ERR_INVALID_FILE; const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(file.constData() + sectionOffset); sectionSize = uint24ToUint32(sectionHeader->Size); return ERR_SUCCESS; @@ -1186,6 +1400,9 @@ UINT8 FfsEngine::parseSections(const QByteArray & body, const QModelIndex & pare result = getSectionSize(body, sectionOffset, sectionSize); if (result) return result; + // Exit from loop if no sections left + if (sectionSize == 0) + break; // Parse section QModelIndex sectionIndex; @@ -1227,7 +1444,7 @@ UINT8 FfsEngine::parseDepexSection(const QByteArray & body, QString & parsed) const EFI_GUID * guid; const UINT8* current = (const UINT8*)body.constData(); - + // Special cases of first opcode switch (*current) { case EFI_DEP_BEFORE: @@ -1257,7 +1474,7 @@ UINT8 FfsEngine::parseDepexSection(const QByteArray & body, QString & parsed) break; } - // Parse the rest of depex + // Parse the rest of depex while (current - (const UINT8*)body.constData() < body.size()) { switch (*current) { case EFI_DEP_BEFORE: @@ -1303,9 +1520,9 @@ UINT8 FfsEngine::parseDepexSection(const QByteArray & body, QString & parsed) return ERR_DEPEX_PARSE_FAILED; } break; - default: - return ERR_DEPEX_PARSE_FAILED; - break; + default: + return ERR_DEPEX_PARSE_FAILED; + break; } } @@ -1348,7 +1565,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(compressedSectionHeader->UncompressedLength).arg(compressedSectionHeader->UncompressedLength); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, parent, mode); // Show message if (!parseCurrentSection) @@ -1367,6 +1584,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c bool msgInvalidCrc = false; bool msgUnknownAuth = false; bool msgSigned = false; + bool msgInvalidSignatureLength = false; bool msgUnknownSignature = false; bool msgUnknownUefiGuidSignature = false; @@ -1395,11 +1613,11 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c // Tiano compressed section if (QByteArray((const char*)&guidDefinedSectionHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_TIANO) { algorithm = COMPRESSION_ALGORITHM_UNKNOWN; - + result = decompress(body, EFI_STANDARD_COMPRESSION, processed, &algorithm); if (result) parseCurrentSection = false; - + if (algorithm == COMPRESSION_ALGORITHM_TIANO) { info += tr("\nCompression type: Tiano"); info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); @@ -1408,17 +1626,17 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c info += tr("\nCompression type: EFI 1.1"); info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); } - else + else info += tr("\nCompression type: unknown"); } // LZMA compressed section else if (QByteArray((const char*)&guidDefinedSectionHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_LZMA) { algorithm = COMPRESSION_ALGORITHM_UNKNOWN; - + result = decompress(body, EFI_CUSTOMIZED_COMPRESSION, processed, &algorithm); if (result) parseCurrentSection = false; - + if (algorithm == COMPRESSION_ALGORITHM_LZMA) { info += tr("\nCompression type: LZMA"); info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); @@ -1430,7 +1648,12 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c else if (QByteArray((const char*)&guidDefinedSectionHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { msgSigned = true; const WIN_CERTIFICATE* certificateHeader = (const WIN_CERTIFICATE*)body.constData(); - if (certificateHeader->CertificateType == WIN_CERT_TYPE_EFI_GUID) { + if ((UINT32)body.size() < sizeof(WIN_CERTIFICATE)) { + info += tr("\nSignature type: invalid, wrong length"); + msgInvalidSignatureLength = true; + parseCurrentSection = false; + } + else if (certificateHeader->CertificateType == WIN_CERT_TYPE_EFI_GUID) { info += tr("\nSignature type: UEFI"); const WIN_CERTIFICATE_UEFI_GUID* guidCertificateHeader = (const WIN_CERTIFICATE_UEFI_GUID*)certificateHeader; if (QByteArray((const char*)&guidCertificateHeader->CertType, sizeof(EFI_GUID)) == EFI_CERT_TYPE_RSA2048_SHA256_GUID) { @@ -1455,10 +1678,17 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c msgUnknownSignature = true; } - // Add additional to the header - header.append(body.left(certificateHeader->Length)); - // Get new body - processed = body = body.mid(certificateHeader->Length); + if ((UINT32)body.size() < certificateHeader->Length) { + info += tr("\nSignature type: invalid, wrong length"); + msgInvalidSignatureLength = true; + parseCurrentSection = false; + } + else { + // Add additional data to the header + header.append(body.left(certificateHeader->Length)); + // Get new body + processed = body = body.mid(certificateHeader->Length); + } } // Unknown GUIDed section else { @@ -1488,7 +1718,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, parent, mode); // Show messages if (msgUnknownGuid) @@ -1501,6 +1731,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c msg(tr("parseSection: signature may become invalid after any modification"), index); if (msgUnknownUefiGuidSignature) msg(tr("parseSection: GUID defined section with unknown signature subtype"), index); + if (msgInvalidSignatureLength) + msg(tr("parseSection: GUID defined section with invalid signature length"), index); if (msgUnknownSignature) msg(tr("parseSection: GUID defined section with unknown signature type"), index); @@ -1527,7 +1759,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(body.size()).arg(body.size()); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Parse section body result = parseSections(body, index); @@ -1560,7 +1792,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c info += tr("\nParsed expression:%1").arg(str); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgDepexParseFailed) @@ -1586,7 +1818,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c if (teHeader->Signature != EFI_IMAGE_TE_SIGNATURE) { info += tr("\nSignature: %1h, invalid").hexarg2(teHeader->Signature, 4); msgInvalidSignature = true; - } + } else { info += tr("\nSignature: %1h\nMachine type: %2\nNumber of sections: %3\nSubsystem: %4h\nStrippedSize: %5h (%6)\nBaseOfCode: %7h\nRelativeEntryPoint: %8h\nImageBase: %9h\nEntryPoint: %10h") .hexarg2(teHeader->Signature, 4) @@ -1600,7 +1832,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(teHeader->ImageBase + teHeader->AddressOfEntryPoint - teFixup); } // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgInvalidSignature) { @@ -1632,6 +1864,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c // Get PE info bool msgInvalidDosSignature = false; + bool msgInvalidDosHeader = false; bool msgInvalidPeSignature = false; bool msgUnknownOptionalHeaderSignature = false; @@ -1642,7 +1875,11 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } else { const EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(body.constData() + dosHeader->e_lfanew); - if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) { + if ((UINT32)body.size() < dosHeader->e_lfanew + sizeof(EFI_IMAGE_PE_HEADER)) { + info += tr("\nDOS lfanew: %1h, invalid").hexarg2(dosHeader->e_lfanew, 8); + msgInvalidDosHeader = true; + } + else if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) { info += tr("\nPE signature: %1h, invalid").hexarg2(peHeader->Signature, 8); msgInvalidPeSignature = true; } @@ -1683,12 +1920,15 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgInvalidDosSignature) { msg("parseSection: PE32 image with invalid DOS signature", index); } + if (msgInvalidDosHeader) { + msg("parseSection: PE32 image with invalid DOS header", index); + } if (msgInvalidPeSignature) { msg("parseSection: PE32 image with invalid PE signature", index); } @@ -1719,7 +1959,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(body.size()).arg(body.size()); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); } break; case EFI_SECTION_FREEFORM_SUBTYPE_GUID: { @@ -1736,7 +1976,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .arg(guidToQString(fsgHeader->SubTypeGuid)); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Rename section model->setName(index, guidToQString(fsgHeader->SubTypeGuid)); @@ -1758,7 +1998,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .arg(QString::fromUtf16((const ushort*)body.constData())); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); } break; case EFI_SECTION_USER_INTERFACE: { @@ -1775,7 +2015,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .arg(text); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Rename parent file model->setText(model->findParentOfType(parent, Types::File), text); @@ -1793,7 +2033,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(body.size()).arg(body.size()); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Parse section body as BIOS space result = parseBios(body, index); @@ -1846,10 +2086,10 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Parse section body as BIOS space - if (!parsed) { + if (!parsed) { result = parseBios(body, index); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) { msg(tr("parseSection: parsing raw section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); @@ -1874,7 +2114,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(postcodeHeader->Postcode); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); } break; default: @@ -1888,7 +2128,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(body.size()).arg(body.size()); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); msg(tr("parseSection: section with unknown type %1h").hexarg2(sectionHeader->Type, 2), index); } return ERR_SUCCESS; @@ -1936,12 +2176,24 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte // Set action model->setAction(fileIndex, action); } + else if (type == Types::Padding) { + // Get info + QString name = tr("Padding"); + QString info = tr("Full size: %1h (%2)") + .hexarg(body.size()).arg(body.size()); + + // Add tree item + QModelIndex fileIndex = model->addItem(Types::Padding, getPaddingType(body), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, index, mode); + + // Set action + model->setAction(fileIndex, action); + } else if (type == Types::Volume) { QByteArray volume; - if (header.isEmpty()) // Whole volume + if (header.isEmpty()) // Whole volume volume.append(body); else { // Body only - volume.append(model->header(index)).append(body); + volume.append(header).append(body); INT32 sizeDiff = model->body(index).size() - body.size(); if (sizeDiff > 0) { const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)model->header(index).constData(); @@ -1952,7 +2204,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte result = parseVolume(volume, fileIndex, index, mode); if (result) return result; - + // Set action model->setAction(fileIndex, action); } @@ -1968,12 +2220,22 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte return ERR_INVALID_FILE; QByteArray newHeader = header; + QByteArray newBody = body; EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newHeader.data(); - - // Correct file size + // Check if the file has a tail UINT8 tailSize = fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT ? sizeof(UINT16) : 0; - uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + body.size() + tailSize, fileHeader->Size); + if (tailSize) { + // Remove the tail, it will then be added back for revision 1 volumes + newBody = newBody.left(newBody.size() - tailSize); + // Remove the attribute for rev2+ volumes + if (revision != 1) { + fileHeader->Attributes &= ~(FFS_ATTRIB_TAIL_PRESENT); + } + } + + // Correct file size + uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + newBody.size() + tailSize, fileHeader->Size); // Recalculate header checksum fileHeader->IntegrityCheck.Checksum.Header = 0; @@ -1982,17 +2244,17 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte // Recalculate data checksum, if needed if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) - fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((const UINT8*)body.constData(), body.size()); + fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((const UINT8*)newBody.constData(), newBody.size()); else if (revision == 1) fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; else fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; - // Append body - created.append(body); + // Append new body + created.append(newBody); // Append tail, if needed - if (tailSize) { + if (revision ==1 && tailSize) { UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; created.append(ht).append(ft); @@ -2194,6 +2456,12 @@ UINT8 FfsEngine::replace(const QModelIndex & index, const QByteArray & object, c else return ERR_NOT_IMPLEMENTED; } + else if (model->type(index) == Types::Padding) { + if (mode == REPLACE_MODE_AS_IS) + result = create(index, Types::Padding, QByteArray(), object, CREATE_MODE_AFTER, Actions::Replace); + else + return ERR_NOT_IMPLEMENTED; + } else if (model->type(index) == Types::Volume) { if (mode == REPLACE_MODE_AS_IS) { result = create(index, Types::Volume, QByteArray(), object, CREATE_MODE_AFTER, Actions::Replace); @@ -2494,7 +2762,7 @@ UINT8 FfsEngine::compress(const QByteArray & data, const UINT8 algorithm, QByteA return ERR_STANDARD_COMPRESSION_FAILED; } compressedData = QByteArray((const char*)compressed, compressedSize); - + // Check that compressed data can be decompressed normally QByteArray decompressed; if (decompress(compressedData, EFI_STANDARD_COMPRESSION, decompressed, NULL) == ERR_SUCCESS @@ -2514,7 +2782,7 @@ UINT8 FfsEngine::compress(const QByteArray & data, const UINT8 algorithm, QByteA return ERR_STANDARD_COMPRESSION_FAILED; } compressedData = QByteArray((const char*)compressed, compressedSize); - + // New functions will be trusted here, because another check will reduce performance delete[] compressed; return ERR_SUCCESS; @@ -2653,32 +2921,96 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec return result; reconstructed.append(descriptor); + // Check descriptor size + if ((UINT32)descriptor.size() < FLASH_DESCRIPTOR_SIZE) { + msg(tr("reconstructIntelImage: descriptor is smaller than minimum size of 1000h (4096) bytes")); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor.constData() + sizeof(FLASH_DESCRIPTOR_HEADER)); + // Check sanity of base values + if (descriptorMap->MasterBase > FLASH_DESCRIPTOR_MAX_BASE + || descriptorMap->MasterBase == descriptorMap->RegionBase + || descriptorMap->MasterBase == descriptorMap->ComponentBase) { + msg(tr("reconstructIntelImage: invalid descriptor master base %1h").hexarg2(descriptorMap->MasterBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + if (descriptorMap->RegionBase > FLASH_DESCRIPTOR_MAX_BASE + || descriptorMap->RegionBase == descriptorMap->ComponentBase) { + msg(tr("reconstructIntelImage: invalid descriptor region base %1h").hexarg2(descriptorMap->RegionBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + if (descriptorMap->ComponentBase > FLASH_DESCRIPTOR_MAX_BASE) { + msg(tr("reconstructIntelImage: invalid descriptor component base %1h").hexarg2(descriptorMap->ComponentBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + + const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8((const UINT8*)descriptor.constData(), descriptorMap->RegionBase); QByteArray gbe; UINT32 gbeBegin = calculateRegionOffset(regionSection->GbeBase); UINT32 gbeEnd = gbeBegin + calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); + QByteArray me; UINT32 meBegin = calculateRegionOffset(regionSection->MeBase); UINT32 meEnd = meBegin + calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); + QByteArray bios; UINT32 biosBegin = calculateRegionOffset(regionSection->BiosBase); - UINT32 biosEnd = biosBegin + calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); + UINT32 biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); + // Gigabyte descriptor map + if (biosEnd - biosBegin == (UINT32)(model->header(index).size() + model->body(index).size())) { + biosBegin = meEnd; + biosEnd = model->header(index).size() + model->body(index).size(); + } + // Normal descriptor map + else + biosEnd += biosBegin; + QByteArray pdr; UINT32 pdrBegin = calculateRegionOffset(regionSection->PdrBase); UINT32 pdrEnd = pdrBegin + calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); + QByteArray ec; + UINT32 ecBegin = 0; + UINT32 ecEnd = 0; + + const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8((const UINT8*)descriptor.constData(), descriptorMap->ComponentBase); + // Check descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency + UINT8 descriptorVersion = 0; + if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) { // Old descriptor + descriptorVersion = 1; + } + else if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_17MHZ) { // Skylake+ descriptor + descriptorVersion = 2; + ecBegin = calculateRegionOffset(regionSection->EcBase); + ecEnd = ecBegin + calculateRegionSize(regionSection->EcBase, regionSection->EcLimit); + } + else { + msg(tr("reconstructIntelImage: unknown descriptor version with ReadClockFrequency %1h").hexarg(componentSection->FlashParameters.ReadClockFrequency)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + + UINT32 offset = descriptor.size(); // Reconstruct other regions char empty = '\xFF'; for (int i = 1; i < model->rowCount(index); i++) { QByteArray region; + + // Padding after the end of all Intel regions + if (model->type(index.child(i, 0)) == Types::Padding) { + region = model->body(index.child(i, 0)); + reconstructed.append(region); + offset += region.size(); + continue; + } + result = reconstructRegion(index.child(i, 0), region); if (result) return result; - UINT8 type = model->subtype(index.child(i, 0)); - switch (type) + switch (model->subtype(index.child(i, 0))) { case Subtypes::GbeRegion: gbe = region; @@ -2708,6 +3040,17 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec reconstructed.append(pdr); offset = pdrEnd; break; + case Subtypes::EcRegion: + if (descriptorVersion == 1) { + msg(tr("reconstructIntelImage: incompatible region type found"), index); + return ERR_INVALID_REGION; + } + ec = region; + if (ecBegin > offset) + reconstructed.append(QByteArray(ecBegin - offset, empty)); + reconstructed.append(ec); + offset = ecEnd; + break; default: msg(tr("reconstructIntelImage: unknown region type found"), index); return ERR_INVALID_REGION; @@ -2740,7 +3083,7 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec return ERR_NOT_IMPLEMENTED; } -UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconstructed) +UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconstructed, bool includeHeader) { if (!index.isValid()) return ERR_SUCCESS; @@ -2777,20 +3120,64 @@ UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconst if (reconstructed.size() > model->body(index).size()) { msg(tr("reconstructRegion: reconstructed region size %1h (%2) is bigger then original %3h (%4)") .hexarg(reconstructed.size()).arg(reconstructed.size()) - .hexarg(model->body(index).size()).arg(reconstructed.size()), + .hexarg(model->body(index).size()).arg(model->body(index).size()), index); return ERR_INVALID_PARAMETER; } else if (reconstructed.size() < model->body(index).size()) { msg(tr("reconstructRegion: reconstructed region size %1h (%2) is smaller then original %3h (%4)") .hexarg(reconstructed.size()).arg(reconstructed.size()) - .hexarg(model->body(index).size()).arg(reconstructed.size()), + .hexarg(model->body(index).size()).arg(model->body(index).size()), + index); + return ERR_INVALID_PARAMETER; + } + + // Reconstruction successful + if (includeHeader) + reconstructed = model->header(index).append(reconstructed); + return ERR_SUCCESS; + } + + // All other actions are not supported + return ERR_NOT_IMPLEMENTED; +} + +UINT8 FfsEngine::reconstructPadding(const QModelIndex& index, QByteArray& reconstructed) +{ + if (!index.isValid()) + return ERR_SUCCESS; + + // No action + if (model->action(index) == Actions::NoAction) { + reconstructed = model->body(index); + return ERR_SUCCESS; + } + else if (model->action(index) == Actions::Remove) { + reconstructed.clear(); + return ERR_SUCCESS; + } + else if (model->action(index) == Actions::Rebuild || + model->action(index) == Actions::Replace) { + // Use stored item body + reconstructed = model->body(index); + + // Check size of reconstructed region, it must be same + if (reconstructed.size() > model->body(index).size()) { + msg(tr("reconstructPadding: reconstructed padding size %1h (%2) is bigger then original %3h (%4)") + .hexarg(reconstructed.size()).arg(reconstructed.size()) + .hexarg(model->body(index).size()).arg(model->body(index).size()), + index); + return ERR_INVALID_PARAMETER; + } + else if (reconstructed.size() < model->body(index).size()) { + msg(tr("reconstructPadding: reconstructed padding size %1h (%2) is smaller then original %3h (%4)") + .hexarg(reconstructed.size()).arg(reconstructed.size()) + .hexarg(model->body(index).size()).arg(model->body(index).size()), index); return ERR_INVALID_PARAMETER; } // Reconstruction successful - reconstructed = model->header(index).append(reconstructed); return ERR_SUCCESS; } @@ -2814,20 +3201,27 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon reconstructed.clear(); return ERR_SUCCESS; } - else if (model->action(index) == Actions::Replace || + else if (model->action(index) == Actions::Replace || model->action(index) == Actions::Rebuild) { QByteArray header = model->header(index); QByteArray body = model->body(index); EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); + // Check sanity of HeaderLength + if (volumeHeader->HeaderLength > header.size()) { + msg(tr("reconstructVolume: invalid volume header length, reconstruction is not possible"), index); + return ERR_INVALID_VOLUME; + } + // Recalculate volume header checksum volumeHeader->Checksum = 0; volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); // Get volume size UINT32 volumeSize = header.size() + body.size(); - + // Reconstruct volume body + UINT32 freeSpaceOffset = 0; if (model->rowCount(index)) { reconstructed.clear(); UINT8 polarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE; @@ -2882,8 +3276,8 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon // Calculate relative base address UINT32 relbase = fileOffset + sectionOffset + model->header(image).size(); // Calculate offset of image relative to file base - UINT32 imagebase; - result = getBase(model->body(image), imagebase); + UINT32 imagebase = 0; + result = getBase(model->body(image), imagebase); // imagebase passed by reference if (!result) { // Calculate volume base volumeBase = imagebase - relbase; @@ -2940,7 +3334,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon // Pad file if (fileHeader->Type == EFI_FV_FILETYPE_PAD) { padFileGuid = file.left(sizeof(EFI_GUID)); - + // Parse non-empty pad file if (model->rowCount(index.child(i, 0))) { //TODO: handle it @@ -2995,7 +3389,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon // Get non-UEFI data and it's offset nonUefiData = model->body(index.child(i + 1, 0)); nonUefiDataOffset = body.size() - nonUefiData.size(); - break; + break; } } } @@ -3006,6 +3400,17 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon return ERR_INVALID_VOLUME; } + // Check for free space offset in ZeroVector + if (model->text(index).contains("AppleFSO ")) { + // Align current offset to 8 byte boundary + UINT32 alignment = offset % 8; + freeSpaceOffset = model->header(index).size() + offset; + if (alignment) { + alignment = 8 - alignment; + freeSpaceOffset += alignment; + } + } + // Insert VTF or non-UEFI data to it's correct place if (!vtf.isEmpty()) { // VTF found // Determine correct VTF offset @@ -3107,22 +3512,36 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon reconstructed = header.append(reconstructed); // Recalculate CRC32 in ZeroVector, if needed - const PARSING_DATA* pdata = (const PARSING_DATA*)model->parsingData(index).constData(); - if (pdata->Type == VolumeParsingData && pdata->Data.Volume.HasZeroVectorCRC) { + if (model->text(index).contains("AppleCRC32 ")) { // Get current CRC32 value from volume header - const UINT32 current = *(const UINT32*)(reconstructed.constData() + 8); + const UINT32 currentCrc = *(const UINT32*)(reconstructed.constData() + 8); // Calculate new value UINT32 crc = crc32(0, (const UINT8*)reconstructed.constData() + volumeHeader->HeaderLength, reconstructed.size() - volumeHeader->HeaderLength); // Update the value - if (current != crc) { + if (currentCrc != crc) { *(UINT32*)(reconstructed.data() + 8) = crc; - // Recalculate header checksum again + // Recalculate header checksum volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)reconstructed.data(); volumeHeader->Checksum = 0; volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); } } + // Store new free space offset, if needed + if (model->text(index).contains("AppleFSO ")) { + // Get current CRC32 value from volume header + const UINT32 currentFso = *(const UINT32*)(reconstructed.constData() + 12); + // Update the value + if (freeSpaceOffset != 0 && currentFso != freeSpaceOffset) { + *(UINT32*)(reconstructed.data() + 12) = freeSpaceOffset; + + // Recalculate header checksum + volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)reconstructed.data(); + volumeHeader->Checksum = 0; + volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); + } + } + return ERR_SUCCESS; } @@ -3207,9 +3626,9 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, if (model->rowCount(index)) { reconstructed.clear(); // Construct new file body - // File contains raw data, must be parsed as region + // File contains raw data, must be parsed as region without header if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) { - result = reconstructRegion(index, reconstructed); + result = reconstructRegion(index, reconstructed, false); if (result) return result; } @@ -3381,6 +3800,12 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, if (guidDefinedHeader->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) { // CRC32 section if (QByteArray((const char*)&guidDefinedHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_CRC32) { + // Check header size + if ((UINT32)header.size() != sizeof(EFI_GUID_DEFINED_SECTION) + sizeof(UINT32)) { + msg(tr("reconstructSection: invalid CRC32 section size %1h (%2)") + .hexarg(header.size()).arg(header.size()), index); + return ERR_INVALID_SECTION; + } // Calculate CRC32 of section data UINT32 crc = crc32(0, (const UINT8*)compressed.constData(), compressed.size()); // Store new CRC32 @@ -3424,7 +3849,7 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)model->body(index).constData(); teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); }*/ - + if (base) { result = rebase(reconstructed, base - teFixup + header.size()); if (result) { @@ -3487,9 +3912,9 @@ UINT8 FfsEngine::reconstruct(const QModelIndex &index, QByteArray& reconstructed break; case Types::Padding: - // No reconstruction needed - reconstructed = model->header(index).append(model->body(index)); - return ERR_SUCCESS; + result = reconstructPadding(index, reconstructed); + if (result) + return result; break; case Types::Volume: @@ -3518,6 +3943,10 @@ UINT8 FfsEngine::reconstruct(const QModelIndex &index, QByteArray& reconstructed UINT8 FfsEngine::growVolume(QByteArray & header, const UINT32 size, UINT32 & newSize) { + // Check sanity + if ((UINT32)header.size() < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) + return ERR_INVALID_VOLUME; + // Adjust new size to be representable by current FvBlockMap EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); EFI_FV_BLOCK_MAP_ENTRY* blockMap = (EFI_FV_BLOCK_MAP_ENTRY*)(header.data() + sizeof(EFI_FIRMWARE_VOLUME_HEADER)); @@ -3729,11 +4158,15 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) QByteArray file = executable; // Populate DOS header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) + return ERR_INVALID_FILE; EFI_IMAGE_DOS_HEADER* dosHeader = (EFI_IMAGE_DOS_HEADER*)file.data(); // Check signature if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ UINT32 offset = dosHeader->e_lfanew; + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) + return ERR_UNKNOWN_IMAGE_TYPE; EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(file.data() + offset); if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) return ERR_UNKNOWN_IMAGE_TYPE; @@ -3741,8 +4174,12 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) // Skip file header offset += sizeof(EFI_IMAGE_FILE_HEADER); // Check optional header magic + if ((UINT32)file.size() < offset + sizeof(UINT16)) + return ERR_UNKNOWN_IMAGE_TYPE; UINT16 magic = *(UINT16*)(file.data() + offset); if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (EFI_IMAGE_OPTIONAL_HEADER32*)(file.data() + offset); delta = base - optHeader->ImageBase; if (!delta) @@ -3754,6 +4191,8 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) optHeader->ImageBase = base; } else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (EFI_IMAGE_OPTIONAL_HEADER64*)(file.data() + offset); delta = base - optHeader->ImageBase; if (!delta) @@ -3769,6 +4208,8 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) } else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ // Populate TE header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) + return ERR_INVALID_FILE; EFI_IMAGE_TE_HEADER* teHeader = (EFI_IMAGE_TE_HEADER*)file.data(); delta = base - teHeader->ImageBase; if (!delta) @@ -3808,7 +4249,10 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) // Run this relocation record while (Reloc < RelocEnd) { - UINT8* data = (UINT8*)(file.data() + RelocBase->VirtualAddress - teFixup + (*Reloc & 0x0FFF)); + UINT32 RelocLocation = RelocBase->VirtualAddress - teFixup + (*Reloc & 0x0FFF); + if ((UINT32)file.size() < RelocLocation) + return ERR_BAD_RELOCATION_ENTRY; + UINT8* data = (UINT8*)(file.data() + RelocLocation); switch ((*Reloc) >> 12) { case EFI_IMAGE_REL_BASED_ABSOLUTE: // Do nothing @@ -3884,11 +4328,15 @@ UINT8 FfsEngine::getEntryPoint(const QByteArray &file, UINT32& entryPoint) return ERR_INVALID_FILE; // Populate DOS header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) + return ERR_INVALID_FILE; const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)file.constData(); // Check signature if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ UINT32 offset = dosHeader->e_lfanew; + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) + return ERR_UNKNOWN_IMAGE_TYPE; const EFI_IMAGE_PE_HEADER* peHeader = (const EFI_IMAGE_PE_HEADER*)(file.constData() + offset); if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) return ERR_UNKNOWN_IMAGE_TYPE; @@ -3900,10 +4348,14 @@ UINT8 FfsEngine::getEntryPoint(const QByteArray &file, UINT32& entryPoint) // Check optional header magic const UINT16 magic = *(const UINT16*)(file.constData() + offset); if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER32*)(file.constData() + offset); entryPoint = optHeader->ImageBase + optHeader->AddressOfEntryPoint; } else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER64*)(file.constData() + offset); entryPoint = optHeader->ImageBase + optHeader->AddressOfEntryPoint; } @@ -3912,6 +4364,8 @@ UINT8 FfsEngine::getEntryPoint(const QByteArray &file, UINT32& entryPoint) } else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ // Populate TE header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) + return ERR_INVALID_FILE; const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)file.constData(); UINT32 teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); entryPoint = teHeader->ImageBase + teHeader->AddressOfEntryPoint - teFixup; @@ -3925,11 +4379,15 @@ UINT8 FfsEngine::getBase(const QByteArray& file, UINT32& base) return ERR_INVALID_FILE; // Populate DOS header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) + return ERR_INVALID_FILE; const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)file.constData(); // Check signature if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ UINT32 offset = dosHeader->e_lfanew; + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) + return ERR_UNKNOWN_IMAGE_TYPE; const EFI_IMAGE_PE_HEADER* peHeader = (const EFI_IMAGE_PE_HEADER*)(file.constData() + offset); if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) return ERR_UNKNOWN_IMAGE_TYPE; @@ -3941,10 +4399,14 @@ UINT8 FfsEngine::getBase(const QByteArray& file, UINT32& base) // Check optional header magic const UINT16 magic = *(const UINT16*)(file.constData() + offset); if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER32*)(file.constData() + offset); base = optHeader->ImageBase; } else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER64*)(file.constData() + offset); base = optHeader->ImageBase; } @@ -3953,9 +4415,11 @@ UINT8 FfsEngine::getBase(const QByteArray& file, UINT32& base) } else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ // Populate TE header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) + return ERR_INVALID_FILE; const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)file.constData(); //!TODO: add handling - base = teHeader->ImageBase; + base = teHeader->ImageBase; } return ERR_SUCCESS; @@ -4030,10 +4494,10 @@ UINT8 FfsEngine::recursiveDump(const QModelIndex & index, const QString & path, return ERR_INVALID_PARAMETER; QDir dir; - if (guid.isEmpty() || + if (guid.isEmpty() || guidToQString(*(const EFI_GUID*)model->header(index).constData()) == guid || guidToQString(*(const EFI_GUID*)model->header(model->findParentOfType(index, Types::File)).constData()) == guid) { - + if (dir.cd(path)) return ERR_DIR_ALREADY_EXIST; @@ -4068,7 +4532,7 @@ UINT8 FfsEngine::recursiveDump(const QModelIndex & index, const QString & path, file.write(info.toLatin1()); file.close(); dumped = true; - } + } UINT8 result; for (int i = 0; i < model->rowCount(index); i++) { diff --git a/ffsengine.h b/ffsengine.h index 41d9e0a..4fe5223 100644 --- a/ffsengine.h +++ b/ffsengine.h @@ -67,6 +67,7 @@ public: UINT8 parseMeRegion(const QByteArray & me, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseBiosRegion(const QByteArray & bios, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parsePdrRegion(const QByteArray & pdr, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); + UINT8 parseEcRegion(const QByteArray & ec, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseBios(const QByteArray & bios, const QModelIndex & parent = QModelIndex()); UINT8 parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); @@ -81,7 +82,8 @@ public: UINT8 reconstructImageFile(QByteArray &reconstructed); UINT8 reconstruct(const QModelIndex &index, QByteArray & reconstructed); UINT8 reconstructIntelImage(const QModelIndex& index, QByteArray & reconstructed); - UINT8 reconstructRegion(const QModelIndex& index, QByteArray & reconstructed); + UINT8 reconstructRegion(const QModelIndex& index, QByteArray & reconstructed, bool includeHeader = true); + UINT8 reconstructPadding(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructBios(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructVolume(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructFile(const QModelIndex& index, const UINT8 revision, const UINT8 erasePolarity, const UINT32 base, QByteArray& reconstructed); diff --git a/treeitem.cpp b/treeitem.cpp index 94514dd..925d95b 100644 --- a/treeitem.cpp +++ b/treeitem.cpp @@ -17,7 +17,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, const QString & name, const QString & text, const QString & info, - const QByteArray & header, const QByteArray & body, const QByteArray & parsingData, + const QByteArray & header, const QByteArray & body, TreeItem *parent) : itemAction(Actions::NoAction), itemType(type), @@ -28,7 +28,6 @@ TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const UINT8 compressio itemInfo(info), itemHeader(header), itemBody(body), - itemParsingData(parsingData), parentItem(parent) { } @@ -184,11 +183,6 @@ QByteArray TreeItem::body() const return itemBody; } -QByteArray TreeItem::parsingData() const -{ - return itemParsingData; -} - bool TreeItem::hasEmptyHeader() const { return itemHeader.isEmpty(); @@ -199,16 +193,6 @@ bool TreeItem::hasEmptyBody() const return itemBody.isEmpty(); } -bool TreeItem::hasEmptyParsingData() const -{ - return itemParsingData.isEmpty(); -} - -void TreeItem::setParsingData(const QByteArray & data) -{ - itemParsingData = data; -} - UINT8 TreeItem::action() const { return itemAction; diff --git a/treeitem.h b/treeitem.h index 8697a75..58015f4 100644 --- a/treeitem.h +++ b/treeitem.h @@ -26,7 +26,7 @@ class TreeItem public: TreeItem(const UINT8 type, const UINT8 subtype = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, const QString &name = QString(), const QString &text = QString(), const QString &info = QString(), - const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QByteArray & parsingData = QByteArray(), + const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), TreeItem *parent = 0); ~TreeItem(); @@ -63,10 +63,6 @@ public: QByteArray body() const; bool hasEmptyBody() const; - QByteArray parsingData() const; - bool hasEmptyParsingData() const; - void setParsingData(const QByteArray & data); - QString info() const; void addInfo(const QString &info); void setInfo(const QString &info); @@ -87,7 +83,6 @@ private: QString itemInfo; QByteArray itemHeader; QByteArray itemBody; - QByteArray itemParsingData; TreeItem *parentItem; }; diff --git a/treemodel.cpp b/treemodel.cpp index ef83561..a82724b 100644 --- a/treemodel.cpp +++ b/treemodel.cpp @@ -178,22 +178,6 @@ bool TreeModel::hasEmptyBody(const QModelIndex &index) const return item->hasEmptyBody(); } -QByteArray TreeModel::parsingData(const QModelIndex &index) const -{ - if (!index.isValid()) - return QByteArray(); - TreeItem *item = static_cast(index.internalPointer()); - return item->parsingData(); -} - -bool TreeModel::hasEmptyParsingData(const QModelIndex &index) const -{ - if (!index.isValid()) - return true; - TreeItem *item = static_cast(index.internalPointer()); - return item->hasEmptyParsingData(); -} - QString TreeModel::name(const QModelIndex &index) const { if (!index.isValid()) @@ -284,20 +268,9 @@ void TreeModel::setAction(const QModelIndex &index, const UINT8 action) emit dataChanged(this->index(0, 0), index); } -void TreeModel::setParsingData(const QModelIndex &index, const QByteArray &data) -{ - if (!index.isValid()) - return; - - TreeItem *item = static_cast(index.internalPointer()); - item->setParsingData(data); - emit dataChanged(this->index(0, 0), index); -} - QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, const QString & name, const QString & text, const QString & info, - const QByteArray & header, const QByteArray & body, const QByteArray & parsingData, - const QModelIndex & parent, const UINT8 mode) + const QByteArray & header, const QByteArray & body, const QModelIndex & parent, const UINT8 mode) { TreeItem *item = 0; TreeItem *parentItem = 0; @@ -318,7 +291,7 @@ QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT } } - TreeItem *newItem = new TreeItem(type, subtype, compression, name, text, info, header, body, parsingData, parentItem); + TreeItem *newItem = new TreeItem(type, subtype, compression, name, text, info, header, body, parentItem); if (mode == CREATE_MODE_APPEND) { emit layoutAboutToBeChanged(); parentItem->appendChild(newItem); diff --git a/treemodel.h b/treemodel.h index 3c8c210..ae9c717 100644 --- a/treemodel.h +++ b/treemodel.h @@ -65,7 +65,7 @@ public: QModelIndex addItem(const UINT8 type, const UINT8 subtype = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, const QString & name = QString(), const QString & text = QString(), const QString & info = QString(), - const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QByteArray & parsingData = QByteArray(), + const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); QModelIndex findParentOfType(const QModelIndex & index, UINT8 type) const; diff --git a/types.cpp b/types.cpp index 5904e9e..a9a9300 100644 --- a/types.cpp +++ b/types.cpp @@ -24,11 +24,13 @@ QString regionTypeToQString(const UINT8 type) case Subtypes::GbeRegion: return QObject::tr("GbE"); case Subtypes::MeRegion: - return QObject::tr("ME/TXE"); + return QObject::tr("ME"); case Subtypes::BiosRegion: return QObject::tr("BIOS"); case Subtypes::PdrRegion: return QObject::tr("PDR"); + case Subtypes::EcRegion: + return QObject::tr("EC"); default: return QObject::tr("Unknown"); }; @@ -80,7 +82,7 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) return QObject::tr("Non-empty"); else return QObject::tr("Unknown subtype"); - case Types::Volume: + case Types::Volume: if (subtype == Subtypes::UnknownVolume) return QObject::tr("Unknown"); else if (subtype == Subtypes::Ffs2Volume) @@ -89,14 +91,16 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) return QObject::tr("FFSv3"); else return QObject::tr("Unknown subtype"); - case Types::Capsule: + case Types::Capsule: if (subtype == Subtypes::AptioSignedCapsule) return QObject::tr("Aptio signed"); else if (subtype == Subtypes::AptioUnsignedCapsule) return QObject::tr("Aptio unsigned"); else if (subtype == Subtypes::UefiCapsule) - return QObject::tr("UEFI 2.0 "); - else + return QObject::tr("UEFI 2.0"); + else if (subtype == Subtypes::ToshibaCapsule) + return QObject::tr("Toshiba"); + else return QObject::tr("Unknown subtype"); case Types::Region: return regionTypeToQString(subtype); diff --git a/types.h b/types.h index a65b73e..7d71953 100644 --- a/types.h +++ b/types.h @@ -54,7 +54,8 @@ namespace Subtypes { enum CapsuleSubtypes { AptioSignedCapsule = 80, AptioUnsignedCapsule, - UefiCapsule + UefiCapsule, + ToshibaCapsule }; enum VolumeSubtypes { @@ -68,7 +69,8 @@ namespace Subtypes { GbeRegion, MeRegion, BiosRegion, - PdrRegion + PdrRegion, + EcRegion }; enum PaddingSubtypes { @@ -85,25 +87,4 @@ extern QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype); extern QString compressionTypeToQString(const UINT8 algorithm); extern QString regionTypeToQString(const UINT8 type); -enum ParsingDataTypes { - UnknownParsingData, - VolumeParsingData, - FileParsingData -}; - -typedef union _PARSING_DATA_UNION { - struct _PARSING_DATA_UNION_VOLUME { - bool HasZeroVectorCRC; - } Volume; - - struct _PARSING_DATA_UNION_FILE { - UINT32 Offset; - } File; -} PARSING_DATA_UNION; - -typedef struct _PARSING_DATA { - UINT8 Type; - PARSING_DATA_UNION Data; -} PARSING_DATA; - #endif \ No newline at end of file diff --git a/uefitool.cpp b/uefitool.cpp index 0f344f2..34ca9e0 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -16,8 +16,8 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), -ui(new Ui::UEFITool), -version(tr("0.20.4")) +ui(new Ui::UEFITool), +version(tr("0.21.5")) { clipboard = QApplication::clipboard(); @@ -31,6 +31,7 @@ version(tr("0.20.4")) // Connect signals to slots connect(ui->actionOpenImageFile, SIGNAL(triggered()), this, SLOT(openImageFile())); + connect(ui->actionOpenImageFileInNewWindow, SIGNAL(triggered()), this, SLOT(openImageFileInNewWindow())); connect(ui->actionSaveImageFile, SIGNAL(triggered()), this, SLOT(saveImageFile())); connect(ui->actionSearch, SIGNAL(triggered()), this, SLOT(search())); connect(ui->actionExtract, SIGNAL(triggered()), this, SLOT(extractAsIs())); @@ -83,6 +84,11 @@ UEFITool::~UEFITool() delete searchDialog; } +void UEFITool::setProgramPath(QString path) +{ + currentProgramPath = path; +}; + void UEFITool::init() { // Clear components @@ -147,7 +153,7 @@ void UEFITool::populateUi(const QModelIndex ¤t) (type == Types::Section && (subtype == EFI_SECTION_COMPRESSION || subtype == EFI_SECTION_GUID_DEFINED || subtype == EFI_SECTION_DISPOSABLE))); ui->actionInsertBefore->setEnabled(type == Types::File || type == Types::Section); ui->actionInsertAfter->setEnabled(type == Types::File || type == Types::Section); - ui->actionReplace->setEnabled((type == Types::Region && subtype != Subtypes::DescriptorRegion) || type == Types::Volume || type == Types::File || type == Types::Section); + ui->actionReplace->setEnabled((type == Types::Region && subtype != Subtypes::DescriptorRegion) || type == Types::Padding || type == Types::Volume || type == Types::File || type == Types::Section); ui->actionReplaceBody->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->actionMessagesCopy->setEnabled(false); } @@ -321,6 +327,13 @@ void UEFITool::replace(const UINT8 mode) else return; } + else if (model->type(index) == Types::Padding) { + if (mode == REPLACE_MODE_AS_IS) { + path = QFileDialog::getOpenFileName(this, tr("Select padding file to replace selected object"), currentDir, "Padding files (*.pad *.bin);;All files (*)"); + } + else + return; + } else if (model->type(index) == Types::Volume) { if (mode == REPLACE_MODE_AS_IS) { path = QFileDialog::getOpenFileName(this, tr("Select volume file to replace selected object"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); @@ -358,6 +371,8 @@ void UEFITool::replace(const UINT8 mode) path = QFileDialog::getOpenFileName(this, tr("Select volume file to replace body"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); else if (model->subtype(index) == EFI_SECTION_RAW) path = QFileDialog::getOpenFileName(this, tr("Select raw file to replace body"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); + else if (model->subtype(index) == EFI_SECTION_PE32 || model->subtype(index) == EFI_SECTION_TE || model->subtype(index) == EFI_SECTION_PIC) + path = QFileDialog::getOpenFileName(this, tr("Select EFI executable file to replace body"), currentDir, "EFI executable files (*.efi *.dxe *.pei *.bin);;All files (*)"); else path = QFileDialog::getOpenFileName(this, tr("Select file to replace body"), currentDir, "Binary files (*.bin);;All files (*)"); } @@ -447,7 +462,7 @@ void UEFITool::extract(const UINT8 mode) case Types::Capsule: path = QFileDialog::getSaveFileName(this, tr("Save capsule body to image file"), currentDir, "Image files (*.rom *.bin);;All files (*)"); break; - case Types::Volume: + case Types::Volume: path = QFileDialog::getSaveFileName(this, tr("Save volume body to file"), currentDir, "Volume body files (*.vbd *.bin);;All files (*)"); break; case Types::File: { @@ -521,7 +536,7 @@ void UEFITool::exit() void UEFITool::saveImageFile() { - QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.efi *.dec);;All files (*)"); + QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.dec);;All files (*)"); if (path.isEmpty()) return; @@ -552,10 +567,18 @@ void UEFITool::saveImageFile() void UEFITool::openImageFile() { - QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.efi *.dec);;All files (*)"); + QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.dec);;All files (*)"); openImageFile(path); } +void UEFITool::openImageFileInNewWindow() +{ + QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file in new window"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.dec);;All files (*)"); + if (path.trimmed().isEmpty()) + return; + QProcess::startDetached(currentProgramPath, QStringList(path)); +} + void UEFITool::openImageFile(QString path) { if (path.trimmed().isEmpty()) diff --git a/uefitool.h b/uefitool.h index dda7fdc..edac83b 100644 --- a/uefitool.h +++ b/uefitool.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ public: ~UEFITool(); void openImageFile(QString path); + void setProgramPath(QString path); private slots: void init(); @@ -58,6 +60,7 @@ public: void scrollTreeView(QListWidgetItem* item); void openImageFile(); + void openImageFileInNewWindow(); void saveImageFile(); void search(); @@ -95,6 +98,7 @@ private: SearchDialog* searchDialog; QClipboard* clipboard; QString currentDir; + QString currentProgramPath; QQueue messageItems; const QString version; diff --git a/uefitool.ui b/uefitool.ui index 82868da..8e0a19d 100644 --- a/uefitool.ui +++ b/uefitool.ui @@ -188,6 +188,7 @@ &File + @@ -231,6 +232,8 @@ &Padding + + @@ -522,6 +525,17 @@ Ctrl+Alt+C + + + &Open image file in new window... + + + Open image file in new window + + + Ctrl+Shift+O + + diff --git a/uefitool_main.cpp b/uefitool_main.cpp index e5764c0..a9ac03a 100644 --- a/uefitool_main.cpp +++ b/uefitool_main.cpp @@ -23,6 +23,7 @@ int main(int argc, char *argv[]) a.setApplicationName("UEFITool"); UEFITool w; + w.setProgramPath(a.arguments().at(0)); if (a.arguments().length() > 1) w.openImageFile(a.arguments().at(1)); w.show();