From 4cf6b4f37b14be24b526fdd33981db1271f83566 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Tue, 2 Feb 2016 02:08:08 +0100 Subject: [PATCH] UT NE A19 Thanks to lordkag for #41: - improved parsing of Intel flash descriptor - improved detection of Tiano/EFI 1.1 compression type - added 2 UEFI capsule GUIDs used by Lenovo - solved potential crash on very low memory available - UEFIExtract and UEFIFind update to include the latest parser changes --- UEFIExtract/ffsdumper.cpp | 2 +- UEFIExtract/uefiextract_main.cpp | 8 +- UEFIFind/uefifind.cpp | 4 +- UEFIFind/uefifind_main.cpp | 8 +- UEFITool/uefitool.cpp | 8 +- common/Tiano/EfiTianoCompress.c | 1 + common/basetypes.h | 15 +- common/descriptor.h | 22 +- common/ffs.h | 10 +- common/ffsbuilder.h | 12 +- common/ffsparser.cpp | 633 ++++++++++++++++--------------- common/ffsparser.h | 22 +- common/types.cpp | 20 +- common/types.h | 14 +- common/utility.cpp | 74 ++-- common/utility.h | 4 +- 16 files changed, 463 insertions(+), 394 deletions(-) diff --git a/UEFIExtract/ffsdumper.cpp b/UEFIExtract/ffsdumper.cpp index d02efb0..4069e0f 100644 --- a/UEFIExtract/ffsdumper.cpp +++ b/UEFIExtract/ffsdumper.cpp @@ -71,7 +71,7 @@ STATUS FfsDumper::recursiveDump(const QModelIndex & index, const QString & path, QString info = tr("Type: %1\nSubtype: %2\n%3%4") .arg(itemTypeToQString(model->type(index))) .arg(itemSubtypeToQString(model->type(index), model->subtype(index))) - .arg(model->text(index).isEmpty() ? "" : tr("Text: %1\n").arg(model->text(index))) + .arg(model->text(index).isEmpty() ? tr("") : tr("Text: %1\n").arg(model->text(index))) .arg(model->info(index)); file.setFileName(tr("%1/info.txt").arg(path)); if (!file.open(QFile::Text | QFile::WriteOnly)) diff --git a/UEFIExtract/uefiextract_main.cpp b/UEFIExtract/uefiextract_main.cpp index 29d57fc..dd361fa 100644 --- a/UEFIExtract/uefiextract_main.cpp +++ b/UEFIExtract/uefiextract_main.cpp @@ -1,6 +1,6 @@ /* uefiextract_main.cpp -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -49,7 +49,7 @@ int main(int argc, char *argv[]) TreeModel model; FfsParser ffsParser(&model); - STATUS result = ffsParser.parseImageFile(buffer, model.index(0, 0)); + STATUS result = ffsParser.parse(buffer); if (result) return result; @@ -75,8 +75,8 @@ int main(int argc, char *argv[]) } } else { - std::cout << "UEFIExtract 0.10.6" << std::endl << std::endl - << "Usage: uefiextract imagefile [FileGUID_1 FileGUID_2 ... FileGUID_31]" << std::endl + std::cout << "UEFIExtract 0.10.7" << std::endl << std::endl + << "Usage: UEFIExtract imagefile [FileGUID_1 FileGUID_2 ... FileGUID_31]" << std::endl << "Return value is a bit mask where 0 at position N means that file with GUID_N was found and unpacked, 1 otherwise" << std::endl; return 1; } diff --git a/UEFIFind/uefifind.cpp b/UEFIFind/uefifind.cpp index f8be94b..9f8467b 100644 --- a/UEFIFind/uefifind.cpp +++ b/UEFIFind/uefifind.cpp @@ -1,6 +1,6 @@ /* uefifind.cpp -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -46,7 +46,7 @@ STATUS UEFIFind::init(const QString & path) QByteArray buffer = inputFile.readAll(); inputFile.close(); - result = ffsParser->parseImageFile(buffer, model->index(0,0)); + result = ffsParser->parse(buffer); if (result) return result; diff --git a/UEFIFind/uefifind_main.cpp b/UEFIFind/uefifind_main.cpp index 2448e80..3c99391 100644 --- a/UEFIFind/uefifind_main.cpp +++ b/UEFIFind/uefifind_main.cpp @@ -1,6 +1,6 @@ /* uefifind_main.cpp -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -148,9 +148,9 @@ int main(int argc, char *argv[]) return ERR_SUCCESS; } else { - std::cout << "UEFIFind 0.10.4.1" << std::endl << std::endl << - "Usage: uefifind {header | body | all} {list | count} pattern imagefile" << std::endl << - " or uefifind file patternsfile imagefile" << std::endl; + std::cout << "UEFIFind 0.10.5" << std::endl << std::endl << + "Usage: UEFIFind {header | body | all} {list | count} pattern imagefile" << std::endl << + " or UEFIFind file patternsfile imagefile" << std::endl; return ERR_INVALID_PARAMETER; } } diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index dd10e1b..59e4de4 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -1,6 +1,6 @@ /* uefitool.cpp - Copyright (c) 2015, Nikolaj Schlej. All rights reserved. + Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.30.0_alpha18")) +version(tr("0.30.0_alpha19")) { clipboard = QApplication::clipboard(); @@ -566,7 +566,7 @@ void UEFITool::extract(const UINT8 mode) void UEFITool::about() { QMessageBox::about(this, tr("About UEFITool"), tr( - "Copyright (c) 2015, Nikolaj Schlej aka CodeRush.
" + "Copyright (c) 2016, Nikolaj Schlej aka CodeRush.
" "Program icon made by Alexander Zhidkov.

" "The program is dedicated to RevoGirl. Rest in peace, young genius.

