From 1f0a80d035aaec97f66fd9e4c677c59afa18439c Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Fri, 13 Mar 2015 07:48:53 +0100 Subject: [PATCH] First commit of the new_engine branch - new ffsParser is done, supports FFSv3, non-UEFI data everywhere, fixed files, offsets for uncompressed data and many other things - command-line utilities are removed until the end of new engine's development --- UEFIExtract/uefiextract.cpp | 49 - UEFIExtract/uefiextract.h | 43 - UEFIExtract/uefiextract.pro | 43 - UEFIExtract/uefiextract_main.cpp | 60 - UEFIFind/uefifind.cpp | 160 -- UEFIFind/uefifind.h | 52 - UEFIFind/uefifind.pro | 43 - UEFIFind/uefifind_main.cpp | 72 - UEFIPatch/patches.txt | 42 - UEFIPatch/uefipatch.cpp | 149 -- UEFIPatch/uefipatch.h | 45 - UEFIPatch/uefipatch.pro | 43 - UEFIPatch/uefipatch_main.cpp | 76 - basetypes.h | 3 +- ffs.cpp | 96 +- ffs.h | 6 - ffsengine.cpp | 4208 ------------------------------ ffsengine.h | 153 -- ffsparser.cpp | 2549 ++++++++++++++++++ ffsparser.h | 188 ++ peimage.cpp | 2 +- treeitem.cpp | 9 +- treeitem.h | 6 +- treemodel.cpp | 32 +- treemodel.h | 7 +- types.h | 21 - uefitool.cpp | 137 +- uefitool.h | 5 +- uefitool.pro | 6 +- utility.cpp | 234 ++ utility.h | 26 + 31 files changed, 3121 insertions(+), 5444 deletions(-) delete mode 100644 UEFIExtract/uefiextract.cpp delete mode 100644 UEFIExtract/uefiextract.h delete mode 100644 UEFIExtract/uefiextract.pro delete mode 100644 UEFIExtract/uefiextract_main.cpp delete mode 100644 UEFIFind/uefifind.cpp delete mode 100644 UEFIFind/uefifind.h delete mode 100644 UEFIFind/uefifind.pro delete mode 100644 UEFIFind/uefifind_main.cpp delete mode 100644 UEFIPatch/patches.txt delete mode 100644 UEFIPatch/uefipatch.cpp delete mode 100644 UEFIPatch/uefipatch.h delete mode 100644 UEFIPatch/uefipatch.pro delete mode 100644 UEFIPatch/uefipatch_main.cpp delete mode 100644 ffsengine.cpp delete mode 100644 ffsengine.h create mode 100644 ffsparser.cpp create mode 100644 ffsparser.h create mode 100644 utility.cpp create mode 100644 utility.h diff --git a/UEFIExtract/uefiextract.cpp b/UEFIExtract/uefiextract.cpp deleted file mode 100644 index 1fd2557..0000000 --- a/UEFIExtract/uefiextract.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* uefiextract.cpp - -Copyright (c) 2014, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -*/ - -#include "uefiextract.h" - -UEFIExtract::UEFIExtract(QObject *parent) : - QObject(parent) -{ - ffsEngine = new FfsEngine(this); -} - -UEFIExtract::~UEFIExtract() -{ - delete ffsEngine; -} - -UINT8 UEFIExtract::init(const QString & path) -{ - fileInfo = QFileInfo(path); - - if (!fileInfo.exists()) - return ERR_FILE_OPEN; - - QFile inputFile; - inputFile.setFileName(path); - - if (!inputFile.open(QFile::ReadOnly)) - return ERR_FILE_OPEN; - - QByteArray buffer = inputFile.readAll(); - inputFile.close(); - - return ffsEngine->parseImageFile(buffer); -} - -UINT8 UEFIExtract::extract(QString guid) -{ - return ffsEngine->dump(ffsEngine->treeModel()->index(0, 0), fileInfo.fileName().append(".dump"), guid); -} \ No newline at end of file diff --git a/UEFIExtract/uefiextract.h b/UEFIExtract/uefiextract.h deleted file mode 100644 index 9cf9c40..0000000 --- a/UEFIExtract/uefiextract.h +++ /dev/null @@ -1,43 +0,0 @@ -/* uefiextract.h - -Copyright (c) 2014, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -*/ - -#ifndef __UEFIEXTRACT_H__ -#define __UEFIEXTRACT_H__ - -#include -#include -#include -#include -#include - -#include "../basetypes.h" -#include "../ffsengine.h" - -class UEFIExtract : public QObject -{ - Q_OBJECT - -public: - explicit UEFIExtract(QObject *parent = 0); - ~UEFIExtract(); - - UINT8 init(const QString & path); - UINT8 extract(QString guid = QString()); - -private: - FfsEngine* ffsEngine; - QFileInfo fileInfo; - -}; - -#endif diff --git a/UEFIExtract/uefiextract.pro b/UEFIExtract/uefiextract.pro deleted file mode 100644 index bc72ff7..0000000 --- a/UEFIExtract/uefiextract.pro +++ /dev/null @@ -1,43 +0,0 @@ -QT += core -QT -= gui - -TARGET = UEFIExtract -TEMPLATE = app -CONFIG += console -CONFIG -= app_bundle -DEFINES += _CONSOLE - -SOURCES += uefiextract_main.cpp \ - uefiextract.cpp \ - ../types.cpp \ - ../descriptor.cpp \ - ../ffs.cpp \ - ../ffsengine.cpp \ - ../peimage.cpp \ - ../treeitem.cpp \ - ../treemodel.cpp \ - ../LZMA/LzmaCompress.c \ - ../LZMA/LzmaDecompress.c \ - ../LZMA/SDK/C/LzFind.c \ - ../LZMA/SDK/C/LzmaDec.c \ - ../LZMA/SDK/C/LzmaEnc.c \ - ../Tiano/EfiTianoDecompress.c \ - ../Tiano/EfiTianoCompress.c \ - ../Tiano/EfiTianoCompressLegacy.c - -HEADERS += uefiextract.h \ - ../basetypes.h \ - ../descriptor.h \ - ../gbe.h \ - ../me.h \ - ../ffs.h \ - ../peimage.h \ - ../types.h \ - ../ffsengine.h \ - ../treeitem.h \ - ../treemodel.h \ - ../LZMA/LzmaCompress.h \ - ../LZMA/LzmaDecompress.h \ - ../Tiano/EfiTianoDecompress.h \ - ../Tiano/EfiTianoCompress.h - diff --git a/UEFIExtract/uefiextract_main.cpp b/UEFIExtract/uefiextract_main.cpp deleted file mode 100644 index 83b8c29..0000000 --- a/UEFIExtract/uefiextract_main.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* uefiextract_main.cpp - -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -*/ -#include -#include -#include -#include -#include "uefiextract.h" - -int main(int argc, char *argv[]) -{ - QCoreApplication a(argc, argv); - a.setOrganizationName("CodeRush"); - a.setOrganizationDomain("coderush.me"); - a.setApplicationName("UEFIExtract"); - - UEFIExtract w; - UINT8 result = ERR_SUCCESS; - UINT32 returned = 0; - - if (a.arguments().length() > 32) { - std::cout << "Too many arguments" << std::endl; - return 1; - } - - if (a.arguments().length() > 1 ) { - if (w.init(a.arguments().at(1))) - return 1; - - if (a.arguments().length() == 2) { - result = w.extract(); - if (result) - return 2; - } - else { - for (int i = 2; i < a.arguments().length(); i++) { - result = w.extract(a.arguments().at(i)); - if (result) - returned |= (1 << (i - 1)); - } - return returned; - } - - } - else { - std::cout << "UEFIExtract 0.4.4" << std::endl << std::endl << - "Usage: uefiextract imagefile [FileGUID_1 FileGUID_2 ... FileGUID_31]" << std::endl << - "Returned value is a bit mask where 0 on position N meant File with GUID_N was found and unpacked, 1 otherwise" << std::endl; - return 1; - } -} diff --git a/UEFIFind/uefifind.cpp b/UEFIFind/uefifind.cpp deleted file mode 100644 index 2adfafb..0000000 --- a/UEFIFind/uefifind.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* uefifind.cpp - -Copyright (c) 2014, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -*/ - -#include "uefifind.h" - -UEFIFind::UEFIFind(QObject *parent) : - QObject(parent) -{ - ffsEngine = new FfsEngine(this); - model = ffsEngine->treeModel(); - initDone = false; -} - -UEFIFind::~UEFIFind() -{ - model = NULL; - delete ffsEngine; -} - -UINT8 UEFIFind::init(const QString & path) -{ - UINT8 result; - - fileInfo = QFileInfo(path); - - if (!fileInfo.exists()) - return ERR_FILE_OPEN; - - QFile inputFile; - inputFile.setFileName(path); - - if (!inputFile.open(QFile::ReadOnly)) - return ERR_FILE_OPEN; - - QByteArray buffer = inputFile.readAll(); - inputFile.close(); - - result = ffsEngine->parseImageFile(buffer); - if (result) - return result; - - initDone = true; - return ERR_SUCCESS; -} - -QString UEFIFind::guidToQString(const UINT8* guid) -{ - const UINT32 u32 = *(const UINT32*)guid; - const UINT16 u16_1 = *(const UINT16*)(guid + 4); - const UINT16 u16_2 = *(const UINT16*)(guid + 6); - const UINT8 u8_1 = *(const UINT8*)(guid + 8); - const UINT8 u8_2 = *(const UINT8*)(guid + 9); - const UINT8 u8_3 = *(const UINT8*)(guid + 10); - const UINT8 u8_4 = *(const UINT8*)(guid + 11); - const UINT8 u8_5 = *(const UINT8*)(guid + 12); - const UINT8 u8_6 = *(const UINT8*)(guid + 13); - const UINT8 u8_7 = *(const UINT8*)(guid + 14); - const UINT8 u8_8 = *(const UINT8*)(guid + 15); - - return QString("%1-%2-%3-%4%5-%6%7%8%9%10%11").hexarg2(u32, 8).hexarg2(u16_1, 4).hexarg2(u16_2, 4).hexarg2(u8_1, 2).hexarg2(u8_2, 2) - .hexarg2(u8_3, 2).hexarg2(u8_4, 2).hexarg2(u8_5, 2).hexarg2(u8_6, 2).hexarg2(u8_7, 2).hexarg2(u8_8, 2); -} - -UINT8 UEFIFind::find(const UINT8 mode, const bool count, const QString & hexPattern, QString & result) -{ - QModelIndex root = model->index(0, 0); - QSet > files; - - result.clear(); - - UINT8 returned = findFileRecursive(root, hexPattern, mode, files); - if (returned) - return returned; - - if (count) { - if (files.count()) - result.append(QString("%1\n").arg(files.count())); - return ERR_SUCCESS; - } - - QPair indexes; - Q_FOREACH(indexes, files) { - QByteArray data = model->header(indexes.first).left(16); - result.append(guidToQString((const UINT8*)data.constData())); - - // Special case of freeform subtype GUID files - if (indexes.second.isValid() && model->subtype(indexes.second) == EFI_SECTION_FREEFORM_SUBTYPE_GUID) { - data = model->header(indexes.second).left(sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION)); - result.append(" ").append(guidToQString((const UINT8*)data.constData() + sizeof(EFI_COMMON_SECTION_HEADER))); - } - - result.append("\n"); - - } - return ERR_SUCCESS; -} - -UINT8 UEFIFind::findFileRecursive(const QModelIndex index, const QString & hexPattern, const UINT8 mode, QSet > & files) -{ - if (!index.isValid()) - return ERR_SUCCESS; - - if (hexPattern.isEmpty()) - return ERR_INVALID_PARAMETER; - - // Check for "all substrings" pattern - if (hexPattern.count('.') == hexPattern.length()) - return ERR_SUCCESS; - - bool hasChildren = (model->rowCount(index) > 0); - for (int i = 0; i < model->rowCount(index); i++) { - findFileRecursive(index.child(i, index.column()), hexPattern, mode, files); - } - - QByteArray data; - if (hasChildren) { - if (mode == SEARCH_MODE_HEADER || mode == SEARCH_MODE_ALL) - data.append(model->header(index)); - } - else { - if (mode == SEARCH_MODE_HEADER) - data.append(model->header(index)); - else if (mode == SEARCH_MODE_BODY) - data.append(model->body(index)); - else - data.append(model->header(index)).append(model->body(index)); - } - - QString hexBody = QString(data.toHex()); - QRegExp regexp = QRegExp(QString(hexPattern), Qt::CaseInsensitive); - INT32 offset = regexp.indexIn(hexBody); - while (offset >= 0) { - if (offset % 2 == 0) { - if (model->type(index) != Types::File) { - QModelIndex ffs = model->findParentOfType(index, Types::File); - if (model->type(index) == Types::Section && model->subtype(index) == EFI_SECTION_FREEFORM_SUBTYPE_GUID) - files.insert(QPair(ffs, index)); - else - files.insert(QPair(ffs, QModelIndex())); - } - else - files.insert(QPair(index, QModelIndex())); - - break; - } - offset = regexp.indexIn(hexBody, offset + 1); - } - - return ERR_SUCCESS; -} \ No newline at end of file diff --git a/UEFIFind/uefifind.h b/UEFIFind/uefifind.h deleted file mode 100644 index 69eee52..0000000 --- a/UEFIFind/uefifind.h +++ /dev/null @@ -1,52 +0,0 @@ -/* uefifind.h - -Copyright (c) 2014, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -*/ - -#ifndef __UEFIFIND_H__ -#define __UEFIFIND_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../basetypes.h" -#include "../ffsengine.h" -#include "../ffs.h" - -class UEFIFind : public QObject -{ - Q_OBJECT - -public: - explicit UEFIFind(QObject *parent = 0); - ~UEFIFind(); - - UINT8 init(const QString & path); - UINT8 find(const UINT8 mode, const bool count, const QString & hexPattern, QString & result); - -private: - UINT8 findFileRecursive(const QModelIndex index, const QString & hexPattern, const UINT8 mode, QSet > & files); - QString guidToQString(const UINT8* guid); - - FfsEngine* ffsEngine; - TreeModel* model; - QFileInfo fileInfo; - bool initDone; -}; - -#endif diff --git a/UEFIFind/uefifind.pro b/UEFIFind/uefifind.pro deleted file mode 100644 index 177c742..0000000 --- a/UEFIFind/uefifind.pro +++ /dev/null @@ -1,43 +0,0 @@ -QT += core -QT -= gui - -TARGET = UEFIFind -TEMPLATE = app -CONFIG += console -CONFIG -= app_bundle -DEFINES += _CONSOLE _DISABLE_ENGINE_MESSAGES - -SOURCES += uefifind_main.cpp \ - uefifind.cpp \ - ../types.cpp \ - ../descriptor.cpp \ - ../ffs.cpp \ - ../ffsengine.cpp \ - ../peimage.cpp \ - ../treeitem.cpp \ - ../treemodel.cpp \ - ../LZMA/LzmaCompress.c \ - ../LZMA/LzmaDecompress.c \ - ../LZMA/SDK/C/LzFind.c \ - ../LZMA/SDK/C/LzmaDec.c \ - ../LZMA/SDK/C/LzmaEnc.c \ - ../Tiano/EfiTianoDecompress.c \ - ../Tiano/EfiTianoCompress.c \ - ../Tiano/EfiTianoCompressLegacy.c - -HEADERS += uefifind.h \ - ../basetypes.h \ - ../descriptor.h \ - ../gbe.h \ - ../me.h \ - ../ffs.h \ - ../peimage.h \ - ../types.h \ - ../ffsengine.h \ - ../treeitem.h \ - ../treemodel.h \ - ../LZMA/LzmaCompress.h \ - ../LZMA/LzmaDecompress.h \ - ../Tiano/EfiTianoDecompress.h \ - ../Tiano/EfiTianoCompress.h - diff --git a/UEFIFind/uefifind_main.cpp b/UEFIFind/uefifind_main.cpp deleted file mode 100644 index ebe87b7..0000000 --- a/UEFIFind/uefifind_main.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* uefifind_main.cpp - -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -*/ -#include -#include -#include "uefifind.h" - -int main(int argc, char *argv[]) -{ - QCoreApplication a(argc, argv); - a.setOrganizationName("CodeRush"); - a.setOrganizationDomain("coderush.me"); - a.setApplicationName("UEFIFind"); - - UEFIFind w; - UINT8 result; - - if (a.arguments().length() == 5) { - result = w.init(a.arguments().at(4)); - if (result) - return result; - - // Get search mode - UINT8 mode; - if (a.arguments().at(1) == QString("header")) - mode = SEARCH_MODE_HEADER; - else if (a.arguments().at(1) == QString("body")) - mode = SEARCH_MODE_BODY; - else if (a.arguments().at(1) == QString("all")) - mode = SEARCH_MODE_ALL; - else - return ERR_INVALID_PARAMETER; - - // Get result type - bool count; - if (a.arguments().at(2) == QString("list")) - count = false; - else if (a.arguments().at(2) == QString("count")) - count = true; - else - return ERR_INVALID_PARAMETER; - - // Go find the supplied pattern - QString found; - result = w.find(mode, count, a.arguments().at(3), found); - if (result) - return result; - - // Nothing was found - if (found.isEmpty()) - return ERR_ITEM_NOT_FOUND; - - // Print result - std::cout << found.toStdString(); - return ERR_SUCCESS; - } - else { - std::cout << "UEFIFind 0.3.4" << std::endl << std::endl << - "Usage: uefifind {header | body | all} {list | count} pattern imagefile\n"; - return ERR_INVALID_PARAMETER; - } -} - diff --git a/UEFIPatch/patches.txt b/UEFIPatch/patches.txt deleted file mode 100644 index 74eac08..0000000 --- a/UEFIPatch/patches.txt +++ /dev/null @@ -1,42 +0,0 @@ -# Patch string format -# FileGuid SectionType PatchType:FindPatternOrOffset:ReplacePattern -# Please ensure that the latest symbol in patch string is space - -# Possible section types: -# PE32 image 10 -# Position-independent code 11 -# TE Image 12 -# DXE Dependency 13 -# Version information 14 -# User interface string 15 -# 16-bit code 16 -# Guided freeform 18 -# Raw data 19 -# PEI Dependency 1B -# SMM Dependency 1C -# Please do not try another section types, it can make the resulting image broken - -# Possible patch types: -# P - pattern-based, first parameter is a pattern to find, second - a pattern to replace -# O - offset-based, first parameter is hexadecimal offset, second - a pattern to replace -# Patterns can have . as "any possible value" symbol - -#---------------------------------------------------------------------------------- -# OSX CPU Power Management patches -# Remove lock from MSR 0xE2 register -#---------------------------------------------------------------------------------- - -# PowerMgmtDxe | Haswell -F7731B4C-58A2-4DF4-8980-5645D39ECE58 10 P:75080FBAE80F89442430:EB080FBAE80F89442430 - -# PowerMgmtDxe | Haswell-E -F7731B4C-58A2-4DF4-8980-5645D39ECE58 10 P:0FBA6C24380F:0FBA7424380F - -# PowerManagement | Sandy Bridge with ME 8.xx, Ivy Bridge -8C783970-F02A-4A4D-AF09-8797A51EEC8D 10 P:75080FBAE80F89442430:EB080FBAE80F89442430 -# PowerManagement | New SB-E/IB-E -8C783970-F02A-4A4D-AF09-8797A51EEC8D 10 P:0FBA6C24380F:0FBA7424380F - -# CpuPei | Sandy Bridge with ME 7.xx, old SB-E/IB-E -2BB5AFA9-FF33-417B-8497-CB773C2B93BF 10 P:800018EB050D0080:000018EB050D0000 - diff --git a/UEFIPatch/uefipatch.cpp b/UEFIPatch/uefipatch.cpp deleted file mode 100644 index c2e3a20..0000000 --- a/UEFIPatch/uefipatch.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* uefipatch.cpp - -Copyright (c) 2014, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -*/ - -#include "uefipatch.h" - -UEFIPatch::UEFIPatch(QObject *parent) : - QObject(parent) -{ - ffsEngine = new FfsEngine(this); - model = ffsEngine->treeModel(); -} - -UEFIPatch::~UEFIPatch() -{ - delete ffsEngine; -} - -UINT8 UEFIPatch::patchFromFile(QString path) -{ - QFileInfo patchInfo = QFileInfo("patches.txt"); - - if (!patchInfo.exists()) - return ERR_INVALID_FILE; - - QFile file; - file.setFileName("patches.txt"); - - if (!file.open(QFile::ReadOnly | QFile::Text)) - return ERR_INVALID_FILE; - - QFileInfo fileInfo = QFileInfo(path); - - if (!fileInfo.exists()) - return ERR_FILE_OPEN; - - QFile inputFile; - inputFile.setFileName(path); - - if (!inputFile.open(QFile::ReadOnly)) - return ERR_FILE_READ; - - QByteArray buffer = inputFile.readAll(); - inputFile.close(); - - UINT8 result = ffsEngine->parseImageFile(buffer); - if (result) - return result; - - UINT8 counter = 0; - while (!file.atEnd()) { - QByteArray line = file.readLine(); - // Use sharp sign as commentary - if (line.count() == 0 || line[0] == '#') - continue; - - QList list = line.split(' '); - if (list.count() < 3) - continue; - - QUuid uuid = QUuid(list.at(0)); - QByteArray guid = QByteArray::fromRawData((const char*)&uuid.data1, sizeof(EFI_GUID)); - bool converted; - UINT8 sectionType = (UINT8)list.at(1).toUShort(&converted, 16); - if (!converted) - return ERR_INVALID_PARAMETER; - - QVector patches; - - for (int i = 2; i < list.count(); i++) { - QList patchList = list.at(i).split(':'); - PatchData patch; - patch.type = *(UINT8*)patchList.at(0).constData(); - if (patch.type == PATCH_TYPE_PATTERN) { - patch.offset = 0xFFFFFFFF; - patch.hexFindPattern = patchList.at(1); - patch.hexReplacePattern = patchList.at(2); - patches.append(patch); - } - else if (patch.type == PATCH_TYPE_OFFSET) { - patch.offset = patchList.at(1).toUInt(NULL, 16); - patch.hexReplacePattern = patchList.at(2); - patches.append(patch); - } - else { - // Ignore unknown patch type - continue; - } - } - result = patchFile(model->index(0, 0), guid, sectionType, patches); - if (result && result != ERR_NOTHING_TO_PATCH) - return result; - counter++; - } - - QByteArray reconstructed; - result = ffsEngine->reconstructImageFile(reconstructed); - if (result) - return result; - if (reconstructed == buffer) - return ERR_NOTHING_TO_PATCH; - - QFile outputFile; - outputFile.setFileName(path.append(".patched")); - if (!outputFile.open(QFile::WriteOnly)) - return ERR_FILE_WRITE; - - outputFile.resize(0); - outputFile.write(reconstructed); - outputFile.close(); - - return ERR_SUCCESS; -} - -UINT8 UEFIPatch::patchFile(const QModelIndex & index, const QByteArray & fileGuid, const UINT8 sectionType, const QVector & patches) -{ - - if (!model || !index.isValid()) - return ERR_INVALID_PARAMETER; - if (model->type(index) == Types::Section && model->subtype(index) == sectionType) { - QModelIndex fileIndex = model->findParentOfType(index, Types::File); - if (model->type(fileIndex) == Types::File && - model->header(fileIndex).left(sizeof(EFI_GUID)) == fileGuid) - { - return ffsEngine->patch(index, patches); - } - } - - if (model->rowCount(index) > 0) { - for (int i = 0; i < model->rowCount(index); i++) { - UINT8 result = patchFile(index.child(i, 0), fileGuid, sectionType, patches); - if (!result) - break; - else if (result != ERR_NOTHING_TO_PATCH) - return result; - } - } - - return ERR_NOTHING_TO_PATCH; -} diff --git a/UEFIPatch/uefipatch.h b/UEFIPatch/uefipatch.h deleted file mode 100644 index 3d94ca2..0000000 --- a/UEFIPatch/uefipatch.h +++ /dev/null @@ -1,45 +0,0 @@ -/* uefipatch.h - -Copyright (c) 2014, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -*/ - -#ifndef __UEFIPATCH_H__ -#define __UEFIPATCH_H__ - -#include -#include -#include -#include -#include -#include - -#include "../basetypes.h" -#include "../ffs.h" -#include "../ffsengine.h" - -class UEFIPatch : public QObject -{ - Q_OBJECT - -public: - explicit UEFIPatch(QObject *parent = 0); - ~UEFIPatch(); - - UINT8 patchFromFile(QString path); - UINT8 patch(QString path, QString fileGuid, QString findPattern, QString replacePattern); - -private: - UINT8 patchFile(const QModelIndex & index, const QByteArray & fileGuid, const UINT8 sectionType, const QVector & patches); - FfsEngine* ffsEngine; - TreeModel* model; -}; - -#endif diff --git a/UEFIPatch/uefipatch.pro b/UEFIPatch/uefipatch.pro deleted file mode 100644 index 5465b31..0000000 --- a/UEFIPatch/uefipatch.pro +++ /dev/null @@ -1,43 +0,0 @@ -QT += core -QT -= gui - -TARGET = UEFIPatch -TEMPLATE = app -CONFIG += console -CONFIG -= app_bundle -DEFINES += _CONSOLE - -SOURCES += uefipatch_main.cpp \ - uefipatch.cpp \ - ../types.cpp \ - ../descriptor.cpp \ - ../ffs.cpp \ - ../ffsengine.cpp \ - ../peimage.cpp \ - ../treeitem.cpp \ - ../treemodel.cpp \ - ../LZMA/LzmaCompress.c \ - ../LZMA/LzmaDecompress.c \ - ../LZMA/SDK/C/LzFind.c \ - ../LZMA/SDK/C/LzmaDec.c \ - ../LZMA/SDK/C/LzmaEnc.c \ - ../Tiano/EfiTianoDecompress.c \ - ../Tiano/EfiTianoCompress.c \ - ../Tiano/EfiTianoCompressLegacy.c - -HEADERS += uefipatch.h \ - ../basetypes.h \ - ../descriptor.h \ - ../gbe.h \ - ../me.h \ - ../ffs.h \ - ../peimage.h \ - ../types.h \ - ../ffsengine.h \ - ../treeitem.h \ - ../treemodel.h \ - ../LZMA/LzmaCompress.h \ - ../LZMA/LzmaDecompress.h \ - ../Tiano/EfiTianoDecompress.h \ - ../Tiano/EfiTianoCompress.h - diff --git a/UEFIPatch/uefipatch_main.cpp b/UEFIPatch/uefipatch_main.cpp deleted file mode 100644 index 2ff15c9..0000000 --- a/UEFIPatch/uefipatch_main.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* uefipatch_main.cpp - -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -*/ -#include -#include -#include -#include -#include "uefipatch.h" - -int main(int argc, char *argv[]) -{ - QCoreApplication a(argc, argv); - a.setOrganizationName("CodeRush"); - a.setOrganizationDomain("coderush.me"); - a.setApplicationName("UEFIPatch"); - - UEFIPatch w; - UINT8 result = ERR_SUCCESS; - UINT32 argumentsCount = a.arguments().length(); - - if (argumentsCount == 2) { - result = w.patchFromFile(a.arguments().at(1)); - } - else { - std::cout << "UEFIPatch 0.3.4 - 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; - } - - switch (result) { - case ERR_SUCCESS: - std::cout << "Image patched" << std::endl; - break; - case ERR_INVALID_PARAMETER: - std::cout << "Function called with invalid parameter" << std::endl; - break; - case ERR_NOTHING_TO_PATCH: - std::cout << "No patches can be applied to input file" << std::endl; - break; - case ERR_UNKNOWN_PATCH_TYPE: - std::cout << "Unknown patch type" << std::endl; - break; - case ERR_PATCH_OFFSET_OUT_OF_BOUNDS: - std::cout << "Patch offset out of bounds" << std::endl; - break; - case ERR_INVALID_SYMBOL: - std::cout << "Pattern format mismatch" << std::endl; - break; - case ERR_INVALID_FILE: - std::cout << "patches.txt file not found or can't be read" << std::endl; - break; - case ERR_FILE_OPEN: - std::cout << "Input file not found" << std::endl; - break; - case ERR_FILE_READ: - std::cout << "Input file can't be read" << std::endl; - break; - case ERR_FILE_WRITE: - std::cout << "Output file can't be written" << std::endl; - break; - default: - std::cout << "Error " << result << std::endl; - } - - return result; -} diff --git a/basetypes.h b/basetypes.h index 083267b..c675e0a 100644 --- a/basetypes.h +++ b/basetypes.h @@ -42,6 +42,7 @@ typedef unsigned int UINTN; #define FALSE ((BOOLEAN)(0==1)) #endif +typedef UINT8 STATUS; #define ERR_SUCCESS 0 #define ERR_INVALID_PARAMETER 1 #define ERR_BUFFER_TOO_SMALL 2 @@ -68,7 +69,7 @@ typedef unsigned int UINTN; #define ERR_CUSTOMIZED_COMPRESSION_FAILED 23 #define ERR_STANDARD_DECOMPRESSION_FAILED 24 #define ERR_CUSTOMIZED_DECOMPRESSION_FAILED 25 -#define ERR_UNKNOWN_COMPRESSION_ALGORITHM 26 +#define ERR_UNKNOWN_COMPRESSION_TYPE 26 #define ERR_UNKNOWN_EXTRACT_MODE 27 #define ERR_UNKNOWN_INSERT_MODE 28 #define ERR_UNKNOWN_IMAGE_TYPE 29 diff --git a/ffs.cpp b/ffs.cpp index fcdf03f..cd2cff4 100644 --- a/ffs.cpp +++ b/ffs.cpp @@ -40,7 +40,7 @@ UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize) while (bufferSize--) counter += buffer[bufferSize]; - return (UINT8)0x100 - counter; + return (UINT8)(0x100 - counter); } UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize) @@ -57,7 +57,7 @@ UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize) counter = (UINT16)(counter + buffer[index]); } - return (UINT16)0x10000 - counter; + return (UINT16)(0x10000 - counter); } VOID uint32ToUint24(UINT32 size, UINT8* ffsSize) @@ -69,38 +69,23 @@ VOID uint32ToUint24(UINT32 size, UINT8* ffsSize) UINT32 uint24ToUint32(const UINT8* ffsSize) { - return (ffsSize[2] << 16) + - (ffsSize[1] << 8) + - ffsSize[0]; + return *(UINT32*)ffsSize & 0x00FFFFFF; } -QString guidToQString(const EFI_GUID& guid) +QString guidToQString(const EFI_GUID & guid) { - QByteArray baGuid = QByteArray::fromRawData((const char*)guid.Data, sizeof(EFI_GUID)); - const UINT32 i32 = *(const UINT32*)baGuid.left(4).constData(); - const UINT16 i16_0 = *(const UINT16*)baGuid.mid(4, 2).constData(); - const UINT16 i16_1 = *(const UINT16*)baGuid.mid(6, 2).constData(); - const UINT8 i8_0 = *(const UINT8*)baGuid.mid(8, 1).constData(); - const UINT8 i8_1 = *(const UINT8*)baGuid.mid(9, 1).constData(); - const UINT8 i8_2 = *(const UINT8*)baGuid.mid(10, 1).constData(); - const UINT8 i8_3 = *(const UINT8*)baGuid.mid(11, 1).constData(); - const UINT8 i8_4 = *(const UINT8*)baGuid.mid(12, 1).constData(); - const UINT8 i8_5 = *(const UINT8*)baGuid.mid(13, 1).constData(); - const UINT8 i8_6 = *(const UINT8*)baGuid.mid(14, 1).constData(); - const UINT8 i8_7 = *(const UINT8*)baGuid.mid(15, 1).constData(); - return QString("%1-%2-%3-%4%5-%6%7%8%9%10%11") - .arg(i32, 8, 16, QChar('0')) - .arg(i16_0, 4, 16, QChar('0')) - .arg(i16_1, 4, 16, QChar('0')) - .arg(i8_0, 2, 16, QChar('0')) - .arg(i8_1, 2, 16, QChar('0')) - .arg(i8_2, 2, 16, QChar('0')) - .arg(i8_3, 2, 16, QChar('0')) - .arg(i8_4, 2, 16, QChar('0')) - .arg(i8_5, 2, 16, QChar('0')) - .arg(i8_6, 2, 16, QChar('0')) - .arg(i8_7, 2, 16, QChar('0')).toUpper(); + .arg(*(const UINT32*)&guid.Data[0], 8, 16, QChar('0')) + .arg(*(const UINT16*)&guid.Data[4], 4, 16, QChar('0')) + .arg(*(const UINT16*)&guid.Data[6], 4, 16, QChar('0')) + .arg(guid.Data[8], 2, 16, QChar('0')) + .arg(guid.Data[9], 2, 16, QChar('0')) + .arg(guid.Data[10], 2, 16, QChar('0')) + .arg(guid.Data[11], 2, 16, QChar('0')) + .arg(guid.Data[12], 2, 16, QChar('0')) + .arg(guid.Data[13], 2, 16, QChar('0')) + .arg(guid.Data[14], 2, 16, QChar('0')) + .arg(guid.Data[15], 2, 16, QChar('0')).toUpper(); } QString fileTypeToQString(const UINT8 type) @@ -137,7 +122,7 @@ QString sectionTypeToQString(const UINT8 type) case EFI_SECTION_TE: return QObject::tr("TE image"); case EFI_SECTION_DXE_DEPEX: return QObject::tr("DXE dependency"); case EFI_SECTION_VERSION: return QObject::tr("Version"); - case EFI_SECTION_USER_INTERFACE: return QObject::tr("User interface"); + case EFI_SECTION_USER_INTERFACE: return QObject::tr("UI"); case EFI_SECTION_COMPATIBILITY16: return QObject::tr("16-bit image"); case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: return QObject::tr("Volume image"); case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return QObject::tr("Freeform subtype GUID"); @@ -150,52 +135,3 @@ QString sectionTypeToQString(const UINT8 type) } } -UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header) -{ - if (!header) - return 0; - - bool extended = false; - /*if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) { - extended = true; - }*/ - - switch (header->Type) - { - case EFI_SECTION_GUID_DEFINED: { - if (!extended) { - const EFI_GUID_DEFINED_SECTION* gdsHeader = (const EFI_GUID_DEFINED_SECTION*)header; - if (QByteArray((const char*)&gdsHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { - const WIN_CERTIFICATE* certificateHeader = (const WIN_CERTIFICATE*)(gdsHeader + 1); - return gdsHeader->DataOffset + certificateHeader->Length; - } - return gdsHeader->DataOffset; - } - else { - const EFI_GUID_DEFINED_SECTION2* gdsHeader = (const EFI_GUID_DEFINED_SECTION2*)header; - if (QByteArray((const char*)&gdsHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { - const WIN_CERTIFICATE* certificateHeader = (const WIN_CERTIFICATE*)(gdsHeader + 1); - return gdsHeader->DataOffset + certificateHeader->Length; - } - return gdsHeader->DataOffset; - } - } - case EFI_SECTION_COMPRESSION: return extended ? sizeof(EFI_COMPRESSION_SECTION2) : sizeof(EFI_COMPRESSION_SECTION); - case EFI_SECTION_DISPOSABLE: return extended ? sizeof(EFI_DISPOSABLE_SECTION2) : sizeof(EFI_DISPOSABLE_SECTION); - case EFI_SECTION_PE32: return extended ? sizeof(EFI_PE32_SECTION2) : sizeof(EFI_PE32_SECTION); - case EFI_SECTION_PIC: return extended ? sizeof(EFI_PIC_SECTION2) : sizeof(EFI_PIC_SECTION); - case EFI_SECTION_TE: return extended ? sizeof(EFI_TE_SECTION2) : sizeof(EFI_TE_SECTION); - case EFI_SECTION_DXE_DEPEX: return extended ? sizeof(EFI_DXE_DEPEX_SECTION2) : sizeof(EFI_DXE_DEPEX_SECTION); - case EFI_SECTION_VERSION: return extended ? sizeof(EFI_VERSION_SECTION2) : sizeof(EFI_VERSION_SECTION); - case EFI_SECTION_USER_INTERFACE: return extended ? sizeof(EFI_USER_INTERFACE_SECTION2) : sizeof(EFI_USER_INTERFACE_SECTION); - case EFI_SECTION_COMPATIBILITY16: return extended ? sizeof(EFI_COMPATIBILITY16_SECTION2) : sizeof(EFI_COMPATIBILITY16_SECTION); - case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: return extended ? sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION2) : sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION); - case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return extended ? sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION2) : sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION); - case EFI_SECTION_RAW: return extended ? sizeof(EFI_RAW_SECTION2) : sizeof(EFI_RAW_SECTION); - case EFI_SECTION_PEI_DEPEX: return extended ? sizeof(EFI_PEI_DEPEX_SECTION2) : sizeof(EFI_PEI_DEPEX_SECTION); - case EFI_SECTION_SMM_DEPEX: return extended ? sizeof(EFI_SMM_DEPEX_SECTION2) : sizeof(EFI_SMM_DEPEX_SECTION); - case INSYDE_SECTION_POSTCODE: return extended ? sizeof(POSTCODE_SECTION2) : sizeof(POSTCODE_SECTION); - case SCT_SECTION_POSTCODE: return extended ? sizeof(POSTCODE_SECTION2) : sizeof(POSTCODE_SECTION); - default: return extended ? sizeof(EFI_COMMON_SECTION_HEADER2) : sizeof(EFI_COMMON_SECTION_HEADER); - } -} diff --git a/ffs.h b/ffs.h index 8b7da95..4bbec90 100644 --- a/ffs.h +++ b/ffs.h @@ -240,9 +240,6 @@ typedef struct _EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE { //UINT8 Data[]; } EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE; -//!TODO: add proper NVRAM parsing -//const QByteArray EFI_FIRMWARE_VOLUME_NVRAM_SIGNATURE("$VSS", 4); - // Volume header 16bit checksum calculation routine extern UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); @@ -512,9 +509,6 @@ typedef EFI_COMMON_SECTION_HEADER2 EFI_FIRMWARE_VOLUME_IMAGE_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_USER_INTERFACE_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_USER_INTERFACE_SECTION2; -//Section routines -extern UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header); - //***************************************************************************** // EFI Dependency Expression //***************************************************************************** diff --git a/ffsengine.cpp b/ffsengine.cpp deleted file mode 100644 index 2e0a8fe..0000000 --- a/ffsengine.cpp +++ /dev/null @@ -1,4208 +0,0 @@ -/* ffsengine.cpp - -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. -*/ - -#include - -#include "ffsengine.h" -#include "types.h" -#include "treemodel.h" -#include "descriptor.h" -#include "ffs.h" -#include "gbe.h" -#include "me.h" -#include "Tiano/EfiTianoCompress.h" -#include "Tiano/EfiTianoDecompress.h" -#include "LZMA/LzmaCompress.h" -#include "LZMA/LzmaDecompress.h" - -#ifdef _CONSOLE -#include -#endif - -QString errorMessage(UINT8 errorCode) -{ - switch (errorCode) { - case ERR_SUCCESS: return QObject::tr("Success"); - case ERR_NOT_IMPLEMENTED: return QObject::tr("Not implemented"); - case ERR_INVALID_PARAMETER: return QObject::tr("Function called with invalid parameter"); - case ERR_BUFFER_TOO_SMALL: return QObject::tr("Buffer too small"); - case ERR_OUT_OF_RESOURCES: return QObject::tr("Out of resources"); - case ERR_OUT_OF_MEMORY: return QObject::tr("Out of memory"); - case ERR_FILE_OPEN: return QObject::tr("File can't be opened"); - case ERR_FILE_READ: return QObject::tr("File can't be read"); - case ERR_FILE_WRITE: return QObject::tr("File can't be written"); - case ERR_ITEM_NOT_FOUND: return QObject::tr("Item not found"); - case ERR_UNKNOWN_ITEM_TYPE: return QObject::tr("Unknown item type"); - case ERR_INVALID_FLASH_DESCRIPTOR: return QObject::tr("Invalid flash descriptor"); - case ERR_INVALID_REGION: return QObject::tr("Invalid region"); - case ERR_EMPTY_REGION: return QObject::tr("Empty region"); - case ERR_BIOS_REGION_NOT_FOUND: return QObject::tr("BIOS region not found"); - case ERR_VOLUMES_NOT_FOUND: return QObject::tr("UEFI volumes not found"); - case ERR_INVALID_VOLUME: return QObject::tr("Invalid UEFI volume"); - case ERR_VOLUME_REVISION_NOT_SUPPORTED: return QObject::tr("Volume revision not supported"); - case ERR_VOLUME_GROW_FAILED: return QObject::tr("Volume grow failed"); - case ERR_UNKNOWN_FFS: return QObject::tr("Unknown file system"); - case ERR_INVALID_FILE: return QObject::tr("Invalid file"); - case ERR_INVALID_SECTION: return QObject::tr("Invalid section"); - case ERR_UNKNOWN_SECTION: return QObject::tr("Unknown section"); - case ERR_STANDARD_COMPRESSION_FAILED: return QObject::tr("Standard compression failed"); - case ERR_CUSTOMIZED_COMPRESSION_FAILED: return QObject::tr("Customized compression failed"); - case ERR_STANDARD_DECOMPRESSION_FAILED: return QObject::tr("Standard decompression failed"); - case ERR_CUSTOMIZED_DECOMPRESSION_FAILED: return QObject::tr("Customized compression failed"); - case ERR_UNKNOWN_COMPRESSION_ALGORITHM: return QObject::tr("Unknown compression method"); - case ERR_UNKNOWN_EXTRACT_MODE: return QObject::tr("Unknown extract mode"); - case ERR_UNKNOWN_INSERT_MODE: return QObject::tr("Unknown insert mode"); - case ERR_UNKNOWN_IMAGE_TYPE: return QObject::tr("Unknown executable image type"); - case ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE: return QObject::tr("Unknown PE optional header type"); - case ERR_UNKNOWN_RELOCATION_TYPE: return QObject::tr("Unknown relocation type"); - case ERR_GENERIC_CALL_NOT_SUPPORTED: return QObject::tr("Generic call not supported"); - case ERR_VOLUME_BASE_NOT_FOUND: return QObject::tr("Volume base address not found"); - case ERR_PEI_CORE_ENTRY_POINT_NOT_FOUND: return QObject::tr("PEI core entry point not found"); - case ERR_COMPLEX_BLOCK_MAP: return QObject::tr("Block map structure too complex for correct analysis"); - case ERR_DIR_ALREADY_EXIST: return QObject::tr("Directory already exists"); - case ERR_DIR_CREATE: return QObject::tr("Directory can't be created"); - case ERR_UNKNOWN_PATCH_TYPE: return QObject::tr("Unknown patch type"); - case ERR_PATCH_OFFSET_OUT_OF_BOUNDS: return QObject::tr("Patch offset out of bounds"); - 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"); - default: return QObject::tr("Unknown error %1").arg(errorCode); - } -} - -FfsEngine::FfsEngine(QObject *parent) - : QObject(parent) -{ - model = new TreeModel(); - oldPeiCoreEntryPoint = 0; - newPeiCoreEntryPoint = 0; - dumped = false; -} - -FfsEngine::~FfsEngine(void) -{ - delete model; -} - -TreeModel* FfsEngine::treeModel() const -{ - return model; -} - -void FfsEngine::msg(const QString & message, const QModelIndex & index) -{ -#ifndef _DISABLE_ENGINE_MESSAGES -#ifndef _CONSOLE - messageItems.enqueue(MessageListItem(message, NULL, 0, index)); -#else - (void) index; - std::cout << message.toLatin1().constData() << std::endl; -#endif -#else - (void)message; - (void)index; -#endif -} - -#ifndef _CONSOLE -QQueue FfsEngine::messages() const -{ - return messageItems; -} - -void FfsEngine::clearMessages() -{ - messageItems.clear(); -} -#endif - -bool FfsEngine::hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2) -{ - 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; -} - -// Firmware image parsing -UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) -{ - oldPeiCoreEntryPoint = 0; - newPeiCoreEntryPoint = 0; - - // 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))); - return ERR_INVALID_PARAMETER; - } - - // Check buffer for being normal EFI capsule header - UINT32 capsuleHeaderSize = 0; - QModelIndex index; - if (buffer.startsWith(EFI_CAPSULE_GUID)) { - // Get info - const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_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->CapsuleImageSize).arg(capsuleHeader->CapsuleImageSize) - .hexarg2(capsuleHeader->Flags, 8); - - // Add tree item - index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, 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 - bool signedCapsule = buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID); - - const APTIO_CAPSULE_HEADER* capsuleHeader = (const APTIO_CAPSULE_HEADER*)buffer.constData(); - capsuleHeaderSize = capsuleHeader->RomImageOffset; - QByteArray header = buffer.left(capsuleHeaderSize); - QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize); - QString name = tr("AMI Aptio capsule"); - QString info = tr("Capsule GUID: %1\nFull size: %2h (%3)\nHeader size: %4h (%5)\nImage size: %6h (%7)\nFlags: %8h") - .arg(guidToQString(capsuleHeader->CapsuleHeader.CapsuleGuid)) - .hexarg(buffer.size()).arg(buffer.size()) - .hexarg(capsuleHeaderSize).arg(capsuleHeaderSize) - .hexarg(capsuleHeader->CapsuleHeader.CapsuleImageSize - capsuleHeaderSize).arg(capsuleHeader->CapsuleHeader.CapsuleImageSize - capsuleHeaderSize) - .hexarg2(capsuleHeader->CapsuleHeader.Flags, 8); - - //!TODO: more info about Aptio capsule - - // 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); - } - } - - // Skip capsule header to have flash chip image - QByteArray flashImage = buffer.right(buffer.size() - capsuleHeaderSize); - - // Check for Intel flash descriptor presence - const FLASH_DESCRIPTOR_HEADER* descriptorHeader = (const FLASH_DESCRIPTOR_HEADER*)flashImage.constData(); - - // Check descriptor signature - UINT8 result; - if (descriptorHeader->Signature == FLASH_DESCRIPTOR_SIGNATURE) { - // Parse as Intel image - QModelIndex imageIndex; - result = parseIntelImage(flashImage, imageIndex, index); - if (result != ERR_INVALID_FLASH_DESCRIPTOR) - return result; - } - - // Get info - QString name = tr("UEFI image"); - QString info = tr("Full size: %1h (%2)") - .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); - return parseBios(flashImage, index); -} - -UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & index, const QModelIndex & parent) -{ - // Sanity check - if (intelImage.isEmpty()) - return EFI_INVALID_PARAMETER; - - // 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) { - msg(tr("parseIntelImage: input file is smaller then minimum descriptor size of %1h (%2) bytes").hexarg(FLASH_DESCRIPTOR_SIZE).arg(FLASH_DESCRIPTOR_SIZE)); - 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; - } - // ME region - QByteArray me; - UINT32 meBegin = 0; - UINT32 meEnd = 0; - if (regionSection->MeLimit) { - meBegin = calculateRegionOffset(regionSection->MeBase); - meEnd = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); - 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; - UINT32 biosEnd = 0; - if (regionSection->BiosLimit) { - biosBegin = calculateRegionOffset(regionSection->BiosBase); - biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); - - // Check for Gigabyte specific descriptor map - if (biosEnd - biosBegin == (UINT32)intelImage.size()) { - if (!meEnd) { - 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 += biosBegin; - } - else { - msg(tr("parseIntelImage: descriptor parsing failed, BIOS region not found in descriptor")); - return ERR_INVALID_FLASH_DESCRIPTOR; - } - - // Check for intersections between regions - 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 (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 (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 (hasIntersection(biosBegin, biosEnd, pdrBegin, pdrEnd)) { - msg(tr("parseIntelImage: descriptor parsing failed, BIOS region has intersection with PDR 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") - .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->NumberOfPchStraps) - .arg(descriptorMap->NumberOfProcStraps) - .arg(descriptorMap->NumberOfIccTableEntries); - - // Add Intel image tree item - index = model->addItem(Types::Image, Subtypes::IntelImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), intelImage, QByteArray(), parent); - - // Descriptor - // Get descriptor info - 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); - } - if (regionSection->MeLimit) { - offsets.append(meBegin); - info += tr("\nME region offset: %1h").hexarg(meBegin); - } - if (regionSection->BiosLimit) { - offsets.append(biosBegin); - info += tr("\nBIOS region offset: %1h").hexarg(biosBegin); - } - if (regionSection->PdrLimit) { - offsets.append(pdrBegin); - info += tr("\nPDR region offset: %1h").hexarg(pdrBegin); - } - - // 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); - - // 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 "); - - // VSCC table - const VSCC_TABLE_ENTRY* vsccTableEntry = (const VSCC_TABLE_ENTRY*)(descriptor + ((UINT16)upperMap->VsccTableBase << 4)); - info += tr("\nFlash chips in VSCC table:"); - UINT8 vsscTableSize = upperMap->VsccTableSize * sizeof(UINT32) / sizeof(VSCC_TABLE_ENTRY); - for (int i = 0; i < vsscTableSize; i++) { - info += tr("\n%1%2%3h") - .hexarg2(vsccTableEntry->VendorId, 2) - .hexarg2(vsccTableEntry->DeviceId0, 2) - .hexarg2(vsccTableEntry->DeviceId1, 2); - vsccTableEntry++; - } - - // Add descriptor tree item - model->addItem(Types::Region, Subtypes::DescriptorRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, QByteArray(), index); - - // Sort regions in ascending order - qSort(offsets); - - // 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, gbeIndex, index); - } - // Parse ME region - else if (offsets.at(i) == meBegin) { - QModelIndex meIndex; - result = parseMeRegion(me, meIndex, index); - } - // Parse BIOS region - else if (offsets.at(i) == biosBegin) { - QModelIndex biosIndex; - result = parseBiosRegion(bios, biosIndex, index); - } - // Parse PDR region - else if (offsets.at(i) == pdrBegin) { - QModelIndex pdrIndex; - result = parsePdrRegion(pdr, pdrIndex, index); - } - if (result) - return result; - } - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::parseGbeRegion(const QByteArray & gbe, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) -{ - // Check sanity - if (gbe.isEmpty()) - return ERR_EMPTY_REGION; - - // Get info - QString name = tr("GbE region"); - const GBE_MAC_ADDRESS* mac = (const GBE_MAC_ADDRESS*)gbe.constData(); - const GBE_VERSION* version = (const GBE_VERSION*)(gbe.constData() + GBE_VERSION_OFFSET); - QString info = tr("Full size: %1h (%2)\nMAC: %3:%4:%5:%6:%7:%8\nVersion: %9.%10") - .hexarg(gbe.size()).arg(gbe.size()) - .hexarg2(mac->vendor[0], 2) - .hexarg2(mac->vendor[1], 2) - .hexarg2(mac->vendor[2], 2) - .hexarg2(mac->device[0], 2) - .hexarg2(mac->device[1], 2) - .hexarg2(mac->device[2], 2) - .arg(version->major) - .arg(version->minor); - - // Add tree item - index = model->addItem(Types::Region, Subtypes::GbeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), gbe, QByteArray(), parent, mode); - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) -{ - // Check sanity - if (me.isEmpty()) - return ERR_EMPTY_REGION; - - // Get info - QString name = tr("ME/TXE region"); - QString info = tr("Full size: %1h (%2)"). - hexarg(me.size()).arg(me.size()); - - // Parse region - bool versionFound = true; - bool emptyRegion = false; - // Check for empty region - if (me.count() == me.count('\xFF') || me.count() == me.count('\x00')) { - // Further parsing not needed - emptyRegion = true; - info += tr("\nState: empty"); - } - else { - // Search for new signature - INT32 versionOffset = me.indexOf(ME_VERSION_SIGNATURE2); - if (versionOffset < 0){ // New signature not found - // Search for old signature - versionOffset = me.indexOf(ME_VERSION_SIGNATURE); - if (versionOffset < 0){ - info += tr("\nVersion: unknown"); - versionFound = false; - } - } - - // Add version information - if (versionFound) { - const ME_VERSION* version = (const ME_VERSION*)(me.constData() + versionOffset); - info += tr("\nVersion: %1.%2.%3.%4") - .arg(version->major) - .arg(version->minor) - .arg(version->bugfix) - .arg(version->build); - } - } - - // Add tree item - index = model->addItem(Types::Region, Subtypes::MeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), me, QByteArray(), parent, mode); - - // Show messages - if (emptyRegion) { - msg(tr("parseRegion: ME/TXE region is empty"), index); - } - else if (!versionFound) { - msg(tr("parseRegion: ME/TXE region version is unknown, it can be damaged"), index); - } - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::parsePdrRegion(const QByteArray & pdr, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) -{ - // Check sanity - if (pdr.isEmpty()) - return ERR_EMPTY_REGION; - - // Get info - QString name = tr("PDR region"); - QString info = tr("Full size: %1h (%2)"). - 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); - - // Parse PDR region as BIOS space - UINT8 result = parseBios(pdr, index); - if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) - return result; - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::parseBiosRegion(const QByteArray & bios, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) -{ - if (bios.isEmpty()) - return ERR_EMPTY_REGION; - - // Get info - QString name = tr("BIOS region"); - QString info = tr("Full size: %1h (%2)"). - 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); - - return parseBios(bios, index); -} - -UINT32 FfsEngine::getPaddingType(const QByteArray & padding) -{ - if (padding.count('\x00') == padding.count()) - return Subtypes::ZeroPadding; - if (padding.count('\xFF') == padding.count()) - return Subtypes::OnePadding; - return Subtypes::DataPadding; -} - -UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) -{ - // Search for first volume - UINT32 prevVolumeOffset; - UINT8 result; - - result = findNextVolume(bios, 0, prevVolumeOffset); - if (result) - return result; - - // First volume is not at the beginning of BIOS space - QString name; - QString info; - if (prevVolumeOffset > 0) { - // Get info - QByteArray padding = bios.left(prevVolumeOffset); - 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); - } - - // Search for and parse all volumes - UINT32 volumeOffset = prevVolumeOffset; - UINT32 prevVolumeSize = 0; - UINT32 volumeSize = 0; - UINT32 bmVolumeSize = 0; - - while (true) - { - bool msgAlignmentBitsSet = false; - bool msgUnaligned = false; - bool msgUnknownRevision = false; - bool msgSizeMismach = false; - - // Padding between volumes - if (volumeOffset > prevVolumeOffset + prevVolumeSize) { - UINT32 paddingSize = volumeOffset - prevVolumeOffset - prevVolumeSize; - QByteArray padding = bios.mid(prevVolumeOffset + prevVolumeSize, paddingSize); - // 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, QByteArray(), parent); - } - - // Get volume size - result = getVolumeSize(bios, volumeOffset, volumeSize, bmVolumeSize); - if (result) - return result; - - // Check reported size - if (volumeSize != bmVolumeSize) - msgSizeMismach = true; - - //Check that volume is fully present in input - if (volumeOffset + volumeSize > (UINT32)bios.size()) { - msg(tr("parseBios: one of volumes inside overlaps the end of data"), parent); - return ERR_INVALID_VOLUME; - } - - // Check volume revision and alignment - const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + volumeOffset); - UINT32 alignment; - if (volumeHeader->Revision == 1) { - // Acquire alignment capability bit - bool alignmentCap = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_CAP; - if (!alignmentCap) { - if (volumeHeader->Attributes & 0xFFFF0000) - msgAlignmentBitsSet = true; - } - } - else if (volumeHeader->Revision == 2) { - // Acquire alignment - alignment = (UINT32)pow(2.0, (int)(volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16); - - // Check alignment - if (volumeOffset % alignment) - msgUnaligned = true; - } - else - msgUnknownRevision = true; - - // Parse volume - QModelIndex index; - UINT8 result = parseVolume(bios.mid(volumeOffset, volumeSize), index, parent); - if (result) - msg(tr("parseBios: volume parsing failed with error \"%1\"").arg(errorMessage(result)), parent); - - // Show messages - if (msgAlignmentBitsSet) - msg("parseBios: alignment bits set on volume without alignment capability", index); - if (msgUnaligned) - msg(tr("parseBios: unaligned revision 2 volume"), index); - if (msgUnknownRevision) - msg(tr("parseBios: unknown volume revision %1").arg(volumeHeader->Revision), index); - if (msgSizeMismach) - msg(tr("parseBios: volume size stored in header %1h (%2) differs from calculated using block map %3h (%4)") - .hexarg(volumeSize).arg(volumeSize) - .hexarg(bmVolumeSize).arg(bmVolumeSize), - index); - - // Go to next volume - prevVolumeOffset = volumeOffset; - prevVolumeSize = volumeSize; - - result = findNextVolume(bios, volumeOffset + prevVolumeSize, volumeOffset); - if (result) { - UINT32 endPaddingSize = bios.size() - prevVolumeOffset - prevVolumeSize; - // Padding at the end of BIOS space - if (endPaddingSize > 0) { - QByteArray padding = bios.right(endPaddingSize); - // 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, QByteArray(), parent); - } - break; - } - } - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::findNextVolume(const QByteArray & bios, UINT32 volumeOffset, UINT32 & nextVolumeOffset) -{ - int nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset); - if (nextIndex < EFI_FV_SIGNATURE_OFFSET) { - return ERR_VOLUMES_NOT_FOUND; - } - - nextVolumeOffset = nextIndex - EFI_FV_SIGNATURE_OFFSET; - return ERR_SUCCESS; -} - -UINT8 FfsEngine::getVolumeSize(const QByteArray & bios, UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize) -{ - // Populate volume header - const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + volumeOffset); - - // Check volume signature - if (QByteArray((const char*)&volumeHeader->Signature, sizeof(volumeHeader->Signature)) != EFI_FV_SIGNATURE) - return ERR_INVALID_VOLUME; - - // Calculate volume size using BlockMap - const EFI_FV_BLOCK_MAP_ENTRY* entry = (const EFI_FV_BLOCK_MAP_ENTRY*)(bios.constData() + volumeOffset + sizeof(EFI_FIRMWARE_VOLUME_HEADER)); - UINT32 calcVolumeSize = 0; - while (entry->NumBlocks != 0 && entry->Length != 0) { - if ((void*)entry > bios.constData() + bios.size()) - return ERR_INVALID_VOLUME; - - calcVolumeSize += entry->NumBlocks * entry->Length; - entry += 1; - } - - volumeSize = volumeHeader->FvLength; - bmVolumeSize = calcVolumeSize; - return ERR_SUCCESS; -} - -UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) -{ - // Populate volume header - const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(volume.constData()); - - // Calculate volume header size - UINT32 headerSize; - if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { - const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)(volume.constData() + volumeHeader->ExtHeaderOffset); - headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize; - } - else - headerSize = volumeHeader->HeaderLength; - - // Sanity check after some new crazy MSI images - headerSize = ALIGN8(headerSize); - - // 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'; - - // Get volume size - UINT32 volumeSize; - UINT32 bmVolumeSize; - - UINT8 result = getVolumeSize(volume, 0, volumeSize, bmVolumeSize); - if (result) - return result; - - // Check for Apple CRC32 in ZeroVector - bool volumeHasZVCRC = false; - UINT32 crc32FromZeroVector = *(UINT32*)(volume.constData() + 8); - 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 header checksum by recalculating it - bool msgInvalidChecksum = false; - if (calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength)) - msgInvalidChecksum = true; - - // Get info - QString name = guidToQString(volumeHeader->FileSystemGuid); - QString info = tr("ZeroVector:\n%1 %2 %3 %4 %5 %6 %7 %8\n%9 %10 %11 %12 %13 %14 %15 %16\nFileSystem GUID: %17\nFull size: %18h (%19)\n" - "Header size: %20h (%21)\nBody size: %22h (%23)\nRevision: %24\nAttributes: %25h\nErase polarity: %26") - .hexarg2(volumeHeader->ZeroVector[0], 2).hexarg2(volumeHeader->ZeroVector[1], 2).hexarg2(volumeHeader->ZeroVector[2], 2).hexarg2(volumeHeader->ZeroVector[3], 2) - .hexarg2(volumeHeader->ZeroVector[4], 2).hexarg2(volumeHeader->ZeroVector[5], 2).hexarg2(volumeHeader->ZeroVector[6], 2).hexarg2(volumeHeader->ZeroVector[7], 2) - .hexarg2(volumeHeader->ZeroVector[8], 2).hexarg2(volumeHeader->ZeroVector[9], 2).hexarg2(volumeHeader->ZeroVector[10], 2).hexarg2(volumeHeader->ZeroVector[11], 2) - .hexarg2(volumeHeader->ZeroVector[12], 2).hexarg2(volumeHeader->ZeroVector[13], 2).hexarg2(volumeHeader->ZeroVector[14], 2).hexarg2(volumeHeader->ZeroVector[15], 2) - .arg(guidToQString(volumeHeader->FileSystemGuid)) - .hexarg(volumeSize).arg(volumeSize) - .hexarg(headerSize).arg(headerSize) - .hexarg(volumeSize - headerSize).arg(volumeSize - headerSize) - .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) { - const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)(volume.constData() + volumeHeader->ExtHeaderOffset); - info += tr("\nExtended header size: %1h (%2)\nVolume GUID: %3") - .hexarg(extendedHeader->ExtHeaderSize).arg(extendedHeader->ExtHeaderSize) - .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 "); - - // 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); - - // Show messages - if (volumeIsUnknown) { - msg(tr("parseVolume: unknown file system %1").arg(guidToQString(volumeHeader->FileSystemGuid)), index); - // Do not parse unknown volumes - return ERR_SUCCESS; - } - if (msgInvalidChecksum) { - msg(tr("parseVolume: volume header checksum is invalid"), index); - } - - // Search for and parse all files - UINT32 fileOffset = headerSize; - UINT32 fileSize; - QQueue files; - - while (fileOffset < volumeSize) { - bool msgUnalignedFile = false; - bool msgDuplicateGuid = false; - - result = getFileSize(volume, fileOffset, fileSize); - if (result) - return result; - - // Check file size to be at least size of EFI_FFS_FILE_HEADER - if (fileSize < sizeof(EFI_FFS_FILE_HEADER)) { - msg(tr("parseVolume: volume has FFS file with invalid size"), index); - return ERR_INVALID_FILE; - } - - QByteArray file = volume.mid(fileOffset, fileSize); - QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); - - // If we are at empty space in the end of volume - if (header.count(empty) == header.size()) { - // Check free space to be actually free - QByteArray freeSpace = volume.mid(fileOffset); - if (freeSpace.count(empty) != freeSpace.count()) { - // Search for the first non-empty byte - UINT32 i; - UINT32 size = freeSpace.size(); - const CHAR8* current = freeSpace.constData(); - for (i = 0; i < size; i++) { - if (*current++ != empty) - break; - } - - // Align found index to file alignment - // It must be possible because minimum 16 bytes of empty were found before - if (i != ALIGN8(i)) - i = ALIGN8(i) - 8; - - // Add all bytes before as free space... - 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); - } - // ... 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); - 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); - } - break; // Exit from loop - } - - // Check file alignment - const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)header.constData(); - UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; - UINT32 alignment = (UINT32)pow(2.0, alignmentPower); - if ((fileOffset + sizeof(EFI_FFS_FILE_HEADER)) % alignment) - msgUnalignedFile = true; - - // Check file GUID - if (fileHeader->Type != EFI_FV_FILETYPE_PAD && files.indexOf(header.left(sizeof(EFI_GUID))) != -1) - msgDuplicateGuid = true; - - // Add file GUID to queue - files.enqueue(header.left(sizeof(EFI_GUID))); - - // Parse file - QModelIndex fileIndex; - result = parseFile(file, fileIndex, empty == '\xFF' ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index); - 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); - if (msgDuplicateGuid) - msg(tr("parseVolume: file with duplicate GUID %1").arg(guidToQString(fileHeader->Name)), fileIndex); - - // Move to next file - fileOffset += fileSize; - fileOffset = ALIGN8(fileOffset); - } - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize) -{ - const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)(volume.constData() + fileOffset); - fileSize = uint24ToUint32(fileHeader->Size); - return ERR_SUCCESS; -} - -UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity, const QModelIndex & parent, const UINT8 mode) -{ - bool msgInvalidHeaderChecksum = false; - bool msgInvalidDataChecksum = false; - bool msgInvalidTailValue = false; - bool msgInvalidType = false; - - // Populate file header - const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)file.constData(); - - // Check file state - // Construct empty byte for this file - char empty = erasePolarity ? '\xFF' : '\x00'; - - // Check header checksum - QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); - QByteArray tempHeader = header; - EFI_FFS_FILE_HEADER* tempFileHeader = (EFI_FFS_FILE_HEADER*)(tempHeader.data()); - tempFileHeader->IntegrityCheck.Checksum.Header = 0; - tempFileHeader->IntegrityCheck.Checksum.File = 0; - UINT8 calculated = calculateChecksum8((const UINT8*)tempFileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1); - if (fileHeader->IntegrityCheck.Checksum.Header != calculated) - msgInvalidHeaderChecksum = true; - - // Check data checksum - // Data checksum must be calculated - if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { - UINT32 bufferSize = file.size() - sizeof(EFI_FFS_FILE_HEADER); - // Exclude file tail from data checksum calculation - if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) - bufferSize -= sizeof(UINT16); - calculated = calculateChecksum8((const UINT8*)(file.constData() + sizeof(EFI_FFS_FILE_HEADER)), bufferSize); - if (fileHeader->IntegrityCheck.Checksum.File != calculated) - msgInvalidDataChecksum = true; - } - // Data checksum must be one of predefined values - else if (fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM && fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2) - msgInvalidDataChecksum = true; - - // Get file body - QByteArray body = file.right(file.size() - sizeof(EFI_FFS_FILE_HEADER)); - // Check for file tail presence - QByteArray tail; - if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) - { - //Check file tail; - tail = body.right(sizeof(UINT16)); - UINT16 tailValue = *(UINT16*)tail.constData(); - if (fileHeader->IntegrityCheck.TailReference != (UINT16)~tailValue) - msgInvalidTailValue = true; - - // Remove tail from file body - body = body.left(body.size() - sizeof(UINT16)); - } - - // Parse current file by default - bool parseCurrentFile = true; - bool parseAsBios = false; - - // Check file type - switch (fileHeader->Type) - { - case EFI_FV_FILETYPE_ALL: - parseAsBios = true; - break; - case EFI_FV_FILETYPE_RAW: - parseAsBios = true; - break; - case EFI_FV_FILETYPE_FREEFORM: - break; - case EFI_FV_FILETYPE_SECURITY_CORE: - break; - case EFI_FV_FILETYPE_PEI_CORE: - break; - case EFI_FV_FILETYPE_DXE_CORE: - break; - case EFI_FV_FILETYPE_PEIM: - break; - case EFI_FV_FILETYPE_DRIVER: - break; - case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: - break; - case EFI_FV_FILETYPE_APPLICATION: - break; - case EFI_FV_FILETYPE_SMM: - break; - case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: - break; - case EFI_FV_FILETYPE_COMBINED_SMM_DXE: - break; - case EFI_FV_FILETYPE_SMM_CORE: - break; - case EFI_FV_FILETYPE_PAD: - break; - default: - msgInvalidType = true; - parseCurrentFile = false; - }; - - // Check for empty file - bool parseAsNonEmptyPadFile = false; - if (body.count(empty) == body.size()) { - // No need to parse empty files - parseCurrentFile = false; - } - // Check for non-empty pad file - else if (fileHeader->Type == EFI_FV_FILETYPE_PAD) { - parseAsNonEmptyPadFile = true; - } - - // Get info - QString name; - QString info; - if (fileHeader->Type != EFI_FV_FILETYPE_PAD) - name = guidToQString(fileHeader->Name); - 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()).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); - - // Show messages - if (msgInvalidHeaderChecksum) - msg(tr("parseFile: invalid header checksum"), index); - if (msgInvalidDataChecksum) - msg(tr("parseFile: invalid data checksum"), index); - if (msgInvalidTailValue) - 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; - - // Parse non-empty pad file - if (parseAsNonEmptyPadFile) { - // Search for the first non-empty byte - UINT32 i; - UINT32 size = body.size(); - const CHAR8* current = body.constData(); - for (i = 0; i < size; i++) { - if (*current++ != empty) - break; - } - // 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); - } - // ... 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); - - // Show message - msg(tr("parseFile: non-empty pad-file contents will be destroyed after volume modifications"), dataIndex); - - return ERR_SUCCESS; - } - - // Parse file as BIOS space - UINT8 result; - if (parseAsBios) { - result = parseBios(body, index); - if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) - msg(tr("parseFile: parsing file as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); - return result; - } - - // Parse sections - result = parseSections(body, index); - if (result) - return result; - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize) -{ - const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(file.constData() + sectionOffset); - sectionSize = uint24ToUint32(sectionHeader->Size); - return ERR_SUCCESS; -} - -UINT8 FfsEngine::parseSections(const QByteArray & body, const QModelIndex & parent) -{ - // Search for and parse all sections - UINT32 sectionOffset = 0; - UINT32 sectionSize; - UINT32 bodySize = body.size(); - UINT8 result; - - while (true) { - // Get section size - result = getSectionSize(body, sectionOffset, sectionSize); - if (result) - return result; - - // Parse section - QModelIndex sectionIndex; - result = parseSection(body.mid(sectionOffset, sectionSize), sectionIndex, parent); - if (result) - return result; - - // Move to next section - sectionOffset += sectionSize; - sectionOffset = ALIGN4(sectionOffset); - - // Exit from loop if no sections left - if (sectionOffset >= bodySize) - break; - } - - return ERR_SUCCESS; -} - -void FfsEngine::parseAprioriRawSection(const QByteArray & body, QString & parsed) -{ - parsed.clear(); - - UINT32 count = body.size() / sizeof(EFI_GUID); - if (count > 0) { - for (UINT32 i = 0; i < count; i++) { - const EFI_GUID* guid = (const EFI_GUID*)body.constData() + i; - parsed += tr("\n%1").arg(guidToQString(*guid)); - } - } -} - -UINT8 FfsEngine::parseDepexSection(const QByteArray & body, QString & parsed) -{ - parsed.clear(); - // Check data to be present - if (!body.size()) - return ERR_INVALID_PARAMETER; - - const EFI_GUID * guid; - const UINT8* current = (const UINT8*)body.constData(); - - // Special cases of first opcode - switch (*current) { - case EFI_DEP_BEFORE: - if (body.size() != 2*EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) - return ERR_DEPEX_PARSE_FAILED; - guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); - parsed += tr("\nBEFORE %1").arg(guidToQString(*guid)); - current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); - if (*current != EFI_DEP_END) - return ERR_DEPEX_PARSE_FAILED; - return ERR_SUCCESS; - case EFI_DEP_AFTER: - if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) - return ERR_DEPEX_PARSE_FAILED; - guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); - parsed += tr("\nAFTER %1").arg(guidToQString(*guid)); - current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); - if (*current != EFI_DEP_END) - return ERR_DEPEX_PARSE_FAILED; - return ERR_SUCCESS; - case EFI_DEP_SOR: - if (body.size() <= 2 * EFI_DEP_OPCODE_SIZE) { - return ERR_DEPEX_PARSE_FAILED; - } - parsed += tr("\nSOR"); - current += EFI_DEP_OPCODE_SIZE; - break; - } - - // Parse the rest of depex - while (current - (const UINT8*)body.constData() < body.size()) { - switch (*current) { - case EFI_DEP_BEFORE: - case EFI_DEP_AFTER: - case EFI_DEP_SOR: - return ERR_DEPEX_PARSE_FAILED; - case EFI_DEP_PUSH: - // Check that the rest of depex has correct size - if ((UINT32)body.size() - (UINT32)(current - (const UINT8*)body.constData()) <= EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) { - parsed.clear(); - return ERR_DEPEX_PARSE_FAILED; - } - guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); - parsed += tr("\nPUSH %1").arg(guidToQString(*guid)); - current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); - break; - case EFI_DEP_AND: - parsed += tr("\nAND"); - current += EFI_DEP_OPCODE_SIZE; - break; - case EFI_DEP_OR: - parsed += tr("\nOR"); - current += EFI_DEP_OPCODE_SIZE; - break; - case EFI_DEP_NOT: - parsed += tr("\nNOT"); - current += EFI_DEP_OPCODE_SIZE; - break; - case EFI_DEP_TRUE: - parsed += tr("\nTRUE"); - current += EFI_DEP_OPCODE_SIZE; - break; - case EFI_DEP_FALSE: - parsed += tr("\nFALSE"); - current += EFI_DEP_OPCODE_SIZE; - break; - case EFI_DEP_END: - parsed += tr("\nEND"); - current += EFI_DEP_OPCODE_SIZE; - // Check that END is the last opcode - if (current - (const UINT8*)body.constData() < body.size()) { - parsed.clear(); - return ERR_DEPEX_PARSE_FAILED; - } - break; - default: - return ERR_DEPEX_PARSE_FAILED; - break; - } - } - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) -{ - const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); - QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); - QString info; - QByteArray header; - QByteArray body; - UINT32 headerSize; - UINT8 result; - - switch (sectionHeader->Type) { - // Encapsulated sections - case EFI_SECTION_COMPRESSION: - { - bool parseCurrentSection = true; - QByteArray decompressed; - UINT8 algorithm; - const EFI_COMPRESSION_SECTION* compressedSectionHeader = (const EFI_COMPRESSION_SECTION*)sectionHeader; - header = section.left(sizeof(EFI_COMPRESSION_SECTION)); - body = section.mid(sizeof(EFI_COMPRESSION_SECTION)); - algorithm = COMPRESSION_ALGORITHM_UNKNOWN; - // Decompress section - result = decompress(body, compressedSectionHeader->CompressionType, decompressed, &algorithm); - if (result) - parseCurrentSection = false; - - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nCompression type: %8\nDecompressed size: %9h (%10)") - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()) - .arg(compressionTypeToQString(algorithm)) - .hexarg(compressedSectionHeader->UncompressedLength).arg(compressedSectionHeader->UncompressedLength); - - // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, QByteArray(), parent, mode); - - // Show message - if (!parseCurrentSection) - msg(tr("parseSection: decompression failed with error \"%1\"").arg(errorMessage(result)), index); - else { // Parse decompressed data - result = parseSections(decompressed, index); - if (result) - return result; - } - } break; - - case EFI_SECTION_GUID_DEFINED: - { - bool parseCurrentSection = true; - bool msgUnknownGuid = false; - bool msgInvalidCrc = false; - bool msgUnknownAuth = false; - bool msgSigned = false; - bool msgUnknownSignature = false; - bool msgUnknownUefiGuidSignature = false; - - const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader; - header = section.left(sizeof(EFI_GUID_DEFINED_SECTION)); - guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(header.constData()); - header = section.left(guidDefinedSectionHeader->DataOffset); - guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(header.constData()); - body = section.mid(guidDefinedSectionHeader->DataOffset); - QByteArray processed = body; - - // Get info - name = guidToQString(guidDefinedSectionHeader->SectionDefinitionGuid); - info = tr("Section GUID: %1\nType: %2h\nFull size: %3h (%4)\nHeader size: %5h (%6)\nBody size: %7h (%8)\nData offset: %9h\nAttributes: %10h") - .arg(name) - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()) - .hexarg(guidDefinedSectionHeader->DataOffset) - .hexarg2(guidDefinedSectionHeader->Attributes, 4); - - UINT8 algorithm = COMPRESSION_ALGORITHM_NONE; - // Check if section requires processing - if (guidDefinedSectionHeader->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) { - // 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()); - } - else if (algorithm == COMPRESSION_ALGORITHM_EFI11) { - info += tr("\nCompression type: EFI 1.1"); - info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); - } - 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()); - } - else - info += tr("\nCompression type: unknown"); - } - // Signed section - 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) { - 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) { - info += tr("\nSignature subtype: RSA2048/SHA256"); - // TODO: show signature info in Information panel - } - else if (QByteArray((const char*)&guidCertificateHeader->CertType, sizeof(EFI_GUID)) == EFI_CERT_TYPE_PKCS7_GUID) { - info += tr("\nSignature subtype: PCKS7"); - // TODO: show signature info in Information panel - } - else { - info += tr("\nSignature subtype: unknown"); - msgUnknownUefiGuidSignature = true; - } - } - else if (certificateHeader->CertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { - info += tr("\nSignature type: PCKS7"); - // TODO: show signature info in Information panel - } - else { - info += tr("\nSignature type: unknown"); - msgUnknownSignature = true; - } - - // Add additional to the header - header.append(body.left(certificateHeader->Length)); - // Get new body - processed = body = body.mid(certificateHeader->Length); - } - // Unknown GUIDed section - else { - msgUnknownGuid = true; - parseCurrentSection = false; - } - } - // Check if section requires checksum calculation - else if (guidDefinedSectionHeader->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) - { - // CRC32 section - if (QByteArray((const char*)&guidDefinedSectionHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_CRC32) { - info += tr("\nChecksum type: CRC32"); - // Calculate CRC32 of section data - UINT32 crc = crc32(0, (const UINT8*)body.constData(), body.size()); - // Check stored CRC32 - if (crc == *(const UINT32*)(header.constData() + sizeof(EFI_GUID_DEFINED_SECTION))) { - info += tr("\nChecksum: valid"); - } - else { - info += tr("\nChecksum: invalid"); - msgInvalidCrc = true; - } - } - else - msgUnknownAuth = true; - } - - // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, QByteArray(), parent, mode); - - // Show messages - if (msgUnknownGuid) - msg(tr("parseSection: GUID defined section with unknown processing method"), index); - if (msgUnknownAuth) - msg(tr("parseSection: GUID defined section with unknown authentication method"), index); - if (msgInvalidCrc) - msg(tr("parseSection: GUID defined section with invalid CRC32"), index); - if (msgSigned) - 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 (msgUnknownSignature) - msg(tr("parseSection: GUID defined section with unknown signature type"), index); - - if (!parseCurrentSection) { - msg(tr("parseSection: GUID defined section can not be processed"), index); - } - else { // Parse processed data - result = parseSections(processed, index); - if (result) - return result; - } - } break; - - case EFI_SECTION_DISPOSABLE: - { - header = section.left(sizeof(EFI_DISPOSABLE_SECTION)); - body = section.mid(sizeof(EFI_DISPOSABLE_SECTION)); - - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .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); - - // Parse section body - result = parseSections(body, index); - if (result) - return result; - } break; - - // Leaf sections - case EFI_SECTION_DXE_DEPEX: - case EFI_SECTION_PEI_DEPEX: - case EFI_SECTION_SMM_DEPEX: { - bool msgDepexParseFailed = false; - headerSize = sizeOfSectionHeader(sectionHeader); - header = section.left(headerSize); - body = section.mid(headerSize); - - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()); - - // Parse dependency expression - QString str; - result = parseDepexSection(body, str); - if (result) - msgDepexParseFailed = true; - else if (str.count()) - 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); - - // Show messages - if (msgDepexParseFailed) - msg(tr("parseSection: dependency expression parsing failed"), index); - } break; - - case EFI_SECTION_TE: { - headerSize = sizeOfSectionHeader(sectionHeader); - header = section.left(headerSize); - body = section.mid(headerSize); - - // Get standard info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()); - - // Get TE info - bool msgInvalidSignature = false; - const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)body.constData(); - UINT32 teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); - 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) - .arg(machineTypeToQString(teHeader->Machine)) - .arg(teHeader->NumberOfSections) - .hexarg2(teHeader->Subsystem, 2) - .hexarg(teHeader->StrippedSize).arg(teHeader->StrippedSize) - .hexarg(teHeader->BaseOfCode) - .hexarg(teHeader->AddressOfEntryPoint) - .hexarg(teHeader->ImageBase) - .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); - - // Show messages - if (msgInvalidSignature) { - msg("parseSection: TE image with invalid TE signature", index); - } - - // Special case of PEI Core - QModelIndex core = model->findParentOfType(index, Types::File); - if (core.isValid() && model->subtype(core) == EFI_FV_FILETYPE_PEI_CORE - && oldPeiCoreEntryPoint == 0) { - result = getEntryPoint(model->body(index), oldPeiCoreEntryPoint); - if (result) - msg(tr("parseSection: can't get original PEI core entry point"), index); - } - } break; - - case EFI_SECTION_PE32: - case EFI_SECTION_PIC: { - headerSize = sizeOfSectionHeader(sectionHeader); - header = section.left(headerSize); - body = section.mid(headerSize); - - // Get standard info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()); - - // Get PE info - bool msgInvalidDosSignature = false; - bool msgInvalidPeSignature = false; - bool msgUnknownOptionalHeaderSignature = false; - - const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)body.constData(); - if (dosHeader->e_magic != EFI_IMAGE_DOS_SIGNATURE) { - info += tr("\nDOS signature: %1h, invalid").hexarg2(dosHeader->e_magic, 4); - msgInvalidDosSignature = true; - } - else { - const EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(body.constData() + dosHeader->e_lfanew); - if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) { - info += tr("\nPE signature: %1h, invalid").hexarg2(peHeader->Signature, 8); - msgInvalidPeSignature = true; - } - else { - const EFI_IMAGE_FILE_HEADER* imageFileHeader = (const EFI_IMAGE_FILE_HEADER*)(peHeader + 1); - info += tr("\nDOS signature: %1h\nPE signature: %2h\nMachine type: %3\nNumber of sections: %4\nCharacteristics: %5h") - .hexarg2(dosHeader->e_magic, 4) - .hexarg2(peHeader->Signature, 8) - .arg(machineTypeToQString(imageFileHeader->Machine)) - .arg(imageFileHeader->NumberOfSections) - .hexarg2(imageFileHeader->Characteristics, 4); - - EFI_IMAGE_OPTIONAL_HEADER_POINTERS_UNION optionalHeader; - optionalHeader.H32 = (const EFI_IMAGE_OPTIONAL_HEADER32*)(imageFileHeader + 1); - if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { - info += tr("\nOptional header signature: %1h\nSubsystem: %2h\nRelativeEntryPoint: %3h\nBaseOfCode: %4h\nImageBase: %5h\nEntryPoint: %6h") - .hexarg2(optionalHeader.H32->Magic, 4) - .hexarg2(optionalHeader.H32->Subsystem, 4) - .hexarg(optionalHeader.H32->AddressOfEntryPoint) - .hexarg(optionalHeader.H32->BaseOfCode) - .hexarg(optionalHeader.H32->ImageBase) - .hexarg(optionalHeader.H32->ImageBase + optionalHeader.H32->AddressOfEntryPoint); - } - else if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { - info += tr("\nOptional header signature: %1h\nSubsystem: %2h\nRelativeEntryPoint: %3h\nBaseOfCode: %4h\nImageBase: %5h\nEntryPoint: %6h") - .hexarg2(optionalHeader.H64->Magic, 4) - .hexarg2(optionalHeader.H64->Subsystem, 4) - .hexarg(optionalHeader.H64->AddressOfEntryPoint) - .hexarg(optionalHeader.H64->BaseOfCode) - .hexarg(optionalHeader.H64->ImageBase) - .hexarg(optionalHeader.H64->ImageBase + optionalHeader.H64->AddressOfEntryPoint); - } - else { - info += tr("\nOptional header signature: %1h, unknown").hexarg2(optionalHeader.H64->Magic, 4); - msgUnknownOptionalHeaderSignature = true; - } - } - } - - // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); - - // Show messages - if (msgInvalidDosSignature) { - msg("parseSection: PE32 image with invalid DOS signature", index); - } - if (msgInvalidPeSignature) { - msg("parseSection: PE32 image with invalid PE signature", index); - } - if (msgUnknownOptionalHeaderSignature) { - msg("parseSection: PE32 image with unknown optional header signature", index); - } - - // Special case of PEI Core - QModelIndex core = model->findParentOfType(index, Types::File); - if (core.isValid() && model->subtype(core) == EFI_FV_FILETYPE_PEI_CORE - && oldPeiCoreEntryPoint == 0) { - result = getEntryPoint(model->body(index), oldPeiCoreEntryPoint); - if (result) - msg(tr("parseSection: can't get original PEI core entry point"), index); - } - } break; - - case EFI_SECTION_COMPATIBILITY16: { - headerSize = sizeOfSectionHeader(sectionHeader); - header = section.left(headerSize); - body = section.mid(headerSize); - - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .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); - } break; - - case EFI_SECTION_FREEFORM_SUBTYPE_GUID: { - header = section.left(sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION)); - body = section.mid(sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION)); - - const EFI_FREEFORM_SUBTYPE_GUID_SECTION* fsgHeader = (const EFI_FREEFORM_SUBTYPE_GUID_SECTION*)sectionHeader; - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nSubtype GUID: %8") - .hexarg2(fsgHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()) - .arg(guidToQString(fsgHeader->SubTypeGuid)); - - // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); - - // Rename section - model->setName(index, guidToQString(fsgHeader->SubTypeGuid)); - } break; - - case EFI_SECTION_VERSION: { - header = section.left(sizeof(EFI_VERSION_SECTION)); - body = section.mid(sizeof(EFI_VERSION_SECTION)); - - const EFI_VERSION_SECTION* versionHeader = (const EFI_VERSION_SECTION*)sectionHeader; - - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nBuild number: %8\nVersion string: %9") - .hexarg2(versionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()) - .arg(versionHeader->BuildNumber) - .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); - } break; - - case EFI_SECTION_USER_INTERFACE: { - header = section.left(sizeof(EFI_USER_INTERFACE_SECTION)); - body = section.mid(sizeof(EFI_USER_INTERFACE_SECTION)); - QString text = QString::fromUtf16((const ushort*)body.constData()); - - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nText: %8") - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()) - .arg(text); - - // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); - - // Rename parent file - model->setText(model->findParentOfType(parent, Types::File), text); - } break; - - case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: { - header = section.left(sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); - body = section.mid(sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); - - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .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); - - // Parse section body as BIOS space - result = parseBios(body, index); - if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) { - msg(tr("parseSection: parsing firmware volume image section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); - return result; - } - } break; - - case EFI_SECTION_RAW: { - bool parsed = false; - header = section.left(sizeof(EFI_RAW_SECTION)); - body = section.mid(sizeof(EFI_RAW_SECTION)); - - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()); - - // Check for apriori file - QModelIndex parentFile = model->findParentOfType(parent, Types::File); - QByteArray parentFileGuid = model->header(parentFile).left(sizeof(EFI_GUID)); - if (parentFileGuid == EFI_PEI_APRIORI_FILE_GUID) { - // Mark file as parsed - parsed = true; - - // Parse apriori file list - QString str; - parseAprioriRawSection(body, str); - if (str.count()) - info += tr("\nFile list:%1").arg(str); - - // Set parent file text - model->setText(parentFile, tr("PEI apriori file")); - } - else if (parentFileGuid == EFI_DXE_APRIORI_FILE_GUID) { - // Mark file as parsed - parsed = true; - - // Parse apriori file list - QString str; - parseAprioriRawSection(body, str); - if (str.count()) - info += tr("\nFile list:%1").arg(str); - - // Set parent file text - model->setText(parentFile, tr("DXE apriori file")); - } - - // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); - - // Parse section body as BIOS space - 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); - return result; - } - } - } break; - - case SCT_SECTION_POSTCODE: - case INSYDE_SECTION_POSTCODE: { - header = section.left(sizeof(POSTCODE_SECTION)); - body = section.mid(sizeof(POSTCODE_SECTION)); - - const POSTCODE_SECTION* postcodeHeader = (const POSTCODE_SECTION*)sectionHeader; - - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nPostcode: %8h") - .hexarg2(postcodeHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()) - .hexarg(postcodeHeader->Postcode); - - // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); - } break; - - default: - header = section.left(sizeof(EFI_COMMON_SECTION_HEADER)); - body = section.mid(sizeof(EFI_COMMON_SECTION_HEADER)); - // Get info - info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") - .hexarg2(sectionHeader->Type, 2) - .hexarg(section.size()).arg(section.size()) - .hexarg(header.size()).arg(header.size()) - .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); - msg(tr("parseSection: section with unknown type %1h").hexarg2(sectionHeader->Type, 2), index); - } - return ERR_SUCCESS; -} - -// Operations on tree items -UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByteArray & header, const QByteArray & body, const UINT8 mode, const UINT8 action, const UINT8 algorithm) -{ - QByteArray created; - UINT8 result; - QModelIndex fileIndex; - - if (!index.isValid() || !index.parent().isValid()) - return ERR_INVALID_PARAMETER; - - QModelIndex parent; - if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) - parent = index.parent(); - else - parent = index; - - // Create item - if (type == Types::Region) { - UINT8 type = model->subtype(index); - switch (type) { - case Subtypes::BiosRegion: - result = parseBiosRegion(body, fileIndex, index, mode); - break; - case Subtypes::MeRegion: - result = parseMeRegion(body, fileIndex, index, mode); - break; - case Subtypes::GbeRegion: - result = parseGbeRegion(body, fileIndex, index, mode); - break; - case Subtypes::PdrRegion: - result = parsePdrRegion(body, fileIndex, index, mode); - break; - default: - return ERR_NOT_IMPLEMENTED; - } - - if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) - return result; - - // Set action - model->setAction(fileIndex, action); - } - else if (type == Types::Volume) { - QByteArray volume; - if (header.isEmpty()) // Whole volume - volume.append(body); - else { // Body only - volume.append(model->header(index)).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(); - bool erasePolarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY; - volume.append(QByteArray(sizeDiff, erasePolarity ? '\xFF' : '\x00')); - } - } - result = parseVolume(volume, fileIndex, index, mode); - if (result) - return result; - - // Set action - model->setAction(fileIndex, action); - } - else if (type == Types::File) { - if (model->type(parent) != Types::Volume) - return ERR_INVALID_FILE; - - const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)model->header(parent).constData(); - UINT8 revision = volumeHeader->Revision; - bool erasePolarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY; - - if (header.size() != sizeof(EFI_FFS_FILE_HEADER)) - return ERR_INVALID_FILE; - - QByteArray newHeader = header; - EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newHeader.data(); - - - // Correct file size - UINT8 tailSize = fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT ? sizeof(UINT16) : 0; - uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + body.size() + tailSize, fileHeader->Size); - - // Recalculate header checksum - fileHeader->IntegrityCheck.Checksum.Header = 0; - fileHeader->IntegrityCheck.Checksum.File = 0; - fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)fileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1); - - // Recalculate data checksum, if needed - if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) - fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((const UINT8*)body.constData(), body.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 tail, if needed - if (tailSize) { - UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; - UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; - created.append(ht).append(ft); - } - - // Set file state - UINT8 state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION; - if (erasePolarity) - state = ~state; - fileHeader->State = state; - - // Prepend header - created.prepend(newHeader); - - // Parse file - result = parseFile(created, fileIndex, erasePolarity ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index, mode); - if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) - return result; - - // Set action - model->setAction(fileIndex, action); - - // Rebase all PEI-files that follow - rebasePeiFiles(fileIndex); - } - else if (type == Types::Section) { - if (model->type(parent) != Types::File && model->type(parent) != Types::Section) - return ERR_INVALID_SECTION; - - if (header.size() < (int) sizeof(EFI_COMMON_SECTION_HEADER)) - return ERR_INVALID_SECTION; - - QByteArray newHeader = header; - EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)newHeader.data(); - - switch (commonHeader->Type) - { - case EFI_SECTION_COMPRESSION: { - EFI_COMPRESSION_SECTION* sectionHeader = (EFI_COMPRESSION_SECTION*)newHeader.data(); - // Correct uncompressed size - sectionHeader->UncompressedLength = body.size(); - - // Set compression type - if (algorithm == COMPRESSION_ALGORITHM_NONE) - sectionHeader->CompressionType = EFI_NOT_COMPRESSED; - else if (algorithm == COMPRESSION_ALGORITHM_EFI11 || algorithm == COMPRESSION_ALGORITHM_TIANO) - sectionHeader->CompressionType = EFI_STANDARD_COMPRESSION; - else if (algorithm == COMPRESSION_ALGORITHM_LZMA || algorithm == COMPRESSION_ALGORITHM_IMLZMA) - sectionHeader->CompressionType = EFI_CUSTOMIZED_COMPRESSION; - else - return ERR_UNKNOWN_COMPRESSION_ALGORITHM; - - // Compress body - QByteArray compressed; - result = compress(body, algorithm, compressed); - if (result) - return result; - - // Correct section size - uint32ToUint24(header.size() + compressed.size(), commonHeader->Size); - - // Append header and body - created.append(newHeader).append(compressed); - - // Parse section - QModelIndex sectionIndex; - result = parseSection(created, sectionIndex, index, mode); - if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) - return result; - - // Set create action - model->setAction(sectionIndex, action); - - // Find parent file for rebase - fileIndex = model->findParentOfType(parent, Types::File); - } - break; - case EFI_SECTION_GUID_DEFINED:{ - // Compress body - QByteArray compressed; - result = compress(body, algorithm, compressed); - if (result) - return result; - - // Correct section size - uint32ToUint24(header.size() + compressed.size(), commonHeader->Size); - - // Append header and body - created.append(newHeader).append(compressed); - - // Parse section - QModelIndex sectionIndex; - result = parseSection(created, sectionIndex, index, mode); - if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) - return result; - - // Set create action - model->setAction(sectionIndex, action); - - // Find parent file for rebase - fileIndex = model->findParentOfType(parent, Types::File); - } - break; - default: - // Correct section size - uint32ToUint24(header.size() + body.size(), commonHeader->Size); - - // Append header and body - created.append(newHeader).append(body); - - // Parse section - QModelIndex sectionIndex; - result = parseSection(created, sectionIndex, index, mode); - if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) - return result; - - // Set create action - model->setAction(sectionIndex, action); - - // Find parent file for rebase - fileIndex = model->findParentOfType(parent, Types::File); - } - - // Rebase all PEI-files that follow - rebasePeiFiles(fileIndex); - } - else - return ERR_NOT_IMPLEMENTED; - - return ERR_SUCCESS; -} - -void FfsEngine::rebasePeiFiles(const QModelIndex & index) -{ - // Rebase all PE32 and TE sections in PEI-files after modified file - for (int i = index.row(); i < model->rowCount(index.parent()); i++) { - // PEI-file - QModelIndex currentFileIndex = index.parent().child(i, 0); - if (model->subtype(currentFileIndex) == EFI_FV_FILETYPE_PEI_CORE || - model->subtype(currentFileIndex) == EFI_FV_FILETYPE_PEIM || - model->subtype(currentFileIndex) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER) { - for (int j = 0; j < model->rowCount(currentFileIndex); j++) { - // Section in that file - QModelIndex currentSectionIndex = currentFileIndex.child(j, 0); - // If section stores PE32 or TE image - if (model->subtype(currentSectionIndex) == EFI_SECTION_PE32 || model->subtype(currentSectionIndex) == EFI_SECTION_TE) - // Set rebase action - model->setAction(currentSectionIndex, Actions::Rebase); - } - } - } -} - -UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, const UINT8 mode) -{ - if (!index.isValid() || !index.parent().isValid()) - return ERR_INVALID_PARAMETER; - - QModelIndex parent; - if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) - parent = index.parent(); - else - parent = index; - - // Determine type of item to insert - UINT8 type; - UINT32 headerSize; - if (model->type(parent) == Types::Volume) { - type = Types::File; - headerSize = sizeof(EFI_FFS_FILE_HEADER); - } - else if (model->type(parent) == Types::File) { - type = Types::Section; - const EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData(); - headerSize = sizeOfSectionHeader(commonHeader); - } - else if (model->type(parent) == Types::Section) { - type = Types::Section; - const EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData(); - headerSize = sizeOfSectionHeader(commonHeader); - } - else - return ERR_NOT_IMPLEMENTED; - - return create(index, type, object.left(headerSize), object.right(object.size() - headerSize), mode, Actions::Insert); -} - -UINT8 FfsEngine::replace(const QModelIndex & index, const QByteArray & object, const UINT8 mode) -{ - if (!index.isValid()) - return ERR_INVALID_PARAMETER; - - // Determine type of item to replace - UINT32 headerSize; - UINT8 result; - if (model->type(index) == Types::Region) { - if (mode == REPLACE_MODE_AS_IS) - result = create(index, Types::Region, 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); - } - else if (mode == REPLACE_MODE_BODY) { - result = create(index, Types::Volume, model->header(index), object, CREATE_MODE_AFTER, Actions::Replace); - } - else - return ERR_NOT_IMPLEMENTED; - } - else if (model->type(index) == Types::File) { - if (mode == REPLACE_MODE_AS_IS) { - headerSize = sizeof(EFI_FFS_FILE_HEADER); - result = create(index, Types::File, object.left(headerSize), object.right(object.size() - headerSize), CREATE_MODE_AFTER, Actions::Replace); - } - else if (mode == REPLACE_MODE_BODY) - result = create(index, Types::File, model->header(index), object, CREATE_MODE_AFTER, Actions::Replace); - else - return ERR_NOT_IMPLEMENTED; - } - else if (model->type(index) == Types::Section) { - if (mode == REPLACE_MODE_AS_IS) { - const EFI_COMMON_SECTION_HEADER* commonHeader = (const EFI_COMMON_SECTION_HEADER*)object.constData(); - headerSize = sizeOfSectionHeader(commonHeader); - result = create(index, Types::Section, object.left(headerSize), object.right(object.size() - headerSize), CREATE_MODE_AFTER, Actions::Replace); - } - else if (mode == REPLACE_MODE_BODY) { - result = create(index, Types::Section, model->header(index), object, CREATE_MODE_AFTER, Actions::Replace, model->compression(index)); - } - else - return ERR_NOT_IMPLEMENTED; - } - else - return ERR_NOT_IMPLEMENTED; - - // Check create result - if (result) - return result; - - // Set remove action to replaced item - model->setAction(index, Actions::Remove); - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::extract(const QModelIndex & index, QByteArray & extracted, const UINT8 mode) -{ - if (!index.isValid()) - return ERR_INVALID_PARAMETER; - - if (mode == EXTRACT_MODE_AS_IS) { - // Extract as is, with header and body - extracted.clear(); - extracted.append(model->header(index)); - extracted.append(model->body(index)); - if (model->type(index) == Types::File) { - //!TODO: add volume revision check, maybe? - const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)model->header(index).constData(); - if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { - UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; - UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; - extracted.append(ht).append(ft); - } - } - } - else if (mode == EXTRACT_MODE_BODY) { - // Extract without header and tail - extracted.clear(); - // Special case of compressed bodies - if (model->type(index) == Types::Section) { - QByteArray decompressed; - UINT8 result; - if (model->subtype(index) == EFI_SECTION_COMPRESSION) { - const EFI_COMPRESSION_SECTION* compressedHeader = (const EFI_COMPRESSION_SECTION*)model->header(index).constData(); - result = decompress(model->body(index), compressedHeader->CompressionType, decompressed); - if (result) - return result; - extracted.append(decompressed); - return ERR_SUCCESS; - } - else if (model->subtype(index) == EFI_SECTION_GUID_DEFINED) { - QByteArray decompressed; - // Check if section requires processing - const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)model->header(index).constData(); - if (guidDefinedSectionHeader->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) { - // Try to decompress section body using both known compression algorithms - result = decompress(model->body(index), EFI_STANDARD_COMPRESSION, decompressed); - if (result) { - result = decompress(model->body(index), EFI_CUSTOMIZED_COMPRESSION, decompressed); - if (result) - return result; - } - extracted.append(decompressed); - return ERR_SUCCESS; - } - } - } - - extracted.append(model->body(index)); - } - else - return ERR_UNKNOWN_EXTRACT_MODE; - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::remove(const QModelIndex & index) -{ - if (!index.isValid()) - return ERR_INVALID_PARAMETER; - - // Set action for the item - model->setAction(index, Actions::Remove); - - QModelIndex fileIndex; - - if (model->type(index) == Types::Volume && model->rowCount(index) > 0) - fileIndex = index.child(0, 0); - else if (model->type(index) == Types::File) - fileIndex = index; - else if (model->type(index) == Types::Section) - fileIndex = model->findParentOfType(index, Types::File); - else - return ERR_SUCCESS; - - // Rebase all PEI-files that follow - rebasePeiFiles(fileIndex); - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::rebuild(const QModelIndex & index) -{ - if (!index.isValid()) - return ERR_INVALID_PARAMETER; - - // Set action for the item - model->setAction(index, Actions::Rebuild); - - QModelIndex fileIndex; - - if (model->type(index) == Types::Volume && model->rowCount(index) > 0) - fileIndex = index.child(0, 0); - else if (model->type(index) == Types::File) - fileIndex = index; - else if (model->type(index) == Types::Section) - fileIndex = model->findParentOfType(index, Types::File); - else - return ERR_SUCCESS; - - // Rebase all PEI-files that follow - rebasePeiFiles(fileIndex); - - return ERR_SUCCESS; -} - -// Compression routines -UINT8 FfsEngine::decompress(const QByteArray & compressedData, const UINT8 compressionType, QByteArray & decompressedData, UINT8 * algorithm) -{ - const UINT8* data; - UINT32 dataSize; - UINT8* decompressed; - UINT32 decompressedSize = 0; - UINT8* scratch; - UINT32 scratchSize = 0; - const EFI_TIANO_HEADER* header; - - switch (compressionType) - { - case EFI_NOT_COMPRESSED: - decompressedData = compressedData; - if (algorithm) - *algorithm = COMPRESSION_ALGORITHM_NONE; - return ERR_SUCCESS; - case EFI_STANDARD_COMPRESSION: - // Get buffer sizes - data = (UINT8*)compressedData.data(); - dataSize = compressedData.size(); - - // Check header to be valid - header = (const EFI_TIANO_HEADER*)data; - if (header->CompSize + sizeof(EFI_TIANO_HEADER) != dataSize) - return ERR_STANDARD_DECOMPRESSION_FAILED; - - // Get info function is the same for both algorithms - if (ERR_SUCCESS != EfiTianoGetInfo(data, dataSize, &decompressedSize, &scratchSize)) - return ERR_STANDARD_DECOMPRESSION_FAILED; - - // Allocate memory - decompressed = new UINT8[decompressedSize]; - scratch = new UINT8[scratchSize]; - - // Decompress section data - - //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)) { - if (algorithm) - *algorithm = COMPRESSION_ALGORITHM_UNKNOWN; - - delete[] decompressed; - delete[] scratch; - return ERR_STANDARD_DECOMPRESSION_FAILED; - } - else if (algorithm) - *algorithm = COMPRESSION_ALGORITHM_EFI11; - } - else if (algorithm) - *algorithm = COMPRESSION_ALGORITHM_TIANO; - - decompressedData = QByteArray((const char*)decompressed, decompressedSize); - - delete[] decompressed; - delete[] scratch; - return ERR_SUCCESS; - case EFI_CUSTOMIZED_COMPRESSION: - // Get buffer sizes - data = (const UINT8*)compressedData.constData(); - dataSize = compressedData.size(); - - // Get info - if (ERR_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) - return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; - - // Allocate memory - decompressed = new UINT8[decompressedSize]; - - // Decompress section data - if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed)) { - // Intel modified LZMA workaround - EFI_COMMON_SECTION_HEADER* shittySectionHeader; - UINT32 shittySectionSize; - // Shitty compressed section with a section header between COMPRESSED_SECTION_HEADER and LZMA_HEADER - // We must determine section header size by checking it's type before we can unpack that non-standard compressed section - shittySectionHeader = (EFI_COMMON_SECTION_HEADER*)data; - shittySectionSize = sizeOfSectionHeader(shittySectionHeader); - - // Decompress section data once again - data += shittySectionSize; - - // Get info again - if (ERR_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) { - delete[] decompressed; - return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; - } - - // Decompress section data again - if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed)) { - if (algorithm) - *algorithm = COMPRESSION_ALGORITHM_UNKNOWN; - delete[] decompressed; - return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; - } - else { - if (algorithm) - *algorithm = COMPRESSION_ALGORITHM_IMLZMA; - decompressedData = QByteArray((const char*)decompressed, decompressedSize); - } - } - else { - if (algorithm) - *algorithm = COMPRESSION_ALGORITHM_LZMA; - decompressedData = QByteArray((const char*)decompressed, decompressedSize); - } - - delete[] decompressed; - return ERR_SUCCESS; - default: - msg(tr("decompress: unknown compression type %1").arg(compressionType)); - if (algorithm) - *algorithm = COMPRESSION_ALGORITHM_UNKNOWN; - return ERR_UNKNOWN_COMPRESSION_ALGORITHM; - } -} - -UINT8 FfsEngine::compress(const QByteArray & data, const UINT8 algorithm, QByteArray & compressedData) -{ - UINT8* compressed; - - switch (algorithm) { - case COMPRESSION_ALGORITHM_NONE: - { - compressedData = data; - return ERR_SUCCESS; - } - break; - case COMPRESSION_ALGORITHM_EFI11: - { - // Try legacy function first - UINT32 compressedSize = 0; - if (EfiCompressLegacy(data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) - return ERR_STANDARD_COMPRESSION_FAILED; - compressed = new UINT8[compressedSize]; - if (EfiCompressLegacy(data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) { - delete[] compressed; - 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 - && decompressed == data) { - delete[] compressed; - return ERR_SUCCESS; - } - delete[] compressed; - - // Legacy function failed, use current one - compressedSize = 0; - if (EfiCompress(data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) - return ERR_STANDARD_COMPRESSION_FAILED; - compressed = new UINT8[compressedSize]; - if (EfiCompress(data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) { - delete[] compressed; - 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; - } - break; - case COMPRESSION_ALGORITHM_TIANO: - { - // Try legacy function first - UINT32 compressedSize = 0; - if (TianoCompressLegacy(data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) - return ERR_STANDARD_COMPRESSION_FAILED; - compressed = new UINT8[compressedSize]; - if (TianoCompressLegacy(data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) { - delete[] compressed; - 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 - && decompressed == data) { - delete[] compressed; - return ERR_SUCCESS; - } - delete[] compressed; - - // Legacy function failed, use current one - compressedSize = 0; - if (TianoCompress(data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) - return ERR_STANDARD_COMPRESSION_FAILED; - compressed = new UINT8[compressedSize]; - if (TianoCompress(data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) { - delete[] compressed; - 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; - } - break; - case COMPRESSION_ALGORITHM_LZMA: - { - UINT32 compressedSize = 0; - if (LzmaCompress((const UINT8*)data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) - return ERR_CUSTOMIZED_COMPRESSION_FAILED; - compressed = new UINT8[compressedSize]; - if (LzmaCompress((const UINT8*)data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) { - delete[] compressed; - return ERR_CUSTOMIZED_COMPRESSION_FAILED; - } - compressedData = QByteArray((const char*)compressed, compressedSize); - delete[] compressed; - return ERR_SUCCESS; - } - break; - case COMPRESSION_ALGORITHM_IMLZMA: - { - UINT32 compressedSize = 0; - QByteArray header = data.left(sizeof(EFI_COMMON_SECTION_HEADER)); - const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)header.constData(); - UINT32 headerSize = sizeOfSectionHeader(sectionHeader); - header = data.left(headerSize); - QByteArray newData = data.mid(headerSize); - if (LzmaCompress((const UINT8*)newData.constData(), newData.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) - return ERR_CUSTOMIZED_COMPRESSION_FAILED; - compressed = new UINT8[compressedSize]; - if (LzmaCompress((const UINT8*)newData.constData(), newData.size(), compressed, &compressedSize) != ERR_SUCCESS) { - delete[] compressed; - return ERR_CUSTOMIZED_COMPRESSION_FAILED; - } - compressedData = header.append(QByteArray((const char*)compressed, compressedSize)); - delete[] compressed; - return ERR_SUCCESS; - } - break; - default: - msg(tr("compress: unknown compression algorithm %1").arg(algorithm)); - return ERR_UNKNOWN_COMPRESSION_ALGORITHM; - } -} - -// Construction routines -UINT8 FfsEngine::constructPadFile(const QByteArray &guid, const UINT32 size, const UINT8 revision, const UINT8 erasePolarity, QByteArray & pad) -{ - if (size < sizeof(EFI_FFS_FILE_HEADER) || erasePolarity == ERASE_POLARITY_UNKNOWN) - return ERR_INVALID_PARAMETER; - - pad = QByteArray(size - guid.size(), erasePolarity == ERASE_POLARITY_TRUE ? '\xFF' : '\x00'); - pad.prepend(guid); - EFI_FFS_FILE_HEADER* header = (EFI_FFS_FILE_HEADER*)pad.data(); - uint32ToUint24(size, header->Size); - header->Attributes = 0x00; - header->Type = EFI_FV_FILETYPE_PAD; - header->State = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID; - // Invert state bits if erase polarity is true - if (erasePolarity == ERASE_POLARITY_TRUE) - header->State = ~header->State; - - // Calculate header checksum - header->IntegrityCheck.Checksum.Header = 0; - header->IntegrityCheck.Checksum.File = 0; - header->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)header, sizeof(EFI_FFS_FILE_HEADER) - 1); - - // Set data checksum - if (revision == 1) - header->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; - else - header->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& reconstructed) -{ - if (!index.isValid()) - return ERR_SUCCESS; - - UINT8 result; - - // No action - if (model->action(index) == Actions::NoAction) { - reconstructed = model->header(index).append(model->body(index)); - return ERR_SUCCESS; - } - - // Other supported actions - else if (model->action(index) == Actions::Rebuild) { - reconstructed.clear(); - // First child will always be descriptor for this type of image - QByteArray descriptor; - result = reconstructRegion(index.child(0, 0), descriptor); - if (result) - return result; - reconstructed.append(descriptor); - - const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor.constData() + sizeof(FLASH_DESCRIPTOR_HEADER)); - 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); - QByteArray pdr; - UINT32 pdrBegin = calculateRegionOffset(regionSection->PdrBase); - UINT32 pdrEnd = pdrBegin + calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); - - UINT32 offset = descriptor.size(); - // Reconstruct other regions - char empty = '\xFF'; - for (int i = 1; i < model->rowCount(index); i++) { - QByteArray region; - result = reconstructRegion(index.child(i, 0), region); - if (result) - return result; - - UINT8 type = model->subtype(index.child(i, 0)); - switch (type) - { - case Subtypes::GbeRegion: - gbe = region; - if (gbeBegin > offset) - reconstructed.append(QByteArray(gbeBegin - offset, empty)); - reconstructed.append(gbe); - offset = gbeEnd; - break; - case Subtypes::MeRegion: - me = region; - if (meBegin > offset) - reconstructed.append(QByteArray(meBegin - offset, empty)); - reconstructed.append(me); - offset = meEnd; - break; - case Subtypes::BiosRegion: - bios = region; - if (biosBegin > offset) - reconstructed.append(QByteArray(biosBegin - offset, empty)); - reconstructed.append(bios); - offset = biosEnd; - break; - case Subtypes::PdrRegion: - pdr = region; - if (pdrBegin > offset) - reconstructed.append(QByteArray(pdrBegin - offset, empty)); - reconstructed.append(pdr); - offset = pdrEnd; - break; - default: - msg(tr("reconstructIntelImage: unknown region type found"), index); - return ERR_INVALID_REGION; - } - } - if ((UINT32)model->body(index).size() > offset) - reconstructed.append(QByteArray((UINT32)model->body(index).size() - offset, empty)); - - // Check size of reconstructed image, it must be same - if (reconstructed.size() > model->body(index).size()) { - msg(tr("reconstructIntelImage: reconstructed body 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("reconstructIntelImage: reconstructed body 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 - return ERR_SUCCESS; - } - - // All other actions are not supported - return ERR_NOT_IMPLEMENTED; -} - -UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconstructed) -{ - if (!index.isValid()) - return ERR_SUCCESS; - - UINT8 result; - - // No action - if (model->action(index) == Actions::NoAction) { - reconstructed = model->header(index).append(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) { - if (model->rowCount(index)) { - reconstructed.clear(); - // Reconstruct children - for (int i = 0; i < model->rowCount(index); i++) { - QByteArray child; - result = reconstruct(index.child(i, 0), child); - if (result) - return result; - reconstructed.append(child); - } - } - // Use stored item body - else - reconstructed = model->body(index); - - // Check size of reconstructed region, it must be same - 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()), - 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()), - index); - return ERR_INVALID_PARAMETER; - } - - // Reconstruction successful - reconstructed = model->header(index).append(reconstructed); - return ERR_SUCCESS; - } - - // All other actions are not supported - return ERR_NOT_IMPLEMENTED; -} - -UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & reconstructed) -{ - if (!index.isValid()) - return ERR_SUCCESS; - - UINT8 result; - - // No action - if (model->action(index) == Actions::NoAction) { - reconstructed = model->header(index).append(model->body(index)); - return ERR_SUCCESS; - } - else if (model->action(index) == Actions::Remove) { - reconstructed.clear(); - return ERR_SUCCESS; - } - 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(); - - // 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 - if (model->rowCount(index)) { - reconstructed.clear(); - UINT8 polarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE; - char empty = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; - - // Calculate volume base for volume - UINT32 volumeBase; - QByteArray file; - bool baseFound = false; - - // Search for VTF - for (int i = 0; i < model->rowCount(index); i++) { - file = model->header(index.child(i, 0)); - // VTF found - if (file.left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { - baseFound = true; - volumeBase = (UINT32)(0x100000000 - volumeSize); - break; - } - } - - // Determine if volume is inside compressed item - if (!baseFound) { - // Iterate up to the root, checking for compression type to be other then none - for (QModelIndex parentIndex = index.parent(); model->type(parentIndex) != Types::Root; parentIndex = parentIndex.parent()) - if (model->compression(parentIndex) != COMPRESSION_ALGORITHM_NONE) { - // No rebase needed for compressed PEI files - baseFound = true; - volumeBase = 0; - break; - } - } - - // Find volume base address using first PEI file in it - if (!baseFound) { - // Search for first PEI-file and use it as base source - UINT32 fileOffset = header.size(); - for (int i = 0; i < model->rowCount(index); i++) { - if ((model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_PEI_CORE || - model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_PEIM || - model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)){ - QModelIndex peiFile = index.child(i, 0); - UINT32 sectionOffset = sizeof(EFI_FFS_FILE_HEADER); - // Search for PE32 or TE section - for (int j = 0; j < model->rowCount(peiFile); j++) { - if (model->subtype(peiFile.child(j, 0)) == EFI_SECTION_PE32 || - model->subtype(peiFile.child(j, 0)) == EFI_SECTION_TE) { - QModelIndex image = peiFile.child(j, 0); - // Check for correct action - if (model->action(image) == Actions::Remove || model->action(image) == Actions::Insert) - continue; - // 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); - if (!result) { - // Calculate volume base - volumeBase = imagebase - relbase; - baseFound = true; - goto out; - } - } - sectionOffset += model->header(peiFile.child(j, 0)).size() + model->body(peiFile.child(j, 0)).size(); - sectionOffset = ALIGN4(sectionOffset); - } - } - fileOffset += model->header(index.child(i, 0)).size() + model->body(index.child(i, 0)).size(); - fileOffset = ALIGN8(fileOffset); - } - } - out: - // Do not set volume base - if (!baseFound) - volumeBase = 0; - - // Reconstruct files in volume - UINT32 offset = 0; - QByteArray padFileGuid = EFI_FFS_PAD_FILE_GUID; - QByteArray vtf; - QModelIndex vtfIndex; - UINT32 nonUefiDataOffset = 0; - QByteArray nonUefiData; - for (int i = 0; i < model->rowCount(index); i++) { - // Inside a volume can be files, free space or padding with non-UEFI data - if (model->type(index.child(i, 0)) == Types::File) { // Next item is a file - - // Align to 8 byte boundary - UINT32 alignment = offset % 8; - if (alignment) { - alignment = 8 - alignment; - offset += alignment; - reconstructed.append(QByteArray(alignment, empty)); - } - - // Calculate file base - UINT32 fileBase = volumeBase ? volumeBase + header.size() + offset : 0; - - // Reconstruct file - result = reconstructFile(index.child(i, 0), volumeHeader->Revision, polarity, fileBase, file); - if (result) - return result; - - // Empty file - if (file.isEmpty()) - continue; - - EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)file.data(); - - // 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 - continue; - } - // Skip empty pad-file - else - continue; - } - - // Volume Top File - if (file.left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { - vtf = file; - vtfIndex = index.child(i, 0); - continue; - } - - // Normal file - // Ensure correct alignment - UINT8 alignmentPower; - UINT32 alignmentBase; - alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; - alignment = (UINT32)pow(2.0, alignmentPower); - alignmentBase = header.size() + offset + sizeof(EFI_FFS_FILE_HEADER); - if (alignmentBase % alignment) { - // File will be unaligned if added as is, so we must add pad file before it - // Determine pad file size - UINT32 size = alignment - (alignmentBase % alignment); - // Required padding is smaller then minimal pad file size - while (size < sizeof(EFI_FFS_FILE_HEADER)) { - size += alignment; - } - // Construct pad file - QByteArray pad; - result = constructPadFile(padFileGuid, size, volumeHeader->Revision, polarity, pad); - if (result) - return result; - // Append constructed pad file to volume body - reconstructed.append(pad); - offset += size; - } - - // Append current file to new volume body - reconstructed.append(file); - - // Change current file offset - offset += file.size(); - } - else if (model->type(index.child(i, 0)) == Types::FreeSpace) { //Next item is a free space - // Some data are located beyond free space - if (offset + (UINT32)model->body(index.child(i, 0)).size() < (UINT32)model->body(index).size()) { - // Get non-UEFI data and it's offset - nonUefiData = model->body(index.child(i + 1, 0)); - nonUefiDataOffset = body.size() - nonUefiData.size(); - break; - } - } - } - - // Check volume sanity - if (!vtf.isEmpty() && !nonUefiData.isEmpty()) { - msg(tr("reconstructVolume: both VTF and non-UEFI data found in the volume, reconstruction is not possible"), index); - return ERR_INVALID_VOLUME; - } - - // Insert VTF or non-UEFI data to it's correct place - if (!vtf.isEmpty()) { // VTF found - // Determine correct VTF offset - UINT32 vtfOffset = model->body(index).size() - vtf.size(); - - if (vtfOffset % 8) { - msg(tr("reconstructVolume: wrong size of the Volume Top File"), index); - return ERR_INVALID_FILE; - } - // Insert pad file to fill the gap - if (vtfOffset > offset) { - // Determine pad file size - UINT32 size = vtfOffset - offset; - // Construct pad file - QByteArray pad; - result = constructPadFile(padFileGuid, size, volumeHeader->Revision, polarity, pad); - if (result) - return result; - // Append constructed pad file to volume body - reconstructed.append(pad); - } - // No more space left in volume - else if (offset > vtfOffset) { - msg(tr("reconstructVolume: no space left to insert VTF, need %1h (%2) byte(s) more") - .hexarg(offset - vtfOffset).arg(offset - vtfOffset), index); - return ERR_INVALID_VOLUME; - } - - // Calculate VTF base - UINT32 vtfBase = volumeBase ? volumeBase + vtfOffset : 0; - - // Reconstruct VTF again - result = reconstructFile(vtfIndex, volumeHeader->Revision, polarity, vtfBase, vtf); - if (result) - return result; - - // Patch PEI core entry point in VTF - result = patchVtf(vtf); - if (result) - return result; - - // Append VTF - reconstructed.append(vtf); - } - else if (!nonUefiData.isEmpty()) { //Non-UEFI data found - // No space left - if (offset > nonUefiDataOffset) { - msg(tr("reconstructVolume: no space left to insert non-UEFI data, need %1h (%2) byte(s) more") - .hexarg(offset - nonUefiDataOffset).arg(offset - nonUefiDataOffset), index); - return ERR_INVALID_VOLUME; - } - // Append additional free space - else if (nonUefiDataOffset > offset) { - reconstructed.append(QByteArray(nonUefiDataOffset - offset, empty)); - } - - // Append VTF - reconstructed.append(nonUefiData); - } - else { - // Fill the rest of volume space with empty char - if (body.size() > reconstructed.size()) { - // Fill volume end with empty char - reconstructed.append(QByteArray(body.size() - reconstructed.size(), empty)); - } - else if (body.size() < reconstructed.size()) { - // Check if volume can be grown - // Root volume can't be grown - UINT8 parentType = model->type(index.parent()); - if (parentType != Types::File && parentType != Types::Section) { - msg(tr("reconstructVolume: root volume can't be grown"), index); - return ERR_INVALID_VOLUME; - } - - // Grow volume to fit new body - UINT32 newSize = header.size() + reconstructed.size(); - result = growVolume(header, volumeSize, newSize); - if (result) - return result; - - // Fill volume end with empty char - reconstructed.append(QByteArray(newSize - header.size() - reconstructed.size(), empty)); - volumeSize = newSize; - } - } - - // Check new volume size - if ((UINT32)(header.size() + reconstructed.size()) > volumeSize) - { - msg(tr("reconstructVolume: volume grow failed"), index); - return ERR_INVALID_VOLUME; - } - } - // Use current volume body - else - reconstructed = model->body(index); - - // Reconstruction successful - 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) { - // Get current CRC32 value from volume header - const UINT32 current = *(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) { - *(UINT32*)(reconstructed.data() + 8) = crc; - - // Recalculate header checksum again - volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)reconstructed.data(); - volumeHeader->Checksum = 0; - volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); - } - } - return ERR_SUCCESS; - } - - // All other actions are not supported - return ERR_NOT_IMPLEMENTED; -} - -UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, const UINT8 erasePolarity, const UINT32 base, QByteArray& reconstructed) -{ - if (!index.isValid()) - return ERR_SUCCESS; - - UINT8 result; - - // No action - if (model->action(index) == Actions::NoAction) { - reconstructed = model->header(index).append(model->body(index)); - const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)model->header(index).constData(); - // Append tail, if needed - if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { - UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; - UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; - reconstructed.append(ht).append(ft); - } - return ERR_SUCCESS; - } - else if (model->action(index) == Actions::Remove) { - reconstructed.clear(); - return ERR_SUCCESS; - } - else if (model->action(index) == Actions::Insert || - model->action(index) == Actions::Replace || - model->action(index) == Actions::Rebuild) { - QByteArray header = model->header(index); - EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)header.data(); - - // Check erase polarity - if (erasePolarity == ERASE_POLARITY_UNKNOWN) { - msg(tr("reconstructFile: unknown erase polarity"), index); - return ERR_INVALID_PARAMETER; - } - - // Check file state - // Invert it first if erase polarity is true - UINT8 state = fileHeader->State; - if (erasePolarity == ERASE_POLARITY_TRUE) - state = ~state; - - // Order of this checks must be preserved - // Check file to have valid state, or delete it otherwise - if (state & EFI_FILE_HEADER_INVALID) { - // File marked to have invalid header and must be deleted - // Do not add anything to queue - msg(tr("reconstructFile: file is HEADER_INVALID state, and will be removed from reconstructed image"), index); - return ERR_SUCCESS; - } - else if (state & EFI_FILE_DELETED) { - // File marked to have been deleted form and must be deleted - // Do not add anything to queue - msg(tr("reconstructFile: file is in DELETED state, and will be removed from reconstructed image"), index); - return ERR_SUCCESS; - } - else if (state & EFI_FILE_MARKED_FOR_UPDATE) { - // File is marked for update, the mark must be removed - msg(tr("reconstructFile: file's MARKED_FOR_UPDATE state cleared"), index); - } - else if (state & EFI_FILE_DATA_VALID) { - // File is in good condition, reconstruct it - } - else if (state & EFI_FILE_HEADER_VALID) { - // Header is valid, but data is not, so file must be deleted - msg(tr("reconstructFile: file is in HEADER_VALID (but not in DATA_VALID) state, and will be removed from reconstructed image"), index); - return ERR_SUCCESS; - } - else if (state & EFI_FILE_HEADER_CONSTRUCTION) { - // Header construction not finished, so file must be deleted - msg(tr("reconstructFile: file is in HEADER_CONSTRUCTION (but not in DATA_VALID) state, and will be removed from reconstructed image"), index); - return ERR_SUCCESS; - } - - // Reconstruct file body - if (model->rowCount(index)) { - reconstructed.clear(); - // Construct new file body - // File contains raw data, must be parsed as region - if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) { - result = reconstructRegion(index, reconstructed); - if (result) - return result; - } - // File contains sections - else { - UINT32 offset = 0; - - for (int i = 0; i < model->rowCount(index); i++) { - // Align to 4 byte boundary - UINT8 alignment = offset % 4; - if (alignment) { - alignment = 4 - alignment; - offset += alignment; - reconstructed.append(QByteArray(alignment, '\x00')); - } - - // Calculate section base - UINT32 sectionBase = base ? base + sizeof(EFI_FFS_FILE_HEADER) + offset : 0; - - // Reconstruct section - QByteArray section; - result = reconstructSection(index.child(i, 0), sectionBase, section); - if (result) - return result; - - // Check for empty section - if (section.isEmpty()) - continue; - - // Append current section to new file body - reconstructed.append(section); - - // Change current file offset - offset += section.size(); - } - } - - // Correct file size - UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0; - - uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize, fileHeader->Size); - - // Recalculate header checksum - fileHeader->IntegrityCheck.Checksum.Header = 0; - fileHeader->IntegrityCheck.Checksum.File = 0; - fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)fileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1); - } - // Use current file body - else - reconstructed = model->body(index); - - // Recalculate data checksum, if needed - if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { - fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((const UINT8*)reconstructed.constData(), reconstructed.size()); - } - else if (revision == 1) - fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; - else - fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; - - // Append tail, if needed - if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { - UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; - UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; - reconstructed.append(ht).append(ft); - } - // Set file state - state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION; - if (erasePolarity == ERASE_POLARITY_TRUE) - state = ~state; - fileHeader->State = state; - - // Reconstruction successful - reconstructed = header.append(reconstructed); - return ERR_SUCCESS; - } - - // All other actions are not supported - return ERR_NOT_IMPLEMENTED; -} - -UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, QByteArray& reconstructed) -{ - if (!index.isValid()) - return ERR_SUCCESS; - - UINT8 result; - - // No action - if (model->action(index) == Actions::NoAction) { - reconstructed = model->header(index).append(model->body(index)); - return ERR_SUCCESS; - } - else if (model->action(index) == Actions::Remove) { - reconstructed.clear(); - return ERR_SUCCESS; - } - else if (model->action(index) == Actions::Insert || - model->action(index) == Actions::Replace || - model->action(index) == Actions::Rebuild || - model->action(index) == Actions::Rebase) { - QByteArray header = model->header(index); - EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)header.data(); - - // Reconstruct section with children - if (model->rowCount(index)) { - reconstructed.clear(); - // Construct new section body - UINT32 offset = 0; - - // Reconstruct section body - for (int i = 0; i < model->rowCount(index); i++) { - // Align to 4 byte boundary - UINT8 alignment = offset % 4; - if (alignment) { - alignment = 4 - alignment; - offset += alignment; - reconstructed.append(QByteArray(alignment, '\x00')); - } - - // Reconstruct subsections - QByteArray section; - result = reconstruct(index.child(i, 0), section); - if (result) - return result; - - // Check for empty queue - if (section.isEmpty()) - continue; - - // Append current subsection to new section body - reconstructed.append(section); - - // Change current file offset - offset += section.size(); - } - - // Only this 2 sections can have compressed body - if (model->subtype(index) == EFI_SECTION_COMPRESSION) { - EFI_COMPRESSION_SECTION* compessionHeader = (EFI_COMPRESSION_SECTION*)header.data(); - // Set new uncompressed size - compessionHeader->UncompressedLength = reconstructed.size(); - // Compress new section body - QByteArray compressed; - result = compress(reconstructed, model->compression(index), compressed); - if (result) - return result; - // Correct compression type - if (model->compression(index) == COMPRESSION_ALGORITHM_NONE) - compessionHeader->CompressionType = EFI_NOT_COMPRESSED; - else if (model->compression(index) == COMPRESSION_ALGORITHM_LZMA || model->compression(index) == COMPRESSION_ALGORITHM_IMLZMA) - compessionHeader->CompressionType = EFI_CUSTOMIZED_COMPRESSION; - else if (model->compression(index) == COMPRESSION_ALGORITHM_EFI11 || model->compression(index) == COMPRESSION_ALGORITHM_TIANO) - compessionHeader->CompressionType = EFI_STANDARD_COMPRESSION; - else - return ERR_UNKNOWN_COMPRESSION_ALGORITHM; - - // Replace new section body - reconstructed = compressed; - } - else if (model->subtype(index) == EFI_SECTION_GUID_DEFINED) { - EFI_GUID_DEFINED_SECTION* guidDefinedHeader = (EFI_GUID_DEFINED_SECTION*)header.data(); - // Compress new section body - QByteArray compressed; - result = compress(reconstructed, model->compression(index), compressed); - if (result) - return result; - // Check for authentication status valid attribute - if (guidDefinedHeader->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) { - // CRC32 section - if (QByteArray((const char*)&guidDefinedHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_CRC32) { - // Calculate CRC32 of section data - UINT32 crc = crc32(0, (const UINT8*)compressed.constData(), compressed.size()); - // Store new CRC32 - *(UINT32*)(header.data() + sizeof(EFI_GUID_DEFINED_SECTION)) = crc; - } - else { - msg(tr("reconstructSection: GUID defined section authentication info can become invalid") - .arg(guidToQString(guidDefinedHeader->SectionDefinitionGuid)), index); - } - } - // Check for Intel signed section - if (guidDefinedHeader->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED - && QByteArray((const char*)&guidDefinedHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { - msg(tr("reconstructSection: GUID defined section signature can become invalid") - .arg(guidToQString(guidDefinedHeader->SectionDefinitionGuid)), index); - } - // Replace new section body - reconstructed = compressed; - } - else if (model->compression(index) != COMPRESSION_ALGORITHM_NONE) { - msg(tr("reconstructSection: incorrectly required compression for section of type %1") - .arg(model->subtype(index)), index); - return ERR_INVALID_SECTION; - } - - // Correct section size - uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size); - } - // Leaf section - else - reconstructed = model->body(index); - - // Rebase PE32 or TE image, if needed - if ((model->subtype(index) == EFI_SECTION_PE32 || model->subtype(index) == EFI_SECTION_TE) && - (model->subtype(index.parent()) == EFI_FV_FILETYPE_PEI_CORE || - model->subtype(index.parent()) == EFI_FV_FILETYPE_PEIM || - model->subtype(index.parent()) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)) { - UINT16 teFixup = 0; - //TODO: add proper handling - /*if (model->subtype(index) == EFI_SECTION_TE) { - 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) { - msg(tr("reconstructSection: executable section rebase failed"), index); - return result; - } - - // Special case of PEI Core rebase - if (model->subtype(index.parent()) == EFI_FV_FILETYPE_PEI_CORE) { - result = getEntryPoint(reconstructed, newPeiCoreEntryPoint); - if (result) - msg(tr("reconstructSection: can't get entry point of PEI core"), index); - } - } - } - - // Reconstruction successful - reconstructed = header.append(reconstructed); - - return ERR_SUCCESS; - } - - // All other actions are not supported - return ERR_NOT_IMPLEMENTED; -} - -UINT8 FfsEngine::reconstruct(const QModelIndex &index, QByteArray& reconstructed) -{ - if (!index.isValid()) - return ERR_SUCCESS; - - UINT8 result; - - switch (model->type(index)) { - case Types::Image: - if (model->subtype(index) == Subtypes::IntelImage) { - result = reconstructIntelImage(index, reconstructed); - if (result) - return result; - } - else { - //Other images types can be reconstructed like regions - result = reconstructRegion(index, reconstructed); - if (result) - return result; - } - break; - - case Types::Capsule: - // Capsules can be reconstructed like regions - result = reconstructRegion(index, reconstructed); - if (result) - return result; - break; - - case Types::Region: - result = reconstructRegion(index, reconstructed); - if (result) - return result; - break; - - case Types::Padding: - // No reconstruction needed - reconstructed = model->header(index).append(model->body(index)); - return ERR_SUCCESS; - break; - - case Types::Volume: - result = reconstructVolume(index, reconstructed); - if (result) - return result; - break; - - case Types::File: //Must not be called that way - msg(tr("reconstruct: call of generic function is not supported for files").arg(model->type(index)), index); - return ERR_GENERIC_CALL_NOT_SUPPORTED; - break; - - case Types::Section: - result = reconstructSection(index, 0, reconstructed); - if (result) - return result; - break; - default: - msg(tr("reconstruct: unknown item type %1").arg(model->type(index)), index); - return ERR_UNKNOWN_ITEM_TYPE; - } - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::growVolume(QByteArray & header, const UINT32 size, UINT32 & newSize) -{ - // 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)); - - // Get block map size - UINT32 blockMapSize = volumeHeader->HeaderLength - sizeof(EFI_FIRMWARE_VOLUME_HEADER); - if (blockMapSize % sizeof(EFI_FV_BLOCK_MAP_ENTRY)) - return ERR_INVALID_VOLUME; - UINT32 blockMapCount = blockMapSize / sizeof(EFI_FV_BLOCK_MAP_ENTRY); - - // Check blockMap validity - if (blockMap[blockMapCount - 1].NumBlocks != 0 || blockMap[blockMapCount - 1].Length != 0) - return ERR_INVALID_VOLUME; - - // Case of complex blockMap - if (blockMapCount > 2) - return ERR_COMPLEX_BLOCK_MAP; - - // Calculate new size - if (newSize <= size) - return ERR_INVALID_PARAMETER; - - newSize += blockMap[0].Length - newSize % blockMap[0].Length; - - // Recalculate number of blocks - blockMap[0].NumBlocks = newSize / blockMap[0].Length; - - // Set new volume size - volumeHeader->FvLength = 0; - for (UINT8 i = 0; i < blockMapCount; i++) { - volumeHeader->FvLength += blockMap[i].NumBlocks * blockMap[i].Length; - } - - // Recalculate volume header checksum - volumeHeader->Checksum = 0; - volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::reconstructImageFile(QByteArray & reconstructed) -{ - return reconstruct(model->index(0, 0), reconstructed); -} - -// Search routines -UINT8 FfsEngine::findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode) -{ - if (!index.isValid()) - return ERR_SUCCESS; - - if (hexPattern.isEmpty()) - return ERR_INVALID_PARAMETER; - - // Check for "all substrings" pattern - if (hexPattern.count('.') == hexPattern.length()) - return ERR_SUCCESS; - - bool hasChildren = (model->rowCount(index) > 0); - for (int i = 0; i < model->rowCount(index); i++) { - findHexPattern(index.child(i, index.column()), hexPattern, mode); - } - - QByteArray data; - if (hasChildren) { - if (mode != SEARCH_MODE_BODY) - data = model->header(index); - } - else { - if (mode == SEARCH_MODE_HEADER) - data.append(model->header(index)); - else if (mode == SEARCH_MODE_BODY) - data.append(model->body(index)); - else - data.append(model->header(index)).append(model->body(index)); - } - - QString hexBody = QString(data.toHex()); - QRegExp regexp = QRegExp(QString(hexPattern), Qt::CaseInsensitive); - INT32 offset = regexp.indexIn(hexBody); - while (offset >= 0) { - if (offset % 2 == 0) { - msg(tr("Hex pattern \"%1\" found as \"%2\" in %3 at %4-offset %5h") - .arg(QString(hexPattern)) - .arg(hexBody.mid(offset, hexPattern.length()).toUpper()) - .arg(model->name(index)) - .arg(mode == SEARCH_MODE_BODY ? tr("body") : tr("header")) - .hexarg(offset / 2), - index); - } - offset = regexp.indexIn(hexBody, offset + 1); - } - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode) -{ - if (guidPattern.isEmpty()) - return ERR_INVALID_PARAMETER; - - if (!index.isValid()) - return ERR_SUCCESS; - - bool hasChildren = (model->rowCount(index) > 0); - for (int i = 0; i < model->rowCount(index); i++) { - findGuidPattern(index.child(i, index.column()), guidPattern, mode); - } - - QByteArray data; - if (hasChildren) { - if (mode != SEARCH_MODE_BODY) - data = model->header(index); - } - else { - if (mode == SEARCH_MODE_HEADER) - data.append(model->header(index)); - else if (mode == SEARCH_MODE_BODY) - data.append(model->body(index)); - else - data.append(model->header(index)).append(model->body(index)); - } - - QString hexBody = QString(data.toHex()); - QList list = guidPattern.split('-'); - if (list.count() != 5) - return ERR_INVALID_PARAMETER; - - QByteArray hexPattern; - // Reverse first GUID block - hexPattern.append(list.at(0).mid(6, 2)); - hexPattern.append(list.at(0).mid(4, 2)); - hexPattern.append(list.at(0).mid(2, 2)); - hexPattern.append(list.at(0).mid(0, 2)); - // Reverse second GUID block - hexPattern.append(list.at(1).mid(2, 2)); - hexPattern.append(list.at(1).mid(0, 2)); - // Reverse third GUID block - hexPattern.append(list.at(2).mid(2, 2)); - hexPattern.append(list.at(2).mid(0, 2)); - // Append fourth and fifth GUID blocks as is - hexPattern.append(list.at(3)).append(list.at(4)); - - // Check for "all substrings" pattern - if (hexPattern.count('.') == hexPattern.length()) - return ERR_SUCCESS; - - QRegExp regexp = QRegExp(QString(hexPattern), Qt::CaseInsensitive); - INT32 offset = regexp.indexIn(hexBody); - while (offset >= 0) { - if (offset % 2 == 0) { - msg(tr("GUID pattern \"%1\" found as \"%2\" in %3 at %4-offset %5h") - .arg(QString(guidPattern)) - .arg(hexBody.mid(offset, hexPattern.length()).toUpper()) - .arg(model->name(index)) - .arg(mode == SEARCH_MODE_BODY ? tr("body") : tr("header")) - .hexarg(offset / 2), - index); - } - offset = regexp.indexIn(hexBody, offset + 1); - } - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive) -{ - if (pattern.isEmpty()) - return ERR_INVALID_PARAMETER; - - if (!index.isValid()) - return ERR_SUCCESS; - - bool hasChildren = (model->rowCount(index) > 0); - for (int i = 0; i < model->rowCount(index); i++) { - findTextPattern(index.child(i, index.column()), pattern, unicode, caseSensitive); - } - - if (hasChildren) - return ERR_SUCCESS; - - QString data; - if (unicode) - data = QString::fromUtf16((const ushort*)model->body(index).data(), model->body(index).length() / 2); - else - data = QString::fromLatin1((const char*)model->body(index).data(), model->body(index).length()); - - int offset = -1; - while ((offset = data.indexOf(pattern, offset + 1, caseSensitive)) >= 0) { - msg(tr("%1 text \"%2\" found in %3 at offset %4h") - .arg(unicode ? "Unicode" : "ASCII") - .arg(pattern) - .arg(model->name(index)) - .hexarg(unicode ? offset * 2 : offset), - index); - } - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) -{ - UINT32 delta; // Difference between old and new base addresses - UINT32 relocOffset; // Offset of relocation region - UINT32 relocSize; // Size of relocation region - UINT32 teFixup = 0; // Bytes removed form PE header for TE images - - // Copy input data to local storage - QByteArray file = executable; - - // Populate DOS header - 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; - EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(file.data() + offset); - if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) - return ERR_UNKNOWN_IMAGE_TYPE; - offset += sizeof(EFI_IMAGE_PE_HEADER); - // Skip file header - offset += sizeof(EFI_IMAGE_FILE_HEADER); - // Check optional header magic - UINT16 magic = *(UINT16*)(file.data() + offset); - if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { - EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (EFI_IMAGE_OPTIONAL_HEADER32*)(file.data() + offset); - delta = base - optHeader->ImageBase; - if (!delta) - // No need to rebase - return ERR_SUCCESS; - relocOffset = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; - relocSize = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; - // Set new base - optHeader->ImageBase = base; - } - else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { - EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (EFI_IMAGE_OPTIONAL_HEADER64*)(file.data() + offset); - delta = base - optHeader->ImageBase; - if (!delta) - // No need to rebase - return ERR_SUCCESS; - relocOffset = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; - relocSize = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; - // Set new base - optHeader->ImageBase = base; - } - else - return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; - } - else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ - // Populate TE header - EFI_IMAGE_TE_HEADER* teHeader = (EFI_IMAGE_TE_HEADER*)file.data(); - delta = base - teHeader->ImageBase; - if (!delta) - // No need to rebase - return ERR_SUCCESS; - relocOffset = teHeader->DataDirectory[EFI_IMAGE_TE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; - teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); - relocSize = teHeader->DataDirectory[EFI_IMAGE_TE_DIRECTORY_ENTRY_BASERELOC].Size; - // Set new base - teHeader->ImageBase = base; - } - else - return ERR_UNKNOWN_IMAGE_TYPE; - - // No relocations - if (relocOffset == 0) { - // No need to fix relocations - executable = file; - return ERR_SUCCESS; - } - - EFI_IMAGE_BASE_RELOCATION *RelocBase; - EFI_IMAGE_BASE_RELOCATION *RelocBaseEnd; - UINT16 *Reloc; - UINT16 *RelocEnd; - UINT16 *F16; - UINT32 *F32; - UINT64 *F64; - - // Run the whole relocation block - RelocBase = (EFI_IMAGE_BASE_RELOCATION*)(file.data() + relocOffset - teFixup); - RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION*)(file.data() + relocOffset - teFixup + relocSize); - - while (RelocBase < RelocBaseEnd) { - Reloc = (UINT16*)((UINT8*)RelocBase + sizeof(EFI_IMAGE_BASE_RELOCATION)); - RelocEnd = (UINT16*)((UINT8*)RelocBase + RelocBase->SizeOfBlock); - - // Run this relocation record - while (Reloc < RelocEnd) { - UINT8* data = (UINT8*)(file.data() + RelocBase->VirtualAddress - teFixup + (*Reloc & 0x0FFF)); - switch ((*Reloc) >> 12) { - case EFI_IMAGE_REL_BASED_ABSOLUTE: - // Do nothing - break; - - case EFI_IMAGE_REL_BASED_HIGH: - // Add second 16 bits of delta - F16 = (UINT16*)data; - *F16 = (UINT16)(*F16 + (UINT16)(((UINT32)delta) >> 16)); - break; - - case EFI_IMAGE_REL_BASED_LOW: - // Add first 16 bits of delta - F16 = (UINT16*)data; - *F16 = (UINT16)(*F16 + (UINT16)delta); - break; - - case EFI_IMAGE_REL_BASED_HIGHLOW: - // Add first 32 bits of delta - F32 = (UINT32*)data; - *F32 = *F32 + (UINT32)delta; - break; - - case EFI_IMAGE_REL_BASED_DIR64: - // Add all 64 bits of delta - F64 = (UINT64*)data; - *F64 = *F64 + (UINT64)delta; - break; - - default: - return ERR_UNKNOWN_RELOCATION_TYPE; - } - - // Next relocation record - Reloc += 1; - } - - // Next relocation block - RelocBase = (EFI_IMAGE_BASE_RELOCATION*)RelocEnd; - } - - executable = file; - return ERR_SUCCESS; -} - -UINT8 FfsEngine::patchVtf(QByteArray &vtf) -{ - if (!oldPeiCoreEntryPoint) { - msg(tr("patchVtf: PEI Core entry point can't be determined. VTF can't be patched.")); - return ERR_PEI_CORE_ENTRY_POINT_NOT_FOUND; - } - - if (!newPeiCoreEntryPoint || oldPeiCoreEntryPoint == newPeiCoreEntryPoint) - // No need to patch anything - return ERR_SUCCESS; - - // Replace last occurrence of oldPeiCoreEntryPoint with newPeiCoreEntryPoint - QByteArray old((char*)&oldPeiCoreEntryPoint, sizeof(oldPeiCoreEntryPoint)); - int i = vtf.lastIndexOf(old); - if (i == -1) { - msg(tr("patchVtf: PEI Core entry point can't be found in VTF. VTF not patched.")); - return ERR_SUCCESS; - } - UINT32* data = (UINT32*)(vtf.data() + i); - *data = newPeiCoreEntryPoint; - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::getEntryPoint(const QByteArray &file, UINT32& entryPoint) -{ - if (file.isEmpty()) - return ERR_INVALID_FILE; - - // Populate DOS header - 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; - 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; - offset += sizeof(EFI_IMAGE_PE_HEADER); - - // Skip file header - offset += sizeof(EFI_IMAGE_FILE_HEADER); - - // Check optional header magic - const UINT16 magic = *(const UINT16*)(file.constData() + offset); - if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { - 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) { - const EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER64*)(file.constData() + offset); - entryPoint = optHeader->ImageBase + optHeader->AddressOfEntryPoint; - } - else - return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; - } - else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ - // Populate TE header - 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; - } - return ERR_SUCCESS; -} - -UINT8 FfsEngine::getBase(const QByteArray& file, UINT32& base) -{ - if (file.isEmpty()) - return ERR_INVALID_FILE; - - // Populate DOS header - 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; - 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; - offset += sizeof(EFI_IMAGE_PE_HEADER); - - // Skip file header - offset += sizeof(EFI_IMAGE_FILE_HEADER); - - // Check optional header magic - const UINT16 magic = *(const UINT16*)(file.constData() + offset); - if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { - 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) { - const EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER64*)(file.constData() + offset); - base = optHeader->ImageBase; - } - else - return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; - } - else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ - // Populate TE header - const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)file.constData(); - //!TODO: add handling - base = teHeader->ImageBase; - } - - return ERR_SUCCESS; -} - -UINT32 FfsEngine::crc32(UINT32 initial, const UINT8* buffer, UINT32 length) -{ - static const UINT32 crcTable[256] = { - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, - 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, - 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, - 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, - 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, - 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, - 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, - 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, - 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, - 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, - 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, - 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, - 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, - 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, - 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, - 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, - 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, - 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, - 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, - 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, - 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, - 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, - 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, - 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, - 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, - 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, - 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, - 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, - 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, - 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, - 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, - 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, - 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; - UINT32 crc32; - UINT32 i; - - // Accumulate crc32 for buffer - crc32 = initial ^ 0xFFFFFFFF; - for (i = 0; i < length; i++) { - crc32 = (crc32 >> 8) ^ crcTable[(crc32 ^ buffer[i]) & 0xFF]; - } - - return(crc32 ^ 0xFFFFFFFF); -} - -UINT8 FfsEngine::dump(const QModelIndex & index, const QString & path, const QString & guid) -{ - dumped = false; - UINT8 result = recursiveDump(index, path, guid); - if (result) - return result; - else if (!dumped) - return ERR_ITEM_NOT_FOUND; - return ERR_SUCCESS; -} - -UINT8 FfsEngine::recursiveDump(const QModelIndex & index, const QString & path, const QString & guid) -{ - if (!index.isValid()) - return ERR_INVALID_PARAMETER; - - QDir dir; - 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; - - if (!dir.mkpath(path)) - return ERR_DIR_CREATE; - - QFile file; - if (!model->header(index).isEmpty()) { - file.setFileName(tr("%1/header.bin").arg(path)); - if (!file.open(QFile::WriteOnly)) - return ERR_FILE_OPEN; - file.write(model->header(index)); - file.close(); - } - - if (!model->body(index).isEmpty()) { - file.setFileName(tr("%1/body.bin").arg(path)); - if (!file.open(QFile::WriteOnly)) - return ERR_FILE_OPEN; - file.write(model->body(index)); - file.close(); - } - - 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->info(index)); - file.setFileName(tr("%1/info.txt").arg(path)); - if (!file.open(QFile::Text | QFile::WriteOnly)) - return ERR_FILE_OPEN; - file.write(info.toLatin1()); - file.close(); - dumped = true; - } - - UINT8 result; - for (int i = 0; i < model->rowCount(index); i++) { - QModelIndex childIndex = index.child(i, 0); - QString childPath = QString("%1/%2 %3").arg(path).arg(i).arg(model->text(childIndex).isEmpty() ? model->name(childIndex) : model->text(childIndex)); - result = recursiveDump(childIndex, childPath, guid); - if (result) - return result; - } - - return ERR_SUCCESS; -} - -UINT8 FfsEngine::patch(const QModelIndex & index, const QVector & patches) -{ - if (!index.isValid() || patches.isEmpty() || model->rowCount(index)) - return ERR_INVALID_PARAMETER; - - // Skip removed items - if (model->action(index) == Actions::Remove) - return ERR_NOTHING_TO_PATCH; - - UINT8 result; - - // Apply patches to item's body - QByteArray body = model->body(index); - PatchData current; - Q_FOREACH(current, patches) - { - if (current.type == PATCH_TYPE_OFFSET) { - result = patchViaOffset(body, current.offset, current.hexReplacePattern); - if (result) - return result; - } - else if (current.type == PATCH_TYPE_PATTERN) { - result = patchViaPattern(body, current.hexFindPattern, current.hexReplacePattern); - if (result) - return result; - } - else - return ERR_UNKNOWN_PATCH_TYPE; - } - - if (body != model->body(index)) { - QByteArray patched = model->header(index); - patched.append(body); - return replace(index, patched, REPLACE_MODE_AS_IS); - } - - return ERR_NOTHING_TO_PATCH; -} - -UINT8 FfsEngine::patchViaOffset(QByteArray & data, const UINT32 offset, const QByteArray & hexReplacePattern) -{ - QByteArray body = data; - - // Skip patterns with odd length - if (hexReplacePattern.length() % 2 > 0) - return ERR_INVALID_PARAMETER; - - // Check offset bounds - if (offset > (UINT32)(body.length() - hexReplacePattern.length() / 2)) - return ERR_PATCH_OFFSET_OUT_OF_BOUNDS; - - // Parse replace pattern - QByteArray replacePattern; - bool converted; - for (int i = 0; i < hexReplacePattern.length() / 2; i++) { - QByteArray hex = hexReplacePattern.mid(2 * i, 2); - UINT8 value = 0; - - if (!hex.contains('.')) { // Normal byte pattern - value = (UINT8)hex.toUShort(&converted, 16); - if (!converted) - return ERR_INVALID_SYMBOL; - } - else { // Placeholder byte pattern - if (hex[0] == '.' && hex[1] == '.') { // Full byte placeholder - value = body.at(offset + i); - } - else if (hex[0] == '.') {// Upper byte part placeholder - hex[0] = '0'; - value = (UINT8)(body.at(offset + i) & 0xF0); - value += (UINT8)hex.toUShort(&converted, 16); - if (!converted) - return ERR_INVALID_SYMBOL; - } - else if (hex[1] == '.') { // Lower byte part placeholder - hex[1] = '0'; - value = (UINT8)(body.at(offset + i) & 0x0F); - value += (UINT8)hex.toUShort(&converted, 16); - if (!converted) - return ERR_INVALID_SYMBOL; - } - else - return ERR_INVALID_SYMBOL; - } - - // Append calculated value to real pattern - replacePattern.append(value); - } - - body.replace(offset, replacePattern.length(), replacePattern); - msg(tr("patch: replaced %1 bytes at offset %2h %3 -> %4") - .arg(replacePattern.length()) - .hexarg(offset) - .arg(QString(data.mid(offset, replacePattern.length()).toHex()).toUpper()) - .arg(QString(replacePattern.toHex()).toUpper())); - data = body; - return ERR_SUCCESS; -} - -UINT8 FfsEngine::patchViaPattern(QByteArray & data, const QByteArray & hexFindPattern, const QByteArray & hexReplacePattern) -{ - QByteArray body = data; - - // Skip patterns with odd length - if (hexFindPattern.length() % 2 > 0 || hexReplacePattern.length() % 2 > 0) - return ERR_INVALID_PARAMETER; - - // Convert file body to hex; - QString hexBody = QString(body.toHex()); - QRegExp regexp = QRegExp(QString(hexFindPattern), Qt::CaseInsensitive); - INT32 offset = regexp.indexIn(hexBody); - while (offset >= 0) { - if (offset % 2 == 0) { - UINT8 result = patchViaOffset(body, offset / 2, hexReplacePattern); - if (result) - return result; - } - offset = regexp.indexIn(hexBody, offset + 1); - } - - data = body; - return ERR_SUCCESS; -} - diff --git a/ffsengine.h b/ffsengine.h deleted file mode 100644 index 41d9e0a..0000000 --- a/ffsengine.h +++ /dev/null @@ -1,153 +0,0 @@ -/* ffsengine.h - -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. -This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. -*/ - -#ifndef __FFSENGINE_H__ -#define __FFSENGINE_H__ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "basetypes.h" -#include "treemodel.h" -#include "peimage.h" - -#ifndef _CONSOLE -#include "messagelistitem.h" -#endif - -class TreeModel; - -QString errorMessage(UINT8 errorCode); - -struct PatchData { - UINT8 type; - UINT32 offset; - QByteArray hexFindPattern; - QByteArray hexReplacePattern; -}; - -class FfsEngine : public QObject -{ - Q_OBJECT - -public: - // Default constructor and destructor - FfsEngine(QObject *parent = 0); - ~FfsEngine(void); - - // Returns model for Qt view classes - TreeModel* treeModel() const; - -#ifndef _CONSOLE - // Returns message items queue - QQueue messages() const; - // Clears message items queue - void clearMessages(); -#endif - - // Firmware image parsing - UINT8 parseImageFile(const QByteArray & buffer); - UINT8 parseIntelImage(const QByteArray & intelImage, QModelIndex & index, const QModelIndex & parent = QModelIndex()); - UINT8 parseGbeRegion(const QByteArray & gbe, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); - 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 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); - UINT8 parseSections(const QByteArray & body, const QModelIndex & parent = QModelIndex()); - UINT8 parseSection(const QByteArray & section, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); - - // Compression routines - UINT8 decompress(const QByteArray & compressed, const UINT8 compressionType, QByteArray & decompressedData, UINT8 * algorithm = NULL); - UINT8 compress(const QByteArray & data, const UINT8 algorithm, QByteArray & compressedData); - - // Construction routines - 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 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); - UINT8 reconstructSection(const QModelIndex& index, const UINT32 base, QByteArray & reconstructed); - - // Operations on tree items - UINT8 extract(const QModelIndex & index, QByteArray & extracted, const UINT8 mode); - UINT8 create(const QModelIndex & index, const UINT8 type, const QByteArray & header, const QByteArray & body, const UINT8 mode, const UINT8 action, const UINT8 algorithm = COMPRESSION_ALGORITHM_NONE); - UINT8 insert(const QModelIndex & index, const QByteArray & object, const UINT8 mode); - UINT8 replace(const QModelIndex & index, const QByteArray & object, const UINT8 mode); - UINT8 remove(const QModelIndex & index); - UINT8 rebuild(const QModelIndex & index); - UINT8 dump(const QModelIndex & index, const QString & path, const QString & filter = QString()); - UINT8 patch(const QModelIndex & index, const QVector & patches); - - // Search routines - UINT8 findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode); - UINT8 findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode); - UINT8 findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive); - -private: - TreeModel *model; - - // PEI Core entry point - UINT32 oldPeiCoreEntryPoint; - UINT32 newPeiCoreEntryPoint; - - // Parsing helpers - UINT32 getPaddingType(const QByteArray & padding); - void parseAprioriRawSection(const QByteArray & body, QString & parsed); - UINT8 parseDepexSection(const QByteArray & body, QString & parsed); - UINT8 findNextVolume(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); - UINT8 getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize); - UINT8 getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize); - UINT8 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize); - - // Reconstruction helpers - UINT8 constructPadFile(const QByteArray &guid, const UINT32 size, const UINT8 revision, const UINT8 erasePolarity, QByteArray & pad); - UINT8 growVolume(QByteArray & header, const UINT32 size, UINT32 & newSize); - - // Rebase routines - UINT8 getBase(const QByteArray& file, UINT32& base); - UINT8 getEntryPoint(const QByteArray& file, UINT32 &entryPoint); - UINT8 rebase(QByteArray & executable, const UINT32 base); - void rebasePeiFiles(const QModelIndex & index); - - // Patch routines - UINT8 patchVtf(QByteArray &vtf); - - // Patch helpers - UINT8 patchViaOffset(QByteArray & data, const UINT32 offset, const QByteArray & hexReplacePattern); - UINT8 patchViaPattern(QByteArray & data, const QByteArray & hexFindPattern, const QByteArray & hexReplacePattern); - -#ifndef _CONSOLE - QQueue messageItems; -#endif - // Message helper - void msg(const QString & message, const QModelIndex &index = QModelIndex()); - - // Internal operations - bool hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2); - UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length); - - // Recursive dump - bool dumped; - UINT8 recursiveDump(const QModelIndex & index, const QString & path, const QString & filter); -}; - -#endif diff --git a/ffsparser.cpp b/ffsparser.cpp new file mode 100644 index 0000000..91e33bc --- /dev/null +++ b/ffsparser.cpp @@ -0,0 +1,2549 @@ +/* ffsparser.cpp + +Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +*/ + +#include + +#include "ffsparser.h" +#include "types.h" +#include "treemodel.h" +#include "descriptor.h" +#include "ffs.h" +#include "gbe.h" +#include "me.h" + + +#ifdef _CONSOLE +#include +#endif + +FfsParser::FfsParser(QObject *parent) + : QObject(parent) +{ + model = new TreeModel(); + //oldPeiCoreEntryPoint = 0; + //newPeiCoreEntryPoint = 0; + //dumped = false; +} + +FfsParser::~FfsParser(void) +{ + delete model; +} + +TreeModel* FfsParser::treeModel() const +{ + return model; +} + +void FfsParser::msg(const QString & message, const QModelIndex & index) +{ +#ifndef _DISABLE_ENGINE_MESSAGES +#ifndef _CONSOLE + messageItems.enqueue(MessageListItem(message, NULL, 0, index)); +#else + (void) index; + std::cout << message.toLatin1().constData() << std::endl; +#endif +#else + (void)message; + (void)index; +#endif +} + +#ifndef _CONSOLE +QQueue FfsParser::messages() const +{ + return messageItems; +} + +void FfsParser::clearMessages() +{ + messageItems.clear(); +} +#endif + +// Utility functions +PARSING_DATA FfsParser::getParsingData(const QModelIndex & index) +{ + if (index.isValid()) { + return *(PARSING_DATA*)model->parsingData(index).data(); + } + + PARSING_DATA data; + data.fixed = FALSE; // Item is not fixed by default + data.isOnFlash = TRUE; // Data is on flash by default + data.offset = 0; + data.address = 0; + data.ffsVersion = 0; // Unknown by default + + // Type-specific parts remain unitialized + return data; +} + +QByteArray FfsParser::convertParsingData(const PARSING_DATA & pdata) +{ + return QByteArray((const char*)&pdata, sizeof(PARSING_DATA)); +} + +BOOLEAN FfsParser::hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2) +{ + 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; +} + +// Search routines +STATUS FfsParser::findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode) +{ + if (!index.isValid()) + return ERR_SUCCESS; + + if (hexPattern.isEmpty()) + return ERR_INVALID_PARAMETER; + + // Check for "all substrings" pattern + if (hexPattern.count('.') == hexPattern.length()) + return ERR_SUCCESS; + + bool hasChildren = (model->rowCount(index) > 0); + for (int i = 0; i < model->rowCount(index); i++) { + findHexPattern(index.child(i, index.column()), hexPattern, mode); + } + + QByteArray data; + if (hasChildren) { + if (mode != SEARCH_MODE_BODY) + data = model->header(index); + } + else { + if (mode == SEARCH_MODE_HEADER) + data.append(model->header(index)); + else if (mode == SEARCH_MODE_BODY) + data.append(model->body(index)); + else + data.append(model->header(index)).append(model->body(index)); + } + + QString hexBody = QString(data.toHex()); + QRegExp regexp = QRegExp(QString(hexPattern), Qt::CaseInsensitive); + INT32 offset = regexp.indexIn(hexBody); + while (offset >= 0) { + if (offset % 2 == 0) { + msg(tr("Hex pattern \"%1\" found as \"%2\" in %3 at %4-offset %5h") + .arg(QString(hexPattern)) + .arg(hexBody.mid(offset, hexPattern.length()).toUpper()) + .arg(model->name(index)) + .arg(mode == SEARCH_MODE_BODY ? tr("body") : tr("header")) + .hexarg(offset / 2), + index); + } + offset = regexp.indexIn(hexBody, offset + 1); + } + + return ERR_SUCCESS; +} + +STATUS FfsParser::findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode) +{ + if (guidPattern.isEmpty()) + return ERR_INVALID_PARAMETER; + + if (!index.isValid()) + return ERR_SUCCESS; + + bool hasChildren = (model->rowCount(index) > 0); + for (int i = 0; i < model->rowCount(index); i++) { + findGuidPattern(index.child(i, index.column()), guidPattern, mode); + } + + QByteArray data; + if (hasChildren) { + if (mode != SEARCH_MODE_BODY) + data = model->header(index); + } + else { + if (mode == SEARCH_MODE_HEADER) + data.append(model->header(index)); + else if (mode == SEARCH_MODE_BODY) + data.append(model->body(index)); + else + data.append(model->header(index)).append(model->body(index)); + } + + QString hexBody = QString(data.toHex()); + QList list = guidPattern.split('-'); + if (list.count() != 5) + return ERR_INVALID_PARAMETER; + + QByteArray hexPattern; + // Reverse first GUID block + hexPattern.append(list.at(0).mid(6, 2)); + hexPattern.append(list.at(0).mid(4, 2)); + hexPattern.append(list.at(0).mid(2, 2)); + hexPattern.append(list.at(0).mid(0, 2)); + // Reverse second GUID block + hexPattern.append(list.at(1).mid(2, 2)); + hexPattern.append(list.at(1).mid(0, 2)); + // Reverse third GUID block + hexPattern.append(list.at(2).mid(2, 2)); + hexPattern.append(list.at(2).mid(0, 2)); + // Append fourth and fifth GUID blocks as is + hexPattern.append(list.at(3)).append(list.at(4)); + + // Check for "all substrings" pattern + if (hexPattern.count('.') == hexPattern.length()) + return ERR_SUCCESS; + + QRegExp regexp = QRegExp(QString(hexPattern), Qt::CaseInsensitive); + INT32 offset = regexp.indexIn(hexBody); + while (offset >= 0) { + if (offset % 2 == 0) { + msg(tr("GUID pattern \"%1\" found as \"%2\" in %3 at %4-offset %5h") + .arg(QString(guidPattern)) + .arg(hexBody.mid(offset, hexPattern.length()).toUpper()) + .arg(model->name(index)) + .arg(mode == SEARCH_MODE_BODY ? tr("body") : tr("header")) + .hexarg(offset / 2), + index); + } + offset = regexp.indexIn(hexBody, offset + 1); + } + + return ERR_SUCCESS; +} + +STATUS FfsParser::findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive) +{ + if (pattern.isEmpty()) + return ERR_INVALID_PARAMETER; + + if (!index.isValid()) + return ERR_SUCCESS; + + bool hasChildren = (model->rowCount(index) > 0); + for (int i = 0; i < model->rowCount(index); i++) { + findTextPattern(index.child(i, index.column()), pattern, unicode, caseSensitive); + } + + if (hasChildren) + return ERR_SUCCESS; + + QString data; + if (unicode) + data = QString::fromUtf16((const ushort*)model->body(index).data(), model->body(index).length() / 2); + else + data = QString::fromLatin1((const char*)model->body(index).data(), model->body(index).length()); + + int offset = -1; + while ((offset = data.indexOf(pattern, offset + 1, caseSensitive)) >= 0) { + msg(tr("%1 text \"%2\" found in %3 at offset %4h") + .arg(unicode ? "Unicode" : "ASCII") + .arg(pattern) + .arg(model->name(index)) + .hexarg(unicode ? offset * 2 : offset), + index); + } + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseAprioriRawSection(const QByteArray & body, QString & parsed) +{ + parsed.clear(); + + UINT32 count = body.size() / sizeof(EFI_GUID); + if (count > 0) { + for (UINT32 i = 0; i < count; i++) { + const EFI_GUID* guid = (const EFI_GUID*)body.constData() + i; + parsed += tr("\n%1").arg(guidToQString(*guid)); + } + } + + return ERR_SUCCESS; +} + +// Firmware image parsing functions +STATUS FfsParser::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))); + return ERR_INVALID_PARAMETER; + } + + // Check buffer for being normal EFI capsule header + UINT32 capsuleHeaderSize = 0; + QModelIndex index; + if (buffer.startsWith(EFI_CAPSULE_GUID)) { + // Get info + const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_CAPSULE_HEADER*)buffer.constData(); + capsuleHeaderSize = capsuleHeader->HeaderSize; + QByteArray header = buffer.left(capsuleHeaderSize); + QByteArray body = buffer.mid(capsuleHeaderSize); + QString name = tr("UEFI capsule"); + QString info = tr("Offset: 0h\nCapsule 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->CapsuleImageSize).arg(capsuleHeader->CapsuleImageSize) + .hexarg2(capsuleHeader->Flags, 8); + + // Construct parsing data + PARSING_DATA pdata = getParsingData(); + pdata.fixed = TRUE; + + // Add tree item + index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, name, QString(), info, header, body, convertParsingData(pdata)); + } + // Check buffer for being extended Aptio signed capsule header + else if (buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID) || buffer.startsWith(APTIO_UNSIGNED_CAPSULE_GUID)) { + + bool signedCapsule = buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID); + // Get info + const APTIO_CAPSULE_HEADER* capsuleHeader = (const APTIO_CAPSULE_HEADER*)buffer.constData(); + capsuleHeaderSize = capsuleHeader->RomImageOffset; + QByteArray header = buffer.left(capsuleHeaderSize); + QByteArray body = buffer.mid(capsuleHeaderSize); + QString name = tr("AMI Aptio capsule"); + QString info = tr("Offset: 0h\nCapsule GUID: %1\nFull size: %2h (%3)\nHeader size: %4h (%5)\nImage size: %6h (%7)\nFlags: %8h") + .arg(guidToQString(capsuleHeader->CapsuleHeader.CapsuleGuid)) + .hexarg(buffer.size()).arg(buffer.size()) + .hexarg(capsuleHeaderSize).arg(capsuleHeaderSize) + .hexarg(capsuleHeader->CapsuleHeader.CapsuleImageSize - capsuleHeaderSize).arg(capsuleHeader->CapsuleHeader.CapsuleImageSize - capsuleHeaderSize) + .hexarg2(capsuleHeader->CapsuleHeader.Flags, 8); + + // Construct parsing data + PARSING_DATA pdata = getParsingData(); + pdata.fixed = TRUE; + + // Add tree item + index = model->addItem(Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, name, QString(), info, header, body, convertParsingData(pdata)); + + // Show message about possible Aptio signature break + if (signedCapsule) { + msg(tr("parseImageFile: Aptio capsule signature may become invalid after image modifications"), index); + } + } + + // Skip capsule header to have flash chip image + QByteArray flashImage = buffer.mid(capsuleHeaderSize); + + // Check for Intel flash descriptor presence + const FLASH_DESCRIPTOR_HEADER* descriptorHeader = (const FLASH_DESCRIPTOR_HEADER*)flashImage.constData(); + + // Check descriptor signature + STATUS result; + if (descriptorHeader->Signature == FLASH_DESCRIPTOR_SIGNATURE) { + // Parse as Intel image + QModelIndex imageIndex; + result = parseIntelImage(flashImage, index, imageIndex); + if (result != ERR_INVALID_FLASH_DESCRIPTOR) + return result; + } + + // Get info + QString name = tr("UEFI image"); + QString info = tr("Offset: %1h\nFull size: %2h (%3)") + .hexarg(capsuleHeaderSize).hexarg(flashImage.size()).arg(flashImage.size()); + + // Construct parsing data + PARSING_DATA pdata = getParsingData(index); + pdata.fixed = TRUE; + pdata.offset = capsuleHeaderSize; + + // Add tree item + QModelIndex biosIndex = model->addItem(Types::Image, Subtypes::UefiImage, name, QString(), info, QByteArray(), flashImage, convertParsingData(pdata), index); + return parseRawArea(flashImage, biosIndex); +} + +STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const QModelIndex & parent, QModelIndex & index) +{ + // Sanity check + if (intelImage.isEmpty()) + return EFI_INVALID_PARAMETER; + + // Get parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // 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) { + msg(tr("parseIntelImage: input file is smaller then minimum descriptor size of %1h (%2) bytes").hexarg(FLASH_DESCRIPTOR_SIZE).arg(FLASH_DESCRIPTOR_SIZE)); + 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; + } + // ME region + QByteArray me; + UINT32 meBegin = 0; + UINT32 meEnd = 0; + if (regionSection->MeLimit) { + meBegin = calculateRegionOffset(regionSection->MeBase); + meEnd = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); + 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; + UINT32 biosEnd = 0; + if (regionSection->BiosLimit) { + biosBegin = calculateRegionOffset(regionSection->BiosBase); + biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); + + // Check for Gigabyte specific descriptor map + if (biosEnd - biosBegin == (UINT32)intelImage.size()) { + if (!meEnd) { + 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 += biosBegin; + } + else { + msg(tr("parseIntelImage: descriptor parsing failed, BIOS region not found in descriptor")); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + + // Check for intersections between regions + 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 (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 (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 (hasIntersection(biosBegin, biosEnd, pdrBegin, pdrEnd)) { + msg(tr("parseIntelImage: descriptor parsing failed, BIOS region has intersection with PDR 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") + .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->NumberOfPchStraps) + .arg(descriptorMap->NumberOfProcStraps) + .arg(descriptorMap->NumberOfIccTableEntries); + + // Construct parsing data + pdata.fixed = TRUE; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add Intel image tree item + index = model->addItem(Types::Image, Subtypes::IntelImage, name, QString(), info, QByteArray(), intelImage, convertParsingData(pdata), parent); + + // Descriptor + // Get descriptor info + 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); + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Check regions presence once again + QVector offsets; + if (regionSection->GbeLimit) { + offsets.append(gbeBegin); + info += tr("\nGbE region offset: %1h").hexarg(gbeBegin); + } + if (regionSection->MeLimit) { + offsets.append(meBegin); + info += tr("\nME region offset: %1h").hexarg(meBegin); + } + if (regionSection->BiosLimit) { + offsets.append(biosBegin); + info += tr("\nBIOS region offset: %1h").hexarg(biosBegin); + } + if (regionSection->PdrLimit) { + offsets.append(pdrBegin); + info += tr("\nPDR region offset: %1h").hexarg(pdrBegin); + } + + // 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); + + // 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 "); + + // VSCC table + const VSCC_TABLE_ENTRY* vsccTableEntry = (const VSCC_TABLE_ENTRY*)(descriptor + ((UINT16)upperMap->VsccTableBase << 4)); + info += tr("\nFlash chips in VSCC table:"); + UINT8 vsscTableSize = upperMap->VsccTableSize * sizeof(UINT32) / sizeof(VSCC_TABLE_ENTRY); + for (int i = 0; i < vsscTableSize; i++) { + info += tr("\n%1%2%3h") + .hexarg2(vsccTableEntry->VendorId, 2) + .hexarg2(vsccTableEntry->DeviceId0, 2) + .hexarg2(vsccTableEntry->DeviceId1, 2); + vsccTableEntry++; + } + + // Add descriptor tree item + model->addItem(Types::Region, Subtypes::DescriptorRegion, name, QString(), info, QByteArray(), body, convertParsingData(pdata), index); + + // Sort regions in ascending order + qSort(offsets); + + // 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); + } + // 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); + } + if (result) + return result; + } + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseGbeRegion(const QByteArray & gbe, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Check sanity + if (gbe.isEmpty()) + return ERR_EMPTY_REGION; + + // Get parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Get info + QString name = tr("GbE region"); + const GBE_MAC_ADDRESS* mac = (const GBE_MAC_ADDRESS*)gbe.constData(); + const GBE_VERSION* version = (const GBE_VERSION*)(gbe.constData() + GBE_VERSION_OFFSET); + QString info = tr("Full size: %1h (%2)\nMAC: %3:%4:%5:%6:%7:%8\nVersion: %9.%10") + .hexarg(gbe.size()).arg(gbe.size()) + .hexarg2(mac->vendor[0], 2) + .hexarg2(mac->vendor[1], 2) + .hexarg2(mac->vendor[2], 2) + .hexarg2(mac->device[0], 2) + .hexarg2(mac->device[1], 2) + .hexarg2(mac->device[2], 2) + .arg(version->major) + .arg(version->minor); + + // Construct parsing data + pdata.fixed = TRUE; + pdata.offset += parentOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::Region, Subtypes::GbeRegion, name, QString(), info, QByteArray(), gbe, convertParsingData(pdata), parent); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseMeRegion(const QByteArray & me, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Check sanity + if (me.isEmpty()) + return ERR_EMPTY_REGION; + + // Get parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Get info + QString name = tr("ME/TXE region"); + QString info = tr("Full size: %1h (%2)"). + hexarg(me.size()).arg(me.size()); + + // Parse region + bool versionFound = true; + bool emptyRegion = false; + // Check for empty region + if (me.count() == me.count('\xFF') || me.count() == me.count('\x00')) { + // Further parsing not needed + emptyRegion = true; + info += tr("\nState: empty"); + } + else { + // Search for new signature + INT32 versionOffset = me.indexOf(ME_VERSION_SIGNATURE2); + if (versionOffset < 0){ // New signature not found + // Search for old signature + versionOffset = me.indexOf(ME_VERSION_SIGNATURE); + if (versionOffset < 0){ + info += tr("\nVersion: unknown"); + versionFound = false; + } + } + + // Add version information + if (versionFound) { + const ME_VERSION* version = (const ME_VERSION*)(me.constData() + versionOffset); + info += tr("\nVersion: %1.%2.%3.%4") + .arg(version->major) + .arg(version->minor) + .arg(version->bugfix) + .arg(version->build); + } + } + + // Construct parsing data + pdata.fixed = TRUE; + pdata.offset += parentOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::Region, Subtypes::MeRegion, name, QString(), info, QByteArray(), me, convertParsingData(pdata), parent); + + // Show messages + if (emptyRegion) { + msg(tr("parseMeRegion: ME/TXE region is empty"), index); + } + else if (!versionFound) { + msg(tr("parseMeRegion: ME/TXE region version is unknown, it can be damaged"), index); + } + + return ERR_SUCCESS; +} + +STATUS FfsParser::parsePdrRegion(const QByteArray & pdr, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Check sanity + if (pdr.isEmpty()) + return ERR_EMPTY_REGION; + + // Get parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Get info + QString name = tr("PDR region"); + QString info = tr("Full size: %1h (%2)"). + hexarg(pdr.size()).arg(pdr.size()); + + // Construct parsing data + pdata.fixed = TRUE; + pdata.offset += parentOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::Region, Subtypes::PdrRegion, name, QString(), info, QByteArray(), pdr, convertParsingData(pdata), parent); + + // Parse PDR region as BIOS space + UINT8 result = parseRawArea(pdr, index); + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) + return result; + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseBiosRegion(const QByteArray & bios, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Sanity check + if (bios.isEmpty()) + return ERR_EMPTY_REGION; + + // Get parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Get info + QString name = tr("BIOS region"); + QString info = tr("Full size: %1h (%2)"). + hexarg(bios.size()).arg(bios.size()); + + // Construct parsing data + pdata.fixed = TRUE; + pdata.offset += parentOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::Region, Subtypes::BiosRegion, name, QString(), info, QByteArray(), bios, convertParsingData(pdata), parent); + + return parseRawArea(bios, index); +} + +UINT8 FfsParser::getPaddingType(const QByteArray & padding) +{ + if (padding.count('\x00') == padding.count()) + return Subtypes::ZeroPadding; + if (padding.count('\xFF') == padding.count()) + return Subtypes::OnePadding; + return Subtypes::DataPadding; +} + +STATUS FfsParser::parseRawArea(const QByteArray & bios, const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Get parsing data + PARSING_DATA pdata = getParsingData(index); + UINT32 offset = pdata.offset; + UINT32 headerSize = model->header(index).size(); + + // Search for first volume + STATUS result; + UINT32 prevVolumeOffset; + + result = findNextVolume(bios, 0, prevVolumeOffset); + if (result) + return result; + + // First volume is not at the beginning of BIOS space + QString name; + QString info; + if (prevVolumeOffset > 0) { + // Get info + QByteArray padding = bios.left(prevVolumeOffset); + name = tr("Padding"); + info = tr("Full size: %1h (%2)") + .hexarg(padding.size()).arg(padding.size()); + + // Construct parsing data + pdata.fixed = TRUE; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, convertParsingData(pdata), index); + } + + // Search for and parse all volumes + UINT32 volumeOffset = prevVolumeOffset; + UINT32 prevVolumeSize = 0; + + while (!result) + { + // Padding between volumes + if (volumeOffset > prevVolumeOffset + prevVolumeSize) { + UINT32 paddingOffset = prevVolumeOffset + prevVolumeSize; + UINT32 paddingSize = volumeOffset - paddingOffset; + QByteArray padding = bios.mid(paddingOffset, paddingSize); + + // Get info + name = tr("Padding"); + info = tr("Full size: %1h (%2)") + .hexarg(padding.size()).arg(padding.size()); + + // Construct parsing data + pdata.fixed = TRUE; + pdata.offset = offset + headerSize + paddingOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, convertParsingData(pdata), index); + } + + // Get volume size + UINT32 volumeSize = 0; + UINT32 bmVolumeSize = 0; + result = getVolumeSize(bios, volumeOffset, volumeSize, bmVolumeSize); + if (result) + return result; + + // Check that volume is fully present in input + QByteArray volume = bios.mid(volumeOffset, volumeSize); + if (volumeSize > (UINT32)volume.size()) { + // Mark the rest as padding and finish the parsing + QByteArray padding = bios.right(volume.size()); + + // Get info + name = tr("Padding"); + info = tr("Full size: %1h (%2)") + .hexarg(padding.size()).arg(padding.size()); + + // Construct parsing data + pdata.fixed = TRUE; + pdata.offset = offset + headerSize + volumeOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + QModelIndex paddingIndex = model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, convertParsingData(pdata), index); + msg(tr("parseRawArea: one of volumes inside overlaps the end of data"), paddingIndex); + + // Update variables + prevVolumeOffset = volumeOffset; + prevVolumeSize = padding.size(); + break; + } + + // Parse current volume's header + QModelIndex volumeIndex; + result = parseVolumeHeader(volume, model->header(index).size() + volumeOffset, index, volumeIndex); + if (result) + msg(tr("parseRawArea: volume header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index); + else { + // Show messages + if (volumeSize != bmVolumeSize) + msg(tr("parseBiosBody: volume size stored in header %1h (%2) differs from calculated using block map %3h (%4)") + .hexarg(volumeSize).arg(volumeSize) + .hexarg(bmVolumeSize).arg(bmVolumeSize), + volumeIndex); + } + + // Go to next volume + prevVolumeOffset = volumeOffset; + prevVolumeSize = volumeSize; + result = findNextVolume(bios, volumeOffset + prevVolumeSize, volumeOffset); + } + + // Padding at the end of BIOS space + volumeOffset = prevVolumeOffset + prevVolumeSize; + if ((UINT32)bios.size() > volumeOffset) { + QByteArray padding = bios.mid(volumeOffset); + + // Get info + name = tr("Padding"); + info = tr("Full size: %1h (%2)") + .hexarg(padding.size()).arg(padding.size()); + + // Construct parsing data + pdata.fixed = TRUE; + pdata.offset = offset + headerSize + volumeOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, convertParsingData(pdata), index); + } + + //Parse bodies + for (int i = 0; i < model->rowCount(index); i++) { + QModelIndex current = index.child(i, 0); + switch (model->type(current)) { + case Types::Volume: + parseVolumeBody(current); + break; + case Types::Padding: + // No parsing required + break; + default: + return ERR_UNKNOWN_ITEM_TYPE; + } + } + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Sanity check + if (volume.isEmpty()) + return ERR_INVALID_PARAMETER; + + // Get parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Populate volume header + const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(volume.constData()); + + // Calculate volume header size + UINT32 headerSize; + EFI_GUID extendedHeaderGuid = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; + bool hasExtendedHeader = false; + if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { + hasExtendedHeader = true; + const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)(volume.constData() + volumeHeader->ExtHeaderOffset); + headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize; + extendedHeaderGuid = extendedHeader->FvName; + } + else + headerSize = volumeHeader->HeaderLength; + + // Extended header end can be unaligned + headerSize = ALIGN8(headerSize); + + // Check for volume structure to be known + bool isUnknown = true; + UINT8 ffsVersion = 0; + + // Check for FFS v2 volume + if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { + isUnknown = false; + ffsVersion = 2; + } + + // Check for FFS v3 volume + if (FFSv3Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { + isUnknown = false; + ffsVersion = 3; + } + + // Check volume revision and alignment + bool msgAlignmentBitsSet = false; + bool msgUnaligned = false; + bool msgUnknownRevision = false; + UINT32 alignment = 65536; // Default volume alignment is 64K + if (volumeHeader->Revision == 1) { + // Acquire alignment capability bit + bool alignmentCap = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_CAP; + if (!alignmentCap) { + if ((volumeHeader->Attributes & 0xFFFF0000)) + msgAlignmentBitsSet = true; + } + // Do not check for volume alignment on revision 1 volumes + // No one gives a single crap about setting it correctly + /*else { + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_2) alignment = 2; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_4) alignment = 4; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_8) alignment = 8; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_16) alignment = 16; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_32) alignment = 32; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_64) alignment = 64; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_128) alignment = 128; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_256) alignment = 256; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_512) alignment = 512; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_1K) alignment = 1024; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_2K) alignment = 2048; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_4K) alignment = 4096; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_8K) alignment = 8192; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_16K) alignment = 16384; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_32K) alignment = 32768; + if (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_64K) alignment = 65536; + }*/ + } + else if (volumeHeader->Revision == 2) { + // Acquire alignment + alignment = (UINT32)pow(2.0, (int)(volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16); + // Check alignment + if (!isUnknown && pdata.isOnFlash && ((pdata.offset + parentOffset) % alignment)) + msgUnaligned = true; + } + else + msgUnknownRevision = true; + + // Check attributes + // Determine value of empty byte + UINT8 emptyByte = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; + + // Check for Apple CRC32 in ZeroVector + bool hasZeroVectorCRC32 = false; + UINT32 volumeSize = volume.size(); + UINT32 crc32FromZeroVector = *(UINT32*)(volume.constData() + 8); + 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) { + hasZeroVectorCRC32 = true; + } + } + + // Check header checksum by recalculating it + bool msgInvalidChecksum = false; + if (calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength)) + msgInvalidChecksum = true; + + // Get info + QByteArray header = volume.left(headerSize); + QByteArray body = volume.mid(headerSize); + QString name = guidToQString(volumeHeader->FileSystemGuid); + QString info = tr("ZeroVector:\n%1 %2 %3 %4 %5 %6 %7 %8\n%9 %10 %11 %12 %13 %14 %15 %16\nFileSystem GUID: %17\nFull size: %18h (%19)\n" + "Header size: %20h (%21)\nBody size: %22h (%23)\nRevision: %24\nAttributes: %25h\nErase polarity: %26") + .hexarg2(volumeHeader->ZeroVector[0], 2).hexarg2(volumeHeader->ZeroVector[1], 2).hexarg2(volumeHeader->ZeroVector[2], 2).hexarg2(volumeHeader->ZeroVector[3], 2) + .hexarg2(volumeHeader->ZeroVector[4], 2).hexarg2(volumeHeader->ZeroVector[5], 2).hexarg2(volumeHeader->ZeroVector[6], 2).hexarg2(volumeHeader->ZeroVector[7], 2) + .hexarg2(volumeHeader->ZeroVector[8], 2).hexarg2(volumeHeader->ZeroVector[9], 2).hexarg2(volumeHeader->ZeroVector[10], 2).hexarg2(volumeHeader->ZeroVector[11], 2) + .hexarg2(volumeHeader->ZeroVector[12], 2).hexarg2(volumeHeader->ZeroVector[13], 2).hexarg2(volumeHeader->ZeroVector[14], 2).hexarg2(volumeHeader->ZeroVector[15], 2) + .arg(guidToQString(volumeHeader->FileSystemGuid)) + .hexarg(volumeSize).arg(volumeSize) + .hexarg(headerSize).arg(headerSize) + .hexarg(volumeSize - headerSize).arg(volumeSize - headerSize) + .arg(volumeHeader->Revision) + .hexarg2(volumeHeader->Attributes, 8) + .arg(emptyByte ? "1" : "0"); + + // Apple CRC32 volume + if (hasZeroVectorCRC32) { + info += tr("\nCRC32 in ZeroVector: valid"); + } + + // Extended header present + if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { + const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)(volume.constData() + volumeHeader->ExtHeaderOffset); + info += tr("\nExtended header size: %1h (%2)\nVolume GUID: %3") + .hexarg(extendedHeader->ExtHeaderSize).arg(extendedHeader->ExtHeaderSize) + .arg(guidToQString(extendedHeader->FvName)); + } + + // Construct parsing data + pdata.fixed = TRUE; + pdata.offset += parentOffset; + pdata.emptyByte = emptyByte; + pdata.ffsVersion = ffsVersion; + pdata.volume.hasExtendedHeader = hasExtendedHeader ? TRUE : FALSE; + pdata.volume.extendedHeaderGuid = extendedHeaderGuid; + pdata.volume.alignment = alignment; + pdata.volume.revision = volumeHeader->Revision; + pdata.volume.hasZeroVectorCRC32 = hasZeroVectorCRC32; + pdata.volume.isWeakAligned = (volumeHeader->Revision > 1 && (volumeHeader->Attributes & EFI_FVB2_WEAK_ALIGNMENT)); + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add text + QString text; + if (hasZeroVectorCRC32) + text += tr("ZeroVectorCRC32 "); + + // Add tree item + UINT8 subtype = Subtypes::UnknownVolume; + if (!isUnknown) { + if (ffsVersion == 2) + subtype = Subtypes::Ffs2Volume; + else if (ffsVersion == 3) + subtype = Subtypes::Ffs3Volume; + } + index = model->addItem(Types::Volume, subtype, name, text, info, header, body, convertParsingData(pdata), parent); + + // Show messages + if (isUnknown) + msg(tr("parseVolumeHeader: unknown file system %1").arg(guidToQString(volumeHeader->FileSystemGuid)), index); + if (msgInvalidChecksum) + msg(tr("parseVolumeHeader: volume header checksum is invalid"), index); + if (msgAlignmentBitsSet) + msg(tr("parseVolumeHeader: alignment bits set on volume without alignment capability"), index); + if (msgUnaligned) + msg(tr("parseVolumeHeader: unaligned volume"), index); + if (msgUnknownRevision) + msg(tr("parseVolumeHeader: unknown volume revision %1").arg(volumeHeader->Revision), index); + + return ERR_SUCCESS; +} + +STATUS FfsParser::findNextVolume(const QByteArray & bios, UINT32 volumeOffset, UINT32 & nextVolumeOffset) +{ + int nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset); + if (nextIndex < EFI_FV_SIGNATURE_OFFSET) + return ERR_VOLUMES_NOT_FOUND; + + nextVolumeOffset = nextIndex - EFI_FV_SIGNATURE_OFFSET; + return ERR_SUCCESS; +} + +STATUS FfsParser::getVolumeSize(const QByteArray & bios, UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize) +{ + // Populate volume header + const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + volumeOffset); + + // Check volume signature + if (QByteArray((const char*)&volumeHeader->Signature, sizeof(volumeHeader->Signature)) != EFI_FV_SIGNATURE) + return ERR_INVALID_VOLUME; + + // Calculate volume size using BlockMap + const EFI_FV_BLOCK_MAP_ENTRY* entry = (const EFI_FV_BLOCK_MAP_ENTRY*)(bios.constData() + volumeOffset + sizeof(EFI_FIRMWARE_VOLUME_HEADER)); + UINT32 calcVolumeSize = 0; + while (entry->NumBlocks != 0 && entry->Length != 0) { + if ((void*)entry > bios.constData() + bios.size()) + return ERR_INVALID_VOLUME; + + calcVolumeSize += entry->NumBlocks * entry->Length; + entry += 1; + } + + volumeSize = volumeHeader->FvLength; + bmVolumeSize = calcVolumeSize; + return ERR_SUCCESS; +} + +STATUS FfsParser::parseVolumeBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Get volume header size and body + QByteArray volumeBody = model->body(index); + UINT32 volumeHeaderSize = model->header(index).size(); + + // Get parsing data + PARSING_DATA pdata = getParsingData(index); + UINT32 offset = pdata.offset; + + if (pdata.ffsVersion != 2 && pdata.ffsVersion != 3) // Don't parse unknown volumes + return ERR_SUCCESS; + + // Search for and parse all files + UINT32 volumeBodySize = volumeBody.size(); + UINT32 fileOffset = 0; + + while (fileOffset < volumeBodySize) { + UINT32 fileSize = getFileSize(volumeBody, fileOffset, pdata.ffsVersion); + // Check file size + if (fileSize < sizeof(EFI_FFS_FILE_HEADER) || fileSize > volumeBodySize - fileOffset) { + // Check that we are at the empty space + QByteArray header = volumeBody.mid(fileOffset, sizeof(EFI_FFS_FILE_HEADER)); + if (header.count(pdata.emptyByte) == header.size()) { //Empty space + // Check free space to be actually free + QByteArray freeSpace = volumeBody.mid(fileOffset); + if (freeSpace.count(pdata.emptyByte) != freeSpace.count()) { + // Search for the first non-empty byte + UINT32 i; + UINT32 size = freeSpace.size(); + const UINT8* current = (UINT8*)freeSpace.constData(); + for (i = 0; i < size; i++) { + if (*current++ != pdata.emptyByte) + break; + } + + // Align found index to file alignment + // It must be possible because minimum 16 bytes of empty were found before + if (i != ALIGN8(i)) + i = ALIGN8(i) - 8; + + // Construct parsing data + pdata.fixed = FALSE; // Free space is not fixed + pdata.offset = offset + volumeHeaderSize + fileOffset; + + // Add all bytes before as free space... + if (i > 0) { + QByteArray free = freeSpace.left(i); + + // Get info + QString info = tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()); + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add free space item + model->addItem(Types::FreeSpace, 0, tr("Volume free space"), "", info, QByteArray(), free, convertParsingData(pdata), index); + } + // ... and all bytes after as a padding + pdata.fixed = TRUE; // Non-UEFI data is fixed + pdata.offset += i; + QByteArray padding = freeSpace.mid(i); + + // Get info + QString info = tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()); + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add padding tree item + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, tr("Non-UEFI data"), "", info, QByteArray(), padding, convertParsingData(pdata), index); + msg(tr("parseVolumeBody: non-UEFI data found in volume's free space"), dataIndex); + } + else { + // Construct parsing data + pdata.fixed = FALSE; // Free space is not fixed + pdata.offset = offset + volumeHeaderSize + fileOffset; + + // Get info + QString info = tr("Full size: %1h (%2)").hexarg(freeSpace.size()).arg(freeSpace.size()); + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add free space item + model->addItem(Types::FreeSpace, 0, tr("Volume free space"), "", info, QByteArray(), freeSpace, convertParsingData(pdata), index); + } + break; // Exit from parsing loop + } + else { //File space + // Add padding to the end of the volume + pdata.fixed = TRUE; // Non-UEFI data is fixed + pdata.offset = offset + volumeHeaderSize + fileOffset; + QByteArray padding = volumeBody.mid(fileOffset); + + // Get info + QString info = tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()); + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add padding tree item + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, tr("Non-UEFI data"), "", info, QByteArray(), padding, convertParsingData(pdata), index); + + // Show message + msg(tr("parseVolumeBody: non-UEFI data found inside volume's file space"), dataIndex); + + break; // Exit from parsing loop + } + } + + // Get file header + QByteArray file = volumeBody.mid(fileOffset, fileSize); + QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); + const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)header.constData(); + if (pdata.ffsVersion == 3 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + header = file.left(sizeof(EFI_FFS_FILE_HEADER2)); + } + + //Parse current file's header + QModelIndex fileIndex; + STATUS result = parseFileHeader(file, volumeHeaderSize + fileOffset, index, fileIndex); + if (result) + msg(tr("parseVolumeBody: file header parsing failed with error %1").arg(errorCodeToQString(result)), index); + + // Move to next file + fileOffset += fileSize; + fileOffset = ALIGN8(fileOffset); + } + + // Check for duplicate GUIDs + for (int i = 0; i < model->rowCount(index); i++) { + QModelIndex current = index.child(i, 0); + // Skip non-file entries and pad files + if (model->type(current) != Types::File || model->subtype(current) == EFI_FV_FILETYPE_PAD) + continue; + QByteArray currentGuid = model->header(current).left(sizeof(EFI_GUID)); + // Check files after current for having the same GUID + for (int j = i + 1; j < model->rowCount(index); j++) { + QModelIndex another = index.child(j, 0); + // Skip non-file entries + if (model->type(another) != Types::File) + continue; + // Check GUIDs for being same + QByteArray anotherGuid = model->header(another).left(sizeof(EFI_GUID)); + if (currentGuid == anotherGuid) { + msg(tr("parseVolumeBody: file with duplicate GUID %1").arg(guidToQString(*(const EFI_GUID*)anotherGuid.constData())), another); + } + } + } + + //Parse bodies + for (int i = 0; i < model->rowCount(index); i++) { + QModelIndex current = index.child(i, 0); + switch (model->type(current)) { + case Types::File: + parseFileBody(current); + break; + case Types::Padding: + case Types::FreeSpace: + // No parsing required + break; + default: + return ERR_UNKNOWN_ITEM_TYPE; + } + } + + return ERR_SUCCESS; +} + +UINT32 FfsParser::getFileSize(const QByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion) +{ + if (ffsVersion == 2) { + const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)(volume.constData() + fileOffset); + return uint24ToUint32(fileHeader->Size); + } + else if (ffsVersion == 3) { + const EFI_FFS_FILE_HEADER2* fileHeader = (const EFI_FFS_FILE_HEADER2*)(volume.constData() + fileOffset); + if (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE) + return fileHeader->ExtendedSize; + else + return uint24ToUint32(fileHeader->Size); + } + else + return 0; +} + +STATUS FfsParser::parseFileHeader(const QByteArray & file, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Sanity check + if (file.isEmpty()) + return ERR_INVALID_PARAMETER; + + // Get parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Get file header + QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); + const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)header.constData(); + if (pdata.ffsVersion == 3 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + header = file.left(sizeof(EFI_FFS_FILE_HEADER2)); + } + + // Check file alignment + bool msgUnalignedFile = false; + UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; + UINT32 alignment = (UINT32)pow(2.0, alignmentPower); + if ((parentOffset + header.size()) % alignment) + msgUnalignedFile = true; + + // Check file alignment agains volume alignment + bool msgFileAlignmentIsGreaterThenVolumes = false; + if (!pdata.volume.isWeakAligned && pdata.volume.alignment < alignment) + msgFileAlignmentIsGreaterThenVolumes = true; + + // Check header checksum + QByteArray tempHeader = header; + EFI_FFS_FILE_HEADER* tempFileHeader = (EFI_FFS_FILE_HEADER*)(tempHeader.data()); + tempFileHeader->IntegrityCheck.Checksum.Header = 0; + tempFileHeader->IntegrityCheck.Checksum.File = 0; + UINT8 calculated = calculateChecksum8((const UINT8*)tempFileHeader, header.size() - 1); + bool msgInvalidHeaderChecksum = false; + if (fileHeader->IntegrityCheck.Checksum.Header != calculated) + msgInvalidHeaderChecksum = true; + + // Check data checksum + // Data checksum must be calculated + bool msgInvalidDataChecksum = false; + if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { + UINT32 bufferSize = file.size() - header.size(); + // Exclude file tail from data checksum calculation + if (pdata.volume.revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) + bufferSize -= sizeof(UINT16); + calculated = calculateChecksum8((const UINT8*)(file.constData() + header.size()), bufferSize); + if (fileHeader->IntegrityCheck.Checksum.File != calculated) + msgInvalidDataChecksum = true; + } + // Data checksum must be one of predefined values + else if (pdata.volume.revision == 1 && fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM) + msgInvalidDataChecksum = true; + else if (pdata.volume.revision == 2 && fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2) + msgInvalidDataChecksum = true; + + // Check file type + bool msgUnknownType = false; + if (fileHeader->Type > EFI_FV_FILETYPE_SMM_CORE && fileHeader->Type != EFI_FV_FILETYPE_PAD) { + msgUnknownType = true; + }; + + // Get file body + QByteArray body = file.mid(header.size()); + + // Check for file tail presence + UINT16 tail = 0; + bool msgInvalidTailValue = false; + bool hasTail = false; + if (pdata.volume.revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) + { + hasTail = true; + + //Check file tail; + tail = *(UINT16*)body.right(sizeof(UINT16)).constData(); + if (fileHeader->IntegrityCheck.TailReference != (UINT16)~tail) + msgInvalidTailValue = true; + + // Remove tail from file body + body = body.left(body.size() - sizeof(UINT16)); + } + + // Get info + QString name; + QString info; + if (fileHeader->Type != EFI_FV_FILETYPE_PAD) + name = guidToQString(fileHeader->Name); + else + name = 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()).arg(header.size()) + .hexarg(body.size()).arg(body.size()) + .hexarg2(fileHeader->State, 2); + + // Construct parsing data + pdata.fixed = fileHeader->Attributes & FFS_ATTRIB_FIXED; + pdata.offset += parentOffset; + pdata.file.hasTail = hasTail ? TRUE : FALSE; + pdata.file.tail = tail; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::File, fileHeader->Type, name, "", info, header, body, convertParsingData(pdata), parent); + + // Show messages + if (msgUnalignedFile) + msg(tr("parseFileHeader: unaligned file"), index); + if (msgFileAlignmentIsGreaterThenVolumes) + msg(tr("parseFileHeader: file alignment %1h is greater than parent volume alignment %2h").hexarg(alignment).hexarg(pdata.volume.alignment), index); + if (msgInvalidHeaderChecksum) + msg(tr("parseFileHeader: invalid header checksum"), index); + if (msgInvalidDataChecksum) + msg(tr("parseFileHeader: invalid data checksum"), index); + if (msgInvalidTailValue) + msg(tr("parseFileHeader: invalid tail value"), index); + if (msgUnknownType) + msg(tr("parseFileHeader: unknown file type %1h").hexarg2(fileHeader->Type, 2), index); + + return ERR_SUCCESS; +} + +UINT32 FfsParser::getSectionSize(const QByteArray & file, const UINT32 sectionOffset, const UINT8 ffsVersion) +{ + if (ffsVersion == 2) { + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(file.constData() + sectionOffset); + return uint24ToUint32(sectionHeader->Size); + } + else if (ffsVersion == 3) { + const EFI_COMMON_SECTION_HEADER2* sectionHeader = (const EFI_COMMON_SECTION_HEADER2*)(file.constData() + sectionOffset); + UINT32 size = uint24ToUint32(sectionHeader->Size); + if (size == EFI_SECTION2_IS_USED) + return sectionHeader->ExtendedSize; + else + return size; + } + else + return 0; +} + +STATUS FfsParser::parseFileBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Do not parse non-file bodies + if (model->type(index) != Types::File) + return ERR_SUCCESS; + + // Parse pad-file body + if (model->subtype(index) == EFI_FV_FILETYPE_PAD) + return parsePadFileBody(index); + + // Parse raw files as raw areas + if (model->subtype(index) == EFI_FV_FILETYPE_RAW || model->subtype(index) == EFI_FV_FILETYPE_ALL) + return parseRawArea(model->body(index), index); + + // Parse sections + return parseSections(model->body(index), index); +} + +STATUS FfsParser::parsePadFileBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Get data from parsing data + PARSING_DATA pdata = getParsingData(index); + + // Check if all bytes of the file are empty + QByteArray body = model->body(index); + if (body.size() == body.count(pdata.emptyByte)) + return ERR_SUCCESS; + + // Search for the first non-empty byte + UINT32 i; + UINT32 size = body.size(); + const UINT8* current = (const UINT8*)body.constData(); + for (i = 0; i < size; i++) { + if (*current++ != pdata.emptyByte) + break; + } + + // Add all bytes before as free space... + if (i >= 8) { + // Align free space to 8 bytes boundary + if (i != ALIGN8(i)) + i = ALIGN8(i) - 8; + + QByteArray free = body.left(i); + + // Get info + QString info = tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()); + + // Constuct parsing data + pdata.offset += model->header(index).size(); + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + model->addItem(Types::FreeSpace, 0, tr("Free space"), QString(), info, QByteArray(), free, convertParsingData(pdata), index); + } + else + i = 0; + + // ... and all bytes after as a padding + QByteArray padding = body.mid(i); + + // Get info + QString info = tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()); + + // Constuct parsing data + pdata.offset += i; + pdata.fixed = TRUE; // This data is fixed + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, tr("Non-UEFI data"), "", info, QByteArray(), padding, convertParsingData(pdata), index); + + // Show message + msg(tr("parsePadFileBody: non-UEFI data found in pad-file"), dataIndex); + + // Rename the file + model->setName(index, tr("Non-empty pad-file")); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseSections(QByteArray sections, const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Get data from parsing data + PARSING_DATA pdata = getParsingData(index); + + // Search for and parse all sections + UINT32 bodySize = sections.size(); + UINT32 headerSize = model->header(index).size(); + UINT32 sectionOffset = 0; + + while (sectionOffset < bodySize) { + // Get section size + UINT32 sectionSize = getSectionSize(sections, sectionOffset, pdata.ffsVersion); + + // Check section size + if (sectionSize < sizeof(EFI_COMMON_SECTION_HEADER) || sectionSize > (bodySize - sectionOffset)) { + // Add padding to fill the rest of sections + QByteArray padding = sections.mid(sectionOffset); + // Get info + QString info = tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()); + + // Constuct parsing data + pdata.fixed = TRUE; // Non-UEFI data is fixed + pdata.offset += headerSize + sectionOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, tr("Non-UEFI data"), "", info, QByteArray(), padding, convertParsingData(pdata), index); + + // Show message + msg(tr("parseSections: non-UEFI data found in sections area"), dataIndex); + + 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); + + // Move to next section + sectionOffset += sectionSize; + sectionOffset = ALIGN4(sectionOffset); + } + + //Parse bodies + for (int i = 0; i < model->rowCount(index); i++) { + QModelIndex current = index.child(i, 0); + switch (model->type(current)) { + case Types::Section: + parseSectionBody(current); + break; + case Types::Padding: + // No parsing required + break; + default: + return ERR_UNKNOWN_ITEM_TYPE; + } + } + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); + + switch (sectionHeader->Type) { + // Special + case EFI_SECTION_COMPRESSION: return parseCompressedSectionHeader(section, parentOffset, parent, index); + case EFI_SECTION_GUID_DEFINED: return parseGuidedSectionHeader(section, parentOffset, parent, index); + case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return parseFreeformGuidedSectionHeader(section, parentOffset, parent, index); + case EFI_SECTION_VERSION: return parseVersionSectionHeader(section, parentOffset, parent, index); + case SCT_SECTION_POSTCODE: + case INSYDE_SECTION_POSTCODE: return parsePostcodeSectionHeader(section, parentOffset, parent, index); + // Common + case EFI_SECTION_DISPOSABLE: + case EFI_SECTION_DXE_DEPEX: + case EFI_SECTION_PEI_DEPEX: + case EFI_SECTION_SMM_DEPEX: + case EFI_SECTION_TE: + case EFI_SECTION_PE32: + case EFI_SECTION_PIC: + case EFI_SECTION_COMPATIBILITY16: + case EFI_SECTION_USER_INTERFACE: + case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: + case EFI_SECTION_RAW: return parseCommonSectionHeader(section, parentOffset, parent, index); + // Unknown + default: + STATUS result = parseCommonSectionHeader(section, parentOffset, parent, index); + msg(tr("parseSectionHeader: section with unknown type %1h").hexarg2(sectionHeader->Type, 2), index); + return result; + } +} + +STATUS FfsParser::parseCommonSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Get data from parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Obtain header fields + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); + UINT32 headerSize = sizeof(EFI_COMMON_SECTION_HEADER); + if (pdata.ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) + headerSize = sizeof(EFI_COMMON_SECTION_HEADER2); + + QByteArray header = section.left(headerSize); + QByteArray body = section.mid(headerSize); + + // Get info + QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); + QString info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") + .hexarg2(sectionHeader->Type, 2) + .hexarg(section.size()).arg(section.size()) + .hexarg(headerSize).arg(headerSize) + .hexarg(body.size()).arg(body.size()); + + // Construct parsing data + pdata.offset += parentOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::Section, sectionHeader->Type, name, QString(), info, header, body, convertParsingData(pdata), parent); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseCompressedSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Get data from parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Obtain header fields + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); + const EFI_COMPRESSION_SECTION* compressedSectionHeader = (const EFI_COMPRESSION_SECTION*)sectionHeader; + UINT32 headerSize = sizeof(EFI_COMPRESSION_SECTION); + UINT8 compressionType = compressedSectionHeader->CompressionType; + UINT32 uncompressedLength = compressedSectionHeader->UncompressedLength; + if (pdata.ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) { + const EFI_COMPRESSION_SECTION2* compressedSectionHeader2 = (const EFI_COMPRESSION_SECTION2*)sectionHeader; + headerSize = sizeof(EFI_COMPRESSION_SECTION2); + compressionType = compressedSectionHeader2->CompressionType; + uncompressedLength = compressedSectionHeader->UncompressedLength; + } + + QByteArray header = section.left(headerSize); + QByteArray body = section.mid(headerSize); + + // Get info + QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); + QString info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nCompression type: %8h\nDecompressed size: %9h (%10)") + .hexarg2(sectionHeader->Type, 2) + .hexarg(section.size()).arg(section.size()) + .hexarg(headerSize).arg(headerSize) + .hexarg(body.size()).arg(body.size()) + .hexarg2(compressionType, 2) + .hexarg(uncompressedLength).arg(uncompressedLength); + + // Construct parsing data + pdata.offset += parentOffset; + pdata.section.compressed.compressionType = compressionType; + pdata.section.compressed.uncompressedSize = uncompressedLength; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::Section, sectionHeader->Type, name, QString(), info, header, body, convertParsingData(pdata), parent); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseGuidedSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Get data from parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Obtain header fields + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); + const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)sectionHeader; + EFI_GUID guid = guidDefinedSectionHeader->SectionDefinitionGuid; + UINT16 dataOffset = guidDefinedSectionHeader->DataOffset; + UINT16 attributes = guidDefinedSectionHeader->Attributes; + if (pdata.ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) { + const EFI_GUID_DEFINED_SECTION2* guidDefinedSectionHeader2 = (const EFI_GUID_DEFINED_SECTION2*)sectionHeader; + guid = guidDefinedSectionHeader2->SectionDefinitionGuid; + dataOffset = guidDefinedSectionHeader2->DataOffset; + attributes = guidDefinedSectionHeader2->Attributes; + } + + QByteArray header = section.left(dataOffset); + QByteArray body = section.mid(dataOffset); + + // Get info + QString name = guidToQString(guid); + QString info = tr("Section GUID: %1\nType: %2h\nFull size: %3h (%4)\nHeader size: %5h (%6)\nBody size: %7h (%8)\nData offset: %9h\nAttributes: %10h") + .arg(name) + .hexarg2(sectionHeader->Type, 2) + .hexarg(section.size()).arg(section.size()) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()) + .hexarg(dataOffset) + .hexarg2(attributes, 4); + + // Construct parsing data + pdata.offset += parentOffset; + pdata.section.guidDefined.attributes = attributes; + pdata.section.guidDefined.guid = guid; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::Section, sectionHeader->Type, name, QString(), info, header, body, convertParsingData(pdata), parent); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseFreeformGuidedSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Get data from parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Obtain header fields + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); + const EFI_FREEFORM_SUBTYPE_GUID_SECTION* fsgHeader = (const EFI_FREEFORM_SUBTYPE_GUID_SECTION*)sectionHeader; + UINT32 headerSize = sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION); + EFI_GUID guid = fsgHeader->SubTypeGuid; + if (pdata.ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) { + const EFI_FREEFORM_SUBTYPE_GUID_SECTION2* fsgHeader2 = (const EFI_FREEFORM_SUBTYPE_GUID_SECTION2*)sectionHeader; + headerSize = sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION2); + guid = fsgHeader2->SubTypeGuid; + } + + QByteArray header = section.left(headerSize); + QByteArray body = section.mid(headerSize); + + // Check for signed section + bool msgSigned = false; + bool msgUnknownSignature = false; + bool msgUnknownUefiGuidSignature = false; + QString signInfo; + if (QByteArray((const char*)&guid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { + msgSigned = true; + const WIN_CERTIFICATE* certificateHeader = (const WIN_CERTIFICATE*)model->body(index).constData(); + if (certificateHeader->CertificateType == WIN_CERT_TYPE_EFI_GUID) { + signInfo += 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) { + signInfo += tr("\nSignature subtype: RSA2048/SHA256"); + } + else if (QByteArray((const char*)&guidCertificateHeader->CertType, sizeof(EFI_GUID)) == EFI_CERT_TYPE_PKCS7_GUID) { + signInfo += tr("\nSignature subtype: PCKS7"); + } + else { + signInfo += tr("\nSignature subtype: unknown"); + msgUnknownUefiGuidSignature = true; + } + } + else if (certificateHeader->CertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { + signInfo += tr("\nSignature type: PCKS7"); + } + else { + signInfo += tr("\nSignature type: unknown"); + msgUnknownSignature = true; + } + + // Add additional to the header + header.append(body.left(certificateHeader->Length)); + // Get new body + body = body.mid(certificateHeader->Length); + } + + // Get info + QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); + QString info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nSubtype GUID: %8") + .hexarg2(fsgHeader->Type, 2) + .hexarg(section.size()).arg(section.size()) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()) + .arg(guidToQString(guid)); + if (!signInfo.isEmpty()) info.append(signInfo); + + // Construct parsing data + pdata.offset += parentOffset; + pdata.section.freeformSubtypeGuid.guid = guid; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::Section, sectionHeader->Type, name, QString(), info, header, body, convertParsingData(pdata), parent); + + // Show messages + if (msgSigned) + 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 (msgUnknownSignature) + msg(tr("parseSection: GUID defined section with unknown signature type"), index); + + // Rename section + model->setName(index, guidToQString(guid)); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseVersionSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Get data from parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Obtain header fields + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); + const EFI_VERSION_SECTION* versionHeader = (const EFI_VERSION_SECTION*)sectionHeader; + UINT32 headerSize = sizeof(EFI_VERSION_SECTION); + UINT16 buildNumber = versionHeader->BuildNumber; + if (pdata.ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) { + const EFI_VERSION_SECTION2* versionHeader2 = (const EFI_VERSION_SECTION2*)sectionHeader; + headerSize = sizeof(EFI_VERSION_SECTION2); + buildNumber = versionHeader2->BuildNumber; + } + + QByteArray header = section.left(headerSize); + QByteArray body = section.mid(headerSize); + + // Get info + QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); + QString info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nBuild number: %8") + .hexarg2(versionHeader->Type, 2) + .hexarg(section.size()).arg(section.size()) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()) + .arg(buildNumber); + + // Construct parsing data + pdata.offset += parentOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::Section, sectionHeader->Type, name, QString(), info, header, body, convertParsingData(pdata), parent); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parsePostcodeSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + // Get data from parent's parsing data + PARSING_DATA pdata = getParsingData(parent); + + // Obtain header fields + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); + const POSTCODE_SECTION* postcodeHeader = (const POSTCODE_SECTION*)sectionHeader; + UINT32 headerSize = sizeof(POSTCODE_SECTION); + UINT32 postCode = postcodeHeader->Postcode; + if (pdata.ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) { + const POSTCODE_SECTION2* postcodeHeader2 = (const POSTCODE_SECTION2*)sectionHeader; + headerSize = sizeof(POSTCODE_SECTION2); + postCode = postcodeHeader2->Postcode; + } + + QByteArray header = section.left(headerSize); + QByteArray body = section.mid(headerSize); + + // Get info + QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); + QString info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nPostcode: %8\n") + .hexarg2(postcodeHeader->Type, 2) + .hexarg(section.size()).arg(section.size()) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()) + .arg(postCode); + + // Construct parsing data + pdata.offset += parentOffset; + if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); + + // Add tree item + index = model->addItem(Types::Section, sectionHeader->Type, name, QString(), info, header, body, convertParsingData(pdata), parent); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseSectionBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(model->header(index).constData()); + + switch (sectionHeader->Type) { + // Encapsulation + case EFI_SECTION_COMPRESSION: return parseCompressedSectionBody(index); + case EFI_SECTION_GUID_DEFINED: return parseGuidedSectionBody(index); + case EFI_SECTION_DISPOSABLE: return parseSections(model->body(index), index); + // Leaf + case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return parseRawArea(model->body(index), index); + case EFI_SECTION_VERSION: return parseVersionSectionBody(index); + case EFI_SECTION_DXE_DEPEX: + case EFI_SECTION_PEI_DEPEX: + case EFI_SECTION_SMM_DEPEX: return parseDepexSectionBody(index); + case EFI_SECTION_TE: return ERR_SUCCESS;//return parseTeSectionBody(index); + case EFI_SECTION_PE32: + case EFI_SECTION_PIC: return ERR_SUCCESS;//return parsePeSectionBody(index); + case EFI_SECTION_USER_INTERFACE: return parseUiSectionBody(index); + case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: return parseRawArea(model->body(index), index); + case EFI_SECTION_RAW: return parseRawSectionBody(index); + // No parsing needed + case EFI_SECTION_COMPATIBILITY16: + case SCT_SECTION_POSTCODE: + case INSYDE_SECTION_POSTCODE: + default: + return ERR_SUCCESS; + } +} + +STATUS FfsParser::parseCompressedSectionBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Get data from parsing data + PARSING_DATA pdata = getParsingData(index); + UINT8 algorithm = pdata.section.compressed.compressionType; + + // Decompress section + QByteArray decompressed; + STATUS result = decompress(model->body(index), algorithm, decompressed); + if (result) { + msg(tr("parseCompressedSectionBody: decompression failed with error \"%1\"").arg(errorCodeToQString(result)), index); + return ERR_SUCCESS; + } + + // Check reported uncompressed size + if (pdata.section.compressed.uncompressedSize != decompressed.size()) { + msg(tr("parseCompressedSectionBody: decompressed size stored in header %1h (%2) differs from actual %3h (%4)") + .hexarg(pdata.section.compressed.uncompressedSize) + .arg(pdata.section.compressed.uncompressedSize) + .hexarg(decompressed.size()) + .arg(decompressed.size()), index); + model->addInfo(index, tr("\nActual decompressed size: %1h (%2)").hexarg(decompressed.size()).arg(decompressed.size())); + } + + // Add info + model->addInfo(index, tr("\nCompression algorithm: %1").arg(compressionTypeToQString(algorithm))); + + // Update parsing data + pdata.isOnFlash = (algorithm == COMPRESSION_ALGORITHM_NONE); // Data is not on flash unless not compressed + pdata.section.compressed.algorithm = algorithm; + model->setParsingData(index, convertParsingData(pdata)); + + // Parse decompressed data + return parseSections(decompressed, index); +} + +STATUS FfsParser::parseGuidedSectionBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Get data from parsing data + PARSING_DATA pdata = getParsingData(index); + UINT32 attributes = pdata.section.guidDefined.attributes; + EFI_GUID guid = pdata.section.guidDefined.guid; + + // Check if section requires processing + QByteArray processed = model->body(index); + QString info; + bool parseCurrentSection = true; + UINT8 algorithm = COMPRESSION_ALGORITHM_NONE; + if (attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) { + // Tiano compressed section + if (QByteArray((const char*)&guid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_TIANO) { + algorithm = EFI_STANDARD_COMPRESSION; + STATUS result = decompress(model->body(index), algorithm, processed); + 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()); + } + 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"); + } + // LZMA compressed section + else if (QByteArray((const char*)&guid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_LZMA) { + algorithm = EFI_CUSTOMIZED_COMPRESSION; + STATUS result = decompress(model->body(index), algorithm, processed); + if (result) { + parseCurrentSection = false; + msg(tr("parseGuidedSectionBody: decompression failed with error \"%1\"").arg(errorCodeToQString(result)), index); + return ERR_SUCCESS; + } + + if (algorithm == COMPRESSION_ALGORITHM_LZMA) { + info += tr("\nCompression algorithm: LZMA"); + info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); + } + else + info += tr("\nCompression algorithm: unknown"); + } + // Unknown GUIDed section + else { + parseCurrentSection = false; + msg(tr("parseGuidedSectionBody: GUID defined section with unknown processing method"), index); + } + } + + // Check if section requires checksum calculation + if (attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) + { + // CRC32 section + if (QByteArray((const char*)&guid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_CRC32) { + info += tr("\nChecksum type: CRC32"); + // Calculate CRC32 of section data + QByteArray body = model->body(index); + UINT32 crc = crc32(0, (const UINT8*)body.constData(), body.size()); + // Check stored CRC32 + if (crc == *(const UINT32*)(model->header(index).constData() + sizeof(EFI_GUID_DEFINED_SECTION))) { + info += tr("\nChecksum: valid"); + } + else { + info += tr("\nChecksum: invalid"); + msg(tr("parseGuidedSectionBody: GUID defined section with invalid CRC32"), index); + } + } + else + msg(tr("parseGuidedSectionBody: GUID defined section with unknown authentication method"), index); + } + + // Add info + model->addInfo(index, info); + + // Update parsing data + pdata.isOnFlash = (algorithm == COMPRESSION_ALGORITHM_NONE); // Data is not on flash unless not compressed + model->setParsingData(index, convertParsingData(pdata)); + + if (!parseCurrentSection) { + msg(tr("parseGuidedSectionBody: GUID defined section can not be processed"), index); + return ERR_SUCCESS; + } + + return parseSections(processed, index); +} + +STATUS FfsParser::parseVersionSectionBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Add info + model->addInfo(index, tr("\nVersion string: %1").arg(QString::fromUtf16((const ushort*)model->body(index).constData()))); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseDepexSectionBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + QByteArray body = model->body(index); + QString parsed; + + // Check data to be present + if (!body.size()) + return ERR_INVALID_PARAMETER; + + const EFI_GUID * guid; + const UINT8* current = (const UINT8*)body.constData(); + + // Special cases of first opcode + switch (*current) { + case EFI_DEP_BEFORE: + if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) { + msg(tr("parseDepexSectionBody: DEPEX section too long for a section starting with BEFORE opcode"), index); + return ERR_SUCCESS; + } + guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); + parsed += tr("\nBEFORE %1").arg(guidToQString(*guid)); + current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); + if (*current != EFI_DEP_END){ + msg(tr("parseDepexSectionBody: DEPEX section ends with non-END opcode"), index); + return ERR_SUCCESS; + } + return ERR_SUCCESS; + case EFI_DEP_AFTER: + if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)){ + msg(tr("parseDepexSectionBody: DEPEX section too long for a section starting with AFTER opcode"), index); + return ERR_SUCCESS; + } + guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); + parsed += tr("\nAFTER %1").arg(guidToQString(*guid)); + current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); + if (*current != EFI_DEP_END) { + msg(tr("parseDepexSectionBody: DEPEX section ends with non-END opcode"), index); + return ERR_SUCCESS; + } + return ERR_SUCCESS; + case EFI_DEP_SOR: + if (body.size() <= 2 * EFI_DEP_OPCODE_SIZE) { + msg(tr("parseDepexSectionBody: DEPEX section too short for a section starting with SOR opcode"), index); + return ERR_SUCCESS; + } + parsed += tr("\nSOR"); + current += EFI_DEP_OPCODE_SIZE; + break; + } + + // Parse the rest of depex + while (current - (const UINT8*)body.constData() < body.size()) { + switch (*current) { + case EFI_DEP_BEFORE: { + msg(tr("parseDepexSectionBody: misplaced BEFORE opcode"), index); + return ERR_SUCCESS; + } + case EFI_DEP_AFTER: { + msg(tr("parseDepexSectionBody: misplaced AFTER opcode"), index); + return ERR_SUCCESS; + } + case EFI_DEP_SOR: { + msg(tr("parseDepexSectionBody: misplaced SOR opcode"), index); + return ERR_SUCCESS; + } + case EFI_DEP_PUSH: + // Check that the rest of depex has correct size + if ((UINT32)body.size() - (UINT32)(current - (const UINT8*)body.constData()) <= EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) { + parsed.clear(); + msg(tr("parseDepexSectionBody: remains of DEPEX section too short for PUSH opcode"), index); + return ERR_SUCCESS; + } + guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); + parsed += tr("\nPUSH %1").arg(guidToQString(*guid)); + current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); + break; + case EFI_DEP_AND: + parsed += tr("\nAND"); + current += EFI_DEP_OPCODE_SIZE; + break; + case EFI_DEP_OR: + parsed += tr("\nOR"); + current += EFI_DEP_OPCODE_SIZE; + break; + case EFI_DEP_NOT: + parsed += tr("\nNOT"); + current += EFI_DEP_OPCODE_SIZE; + break; + case EFI_DEP_TRUE: + parsed += tr("\nTRUE"); + current += EFI_DEP_OPCODE_SIZE; + break; + case EFI_DEP_FALSE: + parsed += tr("\nFALSE"); + current += EFI_DEP_OPCODE_SIZE; + break; + case EFI_DEP_END: + parsed += tr("\nEND"); + current += EFI_DEP_OPCODE_SIZE; + // Check that END is the last opcode + if (current - (const UINT8*)body.constData() < body.size()) { + parsed.clear(); + msg(tr("parseDepexSectionBody: DEPEX section ends with non-END opcode"), index); + } + break; + default: + msg(tr("parseDepexSectionBody: unknown opcode"), index); + return ERR_SUCCESS; + break; + } + } + + // Add info + model->addInfo(index, tr("\nParsed expression:%1").arg(parsed)); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseUiSectionBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + QString text = QString::fromUtf16((const ushort*)model->body(index).constData()); + + // Add info + model->addInfo(index, tr("\nText: %1").arg(text)); + + // Rename parent file + model->setText(model->findParentOfType(index, Types::File), text); + + return ERR_SUCCESS; +} + +STATUS FfsParser::parseRawSectionBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Check for apriori file + QModelIndex parentFile = model->findParentOfType(index, Types::File); + QByteArray parentFileGuid = model->header(parentFile).left(sizeof(EFI_GUID)); + if (parentFileGuid == EFI_PEI_APRIORI_FILE_GUID) { // PEI apriori file + // Parse apriori file list + QString str; + STATUS result = parseAprioriRawSection(model->body(index), str); + if (!result && !str.isEmpty()) + model->addInfo(index, tr("\nFile list:%1").arg(str)); + + // Set parent file text + model->setText(parentFile, tr("PEI apriori file")); + + return ERR_SUCCESS; + } + else if (parentFileGuid == EFI_DXE_APRIORI_FILE_GUID) { // DXE apriori file + // Parse apriori file list + QString str; + STATUS result = parseAprioriRawSection(model->body(index), str); + if (!result && !str.isEmpty()) + model->addInfo(index, tr("\nFile list:%1").arg(str)); + + // Set parent file text + model->setText(parentFile, tr("DXE apriori file")); + + return ERR_SUCCESS; + } + + // Parse as raw area + return parseRawArea(model->body(index), index); +} + +/* +STATUS FfsEngine::parsePeSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); + UINT32 headerSize = sizeOfSectionHeader(sectionHeader); + QByteArray header = section.left(headerSize); + QByteArray body = section.mid(headerSize); + + // Get standard info + QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); + QString info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") + .hexarg2(sectionHeader->Type, 2) + .hexarg(section.size()).arg(section.size()) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()); + + // Get PE info + bool msgInvalidDosSignature = false; + bool msgInvalidPeSignature = false; + bool msgUnknownOptionalHeaderSignature = false; + + const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)body.constData(); + if (dosHeader->e_magic != EFI_IMAGE_DOS_SIGNATURE) { + info += tr("\nDOS signature: %1h, invalid").hexarg2(dosHeader->e_magic, 4); + msgInvalidDosSignature = true; + } + else { + const EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(body.constData() + dosHeader->e_lfanew); + if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) { + info += tr("\nPE signature: %1h, invalid").hexarg2(peHeader->Signature, 8); + msgInvalidPeSignature = true; + } + else { + const EFI_IMAGE_FILE_HEADER* imageFileHeader = (const EFI_IMAGE_FILE_HEADER*)(peHeader + 1); + info += tr("\nDOS signature: %1h\nPE signature: %2h\nMachine type: %3\nNumber of sections: %4\nCharacteristics: %5h") + .hexarg2(dosHeader->e_magic, 4) + .hexarg2(peHeader->Signature, 8) + .arg(machineTypeToQString(imageFileHeader->Machine)) + .arg(imageFileHeader->NumberOfSections) + .hexarg2(imageFileHeader->Characteristics, 4); + + EFI_IMAGE_OPTIONAL_HEADER_POINTERS_UNION optionalHeader; + optionalHeader.H32 = (const EFI_IMAGE_OPTIONAL_HEADER32*)(imageFileHeader + 1); + if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { + info += tr("\nOptional header signature: %1h\nSubsystem: %2h\nRelativeEntryPoint: %3h\nBaseOfCode: %4h\nImageBase: %5h\nEntryPoint: %6h") + .hexarg2(optionalHeader.H32->Magic, 4) + .hexarg2(optionalHeader.H32->Subsystem, 4) + .hexarg(optionalHeader.H32->AddressOfEntryPoint) + .hexarg(optionalHeader.H32->BaseOfCode) + .hexarg(optionalHeader.H32->ImageBase) + .hexarg(optionalHeader.H32->ImageBase + optionalHeader.H32->AddressOfEntryPoint); + } + else if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { + info += tr("\nOptional header signature: %1h\nSubsystem: %2h\nRelativeEntryPoint: %3h\nBaseOfCode: %4h\nImageBase: %5h\nEntryPoint: %6h") + .hexarg2(optionalHeader.H64->Magic, 4) + .hexarg2(optionalHeader.H64->Subsystem, 4) + .hexarg(optionalHeader.H64->AddressOfEntryPoint) + .hexarg(optionalHeader.H64->BaseOfCode) + .hexarg(optionalHeader.H64->ImageBase) + .hexarg(optionalHeader.H64->ImageBase + optionalHeader.H64->AddressOfEntryPoint); + } + else { + info += tr("\nOptional header signature: %1h, unknown").hexarg2(optionalHeader.H64->Magic, 4); + msgUnknownOptionalHeaderSignature = true; + } + } + } + + // Add tree item + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + + // Show messages + if (msgInvalidDosSignature) { + msg("parseSection: PE32 image with invalid DOS signature", index); + } + if (msgInvalidPeSignature) { + msg("parseSection: PE32 image with invalid PE signature", index); + } + if (msgUnknownOptionalHeaderSignature) { + msg("parseSection: PE32 image with unknown optional header signature", index); + } + + // Special case of PEI Core + QModelIndex core = model->findParentOfType(index, Types::File); + if (core.isValid() && model->subtype(core) == EFI_FV_FILETYPE_PEI_CORE + && oldPeiCoreEntryPoint == 0) { + result = getEntryPoint(model->body(index), oldPeiCoreEntryPoint); + if (result) + msg(tr("parseSection: can't get original PEI core entry point"), index); + } + return ERR_SUCCESS; +} +*/ + +/* +STATUS FfsEngine::parseTeSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); + UINT32 headerSize = sizeOfSectionHeader(sectionHeader); + QByteArray header = section.left(headerSize); + QByteArray body = section.mid(headerSize); + + // Get standard info + QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); + QString info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") + .hexarg2(sectionHeader->Type, 2) + .hexarg(section.size()).arg(section.size()) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()); + + // Get TE info + bool msgInvalidSignature = false; + const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)body.constData(); + UINT32 teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); + 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) + .arg(machineTypeToQString(teHeader->Machine)) + .arg(teHeader->NumberOfSections) + .hexarg2(teHeader->Subsystem, 2) + .hexarg(teHeader->StrippedSize).arg(teHeader->StrippedSize) + .hexarg(teHeader->BaseOfCode) + .hexarg(teHeader->AddressOfEntryPoint) + .hexarg(teHeader->ImageBase) + .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); + + // Show messages + if (msgInvalidSignature) { + msg("parseSection: TE image with invalid TE signature", index); + } + + // Special case of PEI Core + QModelIndex core = model->findParentOfType(index, Types::File); + if (core.isValid() && model->subtype(core) == EFI_FV_FILETYPE_PEI_CORE + && oldPeiCoreEntryPoint == 0) { + result = getEntryPoint(model->body(index), oldPeiCoreEntryPoint); + if (result) + msg(tr("parseSection: can't get original PEI core entry point"), index); + } + return ERR_SUCCESS; +} + +STATUS FfsEngine::parseFirmwareVolumeImageSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) +{ + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); + QByteArray header = section.left(sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); + QByteArray body = section.mid(sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); + + // Get info + QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); + QString info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") + .hexarg2(sectionHeader->Type, 2) + .hexarg(section.size()).arg(section.size()) + .hexarg(header.size()).arg(header.size()) + .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); + + // Parse section body as BIOS space + result = parseBios(body, index); + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) { + msg(tr("parseSection: parsing firmware volume image section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); + return result; + } + return ERR_SUCCESS; +} +*/ + +STATUS FfsParser::extract(const QModelIndex & index, QString & name, QByteArray & extracted, const UINT8 mode) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Get data from parsing data + PARSING_DATA pdata = getParsingData(index); + + // Construct a name for extracted data + QString itemName = model->name(index); + QString itemText = model->text(index); + switch (model->type(index)) { + case Types::Volume: { + if (pdata.volume.hasExtendedHeader) + name = guidToQString(pdata.volume.extendedHeaderGuid); + else + name = itemName; + } break; + case Types::File: { + name = itemText.isEmpty() ? itemName : itemText.replace(' ', '_'); + } break; + case Types::Section: { + // Get parent file name + QModelIndex fileIndex = model->findParentOfType(index, Types::File); + QString fileText = model->text(fileIndex); + name = fileText.isEmpty() ? model->name(fileIndex) : fileText.replace(' ', '_'); + // Append section subtype name + name += QChar('_') + itemName.replace(' ', '_'); + } break; + + case Types::Capsule: + case Types::Image: + case Types::Region: + case Types::Padding: + default: + name = itemName.replace(' ', '_').replace('/', '_'); + } + + // Get extracted data + if (mode == EXTRACT_MODE_AS_IS) { + // Extract as is, with header body and tail + extracted.clear(); + extracted.append(model->header(index)); + extracted.append(model->body(index)); + // Handle file tail + if (model->type(index) == Types::File) { + if (pdata.file.hasTail) + extracted.append(pdata.file.tail); + } + } + else if (mode == EXTRACT_MODE_BODY) { + name += tr("_body"); + // Extract without header and tail + extracted.clear(); + extracted.append(model->body(index)); + } + else + return ERR_UNKNOWN_EXTRACT_MODE; + + return ERR_SUCCESS; +} diff --git a/ffsparser.h b/ffsparser.h new file mode 100644 index 0000000..e1503a4 --- /dev/null +++ b/ffsparser.h @@ -0,0 +1,188 @@ +/* ffsparser.h + +Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +*/ + +#ifndef __FFSPARSER_H__ +#define __FFSPARSER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basetypes.h" +#include "treemodel.h" +#include "utility.h" +#include "peimage.h" + +#ifndef _CONSOLE +#include "messagelistitem.h" +#endif + +class TreeModel; + +//The whole parsing data is an information needed for each level of image reconstruction +//routines without the need of backward traversal + +//typedef struct _CAPSULE_PARSING_DATA { +//} CAPSULE_PARSING_DATA; + +//typedef struct _IMAGE_PARSING_DATA { +//} IMAGE_PARSING_DATA; + +//typedef struct _PADDING_PARSING_DATA { +//} PADDING_PARSING_DATA; + +typedef struct _VOLUME_PARSING_DATA { + EFI_GUID extendedHeaderGuid; + UINT32 alignment; + UINT8 revision; + BOOLEAN hasExtendedHeader; + BOOLEAN hasZeroVectorCRC32; + BOOLEAN isWeakAligned; +} VOLUME_PARSING_DATA; + +//typedef struct _FREE_SPACE_PARSING_DATA { +//} FREE_SPACE_PARSING_DATA; + +typedef struct _FILE_PARSING_DATA { + UINT16 tail; + BOOLEAN hasTail; +} FILE_PARSING_DATA; + +typedef struct _COMPRESSED_SECTION_PARSING_DATA { + UINT32 uncompressedSize; + UINT8 compressionType; + UINT8 algorithm; +} COMPRESSED_SECTION_PARSING_DATA; + +typedef struct _GUIDED_SECTION_PARSING_DATA { + EFI_GUID guid; + UINT32 attributes; +} GUIDED_SECTION_PARSING_DATA; + +typedef struct _FREEFORM_GUIDED_SECTION_PARSING_DATA { + EFI_GUID guid; +} FREEFORM_GUIDED_SECTION_PARSING_DATA; + +typedef struct _SECTION_PARSING_DATA { + union { + COMPRESSED_SECTION_PARSING_DATA compressed; + GUIDED_SECTION_PARSING_DATA guidDefined; + FREEFORM_GUIDED_SECTION_PARSING_DATA freeformSubtypeGuid; + }; +} SECTION_PARSING_DATA; + +typedef struct _PARSING_DATA { + BOOLEAN fixed; + BOOLEAN isOnFlash; + UINT8 emptyByte; + UINT8 ffsVersion; + UINT32 offset; + UINT64 address; + union { + //CAPSULE_PARSING_DATA capsule; + //IMAGE_PARSING_DATA image; + //PADDING_PARSING_DATA padding; + VOLUME_PARSING_DATA volume; + //FREE_SPACE_PARSING_DATA freeSpace; + FILE_PARSING_DATA file; + SECTION_PARSING_DATA section; + }; +} PARSING_DATA; + +class FfsParser : public QObject +{ + Q_OBJECT + +public: + // Default constructor and destructor + FfsParser(QObject *parent = 0); + ~FfsParser(void); + + // Returns model for Qt view classes + TreeModel* treeModel() const; + +#ifndef _CONSOLE + // Returns message items queue + QQueue messages() const; + // Clears message items queue + void clearMessages(); +#endif + + // Firmware image parsing + STATUS parseImageFile(const QByteArray & imageFile); + STATUS parseRawArea(const QByteArray & bios, const QModelIndex & index); + STATUS parseVolumeHeader(const QByteArray & volume, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); + STATUS parseVolumeBody(const QModelIndex & index); + STATUS parseFileHeader(const QByteArray & file, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); + STATUS parseFileBody(const QModelIndex & index); + STATUS parseSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); + STATUS parseSectionBody(const QModelIndex & index); + + // Search routines TODO: move to another class + STATUS findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode); + STATUS findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode); + STATUS findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive); + + STATUS extract(const QModelIndex & index, QString & name, QByteArray & extracted, const UINT8 mode); + +private: + TreeModel *model; + + STATUS parseIntelImage(const QByteArray & intelImage, const QModelIndex & parent, QModelIndex & index); + STATUS parseGbeRegion(const QByteArray & gbe, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); + 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 parsePadFileBody(const QModelIndex & index); + STATUS parseSections(QByteArray sections, const QModelIndex & index); + + 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); + STATUS parseGuidedSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); + STATUS parseFreeformGuidedSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); + STATUS parseVersionSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); + STATUS parsePostcodeSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); + + STATUS parseCompressedSectionBody(const QModelIndex & index); + STATUS parseGuidedSectionBody(const QModelIndex & index); + STATUS parseVersionSectionBody(const QModelIndex & index); + STATUS parseDepexSectionBody(const QModelIndex & index); + STATUS parseUiSectionBody(const QModelIndex & index); + STATUS parseRawSectionBody(const QModelIndex & index); + + UINT8 getPaddingType(const QByteArray & padding); + STATUS parseAprioriRawSection(const QByteArray & body, QString & parsed); + STATUS findNextVolume(const QByteArray & bios, 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); + + // Internal operations + BOOLEAN hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2); + PARSING_DATA getParsingData(const QModelIndex & index = QModelIndex()); + QByteArray convertParsingData(const PARSING_DATA & pdata); + +#ifndef _CONSOLE + QQueue messageItems; +#endif + // Message helper + void msg(const QString & message, const QModelIndex &index = QModelIndex()); + +}; + +#endif diff --git a/peimage.cpp b/peimage.cpp index 3f9085e..6845e6e 100644 --- a/peimage.cpp +++ b/peimage.cpp @@ -25,6 +25,6 @@ QString machineTypeToQString(UINT16 machineType) case IMAGE_FILE_MACHINE_I386: return QObject::tr("x86"); case IMAGE_FILE_MACHINE_IA64: return QObject::tr("IA64"); case IMAGE_FILE_MACHINE_THUMB: return QObject::tr("Thumb"); - default: return QObject::tr("Unknown %1").hexarg2(machineType, 4); + default: return QObject::tr("Unknown %1h").hexarg2(machineType, 4); } } \ No newline at end of file diff --git a/treeitem.cpp b/treeitem.cpp index 94514dd..e7b513c 100644 --- a/treeitem.cpp +++ b/treeitem.cpp @@ -15,14 +15,13 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "treeitem.h" #include "types.h" -TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, +TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const QString & name, const QString & text, const QString & info, const QByteArray & header, const QByteArray & body, const QByteArray & parsingData, TreeItem *parent) : itemAction(Actions::NoAction), itemType(type), itemSubtype(subtype), - itemCompression(compression), itemName(name), itemText(text), itemInfo(info), @@ -168,12 +167,6 @@ void TreeItem::setSubtype(const UINT8 subtype) itemSubtype = subtype; } - -UINT8 TreeItem::compression() const -{ - return itemCompression; -} - QByteArray TreeItem::header() const { return itemHeader; diff --git a/treeitem.h b/treeitem.h index 8697a75..8ea2629 100644 --- a/treeitem.h +++ b/treeitem.h @@ -24,8 +24,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 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(), + TreeItem(const UINT8 type, const UINT8 subtype = 0, const QString &name = QString(), const QString &text = QString(), const QString &info = QString(), const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QByteArray & parsingData = QByteArray(), TreeItem *parent = 0); ~TreeItem(); @@ -74,14 +73,11 @@ public: UINT8 action() const; void setAction(const UINT8 action); - UINT8 compression() const; - private: QList childItems; UINT8 itemAction; UINT8 itemType; UINT8 itemSubtype; - UINT8 itemCompression; QString itemName; QString itemText; QString itemInfo; diff --git a/treemodel.cpp b/treemodel.cpp index ef83561..a414847 100644 --- a/treemodel.cpp +++ b/treemodel.cpp @@ -226,14 +226,6 @@ UINT8 TreeModel::action(const QModelIndex &index) const return item->action(); } -UINT8 TreeModel::compression(const QModelIndex &index) const -{ - if (!index.isValid()) - return COMPRESSION_ALGORITHM_UNKNOWN; - TreeItem *item = static_cast(index.internalPointer()); - return item->compression(); -} - void TreeModel::setSubtype(const QModelIndex & index, const UINT8 subtype) { if (!index.isValid()) @@ -274,6 +266,26 @@ void TreeModel::setText(const QModelIndex &index, const QString &data) emit dataChanged(index, index); } +void TreeModel::setInfo(const QModelIndex &index, const QString &data) +{ + if (!index.isValid()) + return; + + TreeItem *item = static_cast(index.internalPointer()); + item->setInfo(data); + emit dataChanged(index, index); +} + +void TreeModel::addInfo(const QModelIndex &index, const QString &data) +{ + if (!index.isValid()) + return; + + TreeItem *item = static_cast(index.internalPointer()); + item->addInfo(data); + emit dataChanged(index, index); +} + void TreeModel::setAction(const QModelIndex &index, const UINT8 action) { if (!index.isValid()) @@ -294,7 +306,7 @@ void TreeModel::setParsingData(const QModelIndex &index, const QByteArray &data) emit dataChanged(this->index(0, 0), index); } -QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, +QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const QString & name, const QString & text, const QString & info, const QByteArray & header, const QByteArray & body, const QByteArray & parsingData, const QModelIndex & parent, const UINT8 mode) @@ -318,7 +330,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, name, text, info, header, body, parsingData, parentItem); if (mode == CREATE_MODE_APPEND) { emit layoutAboutToBeChanged(); parentItem->appendChild(newItem); diff --git a/treemodel.h b/treemodel.h index 3c8c210..3039a4e 100644 --- a/treemodel.h +++ b/treemodel.h @@ -47,8 +47,10 @@ public: void setSubtype(const QModelIndex &index, const UINT8 subtype); void setName(const QModelIndex &index, const QString &name); void setText(const QModelIndex &index, const QString &text); + void setInfo(const QModelIndex &index, const QString &info); + void addInfo(const QModelIndex &index, const QString &info); void setParsingData(const QModelIndex &index, const QByteArray &data); - + QString name(const QModelIndex &index) const; QString text(const QModelIndex &index) const; QString info(const QModelIndex &index) const; @@ -61,9 +63,8 @@ public: QByteArray parsingData(const QModelIndex &index) const; bool hasEmptyParsingData(const QModelIndex &index) const; UINT8 action(const QModelIndex &index) const; - UINT8 compression(const QModelIndex &index) const; - QModelIndex addItem(const UINT8 type, const UINT8 subtype = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, + QModelIndex addItem(const UINT8 type, const UINT8 subtype = 0, 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 QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); diff --git a/types.h b/types.h index a65b73e..ec605b4 100644 --- a/types.h +++ b/types.h @@ -85,25 +85,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..20621c3 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,14 +17,14 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.20.4")) +version(tr("0.30.0_alpha_parser_only")) { clipboard = QApplication::clipboard(); // Create UI ui->setupUi(this); searchDialog = new SearchDialog(this); - ffsEngine = NULL; + ffsParser = NULL; // Set window title this->setWindowTitle(tr("UEFITool %1").arg(version)); @@ -79,7 +79,7 @@ version(tr("0.20.4")) UEFITool::~UEFITool() { delete ui; - delete ffsEngine; + delete ffsParser; delete searchDialog; } @@ -104,10 +104,10 @@ void UEFITool::init() ui->actionMessagesCopyAll->setDisabled(true); // Make new ffsEngine - if (ffsEngine) - delete ffsEngine; - ffsEngine = new FfsEngine(this); - ui->structureTreeView->setModel(ffsEngine->treeModel()); + if (ffsParser) + delete ffsParser; + ffsParser = new FfsParser(this); + ui->structureTreeView->setModel(ffsParser->treeModel()); // Connect connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), @@ -121,9 +121,9 @@ void UEFITool::populateUi(const QModelIndex ¤t) if (!current.isValid()) return; - TreeModel* model = ffsEngine->treeModel(); + TreeModel* model = ffsParser->treeModel(); UINT8 type = model->type(current); - UINT8 subtype = model->subtype(current); + //UINT8 subtype = model->subtype(current); // Set info text ui->infoEdit->setPlainText(model->info(current)); @@ -139,16 +139,16 @@ void UEFITool::populateUi(const QModelIndex ¤t) // Enable actions ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current)); - ui->actionRebuild->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); + //ui->actionRebuild->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->actionExtractBody->setDisabled(model->hasEmptyBody(current)); - ui->actionRemove->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); - ui->actionInsertInto->setEnabled((type == Types::Volume && subtype != Subtypes::UnknownVolume) || - (type == Types::File && subtype != EFI_FV_FILETYPE_ALL && subtype != EFI_FV_FILETYPE_RAW && subtype != EFI_FV_FILETYPE_PAD) || - (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->actionReplaceBody->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); + //ui->actionRemove->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); + //ui->actionInsertInto->setEnabled((type == Types::Volume && subtype != Subtypes::UnknownVolume) || + // (type == Types::File && subtype != EFI_FV_FILETYPE_ALL && subtype != EFI_FV_FILETYPE_RAW && subtype != EFI_FV_FILETYPE_PAD) || + // (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->actionReplaceBody->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->actionMessagesCopy->setEnabled(false); } @@ -157,7 +157,7 @@ void UEFITool::search() if (searchDialog->exec() != QDialog::Accepted) return; - QModelIndex rootIndex = ffsEngine->treeModel()->index(0, 0); + QModelIndex rootIndex = ffsParser->treeModel()->index(0, 0); int index = searchDialog->ui->tabWidget->currentIndex(); if (index == 0) { // Hex pattern @@ -172,7 +172,7 @@ void UEFITool::search() mode = SEARCH_MODE_BODY; else mode = SEARCH_MODE_ALL; - ffsEngine->findHexPattern(rootIndex, pattern, mode); + ffsParser->findHexPattern(rootIndex, pattern, mode); showMessages(); } else if (index == 1) { // GUID @@ -188,7 +188,7 @@ void UEFITool::search() mode = SEARCH_MODE_BODY; else mode = SEARCH_MODE_ALL; - ffsEngine->findGuidPattern(rootIndex, pattern, mode); + ffsParser->findGuidPattern(rootIndex, pattern, mode); showMessages(); } else if (index == 2) { // Text string @@ -196,7 +196,7 @@ void UEFITool::search() QString pattern = searchDialog->ui->textEdit->text(); if (pattern.isEmpty()) return; - ffsEngine->findTextPattern(rootIndex, pattern, searchDialog->ui->textUnicodeCheckBox->isChecked(), + ffsParser->findTextPattern(rootIndex, pattern, searchDialog->ui->textUnicodeCheckBox->isChecked(), (Qt::CaseSensitivity) searchDialog->ui->textCaseSensitiveCheckBox->isChecked()); showMessages(); } @@ -208,10 +208,10 @@ void UEFITool::rebuild() if (!index.isValid()) return; - UINT8 result = ffsEngine->rebuild(index); + /*UINT8 result = ffsEngine->rebuild(index); if (result == ERR_SUCCESS) - ui->actionSaveImageFile->setEnabled(true); + ui->actionSaveImageFile->setEnabled(true);*/ } void UEFITool::remove() @@ -220,10 +220,10 @@ void UEFITool::remove() if (!index.isValid()) return; - UINT8 result = ffsEngine->remove(index); + /*UINT8 result = ffsEngine->remove(index); if (result == ERR_SUCCESS) - ui->actionSaveImageFile->setEnabled(true); + ui->actionSaveImageFile->setEnabled(true);*/ } void UEFITool::insert(const UINT8 mode) @@ -232,7 +232,7 @@ void UEFITool::insert(const UINT8 mode) if (!index.isValid()) return; - TreeModel* model = ffsEngine->treeModel(); + TreeModel* model = ffsParser->treeModel(); UINT8 type; if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) @@ -273,12 +273,12 @@ void UEFITool::insert(const UINT8 mode) QByteArray buffer = inputFile.readAll(); inputFile.close(); - UINT8 result = ffsEngine->insert(index, buffer, mode); + /*UINT8 result = ffsEngine->insert(index, buffer, mode); if (result) { QMessageBox::critical(this, tr("Insertion failed"), errorMessage(result), QMessageBox::Ok); return; } - ui->actionSaveImageFile->setEnabled(true); + ui->actionSaveImageFile->setEnabled(true);*/ } void UEFITool::insertInto() @@ -312,7 +312,7 @@ void UEFITool::replace(const UINT8 mode) if (!index.isValid()) return; - TreeModel* model = ffsEngine->treeModel(); + TreeModel* model = ffsParser->treeModel(); QString path; if (model->type(index) == Types::Region) { if (mode == REPLACE_MODE_AS_IS) { @@ -387,12 +387,12 @@ void UEFITool::replace(const UINT8 mode) QByteArray buffer = inputFile.readAll(); inputFile.close(); - UINT8 result = ffsEngine->replace(index, buffer, mode); + /*UINT8 result = ffsEngine->replace(index, buffer, mode); if (result) { QMessageBox::critical(this, tr("Replacing failed"), errorMessage(result), QMessageBox::Ok); return; } - ui->actionSaveImageFile->setEnabled(true); + ui->actionSaveImageFile->setEnabled(true);*/ } void UEFITool::extractAsIs() @@ -411,80 +411,83 @@ void UEFITool::extract(const UINT8 mode) if (!index.isValid()) return; - TreeModel* model = ffsEngine->treeModel(); - UINT8 type = model->type(index); + TreeModel* model = ffsParser->treeModel(); + + QByteArray extracted; + QString name; + UINT8 result = ffsParser->extract(index, name, extracted, mode); + if (result) { + QMessageBox::critical(this, tr("Extraction failed"), errorCodeToQString(result), QMessageBox::Ok); + return; + } + + name = currentDir + QDir::separator() + name; + UINT8 type = model->type(index); QString path; if (mode == EXTRACT_MODE_AS_IS) { switch (type) { case Types::Capsule: - path = QFileDialog::getSaveFileName(this, tr("Save capsule to file"), currentDir, "Capsule files (*.cap *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save capsule to file"), name + ".cap", "Capsule files (*.cap *.bin);;All files (*)"); break; case Types::Image: - path = QFileDialog::getSaveFileName(this, tr("Save image to file"), currentDir, "Image files (*.rom *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save image to file"), name + ".rom", "Image files (*.rom *.bin);;All files (*)"); break; case Types::Region: - path = QFileDialog::getSaveFileName(this, tr("Save region to file"), currentDir, "Region files (*.rgn *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save region to file"), name + ".rgn", "Region files (*.rgn *.bin);;All files (*)"); break; case Types::Padding: - path = QFileDialog::getSaveFileName(this, tr("Save padding to file"), currentDir, "Padding files (*.pad *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save padding to file"), name + ".pad", "Padding files (*.pad *.bin);;All files (*)"); break; case Types::Volume: - path = QFileDialog::getSaveFileName(this, tr("Save volume to file"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save volume to file"), name + ".vol", "Volume files (*.vol *.bin);;All files (*)"); break; case Types::File: - path = QFileDialog::getSaveFileName(this, tr("Save FFS file to file"), currentDir, "FFS files (*.ffs *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save FFS file to file"), name + ".ffs", "FFS files (*.ffs *.bin);;All files (*)"); break; case Types::Section: - path = QFileDialog::getSaveFileName(this, tr("Save section file to file"), currentDir, "Section files (*.sct *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save section file to file"), name + ".sct", "Section files (*.sct *.bin);;All files (*)"); break; default: - path = QFileDialog::getSaveFileName(this, tr("Save object to file"), currentDir, "Binary files (*.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); } } else if (mode == EXTRACT_MODE_BODY) { switch (type) { case Types::Capsule: - path = QFileDialog::getSaveFileName(this, tr("Save capsule body to image file"), currentDir, "Image files (*.rom *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save capsule body to image file"), name + ".rom", "Image files (*.rom *.bin);;All files (*)"); break; case Types::Volume: - path = QFileDialog::getSaveFileName(this, tr("Save volume body to file"), currentDir, "Volume body files (*.vbd *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save volume body to file"), name + ".vbd", "Volume body files (*.vbd *.bin);;All files (*)"); break; case Types::File: { if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) - path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to raw file"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to raw file"), name + ".raw", "Raw files (*.raw *.bin);;All files (*)"); else - path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to file"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to file"), name + ".fbd", "FFS file body files (*.fbd *.bin);;All files (*)"); } break; case Types::Section: { if (model->subtype(index) == EFI_SECTION_COMPRESSION || model->subtype(index) == EFI_SECTION_GUID_DEFINED || model->subtype(index) == EFI_SECTION_DISPOSABLE) - path = QFileDialog::getSaveFileName(this, tr("Save encapsulation section body to FFS body file"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save encapsulation section body to FFS body file"), name + ".fbd", "FFS file body files (*.fbd *.bin);;All files (*)"); else if (model->subtype(index) == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) - path = QFileDialog::getSaveFileName(this, tr("Save section body to volume file"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save section body to volume file"), name + ".vol", "Volume files (*.vol *.bin);;All files (*)"); else if (model->subtype(index) == EFI_SECTION_RAW) - path = QFileDialog::getSaveFileName(this, tr("Save section body to raw file"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save section body to raw file"), name + ".raw", "Raw files (*.raw *.bin);;All files (*)"); else - path = QFileDialog::getSaveFileName(this, tr("Save section body to file"), currentDir, "Binary files (*.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save section body to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); } break; default: - path = QFileDialog::getSaveFileName(this, tr("Save object to file"), currentDir, "Binary files (*.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); } } else - path = QFileDialog::getSaveFileName(this, tr("Save object to file"), currentDir, "Binary files (*.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); if (path.trimmed().isEmpty()) return; - QByteArray extracted; - UINT8 result = ffsEngine->extract(index, extracted, mode); - if (result) { - QMessageBox::critical(this, tr("Extraction failed"), errorMessage(result), QMessageBox::Ok); - return; - } - QFile outputFile; outputFile.setFileName(path); if (!outputFile.open(QFile::WriteOnly)) { @@ -521,7 +524,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 *.efi *.dec);;All files (*)"); if (path.isEmpty()) return; @@ -547,7 +550,7 @@ void UEFITool::saveImageFile() outputFile.close(); if (QMessageBox::information(this, tr("Image reconstruction successful"), tr("Open reconstructed file?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) - openImageFile(path); + openImageFile(path);*/ } void UEFITool::openImageFile() @@ -582,10 +585,10 @@ void UEFITool::openImageFile(QString path) init(); this->setWindowTitle(tr("UEFITool %1 - %2").arg(version).arg(fileInfo.fileName())); - UINT8 result = ffsEngine->parseImageFile(buffer); + UINT8 result = ffsParser->parseImageFile(buffer); showMessages(); if (result) - QMessageBox::critical(this, tr("Image parsing failed"), errorMessage(result), QMessageBox::Ok); + QMessageBox::critical(this, tr("Image parsing failed"), errorCodeToQString(result), QMessageBox::Ok); else ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName())); @@ -621,7 +624,7 @@ void UEFITool::enableMessagesCopyActions(QListWidgetItem* item) void UEFITool::clearMessages() { - ffsEngine->clearMessages(); + ffsParser->clearMessages(); messageItems.clear(); ui->messageListWidget->clear(); ui->actionMessagesCopy->setEnabled(false); @@ -643,10 +646,10 @@ void UEFITool::dropEvent(QDropEvent* event) void UEFITool::showMessages() { ui->messageListWidget->clear(); - if (!ffsEngine) + if (!ffsParser) return; - messageItems = ffsEngine->messages(); + messageItems = ffsParser->messages(); for (int i = 0; i < messageItems.count(); i++) { ui->messageListWidget->addItem(new MessageListItem(messageItems.at(i))); } @@ -680,7 +683,7 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event) if (!index.isValid()) return; - TreeModel* model = ffsEngine->treeModel(); + TreeModel* model = ffsParser->treeModel(); switch (model->type(index)) { case Types::Capsule: diff --git a/uefitool.h b/uefitool.h index dda7fdc..35ea3cb 100644 --- a/uefitool.h +++ b/uefitool.h @@ -34,8 +34,9 @@ #include #include "basetypes.h" +#include "utility.h" #include "ffs.h" -#include "ffsengine.h" +#include "ffsparser.h" #include "searchdialog.h" namespace Ui { @@ -91,7 +92,7 @@ public: private: Ui::UEFITool* ui; - FfsEngine* ffsEngine; + FfsParser* ffsParser; SearchDialog* searchDialog; QClipboard* clipboard; QString currentDir; diff --git a/uefitool.pro b/uefitool.pro index 59f6c21..081e069 100644 --- a/uefitool.pro +++ b/uefitool.pro @@ -11,7 +11,8 @@ SOURCES += uefitool_main.cpp \ descriptor.cpp \ ffs.cpp \ peimage.cpp \ - ffsengine.cpp \ + utility.cpp \ + ffsparser.cpp \ treeitem.cpp \ treemodel.cpp \ messagelistitem.cpp \ @@ -34,7 +35,8 @@ HEADERS += uefitool.h \ ffs.h \ peimage.h \ types.h \ - ffsengine.h \ + utility.h \ + ffsparser.h \ treeitem.h \ treemodel.h \ messagelistitem.h \ diff --git a/utility.cpp b/utility.cpp new file mode 100644 index 0000000..57f9ed5 --- /dev/null +++ b/utility.cpp @@ -0,0 +1,234 @@ +/* utility.cpp + +Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ +#include +#include "utility.h" +#include "ffs.h" +#include "Tiano/EfiTianoCompress.h" +#include "Tiano/EfiTianoDecompress.h" +#include "LZMA/LzmaCompress.h" +#include "LZMA/LzmaDecompress.h" + +// Returns text representation of error code +QString errorCodeToQString(UINT8 errorCode) +{ + switch (errorCode) { + case ERR_SUCCESS: return QObject::tr("Success"); + case ERR_NOT_IMPLEMENTED: return QObject::tr("Not implemented"); + case ERR_INVALID_PARAMETER: return QObject::tr("Function called with invalid parameter"); + case ERR_BUFFER_TOO_SMALL: return QObject::tr("Buffer too small"); + case ERR_OUT_OF_RESOURCES: return QObject::tr("Out of resources"); + case ERR_OUT_OF_MEMORY: return QObject::tr("Out of memory"); + case ERR_FILE_OPEN: return QObject::tr("File can't be opened"); + case ERR_FILE_READ: return QObject::tr("File can't be read"); + case ERR_FILE_WRITE: return QObject::tr("File can't be written"); + case ERR_ITEM_NOT_FOUND: return QObject::tr("Item not found"); + case ERR_UNKNOWN_ITEM_TYPE: return QObject::tr("Unknown item type"); + case ERR_INVALID_FLASH_DESCRIPTOR: return QObject::tr("Invalid flash descriptor"); + case ERR_INVALID_REGION: return QObject::tr("Invalid region"); + case ERR_EMPTY_REGION: return QObject::tr("Empty region"); + case ERR_BIOS_REGION_NOT_FOUND: return QObject::tr("BIOS region not found"); + case ERR_VOLUMES_NOT_FOUND: return QObject::tr("UEFI volumes not found"); + case ERR_INVALID_VOLUME: return QObject::tr("Invalid UEFI volume"); + case ERR_VOLUME_REVISION_NOT_SUPPORTED: return QObject::tr("Volume revision not supported"); + case ERR_VOLUME_GROW_FAILED: return QObject::tr("Volume grow failed"); + case ERR_UNKNOWN_FFS: return QObject::tr("Unknown file system"); + case ERR_INVALID_FILE: return QObject::tr("Invalid file"); + case ERR_INVALID_SECTION: return QObject::tr("Invalid section"); + case ERR_UNKNOWN_SECTION: return QObject::tr("Unknown section"); + case ERR_STANDARD_COMPRESSION_FAILED: return QObject::tr("Standard compression failed"); + case ERR_CUSTOMIZED_COMPRESSION_FAILED: return QObject::tr("Customized compression failed"); + case ERR_STANDARD_DECOMPRESSION_FAILED: return QObject::tr("Standard decompression failed"); + case ERR_CUSTOMIZED_DECOMPRESSION_FAILED: return QObject::tr("Customized compression failed"); + case ERR_UNKNOWN_COMPRESSION_TYPE: return QObject::tr("Unknown compression type"); + case ERR_UNKNOWN_EXTRACT_MODE: return QObject::tr("Unknown extract mode"); + case ERR_UNKNOWN_INSERT_MODE: return QObject::tr("Unknown insert mode"); + case ERR_UNKNOWN_IMAGE_TYPE: return QObject::tr("Unknown executable image type"); + case ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE: return QObject::tr("Unknown PE optional header type"); + case ERR_UNKNOWN_RELOCATION_TYPE: return QObject::tr("Unknown relocation type"); + case ERR_GENERIC_CALL_NOT_SUPPORTED: return QObject::tr("Generic call not supported"); + case ERR_VOLUME_BASE_NOT_FOUND: return QObject::tr("Volume base address not found"); + case ERR_PEI_CORE_ENTRY_POINT_NOT_FOUND: return QObject::tr("PEI core entry point not found"); + case ERR_COMPLEX_BLOCK_MAP: return QObject::tr("Block map structure too complex for correct analysis"); + case ERR_DIR_ALREADY_EXIST: return QObject::tr("Directory already exists"); + case ERR_DIR_CREATE: return QObject::tr("Directory can't be created"); + case ERR_UNKNOWN_PATCH_TYPE: return QObject::tr("Unknown patch type"); + case ERR_PATCH_OFFSET_OUT_OF_BOUNDS: return QObject::tr("Patch offset out of bounds"); + 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"); + default: return QObject::tr("Unknown error %1").arg(errorCode); + } +} + +// CRC32 implementation +UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length) +{ + static const UINT32 crcTable[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; + UINT32 crc32; + UINT32 i; + + // Accumulate crc32 for buffer + crc32 = initial ^ 0xFFFFFFFF; + for (i = 0; i < length; i++) { + crc32 = (crc32 >> 8) ^ crcTable[(crc32 ^ buffer[i]) & 0xFF]; + } + + return(crc32 ^ 0xFFFFFFFF); +} + +// Compression routines +STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArray & decompressedData) +{ + const UINT8* data; + UINT32 dataSize; + UINT8* decompressed; + UINT32 decompressedSize = 0; + UINT8* scratch; + UINT32 scratchSize = 0; + const EFI_TIANO_HEADER* header; + + switch (algorithm) + { + case EFI_NOT_COMPRESSED: + decompressedData = compressedData; + algorithm = COMPRESSION_ALGORITHM_NONE; + return ERR_SUCCESS; + case EFI_STANDARD_COMPRESSION: + // Set default algorithm to unknown + algorithm = COMPRESSION_ALGORITHM_UNKNOWN; + + // Get buffer sizes + data = (UINT8*)compressedData.data(); + dataSize = compressedData.size(); + + // Check header to be valid + header = (const EFI_TIANO_HEADER*)data; + if (header->CompSize + sizeof(EFI_TIANO_HEADER) != dataSize) + return ERR_STANDARD_DECOMPRESSION_FAILED; + + // Get info function is the same for both algorithms + if (ERR_SUCCESS != EfiTianoGetInfo(data, dataSize, &decompressedSize, &scratchSize)) + return ERR_STANDARD_DECOMPRESSION_FAILED; + + // Allocate memory + decompressed = new UINT8[decompressedSize]; + scratch = new UINT8[scratchSize]; + + // Decompress section data + + //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; + } + else algorithm = COMPRESSION_ALGORITHM_TIANO; + + decompressedData = QByteArray((const char*)decompressed, decompressedSize); + + delete[] decompressed; + delete[] scratch; + return ERR_SUCCESS; + case EFI_CUSTOMIZED_COMPRESSION: + // Set default algorithm to unknown + algorithm = COMPRESSION_ALGORITHM_UNKNOWN; + + // Get buffer sizes + data = (const UINT8*)compressedData.constData(); + dataSize = compressedData.size(); + + // Get info + if (ERR_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) + return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; + + // Allocate memory + decompressed = new UINT8[decompressedSize]; + + // Decompress section data + if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed)) { + //TODO: implement it again, if needed + // Intel modified LZMA workaround + // Decompress section data once again + data += sizeof(UINT32); + + // Get info again + if (ERR_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) { + delete[] decompressed; + return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; + } + + // Decompress section data again + if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed)) { + delete[] decompressed; + return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; + } + else { + algorithm = COMPRESSION_ALGORITHM_IMLZMA; + decompressedData = QByteArray((const char*)decompressed, decompressedSize); + } + } + else { + algorithm = COMPRESSION_ALGORITHM_LZMA; + decompressedData = QByteArray((const char*)decompressed, decompressedSize); + } + + delete[] decompressed; + return ERR_SUCCESS; + default: + algorithm = COMPRESSION_ALGORITHM_UNKNOWN; + return ERR_UNKNOWN_COMPRESSION_TYPE; + } +} + diff --git a/utility.h b/utility.h new file mode 100644 index 0000000..5b47362 --- /dev/null +++ b/utility.h @@ -0,0 +1,26 @@ +/* utility.h + +Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ +#include +#include "basetypes.h" + +// Converts error code to QString +extern QString errorCodeToQString(UINT8 errorCode); + +// Decompression routine +extern STATUS decompress(const QByteArray & compressed, UINT8 & algorithm, QByteArray & decompressed); + +// Compression routine +//STATUS compress(const QByteArray & decompressed, QByteArray & compressed, const UINT8 & algorithm); + +// CRC32 +extern UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length);