UT NE A19

Thanks to lordkag for #41:
- improved parsing of Intel flash descriptor
- improved detection of Tiano/EFI 1.1 compression type
- added 2 UEFI capsule GUIDs used by Lenovo
- solved potential crash on very low memory available
- UEFIExtract and UEFIFind update to include the latest parser changes
This commit is contained in:
Nikolaj Schlej 2016-02-02 02:08:08 +01:00
parent 61a1e98403
commit 4cf6b4f37b
16 changed files with 463 additions and 394 deletions

View file

@ -71,7 +71,7 @@ STATUS FfsDumper::recursiveDump(const QModelIndex & index, const QString & path,
QString info = tr("Type: %1\nSubtype: %2\n%3%4")
.arg(itemTypeToQString(model->type(index)))
.arg(itemSubtypeToQString(model->type(index), model->subtype(index)))
.arg(model->text(index).isEmpty() ? "" : tr("Text: %1\n").arg(model->text(index)))
.arg(model->text(index).isEmpty() ? tr("") : tr("Text: %1\n").arg(model->text(index)))
.arg(model->info(index));
file.setFileName(tr("%1/info.txt").arg(path));
if (!file.open(QFile::Text | QFile::WriteOnly))

View file

@ -1,6 +1,6 @@
/* uefiextract_main.cpp
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -49,7 +49,7 @@ int main(int argc, char *argv[])
TreeModel model;
FfsParser ffsParser(&model);
STATUS result = ffsParser.parseImageFile(buffer, model.index(0, 0));
STATUS result = ffsParser.parse(buffer);
if (result)
return result;
@ -75,8 +75,8 @@ int main(int argc, char *argv[])
}
}
else {
std::cout << "UEFIExtract 0.10.6" << std::endl << std::endl
<< "Usage: uefiextract imagefile [FileGUID_1 FileGUID_2 ... FileGUID_31]" << std::endl
std::cout << "UEFIExtract 0.10.7" << std::endl << std::endl
<< "Usage: UEFIExtract imagefile [FileGUID_1 FileGUID_2 ... FileGUID_31]" << std::endl
<< "Return value is a bit mask where 0 at position N means that file with GUID_N was found and unpacked, 1 otherwise" << std::endl;
return 1;
}

View file

@ -1,6 +1,6 @@
/* uefifind.cpp
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -46,7 +46,7 @@ STATUS UEFIFind::init(const QString & path)
QByteArray buffer = inputFile.readAll();
inputFile.close();
result = ffsParser->parseImageFile(buffer, model->index(0,0));
result = ffsParser->parse(buffer);
if (result)
return result;

View file

@ -1,6 +1,6 @@
/* uefifind_main.cpp
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -148,9 +148,9 @@ int main(int argc, char *argv[])
return ERR_SUCCESS;
}
else {
std::cout << "UEFIFind 0.10.4.1" << std::endl << std::endl <<
"Usage: uefifind {header | body | all} {list | count} pattern imagefile" << std::endl <<
" or uefifind file patternsfile imagefile" << std::endl;
std::cout << "UEFIFind 0.10.5" << std::endl << std::endl <<
"Usage: UEFIFind {header | body | all} {list | count} pattern imagefile" << std::endl <<
" or UEFIFind file patternsfile imagefile" << std::endl;
return ERR_INVALID_PARAMETER;
}
}

View file

@ -1,6 +1,6 @@
/* uefitool.cpp
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -17,7 +17,7 @@
UEFITool::UEFITool(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::UEFITool),
version(tr("0.30.0_alpha18"))
version(tr("0.30.0_alpha19"))
{
clipboard = QApplication::clipboard();
@ -566,7 +566,7 @@ void UEFITool::extract(const UINT8 mode)
void UEFITool::about()
{
QMessageBox::about(this, tr("About UEFITool"), tr(
"Copyright (c) 2015, Nikolaj Schlej aka <b>CodeRush</b>.<br>"
"Copyright (c) 2016, Nikolaj Schlej aka <b>CodeRush</b>.<br>"
"Program icon made by <a href=https://www.behance.net/alzhidkov>Alexander Zhidkov</a>.<br><br>"
"The program is dedicated to <b>RevoGirl</b>. Rest in peace, young genius.<br><br>"
"The program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License.<br>"
@ -657,7 +657,7 @@ void UEFITool::openImageFile(QString path)
init();
setWindowTitle(tr("UEFITool %1 - %2").arg(version).arg(fileInfo.fileName()));
UINT8 result = ffsParser->parseImageFile(buffer, model->index(0,0));
UINT8 result = ffsParser->parse(buffer);
showParserMessages();
if (result) {
QMessageBox::critical(this, tr("Image parsing failed"), errorCodeToQString(result), QMessageBox::Ok);

View file

@ -506,6 +506,7 @@ Returns:
UINT32 i;
mText = malloc (WNDSIZ * 2 + MAXMATCH);
if (!mText) return EFI_OUT_OF_RESOURCES;
for (i = 0 ; i < WNDSIZ * 2 + MAXMATCH; i ++) {
mText[i] = 0;
}

View file

@ -1,6 +1,6 @@
/* basetypes.h
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -93,12 +93,13 @@ typedef UINT8 STATUS;
#define EFI_ERROR(X) (X)
// Compression algorithms
#define COMPRESSION_ALGORITHM_UNKNOWN 0
#define COMPRESSION_ALGORITHM_NONE 1
#define COMPRESSION_ALGORITHM_EFI11 2
#define COMPRESSION_ALGORITHM_TIANO 3
#define COMPRESSION_ALGORITHM_LZMA 4
#define COMPRESSION_ALGORITHM_IMLZMA 5
#define COMPRESSION_ALGORITHM_UNKNOWN 0
#define COMPRESSION_ALGORITHM_NONE 1
#define COMPRESSION_ALGORITHM_EFI11 2
#define COMPRESSION_ALGORITHM_TIANO 3
#define COMPRESSION_ALGORITHM_UNDECIDED 4
#define COMPRESSION_ALGORITHM_LZMA 5
#define COMPRESSION_ALGORITHM_IMLZMA 6
// Item create modes
#define CREATE_MODE_APPEND 0

View file

@ -1,6 +1,6 @@
/* descriptor.h
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -21,7 +21,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
// Flash descriptor header
typedef struct _FLASH_DESCRIPTOR_HEADER {
UINT8 FfVector[16]; // Must be 16 0xFFs
UINT8 ReservedVector[16]; // Reserved for ARM ResetVector, 0xFFs on x86/x86-64 machines
UINT32 Signature; // 0x0FF0A55A
} FLASH_DESCRIPTOR_HEADER;
@ -98,7 +98,7 @@ typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION {
UINT8 InvalidInstruction2; //
UINT8 InvalidInstruction3; //
UINT16 PartitionBoundary; // Upper 16 bit of partition boundary address. Default is 0x0000, which makes the boundary to be 0x00001000
UINT16 ReservedZero; // Still unknown, zero in all descriptors I have seen
UINT16 : 16;
} FLASH_DESCRIPTOR_COMPONENT_SECTION;
// Region section
@ -115,16 +115,16 @@ typedef struct _FLASH_DESCRIPTOR_REGION_SECTION {
UINT16 GbeLimit; //
UINT16 PdrBase; // PDR
UINT16 PdrLimit; //
UINT16 Region5Base; // Reserved region
UINT16 Region5Limit; //
UINT16 Region6Base; // Reserved region
UINT16 Region6Limit; //
UINT16 Region7Base; // Reserved region
UINT16 Region7Limit; //
UINT16 Reserved1Base; // Reserved1
UINT16 Reserved1Limit; //
UINT16 Reserved2Base; // Reserved2
UINT16 Reserved2Limit; //
UINT16 Reserved3Base; // Reserved3
UINT16 Reserved3Limit; //
UINT16 EcBase; // EC
UINT16 EcLimit; //
UINT16 Region9Base; // Reserved region
UINT16 Region9Limit; //
UINT16 Reserved4Base; // Reserved4
UINT16 Reserved4Limit; //
} FLASH_DESCRIPTOR_REGION_SECTION;
// Master section

View file

@ -1,6 +1,6 @@
/* ffs.h
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -53,6 +53,14 @@ const QByteArray EFI_CAPSULE_GUID
const QByteArray INTEL_CAPSULE_GUID
("\xB9\x82\x91\x53\xB5\xAB\x91\x43\xB6\x9A\xE3\xA9\x43\xF7\x2F\xCC", 16);
// Lenovo capsule GUID
const QByteArray LENOVO_CAPSULE_GUID
("\xD3\xAF\x0B\xE2\x14\x99\x4F\x4F\x95\x37\x31\x29\xE0\x90\xEB\x3C", 16);
// Another Lenovo capsule GUID
const QByteArray LENOVO2_CAPSULE_GUID
("\x76\xFE\xB5\x25\x43\x82\x5C\x4A\xA9\xBD\x7E\xE3\x24\x61\x98\xB5", 16);
// Toshiba EFI Capsule header
typedef struct _TOSHIBA_CAPSULE_HEADER {
EFI_GUID CapsuleGuid;

View file

@ -1,6 +1,6 @@
/* fssbuilder.h
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -19,11 +19,11 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <QString>
#include <QModelIndex>
#include "../common/basetypes.h"
#include "../common/treemodel.h"
#include "../common/descriptor.h"
#include "../common/ffs.h"
#include "../common/utility.h"
#include "basetypes.h"
#include "treemodel.h"
#include "descriptor.h"
#include "ffs.h"
#include "utility.h"
class FfsBuilder : public QObject
{

View file

@ -1,6 +1,6 @@
/* ffsparser.cpp
/* ffsparser.cpp
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -10,16 +10,18 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
#include "ffsparser.h"
#include <math.h>
#include "ffsparser.h"
#include "types.h"
#include "treemodel.h"
#include "descriptor.h"
#include "ffs.h"
#include "gbe.h"
#include "me.h"
#include "fit.h"
// Region info structure definition
struct REGION_INFO {
UINT32 offset;
UINT32 length;
UINT8 type;
QByteArray data;
friend bool operator< (const REGION_INFO & lhs, const REGION_INFO & rhs){ return lhs.offset < rhs.offset; }
};
FfsParser::FfsParser(TreeModel* treeModel, QObject *parent)
: QObject(parent), model(treeModel), capsuleOffsetFixup(0)
@ -45,47 +47,53 @@ void FfsParser::clearMessages()
messagesVector.clear();
}
BOOLEAN FfsParser::hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2)
// Firmware image parsing functions
STATUS FfsParser::parse(const QByteArray & buffer)
{
if (begin1 < begin2 && begin2 < end1)
return TRUE;
if (begin1 < end2 && end2 < end1)
return TRUE;
if (begin2 < begin1 && begin1 < end2)
return TRUE;
if (begin2 < end1 && end1 < end2)
return TRUE;
return FALSE;
QModelIndex root;
STATUS result = performFirstPass(buffer, root);
addOffsetsRecursive(root);
if (result)
return result;
if (lastVtf.isValid()) {
result = performSecondPass(root);
}
else {
msg(tr("parse: not a single Volume Top File is found, the image may be corrupted"));
}
return result;
}
// Firmware image parsing functions
STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & root)
STATUS FfsParser::performFirstPass(const QByteArray & buffer, QModelIndex & index)
{
// Reset capsule offset fixeup value
// Reset capsule offset fixup value
capsuleOffsetFixup = 0;
// Check buffer size to be more than or equal to size of EFI_CAPSULE_HEADER
if ((UINT32)buffer.size() <= sizeof(EFI_CAPSULE_HEADER)) {
msg(tr("parseImageFile: image file is smaller than minimum size of %1h (%2) bytes").hexarg(sizeof(EFI_CAPSULE_HEADER)).arg(sizeof(EFI_CAPSULE_HEADER)));
msg(tr("performFirstPass: image file is smaller than minimum size of %1h (%2) bytes").hexarg(sizeof(EFI_CAPSULE_HEADER)).arg(sizeof(EFI_CAPSULE_HEADER)));
return ERR_INVALID_PARAMETER;
}
QModelIndex index;
UINT32 capsuleHeaderSize = 0;
// Check buffer for being normal EFI capsule header
if (buffer.startsWith(EFI_CAPSULE_GUID)
|| buffer.startsWith(INTEL_CAPSULE_GUID)) {
|| buffer.startsWith(INTEL_CAPSULE_GUID)
|| buffer.startsWith(LENOVO_CAPSULE_GUID)
|| buffer.startsWith(LENOVO2_CAPSULE_GUID)) {
// Get info
const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_CAPSULE_HEADER*)buffer.constData();
// Check sanity of HeaderSize and CapsuleImageSize values
if (capsuleHeader->HeaderSize == 0 || capsuleHeader->HeaderSize > (UINT32)buffer.size() || capsuleHeader->HeaderSize > capsuleHeader->CapsuleImageSize) {
msg(tr("parseImageFile: UEFI capsule header size of %1h (%2) bytes is invalid")
msg(tr("performFirstPass: UEFI capsule header size of %1h (%2) bytes is invalid")
.hexarg(capsuleHeader->HeaderSize).arg(capsuleHeader->HeaderSize));
return ERR_INVALID_CAPSULE;
}
if (capsuleHeader->CapsuleImageSize == 0 || capsuleHeader->CapsuleImageSize > (UINT32)buffer.size()) {
msg(tr("parseImageFile: UEFI capsule image size of %1h (%2) bytes is invalid")
msg(tr("performFirstPass: UEFI capsule image size of %1h (%2) bytes is invalid")
.hexarg(capsuleHeader->CapsuleImageSize).arg(capsuleHeader->CapsuleImageSize));
return ERR_INVALID_CAPSULE;
}
@ -105,7 +113,7 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex &
capsuleOffsetFixup = capsuleHeaderSize;
// Add tree item
index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, name, QString(), info, header, body, TRUE, QByteArray(), root);
index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, name, QString(), info, header, body, true);
}
// Check buffer for being Toshiba capsule header
else if (buffer.startsWith(TOSHIBA_CAPSULE_GUID)) {
@ -114,12 +122,12 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex &
// Check sanity of HeaderSize and FullSize values
if (capsuleHeader->HeaderSize == 0 || capsuleHeader->HeaderSize > (UINT32)buffer.size() || capsuleHeader->HeaderSize > capsuleHeader->FullSize) {
msg(tr("parseImageFile: Toshiba capsule header size of %1h (%2) bytes is invalid")
msg(tr("performFirstPass: Toshiba capsule header size of %1h (%2) bytes is invalid")
.hexarg(capsuleHeader->HeaderSize).arg(capsuleHeader->HeaderSize));
return ERR_INVALID_CAPSULE;
}
if (capsuleHeader->FullSize == 0 || capsuleHeader->FullSize > (UINT32)buffer.size()) {
msg(tr("parseImageFile: Toshiba capsule full size of %1h (%2) bytes is invalid")
msg(tr("performFirstPass: Toshiba capsule full size of %1h (%2) bytes is invalid")
.hexarg(capsuleHeader->FullSize).arg(capsuleHeader->FullSize));
return ERR_INVALID_CAPSULE;
}
@ -139,14 +147,14 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex &
capsuleOffsetFixup = capsuleHeaderSize;
// Add tree item
index = model->addItem(Types::Capsule, Subtypes::ToshibaCapsule, name, QString(), info, header, body, TRUE, QByteArray(), root);
index = model->addItem(Types::Capsule, Subtypes::ToshibaCapsule, name, QString(), info, header, body, true);
}
// Check buffer for being extended Aptio signed capsule header
// Check buffer for being extended Aptio capsule header
else if (buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID) || buffer.startsWith(APTIO_UNSIGNED_CAPSULE_GUID)) {
bool signedCapsule = buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID);
if ((UINT32)buffer.size() <= sizeof(APTIO_CAPSULE_HEADER)) {
msg(tr("parseImageFile: AMI capsule image file is smaller than minimum size of %1h (%2) bytes").hexarg(sizeof(APTIO_CAPSULE_HEADER)).arg(sizeof(APTIO_CAPSULE_HEADER)));
msg(tr("performFirstPass: AMI capsule image file is smaller than minimum size of %1h (%2) bytes").hexarg(sizeof(APTIO_CAPSULE_HEADER)).arg(sizeof(APTIO_CAPSULE_HEADER)));
return ERR_INVALID_PARAMETER;
}
@ -155,11 +163,11 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex &
// Check sanity of RomImageOffset and CapsuleImageSize values
if (capsuleHeader->RomImageOffset == 0 || capsuleHeader->RomImageOffset > (UINT32)buffer.size() || capsuleHeader->RomImageOffset > capsuleHeader->CapsuleHeader.CapsuleImageSize) {
msg(tr("parseImageFile: AMI capsule image offset of %1h (%2) bytes is invalid").hexarg(capsuleHeader->RomImageOffset).arg(capsuleHeader->RomImageOffset));
msg(tr("performFirstPass: AMI capsule image offset of %1h (%2) bytes is invalid").hexarg(capsuleHeader->RomImageOffset).arg(capsuleHeader->RomImageOffset));
return ERR_INVALID_CAPSULE;
}
if (capsuleHeader->CapsuleHeader.CapsuleImageSize == 0 || capsuleHeader->CapsuleHeader.CapsuleImageSize > (UINT32)buffer.size()) {
msg(tr("parseImageFile: AMI capsule image size of %1h (%2) bytes is invalid").hexarg(capsuleHeader->CapsuleHeader.CapsuleImageSize).arg(capsuleHeader->CapsuleHeader.CapsuleImageSize));
msg(tr("performFirstPass: AMI capsule image size of %1h (%2) bytes is invalid").hexarg(capsuleHeader->CapsuleHeader.CapsuleImageSize).arg(capsuleHeader->CapsuleHeader.CapsuleImageSize));
return ERR_INVALID_CAPSULE;
}
@ -178,17 +186,13 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex &
capsuleOffsetFixup = capsuleHeaderSize;
// Add tree item
index = model->addItem(Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, name, QString(), info, header, body, TRUE, QByteArray(), root);
index = model->addItem(Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, name, QString(), info, header, body, true);
// Show message about possible Aptio signature break
if (signedCapsule) {
msg(tr("parseImageFile: Aptio capsule signature may become invalid after image modifications"), index);
msg(tr("performFirstPass: Aptio capsule signature may become invalid after image modifications"), index);
}
}
// Other cases
else {
index = root;
}
// Skip capsule header to have flash chip image
QByteArray flashImage = buffer.mid(capsuleHeaderSize);
@ -202,13 +206,16 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex &
// Parse as Intel image
QModelIndex imageIndex;
result = parseIntelImage(flashImage, capsuleHeaderSize, index, imageIndex);
if (result != ERR_INVALID_FLASH_DESCRIPTOR)
if (result != ERR_INVALID_FLASH_DESCRIPTOR) {
if (!index.isValid())
index = imageIndex;
return result;
}
}
// Get info
QString name = tr("UEFI image");
QString info = tr("Full size: %2h (%3)").hexarg(flashImage.size()).arg(flashImage.size());
QString info = tr("Full size: %1h (%2)").hexarg(flashImage.size()).arg(flashImage.size());
// Construct parsing data
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
@ -219,21 +226,9 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex &
// Parse the image
result = parseRawArea(flashImage, biosIndex);
if (result)
return result;
// Add offsets
addOffsetsRecursive(index);
// Check if the last VTF is found
if (!lastVtf.isValid()) {
msg(tr("parseImageFile: not a single Volume Top File is found, the image may be corrupted"), biosIndex);
}
else {
return performSecondPass(biosIndex);
}
return ERR_SUCCESS;
if (!index.isValid())
index = biosIndex;
return result;
}
STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index)
@ -247,8 +242,6 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa
// Store the beginning of descriptor as descriptor base address
const UINT8* descriptor = (const UINT8*)intelImage.constData();
UINT32 descriptorBegin = 0;
UINT32 descriptorEnd = FLASH_DESCRIPTOR_SIZE;
// Check for buffer size to be greater or equal to descriptor region size
if (intelImage.size() < FLASH_DESCRIPTOR_SIZE) {
@ -258,7 +251,7 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa
// Parse descriptor map
const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor + sizeof(FLASH_DESCRIPTOR_HEADER));
const FLASH_DESCRIPTOR_UPPER_MAP* upperMap = (const FLASH_DESCRIPTOR_UPPER_MAP*)(descriptor + FLASH_DESCRIPTOR_UPPER_MAP_BASE);
const FLASH_DESCRIPTOR_UPPER_MAP* upperMap = (const FLASH_DESCRIPTOR_UPPER_MAP*)(descriptor + FLASH_DESCRIPTOR_UPPER_MAP_BASE);
// Check sanity of base values
if (descriptorMap->MasterBase > FLASH_DESCRIPTOR_MAX_BASE
@ -291,43 +284,47 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// Regions
QVector<REGION_INFO> regions;
// ME region
QByteArray me;
UINT32 meBegin = 0;
UINT32 meEnd = 0;
REGION_INFO me;
me.type = Subtypes::MeRegion;
me.offset = 0;
me.length = 0;
if (regionSection->MeLimit) {
meBegin = calculateRegionOffset(regionSection->MeBase);
meEnd = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit);
me = intelImage.mid(meBegin, meEnd);
meEnd += meBegin;
me.offset = calculateRegionOffset(regionSection->MeBase);
me.length = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit);
me.data = intelImage.mid(me.offset, me.length);
regions.append(me);
}
// BIOS region
QByteArray bios;
UINT32 biosBegin = 0;
UINT32 biosEnd = 0;
REGION_INFO bios;
bios.type = Subtypes::BiosRegion;
bios.offset = 0;
bios.length = 0;
if (regionSection->BiosLimit) {
biosBegin = calculateRegionOffset(regionSection->BiosBase);
biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit);
bios.offset = calculateRegionOffset(regionSection->BiosBase);
bios.length = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit);
// Check for Gigabyte specific descriptor map
if (biosEnd - biosBegin == (UINT32)intelImage.size()) {
if (!meEnd) {
if (bios.length == (UINT32)intelImage.size()) {
if (!me.offset) {
msg(tr("parseIntelImage: can't determine BIOS region start from Gigabyte-specific descriptor"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
biosBegin = meEnd;
bios = intelImage.mid(biosBegin, biosEnd);
// biosEnd will point to the end of the image file
// it may be wrong, but it's pretty hard to detect a padding after BIOS region
// with malformed descriptor
// Use ME region end as BIOS region offset
bios.offset = me.offset + me.length;
bios.length = (UINT32)intelImage.size() - bios.offset;
bios.data = intelImage.mid(bios.offset, bios.length);
}
// Normal descriptor map
else {
bios = intelImage.mid(biosBegin, biosEnd);
// Calculate biosEnd
biosEnd += biosBegin;
bios.data = intelImage.mid(bios.offset, bios.length);
}
regions.append(bios);
}
else {
msg(tr("parseIntelImage: descriptor parsing failed, BIOS region not found in descriptor"));
@ -335,107 +332,148 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa
}
// GbE region
QByteArray gbe;
UINT32 gbeBegin = 0;
UINT32 gbeEnd = 0;
REGION_INFO gbe;
gbe.type = Subtypes::GbeRegion;
gbe.offset = 0;
gbe.length = 0;
if (regionSection->GbeLimit) {
gbeBegin = calculateRegionOffset(regionSection->GbeBase);
gbeEnd = calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit);
gbe = intelImage.mid(gbeBegin, gbeEnd);
gbeEnd += gbeBegin;
gbe.offset = calculateRegionOffset(regionSection->GbeBase);
gbe.length = calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit);
gbe.data = intelImage.mid(gbe.offset, gbe.length);
regions.append(gbe);
}
// PDR region
QByteArray pdr;
UINT32 pdrBegin = 0;
UINT32 pdrEnd = 0;
REGION_INFO pdr;
pdr.type = Subtypes::PdrRegion;
pdr.offset = 0;
pdr.length = 0;
if (regionSection->PdrLimit) {
pdrBegin = calculateRegionOffset(regionSection->PdrBase);
pdrEnd = calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit);
pdr = intelImage.mid(pdrBegin, pdrEnd);
pdrEnd += pdrBegin;
pdr.offset = calculateRegionOffset(regionSection->PdrBase);
pdr.length = calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit);
pdr.data = intelImage.mid(pdr.offset, pdr.length);
regions.append(pdr);
}
// Reserved1 region
REGION_INFO reserved1;
reserved1.type = Subtypes::Reserved1Region;
reserved1.offset = 0;
reserved1.length = 0;
if (regionSection->Reserved1Limit && regionSection->Reserved1Base != 0xFFFF && regionSection->Reserved1Limit != 0xFFFF) {
reserved1.offset = calculateRegionOffset(regionSection->Reserved1Base);
reserved1.length = calculateRegionSize(regionSection->Reserved1Base, regionSection->Reserved1Limit);
reserved1.data = intelImage.mid(reserved1.offset, reserved1.length);
regions.append(reserved1);
}
// Reserved2 region
REGION_INFO reserved2;
reserved2.type = Subtypes::Reserved2Region;
reserved2.offset = 0;
reserved2.length = 0;
if (regionSection->Reserved2Limit && regionSection->Reserved2Base != 0xFFFF && regionSection->Reserved2Limit != 0xFFFF) {
reserved2.offset = calculateRegionOffset(regionSection->Reserved2Base);
reserved2.length = calculateRegionSize(regionSection->Reserved2Base, regionSection->Reserved2Limit);
reserved2.data = intelImage.mid(reserved2.offset, reserved2.length);
regions.append(reserved2);
}
// Reserved3 region
REGION_INFO reserved3;
reserved3.type = Subtypes::Reserved3Region;
reserved3.offset = 0;
reserved3.length = 0;
// EC region
QByteArray ec;
UINT32 ecBegin = 0;
UINT32 ecEnd = 0;
REGION_INFO ec;
ec.type = Subtypes::EcRegion;
ec.offset = 0;
ec.length = 0;
// Reserved4 region
REGION_INFO reserved4;
reserved3.type = Subtypes::Reserved4Region;
reserved4.offset = 0;
reserved4.length = 0;
// Check for EC and reserved region 4 only for v2 descriptor
if (descriptorVersion == 2) {
if (regionSection->Reserved3Limit) {
reserved3.offset = calculateRegionOffset(regionSection->Reserved3Base);
reserved3.length = calculateRegionSize(regionSection->Reserved3Base, regionSection->Reserved3Limit);
reserved3.data = intelImage.mid(reserved3.offset, reserved3.length);
regions.append(reserved3);
}
if (regionSection->EcLimit) {
pdrBegin = calculateRegionOffset(regionSection->EcBase);
pdrEnd = calculateRegionSize(regionSection->EcBase, regionSection->EcLimit);
pdr = intelImage.mid(ecBegin, ecEnd);
ecEnd += ecBegin;
ec.offset = calculateRegionOffset(regionSection->EcBase);
ec.length = calculateRegionSize(regionSection->EcBase, regionSection->EcLimit);
ec.data = intelImage.mid(ec.offset, ec.length);
regions.append(ec);
}
if (regionSection->Reserved4Limit) {
reserved4.offset = calculateRegionOffset(regionSection->Reserved4Base);
reserved4.length = calculateRegionSize(regionSection->Reserved4Base, regionSection->Reserved4Limit);
reserved4.data = intelImage.mid(reserved4.offset, reserved4.length);
regions.append(reserved4);
}
}
// Check for intersections between regions
// Descriptor
if (hasIntersection(descriptorBegin, descriptorEnd, gbeBegin, gbeEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with GbE region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
if (hasIntersection(descriptorBegin, descriptorEnd, meBegin, meEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with ME region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
if (hasIntersection(descriptorBegin, descriptorEnd, biosBegin, biosEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with BIOS region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
if (hasIntersection(descriptorBegin, descriptorEnd, pdrBegin, pdrEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with PDR region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
if (descriptorVersion == 2 && hasIntersection(descriptorBegin, descriptorEnd, ecBegin, ecEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with EC region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// GbE
if (hasIntersection(gbeBegin, gbeEnd, meBegin, meEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with ME region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
if (hasIntersection(gbeBegin, gbeEnd, biosBegin, biosEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with BIOS region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
if (hasIntersection(gbeBegin, gbeEnd, pdrBegin, pdrEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with PDR region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
if (descriptorVersion == 2 && hasIntersection(gbeBegin, gbeEnd, ecBegin, ecEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with EC region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// ME
if (hasIntersection(meBegin, meEnd, biosBegin, biosEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, ME region has intersection with BIOS region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
if (hasIntersection(meBegin, meEnd, pdrBegin, pdrEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, ME region has intersection with PDR region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
if (descriptorVersion == 2 && hasIntersection(meBegin, meEnd, ecBegin, ecEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, ME region has intersection with EC region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// BIOS
if (hasIntersection(biosBegin, biosEnd, pdrBegin, pdrEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, BIOS region has intersection with PDR region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
if (descriptorVersion == 2 && hasIntersection(biosBegin, biosEnd, ecBegin, ecEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, BIOS region has intersection with EC region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// PDR
if (descriptorVersion == 2 && hasIntersection(pdrBegin, pdrEnd, ecBegin, ecEnd)) {
msg(tr("parseIntelImage: descriptor parsing failed, PDR region has intersection with EC region"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// Sort regions in ascending order
qSort(regions);
// Check for intersections and paddings between regions
REGION_INFO region;
// Check intersection with the descriptor
if (regions.first().offset < FLASH_DESCRIPTOR_SIZE) {
msg(tr("parseIntelImage: %1 region has intersection with flash descriptor").arg(itemSubtypeToQString(Types::Region, regions.first().type)), index);
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// Check for padding between descriptor and the first region
else if (regions.first().offset > FLASH_DESCRIPTOR_SIZE) {
region.offset = FLASH_DESCRIPTOR_SIZE;
region.length = regions.first().offset - FLASH_DESCRIPTOR_SIZE;
region.data = intelImage.mid(region.offset, region.length);
region.type = getPaddingType(region.data);
regions.prepend(region);
}
// Check for intersections/paddings between regions
for (int i = 1; i < regions.count(); i++) {
UINT32 previousRegionEnd = regions[i-1].offset + regions[i-1].length;
// Check that current region is fully present in the image
if (regions[i].offset + regions[i].length > (UINT32)intelImage.size()) {
msg(tr("parseIntelImage: %1 region is located outside of opened image, if your system uses dual-chip storage, please append another part to the opened image")
.arg(itemSubtypeToQString(Types::Region, regions[i].type)), index);
return ERR_TRUNCATED_IMAGE;
}
// Check for intersection with previous region
if (regions[i].offset < previousRegionEnd) {
msg(tr("parseIntelImage: %1 region has intersection with %2 region")
.arg(itemSubtypeToQString(Types::Region, regions[i].type))
.arg(itemSubtypeToQString(Types::Region, regions[i-1].type)), index);
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// Check for padding between current and previous regions
else if (regions[i].offset > previousRegionEnd) {
region.offset = previousRegionEnd;
region.length = regions[i].offset - previousRegionEnd;
region.data = intelImage.mid(region.offset, region.length);
region.type = getPaddingType(region.data);
regions.insert(i - 1, region);
}
}
// Check for padding after the last region
if (regions.last().offset + regions.last().length < (UINT32)intelImage.size()) {
region.offset = regions.last().offset + regions.last().length;
region.length = intelImage.size() - region.offset;
region.data = intelImage.mid(region.offset, region.length);
region.type = getPaddingType(region.data);
regions.append(region);
}
// Region map is consistent
// Intel image
@ -459,24 +497,11 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa
QByteArray body = intelImage.left(FLASH_DESCRIPTOR_SIZE);
name = tr("Descriptor region");
info = tr("Full size: %1h (%2)").hexarg(FLASH_DESCRIPTOR_SIZE).arg(FLASH_DESCRIPTOR_SIZE);
// Check regions presence once again
QVector<UINT32> offsets;
if (regionSection->GbeLimit) {
offsets.append(gbeBegin);
info += tr("\nGbE region offset: %1h").hexarg(gbeBegin + parentOffset);
}
if (regionSection->MeLimit) {
offsets.append(meBegin);
info += tr("\nME region offset: %1h").hexarg(meBegin + parentOffset);
}
if (regionSection->BiosLimit) {
offsets.append(biosBegin);
info += tr("\nBIOS region offset: %1h").hexarg(biosBegin + parentOffset);
}
if (regionSection->PdrLimit) {
offsets.append(pdrBegin);
info += tr("\nPDR region offset: %1h").hexarg(pdrBegin + parentOffset);
// Add offsets of actual regions
for (int i = 0; i < regions.count(); i++) {
if (regions[i].type != Subtypes::ZeroPadding && regions[i].type != Subtypes::OnePadding && regions[i].type != Subtypes::DataPadding)
info += tr("\n%1 region offset: %2h").arg(itemSubtypeToQString(Types::Region, regions[i].type)).hexarg(regions[i].offset + parentOffset);
}
// Region access settings
@ -555,93 +580,63 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa
}
// Add descriptor tree item
model->addItem(Types::Region, Subtypes::DescriptorRegion, name, QString(), info, QByteArray(), body, TRUE, parsingDataToQByteArray(pdata), index);
// Sort regions in ascending order
qSort(offsets);
QModelIndex regionIndex = model->addItem(Types::Region, Subtypes::DescriptorRegion, name, QString(), info, QByteArray(), body, TRUE, parsingDataToQByteArray(pdata), index);
// Parse regions
UINT8 result = 0;
for (int i = 0; i < offsets.count(); i++) {
// Parse GbE region
if (offsets.at(i) == gbeBegin) {
QModelIndex gbeIndex;
result = parseGbeRegion(gbe, gbeBegin, index, gbeIndex);
UINT8 result = ERR_SUCCESS;
UINT8 parseResult = ERR_SUCCESS;
Q_FOREACH(region, regions) {
switch (region.type) {
case Subtypes::BiosRegion:
result = parseBiosRegion(region.data, region.offset, index, regionIndex);
break;
case Subtypes::MeRegion:
result = parseMeRegion(region.data, region.offset, index, regionIndex);
break;
case Subtypes::GbeRegion:
result = parseGbeRegion(region.data, region.offset, index, regionIndex);
break;
case Subtypes::PdrRegion:
result = parsePdrRegion(region.data, region.offset, index, regionIndex);
break;
case Subtypes::Reserved1Region:
case Subtypes::Reserved2Region:
case Subtypes::Reserved3Region:
case Subtypes::EcRegion:
case Subtypes::Reserved4Region:
result = parseGeneralRegion(region.type, region.data, region.offset, index, regionIndex);
break;
case Subtypes::ZeroPadding:
case Subtypes::OnePadding:
case Subtypes::DataPadding: {
// Add padding between regions
QByteArray padding = intelImage.mid(region.offset, region.length);
// Get parent's parsing data
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
// Get info
name = tr("Padding");
info = tr("Full size: %1h (%2)")
.hexarg(padding.size()).arg(padding.size());
// Construct parsing data
pdata.offset = parentOffset + region.offset;
// Add tree item
regionIndex = model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
result = ERR_SUCCESS;
} break;
default:
msg(tr("parseIntelImage: region of unknown type found"), index);
result = ERR_INVALID_FLASH_DESCRIPTOR;
}
// Parse ME region
else if (offsets.at(i) == meBegin) {
QModelIndex meIndex;
result = parseMeRegion(me, meBegin, index, meIndex);
}
// Parse BIOS region
else if (offsets.at(i) == biosBegin) {
QModelIndex biosIndex;
result = parseBiosRegion(bios, biosBegin, index, biosIndex);
}
// Parse PDR region
else if (offsets.at(i) == pdrBegin) {
QModelIndex pdrIndex;
result = parsePdrRegion(pdr, pdrBegin, index, pdrIndex);
}
// Parse EC region
else if (descriptorVersion == 2 && offsets.at(i) == ecBegin) {
QModelIndex ecIndex;
result = parseEcRegion(ec, ecBegin, index, ecIndex);
}
if (result)
return result;
// Store the first failed result as a final result
if (!parseResult && result)
parseResult = result;
}
// Add the data after the last region as padding
UINT32 IntelDataEnd = 0;
UINT32 LastRegionOffset = offsets.last();
if (LastRegionOffset == gbeBegin)
IntelDataEnd = gbeEnd;
else if (LastRegionOffset == meBegin)
IntelDataEnd = meEnd;
else if (LastRegionOffset == biosBegin)
IntelDataEnd = biosEnd;
else if (LastRegionOffset == pdrBegin)
IntelDataEnd = pdrEnd;
else if (descriptorVersion == 2 && LastRegionOffset == ecBegin)
IntelDataEnd = ecEnd;
if (IntelDataEnd > (UINT32)intelImage.size()) { // Image file is truncated
msg(tr("parseIntelImage: image size %1 (%2) is smaller than the end of last region %3 (%4), may be damaged")
.hexarg(intelImage.size()).arg(intelImage.size())
.hexarg(IntelDataEnd).arg(IntelDataEnd), index);
return ERR_TRUNCATED_IMAGE;
}
else if (IntelDataEnd < (UINT32)intelImage.size()) { // Insert padding
QByteArray padding = intelImage.mid(IntelDataEnd);
// Get parent's parsing data
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
// Get info
name = tr("Padding");
info = tr("Full size: %1h (%2)")
.hexarg(padding.size()).arg(padding.size());
// Construct parsing data
pdata.offset = IntelDataEnd;
// Add tree item
model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
}
// Add offsets
addOffsetsRecursive(index);
// Check if the last VTF is found
if (!lastVtf.isValid()) {
msg(tr("parseIntelImage: not a single Volume Top File is found, the image may be corrupted"), index);
}
else {
return performSecondPass(index);
}
return ERR_SUCCESS;
return parseResult;
}
STATUS FfsParser::parseGbeRegion(const QByteArray & gbe, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index)
@ -774,25 +769,25 @@ STATUS FfsParser::parsePdrRegion(const QByteArray & pdr, const UINT32 parentOffs
return ERR_SUCCESS;
}
STATUS FfsParser::parseEcRegion(const QByteArray & ec, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index)
STATUS FfsParser::parseGeneralRegion(const UINT8 subtype, const QByteArray & region, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index)
{
// Check sanity
if (ec.isEmpty())
if (region.isEmpty())
return ERR_EMPTY_REGION;
// Get parent's parsing data
PARSING_DATA pdata = parsingDataFromQModelIndex(parent);
// Get info
QString name = tr("EC region");
QString name = tr("%1 region").arg(itemSubtypeToQString(Types::Region, subtype));
QString info = tr("Full size: %1h (%2)").
hexarg(ec.size()).arg(ec.size());
hexarg(region.size()).arg(region.size());
// Construct parsing data
pdata.offset += parentOffset;
// Add tree item
index = model->addItem(Types::Region, Subtypes::EcRegion, name, QString(), info, QByteArray(), ec, TRUE, parsingDataToQByteArray(pdata), parent);
index = model->addItem(Types::Region, subtype, name, QString(), info, QByteArray(), region, TRUE, parsingDataToQByteArray(pdata), parent);
return ERR_SUCCESS;
}
@ -1190,7 +1185,7 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare
return ERR_SUCCESS;
}
STATUS FfsParser::findNextVolume(const QModelIndex index, const QByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset)
STATUS FfsParser::findNextVolume(const QModelIndex & index, const QByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset)
{
int nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset);
if (nextIndex < EFI_FV_SIGNATURE_OFFSET)
@ -1745,7 +1740,7 @@ STATUS FfsParser::parsePadFileBody(const QModelIndex & index)
return ERR_SUCCESS;
}
STATUS FfsParser::parseSections(const QByteArray & sections, const QModelIndex & index)
STATUS FfsParser::parseSections(const QByteArray & sections, const QModelIndex & index, const bool preparse)
{
// Sanity check
if (!index.isValid())
@ -1759,6 +1754,7 @@ STATUS FfsParser::parseSections(const QByteArray & sections, const QModelIndex &
UINT32 headerSize = model->header(index).size();
UINT32 sectionOffset = 0;
STATUS result = ERR_SUCCESS;
while (sectionOffset < bodySize) {
// Get section size
UINT32 sectionSize = getSectionSize(sections, sectionOffset, pdata.ffsVersion);
@ -1773,27 +1769,36 @@ STATUS FfsParser::parseSections(const QByteArray & sections, const QModelIndex &
// Constuct parsing data
pdata.offset += headerSize + sectionOffset;
// Add tree item
QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, tr("Non-UEFI data"), "", info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
// Show message
msg(tr("parseSections: non-UEFI data found in sections area"), dataIndex);
// Final parsing
if (!preparse) {
// Add tree item
QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, tr("Non-UEFI data"), "", info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
// Show message
msg(tr("parseSections: non-UEFI data found in sections area"), dataIndex);
}
// Preparsing
else {
return ERR_INVALID_SECTION;
}
break; // Exit from parsing loop
}
// Parse section header
QModelIndex sectionIndex;
STATUS result = parseSectionHeader(sections.mid(sectionOffset, sectionSize), headerSize + sectionOffset, index, sectionIndex);
if (result)
msg(tr("parseSections: section header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index);
result = parseSectionHeader(sections.mid(sectionOffset, sectionSize), headerSize + sectionOffset, index, sectionIndex);
if (result) {
if (!preparse)
msg(tr("parseSections: section header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index);
else
return ERR_INVALID_SECTION;
}
// Move to next section
sectionOffset += sectionSize;
sectionOffset = ALIGN4(sectionOffset);
}
//Parse bodies
//Parse bodies, will be skipped on preparse phase
for (int i = 0; i < model->rowCount(index); i++) {
QModelIndex current = index.child(i, 0);
switch (model->type(current)) {
@ -2013,7 +2018,7 @@ STATUS FfsParser::parseGuidedSectionHeader(const QByteArray & section, const UIN
// Check certificate type
if (certType == WIN_CERT_TYPE_EFI_GUID) {
additionalInfo += tr("\nCertificate type: UEFI").hexarg2(certType, 4);
additionalInfo += tr("\nCertificate type: UEFI");
// Get certificate GUID
const WIN_CERTIFICATE_UEFI_GUID* winCertificateUefiGuid = (const WIN_CERTIFICATE_UEFI_GUID*)(section.constData() + nextHeaderOffset);
@ -2256,7 +2261,8 @@ STATUS FfsParser::parseCompressedSectionBody(const QModelIndex & index)
// Decompress section
QByteArray decompressed;
STATUS result = decompress(model->body(index), algorithm, decompressed);
QByteArray efiDecompressed;
STATUS result = decompress(model->body(index), algorithm, decompressed, efiDecompressed);
if (result) {
msg(tr("parseCompressedSectionBody: decompression failed with error \"%1\"").arg(errorCodeToQString(result)), index);
return ERR_SUCCESS;
@ -2272,6 +2278,22 @@ STATUS FfsParser::parseCompressedSectionBody(const QModelIndex & index)
model->addInfo(index, tr("\nActual decompressed size: %1h (%2)").hexarg(decompressed.size()).arg(decompressed.size()));
}
// Check for undecided compression algorithm, this is a special case
if (algorithm == COMPRESSION_ALGORITHM_UNDECIDED) {
// Try preparse of sections decompressed with Tiano algorithm
if (ERR_SUCCESS == parseSections(decompressed, index, true)) {
algorithm = COMPRESSION_ALGORITHM_TIANO;
}
// Try preparse of sections decompressed with EFI 1.1 algorithm
else if (ERR_SUCCESS == parseSections(efiDecompressed, index, true)) {
algorithm = COMPRESSION_ALGORITHM_EFI11;
decompressed = efiDecompressed;
}
else {
msg(tr("parseCompressedSectionBody: can't guess the correct decompression algorithm, both preparse steps are failed"), index);
}
}
// Add info
model->addInfo(index, tr("\nCompression algorithm: %1").arg(compressionTypeToQString(algorithm)));
@ -2302,24 +2324,33 @@ STATUS FfsParser::parseGuidedSectionBody(const QModelIndex & index)
UINT8 algorithm = COMPRESSION_ALGORITHM_NONE;
// Tiano compressed section
if (QByteArray((const char*)&guid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_TIANO) {
QByteArray efiDecompressed;
algorithm = EFI_STANDARD_COMPRESSION;
STATUS result = decompress(model->body(index), algorithm, processed);
STATUS result = decompress(model->body(index), algorithm, processed, efiDecompressed);
if (result) {
parseCurrentSection = false;
msg(tr("parseGuidedSectionBody: decompression failed with error \"%1\"").arg(errorCodeToQString(result)), index);
return ERR_SUCCESS;
}
if (algorithm == COMPRESSION_ALGORITHM_TIANO) {
info += tr("\nCompression algorithm: Tiano");
info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length());
// Check for undecided compression algorithm, this is a special case
if (algorithm == COMPRESSION_ALGORITHM_UNDECIDED) {
// Try preparse of sections decompressed with Tiano algorithm
if (ERR_SUCCESS == parseSections(processed, index, true)) {
algorithm = COMPRESSION_ALGORITHM_TIANO;
}
// Try preparse of sections decompressed with EFI 1.1 algorithm
else if (ERR_SUCCESS == parseSections(efiDecompressed, index, true)) {
algorithm = COMPRESSION_ALGORITHM_EFI11;
processed = efiDecompressed;
}
else {
msg(tr("parseGuidedSectionBody: can't guess the correct decompression algorithm, both preparse steps are failed"), index);
}
}
else if (algorithm == COMPRESSION_ALGORITHM_EFI11) {
info += tr("\nCompression algorithm: EFI 1.1");
info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length());
}
else
info += tr("\nCompression type: unknown");
info += tr("\nCompression algorithm: %1").arg(compressionTypeToQString(algorithm));
info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length());
}
// LZMA compressed section
else if (QByteArray((const char*)&guid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_LZMA) {

View file

@ -1,6 +1,6 @@
/* ffsparser.h
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -26,6 +26,13 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include "utility.h"
#include "peimage.h"
#include "parsingdata.h"
#include "types.h"
#include "treemodel.h"
#include "descriptor.h"
#include "ffs.h"
#include "gbe.h"
#include "me.h"
#include "fit.h"
class TreeModel;
@ -44,7 +51,7 @@ public:
void clearMessages();
// Firmware image parsing
STATUS parseImageFile(const QByteArray & imageFile, const QModelIndex & index);
STATUS parse(const QByteArray &buffer);
STATUS parseRawArea(const QByteArray & data, const QModelIndex & index);
STATUS parseVolumeHeader(const QByteArray & volume, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
STATUS parseVolumeBody(const QModelIndex & index);
@ -67,11 +74,11 @@ private:
STATUS parseMeRegion(const QByteArray & me, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
STATUS parseBiosRegion(const QByteArray & bios, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
STATUS parsePdrRegion(const QByteArray & pdr, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
STATUS parseEcRegion(const QByteArray & ec, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
STATUS parseGeneralRegion(const UINT8 subtype, const QByteArray & region, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
STATUS parsePadFileBody(const QModelIndex & index);
STATUS parseVolumeNonUefiData(const QByteArray & data, const UINT32 parentOffset, const QModelIndex & index);
STATUS parseSections(const QByteArray & sections, const QModelIndex & index);
STATUS parseSections(const QByteArray & sections, const QModelIndex & index, const bool preparse = false);
STATUS parseCommonSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
STATUS parseCompressedSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
@ -91,21 +98,18 @@ private:
UINT8 getPaddingType(const QByteArray & padding);
STATUS parseAprioriRawSection(const QByteArray & body, QString & parsed);
STATUS findNextVolume(const QModelIndex index, const QByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset);
STATUS findNextVolume(const QModelIndex & index, const QByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset);
STATUS getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize);
UINT32 getFileSize(const QByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion);
UINT32 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, const UINT8 ffsVersion);
STATUS performFirstPass(const QByteArray & imageFile, QModelIndex & index);
STATUS performSecondPass(const QModelIndex & index);
STATUS addOffsetsRecursive(const QModelIndex & index);
STATUS addMemoryAddressesRecursive(const QModelIndex & index, const UINT32 diff);
// Internal operations
BOOLEAN hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2);
// Message helper
void msg(const QString & message, const QModelIndex &index = QModelIndex());
};
#endif

View file

@ -1,6 +1,6 @@
/* types.cpp
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -21,16 +21,24 @@ QString regionTypeToQString(const UINT8 type)
{
case Subtypes::DescriptorRegion:
return QObject::tr("Descriptor");
case Subtypes::GbeRegion:
return QObject::tr("GbE");
case Subtypes::MeRegion:
return QObject::tr("ME");
case Subtypes::BiosRegion:
return QObject::tr("BIOS");
case Subtypes::MeRegion:
return QObject::tr("ME");
case Subtypes::GbeRegion:
return QObject::tr("GbE");
case Subtypes::PdrRegion:
return QObject::tr("PDR");
case Subtypes::Reserved1Region:
return QObject::tr("Reserved1");
case Subtypes::Reserved2Region:
return QObject::tr("Reserved2");
case Subtypes::Reserved3Region:
return QObject::tr("Reserved3");
case Subtypes::EcRegion:
return QObject::tr("EC");
case Subtypes::Reserved4Region:
return QObject::tr("Reserved4");
default:
return QObject::tr("Unknown");
};
@ -124,6 +132,8 @@ QString compressionTypeToQString(const UINT8 algorithm)
return QObject::tr("EFI 1.1");
case COMPRESSION_ALGORITHM_TIANO:
return QObject::tr("Tiano");
case COMPRESSION_ALGORITHM_UNDECIDED:
return QObject::tr("Undecided Tiano/EFI 1.1");
case COMPRESSION_ALGORITHM_LZMA:
return QObject::tr("LZMA");
case COMPRESSION_ALGORITHM_IMLZMA:

View file

@ -1,6 +1,6 @@
/* types.h
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -66,12 +66,16 @@ namespace Subtypes {
};
enum RegionSubtypes {
DescriptorRegion = 100,
GbeRegion,
MeRegion,
DescriptorRegion = 0,
BiosRegion,
MeRegion,
GbeRegion,
PdrRegion,
EcRegion
Reserved1Region,
Reserved2Region,
Reserved3Region,
EcRegion,
Reserved4Region
};
enum PaddingSubtypes {

View file

@ -1,6 +1,6 @@
/* utility.cpp
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -151,11 +151,12 @@ UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length)
}
// Compression routines
STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArray & decompressedData)
STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArray & decompressedData, QByteArray & efiDecompressedData)
{
const UINT8* data;
UINT32 dataSize;
UINT8* decompressed;
UINT8* efiDecompressed;
UINT32 decompressedSize = 0;
UINT8* scratch;
UINT32 scratchSize = 0;
@ -167,7 +168,7 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr
decompressedData = compressedData;
algorithm = COMPRESSION_ALGORITHM_NONE;
return ERR_SUCCESS;
case EFI_STANDARD_COMPRESSION:
case EFI_STANDARD_COMPRESSION: {
// Set default algorithm to unknown
algorithm = COMPRESSION_ALGORITHM_UNKNOWN;
@ -185,34 +186,45 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr
return ERR_STANDARD_DECOMPRESSION_FAILED;
// Allocate memory
try {
decompressed = new UINT8[decompressedSize];
scratch = new UINT8[scratchSize];
}
catch (std::bad_alloc) {
decompressed = (UINT8*)malloc(decompressedSize);
efiDecompressed = (UINT8*)malloc(decompressedSize);
scratch = (UINT8*)malloc(scratchSize);
if (!decompressed || !efiDecompressed || !scratch) {
if (decompressed) free(decompressed);
if (efiDecompressed) free(efiDecompressed);
if (scratch) free(scratch);
return ERR_STANDARD_DECOMPRESSION_FAILED;
}
// Decompress section data
// Decompress section data using both algorithms
STATUS result = ERR_SUCCESS;
// Try Tiano
STATUS TianoResult = TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize);
// Try EFI 1.1
STATUS EfiResult = EfiDecompress(data, dataSize, efiDecompressed, decompressedSize, scratch, scratchSize);
//TODO: separate EFI1.1 from Tiano another way
// Try Tiano decompression first
if (ERR_SUCCESS != TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) {
// Not Tiano, try EFI 1.1
if (ERR_SUCCESS != EfiDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) {
delete[] decompressed;
delete[] scratch;
return ERR_STANDARD_DECOMPRESSION_FAILED;
}
else algorithm = COMPRESSION_ALGORITHM_EFI11;
if (EfiResult == ERR_SUCCESS && TianoResult == ERR_SUCCESS) { // Both decompressions are OK
algorithm = COMPRESSION_ALGORITHM_UNDECIDED;
decompressedData = QByteArray((const char*)decompressed, decompressedSize);
efiDecompressedData = QByteArray((const char*)efiDecompressed, decompressedSize);
}
else if (TianoResult == ERR_SUCCESS) { // Only Tiano is OK
algorithm = COMPRESSION_ALGORITHM_TIANO;
decompressedData = QByteArray((const char*)decompressed, decompressedSize);
}
else if (EfiResult == ERR_SUCCESS) { // Only EFI 1.1 is OK
algorithm = COMPRESSION_ALGORITHM_EFI11;
decompressedData = QByteArray((const char*)efiDecompressed, decompressedSize);
}
else { // Both decompressions failed
result = ERR_STANDARD_DECOMPRESSION_FAILED;
}
else algorithm = COMPRESSION_ALGORITHM_TIANO;
decompressedData = QByteArray((const char*)decompressed, decompressedSize);
delete[] decompressed;
delete[] scratch;
return ERR_SUCCESS;
free(decompressed);
free(efiDecompressed);
free(scratch);
return result;
}
case EFI_CUSTOMIZED_COMPRESSION:
// Set default algorithm to unknown
algorithm = COMPRESSION_ALGORITHM_UNKNOWN;
@ -226,10 +238,8 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr
return ERR_CUSTOMIZED_DECOMPRESSION_FAILED;
// Allocate memory
try {
decompressed = new UINT8[decompressedSize];
}
catch (std::bad_alloc) {
decompressed = (UINT8*)malloc(decompressedSize);
if (!decompressed) {
return ERR_STANDARD_DECOMPRESSION_FAILED;
}
@ -241,13 +251,13 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr
// Get info again
if (ERR_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) {
delete[] decompressed;
free(decompressed);
return ERR_CUSTOMIZED_DECOMPRESSION_FAILED;
}
// Decompress section data again
if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed)) {
delete[] decompressed;
free(decompressed);
return ERR_CUSTOMIZED_DECOMPRESSION_FAILED;
}
else {
@ -260,7 +270,7 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr
decompressedData = QByteArray((const char*)decompressed, decompressedSize);
}
delete[] decompressed;
free(decompressed);
return ERR_SUCCESS;
default:
algorithm = COMPRESSION_ALGORITHM_UNKNOWN;

View file

@ -1,6 +1,6 @@
/* utility.h
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -29,7 +29,7 @@ QByteArray parsingDataToQByteArray(const PARSING_DATA & pdata);
extern QString errorCodeToQString(UINT8 errorCode);
// Decompression routine
extern STATUS decompress(const QByteArray & compressed, UINT8 & algorithm, QByteArray & decompressed);
extern STATUS decompress(const QByteArray & compressed, UINT8 & algorithm, QByteArray & decompressed, QByteArray & efiDecompressed = QByteArray());
// Compression routine
//STATUS compress(const QByteArray & decompressed, QByteArray & compressed, const UINT8 & algorithm);