Version 0.3.0

Refactor code to separate GUI from FFS ttraversal
This commit is contained in:
Nikolaj Schlej 2013-10-15 17:19:15 +02:00
parent 1827c94977
commit 3ffbc01a3f
17 changed files with 1570 additions and 1081 deletions

View File

@ -2078,7 +2078,7 @@ void LzmaEnc_Finish(CLzmaEncHandle pp)
if (p->mtMode)
MatchFinderMt_ReleaseStream(&p->matchFinderMt);
#else
//pp = pp;
pp = pp;
#endif
}

View File

@ -25,6 +25,12 @@ UINT8* calculateAddress16(UINT8* baseAddress, const UINT16 baseOrLimit)
return baseAddress + baseOrLimit * 0x1000;
}
// Calculate offset of region using its base
UINT32 calculateRegionOffset(const UINT16 base)
{
return base * 0x1000;
}
//Calculate size of region using its base and limit
UINT32 calculateRegionSize(const UINT16 base, const UINT16 limit)
{

View File

@ -158,6 +158,9 @@ typedef struct {
extern UINT8* calculateAddress8(UINT8* baseAddress, const UINT8 baseOrLimit);
// 16 bit base or limit
extern UINT8* calculateAddress16(UINT8* baseAddress, const UINT16 baseOrLimit);
// Calculate offset of region using its base
extern UINT32 calculateRegionOffset(const UINT16 base);
// Calculate size of region using its base and limit
extern UINT32 calculateRegionSize(const UINT16 base, const UINT16 limit);
#endif

View File

@ -10,6 +10,7 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
#include <QObject>
#include "ffs.h"
const UINT8 ffsAlignmentTable[] =

1
ffs.h
View File

@ -15,7 +15,6 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <QByteArray>
#include <QString>
#include <QObject>
#include "basetypes.h"
// C++ functions

1164
ffsengine.cpp Normal file

File diff suppressed because it is too large Load Diff

67
ffsengine.h Normal file
View File

@ -0,0 +1,67 @@
/* ffsengine.h
Copyright (c) 2013, 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 <QObject>
#include <QModelIndex>
#include <QByteArray>
#include "basetypes.h"
#include "treemodel.h"
class TreeModel;
class FfsEngine : public QObject
{
Q_OBJECT
public:
FfsEngine(QObject *parent = 0);
~FfsEngine(void);
TreeModel* model() const;
QString message() const;
UINT8 parseInputFile(const QByteArray & buffer);
UINT8* parseRegion(const QByteArray & flashImage, UINT8 regionSubtype, const UINT16 regionBase, const UINT16 regionLimit, QModelIndex & index);
UINT8 parseBios(const QByteArray & bios, const QModelIndex & parent = QModelIndex());
INT32 findNextVolume(const QByteArray & bios, INT32 volumeOffset = 0);
UINT32 getVolumeSize(const QByteArray & bios, INT32 volumeOffset);
UINT8 parseVolume(const QByteArray & volume, UINT32 volumeBase, UINT8 revision, bool erasePolarity, const QModelIndex & parent = QModelIndex());
UINT8 parseFile(const QByteArray & file, UINT8 revision, bool erasePolarity, const QModelIndex & parent = QModelIndex());
QModelIndex addTreeItem(const UINT8 type, const UINT8 subtype = 0, const UINT32 offset = 0,
const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(),
const QModelIndex & parent = QModelIndex());
bool removeItem(const QModelIndex &index);
QByteArray header(const QModelIndex& index) const;
bool hasEmptyHeader(const QModelIndex& index) const;
QByteArray body(const QModelIndex& index) const;
bool hasEmptyBody(const QModelIndex& index) const;
bool isCompressedFile(const QModelIndex& index) const;
QByteArray uncompressFile(const QModelIndex& index) const;
private:
QString text;
TreeItem *rootItem;
TreeModel *treeModel;
void msg(const QString & message);
QModelIndex findParentOfType(UINT8 type, const QModelIndex& index) const;
bool setTreeItemName(const QString &data, const QModelIndex &index);
bool setTreeItemText(const QString &data, const QModelIndex &index);
};
#endif

36
gbe.h Normal file
View File

@ -0,0 +1,36 @@
/* gbe.h
Copyright (c) 2013, 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 __GBE_H__
#define __GBE_H__
#include "basetypes.h"
// Make sure we use right packing rules
#pragma pack(push,1)
typedef struct {
UINT8 vendor[3];
UINT8 device[3];
} GBE_MAC;
#define GBE_VERSION_OFFSET 10
typedef struct {
UINT8 id: 4;
UINT8 minor: 4;
UINT8 major;
} GBE_VERSION;
// Restore previous packing rules
#pragma pack(pop)
#endif

34
me.h Normal file
View File

@ -0,0 +1,34 @@
/* me.h
Copyright (c) 2013, 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 __ME_H__
#define __ME_H__
#include "basetypes.h"
// Make sure we use right packing rules
#pragma pack(push,1)
const QByteArray ME_VERSION_SIGNATURE("\x24\x4D\x4E\x32", 4);
typedef struct {
UINT32 signature;
UINT32 reserved; // Unknown for me
UINT16 major;
UINT16 minor;
UINT16 bugfix;
UINT16 build;
} ME_VERSION;
// Restore previous packing rules
#pragma pack(pop)
#endif

View File

@ -12,14 +12,14 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
#include "treeitem.h"
#include "treeitemtypes.h"
TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const QString & name, const QString & typeName, const QString & subtypeName,
TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const UINT32 offset, const QString & name, const QString & typeName, const QString & subtypeName,
const QString & text, const QString & info, const QByteArray & header, const QByteArray & body, TreeItem *parent)
{
itemType = type;
itemSubtype = subtype;
itemOffset = offset;
itemName = name;
itemTypeName = typeName;
itemSubtypeName = subtypeName;
@ -134,6 +134,11 @@ UINT8 TreeItem::subtype()
return itemSubtype;
}
UINT32 TreeItem::offset()
{
return itemOffset;
}
QByteArray TreeItem::header()
{
return itemHeader;

View File

@ -23,7 +23,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
class TreeItem
{
public:
TreeItem(const UINT8 type, const UINT8 subtype = 0, const QString & name = QString(), const QString & typeName = QString(), const QString & subtypeName = QString(),
TreeItem(const UINT8 type, const UINT8 subtype = 0, const UINT32 offset = 0, const QString & name = QString(), const QString & typeName = QString(), const QString & subtypeName = QString(),
const QString & text = QString(), const QString & info = QString(), const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), TreeItem *parent = 0);
~TreeItem();
@ -39,6 +39,7 @@ public:
UINT8 type();
UINT8 subtype();
UINT32 offset();
QByteArray header();
QByteArray body();
QString info();
@ -55,6 +56,7 @@ private:
QList<TreeItem*> childItems;
UINT8 itemType;
UINT8 itemSubtype;
UINT32 itemOffset;
QByteArray itemHeader;
QByteArray itemBody;
QString itemName;

View File

@ -13,41 +13,17 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include "treeitem.h"
#include "treemodel.h"
#include "ffs.h"
#include "descriptor.h"
TreeModel::TreeModel(QObject *parent)
TreeModel::TreeModel(TreeItem *root, QObject *parent)
: QAbstractItemModel(parent)
{
rootItem = new TreeItem(RootItem, 0, tr("Object"), tr("Type"), tr("Subtype"), tr("Text"));
rootItem = root;
}
TreeModel::~TreeModel()
{
delete rootItem;
}
bool TreeModel::hasEmptyHeader(const QModelIndex& index)
{
if (!index.isValid())
return true;
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->hasEmptyHeader();
}
bool TreeModel::hasEmptyBody(const QModelIndex& index)
{
if (!index.isValid())
return true;
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->hasEmptyBody();
}
int TreeModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
@ -140,153 +116,6 @@ int TreeModel::rowCount(const QModelIndex &parent) const
return parentItem->childCount();
}
QModelIndex TreeModel::addItem(UINT8 type, UINT8 subtype, const QByteArray &header, const QByteArray &body, const QModelIndex &parent)
{
TreeItem *parentItem;
int parentColumn = 0;
if (!parent.isValid())
parentItem = rootItem;
else
{
parentItem = static_cast<TreeItem*>(parent.internalPointer());
parentColumn = parent.column();
}
// All information extraction must be here
QString name, typeName, subtypeName, info;
EFI_CAPSULE_HEADER* capsuleHeader;
APTIO_CAPSULE_HEADER* aptioCapsuleHeader;
FLASH_DESCRIPTOR_MAP* descriptorMap;
//FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection;
//FLASH_DESCRIPTOR_REGION_SECTION* regionSection;
//FLASH_DESCRIPTOR_MASTER_SECTION* masterSection;
EFI_FIRMWARE_VOLUME_HEADER* volumeHeader;
EFI_FFS_FILE_HEADER* fileHeader;
//EFI_COMMON_SECTION_HEADER* sectionHeader;
switch (type)
{
case RootItem:
// Do not allow to add another root item
return QModelIndex();
break;
case CapsuleItem:
//typeName = tr("Capsule");
switch (subtype)
{
case AptioCapsule:
name = tr("AMI Aptio capsule");
aptioCapsuleHeader = (APTIO_CAPSULE_HEADER*) header.constData();
info = tr("GUID: %1\nHeader size: %2\nFlags: %3\nImage size: %4")
.arg(guidToQString(aptioCapsuleHeader->CapsuleHeader.CapsuleGuid))
.arg(aptioCapsuleHeader->CapsuleHeader.Flags, 8, 16, QChar('0'))
.arg(aptioCapsuleHeader->RomImageOffset, 4, 16, QChar('0'))
.arg(aptioCapsuleHeader->CapsuleHeader.CapsuleImageSize - aptioCapsuleHeader->RomImageOffset, 8, 16, QChar('0'));
//!TODO: more info about Aptio capsule
break;
case UefiCapsule:
name = tr("UEFI capsule");
capsuleHeader = (EFI_CAPSULE_HEADER*) header.constData();
info = tr("GUID: %1\nHeader size: %2\nFlags: %3\nImage size: %4")
.arg(guidToQString(capsuleHeader->CapsuleGuid))
.arg(capsuleHeader->Flags, 8, 16, QChar('0'))
.arg(capsuleHeader->HeaderSize, 8, 16, QChar('0'))
.arg(capsuleHeader->CapsuleImageSize, 8, 16, QChar('0'));
break;
default:
name = tr("Unknown capsule");
info = tr("GUID: %1\n").arg(guidToQString(*(EFI_GUID*)header.constData()));
break;
}
break;
case DescriptorItem:
name = tr("Descriptor");
descriptorMap = (FLASH_DESCRIPTOR_MAP*) body.constData();
info = tr("Flash chips: %1\nRegions: %2\nMasters: %3\nPCH straps:%4\nPROC straps: %5\nICC table entries: %6")
.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);
//!TODO: more info about descriptor
break;
case RegionItem:
typeName = tr("Region");
info = tr("Size: %1").arg(body.size(), 8, 16, QChar('0'));
//!TODO: more info about GbE and ME regions
switch (subtype)
{
case GbeRegion:
name = tr("GbE region");
break;
case MeRegion:
name = tr("ME region");
break;
case BiosRegion:
name = tr("BIOS region");
break;
case PdrRegion:
name = tr("PDR region");
break;
default:
name = tr("Unknown region");
break;
}
break;
case PaddingItem:
name = tr("Padding");
info = tr("Size: %1").arg(body.size(), 8, 16, QChar('0'));
break;
case VolumeItem:
typeName = tr("Volume");
// Parse volume header to determine its revision and file system
volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) header.constData();
name = guidToQString(volumeHeader->FileSystemGuid);
subtypeName = tr("Revision %1").arg(volumeHeader->Revision);
info = tr("Size: %1\nSignature: %2\nAttributes: %3\nHeader size: %4")
.arg(volumeHeader->FvLength, 8, 16, QChar('0'))
.arg(volumeHeader->Signature, 8, 16, QChar('0'))
.arg(volumeHeader->Attributes, 8, 16, QChar('0'))
.arg(volumeHeader->HeaderLength, 4, 16, QChar('0'));
break;
case FileItem:
typeName = tr("File");
// Parse file header to determine its GUID and type
fileHeader = (EFI_FFS_FILE_HEADER*) header.constData();
name = guidToQString(fileHeader->Name);
subtypeName = fileTypeToQString(subtype);
info = tr("Type: %1\nAttributes: %2\nSize: %3\nState: %4")
.arg(fileHeader->Type, 2, 16, QChar('0'))
.arg(fileHeader->Attributes, 2, 16, QChar('0'))
.arg(uint24ToUint32(fileHeader->Size), 6, 16, QChar('0'))
.arg(fileHeader->State, 2, 16, QChar('0'));
break;
case SectionItem:
typeName = tr("Section");
name = sectionTypeToQString(subtype) + tr(" section");
info = tr("Size: %1").arg(body.size(), 8, 16, QChar('0'));
//!TODO: add more specific info for all section types with uncommon headers
// Set name of file
if (subtype == UserInterfaceSection)
{
QString text = QString::fromUtf16((const ushort*)body.constData());
setItemText(text, findParentOfType(FileItem, parent));
}
break;
default:
name = tr("Unknown");
break;
}
emit layoutAboutToBeChanged();
TreeItem *item = new TreeItem(type, subtype, name, typeName, subtypeName, "", info, header, body, parentItem);
parentItem->appendChild(item);
emit layoutChanged();
return createIndex(parentItem->childCount() - 1, parentColumn, item);
}
bool TreeModel::setItemName(const QString &data, const QModelIndex &index)
{
if(!index.isValid())
@ -309,46 +138,24 @@ bool TreeModel::setItemText(const QString &data, const QModelIndex &index)
return true;
}
bool TreeModel::removeItem(const QModelIndex &index)
QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT32 offset, const QString & name, const QString & typeName,
const QString & subtypeName, const QString & text, const QString & info,
const QByteArray & header, const QByteArray & body, const QModelIndex & parent)
{
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
item->parent()->removeChild(item);
delete item;
return true;
TreeItem *parentItem;
int parentColumn = 0;
if (!parent.isValid())
parentItem = rootItem;
else
{
parentItem = static_cast<TreeItem*>(parent.internalPointer());
parentColumn = parent.column();
}
QModelIndex TreeModel::findParentOfType(UINT8 type, const QModelIndex& index)
{
if(!index.isValid())
return QModelIndex();
TreeItem *item;
QModelIndex parent = index;
for(item = static_cast<TreeItem*>(parent.internalPointer());
item != NULL && item != rootItem && item->type() != type;
item = static_cast<TreeItem*>(parent.internalPointer()))
parent = parent.parent();
if (item != NULL && item != rootItem)
return parent;
return QModelIndex();
}
QByteArray TreeModel::header(const QModelIndex& index)
{
if (!index.isValid())
return QByteArray();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->header();
}
QByteArray TreeModel::body(const QModelIndex& index)
{
if (!index.isValid())
return QByteArray();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->body();
emit layoutAboutToBeChanged();
TreeItem *item = new TreeItem(type, subtype, offset, name, typeName, subtypeName, text, info, header, body, parentItem);
parentItem->appendChild(item);
emit layoutChanged();
return createIndex(parentItem->childCount() - 1, parentColumn, item);
}

View File

@ -20,7 +20,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <QVariant>
#include "basetypes.h"
#include "treeitemtypes.h"
class TreeItem;
@ -29,7 +28,7 @@ class TreeModel : public QAbstractItemModel
Q_OBJECT
public:
TreeModel(QObject *parent = 0);
TreeModel(TreeItem *root, QObject *parent = 0);
~TreeModel();
QVariant data(const QModelIndex &index, int role) const;
@ -42,18 +41,14 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QModelIndex addItem(UINT8 type, UINT8 subtype = 0, const QByteArray &header = QByteArray(), const QByteArray &body = QByteArray(), const QModelIndex &parent = QModelIndex());
bool removeItem(const QModelIndex &index);
QByteArray header(const QModelIndex& index);
bool hasEmptyHeader(const QModelIndex& index);
QByteArray body(const QModelIndex& index);
bool hasEmptyBody(const QModelIndex& index);
private:
QModelIndex findParentOfType(UINT8 type, const QModelIndex& index);
bool setItemName(const QString &data, const QModelIndex &index);
bool setItemText(const QString &data, const QModelIndex &index);
QModelIndex addItem(const UINT8 type, const UINT8 subtype = 0, const UINT32 offset = 0, const QString & name = QString(),
const QString & typeName = QString(), const QString & subtypeName = QString(), const QString & text = QString(),
const QString & info = QString(), const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QModelIndex & parent = QModelIndex());
private:
TreeItem *rootItem;
};

View File

@ -12,7 +12,6 @@
*/
#include "uefitool.h"
#include "treeitemtypes.h"
#include "ui_uefitool.h"
UEFITool::UEFITool(QWidget *parent) :
@ -20,13 +19,13 @@ UEFITool::UEFITool(QWidget *parent) :
ui(new Ui::UEFITool)
{
ui->setupUi(this);
treeModel = NULL;
// Signal-slot connections
connect(ui->fromFileButton, SIGNAL(clicked()), this, SLOT(openImageFile()));
connect(ui->exportAllButton, SIGNAL(clicked()), this, SLOT(saveAll()));
connect(ui->exportBodyButton, SIGNAL(clicked()), this, SLOT(saveBody()));
ffsEngine = NULL;
//Connect
connect(ui->actionOpenImageFile, SIGNAL(triggered()), this, SLOT(openImageFile()));
connect(ui->actionExtract, SIGNAL(triggered()), this, SLOT(saveAll()));
connect(ui->actionExtractBody, SIGNAL(triggered()), this, SLOT(saveBody()));
connect(ui->actionExtractUncompressed, SIGNAL(triggered()), this, SLOT(saveUncompressedFile()));
// Enable Drag-and-Drop actions
this->setAcceptDrops(true);
@ -37,7 +36,7 @@ UEFITool::UEFITool(QWidget *parent) :
UEFITool::~UEFITool()
{
delete ui;
delete treeModel;
delete ffsEngine;
}
void UEFITool::init()
@ -45,19 +44,26 @@ void UEFITool::init()
// Clear UI components
ui->debugEdit->clear();
ui->infoEdit->clear();
ui->exportAllButton->setDisabled(true);
ui->exportBodyButton->setDisabled(true);
// Make new tree model
TreeModel * newModel = new TreeModel(this);
ui->structureTreeView->setModel(newModel);
if (treeModel)
delete treeModel;
treeModel = newModel;
// Show info after selection the item in tree view
// Disable all actions except openImageFile
ui->actionExtract->setDisabled(true);
ui->actionExtractBody->setDisabled(true);
ui->actionExtractUncompressed->setDisabled(true);
ui->actionReplace->setDisabled(true);
ui->actionDelete->setDisabled(true);
ui->actionReplaceWithPadding->setDisabled(true);
ui->actionInsertBefore->setDisabled(true);
ui->actionInsertAfter->setDisabled(true);
// Make new ffsEngine
ffsEngine = new FfsEngine(this);
ui->structureTreeView->setModel(ffsEngine->model());
// Connect
connect(ui->structureTreeView, SIGNAL(collapsed(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void)));
connect(ui->structureTreeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void)));
connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
this, SLOT(populateUi(const QModelIndex &)));
//connect(ui->structureTreeView, SIGNAL(collapsed(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void)));
connect(ui->structureTreeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void)));
resizeTreeViewColums();
}
@ -66,13 +72,14 @@ void UEFITool::populateUi(const QModelIndex &current/*, const QModelIndex &previ
//!TODO: make widget
currentIndex = current;
ui->infoEdit->setPlainText(current.data(Qt::UserRole).toString());
ui->exportAllButton->setDisabled(treeModel->hasEmptyBody(current) && treeModel->hasEmptyHeader(current));
ui->exportBodyButton->setDisabled(treeModel->hasEmptyHeader(current));
ui->actionExtract->setDisabled(ffsEngine->hasEmptyBody(current) && ffsEngine->hasEmptyHeader(current));
ui->actionExtractBody->setDisabled(ffsEngine->hasEmptyHeader(current));
ui->actionExtractUncompressed->setEnabled(ffsEngine->isCompressedFile(current));
}
void UEFITool::resizeTreeViewColums(/*const QModelIndex &index*/)
{
int count = treeModel->columnCount();
int count = ffsEngine->model()->columnCount();
for(int i = 0; i < count; i++)
ui->structureTreeView->resizeColumnToContents(i);
}
@ -105,17 +112,19 @@ void UEFITool::openImageFile(QString path)
inputFile.close();
init();
UINT8 result = parseInputFile(buffer);
UINT8 result = ffsEngine->parseInputFile(buffer);
if (result)
debug(tr("Opened file can't be parsed as UEFI image (%1)").arg(result));
ui->statusBar->showMessage(tr("Opened file can't be parsed as UEFI image (%1)").arg(result));
else
ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName()));
ui->debugEdit->appendPlainText(ffsEngine->message());
resizeTreeViewColums();
}
void UEFITool::saveAll()
{
QString path = QFileDialog::getSaveFileName(this, tr("Save header to binary file"),".","Binary files (*.bin);;All files (*.*)");
QString path = QFileDialog::getSaveFileName(this, tr("Save selected item to binary file"),".","Binary files (*.bin);;All files (*.*)");
QFile outputFile;
outputFile.setFileName(path);
@ -125,13 +134,13 @@ void UEFITool::saveAll()
return;
}
outputFile.write(treeModel->header(currentIndex) + treeModel->body(currentIndex));
outputFile.write(ffsEngine->header(currentIndex) + ffsEngine->body(currentIndex));
outputFile.close();
}
void UEFITool::saveBody()
{
QString path = QFileDialog::getSaveFileName(this, tr("Save body to binary file"),".","Binary files (*.bin);;All files (*.*)");
QString path = QFileDialog::getSaveFileName(this, tr("Save selected item without header to binary file"),".","Binary files (*.bin);;All files (*.*)");
QFile outputFile;
outputFile.setFileName(path);
@ -141,7 +150,23 @@ void UEFITool::saveBody()
return;
}
outputFile.write(treeModel->body(currentIndex));
outputFile.write(ffsEngine->body(currentIndex));
outputFile.close();
}
void UEFITool::saveUncompressedFile()
{
QString path = QFileDialog::getSaveFileName(this, tr("Save selected FFS file as uncompressed to binary file"),".","FFS files (*.ffs);;All files (*.*)");
QFile outputFile;
outputFile.setFileName(path);
if (!outputFile.open(QFile::WriteOnly))
{
ui->statusBar->showMessage(tr("Can't open file for writing. Check file permissions."));
return;
}
outputFile.write(ffsEngine->uncompressFile(currentIndex));
outputFile.close();
}
@ -177,769 +202,3 @@ void UEFITool::dropEvent(QDropEvent* event)
openImageFile(path);
}
void UEFITool::debug(const QString & text)
{
//!TODO: log to separate debug window
ui->debugEdit->appendPlainText(text);
}
QModelIndex UEFITool::addTreeItem(UINT8 type, UINT8 subtype,
const QByteArray & header, const QByteArray & body, const QModelIndex & parent)
{
return treeModel->addItem(type, subtype, header, body, parent);
}
UINT8 UEFITool::parseInputFile(const QByteArray & buffer)
{
UINT32 capsuleHeaderSize = 0;
FLASH_DESCRIPTOR_HEADER* descriptorHeader = NULL;
QByteArray flashImage;
QByteArray bios;
QModelIndex index;
// Check buffer for being normal EFI capsule header
if (buffer.startsWith(EFI_CAPSULE_GUID)) {
EFI_CAPSULE_HEADER* capsuleHeader = (EFI_CAPSULE_HEADER*) buffer.constData();
capsuleHeaderSize = capsuleHeader->HeaderSize;
QByteArray header = buffer.left(capsuleHeaderSize);
QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize);
index = addTreeItem(CapsuleItem, UefiCapsule, header, body);
}
// Check buffer for being extended Aptio capsule header
else if (buffer.startsWith(APTIO_CAPSULE_GUID)) {
APTIO_CAPSULE_HEADER* aptioCapsuleHeader = (APTIO_CAPSULE_HEADER*) buffer.constData();
capsuleHeaderSize = aptioCapsuleHeader->RomImageOffset;
QByteArray header = buffer.left(capsuleHeaderSize);
QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize);
index = addTreeItem(CapsuleItem, AptioCapsule, header, body);
}
// Skip capsule header to have flash chip image
flashImage = buffer.right(buffer.size() - capsuleHeaderSize);
// Check buffer for being Intel flash descriptor
descriptorHeader = (FLASH_DESCRIPTOR_HEADER*) flashImage.constData();
// Check descriptor signature
if (descriptorHeader->Signature == FLASH_DESCRIPTOR_SIGNATURE) {
FLASH_DESCRIPTOR_MAP* descriptorMap;
FLASH_DESCRIPTOR_REGION_SECTION* regionSection;
// Store the beginning of descriptor as descriptor base address
UINT8* descriptor = (UINT8*) flashImage.constData();
UINT8* gbeRegion = NULL;
UINT8* meRegion = NULL;
UINT8* biosRegion = NULL;
UINT8* pdrRegion = NULL;
// Check for buffer size to be greater or equal to descriptor region size
if (flashImage.size() < FLASH_DESCRIPTOR_SIZE) {
debug(tr("Input file is smaller then mininum descriptor size of 4KB"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// Parse descriptor map
descriptorMap = (FLASH_DESCRIPTOR_MAP*) (flashImage.constData() + sizeof(FLASH_DESCRIPTOR_HEADER));
regionSection = (FLASH_DESCRIPTOR_REGION_SECTION*) calculateAddress8(descriptor, descriptorMap->RegionBase);
// Add tree item
QByteArray header = flashImage.left(sizeof(FLASH_DESCRIPTOR_HEADER));
QByteArray body = flashImage.mid(sizeof(FLASH_DESCRIPTOR_HEADER), FLASH_DESCRIPTOR_SIZE - sizeof(FLASH_DESCRIPTOR_HEADER));
index = addTreeItem(DescriptorItem, 0, header, body, index);
// Parse region section
QModelIndex gbeIndex(index);
QModelIndex meIndex(index);
QModelIndex biosIndex(index);
QModelIndex pdrIndex(index);
gbeRegion = parseRegion(flashImage, GbeRegion, regionSection->GbeBase, regionSection->GbeLimit, gbeIndex);
meRegion = parseRegion(flashImage, MeRegion, regionSection->MeBase, regionSection->MeLimit, meIndex);
biosRegion = parseRegion(flashImage, BiosRegion, regionSection->BiosBase, regionSection->BiosLimit, biosIndex);
pdrRegion = parseRegion(flashImage, PdrRegion, regionSection->PdrBase, regionSection->PdrLimit, pdrIndex);
// Parse complete
//!TODO: show some info about GbE, ME and PDR regions if found
// Exit if no bios region found
if (!biosRegion) {
debug(tr("BIOS region not found"));
return ERR_BIOS_REGION_NOT_FOUND;
}
index = biosIndex;
bios = QByteArray::fromRawData((const char*) biosRegion, calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit));
}
else {
bios = buffer;
}
// We are in the beginning of BIOS space, where firmware volumes are
// Parse BIOS space
return parseBios(bios, index);
}
UINT8* UEFITool::parseRegion(const QByteArray & flashImage, UINT8 regionSubtype, const UINT16 regionBase, const UINT16 regionLimit, QModelIndex & index)
{
// Check for empty region
if (!regionLimit)
return NULL;
// Calculate region size
UINT32 regionSize = calculateRegionSize(regionBase, regionLimit);
// Populate descriptor map
FLASH_DESCRIPTOR_MAP* descriptor_map = (FLASH_DESCRIPTOR_MAP*) (flashImage.constData() + sizeof(FLASH_DESCRIPTOR_HEADER));
// Determine presence of 2 flash chips
bool twoChips = descriptor_map->NumberOfFlashChips;
// construct region name
//!TODO: make this to regionTypeToQString(const UINT8 type) in descriptor.cpp
QString regionName;
switch (regionSubtype)
{
case GbeRegion:
regionName = "GbE";
break;
case MeRegion:
regionName = "ME";
break;
case BiosRegion:
regionName = "Bios";
break;
case PdrRegion:
regionName = "PDR";
break;
default:
regionName = "Unknown";
debug(tr("Unknown region type"));
};
// Check region base to be in buffer
if (regionBase * 0x1000 >= flashImage.size())
{
debug(tr("%1 region stored in descriptor not found").arg(regionName));
if (twoChips)
debug(tr("Two flash chips installed, so it could be in another flash chip\n"
"Make a dump from another flash chip and open it to view information about %1 region").arg(regionName));
else
debug(tr("One flash chip installed, so it is an error caused by damaged or incomplete dump"));
debug(tr("Absence of %1 region assumed").arg(regionName));
return NULL;
}
// Check region to be fully present in buffer
else if (regionBase * 0x1000 + regionSize > flashImage.size())
{
debug(tr("%s region stored in descriptor overlaps the end of opened file").arg(regionName));
if (twoChips)
debug(tr("Two flash chips installed, so it could be in another flash chip\n"
"Make a dump from another flash chip and open it to view information about %1 region").arg(regionName));
else
debug(tr("One flash chip installed, so it is an error caused by damaged or incomplete dump"));
debug(tr("Absence of %1 region assumed\n").arg(regionName));
return NULL;
}
// Calculate region address
UINT8* region = calculateAddress16((UINT8*) flashImage.constData(), regionBase);
// Add tree item
QByteArray body = flashImage.mid(regionBase * 0x1000, regionSize);
index = addTreeItem(RegionItem, regionSubtype, QByteArray(), body, index);
return region;
}
UINT8 UEFITool::parseBios(const QByteArray & bios, const QModelIndex & parent)
{
// Search for first volume
INT32 prevVolumeIndex = getNextVolumeIndex(bios);
// No volumes found
if (prevVolumeIndex < 0) {
return ERR_VOLUMES_NOT_FOUND;
}
// First volume is not at the beginning of bios space
if (prevVolumeIndex > 0) {
QByteArray padding = bios.left(prevVolumeIndex);
addTreeItem(PaddingItem, 0, QByteArray(), padding, parent);
}
// Search for and parse all volumes
INT32 volumeIndex;
UINT32 prevVolumeSize;
for (volumeIndex = prevVolumeIndex, prevVolumeSize = 0;
volumeIndex >= 0;
prevVolumeIndex = volumeIndex, prevVolumeSize = getVolumeSize(bios, volumeIndex), volumeIndex = getNextVolumeIndex(bios, volumeIndex + prevVolumeSize))
{
// Padding between volumes
if (volumeIndex > prevVolumeIndex + prevVolumeSize) {
UINT32 size = volumeIndex - prevVolumeIndex - prevVolumeSize;
QByteArray padding = bios.mid(prevVolumeIndex + prevVolumeSize, size);
addTreeItem(PaddingItem, 0, QByteArray(), padding, parent);
}
// Populate volume header
EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) (bios.constData() + volumeIndex);
//Check that volume is fully present in input
if (volumeIndex + volumeHeader->FvLength > bios.size()) {
debug(tr("Volume overlaps the end of input buffer"));
return ERR_INVALID_VOLUME;
}
// Check volume revision and alignment
UINT32 alignment;
if (volumeHeader->Revision == 1) {
// Aquire alignment bits
bool alignmentCap = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_CAP;
bool alignment2 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_2;
bool alignment4 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_4;
bool alignment8 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_8;
bool alignment16 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_16;
bool alignment32 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_32;
bool alignment64 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_64;
bool alignment128 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_128;
bool alignment256 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_256;
bool alignment512 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_512;
bool alignment1k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_1K;
bool alignment2k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_2K;
bool alignment4k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_4K;
bool alignment8k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_8K;
bool alignment16k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_16K;
bool alignment32k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_32K;
bool alignment64k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_64K;
// Check alignment setup
if (!alignmentCap &&
( alignment2 || alignment4 || alignment8 || alignment16
|| alignment32 || alignment64 || alignment128 || alignment256
|| alignment512 || alignment1k || alignment2k || alignment4k
|| alignment8k || alignment16k || alignment32k || alignment64k))
debug("Incompatible revision 1 volume alignment setup");
// Assume that smaller alignment value consumes greater
alignment = 0x01;
if (alignment2)
alignment = 0x02;
else if (alignment4)
alignment = 0x04;
else if (alignment8)
alignment = 0x08;
else if (alignment16)
alignment = 0x10;
else if (alignment32)
alignment = 0x20;
else if (alignment64)
alignment = 0x40;
else if (alignment128)
alignment = 0x80;
else if (alignment256)
alignment = 0x100;
else if (alignment512)
alignment = 0x200;
else if (alignment1k)
alignment = 0x400;
else if (alignment2k)
alignment = 0x800;
else if (alignment4k)
alignment = 0x1000;
else if (alignment8k)
alignment = 0x2000;
else if (alignment16k)
alignment = 0x4000;
else if (alignment32k)
alignment = 0x8000;
else if (alignment64k)
alignment = 0x10000;
// Check alignment
if (volumeIndex % alignment) {
debug(tr("Unaligned revision 1 volume"));
}
}
else if (volumeHeader->Revision == 2) {
// Aquire alignment
alignment = pow(2, (volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16);
// Check alignment
if (volumeIndex % alignment) {
debug(tr("Unaligned revision 2 volume"));
}
}
else
debug(tr("Unknown volume revision (%1)").arg(volumeHeader->Revision));
// Check filesystem GUID to be known
// Do not parse volume with unknown FFS, because parsing will fail
bool parseCurrentVolume = true;
// FFS GUID v1
if (QByteArray((const char*) &volumeHeader->FileSystemGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_FILE_SYSTEM_GUID) {
// Code can be added here
}
// FFS GUID v2
else if (QByteArray((const char*) &volumeHeader->FileSystemGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_FILE_SYSTEM2_GUID) {
// Code can be added here
}
// Other GUID
else {
//info = info.append(tr("File system: unknown\n"));
debug(tr("Unknown file system (%1)").arg(guidToQString(volumeHeader->FileSystemGuid)));
parseCurrentVolume = false;
}
// Check attributes
// Determine erase polarity
bool erasePolarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY;
// Check header checksum by recalculating it
if (!calculateChecksum16((UINT8*) volumeHeader, volumeHeader->HeaderLength)) {
debug(tr("Volume header checksum is invalid"));
}
// Check for presence of extended header, only if header revision is not 1
UINT32 headerSize;
if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) {
EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER*) ((UINT8*) volumeHeader + volumeHeader->ExtHeaderOffset);
headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize;
} else {
headerSize = volumeHeader->HeaderLength;
}
// Adding tree item
QByteArray header = bios.mid(volumeIndex, headerSize);
QByteArray body = bios.mid(volumeIndex + headerSize, volumeHeader->FvLength - headerSize);
QModelIndex index = addTreeItem(VolumeItem, 0, header, body, parent);
// Parse volume
if (parseCurrentVolume) {
UINT32 result = parseVolume(bios.mid(volumeIndex + headerSize, volumeHeader->FvLength - headerSize), headerSize, volumeHeader->Revision, erasePolarity, index);
if (result)
debug(tr("Volume parsing failed (%1)").arg(result));
}
}
return ERR_SUCCESS;
}
INT32 UEFITool::getNextVolumeIndex(const QByteArray & bios, INT32 volumeIndex)
{
if (volumeIndex < 0)
return -1;
INT32 nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeIndex);
if (nextIndex < EFI_FV_SIGNATURE_OFFSET) {
return -1;
}
return nextIndex - EFI_FV_SIGNATURE_OFFSET;
}
UINT32 UEFITool::getVolumeSize(const QByteArray & bios, INT32 volumeIndex)
{
// Populate volume header
EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) (bios.constData() + volumeIndex);
// Check volume signature
if (QByteArray((const char*) &volumeHeader->Signature, sizeof(volumeHeader->Signature)) != EFI_FV_SIGNATURE)
return 0;
return volumeHeader->FvLength;
}
UINT8 UEFITool::parseVolume(const QByteArray & volume, UINT32 volumeBase, UINT8 revision, bool erasePolarity, const QModelIndex & parent)
{
// Construct empty byte based on erasePolarity value
// Native char type is used because QByteArray.count() takes it
char empty = erasePolarity ? '\xFF' : '\x00';
// Search for and parse all files
INT32 fileIndex = 0;
while (fileIndex >= 0) {
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) (volume.constData() + fileIndex);
QByteArray file = volume.mid(fileIndex, uint24ToUint32(fileHeader->Size));
QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER));
// Check file size to at least sizeof(EFI_FFS_FILE_HEADER)
if (file.size() < sizeof(EFI_FFS_FILE_HEADER))
{
debug(tr("File with invalid size"));
return ERR_INVALID_FILE;
}
// We are at empty space in the end of volume
if (header.count(empty) == header.size()) {
QByteArray body = volume.right(volume.size() - fileIndex);
addTreeItem(PaddingItem, 0, QByteArray(), body, parent);
break;
}
// 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((UINT8*) tempFileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1);
if (fileHeader->IntegrityCheck.Checksum.Header != calculated)
{
debug(tr("%1: stored header checksum %2 differs from calculated %3")
.arg(guidToQString(fileHeader->Name))
.arg(fileHeader->IntegrityCheck.Checksum.Header, 2, 16, QChar('0'))
.arg(calculated, 2, 16, QChar('0')));
}
// Check data checksum, if no tail was found
// 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(revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)
bufferSize -= sizeof(UINT16);
calculated = calculateChecksum8((UINT8*)(file.constData() + sizeof(EFI_FFS_FILE_HEADER)), bufferSize);
if (fileHeader->IntegrityCheck.Checksum.File != calculated) {
debug(tr("%1: stored data checksum %2 differs from calculated %3")
.arg(guidToQString(fileHeader->Name))
.arg(fileHeader->IntegrityCheck.Checksum.File, 2, 16, QChar('0'))
.arg(calculated, 2, 16, QChar('0')));
}
}
// Data checksum must be one of predefined values
else {
if (fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM &&fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2) {
debug(tr("%1: stored data checksum %2 differs from standard value")
.arg(guidToQString(fileHeader->Name))
.arg(fileHeader->IntegrityCheck.Checksum.File, 2, 16, QChar('0')));
}
}
// Check file alignment
UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
UINT32 alignment = pow(2, alignmentPower);
if ((volumeBase + fileIndex + sizeof(EFI_FFS_FILE_HEADER)) % alignment) {
debug(tr("%1: unaligned file").arg(guidToQString(fileHeader->Name)));
}
// Get file body
QByteArray body = file.right(file.size() - sizeof(EFI_FFS_FILE_HEADER));
// For files in Revision 1 volumes, check for file tail presence
if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)
{
//Check file tail;
UINT16* tail = (UINT16*) body.right(sizeof(UINT16)).constData();
if (!fileHeader->IntegrityCheck.TailReference == *tail)
debug(tr("%1: file tail value %2 is not a bitwise not of %3 stored in file header")
.arg(guidToQString(fileHeader->Name))
.arg(*tail, 4, 16, QChar('0'))
.arg(fileHeader->IntegrityCheck.TailReference, 4, 16, QChar('0')));
// Remove tail from file body
body = body.left(body.size() - sizeof(UINT16));
}
// Parse current file by default
bool parseCurrentFile = true;
// Raw files can hide volumes inside them
// So try to parse them as bios space
bool parseAsBios = false;
// Check file type
//!TODO: add more file specific checks
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:
parseCurrentFile = false;
break;
default:
parseCurrentFile = false;
debug(tr("Unknown file type (%1)").arg(fileHeader->Type, 2, 16, QChar('0')));
};
// Check for empty file
if (body.count(empty) == body.size())
{
// No need to parse empty files
parseCurrentFile = false;
}
// Add tree item
QModelIndex index = addTreeItem(FileItem, fileHeader->Type, header, body, parent);
// Parse file
if (parseCurrentFile) {
if (parseAsBios) {
UINT32 result = parseBios(body, index);
if (result && result != ERR_VOLUMES_NOT_FOUND)
debug(tr("Parse file as BIOS failed (%1)").arg(result));
}
else {
UINT32 result = parseFile(body, revision, erasePolarity, index);
if (result)
debug(tr("Parse file as FFS failed (%1)").arg(result));
}
}
// Move to next file
fileIndex += file.size();
fileIndex = ALIGN8(fileIndex);
// Exit from loop if no files left
if (fileIndex >= volume.size())
fileIndex = -1;
}
return ERR_SUCCESS;
}
UINT8 UEFITool::parseFile(const QByteArray & file, UINT8 revision, bool erasePolarity, const QModelIndex & parent)
{
// Search for and parse all sections
INT32 sectionIndex = 0;
while(sectionIndex >= 0)
{
EFI_COMMON_SECTION_HEADER* sectionHeader = (EFI_COMMON_SECTION_HEADER*) (file.constData() + sectionIndex);
UINT32 sectionSize = uint24ToUint32(sectionHeader->Size);
// This declarations must be here because of the nature of switch statement
EFI_COMPRESSION_SECTION* compressedSectionHeader;
EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader;
QByteArray header;
QByteArray body;
UINT32 decompressedSize;
UINT32 scratchSize;
UINT8* decompressed;
UINT8* scratch;
VOID* data;
UINT32 dataSize;
QModelIndex index;
UINT32 result;
UINT32 shittySectionSize;
EFI_COMMON_SECTION_HEADER* shittySectionHeader;
switch (sectionHeader->Type)
{
// Encapsulated sections
case EFI_SECTION_COMPRESSION:
compressedSectionHeader = (EFI_COMPRESSION_SECTION*) sectionHeader;
header = file.mid(sectionIndex, sizeof(EFI_COMPRESSION_SECTION));
// Try to decompress this section
switch (compressedSectionHeader->CompressionType)
{
case EFI_NOT_COMPRESSED:
body = file.mid(sectionIndex + sizeof(EFI_COMPRESSION_SECTION), compressedSectionHeader->UncompressedLength);
index = addTreeItem(SectionItem, CompressionSection, header, body, parent);
// Parse stored file
result = parseFile(body, revision, erasePolarity, index);
if (result)
debug(tr("Stored section can not be parsed as file (%1)").arg(result));
break;
case EFI_STANDARD_COMPRESSION:
//Must be Tiano for all revisions, needs checking
body = file.mid(sectionIndex + sizeof(EFI_COMPRESSION_SECTION), sectionSize - sizeof(EFI_COMPRESSION_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
// Get buffer sizes
data = (VOID*) (file.constData() + sectionIndex + sizeof(EFI_COMPRESSION_SECTION));
dataSize = uint24ToUint32(sectionHeader->Size) - sizeof(EFI_COMPRESSION_SECTION);
if (TianoGetInfo(data, dataSize, &decompressedSize, &scratchSize) != ERR_SUCCESS
|| decompressedSize != compressedSectionHeader->UncompressedLength)
debug(tr("TianoGetInfo failed"));
else {
decompressed = new UINT8[decompressedSize];
scratch = new UINT8[scratchSize];
// Decompress section data
if (TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize) != ERR_SUCCESS)
debug(tr("TianoDecompress failed"));
else
{
body = QByteArray::fromRawData((const char*) decompressed, decompressedSize);
// Parse stored file
result = parseFile(body, revision, erasePolarity, index);
if (result)
debug(tr("Compressed section with Tiano compression can not be parsed as file (%1)").arg(result));
}
delete[] decompressed;
delete[] scratch;
}
break;
case EFI_CUSTOMIZED_COMPRESSION:
body = file.mid(sectionIndex + sizeof(EFI_COMPRESSION_SECTION), sectionSize - sizeof(EFI_COMPRESSION_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
// Get buffer sizes
data = (VOID*) (file.constData() + sectionIndex + sizeof(EFI_COMPRESSION_SECTION));
dataSize = uint24ToUint32(sectionHeader->Size) - sizeof(EFI_COMPRESSION_SECTION);
if (LzmaGetInfo(data, dataSize, &decompressedSize) != ERR_SUCCESS
|| decompressedSize != compressedSectionHeader->UncompressedLength)
{
// Shitty file 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 = sizeOfSectionHeaderOfType(shittySectionHeader->Type);
data = (VOID*) (file.constData() + sectionIndex + sizeof(EFI_COMPRESSION_SECTION) + shittySectionSize);
dataSize = uint24ToUint32(sectionHeader->Size) - sizeof(EFI_COMPRESSION_SECTION) - shittySectionSize;
if (LzmaGetInfo(data, dataSize, &decompressedSize) != ERR_SUCCESS)
debug(tr("LzmaGetInfo failed"));
}
decompressed = new UINT8[decompressedSize];
// Decompress section data
if (LzmaDecompress(data, dataSize, decompressed) != ERR_SUCCESS)
debug(tr("LzmaDecompress failed"));
else
{
body = QByteArray::fromRawData((const char*) decompressed, decompressedSize);
// Parse stored file
result = parseFile(body, revision, erasePolarity, index);
if (result)
debug(tr("Compressed section with LZMA compression can not be parsed as file (%1)").arg(result));
}
delete[] decompressed;
break;
default:
body = file.mid(sectionIndex + sizeof(EFI_COMPRESSION_SECTION), sectionSize - sizeof(EFI_COMPRESSION_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
debug(tr("Compressed section with unknown compression type found (%1)").arg(compressedSectionHeader->CompressionType));
}
break;
case EFI_SECTION_GUID_DEFINED:
header = file.mid(sectionIndex, sizeof(EFI_GUID_DEFINED_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_GUID_DEFINED_SECTION), sectionSize - sizeof(EFI_GUID_DEFINED_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
// Parse section body as file
guidDefinedSectionHeader = (EFI_GUID_DEFINED_SECTION*) (header.constData());
body = file.mid(sectionIndex + guidDefinedSectionHeader->DataOffset, sectionSize - guidDefinedSectionHeader->DataOffset);
result = parseFile(body, revision, erasePolarity, index);
if (result)
debug(tr("GUID defined section body can not be parsed as file (%1)").arg(result));
break;
case EFI_SECTION_DISPOSABLE:
header = file.mid(sectionIndex, sizeof(EFI_DISPOSABLE_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_DISPOSABLE_SECTION), sectionSize - sizeof(EFI_DISPOSABLE_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
// Leaf sections
case EFI_SECTION_PE32:
header = file.mid(sectionIndex, sizeof(EFI_PE32_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_PE32_SECTION), sectionSize - sizeof(EFI_PE32_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
case EFI_SECTION_PIC:
header = file.mid(sectionIndex, sizeof(EFI_PIC_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_PIC_SECTION), sectionSize - sizeof(EFI_PIC_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
case EFI_SECTION_TE:
header = file.mid(sectionIndex, sizeof(EFI_TE_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_TE_SECTION), sectionSize - sizeof(EFI_TE_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
case EFI_SECTION_VERSION:
header = file.mid(sectionIndex, sizeof(EFI_VERSION_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_VERSION_SECTION), sectionSize - sizeof(EFI_VERSION_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
case EFI_SECTION_USER_INTERFACE:
header = file.mid(sectionIndex, sizeof(EFI_USER_INTERFACE_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_USER_INTERFACE_SECTION), sectionSize - sizeof(EFI_USER_INTERFACE_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
case EFI_SECTION_COMPATIBILITY16:
header = file.mid(sectionIndex, sizeof(EFI_COMPATIBILITY16_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_COMPATIBILITY16_SECTION), sectionSize - sizeof(EFI_COMPATIBILITY16_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
case EFI_SECTION_FIRMWARE_VOLUME_IMAGE:
header = file.mid(sectionIndex, sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION), sectionSize - sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
// Parse section body as BIOS space
result = parseBios(body, index);
if (result && result != ERR_VOLUMES_NOT_FOUND)
debug(tr("Firmware volume image can not be parsed (%1)").arg(result));
break;
case EFI_SECTION_FREEFORM_SUBTYPE_GUID:
header = file.mid(sectionIndex, sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION), sectionSize - sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
case EFI_SECTION_RAW:
header = file.mid(sectionIndex, sizeof(EFI_RAW_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_RAW_SECTION), sectionSize - sizeof(EFI_RAW_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
// Parse section body as BIOS space
result = parseBios(body, index);
if (result && result != ERR_VOLUMES_NOT_FOUND)
debug(tr("Raw section can not be parsed as BIOS (%1)").arg(result));
break;
break;
case EFI_SECTION_DXE_DEPEX:
header = file.mid(sectionIndex, sizeof(EFI_DXE_DEPEX_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_DXE_DEPEX_SECTION), sectionSize - sizeof(EFI_DXE_DEPEX_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
case EFI_SECTION_PEI_DEPEX:
header = file.mid(sectionIndex, sizeof(EFI_PEI_DEPEX_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_PEI_DEPEX_SECTION), sectionSize - sizeof(EFI_PEI_DEPEX_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
case EFI_SECTION_SMM_DEPEX:
header = file.mid(sectionIndex, sizeof(EFI_SMM_DEPEX_SECTION));
body = file.mid(sectionIndex + sizeof(EFI_SMM_DEPEX_SECTION), sectionSize - sizeof(EFI_SMM_DEPEX_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
break;
default:
debug(tr("Section with unknown type (%1)").arg(sectionHeader->Type, 2, 16, QChar('0')));
header = file.mid(sectionIndex, sizeof(EFI_COMMON_SECTION_HEADER));
body = file.mid(sectionIndex + sizeof(EFI_COMMON_SECTION_HEADER), sectionSize - sizeof(EFI_COMMON_SECTION_HEADER));
index = addTreeItem(SectionItem, sectionHeader->Type, header, body, parent);
}
// Move to next section
sectionIndex += uint24ToUint32(sectionHeader->Size);
sectionIndex = ALIGN4(sectionIndex);
// Exit from loop if no sections left
if (sectionIndex >= file.size())
sectionIndex = -1;
}
return ERR_SUCCESS;
}

View File

@ -26,17 +26,8 @@
#include <QString>
#include <QUrl>
#include <math.h>
#include "treemodel.h"
#include "basetypes.h"
#include "descriptor.h"
#include "ffs.h"
#include "Tiano/EfiTianoCompress.h"
#include "Tiano/EfiTianoDecompress.h"
#include "LZMA/LzmaCompress.h"
#include "LZMA/LzmaDecompress.h"
#include "ffsengine.h"
namespace Ui {
class UEFITool;
@ -55,30 +46,19 @@ public:
private slots:
void init();
void openImageFile();
//void saveImageFile();
void populateUi(const QModelIndex &current/*, const QModelIndex &previous*/);
void resizeTreeViewColums(/*const QModelIndex &index*/);
void saveAll();
void saveBody();
void saveUncompressedFile();
private:
Ui::UEFITool * ui;
TreeModel * treeModel;
FfsEngine* ffsEngine;
QModelIndex currentIndex;
void debug(const QString & text);
void dragEnterEvent(QDragEnterEvent* event);
void dropEvent(QDropEvent* event);
QModelIndex addTreeItem(UINT8 type, UINT8 subtype, const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(),
const QModelIndex & parent = QModelIndex());
UINT8 parseInputFile(const QByteArray & buffer);
UINT8* parseRegion(const QByteArray & flashImage, UINT8 regionSubtype, const UINT16 regionBase, const UINT16 regionLimit, QModelIndex & index);
UINT8 parseBios(const QByteArray & bios, const QModelIndex & parent = QModelIndex());
INT32 getNextVolumeIndex(const QByteArray & bios, INT32 volumeIndex = 0);
UINT32 getVolumeSize(const QByteArray & bios, INT32 volumeIndex);
UINT8 parseVolume(const QByteArray & volume, UINT32 volumeBase, UINT8 revision, bool erasePolarity, const QModelIndex & parent = QModelIndex());
UINT8 parseFile(const QByteArray & file, UINT8 revision, bool erasePolarity, const QModelIndex & parent = QModelIndex());
};
#endif

View File

@ -8,6 +8,7 @@ SOURCES += main.cpp \
uefitool.cpp \
descriptor.cpp \
ffs.cpp \
ffsengine.cpp \
treeitem.cpp \
treemodel.cpp \
LZMA/LzmaCompress.c \
@ -21,7 +22,10 @@ SOURCES += main.cpp \
HEADERS += uefitool.h \
basetypes.h \
descriptor.h \
gbe.h \
me.h \
ffs.h \
ffsengine.h \
treeitem.h \
treeitemtypes.h \
treemodel.h \

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>900</width>
<height>600</height>
<height>700</height>
</rect>
</property>
<property name="sizePolicy">
@ -20,7 +20,7 @@
<bool>true</bool>
</property>
<property name="windowTitle">
<string>UEFITool 0.2.4</string>
<string>UEFITool 0.3.0</string>
</property>
<widget class="QWidget" name="centralWidget">
<property name="sizePolicy">
@ -30,7 +30,7 @@
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<item row="0" column="0">
<widget class="QGroupBox" name="structureGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -70,7 +70,7 @@
</layout>
</widget>
</item>
<item row="1" column="1">
<item row="0" column="1">
<widget class="QGroupBox" name="infoGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
@ -88,6 +88,9 @@
<string>Information</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QPlainTextEdit" name="infoEdit">
<property name="sizePolicy">
@ -119,30 +122,10 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="exportAllButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Export...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="exportBodyButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Export without header...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="debugGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -194,16 +177,160 @@
</layout>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QPushButton" name="fromFileButton">
<property name="text">
<string>Open BIOS image file...</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<widget class="QToolBar" name="toolBar">
<property name="enabled">
<bool>true</bool>
</property>
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionOpenImageFile"/>
<addaction name="separator"/>
<addaction name="actionExtract"/>
<addaction name="actionExtractBody"/>
<addaction name="actionExtractUncompressed"/>
<addaction name="separator"/>
<addaction name="actionInsertBefore"/>
<addaction name="actionInsertAfter"/>
<addaction name="separator"/>
<addaction name="actionReplace"/>
<addaction name="separator"/>
<addaction name="actionDelete"/>
<addaction name="actionReplaceWithPadding"/>
<addaction name="separator"/>
</widget>
<action name="actionInsertAfter">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Insert after</string>
</property>
<property name="toolTip">
<string>Insert an object from file after selected object</string>
</property>
<property name="shortcut">
<string>Ctrl+I</string>
</property>
</action>
<action name="actionInsertBefore">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Insert before</string>
</property>
<property name="toolTip">
<string>Insert an object from file before selected object</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+I</string>
</property>
</action>
<action name="actionReplace">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Replace</string>
</property>
<property name="toolTip">
<string>Replace selected object with an object from file</string>
</property>
<property name="shortcut">
<string>Ctrl+R</string>
</property>
</action>
<action name="actionExtract">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Extract</string>
</property>
<property name="toolTip">
<string>Extract selected object to file</string>
</property>
<property name="shortcut">
<string>Ctrl+E</string>
</property>
</action>
<action name="actionExtractBody">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Extract body</string>
</property>
<property name="toolTip">
<string>Extract selected object without header to file</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+E</string>
</property>
</action>
<action name="actionExtractUncompressed">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Extract uncompressed</string>
</property>
<property name="toolTip">
<string>Extract selected FFS file uncompressing it </string>
</property>
<property name="shortcut">
<string>Ctrl+Alt+E</string>
</property>
</action>
<action name="actionDelete">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="toolTip">
<string>Delete selected object</string>
</property>
<property name="shortcut">
<string>Ctrl+D</string>
</property>
</action>
<action name="actionReplaceWithPadding">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Replace with padding</string>
</property>
<property name="toolTip">
<string>Delete an object by replacing it with padding</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+D</string>
</property>
</action>
<action name="actionOpenImageFile">
<property name="text">
<string>Open image file</string>
</property>
<property name="toolTip">
<string>Open image file</string>
</property>
<property name="shortcut">
<string>Ctrl+O</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>