UT 0.20.8

- data after the latest region of Intel image is in tree now
- added Intel, Lenovo and Toshiba-specific capsule GUIDs to the list of
known GUIDs
- fixed bogus "File with invalid size" message while working on almost
full volumes
- pressing Cancel on "Open in new window" dialog now works as expected

Big thanks to Lordkag for spotting most of the issues (#31).
This commit is contained in:
Nikolaj Schlej 2015-08-30 10:52:19 +02:00
parent 63e5a4dd1c
commit 9c4ddbec62
6 changed files with 159 additions and 61 deletions

View file

@ -85,6 +85,7 @@ typedef unsigned int UINTN;
#define ERR_INVALID_SYMBOL 40 #define ERR_INVALID_SYMBOL 40
#define ERR_NOTHING_TO_PATCH 41 #define ERR_NOTHING_TO_PATCH 41
#define ERR_DEPEX_PARSE_FAILED 42 #define ERR_DEPEX_PARSE_FAILED 42
#define ERR_TRUNCATED_IMAGE 43
#define ERR_NOT_IMPLEMENTED 0xFF #define ERR_NOT_IMPLEMENTED 0xFF
// UDK porting definitions // UDK porting definitions

32
ffs.h
View file

@ -32,7 +32,7 @@ extern QString sectionTypeToQString(const UINT8 type);
//***************************************************************************** //*****************************************************************************
// EFI Capsule // EFI Capsule
//***************************************************************************** //*****************************************************************************
// Capsule header // Standard EFI Capsule header
typedef struct _EFI_CAPSULE_HEADER { typedef struct _EFI_CAPSULE_HEADER {
EFI_GUID CapsuleGuid; EFI_GUID CapsuleGuid;
UINT32 HeaderSize; UINT32 HeaderSize;
@ -49,16 +49,36 @@ typedef struct _EFI_CAPSULE_HEADER {
const QByteArray EFI_CAPSULE_GUID const QByteArray EFI_CAPSULE_GUID
("\xBD\x86\x66\x3B\x76\x0D\x30\x40\xB7\x0E\xB5\x51\x9E\x2F\xC5\xA0", 16); ("\xBD\x86\x66\x3B\x76\x0D\x30\x40\xB7\x0E\xB5\x51\x9E\x2F\xC5\xA0", 16);
// Intel 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
("\x8B\xA6\x3C\x4A\x23\x77\xFB\x48\x80\x3D\x57\x8C\xC1\xFE\xC4\x4D", 16);
// Toshiba EFI Capsule header
typedef struct _TOSHIBA_CAPSULE_HEADER {
EFI_GUID CapsuleGuid;
UINT32 HeaderSize;
UINT32 FullSize;
UINT32 Flags;
} TOSHIBA_CAPSULE_HEADER;
// Toshiba capsule GUID
const QByteArray TOSHIBA_CAPSULE_GUID
("\x62\x70\xE0\x3B\x51\x1D\xD2\x45\x83\x2B\xF0\x93\x25\x7E\xD4\x61", 16);
// AMI Aptio extended capsule header // AMI Aptio extended capsule header
typedef struct _APTIO_CAPSULE_HEADER { typedef struct _APTIO_CAPSULE_HEADER {
EFI_CAPSULE_HEADER CapsuleHeader; EFI_CAPSULE_HEADER CapsuleHeader;
UINT16 RomImageOffset; // offset in bytes from the beginning of the capsule header to the start of UINT16 RomImageOffset; // offset in bytes from the beginning of the capsule header to the start of
// the capsule volume // the capsule volume
//!TODO: Enable certificate and ROM layout reading //!TODO: Enable certificate and ROM layout reading
//UINT16 RomLayoutOffset; // offset to the table of the module descriptors in the capsule's volume //UINT16 RomLayoutOffset; // offset to the table of the module descriptors in the capsule's volume
// that are included in the signature calculation // that are included in the signature calculation
//FW_CERTIFICATE FWCert; //FW_CERTIFICATE FWCert;
//ROM_AREA RomAreaMap[1]; //ROM_AREA RomAreaMap[1];
} APTIO_CAPSULE_HEADER; } APTIO_CAPSULE_HEADER;
// AMI Aptio signed extended capsule GUID // AMI Aptio signed extended capsule GUID
@ -252,8 +272,8 @@ extern UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize);
// Integrity check // Integrity check
typedef union { typedef union {
struct { struct {
UINT8 Header; UINT8 Header;
UINT8 File; UINT8 File;
} Checksum; } Checksum;
UINT16 TailReference; // Revision 1 UINT16 TailReference; // Revision 1
UINT16 Checksum16; // Revision 2 UINT16 Checksum16; // Revision 2

View file

@ -75,6 +75,7 @@ QString errorMessage(UINT8 errorCode)
case ERR_INVALID_SYMBOL: return QObject::tr("Invalid symbol"); case ERR_INVALID_SYMBOL: return QObject::tr("Invalid symbol");
case ERR_NOTHING_TO_PATCH: return QObject::tr("Nothing to patch"); case ERR_NOTHING_TO_PATCH: return QObject::tr("Nothing to patch");
case ERR_DEPEX_PARSE_FAILED: return QObject::tr("Dependency expression parsing failed"); case ERR_DEPEX_PARSE_FAILED: return QObject::tr("Dependency expression parsing failed");
case ERR_TRUNCATED_IMAGE: return QObject::tr("Image is truncated");
default: return QObject::tr("Unknown error %1").arg(errorCode); default: return QObject::tr("Unknown error %1").arg(errorCode);
} }
} }
@ -146,14 +147,16 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer)
// Check buffer size to be more then or equal to size of EFI_CAPSULE_HEADER // Check buffer size to be more then or equal to size of EFI_CAPSULE_HEADER
if ((UINT32)buffer.size() <= sizeof(EFI_CAPSULE_HEADER)) { if ((UINT32)buffer.size() <= sizeof(EFI_CAPSULE_HEADER)) {
msg(tr("parseImageFile: image file is smaller then minimum size of %1h (%2) bytes").hexarg(sizeof(EFI_CAPSULE_HEADER)).arg(sizeof(EFI_CAPSULE_HEADER))); msg(tr("parseImageFile: image file is smaller then minimum size of %1h (%2) bytes").hexarg(sizeof(EFI_CAPSULE_HEADER)).arg(sizeof(EFI_CAPSULE_HEADER)));
return ERR_INVALID_PARAMETER; return ERR_INVALID_PARAMETER;
} }
// Check buffer for being normal EFI capsule header // Check buffer for being normal EFI capsule header
UINT32 capsuleHeaderSize = 0; UINT32 capsuleHeaderSize = 0;
QModelIndex index; QModelIndex index;
if (buffer.startsWith(EFI_CAPSULE_GUID)) { if (buffer.startsWith(EFI_CAPSULE_GUID)
|| buffer.startsWith(INTEL_CAPSULE_GUID)
|| buffer.startsWith(LENOVO_CAPSULE_GUID)) {
// Get info // Get info
const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_CAPSULE_HEADER*)buffer.constData(); const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_CAPSULE_HEADER*)buffer.constData();
capsuleHeaderSize = capsuleHeader->HeaderSize; capsuleHeaderSize = capsuleHeader->HeaderSize;
@ -170,7 +173,24 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer)
// Add tree item // Add tree item
index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body);
} }
// Check buffer for being Toshiba capsule header
else if (buffer.startsWith(TOSHIBA_CAPSULE_GUID)) {
// Get info
const TOSHIBA_CAPSULE_HEADER* capsuleHeader = (const TOSHIBA_CAPSULE_HEADER*)buffer.constData();
capsuleHeaderSize = capsuleHeader->HeaderSize;
QByteArray header = buffer.left(capsuleHeaderSize);
QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize);
QString name = tr("UEFI capsule");
QString info = tr("Capsule GUID: %1\nFull size: %2h (%3)\nHeader size: %4h (%5)\nImage size: %6h (%7)\nFlags: %8h")
.arg(guidToQString(capsuleHeader->CapsuleGuid))
.hexarg(buffer.size()).arg(buffer.size())
.hexarg(capsuleHeader->HeaderSize).arg(capsuleHeader->HeaderSize)
.hexarg(capsuleHeader->FullSize - capsuleHeader->HeaderSize).arg(capsuleHeader->FullSize - capsuleHeader->HeaderSize)
.hexarg2(capsuleHeader->Flags, 8);
// Add tree item
index = model->addItem(Types::Capsule, Subtypes::ToshibaCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body);
}
// Check buffer for being extended Aptio signed capsule header // Check buffer for being extended Aptio signed capsule header
else if (buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID) || buffer.startsWith(APTIO_UNSIGNED_CAPSULE_GUID)) { else if (buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID) || buffer.startsWith(APTIO_UNSIGNED_CAPSULE_GUID)) {
// Get info // Get info
@ -458,6 +478,34 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in
return result; return 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;
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 = bios.right(intelImage.size() - IntelDataEnd);
// Get info
name = tr("Padding");
info = tr("Full size: %1h (%2)")
.hexarg(padding.size()).arg(padding.size());
// Add tree item
model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, index);
}
return ERR_SUCCESS; return ERR_SUCCESS;
} }
@ -495,7 +543,7 @@ UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const
return ERR_EMPTY_REGION; return ERR_EMPTY_REGION;
// Get info // Get info
QString name = tr("ME/TXE region"); QString name = tr("ME region");
QString info = tr("Full size: %1h (%2)"). QString info = tr("Full size: %1h (%2)").
hexarg(me.size()).arg(me.size()); hexarg(me.size()).arg(me.size());
@ -536,10 +584,10 @@ UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const
// Show messages // Show messages
if (emptyRegion) { if (emptyRegion) {
msg(tr("parseRegion: ME/TXE region is empty"), index); msg(tr("parseRegion: ME region is empty"), index);
} }
else if (!versionFound) { else if (!versionFound) {
msg(tr("parseRegion: ME/TXE region version is unknown, it can be damaged"), index); msg(tr("parseRegion: ME region version is unknown, it can be damaged"), index);
} }
return ERR_SUCCESS; return ERR_SUCCESS;
@ -874,6 +922,22 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
bool msgUnalignedFile = false; bool msgUnalignedFile = false;
bool msgDuplicateGuid = false; bool msgDuplicateGuid = false;
// Check if it's possibly the latest file in the volume
if (volumeSize - fileOffset < sizeof(EFI_FFS_FILE_HEADER)) {
// No files are possible after this point
// All the rest is either free space or non-UEFI data
QByteArray rest = volume.right(volumeSize - fileOffset);
if (rest.count(empty) == rest.size()) { // It's a free space
model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index, mode);
}
else { //It's non-UEFI data
QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index, mode);
msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex);
}
// Exit from loop
break;
}
result = getFileSize(volume, fileOffset, fileSize); result = getFileSize(volume, fileOffset, fileSize);
if (result) if (result)
return result; return result;
@ -1287,9 +1351,9 @@ UINT8 FfsEngine::parseDepexSection(const QByteArray & body, QString & parsed)
return ERR_DEPEX_PARSE_FAILED; return ERR_DEPEX_PARSE_FAILED;
} }
break; break;
default: default:
return ERR_DEPEX_PARSE_FAILED; return ERR_DEPEX_PARSE_FAILED;
break; break;
} }
} }
@ -2657,12 +2721,20 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec
char empty = '\xFF'; char empty = '\xFF';
for (int i = 1; i < model->rowCount(index); i++) { for (int i = 1; i < model->rowCount(index); i++) {
QByteArray region; QByteArray region;
// Padding after the end of all Intel regions
if (model->type(index.child(i, 0)) == Types::Padding) {
region = model->body(index.child(i, 0));
reconstructed.append(region);
offset += region.size();
continue;
}
result = reconstructRegion(index.child(i, 0), region); result = reconstructRegion(index.child(i, 0), region);
if (result) if (result)
return result; return result;
UINT8 type = model->subtype(index.child(i, 0)); switch (model->subtype(index.child(i, 0)))
switch (type)
{ {
case Subtypes::GbeRegion: case Subtypes::GbeRegion:
gbe = region; gbe = region;

View file

@ -24,7 +24,7 @@ QString regionTypeToQString(const UINT8 type)
case Subtypes::GbeRegion: case Subtypes::GbeRegion:
return QObject::tr("GbE"); return QObject::tr("GbE");
case Subtypes::MeRegion: case Subtypes::MeRegion:
return QObject::tr("ME/TXE"); return QObject::tr("ME");
case Subtypes::BiosRegion: case Subtypes::BiosRegion:
return QObject::tr("BIOS"); return QObject::tr("BIOS");
case Subtypes::PdrRegion: case Subtypes::PdrRegion:
@ -95,7 +95,9 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype)
else if (subtype == Subtypes::AptioUnsignedCapsule) else if (subtype == Subtypes::AptioUnsignedCapsule)
return QObject::tr("Aptio unsigned"); return QObject::tr("Aptio unsigned");
else if (subtype == Subtypes::UefiCapsule) else if (subtype == Subtypes::UefiCapsule)
return QObject::tr("UEFI 2.0 "); return QObject::tr("UEFI 2.0");
else if (subtype == Subtypes::ToshibaCapsule)
return QObject::tr("Toshiba");
else else
return QObject::tr("Unknown subtype"); return QObject::tr("Unknown subtype");
case Types::Region: case Types::Region:

View file

@ -54,7 +54,8 @@ namespace Subtypes {
enum CapsuleSubtypes { enum CapsuleSubtypes {
AptioSignedCapsule = 80, AptioSignedCapsule = 80,
AptioUnsignedCapsule, AptioUnsignedCapsule,
UefiCapsule UefiCapsule,
ToshibaCapsule
}; };
enum VolumeSubtypes { enum VolumeSubtypes {

View file

@ -17,7 +17,7 @@
UEFITool::UEFITool(QWidget *parent) : UEFITool::UEFITool(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::UEFITool), ui(new Ui::UEFITool),
version(tr("0.20.7")) version(tr("0.20.8"))
{ {
clipboard = QApplication::clipboard(); clipboard = QApplication::clipboard();
@ -567,6 +567,8 @@ void UEFITool::openImageFile()
void UEFITool::openImageFileInNewWindow() void UEFITool::openImageFileInNewWindow()
{ {
QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file in new window"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.dec);;All files (*)"); QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file in new window"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.dec);;All files (*)");
if (path.trimmed().isEmpty())
return;
QProcess::startDetached(currentProgramPath, QStringList(path)); QProcess::startDetached(currentProgramPath, QStringList(path));
} }