From 28b985b1f179aea13016a1f068bd56d9f0967bc5 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 2 Nov 2014 11:27:54 +0100 Subject: [PATCH] Version 0.18.9 - padding types changed to Empty and NonEmpty - added apriori files detection and parsing - added depex sections parsing --- basetypes.h | 1 + ffs.h | 41 ++++++++++ ffsengine.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++++----- ffsengine.h | 4 +- types.cpp | 6 +- uefitool.cpp | 2 +- 6 files changed, 235 insertions(+), 22 deletions(-) diff --git a/basetypes.h b/basetypes.h index 896d09d..ddc33d0 100644 --- a/basetypes.h +++ b/basetypes.h @@ -83,6 +83,7 @@ typedef uint16_t CHAR16; #define ERR_PATCH_OFFSET_OUT_OF_BOUNDS 39 #define ERR_INVALID_SYMBOL 40 #define ERR_NOTHING_TO_PATCH 41 +#define ERR_DEPEX_PARSE_FAILED 42 #define ERR_NOT_IMPLEMENTED 0xFF // UDK porting definitions diff --git a/ffs.h b/ffs.h index 4101b60..56fdad4 100644 --- a/ffs.h +++ b/ffs.h @@ -302,6 +302,14 @@ extern const UINT8 ffsAlignmentTable[]; #define EFI_FILE_DELETED 0x10 #define EFI_FILE_HEADER_INVALID 0x20 +// PEI apriori file +const QByteArray EFI_PEI_APRIORI_FILE_GUID +("\x0A\xCC\x45\x1B\x6A\x15\x8A\x42\xAF\x62\x49\x86\x4D\xA0\xE6\xE6", 16); + +// DXE apriori file +const QByteArray EFI_DXE_APRIORI_FILE_GUID +("\xE7\x0E\x51\xFC\xDC\xFF\xD4\x11\xBD\x41\x00\x80\xC7\x3C\x88\x81", 16); + // Volume top file const QByteArray EFI_FFS_VOLUME_TOP_FILE_GUID ("\x2E\x06\xA0\x1B\x79\xC7\x82\x45\x85\x66\x33\x6A\xE8\xF7\x8F\x09", 16); @@ -420,6 +428,39 @@ typedef EFI_COMMON_SECTION_HEADER EFI_USER_INTERFACE_SECTION; //Section routines extern UINT32 sizeOfSectionHeader(EFI_COMMON_SECTION_HEADER* header); +//***************************************************************************** +// EFI Dependency Expression +//***************************************************************************** + +#define EFI_DEP_OPCODE_SIZE 1 + +/// +/// If present, this must be the first and only opcode, +/// EFI_DEP_BEFORE is only used by DXE driver. +/// +#define EFI_DEP_BEFORE 0x00 + +/// +/// If present, this must be the first and only opcode, +/// EFI_DEP_AFTER is only used by DXE driver. +/// +#define EFI_DEP_AFTER 0x01 + +#define EFI_DEP_PUSH 0x02 +#define EFI_DEP_AND 0x03 +#define EFI_DEP_OR 0x04 +#define EFI_DEP_NOT 0x05 +#define EFI_DEP_TRUE 0x06 +#define EFI_DEP_FALSE 0x07 +#define EFI_DEP_END 0x08 + + +/// +/// If present, this must be the first opcode, +/// EFI_DEP_SOR is only used by DXE driver. +/// +#define EFI_DEP_SOR 0x09 + // Restore previous packing rules #pragma pack(pop) diff --git a/ffsengine.cpp b/ffsengine.cpp index ff3d52f..148d7d1 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -162,6 +162,9 @@ QString errorMessage(UINT8 errorCode) case ERR_NOTHING_TO_PATCH: msg = QObject::tr("Nothing to patch"); break; + case ERR_DEPEX_PARSE_FAILED: + msg = QObject::tr("Dependency expression parsing failed"); + break; default: msg = QObject::tr("Unknown error %1").arg(errorCode); break; @@ -621,7 +624,7 @@ UINT8 FfsEngine::parsePdrRegion(const QByteArray & pdr, QModelIndex & index, con // Parse PDR region as BIOS space UINT8 result = parseBios(pdr, index); - if (result && result != ERR_VOLUMES_NOT_FOUND) + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; return ERR_SUCCESS; @@ -969,7 +972,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Parse file QModelIndex fileIndex; result = parseFile(file, fileIndex, empty == '\xFF' ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index); - if (result && result != ERR_VOLUMES_NOT_FOUND) + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) msg(tr("parseVolume: FFS file parsing failed with error \"%1\"").arg(errorMessage(result)), index); // Show messages @@ -1144,7 +1147,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U UINT8 result; if (parseAsBios) { result = parseBios(body, index); - if (result && result != ERR_VOLUMES_NOT_FOUND) + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) msg(tr("parseFile: Parsing file as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); return result; } @@ -1196,6 +1199,112 @@ UINT8 FfsEngine::parseSections(const QByteArray & body, const QModelIndex & pare return ERR_SUCCESS; } +void FfsEngine::parseAprioriRawSection(const QByteArray & body, QString & parsed) +{ + parsed.clear(); + + UINT32 count = body.size() / sizeof(EFI_GUID); + if (count > 0) { + for (UINT32 i = 0; i < count; i++) { + EFI_GUID* guid = (EFI_GUID*)body.data() + i; + parsed += tr("\n%1").arg(guidToQString(*guid)); + } + } +} + +UINT8 FfsEngine::parseDepexSection(const QByteArray & body, QString & parsed) +{ + parsed.clear(); + // Check data to be present + if (!body.size()) + return ERR_INVALID_PARAMETER; + + EFI_GUID * guid; + UINT8* current = (UINT8*)body.data(); + + // Special cases of first opcode + switch (*current) { + case EFI_DEP_BEFORE: + if (body.size() != 2*EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) + return ERR_DEPEX_PARSE_FAILED; + guid = (EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); + parsed += tr("\nBEFORE %1").arg(guidToQString(*guid)); + current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); + if (*current != EFI_DEP_END) + return ERR_DEPEX_PARSE_FAILED; + return ERR_SUCCESS; + case EFI_DEP_AFTER: + if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) + return ERR_DEPEX_PARSE_FAILED; + guid = (EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); + parsed += tr("\nAFTER %1").arg(guidToQString(*guid)); + current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); + if (*current != EFI_DEP_END) + return ERR_DEPEX_PARSE_FAILED; + return ERR_SUCCESS; + case EFI_DEP_SOR: + if (body.size() <= 2 * EFI_DEP_OPCODE_SIZE) { + return ERR_DEPEX_PARSE_FAILED; + } + parsed += tr("\nSOR"); + current += EFI_DEP_OPCODE_SIZE; + break; + default: + break; + } + + // Parse the rest of depex + while (current - (UINT8*)body.data() < body.size()) { + switch (*current) { + case EFI_DEP_BEFORE: + case EFI_DEP_AFTER: + case EFI_DEP_SOR: + return ERR_DEPEX_PARSE_FAILED; + case EFI_DEP_PUSH: + // Check that the rest of depex has correct size + if (body.size() - (current - (UINT8*)body.data()) <= EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) { + parsed.clear(); + return ERR_DEPEX_PARSE_FAILED; + } + guid = (EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); + parsed += tr("\nPUSH %1").arg(guidToQString(*guid)); + current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); + break; + case EFI_DEP_AND: + parsed += tr("\nAND"); + current += EFI_DEP_OPCODE_SIZE; + break; + case EFI_DEP_OR: + parsed += tr("\nOR"); + current += EFI_DEP_OPCODE_SIZE; + break; + case EFI_DEP_NOT: + parsed += tr("\nNOT"); + current += EFI_DEP_OPCODE_SIZE; + break; + case EFI_DEP_TRUE: + parsed += tr("\nTRUE"); + current += EFI_DEP_OPCODE_SIZE; + break; + case EFI_DEP_FALSE: + parsed += tr("\nFALSE"); + current += EFI_DEP_OPCODE_SIZE; + break; + case EFI_DEP_END: + parsed += tr("\nEND"); + current += EFI_DEP_OPCODE_SIZE; + // Check that END is the last opcode + if (current - (UINT8*)body.data() < body.size()) { + parsed.clear(); + return ERR_DEPEX_PARSE_FAILED; + } + break; + } + } + + return ERR_SUCCESS; +} + UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { EFI_COMMON_SECTION_HEADER* sectionHeader = (EFI_COMMON_SECTION_HEADER*)(section.constData()); @@ -1372,13 +1481,40 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c return result; } break; - // Leaf sections + + // Leaf sections + case EFI_SECTION_DXE_DEPEX: + case EFI_SECTION_PEI_DEPEX: + case EFI_SECTION_SMM_DEPEX: { + bool msgDepexParseFailed = false; + headerSize = sizeOfSectionHeader(sectionHeader); + header = section.left(headerSize); + body = section.mid(headerSize, sectionSize - headerSize); + + // Get info + info = tr("Type: 0x%1\nSize: 0x%2") + .arg(sectionHeader->Type, 2, 16, QChar('0')) + .arg(body.size(), 6, 16, QChar('0')); + + // Parse dependency expression + QString str; + result = parseDepexSection(body, str); + if (result) + msgDepexParseFailed = true; + else if (str.count()) + info += tr("\nParsed expression:%1").arg(str); + + // Add tree item + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + + // Show messages + if (msgDepexParseFailed) + msg(tr("parseSection: dependency expression parsing failed"), index); + } + break; case EFI_SECTION_PE32: case EFI_SECTION_TE: case EFI_SECTION_PIC: - case EFI_SECTION_DXE_DEPEX: - case EFI_SECTION_PEI_DEPEX: - case EFI_SECTION_SMM_DEPEX: case EFI_SECTION_COMPATIBILITY16: { headerSize = sizeOfSectionHeader(sectionHeader); header = section.left(headerSize); @@ -1466,13 +1602,14 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c // Parse section body as BIOS space result = parseBios(body, index); - if (result && result != ERR_VOLUMES_NOT_FOUND) { + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) { msg(tr("parseSection: Parsing firmware volume image section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); return result; } } break; case EFI_SECTION_RAW: { + bool parsed = false; header = section.left(sizeof(EFI_RAW_SECTION)); body = section.mid(sizeof(EFI_RAW_SECTION), sectionSize - sizeof(EFI_RAW_SECTION)); @@ -1481,14 +1618,46 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .arg(sectionHeader->Type, 2, 16, QChar('0')) .arg(body.size(), 6, 16, QChar('0')); + // Check for apriori file + QModelIndex parentFile = model->findParentOfType(parent, Types::File); + QByteArray parentFileGuid = model->header(parentFile).left(sizeof(EFI_GUID)); + if (parentFileGuid == EFI_PEI_APRIORI_FILE_GUID) { + // Mark file as parsed + parsed = true; + + // Parse apriori file list + QString str; + parseAprioriRawSection(body, str); + if (str.count()) + info += tr("\nFile list:%1").arg(str); + + // Rename parent file + model->setTextString(parentFile, tr("PEI apriori file")); + } + else if (parentFileGuid == EFI_DXE_APRIORI_FILE_GUID) { + // Mark file as parsed + parsed = true; + + // Parse apriori file list + QString str; + parseAprioriRawSection(body, str); + if (str.count()) + info += tr("\nFile list:%1").arg(str); + + // Rename parent file + model->setTextString(parentFile, tr("DXE apriori file")); + } + // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Parse section body as BIOS space - result = parseBios(body, index); - if (result && result != ERR_VOLUMES_NOT_FOUND) { - msg(tr("parseSection: Parsing raw section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); - return result; + if (!parsed) { + result = parseBios(body, index); + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) { + msg(tr("parseSection: Parsing raw section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); + return result; + } } } break; @@ -1543,7 +1712,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte return ERR_NOT_IMPLEMENTED; } - if (result && result != ERR_VOLUMES_NOT_FOUND) + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; // Set action @@ -1601,7 +1770,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte // Parse file result = parseFile(created, fileIndex, erasePolarity ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index, mode); - if (result && result != ERR_VOLUMES_NOT_FOUND) + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; // Set action @@ -1652,7 +1821,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte // Parse section QModelIndex sectionIndex; result = parseSection(created, sectionIndex, index, mode); - if (result && result != ERR_VOLUMES_NOT_FOUND) + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; // Set create action @@ -1678,7 +1847,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte // Parse section QModelIndex sectionIndex; result = parseSection(created, sectionIndex, index, mode); - if (result && result != ERR_VOLUMES_NOT_FOUND) + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; // Set create action @@ -1698,7 +1867,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte // Parse section QModelIndex sectionIndex; result = parseSection(created, sectionIndex, index, mode); - if (result && result != ERR_VOLUMES_NOT_FOUND) + if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; // Set create action diff --git a/ffsengine.h b/ffsengine.h index b65bdd9..24ea2fe 100644 --- a/ffsengine.h +++ b/ffsengine.h @@ -111,6 +111,8 @@ private: // Parsing helpers UINT8 getPaddingType(const QByteArray & padding); + void parseAprioriRawSection(const QByteArray & body, QString & parsed); + UINT8 parseDepexSection(const QByteArray & body, QString & parsed); UINT8 findNextVolume(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); UINT8 getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize); UINT8 getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize); @@ -124,7 +126,7 @@ private: UINT8 getBase(const QByteArray& file, UINT32& base); UINT8 getEntryPoint(const QByteArray& file, UINT32 &entryPoint); UINT8 rebase(QByteArray & executable, const UINT32 base); - void rebasePeiFiles(const QModelIndex & index); + void rebasePeiFiles(const QModelIndex & index); // Patch routines UINT8 patchVtf(QByteArray &vtf); diff --git a/types.cpp b/types.cpp index 2d78275..dc0d4d2 100644 --- a/types.cpp +++ b/types.cpp @@ -71,11 +71,11 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) return QObject::tr("Unknown"); case Types::Padding: if (subtype == Subtypes::ZeroPadding) - return QObject::tr("0x00s"); + return QObject::tr("Empty(0)"); else if (subtype == Subtypes::OnePadding) - return QObject::tr("0xFFs"); + return QObject::tr("Empty(1)"); else if (subtype == Subtypes::DataPadding) - return QObject::tr("Non-UEFI data"); + return QObject::tr("NonEmpty"); else return ""; case Types::Volume: diff --git a/uefitool.cpp b/uefitool.cpp index 5be84ab..dce7433 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.18.8")) +version(tr("0.18.9")) { clipboard = QApplication::clipboard();