" "The program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License.
" @@ -657,7 +657,7 @@ void UEFITool::openImageFile(QString path) init(); setWindowTitle(tr("UEFITool %1 - %2").arg(version).arg(fileInfo.fileName())); - UINT8 result = ffsParser->parseImageFile(buffer, model->index(0,0)); + UINT8 result = ffsParser->parse(buffer); showParserMessages(); if (result) { QMessageBox::critical(this, tr("Image parsing failed"), errorCodeToQString(result), QMessageBox::Ok); diff --git a/common/Tiano/EfiTianoCompress.c b/common/Tiano/EfiTianoCompress.c index 4f70fa5..9138333 100644 --- a/common/Tiano/EfiTianoCompress.c +++ b/common/Tiano/EfiTianoCompress.c @@ -506,6 +506,7 @@ Returns: UINT32 i; mText = malloc (WNDSIZ * 2 + MAXMATCH); + if (!mText) return EFI_OUT_OF_RESOURCES; for (i = 0 ; i < WNDSIZ * 2 + MAXMATCH; i ++) { mText[i] = 0; } diff --git a/common/basetypes.h b/common/basetypes.h index f186f1a..2738541 100644 --- a/common/basetypes.h +++ b/common/basetypes.h @@ -1,6 +1,6 @@ /* basetypes.h -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -93,12 +93,13 @@ typedef UINT8 STATUS; #define EFI_ERROR(X) (X) // Compression algorithms -#define COMPRESSION_ALGORITHM_UNKNOWN 0 -#define COMPRESSION_ALGORITHM_NONE 1 -#define COMPRESSION_ALGORITHM_EFI11 2 -#define COMPRESSION_ALGORITHM_TIANO 3 -#define COMPRESSION_ALGORITHM_LZMA 4 -#define COMPRESSION_ALGORITHM_IMLZMA 5 +#define COMPRESSION_ALGORITHM_UNKNOWN 0 +#define COMPRESSION_ALGORITHM_NONE 1 +#define COMPRESSION_ALGORITHM_EFI11 2 +#define COMPRESSION_ALGORITHM_TIANO 3 +#define COMPRESSION_ALGORITHM_UNDECIDED 4 +#define COMPRESSION_ALGORITHM_LZMA 5 +#define COMPRESSION_ALGORITHM_IMLZMA 6 // Item create modes #define CREATE_MODE_APPEND 0 diff --git a/common/descriptor.h b/common/descriptor.h index 74a9e66..b3989bc 100644 --- a/common/descriptor.h +++ b/common/descriptor.h @@ -1,6 +1,6 @@ /* descriptor.h -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -21,7 +21,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // Flash descriptor header typedef struct _FLASH_DESCRIPTOR_HEADER { - UINT8 FfVector[16]; // Must be 16 0xFFs + UINT8 ReservedVector[16]; // Reserved for ARM ResetVector, 0xFFs on x86/x86-64 machines UINT32 Signature; // 0x0FF0A55A } FLASH_DESCRIPTOR_HEADER; @@ -98,7 +98,7 @@ typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION { 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 + UINT16 : 16; } FLASH_DESCRIPTOR_COMPONENT_SECTION; // Region section @@ -115,16 +115,16 @@ typedef struct _FLASH_DESCRIPTOR_REGION_SECTION { 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 Reserved1Base; // Reserved1 + UINT16 Reserved1Limit; // + UINT16 Reserved2Base; // Reserved2 + UINT16 Reserved2Limit; // + UINT16 Reserved3Base; // Reserved3 + UINT16 Reserved3Limit; // UINT16 EcBase; // EC UINT16 EcLimit; // - UINT16 Region9Base; // Reserved region - UINT16 Region9Limit; // + UINT16 Reserved4Base; // Reserved4 + UINT16 Reserved4Limit; // } FLASH_DESCRIPTOR_REGION_SECTION; // Master section diff --git a/common/ffs.h b/common/ffs.h index af99369..e62bf78 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -1,6 +1,6 @@ /* ffs.h -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -53,6 +53,14 @@ const QByteArray EFI_CAPSULE_GUID const QByteArray INTEL_CAPSULE_GUID ("\xB9\x82\x91\x53\xB5\xAB\x91\x43\xB6\x9A\xE3\xA9\x43\xF7\x2F\xCC", 16); +// Lenovo capsule GUID +const QByteArray LENOVO_CAPSULE_GUID +("\xD3\xAF\x0B\xE2\x14\x99\x4F\x4F\x95\x37\x31\x29\xE0\x90\xEB\x3C", 16); + +// Another Lenovo capsule GUID +const QByteArray LENOVO2_CAPSULE_GUID +("\x76\xFE\xB5\x25\x43\x82\x5C\x4A\xA9\xBD\x7E\xE3\x24\x61\x98\xB5", 16); + // Toshiba EFI Capsule header typedef struct _TOSHIBA_CAPSULE_HEADER { EFI_GUID CapsuleGuid; diff --git a/common/ffsbuilder.h b/common/ffsbuilder.h index 6f7a7cb..b93689e 100644 --- a/common/ffsbuilder.h +++ b/common/ffsbuilder.h @@ -1,6 +1,6 @@ /* fssbuilder.h -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -19,11 +19,11 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include -#include "../common/basetypes.h" -#include "../common/treemodel.h" -#include "../common/descriptor.h" -#include "../common/ffs.h" -#include "../common/utility.h" +#include "basetypes.h" +#include "treemodel.h" +#include "descriptor.h" +#include "ffs.h" +#include "utility.h" class FfsBuilder : public QObject { diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index d731a47..39093d7 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -1,6 +1,6 @@ -/* ffsparser.cpp +/* ffsparser.cpp -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -10,16 +10,18 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ +#include "ffsparser.h" + #include -#include "ffsparser.h" -#include "types.h" -#include "treemodel.h" -#include "descriptor.h" -#include "ffs.h" -#include "gbe.h" -#include "me.h" -#include "fit.h" +// Region info structure definition +struct REGION_INFO { + UINT32 offset; + UINT32 length; + UINT8 type; + QByteArray data; + friend bool operator< (const REGION_INFO & lhs, const REGION_INFO & rhs){ return lhs.offset < rhs.offset; } +}; FfsParser::FfsParser(TreeModel* treeModel, QObject *parent) : QObject(parent), model(treeModel), capsuleOffsetFixup(0) @@ -45,47 +47,53 @@ void FfsParser::clearMessages() messagesVector.clear(); } -BOOLEAN FfsParser::hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2) +// Firmware image parsing functions +STATUS FfsParser::parse(const QByteArray & buffer) { - if (begin1 < begin2 && begin2 < end1) - return TRUE; - if (begin1 < end2 && end2 < end1) - return TRUE; - if (begin2 < begin1 && begin1 < end2) - return TRUE; - if (begin2 < end1 && end1 < end2) - return TRUE; - return FALSE; + QModelIndex root; + STATUS result = performFirstPass(buffer, root); + addOffsetsRecursive(root); + if (result) + return result; + + if (lastVtf.isValid()) { + result = performSecondPass(root); + } + else { + msg(tr("parse: not a single Volume Top File is found, the image may be corrupted")); + } + + return result; } -// Firmware image parsing functions -STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & root) +STATUS FfsParser::performFirstPass(const QByteArray & buffer, QModelIndex & index) { - // Reset capsule offset fixeup value + // Reset capsule offset fixup value capsuleOffsetFixup = 0; // Check buffer size to be more than or equal to size of EFI_CAPSULE_HEADER if ((UINT32)buffer.size() <= sizeof(EFI_CAPSULE_HEADER)) { - msg(tr("parseImageFile: image file is smaller than minimum size of %1h (%2) bytes").hexarg(sizeof(EFI_CAPSULE_HEADER)).arg(sizeof(EFI_CAPSULE_HEADER))); + msg(tr("performFirstPass: image file is smaller than minimum size of %1h (%2) bytes").hexarg(sizeof(EFI_CAPSULE_HEADER)).arg(sizeof(EFI_CAPSULE_HEADER))); return ERR_INVALID_PARAMETER; } - QModelIndex index; UINT32 capsuleHeaderSize = 0; // Check buffer for being normal EFI capsule header if (buffer.startsWith(EFI_CAPSULE_GUID) - || buffer.startsWith(INTEL_CAPSULE_GUID)) { + || buffer.startsWith(INTEL_CAPSULE_GUID) + || buffer.startsWith(LENOVO_CAPSULE_GUID) + || buffer.startsWith(LENOVO2_CAPSULE_GUID)) { // Get info const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_CAPSULE_HEADER*)buffer.constData(); // Check sanity of HeaderSize and CapsuleImageSize values if (capsuleHeader->HeaderSize == 0 || capsuleHeader->HeaderSize > (UINT32)buffer.size() || capsuleHeader->HeaderSize > capsuleHeader->CapsuleImageSize) { - msg(tr("parseImageFile: UEFI capsule header size of %1h (%2) bytes is invalid") + msg(tr("performFirstPass: UEFI capsule header size of %1h (%2) bytes is invalid") .hexarg(capsuleHeader->HeaderSize).arg(capsuleHeader->HeaderSize)); return ERR_INVALID_CAPSULE; } if (capsuleHeader->CapsuleImageSize == 0 || capsuleHeader->CapsuleImageSize > (UINT32)buffer.size()) { - msg(tr("parseImageFile: UEFI capsule image size of %1h (%2) bytes is invalid") + msg(tr("performFirstPass: UEFI capsule image size of %1h (%2) bytes is invalid") .hexarg(capsuleHeader->CapsuleImageSize).arg(capsuleHeader->CapsuleImageSize)); return ERR_INVALID_CAPSULE; } @@ -105,7 +113,7 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & capsuleOffsetFixup = capsuleHeaderSize; // Add tree item - index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, name, QString(), info, header, body, TRUE, QByteArray(), root); + index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, name, QString(), info, header, body, true); } // Check buffer for being Toshiba capsule header else if (buffer.startsWith(TOSHIBA_CAPSULE_GUID)) { @@ -114,12 +122,12 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & // Check sanity of HeaderSize and FullSize values if (capsuleHeader->HeaderSize == 0 || capsuleHeader->HeaderSize > (UINT32)buffer.size() || capsuleHeader->HeaderSize > capsuleHeader->FullSize) { - msg(tr("parseImageFile: Toshiba capsule header size of %1h (%2) bytes is invalid") + msg(tr("performFirstPass: Toshiba capsule header size of %1h (%2) bytes is invalid") .hexarg(capsuleHeader->HeaderSize).arg(capsuleHeader->HeaderSize)); return ERR_INVALID_CAPSULE; } if (capsuleHeader->FullSize == 0 || capsuleHeader->FullSize > (UINT32)buffer.size()) { - msg(tr("parseImageFile: Toshiba capsule full size of %1h (%2) bytes is invalid") + msg(tr("performFirstPass: Toshiba capsule full size of %1h (%2) bytes is invalid") .hexarg(capsuleHeader->FullSize).arg(capsuleHeader->FullSize)); return ERR_INVALID_CAPSULE; } @@ -139,14 +147,14 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & capsuleOffsetFixup = capsuleHeaderSize; // Add tree item - index = model->addItem(Types::Capsule, Subtypes::ToshibaCapsule, name, QString(), info, header, body, TRUE, QByteArray(), root); + index = model->addItem(Types::Capsule, Subtypes::ToshibaCapsule, name, QString(), info, header, body, true); } - // Check buffer for being extended Aptio signed capsule header + // Check buffer for being extended Aptio capsule header else if (buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID) || buffer.startsWith(APTIO_UNSIGNED_CAPSULE_GUID)) { bool signedCapsule = buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID); if ((UINT32)buffer.size() <= sizeof(APTIO_CAPSULE_HEADER)) { - msg(tr("parseImageFile: AMI capsule image file is smaller than minimum size of %1h (%2) bytes").hexarg(sizeof(APTIO_CAPSULE_HEADER)).arg(sizeof(APTIO_CAPSULE_HEADER))); + msg(tr("performFirstPass: AMI capsule image file is smaller than minimum size of %1h (%2) bytes").hexarg(sizeof(APTIO_CAPSULE_HEADER)).arg(sizeof(APTIO_CAPSULE_HEADER))); return ERR_INVALID_PARAMETER; } @@ -155,11 +163,11 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & // Check sanity of RomImageOffset and CapsuleImageSize values if (capsuleHeader->RomImageOffset == 0 || capsuleHeader->RomImageOffset > (UINT32)buffer.size() || capsuleHeader->RomImageOffset > capsuleHeader->CapsuleHeader.CapsuleImageSize) { - msg(tr("parseImageFile: AMI capsule image offset of %1h (%2) bytes is invalid").hexarg(capsuleHeader->RomImageOffset).arg(capsuleHeader->RomImageOffset)); + msg(tr("performFirstPass: AMI capsule image offset of %1h (%2) bytes is invalid").hexarg(capsuleHeader->RomImageOffset).arg(capsuleHeader->RomImageOffset)); return ERR_INVALID_CAPSULE; } if (capsuleHeader->CapsuleHeader.CapsuleImageSize == 0 || capsuleHeader->CapsuleHeader.CapsuleImageSize > (UINT32)buffer.size()) { - msg(tr("parseImageFile: AMI capsule image size of %1h (%2) bytes is invalid").hexarg(capsuleHeader->CapsuleHeader.CapsuleImageSize).arg(capsuleHeader->CapsuleHeader.CapsuleImageSize)); + msg(tr("performFirstPass: AMI capsule image size of %1h (%2) bytes is invalid").hexarg(capsuleHeader->CapsuleHeader.CapsuleImageSize).arg(capsuleHeader->CapsuleHeader.CapsuleImageSize)); return ERR_INVALID_CAPSULE; } @@ -178,17 +186,13 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & capsuleOffsetFixup = capsuleHeaderSize; // Add tree item - index = model->addItem(Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, name, QString(), info, header, body, TRUE, QByteArray(), root); + index = model->addItem(Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, name, QString(), info, header, body, true); // Show message about possible Aptio signature break if (signedCapsule) { - msg(tr("parseImageFile: Aptio capsule signature may become invalid after image modifications"), index); + msg(tr("performFirstPass: Aptio capsule signature may become invalid after image modifications"), index); } } - // Other cases - else { - index = root; - } // Skip capsule header to have flash chip image QByteArray flashImage = buffer.mid(capsuleHeaderSize); @@ -202,13 +206,16 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & // Parse as Intel image QModelIndex imageIndex; result = parseIntelImage(flashImage, capsuleHeaderSize, index, imageIndex); - if (result != ERR_INVALID_FLASH_DESCRIPTOR) + if (result != ERR_INVALID_FLASH_DESCRIPTOR) { + if (!index.isValid()) + index = imageIndex; return result; + } } // Get info QString name = tr("UEFI image"); - QString info = tr("Full size: %2h (%3)").hexarg(flashImage.size()).arg(flashImage.size()); + QString info = tr("Full size: %1h (%2)").hexarg(flashImage.size()).arg(flashImage.size()); // Construct parsing data PARSING_DATA pdata = parsingDataFromQModelIndex(index); @@ -219,21 +226,9 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & // Parse the image result = parseRawArea(flashImage, biosIndex); - if (result) - return result; - - // Add offsets - addOffsetsRecursive(index); - - // Check if the last VTF is found - if (!lastVtf.isValid()) { - msg(tr("parseImageFile: not a single Volume Top File is found, the image may be corrupted"), biosIndex); - } - else { - return performSecondPass(biosIndex); - } - - return ERR_SUCCESS; + if (!index.isValid()) + index = biosIndex; + return result; } STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) @@ -247,8 +242,6 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa // Store the beginning of descriptor as descriptor base address const UINT8* descriptor = (const UINT8*)intelImage.constData(); - UINT32 descriptorBegin = 0; - UINT32 descriptorEnd = FLASH_DESCRIPTOR_SIZE; // Check for buffer size to be greater or equal to descriptor region size if (intelImage.size() < FLASH_DESCRIPTOR_SIZE) { @@ -258,7 +251,7 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa // 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_UPPER_MAP* upperMap = (const FLASH_DESCRIPTOR_UPPER_MAP*)(descriptor + FLASH_DESCRIPTOR_UPPER_MAP_BASE); // Check sanity of base values if (descriptorMap->MasterBase > FLASH_DESCRIPTOR_MAX_BASE @@ -291,43 +284,47 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa return ERR_INVALID_FLASH_DESCRIPTOR; } + // Regions + QVector regions; + // ME region - QByteArray me; - UINT32 meBegin = 0; - UINT32 meEnd = 0; + REGION_INFO me; + me.type = Subtypes::MeRegion; + me.offset = 0; + me.length = 0; if (regionSection->MeLimit) { - meBegin = calculateRegionOffset(regionSection->MeBase); - meEnd = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); - me = intelImage.mid(meBegin, meEnd); - meEnd += meBegin; + me.offset = calculateRegionOffset(regionSection->MeBase); + me.length = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); + me.data = intelImage.mid(me.offset, me.length); + regions.append(me); } // BIOS region - QByteArray bios; - UINT32 biosBegin = 0; - UINT32 biosEnd = 0; + REGION_INFO bios; + bios.type = Subtypes::BiosRegion; + bios.offset = 0; + bios.length = 0; if (regionSection->BiosLimit) { - biosBegin = calculateRegionOffset(regionSection->BiosBase); - biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); + bios.offset = calculateRegionOffset(regionSection->BiosBase); + bios.length = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); // Check for Gigabyte specific descriptor map - if (biosEnd - biosBegin == (UINT32)intelImage.size()) { - if (!meEnd) { + if (bios.length == (UINT32)intelImage.size()) { + if (!me.offset) { msg(tr("parseIntelImage: can't determine BIOS region start from Gigabyte-specific descriptor")); 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 + // Use ME region end as BIOS region offset + bios.offset = me.offset + me.length; + bios.length = (UINT32)intelImage.size() - bios.offset; + bios.data = intelImage.mid(bios.offset, bios.length); } // Normal descriptor map else { - bios = intelImage.mid(biosBegin, biosEnd); - // Calculate biosEnd - biosEnd += biosBegin; + bios.data = intelImage.mid(bios.offset, bios.length); } + + regions.append(bios); } else { msg(tr("parseIntelImage: descriptor parsing failed, BIOS region not found in descriptor")); @@ -335,107 +332,148 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa } // GbE region - QByteArray gbe; - UINT32 gbeBegin = 0; - UINT32 gbeEnd = 0; + REGION_INFO gbe; + gbe.type = Subtypes::GbeRegion; + gbe.offset = 0; + gbe.length = 0; if (regionSection->GbeLimit) { - gbeBegin = calculateRegionOffset(regionSection->GbeBase); - gbeEnd = calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); - gbe = intelImage.mid(gbeBegin, gbeEnd); - gbeEnd += gbeBegin; + gbe.offset = calculateRegionOffset(regionSection->GbeBase); + gbe.length = calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); + gbe.data = intelImage.mid(gbe.offset, gbe.length); + regions.append(gbe); } // PDR region - QByteArray pdr; - UINT32 pdrBegin = 0; - UINT32 pdrEnd = 0; + REGION_INFO pdr; + pdr.type = Subtypes::PdrRegion; + pdr.offset = 0; + pdr.length = 0; if (regionSection->PdrLimit) { - pdrBegin = calculateRegionOffset(regionSection->PdrBase); - pdrEnd = calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); - pdr = intelImage.mid(pdrBegin, pdrEnd); - pdrEnd += pdrBegin; + pdr.offset = calculateRegionOffset(regionSection->PdrBase); + pdr.length = calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); + pdr.data = intelImage.mid(pdr.offset, pdr.length); + regions.append(pdr); } + // Reserved1 region + REGION_INFO reserved1; + reserved1.type = Subtypes::Reserved1Region; + reserved1.offset = 0; + reserved1.length = 0; + if (regionSection->Reserved1Limit && regionSection->Reserved1Base != 0xFFFF && regionSection->Reserved1Limit != 0xFFFF) { + reserved1.offset = calculateRegionOffset(regionSection->Reserved1Base); + reserved1.length = calculateRegionSize(regionSection->Reserved1Base, regionSection->Reserved1Limit); + reserved1.data = intelImage.mid(reserved1.offset, reserved1.length); + regions.append(reserved1); + } + + // Reserved2 region + REGION_INFO reserved2; + reserved2.type = Subtypes::Reserved2Region; + reserved2.offset = 0; + reserved2.length = 0; + if (regionSection->Reserved2Limit && regionSection->Reserved2Base != 0xFFFF && regionSection->Reserved2Limit != 0xFFFF) { + reserved2.offset = calculateRegionOffset(regionSection->Reserved2Base); + reserved2.length = calculateRegionSize(regionSection->Reserved2Base, regionSection->Reserved2Limit); + reserved2.data = intelImage.mid(reserved2.offset, reserved2.length); + regions.append(reserved2); + } + + // Reserved3 region + REGION_INFO reserved3; + reserved3.type = Subtypes::Reserved3Region; + reserved3.offset = 0; + reserved3.length = 0; + // EC region - QByteArray ec; - UINT32 ecBegin = 0; - UINT32 ecEnd = 0; + REGION_INFO ec; + ec.type = Subtypes::EcRegion; + ec.offset = 0; + ec.length = 0; + + // Reserved4 region + REGION_INFO reserved4; + reserved3.type = Subtypes::Reserved4Region; + reserved4.offset = 0; + reserved4.length = 0; + + // Check for EC and reserved region 4 only for v2 descriptor if (descriptorVersion == 2) { + if (regionSection->Reserved3Limit) { + reserved3.offset = calculateRegionOffset(regionSection->Reserved3Base); + reserved3.length = calculateRegionSize(regionSection->Reserved3Base, regionSection->Reserved3Limit); + reserved3.data = intelImage.mid(reserved3.offset, reserved3.length); + regions.append(reserved3); + } + if (regionSection->EcLimit) { - pdrBegin = calculateRegionOffset(regionSection->EcBase); - pdrEnd = calculateRegionSize(regionSection->EcBase, regionSection->EcLimit); - pdr = intelImage.mid(ecBegin, ecEnd); - ecEnd += ecBegin; + ec.offset = calculateRegionOffset(regionSection->EcBase); + ec.length = calculateRegionSize(regionSection->EcBase, regionSection->EcLimit); + ec.data = intelImage.mid(ec.offset, ec.length); + regions.append(ec); + } + + if (regionSection->Reserved4Limit) { + reserved4.offset = calculateRegionOffset(regionSection->Reserved4Base); + reserved4.length = calculateRegionSize(regionSection->Reserved4Base, regionSection->Reserved4Limit); + reserved4.data = intelImage.mid(reserved4.offset, reserved4.length); + regions.append(reserved4); } } - // 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; - } - if (hasIntersection(descriptorBegin, descriptorEnd, meBegin, meEnd)) { - msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with ME region")); - return ERR_INVALID_FLASH_DESCRIPTOR; - } - if (hasIntersection(descriptorBegin, descriptorEnd, biosBegin, biosEnd)) { - msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with BIOS region")); - return ERR_INVALID_FLASH_DESCRIPTOR; - } - if (hasIntersection(descriptorBegin, descriptorEnd, pdrBegin, pdrEnd)) { - 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; - } - if (hasIntersection(gbeBegin, gbeEnd, biosBegin, biosEnd)) { - msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with BIOS region")); - return ERR_INVALID_FLASH_DESCRIPTOR; - } - if (hasIntersection(gbeBegin, gbeEnd, pdrBegin, pdrEnd)) { - 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; - } - if (hasIntersection(meBegin, meEnd, pdrBegin, pdrEnd)) { - 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; - } + // Sort regions in ascending order + qSort(regions); + // Check for intersections and paddings between regions + REGION_INFO region; + // Check intersection with the descriptor + if (regions.first().offset < FLASH_DESCRIPTOR_SIZE) { + msg(tr("parseIntelImage: %1 region has intersection with flash descriptor").arg(itemSubtypeToQString(Types::Region, regions.first().type)), index); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + // Check for padding between descriptor and the first region + else if (regions.first().offset > FLASH_DESCRIPTOR_SIZE) { + region.offset = FLASH_DESCRIPTOR_SIZE; + region.length = regions.first().offset - FLASH_DESCRIPTOR_SIZE; + region.data = intelImage.mid(region.offset, region.length); + region.type = getPaddingType(region.data); + regions.prepend(region); + } + // Check for intersections/paddings between regions + for (int i = 1; i < regions.count(); i++) { + UINT32 previousRegionEnd = regions[i-1].offset + regions[i-1].length; + // Check that current region is fully present in the image + if (regions[i].offset + regions[i].length > (UINT32)intelImage.size()) { + msg(tr("parseIntelImage: %1 region is located outside of opened image, if your system uses dual-chip storage, please append another part to the opened image") + .arg(itemSubtypeToQString(Types::Region, regions[i].type)), index); + return ERR_TRUNCATED_IMAGE; + } + + // Check for intersection with previous region + if (regions[i].offset < previousRegionEnd) { + msg(tr("parseIntelImage: %1 region has intersection with %2 region") + .arg(itemSubtypeToQString(Types::Region, regions[i].type)) + .arg(itemSubtypeToQString(Types::Region, regions[i-1].type)), index); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + // Check for padding between current and previous regions + else if (regions[i].offset > previousRegionEnd) { + region.offset = previousRegionEnd; + region.length = regions[i].offset - previousRegionEnd; + region.data = intelImage.mid(region.offset, region.length); + region.type = getPaddingType(region.data); + regions.insert(i - 1, region); + } + } + // Check for padding after the last region + if (regions.last().offset + regions.last().length < (UINT32)intelImage.size()) { + region.offset = regions.last().offset + regions.last().length; + region.length = intelImage.size() - region.offset; + region.data = intelImage.mid(region.offset, region.length); + region.type = getPaddingType(region.data); + regions.append(region); + } + // Region map is consistent // Intel image @@ -459,24 +497,11 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa QByteArray body = intelImage.left(FLASH_DESCRIPTOR_SIZE); name = tr("Descriptor region"); info = tr("Full size: %1h (%2)").hexarg(FLASH_DESCRIPTOR_SIZE).arg(FLASH_DESCRIPTOR_SIZE); - - // Check regions presence once again - QVector offsets; - if (regionSection->GbeLimit) { - offsets.append(gbeBegin); - info += tr("\nGbE region offset: %1h").hexarg(gbeBegin + parentOffset); - } - if (regionSection->MeLimit) { - offsets.append(meBegin); - info += tr("\nME region offset: %1h").hexarg(meBegin + parentOffset); - } - if (regionSection->BiosLimit) { - offsets.append(biosBegin); - info += tr("\nBIOS region offset: %1h").hexarg(biosBegin + parentOffset); - } - if (regionSection->PdrLimit) { - offsets.append(pdrBegin); - info += tr("\nPDR region offset: %1h").hexarg(pdrBegin + parentOffset); + + // Add offsets of actual regions + for (int i = 0; i < regions.count(); i++) { + if (regions[i].type != Subtypes::ZeroPadding && regions[i].type != Subtypes::OnePadding && regions[i].type != Subtypes::DataPadding) + info += tr("\n%1 region offset: %2h").arg(itemSubtypeToQString(Types::Region, regions[i].type)).hexarg(regions[i].offset + parentOffset); } // Region access settings @@ -555,93 +580,63 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa } // Add descriptor tree item - model->addItem(Types::Region, Subtypes::DescriptorRegion, name, QString(), info, QByteArray(), body, TRUE, parsingDataToQByteArray(pdata), index); - - // Sort regions in ascending order - qSort(offsets); - + QModelIndex regionIndex = model->addItem(Types::Region, Subtypes::DescriptorRegion, name, QString(), info, QByteArray(), body, TRUE, parsingDataToQByteArray(pdata), index); + // Parse regions - UINT8 result = 0; - for (int i = 0; i < offsets.count(); i++) { - // Parse GbE region - if (offsets.at(i) == gbeBegin) { - QModelIndex gbeIndex; - result = parseGbeRegion(gbe, gbeBegin, index, gbeIndex); + UINT8 result = ERR_SUCCESS; + UINT8 parseResult = ERR_SUCCESS; + Q_FOREACH(region, regions) { + switch (region.type) { + case Subtypes::BiosRegion: + result = parseBiosRegion(region.data, region.offset, index, regionIndex); + break; + case Subtypes::MeRegion: + result = parseMeRegion(region.data, region.offset, index, regionIndex); + break; + case Subtypes::GbeRegion: + result = parseGbeRegion(region.data, region.offset, index, regionIndex); + break; + case Subtypes::PdrRegion: + result = parsePdrRegion(region.data, region.offset, index, regionIndex); + break; + case Subtypes::Reserved1Region: + case Subtypes::Reserved2Region: + case Subtypes::Reserved3Region: + case Subtypes::EcRegion: + case Subtypes::Reserved4Region: + result = parseGeneralRegion(region.type, region.data, region.offset, index, regionIndex); + break; + case Subtypes::ZeroPadding: + case Subtypes::OnePadding: + case Subtypes::DataPadding: { + // Add padding between regions + QByteArray padding = intelImage.mid(region.offset, region.length); + + // Get parent's parsing data + PARSING_DATA pdata = parsingDataFromQModelIndex(index); + + // Get info + name = tr("Padding"); + info = tr("Full size: %1h (%2)") + .hexarg(padding.size()).arg(padding.size()); + + // Construct parsing data + pdata.offset = parentOffset + region.offset; + + // Add tree item + regionIndex = model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index); + result = ERR_SUCCESS; + } break; + default: + msg(tr("parseIntelImage: region of unknown type found"), index); + result = ERR_INVALID_FLASH_DESCRIPTOR; } - // Parse ME region - else if (offsets.at(i) == meBegin) { - QModelIndex meIndex; - result = parseMeRegion(me, meBegin, index, meIndex); - } - // Parse BIOS region - else if (offsets.at(i) == biosBegin) { - QModelIndex biosIndex; - result = parseBiosRegion(bios, biosBegin, index, biosIndex); - } - // Parse PDR region - else if (offsets.at(i) == pdrBegin) { - QModelIndex pdrIndex; - result = parsePdrRegion(pdr, pdrBegin, index, pdrIndex); - } - // Parse EC region - else if (descriptorVersion == 2 && offsets.at(i) == ecBegin) { - QModelIndex ecIndex; - result = parseEcRegion(ec, ecBegin, index, ecIndex); - } - if (result) - return result; + // Store the first failed result as a final result + if (!parseResult && result) + parseResult = 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 parent's parsing data - PARSING_DATA pdata = parsingDataFromQModelIndex(index); - - // Get info - name = tr("Padding"); - info = tr("Full size: %1h (%2)") - .hexarg(padding.size()).arg(padding.size()); - - // Construct parsing data - pdata.offset = IntelDataEnd; - - // Add tree item - model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index); - } - - // Add offsets - addOffsetsRecursive(index); - - // Check if the last VTF is found - if (!lastVtf.isValid()) { - msg(tr("parseIntelImage: not a single Volume Top File is found, the image may be corrupted"), index); - } - else { - return performSecondPass(index); - } - - return ERR_SUCCESS; + return parseResult; } STATUS FfsParser::parseGbeRegion(const QByteArray & gbe, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) @@ -774,25 +769,25 @@ STATUS FfsParser::parsePdrRegion(const QByteArray & pdr, const UINT32 parentOffs return ERR_SUCCESS; } -STATUS FfsParser::parseEcRegion(const QByteArray & ec, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +STATUS FfsParser::parseGeneralRegion(const UINT8 subtype, const QByteArray & region, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) { // Check sanity - if (ec.isEmpty()) + if (region.isEmpty()) return ERR_EMPTY_REGION; // Get parent's parsing data PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Get info - QString name = tr("EC region"); + QString name = tr("%1 region").arg(itemSubtypeToQString(Types::Region, subtype)); QString info = tr("Full size: %1h (%2)"). - hexarg(ec.size()).arg(ec.size()); + hexarg(region.size()).arg(region.size()); // Construct parsing data pdata.offset += parentOffset; // Add tree item - index = model->addItem(Types::Region, Subtypes::EcRegion, name, QString(), info, QByteArray(), ec, TRUE, parsingDataToQByteArray(pdata), parent); + index = model->addItem(Types::Region, subtype, name, QString(), info, QByteArray(), region, TRUE, parsingDataToQByteArray(pdata), parent); return ERR_SUCCESS; } @@ -1190,7 +1185,7 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare return ERR_SUCCESS; } -STATUS FfsParser::findNextVolume(const QModelIndex index, const QByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset) +STATUS FfsParser::findNextVolume(const QModelIndex & index, const QByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset) { int nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset); if (nextIndex < EFI_FV_SIGNATURE_OFFSET) @@ -1745,7 +1740,7 @@ STATUS FfsParser::parsePadFileBody(const QModelIndex & index) return ERR_SUCCESS; } -STATUS FfsParser::parseSections(const QByteArray & sections, const QModelIndex & index) +STATUS FfsParser::parseSections(const QByteArray & sections, const QModelIndex & index, const bool preparse) { // Sanity check if (!index.isValid()) @@ -1759,6 +1754,7 @@ STATUS FfsParser::parseSections(const QByteArray & sections, const QModelIndex & UINT32 headerSize = model->header(index).size(); UINT32 sectionOffset = 0; + STATUS result = ERR_SUCCESS; while (sectionOffset < bodySize) { // Get section size UINT32 sectionSize = getSectionSize(sections, sectionOffset, pdata.ffsVersion); @@ -1773,27 +1769,36 @@ STATUS FfsParser::parseSections(const QByteArray & sections, const QModelIndex & // Constuct parsing data pdata.offset += headerSize + sectionOffset; - // Add tree item - QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, tr("Non-UEFI data"), "", info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index); - - // Show message - msg(tr("parseSections: non-UEFI data found in sections area"), dataIndex); + // Final parsing + if (!preparse) { + // Add tree item + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, tr("Non-UEFI data"), "", info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index); + // Show message + msg(tr("parseSections: non-UEFI data found in sections area"), dataIndex); + } + // Preparsing + else { + return ERR_INVALID_SECTION; + } break; // Exit from parsing loop } // Parse section header QModelIndex sectionIndex; - STATUS result = parseSectionHeader(sections.mid(sectionOffset, sectionSize), headerSize + sectionOffset, index, sectionIndex); - if (result) - msg(tr("parseSections: section header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index); - + result = parseSectionHeader(sections.mid(sectionOffset, sectionSize), headerSize + sectionOffset, index, sectionIndex); + if (result) { + if (!preparse) + msg(tr("parseSections: section header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index); + else + return ERR_INVALID_SECTION; + } // Move to next section sectionOffset += sectionSize; sectionOffset = ALIGN4(sectionOffset); } - //Parse bodies + //Parse bodies, will be skipped on preparse phase for (int i = 0; i < model->rowCount(index); i++) { QModelIndex current = index.child(i, 0); switch (model->type(current)) { @@ -2013,7 +2018,7 @@ STATUS FfsParser::parseGuidedSectionHeader(const QByteArray & section, const UIN // Check certificate type if (certType == WIN_CERT_TYPE_EFI_GUID) { - additionalInfo += tr("\nCertificate type: UEFI").hexarg2(certType, 4); + additionalInfo += tr("\nCertificate type: UEFI"); // Get certificate GUID const WIN_CERTIFICATE_UEFI_GUID* winCertificateUefiGuid = (const WIN_CERTIFICATE_UEFI_GUID*)(section.constData() + nextHeaderOffset); @@ -2256,7 +2261,8 @@ STATUS FfsParser::parseCompressedSectionBody(const QModelIndex & index) // Decompress section QByteArray decompressed; - STATUS result = decompress(model->body(index), algorithm, decompressed); + QByteArray efiDecompressed; + STATUS result = decompress(model->body(index), algorithm, decompressed, efiDecompressed); if (result) { msg(tr("parseCompressedSectionBody: decompression failed with error \"%1\"").arg(errorCodeToQString(result)), index); return ERR_SUCCESS; @@ -2272,6 +2278,22 @@ STATUS FfsParser::parseCompressedSectionBody(const QModelIndex & index) model->addInfo(index, tr("\nActual decompressed size: %1h (%2)").hexarg(decompressed.size()).arg(decompressed.size())); } + // Check for undecided compression algorithm, this is a special case + if (algorithm == COMPRESSION_ALGORITHM_UNDECIDED) { + // Try preparse of sections decompressed with Tiano algorithm + if (ERR_SUCCESS == parseSections(decompressed, index, true)) { + algorithm = COMPRESSION_ALGORITHM_TIANO; + } + // Try preparse of sections decompressed with EFI 1.1 algorithm + else if (ERR_SUCCESS == parseSections(efiDecompressed, index, true)) { + algorithm = COMPRESSION_ALGORITHM_EFI11; + decompressed = efiDecompressed; + } + else { + msg(tr("parseCompressedSectionBody: can't guess the correct decompression algorithm, both preparse steps are failed"), index); + } + } + // Add info model->addInfo(index, tr("\nCompression algorithm: %1").arg(compressionTypeToQString(algorithm))); @@ -2302,24 +2324,33 @@ STATUS FfsParser::parseGuidedSectionBody(const QModelIndex & index) UINT8 algorithm = COMPRESSION_ALGORITHM_NONE; // Tiano compressed section if (QByteArray((const char*)&guid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_TIANO) { + QByteArray efiDecompressed; algorithm = EFI_STANDARD_COMPRESSION; - STATUS result = decompress(model->body(index), algorithm, processed); + STATUS result = decompress(model->body(index), algorithm, processed, efiDecompressed); if (result) { parseCurrentSection = false; msg(tr("parseGuidedSectionBody: decompression failed with error \"%1\"").arg(errorCodeToQString(result)), index); return ERR_SUCCESS; } - if (algorithm == COMPRESSION_ALGORITHM_TIANO) { - info += tr("\nCompression algorithm: Tiano"); - info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); + // Check for undecided compression algorithm, this is a special case + if (algorithm == COMPRESSION_ALGORITHM_UNDECIDED) { + // Try preparse of sections decompressed with Tiano algorithm + if (ERR_SUCCESS == parseSections(processed, index, true)) { + algorithm = COMPRESSION_ALGORITHM_TIANO; + } + // Try preparse of sections decompressed with EFI 1.1 algorithm + else if (ERR_SUCCESS == parseSections(efiDecompressed, index, true)) { + algorithm = COMPRESSION_ALGORITHM_EFI11; + processed = efiDecompressed; + } + else { + msg(tr("parseGuidedSectionBody: can't guess the correct decompression algorithm, both preparse steps are failed"), index); + } } - else if (algorithm == COMPRESSION_ALGORITHM_EFI11) { - info += tr("\nCompression algorithm: EFI 1.1"); - info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); - } - else - info += tr("\nCompression type: unknown"); + + info += tr("\nCompression algorithm: %1").arg(compressionTypeToQString(algorithm)); + info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); } // LZMA compressed section else if (QByteArray((const char*)&guid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_LZMA) { diff --git a/common/ffsparser.h b/common/ffsparser.h index 7be8f09..3b59400 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -1,6 +1,6 @@ /* ffsparser.h -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -26,6 +26,13 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "utility.h" #include "peimage.h" #include "parsingdata.h" +#include "types.h" +#include "treemodel.h" +#include "descriptor.h" +#include "ffs.h" +#include "gbe.h" +#include "me.h" +#include "fit.h" class TreeModel; @@ -44,7 +51,7 @@ public: void clearMessages(); // Firmware image parsing - STATUS parseImageFile(const QByteArray & imageFile, const QModelIndex & index); + STATUS parse(const QByteArray &buffer); STATUS parseRawArea(const QByteArray & data, const QModelIndex & index); STATUS parseVolumeHeader(const QByteArray & volume, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parseVolumeBody(const QModelIndex & index); @@ -67,11 +74,11 @@ private: STATUS parseMeRegion(const QByteArray & me, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parseBiosRegion(const QByteArray & bios, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parsePdrRegion(const QByteArray & pdr, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); - STATUS parseEcRegion(const QByteArray & ec, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); + STATUS parseGeneralRegion(const UINT8 subtype, const QByteArray & region, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parsePadFileBody(const QModelIndex & index); STATUS parseVolumeNonUefiData(const QByteArray & data, const UINT32 parentOffset, const QModelIndex & index); - STATUS parseSections(const QByteArray & sections, const QModelIndex & index); + STATUS parseSections(const QByteArray & sections, const QModelIndex & index, const bool preparse = false); STATUS parseCommonSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parseCompressedSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); @@ -91,21 +98,18 @@ private: UINT8 getPaddingType(const QByteArray & padding); STATUS parseAprioriRawSection(const QByteArray & body, QString & parsed); - STATUS findNextVolume(const QModelIndex index, const QByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); + STATUS findNextVolume(const QModelIndex & index, const QByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); STATUS getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize); UINT32 getFileSize(const QByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion); UINT32 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, const UINT8 ffsVersion); + STATUS performFirstPass(const QByteArray & imageFile, QModelIndex & index); STATUS performSecondPass(const QModelIndex & index); STATUS addOffsetsRecursive(const QModelIndex & index); STATUS addMemoryAddressesRecursive(const QModelIndex & index, const UINT32 diff); - // Internal operations - BOOLEAN hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2); - // Message helper void msg(const QString & message, const QModelIndex &index = QModelIndex()); - }; #endif diff --git a/common/types.cpp b/common/types.cpp index ca97a5a..b9d741a 100644 --- a/common/types.cpp +++ b/common/types.cpp @@ -1,6 +1,6 @@ /* types.cpp -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -21,16 +21,24 @@ QString regionTypeToQString(const UINT8 type) { case Subtypes::DescriptorRegion: return QObject::tr("Descriptor"); - case Subtypes::GbeRegion: - return QObject::tr("GbE"); - case Subtypes::MeRegion: - return QObject::tr("ME"); case Subtypes::BiosRegion: return QObject::tr("BIOS"); + case Subtypes::MeRegion: + return QObject::tr("ME"); + case Subtypes::GbeRegion: + return QObject::tr("GbE"); case Subtypes::PdrRegion: return QObject::tr("PDR"); + case Subtypes::Reserved1Region: + return QObject::tr("Reserved1"); + case Subtypes::Reserved2Region: + return QObject::tr("Reserved2"); + case Subtypes::Reserved3Region: + return QObject::tr("Reserved3"); case Subtypes::EcRegion: return QObject::tr("EC"); + case Subtypes::Reserved4Region: + return QObject::tr("Reserved4"); default: return QObject::tr("Unknown"); }; @@ -124,6 +132,8 @@ QString compressionTypeToQString(const UINT8 algorithm) return QObject::tr("EFI 1.1"); case COMPRESSION_ALGORITHM_TIANO: return QObject::tr("Tiano"); + case COMPRESSION_ALGORITHM_UNDECIDED: + return QObject::tr("Undecided Tiano/EFI 1.1"); case COMPRESSION_ALGORITHM_LZMA: return QObject::tr("LZMA"); case COMPRESSION_ALGORITHM_IMLZMA: diff --git a/common/types.h b/common/types.h index 0451118..b204f68 100644 --- a/common/types.h +++ b/common/types.h @@ -1,6 +1,6 @@ /* types.h -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -66,12 +66,16 @@ namespace Subtypes { }; enum RegionSubtypes { - DescriptorRegion = 100, - GbeRegion, - MeRegion, + DescriptorRegion = 0, BiosRegion, + MeRegion, + GbeRegion, PdrRegion, - EcRegion + Reserved1Region, + Reserved2Region, + Reserved3Region, + EcRegion, + Reserved4Region }; enum PaddingSubtypes { diff --git a/common/utility.cpp b/common/utility.cpp index 3589a1c..d9aacd0 100644 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -1,6 +1,6 @@ /* utility.cpp -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -151,11 +151,12 @@ UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length) } // Compression routines -STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArray & decompressedData) +STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArray & decompressedData, QByteArray & efiDecompressedData) { const UINT8* data; UINT32 dataSize; UINT8* decompressed; + UINT8* efiDecompressed; UINT32 decompressedSize = 0; UINT8* scratch; UINT32 scratchSize = 0; @@ -167,7 +168,7 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr decompressedData = compressedData; algorithm = COMPRESSION_ALGORITHM_NONE; return ERR_SUCCESS; - case EFI_STANDARD_COMPRESSION: + case EFI_STANDARD_COMPRESSION: { // Set default algorithm to unknown algorithm = COMPRESSION_ALGORITHM_UNKNOWN; @@ -185,34 +186,45 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr return ERR_STANDARD_DECOMPRESSION_FAILED; // Allocate memory - try { - decompressed = new UINT8[decompressedSize]; - scratch = new UINT8[scratchSize]; - } - catch (std::bad_alloc) { + decompressed = (UINT8*)malloc(decompressedSize); + efiDecompressed = (UINT8*)malloc(decompressedSize); + scratch = (UINT8*)malloc(scratchSize); + if (!decompressed || !efiDecompressed || !scratch) { + if (decompressed) free(decompressed); + if (efiDecompressed) free(efiDecompressed); + if (scratch) free(scratch); return ERR_STANDARD_DECOMPRESSION_FAILED; } - // Decompress section data + // Decompress section data using both algorithms + STATUS result = ERR_SUCCESS; + // Try Tiano + STATUS TianoResult = TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize); + // Try EFI 1.1 + STATUS EfiResult = EfiDecompress(data, dataSize, efiDecompressed, decompressedSize, scratch, scratchSize); - //TODO: separate EFI1.1 from Tiano another way - // Try Tiano decompression first - if (ERR_SUCCESS != TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) { - // Not Tiano, try EFI 1.1 - if (ERR_SUCCESS != EfiDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) { - delete[] decompressed; - delete[] scratch; - return ERR_STANDARD_DECOMPRESSION_FAILED; - } - else algorithm = COMPRESSION_ALGORITHM_EFI11; + if (EfiResult == ERR_SUCCESS && TianoResult == ERR_SUCCESS) { // Both decompressions are OK + algorithm = COMPRESSION_ALGORITHM_UNDECIDED; + decompressedData = QByteArray((const char*)decompressed, decompressedSize); + efiDecompressedData = QByteArray((const char*)efiDecompressed, decompressedSize); + } + else if (TianoResult == ERR_SUCCESS) { // Only Tiano is OK + algorithm = COMPRESSION_ALGORITHM_TIANO; + decompressedData = QByteArray((const char*)decompressed, decompressedSize); + } + else if (EfiResult == ERR_SUCCESS) { // Only EFI 1.1 is OK + algorithm = COMPRESSION_ALGORITHM_EFI11; + decompressedData = QByteArray((const char*)efiDecompressed, decompressedSize); + } + else { // Both decompressions failed + result = ERR_STANDARD_DECOMPRESSION_FAILED; } - else algorithm = COMPRESSION_ALGORITHM_TIANO; - decompressedData = QByteArray((const char*)decompressed, decompressedSize); - - delete[] decompressed; - delete[] scratch; - return ERR_SUCCESS; + free(decompressed); + free(efiDecompressed); + free(scratch); + return result; + } case EFI_CUSTOMIZED_COMPRESSION: // Set default algorithm to unknown algorithm = COMPRESSION_ALGORITHM_UNKNOWN; @@ -226,10 +238,8 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; // Allocate memory - try { - decompressed = new UINT8[decompressedSize]; - } - catch (std::bad_alloc) { + decompressed = (UINT8*)malloc(decompressedSize); + if (!decompressed) { return ERR_STANDARD_DECOMPRESSION_FAILED; } @@ -241,13 +251,13 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr // Get info again if (ERR_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) { - delete[] decompressed; + free(decompressed); return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; } // Decompress section data again if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed)) { - delete[] decompressed; + free(decompressed); return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; } else { @@ -260,7 +270,7 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr decompressedData = QByteArray((const char*)decompressed, decompressedSize); } - delete[] decompressed; + free(decompressed); return ERR_SUCCESS; default: algorithm = COMPRESSION_ALGORITHM_UNKNOWN; diff --git a/common/utility.h b/common/utility.h index ba44d04..ca9582c 100644 --- a/common/utility.h +++ b/common/utility.h @@ -1,6 +1,6 @@ /* utility.h -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -29,7 +29,7 @@ QByteArray parsingDataToQByteArray(const PARSING_DATA & pdata); extern QString errorCodeToQString(UINT8 errorCode); // Decompression routine -extern STATUS decompress(const QByteArray & compressed, UINT8 & algorithm, QByteArray & decompressed); +extern STATUS decompress(const QByteArray & compressed, UINT8 & algorithm, QByteArray & decompressed, QByteArray & efiDecompressed = QByteArray()); // Compression routine //STATUS compress(const QByteArray & decompressed, QByteArray & compressed, const UINT8 & algorithm);