From acc913769bf473fdeb4485c480a5a6eee8c43a31 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Fri, 7 Oct 2022 14:40:20 +0200 Subject: [PATCH] Add workaround for Lenovo large files inside FFSv2 volumes --- common/ffs.h | 36 ++++++++++++++---------------------- common/ffsparser.cpp | 36 ++++++++++++++++++++++++++++-------- common/ffsparser.h | 2 +- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/common/ffs.h b/common/ffs.h index bfcd3d7..8612241 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -280,11 +280,22 @@ EFI_GUID Name; EFI_FFS_INTEGRITY_CHECK IntegrityCheck; UINT8 Type; UINT8 Attributes; -UINT8 Size[3]; // Set to 0xFFFFFF +UINT8 Size[3]; // Set to 0xFFFFFF or 0x000000 UINT8 State; UINT64 ExtendedSize; } EFI_FFS_FILE_HEADER2; +// Lenovo large file header +typedef struct EFI_FFS_FILE_HEADER_LENOVO_ { +EFI_GUID Name; +EFI_FFS_INTEGRITY_CHECK IntegrityCheck; +UINT8 Type; +UINT8 Attributes; +UINT8 Size[3]; // Set to 0x000000 +UINT8 State; +UINT32 ExtendedSize; +} EFI_FFS_FILE_HEADER2_LENOVO; + // Standard data checksum, used if FFS_ATTRIB_CHECKSUM is clear #define FFS_FIXED_CHECKSUM 0x5A #define FFS_FIXED_CHECKSUM2 0xAA @@ -317,8 +328,8 @@ UINT64 ExtendedSize; // File attributes #define FFS_ATTRIB_TAIL_PRESENT 0x01 // Valid only for revision 1 volumes #define FFS_ATTRIB_RECOVERY 0x02 // Valid only for revision 1 volumes -#define FFS_ATTRIB_LARGE_FILE 0x01 // Valid only for FFSv3 volumes -#define FFS_ATTRIB_DATA_ALIGNMENT2 0x02 // Volaid only for revision 2 volumes, added in UEFI PI 1.6 +#define FFS_ATTRIB_LARGE_FILE 0x01 // Valid only for FFSv3 volumes or FFSv2 volumes with Lenovo large files +#define FFS_ATTRIB_DATA_ALIGNMENT2 0x02 // Valid only for revision 2 volumes, added in UEFI PI 1.6 #define FFS_ATTRIB_FIXED 0x04 #define FFS_ATTRIB_DATA_ALIGNMENT 0x38 #define FFS_ATTRIB_CHECKSUM 0x40 @@ -377,13 +388,6 @@ typedef struct EFI_COMMON_SECTION_HEADER2_ { UINT32 ExtendedSize; } EFI_COMMON_SECTION_HEADER2; -// Apple common section header -typedef struct EFI_COMMON_SECTION_HEADER_APPLE { - UINT8 Size[3]; - UINT8 Type; - UINT32 Reserved; // Must be 0x7FFF for this header to be used -} EFI_COMMON_SECTION_HEADER_APPLE; - // Section2 usage indicator #define EFI_SECTION2_IS_USED 0xFFFFFF @@ -417,11 +421,6 @@ typedef struct EFI_COMPRESSION_SECTION_ { UINT8 CompressionType; } EFI_COMPRESSION_SECTION; -typedef struct EFI_COMPRESSION_SECTION_APPLE_ { - UINT32 UncompressedLength; - UINT32 CompressionType; -} EFI_COMPRESSION_SECTION_APPLE; - // Compression types #define EFI_NOT_COMPRESSED 0x00 #define EFI_STANDARD_COMPRESSION 0x01 @@ -435,13 +434,6 @@ typedef struct EFI_GUID_DEFINED_SECTION_ { UINT16 Attributes; } EFI_GUID_DEFINED_SECTION; -typedef struct EFI_GUID_DEFINED_SECTION_APPLE_ { - EFI_GUID SectionDefinitionGuid; - UINT16 DataOffset; - UINT16 Attributes; - UINT32 Reserved; -} EFI_GUID_DEFINED_SECTION_APPLE; - // Attributes for GUID defined section #define EFI_GUIDED_SECTION_PROCESSING_REQUIRED 0x01 #define EFI_GUIDED_SECTION_AUTH_STATUS_VALID 0x02 diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 2305937..f8f949c 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -1469,12 +1469,14 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) UINT8 emptyByte = 0xFF; UINT8 ffsVersion = 2; UINT32 usedSpace = 0; + UINT8 revision = 2; if (model->hasEmptyParsingData(index) == false) { UByteArray data = model->parsingData(index); const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData(); emptyByte = pdata->emptyByte; ffsVersion = pdata->ffsVersion; usedSpace = pdata->usedSpace; + revision = pdata->revision; } // Check for unknown FFS version @@ -1488,7 +1490,7 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) UINT32 fileOffset = 0; while (fileOffset < volumeBodySize) { - UINT32 fileSize = getFileSize(volumeBody, fileOffset, ffsVersion); + UINT32 fileSize = getFileSize(volumeBody, fileOffset, ffsVersion, revision); if (fileSize == 0) { msg(usprintf("%s: file header parsing failed with invalid size", __FUNCTION__), index); @@ -1628,7 +1630,7 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) return U_SUCCESS; } -UINT32 FfsParser::getFileSize(const UByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion) +UINT32 FfsParser::getFileSize(const UByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion, const UINT8 revision) { if ((UINT32)volume.size() < fileOffset + sizeof(EFI_FFS_FILE_HEADER)) { return 0; @@ -1637,7 +1639,18 @@ UINT32 FfsParser::getFileSize(const UByteArray & volume, const UINT32 fileOffset const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)(volume.constData() + fileOffset); if (ffsVersion == 2) { - return uint24ToUint32(fileHeader->Size); + UINT32 size = uint24ToUint32(fileHeader->Size); + // Special case of Lenovo large file insize FFSv2 Rev2 volume + if (revision == 2 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + if ((UINT32)volume.size() < fileOffset + sizeof(EFI_FFS_FILE_HEADER2_LENOVO)) { + return 0; + } + + const EFI_FFS_FILE_HEADER2_LENOVO* fileHeader2Lenovo = (const EFI_FFS_FILE_HEADER2_LENOVO*)(volume.constData() + fileOffset); + return (UINT32)fileHeader2Lenovo->ExtendedSize; + } + + return size; } else if (ffsVersion == 3) { if (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE) { @@ -1646,7 +1659,7 @@ UINT32 FfsParser::getFileSize(const UByteArray & volume, const UINT32 fileOffset } const EFI_FFS_FILE_HEADER2* fileHeader2 = (const EFI_FFS_FILE_HEADER2*)(volume.constData() + fileOffset); - return (UINT32) fileHeader2->ExtendedSize; + return (UINT32)fileHeader2->ExtendedSize; } return uint24ToUint32(fileHeader->Size); @@ -1683,10 +1696,17 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf // Get file header UByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); EFI_FFS_FILE_HEADER* tempFileHeader = (EFI_FFS_FILE_HEADER*)header.data(); - if (ffsVersion == 3 && (tempFileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { - if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER2)) - return U_INVALID_FILE; - header = file.left(sizeof(EFI_FFS_FILE_HEADER2)); + if (tempFileHeader->Attributes & FFS_ATTRIB_LARGE_FILE) { + if (ffsVersion == 2 && volumeRevision == 2) { + if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER2_LENOVO)) + return U_INVALID_FILE; + header = file.left(sizeof(EFI_FFS_FILE_HEADER2_LENOVO)); + } + if (ffsVersion == 3) { + if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER2)) + return U_INVALID_FILE; + header = file.left(sizeof(EFI_FFS_FILE_HEADER2)); + } } const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)header.constData(); diff --git a/common/ffsparser.h b/common/ffsparser.h index 884b898..162fda4 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -143,7 +143,7 @@ private: USTATUS parseAprioriRawSection(const UByteArray & body, UString & parsed); USTATUS findNextRawAreaItem(const UModelIndex & index, const UINT32 localOffset, UINT8 & nextItemType, UINT32 & nextItemOffset, UINT32 & nextItemSize, UINT32 & nextItemAlternativeSize); - UINT32 getFileSize(const UByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion); + UINT32 getFileSize(const UByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion, const UINT8 revision); UINT32 getSectionSize(const UByteArray & file, const UINT32 sectionOffset, const UINT8 ffsVersion); USTATUS parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index);