From 92c9754c02f67932d05be451f7fea125f03000f0 Mon Sep 17 00:00:00 2001 From: cyrozap Date: Sat, 28 Mar 2015 22:15:06 -0400 Subject: [PATCH 01/27] Add Travis CI configuration --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a10dcd5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: cpp + +compiler: + - clang + - gcc + +before_install: + - sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa + - sudo apt-get update -qq + - sudo apt-get install -qq qt5-qmake qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev + +script: + - qmake uefitool.pro + - make From 351285dfbfb1d3c40d1424f2f8cdba2f463fddbe Mon Sep 17 00:00:00 2001 From: cyrozap Date: Sat, 28 Mar 2015 22:20:38 -0400 Subject: [PATCH 02/27] Fixing Travis build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a10dcd5..bd25b50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,5 +10,5 @@ before_install: - sudo apt-get install -qq qt5-qmake qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev script: - - qmake uefitool.pro + - qmake -qt=qt5 uefitool.pro - make From 3013398a9d66eeecfb89d7772df5825a103950bb Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 17 May 2015 10:38:04 +0200 Subject: [PATCH 03/27] Engine 0.20.5 - raw file header was included twice during reconstruction as region --- ffsengine.cpp | 9 +++++---- ffsengine.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ffsengine.cpp b/ffsengine.cpp index 2e0a8fe..0029f04 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -2740,7 +2740,7 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec return ERR_NOT_IMPLEMENTED; } -UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconstructed) +UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconstructed, bool includeHeader) { if (!index.isValid()) return ERR_SUCCESS; @@ -2790,7 +2790,8 @@ UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconst } // Reconstruction successful - reconstructed = model->header(index).append(reconstructed); + if (includeHeader) + reconstructed = model->header(index).append(reconstructed); return ERR_SUCCESS; } @@ -3207,9 +3208,9 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, if (model->rowCount(index)) { reconstructed.clear(); // Construct new file body - // File contains raw data, must be parsed as region + // File contains raw data, must be parsed as region without header if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) { - result = reconstructRegion(index, reconstructed); + result = reconstructRegion(index, reconstructed, false); if (result) return result; } diff --git a/ffsengine.h b/ffsengine.h index 41d9e0a..281aa6e 100644 --- a/ffsengine.h +++ b/ffsengine.h @@ -81,7 +81,7 @@ public: UINT8 reconstructImageFile(QByteArray &reconstructed); UINT8 reconstruct(const QModelIndex &index, QByteArray & reconstructed); UINT8 reconstructIntelImage(const QModelIndex& index, QByteArray & reconstructed); - UINT8 reconstructRegion(const QModelIndex& index, QByteArray & reconstructed); + UINT8 reconstructRegion(const QModelIndex& index, QByteArray & reconstructed, bool includeHeader = true); UINT8 reconstructBios(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructVolume(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructFile(const QModelIndex& index, const UINT8 revision, const UINT8 erasePolarity, const UINT32 base, QByteArray& reconstructed); From d78df75de6afec0de8d7f30773ce948f43edf2b8 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 17 May 2015 10:39:31 +0200 Subject: [PATCH 04/27] Update version numbers --- UEFIPatch/uefipatch_main.cpp | 2 +- uefitool.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UEFIPatch/uefipatch_main.cpp b/UEFIPatch/uefipatch_main.cpp index 2ff15c9..6f37b1f 100644 --- a/UEFIPatch/uefipatch_main.cpp +++ b/UEFIPatch/uefipatch_main.cpp @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) result = w.patchFromFile(a.arguments().at(1)); } else { - std::cout << "UEFIPatch 0.3.4 - UEFI image file patching utility" << std::endl << std::endl << + std::cout << "UEFIPatch 0.3.5 - UEFI image file patching utility" << std::endl << std::endl << "Usage: UEFIPatch image_file" << std::endl << std::endl << "Patches will be read from patches.txt file\n"; return ERR_SUCCESS; diff --git a/uefitool.cpp b/uefitool.cpp index 0f344f2..44eaeff 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.20.4")) +version(tr("0.20.5")) { clipboard = QApplication::clipboard(); From c5fec376f765dc2b33ab8856f96ace966a6df8a3 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 5 Jul 2015 13:28:53 +0200 Subject: [PATCH 05/27] Engine 0.20.6 - added support for recalculation of Apple-specific free space offset value stored in volume's ZeroVector. Thanks to osxreverser for reporting the issue #29. - ZeroVectorCRC renamed to AppleCRC32, because it's Apple-specific after all. - ParsingData handling removed from old codebase, because it's only needed for the new engine. --- ffsengine.cpp | 134 +++++++++++++++++++++++++++----------------------- treeitem.cpp | 18 +------ treeitem.h | 7 +-- treemodel.cpp | 31 +----------- treemodel.h | 2 +- types.h | 21 -------- uefitool.cpp | 2 +- 7 files changed, 78 insertions(+), 137 deletions(-) diff --git a/ffsengine.cpp b/ffsengine.cpp index 0029f04..16aea9b 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -221,7 +221,7 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) .hexarg(flashImage.size()).arg(flashImage.size()); // Add tree item - index = model->addItem(Types::Image, Subtypes::UefiImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), flashImage, QByteArray(), index); + index = model->addItem(Types::Image, Subtypes::UefiImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), flashImage, index); return parseBios(flashImage, index); } @@ -359,7 +359,7 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in .arg(descriptorMap->NumberOfIccTableEntries); // Add Intel image tree item - index = model->addItem(Types::Image, Subtypes::IntelImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), intelImage, QByteArray(), parent); + index = model->addItem(Types::Image, Subtypes::IntelImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), intelImage, parent); // Descriptor // Get descriptor info @@ -426,7 +426,7 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in } // Add descriptor tree item - model->addItem(Types::Region, Subtypes::DescriptorRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, QByteArray(), index); + model->addItem(Types::Region, Subtypes::DescriptorRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, index); // Sort regions in ascending order qSort(offsets); @@ -483,7 +483,7 @@ UINT8 FfsEngine::parseGbeRegion(const QByteArray & gbe, QModelIndex & index, con .arg(version->minor); // Add tree item - index = model->addItem(Types::Region, Subtypes::GbeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), gbe, QByteArray(), parent, mode); + index = model->addItem(Types::Region, Subtypes::GbeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), gbe, parent, mode); return ERR_SUCCESS; } @@ -532,7 +532,7 @@ UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const } // Add tree item - index = model->addItem(Types::Region, Subtypes::MeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), me, QByteArray(), parent, mode); + index = model->addItem(Types::Region, Subtypes::MeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), me, parent, mode); // Show messages if (emptyRegion) { @@ -557,7 +557,7 @@ UINT8 FfsEngine::parsePdrRegion(const QByteArray & pdr, QModelIndex & index, con hexarg(pdr.size()).arg(pdr.size()); // Add tree item - index = model->addItem(Types::Region, Subtypes::PdrRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), pdr, QByteArray(), parent, mode); + index = model->addItem(Types::Region, Subtypes::PdrRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), pdr, parent, mode); // Parse PDR region as BIOS space UINT8 result = parseBios(pdr, index); @@ -578,7 +578,7 @@ UINT8 FfsEngine::parseBiosRegion(const QByteArray & bios, QModelIndex & index, c hexarg(bios.size()).arg(bios.size()); // Add tree item - index = model->addItem(Types::Region, Subtypes::BiosRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), bios, QByteArray(), parent, mode); + index = model->addItem(Types::Region, Subtypes::BiosRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), bios, parent, mode); return parseBios(bios, index); } @@ -613,7 +613,7 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) .hexarg(padding.size()).arg(padding.size()); // Add tree item - model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, QByteArray(), parent); + model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, parent); } // Search for and parse all volumes @@ -638,7 +638,7 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) info = tr("Full size: %1h (%2)") .hexarg(padding.size()).arg(padding.size()); // Add tree item - model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, QByteArray(), parent); + model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, parent); } // Get volume size @@ -712,7 +712,7 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) info = tr("Full size: %1h (%2)") .hexarg(padding.size()).arg(padding.size()); // Add tree item - model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, QByteArray(), parent); + model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, parent); } break; } @@ -784,12 +784,6 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co /*volumeFfsVersion = 2;*/ } - // Check for FFS v3 volume - /*if (FFSv3Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { - volumeIsUnknown = false; - volumeFfsVersion = 3; - }*/ - // Check attributes // Determine value of empty byte char empty = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; @@ -804,13 +798,20 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Check for Apple CRC32 in ZeroVector bool volumeHasZVCRC = false; + bool volumeHasZVFSO = false; UINT32 crc32FromZeroVector = *(UINT32*)(volume.constData() + 8); + UINT32 freeSpaceOffsetFromZeroVector = *(UINT32*)(volume.constData() + 12); if (crc32FromZeroVector != 0) { // Calculate CRC32 of the volume body UINT32 crc = crc32(0, (const UINT8*)(volume.constData() + volumeHeader->HeaderLength), volumeSize - volumeHeader->HeaderLength); if (crc == crc32FromZeroVector) { volumeHasZVCRC = true; } + + // Check for free space size in zero vector + if (freeSpaceOffsetFromZeroVector != 0) { + volumeHasZVFSO = true; + } } // Check header checksum by recalculating it @@ -833,11 +834,6 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co .arg(volumeHeader->Revision) .hexarg2(volumeHeader->Attributes, 8) .arg(empty ? "1" : "0"); - - // Apple CRC32 volume - if (volumeHasZVCRC) { - info += tr("\nCRC32 in ZeroVector: valid"); - } // Extended header present if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { @@ -847,21 +843,17 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co .arg(guidToQString(extendedHeader->FvName)); } - // Construct parsing data structure - QByteArray parsingData(sizeof(PARSING_DATA), 0); - PARSING_DATA* pdata = (PARSING_DATA*)parsingData.data(); - pdata->Type = VolumeParsingData; - pdata->Data.Volume.HasZeroVectorCRC = volumeHasZVCRC; - // Add text QString text; if (volumeHasZVCRC) - text += tr("ZeroVectorCRC "); + text += tr("AppleCRC32 "); + if (volumeHasZVFSO) + text += tr("AppleFSO "); // Add tree item QByteArray header = volume.left(headerSize); QByteArray body = volume.mid(headerSize, volumeSize - headerSize); - index = model->addItem(Types::Volume, volumeIsUnknown ? Subtypes::UnknownVolume : Subtypes::Ffs2Volume, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parsingData, parent, mode); + index = model->addItem(Types::Volume, volumeIsUnknown ? Subtypes::UnknownVolume : Subtypes::Ffs2Volume, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parent, mode); // Show messages if (volumeIsUnknown) { @@ -917,16 +909,16 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Add all bytes before as free space... if (i > 0) { QByteArray free = freeSpace.left(i); - model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, QByteArray(), index, mode); + model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, index, mode); } // ... and all bytes after as a padding QByteArray padding = freeSpace.mid(i); - QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, QByteArray(), index, mode); + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, index, mode); msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex); } else { // Add free space element - model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(freeSpace.size()).arg(freeSpace.size()), QByteArray(), freeSpace, QByteArray(), index, mode); + model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(freeSpace.size()).arg(freeSpace.size()), QByteArray(), freeSpace, index, mode); } break; // Exit from loop } @@ -951,14 +943,6 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) msg(tr("parseVolume: FFS file parsing failed with error \"%1\"").arg(errorMessage(result)), index); - - // Construct parsing data structure - QByteArray parsingData(sizeof(PARSING_DATA), 0); - PARSING_DATA* pdata = (PARSING_DATA*)parsingData.data(); - pdata->Type = FileParsingData; - pdata->Data.File.Offset = fileOffset; - model->setParsingData(fileIndex, parsingData); - // Show messages if (msgUnalignedFile) msg(tr("parseVolume: unaligned file %1").arg(guidToQString(fileHeader->Name)), fileIndex); @@ -1108,7 +1092,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U .hexarg2(fileHeader->State, 2); // Add tree item - index = model->addItem(Types::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgInvalidHeaderChecksum) @@ -1137,11 +1121,11 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U // Add all bytes before as free space... if (i > 0) { QByteArray free = body.left(i); - model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, QByteArray(), index, mode); + model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, index, mode); } // ... and all bytes after as a padding QByteArray padding = body.mid(i); - QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, QByteArray(), index, mode); + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, index, mode); // Show message msg(tr("parseFile: non-empty pad-file contents will be destroyed after volume modifications"), dataIndex); @@ -1348,7 +1332,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(compressedSectionHeader->UncompressedLength).arg(compressedSectionHeader->UncompressedLength); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, parent, mode); // Show message if (!parseCurrentSection) @@ -1488,7 +1472,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, parent, mode); // Show messages if (msgUnknownGuid) @@ -1527,7 +1511,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(body.size()).arg(body.size()); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Parse section body result = parseSections(body, index); @@ -1560,7 +1544,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c 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); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgDepexParseFailed) @@ -1600,7 +1584,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(teHeader->ImageBase + teHeader->AddressOfEntryPoint - teFixup); } // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgInvalidSignature) { @@ -1683,7 +1667,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgInvalidDosSignature) { @@ -1719,7 +1703,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(body.size()).arg(body.size()); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); } break; case EFI_SECTION_FREEFORM_SUBTYPE_GUID: { @@ -1736,7 +1720,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .arg(guidToQString(fsgHeader->SubTypeGuid)); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Rename section model->setName(index, guidToQString(fsgHeader->SubTypeGuid)); @@ -1758,7 +1742,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .arg(QString::fromUtf16((const ushort*)body.constData())); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); } break; case EFI_SECTION_USER_INTERFACE: { @@ -1775,7 +1759,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .arg(text); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Rename parent file model->setText(model->findParentOfType(parent, Types::File), text); @@ -1793,7 +1777,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(body.size()).arg(body.size()); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Parse section body as BIOS space result = parseBios(body, index); @@ -1846,7 +1830,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Parse section body as BIOS space if (!parsed) { @@ -1874,7 +1858,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(postcodeHeader->Postcode); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); } break; default: @@ -1888,7 +1872,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(body.size()).arg(body.size()); // Add tree item - index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); msg(tr("parseSection: section with unknown type %1h").hexarg2(sectionHeader->Type, 2), index); } return ERR_SUCCESS; @@ -2829,6 +2813,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon UINT32 volumeSize = header.size() + body.size(); // Reconstruct volume body + UINT32 freeSpaceOffset = 0; if (model->rowCount(index)) { reconstructed.clear(); UINT8 polarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE; @@ -3007,6 +2992,17 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon return ERR_INVALID_VOLUME; } + // Check for free space offset in ZeroVector + if (model->text(index).contains("AppleFSO ")) { + // Align current offset to 8 byte boundary + UINT32 alignment = offset % 8; + freeSpaceOffset = model->header(index).size() + offset; + if (alignment) { + alignment = 8 - alignment; + freeSpaceOffset += alignment; + } + } + // Insert VTF or non-UEFI data to it's correct place if (!vtf.isEmpty()) { // VTF found // Determine correct VTF offset @@ -3108,22 +3104,36 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon reconstructed = header.append(reconstructed); // Recalculate CRC32 in ZeroVector, if needed - const PARSING_DATA* pdata = (const PARSING_DATA*)model->parsingData(index).constData(); - if (pdata->Type == VolumeParsingData && pdata->Data.Volume.HasZeroVectorCRC) { + if (model->text(index).contains("AppleCRC32 ")) { // Get current CRC32 value from volume header - const UINT32 current = *(const UINT32*)(reconstructed.constData() + 8); + const UINT32 currentCrc = *(const UINT32*)(reconstructed.constData() + 8); // Calculate new value UINT32 crc = crc32(0, (const UINT8*)reconstructed.constData() + volumeHeader->HeaderLength, reconstructed.size() - volumeHeader->HeaderLength); // Update the value - if (current != crc) { + if (currentCrc != crc) { *(UINT32*)(reconstructed.data() + 8) = crc; - // Recalculate header checksum again + // Recalculate header checksum volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)reconstructed.data(); volumeHeader->Checksum = 0; volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); } } + // Store new free space offset, if needed + if (model->text(index).contains("AppleFSO ")) { + // Get current CRC32 value from volume header + const UINT32 currentFso = *(const UINT32*)(reconstructed.constData() + 12); + // Update the value + if (freeSpaceOffset != 0 && currentFso != freeSpaceOffset) { + *(UINT32*)(reconstructed.data() + 12) = freeSpaceOffset; + + // Recalculate header checksum + volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)reconstructed.data(); + volumeHeader->Checksum = 0; + volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); + } + } + return ERR_SUCCESS; } diff --git a/treeitem.cpp b/treeitem.cpp index 94514dd..925d95b 100644 --- a/treeitem.cpp +++ b/treeitem.cpp @@ -17,7 +17,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, const QString & name, const QString & text, const QString & info, - const QByteArray & header, const QByteArray & body, const QByteArray & parsingData, + const QByteArray & header, const QByteArray & body, TreeItem *parent) : itemAction(Actions::NoAction), itemType(type), @@ -28,7 +28,6 @@ TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const UINT8 compressio itemInfo(info), itemHeader(header), itemBody(body), - itemParsingData(parsingData), parentItem(parent) { } @@ -184,11 +183,6 @@ QByteArray TreeItem::body() const return itemBody; } -QByteArray TreeItem::parsingData() const -{ - return itemParsingData; -} - bool TreeItem::hasEmptyHeader() const { return itemHeader.isEmpty(); @@ -199,16 +193,6 @@ bool TreeItem::hasEmptyBody() const return itemBody.isEmpty(); } -bool TreeItem::hasEmptyParsingData() const -{ - return itemParsingData.isEmpty(); -} - -void TreeItem::setParsingData(const QByteArray & data) -{ - itemParsingData = data; -} - UINT8 TreeItem::action() const { return itemAction; diff --git a/treeitem.h b/treeitem.h index 8697a75..58015f4 100644 --- a/treeitem.h +++ b/treeitem.h @@ -26,7 +26,7 @@ class TreeItem public: TreeItem(const UINT8 type, const UINT8 subtype = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, const QString &name = QString(), const QString &text = QString(), const QString &info = QString(), - const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QByteArray & parsingData = QByteArray(), + const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), TreeItem *parent = 0); ~TreeItem(); @@ -63,10 +63,6 @@ public: QByteArray body() const; bool hasEmptyBody() const; - QByteArray parsingData() const; - bool hasEmptyParsingData() const; - void setParsingData(const QByteArray & data); - QString info() const; void addInfo(const QString &info); void setInfo(const QString &info); @@ -87,7 +83,6 @@ private: QString itemInfo; QByteArray itemHeader; QByteArray itemBody; - QByteArray itemParsingData; TreeItem *parentItem; }; diff --git a/treemodel.cpp b/treemodel.cpp index ef83561..a82724b 100644 --- a/treemodel.cpp +++ b/treemodel.cpp @@ -178,22 +178,6 @@ bool TreeModel::hasEmptyBody(const QModelIndex &index) const return item->hasEmptyBody(); } -QByteArray TreeModel::parsingData(const QModelIndex &index) const -{ - if (!index.isValid()) - return QByteArray(); - TreeItem *item = static_cast(index.internalPointer()); - return item->parsingData(); -} - -bool TreeModel::hasEmptyParsingData(const QModelIndex &index) const -{ - if (!index.isValid()) - return true; - TreeItem *item = static_cast(index.internalPointer()); - return item->hasEmptyParsingData(); -} - QString TreeModel::name(const QModelIndex &index) const { if (!index.isValid()) @@ -284,20 +268,9 @@ void TreeModel::setAction(const QModelIndex &index, const UINT8 action) emit dataChanged(this->index(0, 0), index); } -void TreeModel::setParsingData(const QModelIndex &index, const QByteArray &data) -{ - if (!index.isValid()) - return; - - TreeItem *item = static_cast(index.internalPointer()); - item->setParsingData(data); - emit dataChanged(this->index(0, 0), index); -} - QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, const QString & name, const QString & text, const QString & info, - const QByteArray & header, const QByteArray & body, const QByteArray & parsingData, - const QModelIndex & parent, const UINT8 mode) + const QByteArray & header, const QByteArray & body, const QModelIndex & parent, const UINT8 mode) { TreeItem *item = 0; TreeItem *parentItem = 0; @@ -318,7 +291,7 @@ QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT } } - TreeItem *newItem = new TreeItem(type, subtype, compression, name, text, info, header, body, parsingData, parentItem); + TreeItem *newItem = new TreeItem(type, subtype, compression, name, text, info, header, body, parentItem); if (mode == CREATE_MODE_APPEND) { emit layoutAboutToBeChanged(); parentItem->appendChild(newItem); diff --git a/treemodel.h b/treemodel.h index 3c8c210..ae9c717 100644 --- a/treemodel.h +++ b/treemodel.h @@ -65,7 +65,7 @@ public: QModelIndex addItem(const UINT8 type, const UINT8 subtype = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, const QString & name = QString(), const QString & text = QString(), const QString & info = QString(), - const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QByteArray & parsingData = QByteArray(), + const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); QModelIndex findParentOfType(const QModelIndex & index, UINT8 type) const; diff --git a/types.h b/types.h index a65b73e..ec605b4 100644 --- a/types.h +++ b/types.h @@ -85,25 +85,4 @@ extern QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype); extern QString compressionTypeToQString(const UINT8 algorithm); extern QString regionTypeToQString(const UINT8 type); -enum ParsingDataTypes { - UnknownParsingData, - VolumeParsingData, - FileParsingData -}; - -typedef union _PARSING_DATA_UNION { - struct _PARSING_DATA_UNION_VOLUME { - bool HasZeroVectorCRC; - } Volume; - - struct _PARSING_DATA_UNION_FILE { - UINT32 Offset; - } File; -} PARSING_DATA_UNION; - -typedef struct _PARSING_DATA { - UINT8 Type; - PARSING_DATA_UNION Data; -} PARSING_DATA; - #endif \ No newline at end of file diff --git a/uefitool.cpp b/uefitool.cpp index 44eaeff..4ff8781 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.20.5")) +version(tr("0.20.6")) { clipboard = QApplication::clipboard(); From 1109e4437909a91032e7278222140b51e14b0304 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Tue, 11 Aug 2015 18:12:12 +0200 Subject: [PATCH 06/27] Create LICENSE.md --- LICENSE.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3f16459 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,23 @@ +Copyright (c) 2015, Nikolaj Schlej +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 63e5a4dd1cf187fe6b62bd0da60529746f2ff7a1 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Thu, 13 Aug 2015 20:28:10 +0200 Subject: [PATCH 07/27] UT 0.20.7 - added "Open in new window..." action --- uefitool.cpp | 20 +++++++++++++++++--- uefitool.h | 4 ++++ uefitool.ui | 12 ++++++++++++ uefitool_main.cpp | 1 + 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/uefitool.cpp b/uefitool.cpp index 4ff8781..14b2e56 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.20.6")) +version(tr("0.20.7")) { clipboard = QApplication::clipboard(); @@ -31,6 +31,7 @@ version(tr("0.20.6")) // Connect signals to slots connect(ui->actionOpenImageFile, SIGNAL(triggered()), this, SLOT(openImageFile())); + connect(ui->actionOpenImageFileInNewWindow, SIGNAL(triggered()), this, SLOT(openImageFileInNewWindow())); connect(ui->actionSaveImageFile, SIGNAL(triggered()), this, SLOT(saveImageFile())); connect(ui->actionSearch, SIGNAL(triggered()), this, SLOT(search())); connect(ui->actionExtract, SIGNAL(triggered()), this, SLOT(extractAsIs())); @@ -83,6 +84,11 @@ UEFITool::~UEFITool() delete searchDialog; } +void UEFITool::setProgramPath(QString path) +{ + currentProgramPath = path; +}; + void UEFITool::init() { // Clear components @@ -358,6 +364,8 @@ void UEFITool::replace(const UINT8 mode) path = QFileDialog::getOpenFileName(this, tr("Select volume file to replace body"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); else if (model->subtype(index) == EFI_SECTION_RAW) path = QFileDialog::getOpenFileName(this, tr("Select raw file to replace body"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); + else if (model->subtype(index) == EFI_SECTION_PE32 || model->subtype(index) == EFI_SECTION_TE || model->subtype(index) == EFI_SECTION_PIC) + path = QFileDialog::getOpenFileName(this, tr("Select EFI executable file to replace body"), currentDir, "EFI executable files (*.efi *.dxe *.pei *.bin);;All files (*)"); else path = QFileDialog::getOpenFileName(this, tr("Select file to replace body"), currentDir, "Binary files (*.bin);;All files (*)"); } @@ -521,7 +529,7 @@ void UEFITool::exit() void UEFITool::saveImageFile() { - QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.efi *.dec);;All files (*)"); + QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.dec);;All files (*)"); if (path.isEmpty()) return; @@ -552,10 +560,16 @@ void UEFITool::saveImageFile() void UEFITool::openImageFile() { - QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.efi *.dec);;All files (*)"); + QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.dec);;All files (*)"); openImageFile(path); } +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 (*)"); + QProcess::startDetached(currentProgramPath, QStringList(path)); +} + void UEFITool::openImageFile(QString path) { if (path.trimmed().isEmpty()) diff --git a/uefitool.h b/uefitool.h index dda7fdc..edac83b 100644 --- a/uefitool.h +++ b/uefitool.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ public: ~UEFITool(); void openImageFile(QString path); + void setProgramPath(QString path); private slots: void init(); @@ -58,6 +60,7 @@ public: void scrollTreeView(QListWidgetItem* item); void openImageFile(); + void openImageFileInNewWindow(); void saveImageFile(); void search(); @@ -95,6 +98,7 @@ private: SearchDialog* searchDialog; QClipboard* clipboard; QString currentDir; + QString currentProgramPath; QQueue messageItems; const QString version; diff --git a/uefitool.ui b/uefitool.ui index 82868da..5b5953a 100644 --- a/uefitool.ui +++ b/uefitool.ui @@ -188,6 +188,7 @@ &File + @@ -522,6 +523,17 @@ Ctrl+Alt+C + + + &Open image file in new window... + + + Open image file in new window + + + Ctrl+Shift+O + + diff --git a/uefitool_main.cpp b/uefitool_main.cpp index e5764c0..a9ac03a 100644 --- a/uefitool_main.cpp +++ b/uefitool_main.cpp @@ -23,6 +23,7 @@ int main(int argc, char *argv[]) a.setApplicationName("UEFITool"); UEFITool w; + w.setProgramPath(a.arguments().at(0)); if (a.arguments().length() > 1) w.openImageFile(a.arguments().at(1)); w.show(); From 9c4ddbec6218302e86955cfc53e7dfcc8f858eca Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 30 Aug 2015 10:52:19 +0200 Subject: [PATCH 08/27] 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). --- basetypes.h | 1 + ffs.h | 32 ++++++++-- ffsengine.cpp | 160 ++++++++++++++++++++++++++++++++++++-------------- types.cpp | 12 ++-- types.h | 3 +- uefitool.cpp | 12 ++-- 6 files changed, 159 insertions(+), 61 deletions(-) diff --git a/basetypes.h b/basetypes.h index 083267b..e8589ad 100644 --- a/basetypes.h +++ b/basetypes.h @@ -85,6 +85,7 @@ typedef unsigned int UINTN; #define ERR_INVALID_SYMBOL 40 #define ERR_NOTHING_TO_PATCH 41 #define ERR_DEPEX_PARSE_FAILED 42 +#define ERR_TRUNCATED_IMAGE 43 #define ERR_NOT_IMPLEMENTED 0xFF // UDK porting definitions diff --git a/ffs.h b/ffs.h index 8b7da95..0bb8221 100644 --- a/ffs.h +++ b/ffs.h @@ -32,7 +32,7 @@ extern QString sectionTypeToQString(const UINT8 type); //***************************************************************************** // EFI Capsule //***************************************************************************** -// Capsule header +// Standard EFI Capsule header typedef struct _EFI_CAPSULE_HEADER { EFI_GUID CapsuleGuid; UINT32 HeaderSize; @@ -49,16 +49,36 @@ typedef struct _EFI_CAPSULE_HEADER { const QByteArray EFI_CAPSULE_GUID ("\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 typedef struct _APTIO_CAPSULE_HEADER { 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 //!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 //FW_CERTIFICATE FWCert; - //ROM_AREA RomAreaMap[1]; + //ROM_AREA RomAreaMap[1]; } APTIO_CAPSULE_HEADER; // AMI Aptio signed extended capsule GUID @@ -252,8 +272,8 @@ extern UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); // Integrity check typedef union { struct { - UINT8 Header; - UINT8 File; + UINT8 Header; + UINT8 File; } Checksum; UINT16 TailReference; // Revision 1 UINT16 Checksum16; // Revision 2 diff --git a/ffsengine.cpp b/ffsengine.cpp index 16aea9b..d516019 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -75,6 +75,7 @@ QString errorMessage(UINT8 errorCode) case ERR_INVALID_SYMBOL: return QObject::tr("Invalid symbol"); case ERR_NOTHING_TO_PATCH: return QObject::tr("Nothing to patch"); case ERR_DEPEX_PARSE_FAILED: return QObject::tr("Dependency expression parsing failed"); + case ERR_TRUNCATED_IMAGE: return QObject::tr("Image is truncated"); 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 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; } // Check buffer for being normal EFI capsule header UINT32 capsuleHeaderSize = 0; 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 const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_CAPSULE_HEADER*)buffer.constData(); capsuleHeaderSize = capsuleHeader->HeaderSize; @@ -170,7 +173,24 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) // Add tree item 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 else if (buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID) || buffer.startsWith(APTIO_UNSIGNED_CAPSULE_GUID)) { // Get info @@ -192,7 +212,7 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) // Add tree item index = model->addItem(Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); - + // Show message about possible Aptio signature break if (signedCapsule) { msg(tr("parseImageFile: Aptio capsule signature may become invalid after image modifications"), index); @@ -458,6 +478,34 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in 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; } @@ -495,7 +543,7 @@ UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const return ERR_EMPTY_REGION; // Get info - QString name = tr("ME/TXE region"); + QString name = tr("ME region"); QString info = tr("Full size: %1h (%2)"). hexarg(me.size()).arg(me.size()); @@ -536,10 +584,10 @@ UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const // Show messages if (emptyRegion) { - msg(tr("parseRegion: ME/TXE region is empty"), index); + msg(tr("parseRegion: ME region is empty"), index); } 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; @@ -611,7 +659,7 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) 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, parent); } @@ -777,7 +825,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Check for volume structure to be known bool volumeIsUnknown = true; /*UINT8 volumeFfsVersion = 0;*/ - + // Check for FFS v2 volume if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { volumeIsUnknown = false; @@ -864,7 +912,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co if (msgInvalidChecksum) { msg(tr("parseVolume: volume header checksum is invalid"), index); } - + // Search for and parse all files UINT32 fileOffset = headerSize; UINT32 fileSize; @@ -874,6 +922,22 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co bool msgUnalignedFile = 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); if (result) return result; @@ -907,7 +971,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co i = ALIGN8(i) - 8; // Add all bytes before as free space... - if (i > 0) { + if (i > 0) { QByteArray free = freeSpace.left(i); model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, index, mode); } @@ -1079,7 +1143,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U QString info; if (fileHeader->Type != EFI_FV_FILETYPE_PAD) name = guidToQString(fileHeader->Name); - else + else name = parseAsNonEmptyPadFile ? tr("Non-empty pad-file") : tr("Pad-file"); info = tr("File GUID: %1\nType: %2h\nAttributes: %3h\nFull size: %4h (%5)\nHeader size: %6h (%7)\nBody size: %8h (%9)\nState: %10h") @@ -1103,7 +1167,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U msg(tr("parseFile: invalid tail value"), index); if (msgInvalidType) msg(tr("parseFile: unknown file type %1h").arg(fileHeader->Type, 2), index); - + // No parsing needed if (!parseCurrentFile) return ERR_SUCCESS; @@ -1126,10 +1190,10 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U // ... and all bytes after as a padding QByteArray padding = body.mid(i); QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, index, mode); - + // Show message msg(tr("parseFile: non-empty pad-file contents will be destroyed after volume modifications"), dataIndex); - + return ERR_SUCCESS; } @@ -1211,7 +1275,7 @@ UINT8 FfsEngine::parseDepexSection(const QByteArray & body, QString & parsed) const EFI_GUID * guid; const UINT8* current = (const UINT8*)body.constData(); - + // Special cases of first opcode switch (*current) { case EFI_DEP_BEFORE: @@ -1241,7 +1305,7 @@ UINT8 FfsEngine::parseDepexSection(const QByteArray & body, QString & parsed) break; } - // Parse the rest of depex + // Parse the rest of depex while (current - (const UINT8*)body.constData() < body.size()) { switch (*current) { case EFI_DEP_BEFORE: @@ -1287,9 +1351,9 @@ UINT8 FfsEngine::parseDepexSection(const QByteArray & body, QString & parsed) return ERR_DEPEX_PARSE_FAILED; } break; - default: - return ERR_DEPEX_PARSE_FAILED; - break; + default: + return ERR_DEPEX_PARSE_FAILED; + break; } } @@ -1379,11 +1443,11 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c // Tiano compressed section if (QByteArray((const char*)&guidDefinedSectionHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_TIANO) { algorithm = COMPRESSION_ALGORITHM_UNKNOWN; - + result = decompress(body, EFI_STANDARD_COMPRESSION, processed, &algorithm); if (result) parseCurrentSection = false; - + if (algorithm == COMPRESSION_ALGORITHM_TIANO) { info += tr("\nCompression type: Tiano"); info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); @@ -1392,17 +1456,17 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c info += tr("\nCompression type: EFI 1.1"); info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); } - else + else info += tr("\nCompression type: unknown"); } // LZMA compressed section else if (QByteArray((const char*)&guidDefinedSectionHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_LZMA) { algorithm = COMPRESSION_ALGORITHM_UNKNOWN; - + result = decompress(body, EFI_CUSTOMIZED_COMPRESSION, processed, &algorithm); if (result) parseCurrentSection = false; - + if (algorithm == COMPRESSION_ALGORITHM_LZMA) { info += tr("\nCompression type: LZMA"); info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); @@ -1570,7 +1634,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c if (teHeader->Signature != EFI_IMAGE_TE_SIGNATURE) { info += tr("\nSignature: %1h, invalid").hexarg2(teHeader->Signature, 4); msgInvalidSignature = true; - } + } else { info += tr("\nSignature: %1h\nMachine type: %2\nNumber of sections: %3\nSubsystem: %4h\nStrippedSize: %5h (%6)\nBaseOfCode: %7h\nRelativeEntryPoint: %8h\nImageBase: %9h\nEntryPoint: %10h") .hexarg2(teHeader->Signature, 4) @@ -1833,7 +1897,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Parse section body as BIOS space - if (!parsed) { + 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); @@ -1922,7 +1986,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte } else if (type == Types::Volume) { QByteArray volume; - if (header.isEmpty()) // Whole volume + if (header.isEmpty()) // Whole volume volume.append(body); else { // Body only volume.append(model->header(index)).append(body); @@ -1936,7 +2000,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte result = parseVolume(volume, fileIndex, index, mode); if (result) return result; - + // Set action model->setAction(fileIndex, action); } @@ -1954,7 +2018,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte QByteArray newHeader = header; EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newHeader.data(); - + // Correct file size UINT8 tailSize = fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT ? sizeof(UINT16) : 0; uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + body.size() + tailSize, fileHeader->Size); @@ -2478,7 +2542,7 @@ UINT8 FfsEngine::compress(const QByteArray & data, const UINT8 algorithm, QByteA return ERR_STANDARD_COMPRESSION_FAILED; } compressedData = QByteArray((const char*)compressed, compressedSize); - + // Check that compressed data can be decompressed normally QByteArray decompressed; if (decompress(compressedData, EFI_STANDARD_COMPRESSION, decompressed, NULL) == ERR_SUCCESS @@ -2498,7 +2562,7 @@ UINT8 FfsEngine::compress(const QByteArray & data, const UINT8 algorithm, QByteA return ERR_STANDARD_COMPRESSION_FAILED; } compressedData = QByteArray((const char*)compressed, compressedSize); - + // New functions will be trusted here, because another check will reduce performance delete[] compressed; return ERR_SUCCESS; @@ -2657,12 +2721,20 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec char empty = '\xFF'; for (int i = 1; i < model->rowCount(index); i++) { 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); if (result) return result; - UINT8 type = model->subtype(index.child(i, 0)); - switch (type) + switch (model->subtype(index.child(i, 0))) { case Subtypes::GbeRegion: gbe = region; @@ -2761,7 +2833,7 @@ UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconst if (reconstructed.size() > model->body(index).size()) { msg(tr("reconstructRegion: reconstructed region size %1h (%2) is bigger then original %3h (%4)") .hexarg(reconstructed.size()).arg(reconstructed.size()) - .hexarg(model->body(index).size()).arg(reconstructed.size()), + .hexarg(model->body(index).size()).arg(reconstructed.size()), index); return ERR_INVALID_PARAMETER; } @@ -2799,7 +2871,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon reconstructed.clear(); return ERR_SUCCESS; } - else if (model->action(index) == Actions::Replace || + else if (model->action(index) == Actions::Replace || model->action(index) == Actions::Rebuild) { QByteArray header = model->header(index); QByteArray body = model->body(index); @@ -2811,7 +2883,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon // Get volume size UINT32 volumeSize = header.size() + body.size(); - + // Reconstruct volume body UINT32 freeSpaceOffset = 0; if (model->rowCount(index)) { @@ -2926,7 +2998,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon // Pad file if (fileHeader->Type == EFI_FV_FILETYPE_PAD) { padFileGuid = file.left(sizeof(EFI_GUID)); - + // Parse non-empty pad file if (model->rowCount(index.child(i, 0))) { //TODO: handle it @@ -2981,7 +3053,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon // Get non-UEFI data and it's offset nonUefiData = model->body(index.child(i + 1, 0)); nonUefiDataOffset = body.size() - nonUefiData.size(); - break; + break; } } } @@ -3133,7 +3205,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); } } - + return ERR_SUCCESS; } @@ -3435,7 +3507,7 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)model->body(index).constData(); teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); }*/ - + if (base) { result = rebase(reconstructed, base - teFixup + header.size()); if (result) { @@ -3966,7 +4038,7 @@ UINT8 FfsEngine::getBase(const QByteArray& file, UINT32& base) // Populate TE header const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)file.constData(); //!TODO: add handling - base = teHeader->ImageBase; + base = teHeader->ImageBase; } return ERR_SUCCESS; @@ -4041,10 +4113,10 @@ UINT8 FfsEngine::recursiveDump(const QModelIndex & index, const QString & path, return ERR_INVALID_PARAMETER; QDir dir; - if (guid.isEmpty() || + if (guid.isEmpty() || guidToQString(*(const EFI_GUID*)model->header(index).constData()) == guid || guidToQString(*(const EFI_GUID*)model->header(model->findParentOfType(index, Types::File)).constData()) == guid) { - + if (dir.cd(path)) return ERR_DIR_ALREADY_EXIST; @@ -4079,7 +4151,7 @@ UINT8 FfsEngine::recursiveDump(const QModelIndex & index, const QString & path, file.write(info.toLatin1()); file.close(); dumped = true; - } + } UINT8 result; for (int i = 0; i < model->rowCount(index); i++) { diff --git a/types.cpp b/types.cpp index 5904e9e..17ca51f 100644 --- a/types.cpp +++ b/types.cpp @@ -24,7 +24,7 @@ QString regionTypeToQString(const UINT8 type) case Subtypes::GbeRegion: return QObject::tr("GbE"); case Subtypes::MeRegion: - return QObject::tr("ME/TXE"); + return QObject::tr("ME"); case Subtypes::BiosRegion: return QObject::tr("BIOS"); case Subtypes::PdrRegion: @@ -80,7 +80,7 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) return QObject::tr("Non-empty"); else return QObject::tr("Unknown subtype"); - case Types::Volume: + case Types::Volume: if (subtype == Subtypes::UnknownVolume) return QObject::tr("Unknown"); else if (subtype == Subtypes::Ffs2Volume) @@ -89,14 +89,16 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) return QObject::tr("FFSv3"); else return QObject::tr("Unknown subtype"); - case Types::Capsule: + case Types::Capsule: if (subtype == Subtypes::AptioSignedCapsule) return QObject::tr("Aptio signed"); else if (subtype == Subtypes::AptioUnsignedCapsule) return QObject::tr("Aptio unsigned"); else if (subtype == Subtypes::UefiCapsule) - return QObject::tr("UEFI 2.0 "); - else + return QObject::tr("UEFI 2.0"); + else if (subtype == Subtypes::ToshibaCapsule) + return QObject::tr("Toshiba"); + else return QObject::tr("Unknown subtype"); case Types::Region: return regionTypeToQString(subtype); diff --git a/types.h b/types.h index ec605b4..64df300 100644 --- a/types.h +++ b/types.h @@ -54,7 +54,8 @@ namespace Subtypes { enum CapsuleSubtypes { AptioSignedCapsule = 80, AptioUnsignedCapsule, - UefiCapsule + UefiCapsule, + ToshibaCapsule }; enum VolumeSubtypes { diff --git a/uefitool.cpp b/uefitool.cpp index 14b2e56..5a4ef55 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -16,8 +16,8 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), -ui(new Ui::UEFITool), -version(tr("0.20.7")) +ui(new Ui::UEFITool), +version(tr("0.20.8")) { clipboard = QApplication::clipboard(); @@ -85,8 +85,8 @@ UEFITool::~UEFITool() } void UEFITool::setProgramPath(QString path) -{ - currentProgramPath = path; +{ + currentProgramPath = path; }; void UEFITool::init() @@ -455,7 +455,7 @@ void UEFITool::extract(const UINT8 mode) case Types::Capsule: path = QFileDialog::getSaveFileName(this, tr("Save capsule body to image file"), currentDir, "Image files (*.rom *.bin);;All files (*)"); break; - case Types::Volume: + case Types::Volume: path = QFileDialog::getSaveFileName(this, tr("Save volume body to file"), currentDir, "Volume body files (*.vbd *.bin);;All files (*)"); break; case Types::File: { @@ -567,6 +567,8 @@ void UEFITool::openImageFile() 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 (*)"); + if (path.trimmed().isEmpty()) + return; QProcess::startDetached(currentProgramPath, QStringList(path)); } From aa80837bf5ce68af01f1f432011b546cd8762bef Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 6 Sep 2015 23:46:26 +0200 Subject: [PATCH 09/27] UT 0.21.0 - added support for new Intel descriptor type, based on [this](http://review.coreboot.org/gitweb?p=coreboot.git;a=commit;h=1f7fd720c81755144423f2d4062c39cc651adc0a) coreboot commit, thanks to lordkag for issue #32 - solved a bug with incorrect volume free space item placement during volume replace, now works as expected - solved an issue with incorrect Aptio capsule parsing introduced in 0.20.8 --- descriptor.h | 131 ++++++++++++++-------- ffs.h | 4 - ffsengine.cpp | 292 +++++++++++++++++++++++++++++++++++++------------- ffsengine.h | 1 + types.cpp | 2 + types.h | 3 +- uefitool.cpp | 2 +- 7 files changed, 309 insertions(+), 126 deletions(-) diff --git a/descriptor.h b/descriptor.h index 82ff6da..989761b 100644 --- a/descriptor.h +++ b/descriptor.h @@ -17,7 +17,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "basetypes.h" // Make sure we use right packing rules -#pragma pack(push,1) +#pragma pack(push, 1) // Flash descriptor header typedef struct _FLASH_DESCRIPTOR_HEADER { @@ -34,37 +34,38 @@ typedef struct _FLASH_DESCRIPTOR_HEADER { // Descriptor map // Base fields are storing bits [11:4] of actual base addresses, all other bits are 0 typedef struct _FLASH_DESCRIPTOR_MAP { - UINT8 ComponentBase; // 0x03 on most machines - UINT8 NumberOfFlashChips; // Zero-based number of flash chips installed on board - UINT8 RegionBase; // 0x04 on most machines - UINT8 NumberOfRegions; // Zero-based number of flash regions (descriptor is always included) - UINT8 MasterBase; // 0x06 on most machines - UINT8 NumberOfMasters; // Zero-based number of flash masters - UINT8 PchStrapsBase; // 0x10 on most machines - UINT8 NumberOfPchStraps; // One-based number of UINT32s to read as PCH Straps, min=0, max=255 (1 Kb) - UINT8 ProcStrapsBase; // 0x20 on most machines - UINT8 NumberOfProcStraps; // Number of PROC straps to be read, can be 0 or 1 - UINT8 IccTableBase; // 0x21 on most machines - UINT8 NumberOfIccTableEntries; // 0x00 on most machines - UINT8 DmiTableBase; // 0x25 on most machines - UINT8 NumberOfDmiTableEntries; // 0x00 on most machines - UINT16 ReservedZero; // Still unknown, zeros in all descriptors I have seen + // FLMAP0 + UINT32 ComponentBase : 8; + UINT32 NumberOfFlashChips : 2; // Zero-based number of flash chips installed on board + UINT32 : 6; + UINT32 RegionBase : 8; + UINT32 : 8; + // FLMAP 1 + UINT32 MasterBase : 8; + UINT32 NumberOfMasters : 2; + UINT32 : 6; + UINT32 PchStrapsBase : 8; + UINT32 NumberOfPchStraps : 8; // One-based number of UINT32s to read as PCH straps, min=0, max=255 (1 Kb) + // FLMAP 2 + UINT32 ProcStrapsBase : 8; + UINT32 NumberOfProcStraps : 8; // One-based number of UINT32s to read as processor straps, min=0, max=255 (1 Kb) + UINT32: 16; } FLASH_DESCRIPTOR_MAP; // Component section // Flash parameters DWORD structure typedef struct _FLASH_PARAMETERS { - UINT8 FirstChipDensity : 3; - UINT8 SecondChipDensity : 3; - UINT8 ReservedZero0 : 2; // Still unknown, zeros in all descriptors I have seen - UINT8 ReservedZero1 : 8; // Still unknown, zeros in all descriptors I have seen - UINT8 ReservedZero2 : 4; // Still unknown, zeros in all descriptors I have seen + UINT8 FirstChipDensity : 4; + UINT8 SecondChipDensity : 4; + UINT8 : 8; + UINT8 : 1; + UINT8 ReadClockFreqency : 3; // Hardcoded value of 20 Mhz (000b) in v1 descriptors and 17 Mhz (110b) in v2 ones UINT8 FastReadEnabled : 1; UINT8 FastReadFreqency : 3; UINT8 FlashReadStatusFrequency : 3; UINT8 FlashWriteFrequency : 3; UINT8 DualOutputFastReadSupported : 1; - UINT8 ReservedZero3 : 1; // Still unknown, zero in all descriptors I have seen + UINT8 : 1; } FLASH_PARAMETERS; // Flash densities @@ -74,11 +75,16 @@ typedef struct _FLASH_PARAMETERS { #define FLASH_DENSITY_4MB 0x03 #define FLASH_DENSITY_8MB 0x04 #define FLASH_DENSITY_16MB 0x05 +#define FLASH_DENSITY_32MB 0x06 +#define FLASH_DENSITY_64MB 0x07 +#define FLASH_DENSITY_UNUSED 0x0F // Flash frequencies -#define FLASH_FREQUENCY_20MHZ 0x00 -#define FLASH_FREQUENCY_33MHZ 0x01 -#define FLASH_FREQUENCY_50MHZ 0x04 +#define FLASH_FREQUENCY_20MHZ 0x00 +#define FLASH_FREQUENCY_33MHZ 0x01 +#define FLASH_FREQUENCY_48MHZ 0x02 +#define FLASH_FREQUENCY_50MHZ_30MHZ 0x04 +#define FLASH_FREQUENCY_17MHZ 0x06 // Component section structure typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION { @@ -87,24 +93,45 @@ typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION { UINT8 InvalidInstruction1; // 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 } FLASH_DESCRIPTOR_COMPONENT_SECTION; +// Component section structure +typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION_V2 { + FLASH_PARAMETERS FlashParameters; + UINT8 InvalidInstruction0; // Instructions for SPI chip, that must not be executed, like FLASH ERASE + UINT8 InvalidInstruction1; // + UINT8 InvalidInstruction2; // + UINT8 InvalidInstruction3; // + UINT8 InvalidInstruction4; // + UINT8 InvalidInstruction5; // + UINT8 InvalidInstruction6; // + UINT8 InvalidInstruction7; // +} FLASH_DESCRIPTOR_COMPONENT_SECTION_V2; + // Region section // All base and limit register are storing upper part of actual UINT32 base and limit // If limit is zero - region is not present typedef struct _FLASH_DESCRIPTOR_REGION_SECTION { - UINT16 ReservedZero; // Still unknown, zero in all descriptors I have seen + UINT16 :16; UINT16 FlashBlockEraseSize; // Size of block erased by single BLOCK ERASE command - UINT16 BiosBase; - UINT16 BiosLimit; - UINT16 MeBase; - UINT16 MeLimit; - UINT16 GbeBase; - UINT16 GbeLimit; - UINT16 PdrBase; - UINT16 PdrLimit; + UINT16 Region0Base; // BIOS + UINT16 Region0Limit; // + UINT16 Region1Base; // ME + UINT16 Region1Limit; // + UINT16 Region2Base; // GbE + UINT16 Region2Limit; // + UINT16 Region3Base; // PDR + UINT16 Region3Limit; // + UINT16 Region4Base; // Reserved region + UINT16 Region4Limit; // + UINT16 Region5Base; // Reserved region + UINT16 Region5Limit; // + UINT16 Region6Base; // Reserved region + UINT16 Region6Limit; // + UINT16 Region7Base; // Reserved region + UINT16 Region7Limit; // + UINT16 Region8Base; // EC + UINT16 Region8Limit; // } FLASH_DESCRIPTOR_REGION_SECTION; // Flash block erase sizes @@ -115,24 +142,40 @@ typedef struct _FLASH_DESCRIPTOR_REGION_SECTION { // Master section typedef struct _FLASH_DESCRIPTOR_MASTER_SECTION { UINT16 BiosId; - UINT8 BiosRead; - UINT8 BiosWrite; + UINT8 BiosRead; + UINT8 BiosWrite; UINT16 MeId; - UINT8 MeRead; - UINT8 MeWrite; + UINT8 MeRead; + UINT8 MeWrite; UINT16 GbeId; - UINT8 GbeRead; - UINT8 GbeWrite; + UINT8 GbeRead; + UINT8 GbeWrite; } FLASH_DESCRIPTOR_MASTER_SECTION; +// Master section v2 (Skylake+) +typedef struct _FLASH_DESCRIPTOR_MASTER_SECTION_V2 { + UINT32 : 8; + UINT32 BiosRead : 12; + UINT32 BiosWrite : 12; + UINT32 : 8; + UINT32 MeRead : 12; + UINT32 MeWrite : 12; + UINT32 : 8; + UINT32 GbeRead : 12; + UINT32 GbeWrite : 12; + UINT32 :32; + UINT32 : 8; + UINT32 EcRead : 12; + UINT32 EcWrite : 12; +} FLASH_DESCRIPTOR_MASTER_SECTION_V2; + // Region access bits in master section #define FLASH_DESCRIPTOR_REGION_ACCESS_DESC 0x01 #define FLASH_DESCRIPTOR_REGION_ACCESS_BIOS 0x02 #define FLASH_DESCRIPTOR_REGION_ACCESS_ME 0x04 #define FLASH_DESCRIPTOR_REGION_ACCESS_GBE 0x08 #define FLASH_DESCRIPTOR_REGION_ACCESS_PDR 0x10 - -//!TODO: Describe PCH and PROC straps sections, as well as ICC and DMI tables +#define FLASH_DESCRIPTOR_REGION_ACCESS_EC 0x20 // Base address of descriptor upper map #define FLASH_DESCRIPTOR_UPPER_MAP_BASE 0x0EFC diff --git a/ffs.h b/ffs.h index 0bb8221..0adc031 100644 --- a/ffs.h +++ b/ffs.h @@ -53,10 +53,6 @@ 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 -("\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; diff --git a/ffsengine.cpp b/ffsengine.cpp index d516019..985ea38 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -155,8 +155,7 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) UINT32 capsuleHeaderSize = 0; QModelIndex index; if (buffer.startsWith(EFI_CAPSULE_GUID) - || buffer.startsWith(INTEL_CAPSULE_GUID) - || buffer.startsWith(LENOVO_CAPSULE_GUID)) { + || buffer.startsWith(INTEL_CAPSULE_GUID)) { // Get info const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_CAPSULE_HEADER*)buffer.constData(); capsuleHeaderSize = capsuleHeader->HeaderSize; @@ -266,45 +265,36 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor + sizeof(FLASH_DESCRIPTOR_HEADER)); const FLASH_DESCRIPTOR_UPPER_MAP* upperMap = (const FLASH_DESCRIPTOR_UPPER_MAP*)(descriptor + FLASH_DESCRIPTOR_UPPER_MAP_BASE); const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8(descriptor, descriptorMap->RegionBase); - const FLASH_DESCRIPTOR_MASTER_SECTION* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION*)calculateAddress8(descriptor, descriptorMap->MasterBase); + const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8(descriptor, descriptorMap->ComponentBase); - // GbE region - QByteArray gbe; - UINT32 gbeBegin = 0; - UINT32 gbeEnd = 0; - if (regionSection->GbeLimit) { - gbeBegin = calculateRegionOffset(regionSection->GbeBase); - gbeEnd = calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); - gbe = intelImage.mid(gbeBegin, gbeEnd); - gbeEnd += gbeBegin; + // Check descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency + UINT8 descriptorVersion = 0; + if (componentSection->FlashParameters.ReadClockFreqency == FLASH_FREQUENCY_20MHZ) // Old descriptor + descriptorVersion = 1; + else if (componentSection->FlashParameters.ReadClockFreqency == FLASH_FREQUENCY_17MHZ) // Skylake+ descriptor + descriptorVersion = 2; + else { + msg(tr("parseIntelImage: unknown descriptor version with ReadClockFreqency %1h").hexarg(componentSection->FlashParameters.ReadClockFreqency)); + return ERR_INVALID_FLASH_DESCRIPTOR; } + // ME region QByteArray me; UINT32 meBegin = 0; UINT32 meEnd = 0; - if (regionSection->MeLimit) { - meBegin = calculateRegionOffset(regionSection->MeBase); - meEnd = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); + if (regionSection->Region1Limit) { + meBegin = calculateRegionOffset(regionSection->Region1Base); + meEnd = calculateRegionSize(regionSection->Region1Base, regionSection->Region1Limit); me = intelImage.mid(meBegin, meEnd); meEnd += meBegin; } - // PDR region - QByteArray pdr; - UINT32 pdrBegin = 0; - UINT32 pdrEnd = 0; - if (regionSection->PdrLimit) { - pdrBegin = calculateRegionOffset(regionSection->PdrBase); - pdrEnd = calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); - pdr = intelImage.mid(pdrBegin, pdrEnd); - pdrEnd += pdrBegin; - } // BIOS region QByteArray bios; UINT32 biosBegin = 0; UINT32 biosEnd = 0; - if (regionSection->BiosLimit) { - biosBegin = calculateRegionOffset(regionSection->BiosBase); - biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); + if (regionSection->Region0Limit) { + biosBegin = calculateRegionOffset(regionSection->Region0Base); + biosEnd = calculateRegionSize(regionSection->Region0Base, regionSection->Region0Limit); // Check for Gigabyte specific descriptor map if (biosEnd - biosBegin == (UINT32)intelImage.size()) { @@ -322,8 +312,41 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in msg(tr("parseIntelImage: descriptor parsing failed, BIOS region not found in descriptor")); return ERR_INVALID_FLASH_DESCRIPTOR; } + // GbE region + QByteArray gbe; + UINT32 gbeBegin = 0; + UINT32 gbeEnd = 0; + if (regionSection->Region2Limit) { + gbeBegin = calculateRegionOffset(regionSection->Region2Base); + gbeEnd = calculateRegionSize(regionSection->Region2Base, regionSection->Region2Limit); + gbe = intelImage.mid(gbeBegin, gbeEnd); + gbeEnd += gbeBegin; + } + // PDR region + QByteArray pdr; + UINT32 pdrBegin = 0; + UINT32 pdrEnd = 0; + if (regionSection->Region3Limit) { + pdrBegin = calculateRegionOffset(regionSection->Region3Base); + pdrEnd = calculateRegionSize(regionSection->Region3Base, regionSection->Region3Limit); + pdr = intelImage.mid(pdrBegin, pdrEnd); + pdrEnd += pdrBegin; + } + // EC region + QByteArray ec; + UINT32 ecBegin = 0; + UINT32 ecEnd = 0; + if (descriptorVersion == 2) { + if (regionSection->Region8Limit) { + pdrBegin = calculateRegionOffset(regionSection->Region8Base); + pdrEnd = calculateRegionSize(regionSection->Region8Base, regionSection->Region8Limit); + pdr = intelImage.mid(ecBegin, ecEnd); + ecEnd += ecBegin; + } + } // 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; @@ -340,6 +363,11 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in 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; @@ -352,6 +380,11 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in 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; @@ -360,23 +393,35 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in 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; + } // Region map is consistent // Intel image QString name = tr("Intel image"); - QString info = tr("Full size: %1h (%2)\nFlash chips: %3\nRegions: %4\nMasters: %5\nPCH straps: %6\nPROC straps: %7\nICC table entries: %8") + QString info = tr("Full size: %1h (%2)\nFlash chips: %3\nMasters: %4\nPCH straps: %5\nCPU straps: %6\n") .hexarg(intelImage.size()).arg(intelImage.size()) - .arg(descriptorMap->NumberOfFlashChips + 1) // - .arg(descriptorMap->NumberOfRegions + 1) // Zero-based numbers in storage - .arg(descriptorMap->NumberOfMasters + 1) // + .arg(descriptorMap->NumberOfFlashChips + 1) + .arg(descriptorMap->NumberOfMasters + 1) .arg(descriptorMap->NumberOfPchStraps) - .arg(descriptorMap->NumberOfProcStraps) - .arg(descriptorMap->NumberOfIccTableEntries); + .arg(descriptorMap->NumberOfProcStraps); // Add Intel image tree item index = model->addItem(Types::Image, Subtypes::IntelImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), intelImage, parent); @@ -389,49 +434,89 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in // Check regions presence once again QVector offsets; - if (regionSection->GbeLimit) { + if (regionSection->Region2Limit) { offsets.append(gbeBegin); info += tr("\nGbE region offset: %1h").hexarg(gbeBegin); } - if (regionSection->MeLimit) { + if (regionSection->Region1Limit) { offsets.append(meBegin); info += tr("\nME region offset: %1h").hexarg(meBegin); } - if (regionSection->BiosLimit) { + if (regionSection->Region0Limit) { offsets.append(biosBegin); info += tr("\nBIOS region offset: %1h").hexarg(biosBegin); } - if (regionSection->PdrLimit) { + if (regionSection->Region3Limit) { offsets.append(pdrBegin); info += tr("\nPDR region offset: %1h").hexarg(pdrBegin); } + if (descriptorVersion == 2 && regionSection->Region8Limit) { + offsets.append(ecBegin); + info += tr("\nEC region offset: %1h").hexarg(ecBegin); + } // Region access settings - info += tr("\nRegion access settings:"); - info += tr("\nBIOS:%1%2h ME:%3%4h GbE:%5%6h") - .hexarg2(masterSection->BiosRead, 2) - .hexarg2(masterSection->BiosWrite, 2) - .hexarg2(masterSection->MeRead, 2) - .hexarg2(masterSection->MeWrite, 2) - .hexarg2(masterSection->GbeRead, 2) - .hexarg2(masterSection->GbeWrite, 2); + if (descriptorVersion == 1) { + const FLASH_DESCRIPTOR_MASTER_SECTION* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION*)calculateAddress8(descriptor, descriptorMap->MasterBase); + info += tr("\nRegion access settings:"); + info += tr("\nBIOS:%1%2h ME:%3%4h GbE:%5%6h") + .hexarg2(masterSection->BiosRead, 2) + .hexarg2(masterSection->BiosWrite, 2) + .hexarg2(masterSection->MeRead, 2) + .hexarg2(masterSection->MeWrite, 2) + .hexarg2(masterSection->GbeRead, 2) + .hexarg2(masterSection->GbeWrite, 2); - // BIOS access table - info += tr("\nBIOS access table:"); - info += tr("\n Read Write"); - info += tr("\nDesc %1 %2") - .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ") - .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No "); - info += tr("\nBIOS Yes Yes"); - info += tr("\nME %1 %2") - .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ") - .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No "); - info += tr("\nGbE %1 %2") - .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ") - .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No "); - info += tr("\nPDR %1 %2") - .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ") - .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No "); + // BIOS access table + info += tr("\nBIOS access table:"); + info += tr("\n Read Write"); + info += tr("\nDesc %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No "); + info += tr("\nBIOS Yes Yes"); + info += tr("\nME %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No "); + info += tr("\nGbE %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No "); + info += tr("\nPDR %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No "); + } + else if (descriptorVersion == 2) { + const FLASH_DESCRIPTOR_MASTER_SECTION_V2* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION_V2*)calculateAddress8(descriptor, descriptorMap->MasterBase); + info += tr("\nRegion access settings:"); + info += tr("\nBIOS: %1h %2h ME: %3h %4h\nGbE: %5h %6h EC: %7h %8h") + .hexarg2(masterSection->BiosRead, 3) + .hexarg2(masterSection->BiosWrite, 3) + .hexarg2(masterSection->MeRead, 3) + .hexarg2(masterSection->MeWrite, 3) + .hexarg2(masterSection->GbeRead, 3) + .hexarg2(masterSection->GbeWrite, 3) + .hexarg2(masterSection->EcRead, 3) + .hexarg2(masterSection->EcWrite, 3); + + // BIOS access table + info += tr("\nBIOS access table:"); + info += tr("\n Read Write"); + info += tr("\nDesc %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No "); + info += tr("\nBIOS Yes Yes"); + info += tr("\nME %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No "); + info += tr("\nGbE %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No "); + info += tr("\nPDR %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No "); + info += tr("\nEC %1 %2") + .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_EC ? "Yes " : "No ") + .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_EC ? "Yes " : "No "); + } // VSCC table const VSCC_TABLE_ENTRY* vsccTableEntry = (const VSCC_TABLE_ENTRY*)(descriptor + ((UINT16)upperMap->VsccTableBase << 4)); @@ -474,6 +559,11 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in QModelIndex pdrIndex; result = parsePdrRegion(pdr, pdrIndex, index); } + // Parse EC region + else if (descriptorVersion == 2 && offsets.at(i) == ecBegin) { + QModelIndex ecIndex; + result = parseEcRegion(ec, ecIndex, index); + } if (result) return result; } @@ -489,6 +579,8 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in 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") @@ -615,6 +707,23 @@ UINT8 FfsEngine::parsePdrRegion(const QByteArray & pdr, QModelIndex & index, con return ERR_SUCCESS; } +UINT8 FfsEngine::parseEcRegion(const QByteArray & ec, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) +{ + // Check sanity + if (ec.isEmpty()) + return ERR_EMPTY_REGION; + + // Get info + QString name = tr("EC region"); + QString info = tr("Full size: %1h (%2)"). + hexarg(ec.size()).arg(ec.size()); + + // Add tree item + index = model->addItem(Types::Region, Subtypes::EcRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), ec, parent, mode); + + return ERR_SUCCESS; +} + UINT8 FfsEngine::parseBiosRegion(const QByteArray & bios, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { if (bios.isEmpty()) @@ -928,10 +1037,10 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // 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); + 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); } 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); + 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); msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex); } // Exit from loop @@ -973,16 +1082,16 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Add all bytes before as free space... if (i > 0) { QByteArray free = freeSpace.left(i); - model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, index, mode); + model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, index); } // ... and all bytes after as a padding QByteArray padding = freeSpace.mid(i); - QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, index, mode); + QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, index); msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex); } else { // Add free space element - model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(freeSpace.size()).arg(freeSpace.size()), QByteArray(), freeSpace, index, mode); + model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(freeSpace.size()).arg(freeSpace.size()), QByteArray(), freeSpace, index); } break; // Exit from loop } @@ -1989,7 +2098,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte if (header.isEmpty()) // Whole volume volume.append(body); else { // Body only - volume.append(model->header(index)).append(body); + volume.append(header).append(body); INT32 sizeDiff = model->body(index).size() - body.size(); if (sizeDiff > 0) { const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)model->header(index).constData(); @@ -2704,17 +2813,37 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor.constData() + sizeof(FLASH_DESCRIPTOR_HEADER)); const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8((const UINT8*)descriptor.constData(), descriptorMap->RegionBase); QByteArray gbe; - UINT32 gbeBegin = calculateRegionOffset(regionSection->GbeBase); - UINT32 gbeEnd = gbeBegin + calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); + UINT32 gbeBegin = calculateRegionOffset(regionSection->Region2Base); + UINT32 gbeEnd = gbeBegin + calculateRegionSize(regionSection->Region2Base, regionSection->Region2Limit); QByteArray me; - UINT32 meBegin = calculateRegionOffset(regionSection->MeBase); - UINT32 meEnd = meBegin + calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); + UINT32 meBegin = calculateRegionOffset(regionSection->Region1Base); + UINT32 meEnd = meBegin + calculateRegionSize(regionSection->Region1Base, regionSection->Region1Limit); QByteArray bios; - UINT32 biosBegin = calculateRegionOffset(regionSection->BiosBase); - UINT32 biosEnd = biosBegin + calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); + UINT32 biosBegin = calculateRegionOffset(regionSection->Region0Base); + UINT32 biosEnd = biosBegin + calculateRegionSize(regionSection->Region0Base, regionSection->Region0Limit); QByteArray pdr; - UINT32 pdrBegin = calculateRegionOffset(regionSection->PdrBase); - UINT32 pdrEnd = pdrBegin + calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); + UINT32 pdrBegin = calculateRegionOffset(regionSection->Region3Base); + UINT32 pdrEnd = pdrBegin + calculateRegionSize(regionSection->Region3Base, regionSection->Region3Limit); + QByteArray ec; + UINT32 ecBegin = 0; + UINT32 ecEnd = 0; + + const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8((const UINT8*)descriptor.constData(), descriptorMap->ComponentBase); + // Check descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency + UINT8 descriptorVersion = 0; + if (componentSection->FlashParameters.ReadClockFreqency == FLASH_FREQUENCY_20MHZ) { // Old descriptor + descriptorVersion = 1; + } + else if (componentSection->FlashParameters.ReadClockFreqency == FLASH_FREQUENCY_17MHZ) { // Skylake+ descriptor + descriptorVersion = 2; + ecBegin = calculateRegionOffset(regionSection->Region8Base); + ecEnd = ecBegin + calculateRegionSize(regionSection->Region8Base, regionSection->Region8Limit); + } + else { + msg(tr("reconstructIntelImage: unknown descriptor version with ReadClockFreqency %1h").hexarg(componentSection->FlashParameters.ReadClockFreqency)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + UINT32 offset = descriptor.size(); // Reconstruct other regions @@ -2764,6 +2893,17 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec reconstructed.append(pdr); offset = pdrEnd; break; + case Subtypes::EcRegion: + if (descriptorVersion == 1) { + msg(tr("reconstructIntelImage: incompatible region type found"), index); + return ERR_INVALID_REGION; + } + ec = region; + if (ecBegin > offset) + reconstructed.append(QByteArray(ecBegin - offset, empty)); + reconstructed.append(ec); + offset = ecEnd; + break; default: msg(tr("reconstructIntelImage: unknown region type found"), index); return ERR_INVALID_REGION; diff --git a/ffsengine.h b/ffsengine.h index 281aa6e..c565921 100644 --- a/ffsengine.h +++ b/ffsengine.h @@ -67,6 +67,7 @@ public: UINT8 parseMeRegion(const QByteArray & me, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseBiosRegion(const QByteArray & bios, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parsePdrRegion(const QByteArray & pdr, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); + UINT8 parseEcRegion(const QByteArray & ec, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseBios(const QByteArray & bios, const QModelIndex & parent = QModelIndex()); UINT8 parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); diff --git a/types.cpp b/types.cpp index 17ca51f..a9a9300 100644 --- a/types.cpp +++ b/types.cpp @@ -29,6 +29,8 @@ QString regionTypeToQString(const UINT8 type) return QObject::tr("BIOS"); case Subtypes::PdrRegion: return QObject::tr("PDR"); + case Subtypes::EcRegion: + return QObject::tr("EC"); default: return QObject::tr("Unknown"); }; diff --git a/types.h b/types.h index 64df300..7d71953 100644 --- a/types.h +++ b/types.h @@ -69,7 +69,8 @@ namespace Subtypes { GbeRegion, MeRegion, BiosRegion, - PdrRegion + PdrRegion, + EcRegion }; enum PaddingSubtypes { diff --git a/uefitool.cpp b/uefitool.cpp index 5a4ef55..39d77ce 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.20.8")) +version(tr("0.21.0")) { clipboard = QApplication::clipboard(); From c286459676c8ca41a3066c8e23f9c43932597980 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 6 Sep 2015 23:51:50 +0200 Subject: [PATCH 10/27] UP 0.3.6 - changed version number due to rebuild with engine version 0.21.0 --- UEFIPatch/uefipatch_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UEFIPatch/uefipatch_main.cpp b/UEFIPatch/uefipatch_main.cpp index 6f37b1f..f1642d0 100644 --- a/UEFIPatch/uefipatch_main.cpp +++ b/UEFIPatch/uefipatch_main.cpp @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) result = w.patchFromFile(a.arguments().at(1)); } else { - std::cout << "UEFIPatch 0.3.5 - UEFI image file patching utility" << std::endl << std::endl << + std::cout << "UEFIPatch 0.3.6 - UEFI image file patching utility" << std::endl << std::endl << "Usage: UEFIPatch image_file" << std::endl << std::endl << "Patches will be read from patches.txt file\n"; return ERR_SUCCESS; From 8a149654630a3fa1f02a5d745563ee4630d0146c Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Tue, 8 Sep 2015 08:54:42 +0200 Subject: [PATCH 11/27] Spellchecking Corrections of issue #32 --- descriptor.h | 22 +++++++++++----------- ffs.cpp | 2 +- ffsengine.cpp | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/descriptor.h b/descriptor.h index 989761b..bd4c996 100644 --- a/descriptor.h +++ b/descriptor.h @@ -36,19 +36,20 @@ typedef struct _FLASH_DESCRIPTOR_HEADER { typedef struct _FLASH_DESCRIPTOR_MAP { // FLMAP0 UINT32 ComponentBase : 8; - UINT32 NumberOfFlashChips : 2; // Zero-based number of flash chips installed on board + UINT32 NumberOfFlashChips : 2; // Zero-based number of flash chips installed on board UINT32 : 6; - UINT32 RegionBase : 8; - UINT32 : 8; + UINT32 RegionBase : 8; + UINT32 NumberOfRegions : 3; // Reserved in v2 descriptor + UINT32 : 5; // FLMAP 1 UINT32 MasterBase : 8; UINT32 NumberOfMasters : 2; UINT32 : 6; UINT32 PchStrapsBase : 8; - UINT32 NumberOfPchStraps : 8; // One-based number of UINT32s to read as PCH straps, min=0, max=255 (1 Kb) + UINT32 NumberOfPchStraps : 8; // One-based number of UINT32s to read as PCH straps, min=0, max=255 (1 Kb) // FLMAP 2 UINT32 ProcStrapsBase : 8; - UINT32 NumberOfProcStraps : 8; // One-based number of UINT32s to read as processor straps, min=0, max=255 (1 Kb) + UINT32 NumberOfProcStraps : 8; // One-based number of UINT32s to read as processor straps, min=0, max=255 (1 Kb) UINT32: 16; } FLASH_DESCRIPTOR_MAP; @@ -59,11 +60,11 @@ typedef struct _FLASH_PARAMETERS { UINT8 SecondChipDensity : 4; UINT8 : 8; UINT8 : 1; - UINT8 ReadClockFreqency : 3; // Hardcoded value of 20 Mhz (000b) in v1 descriptors and 17 Mhz (110b) in v2 ones + UINT8 ReadClockFrequency : 3; // Hardcoded value of 20 Mhz (000b) in v1 descriptors and 17 Mhz (110b) in v2 ones UINT8 FastReadEnabled : 1; - UINT8 FastReadFreqency : 3; - UINT8 FlashReadStatusFrequency : 3; + UINT8 FastReadFrequency : 3; UINT8 FlashWriteFrequency : 3; + UINT8 FlashReadStatusFrequency : 3; UINT8 DualOutputFastReadSupported : 1; UINT8 : 1; } FLASH_PARAMETERS; @@ -112,8 +113,7 @@ typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION_V2 { // All base and limit register are storing upper part of actual UINT32 base and limit // If limit is zero - region is not present typedef struct _FLASH_DESCRIPTOR_REGION_SECTION { - UINT16 :16; - UINT16 FlashBlockEraseSize; // Size of block erased by single BLOCK ERASE command + UINT32 :32; UINT16 Region0Base; // BIOS UINT16 Region0Limit; // UINT16 Region1Base; // ME @@ -198,7 +198,7 @@ typedef struct _VSCC_TABLE_ENTRY { // Base address and size of OEM section #define FLASH_DESCRIPTOR_OEM_SECTION_BASE 0x0F00 -#define FLASH_DESCRIPTOR_OEM_SECTION_SIZE 0xFF +#define FLASH_DESCRIPTOR_OEM_SECTION_SIZE 0x100 // Restore previous packing rules #pragma pack(pop) diff --git a/ffs.cpp b/ffs.cpp index fcdf03f..33410c3 100644 --- a/ffs.cpp +++ b/ffs.cpp @@ -155,7 +155,7 @@ UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header) if (!header) return 0; - bool extended = false; + const bool extended = false; /*if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) { extended = true; }*/ diff --git a/ffsengine.cpp b/ffsengine.cpp index 985ea38..42f0a7b 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -269,12 +269,12 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in // Check descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency UINT8 descriptorVersion = 0; - if (componentSection->FlashParameters.ReadClockFreqency == FLASH_FREQUENCY_20MHZ) // Old descriptor + if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Old descriptor descriptorVersion = 1; - else if (componentSection->FlashParameters.ReadClockFreqency == FLASH_FREQUENCY_17MHZ) // Skylake+ descriptor + else if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_17MHZ) // Skylake+ descriptor descriptorVersion = 2; else { - msg(tr("parseIntelImage: unknown descriptor version with ReadClockFreqency %1h").hexarg(componentSection->FlashParameters.ReadClockFreqency)); + msg(tr("parseIntelImage: unknown descriptor version with ReadClockFrequency %1h").hexarg(componentSection->FlashParameters.ReadClockFrequency)); return ERR_INVALID_FLASH_DESCRIPTOR; } @@ -2831,16 +2831,16 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8((const UINT8*)descriptor.constData(), descriptorMap->ComponentBase); // Check descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency UINT8 descriptorVersion = 0; - if (componentSection->FlashParameters.ReadClockFreqency == FLASH_FREQUENCY_20MHZ) { // Old descriptor + if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) { // Old descriptor descriptorVersion = 1; } - else if (componentSection->FlashParameters.ReadClockFreqency == FLASH_FREQUENCY_17MHZ) { // Skylake+ descriptor + else if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_17MHZ) { // Skylake+ descriptor descriptorVersion = 2; ecBegin = calculateRegionOffset(regionSection->Region8Base); ecEnd = ecBegin + calculateRegionSize(regionSection->Region8Base, regionSection->Region8Limit); } else { - msg(tr("reconstructIntelImage: unknown descriptor version with ReadClockFreqency %1h").hexarg(componentSection->FlashParameters.ReadClockFreqency)); + msg(tr("reconstructIntelImage: unknown descriptor version with ReadClockFrequency %1h").hexarg(componentSection->FlashParameters.ReadClockFrequency)); return ERR_INVALID_FLASH_DESCRIPTOR; } From e5cf61f89a639ce77197b9e8a8ba129b58196f5c Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sat, 12 Sep 2015 19:44:59 +0200 Subject: [PATCH 12/27] Small fixes of FLASH_DESCRIPTOR_REGION_SECTION definition --- descriptor.h | 46 ++++++++++++++++++--------------------- ffsengine.cpp | 60 +++++++++++++++++++++++++-------------------------- 2 files changed, 51 insertions(+), 55 deletions(-) diff --git a/descriptor.h b/descriptor.h index bd4c996..579e600 100644 --- a/descriptor.h +++ b/descriptor.h @@ -43,7 +43,7 @@ typedef struct _FLASH_DESCRIPTOR_MAP { UINT32 : 5; // FLMAP 1 UINT32 MasterBase : 8; - UINT32 NumberOfMasters : 2; + UINT32 NumberOfMasters : 2; // Zero-based number of flash masters UINT32 : 6; UINT32 PchStrapsBase : 8; UINT32 NumberOfPchStraps : 8; // One-based number of UINT32s to read as PCH straps, min=0, max=255 (1 Kb) @@ -113,32 +113,28 @@ typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION_V2 { // All base and limit register are storing upper part of actual UINT32 base and limit // If limit is zero - region is not present typedef struct _FLASH_DESCRIPTOR_REGION_SECTION { - UINT32 :32; - UINT16 Region0Base; // BIOS - UINT16 Region0Limit; // - UINT16 Region1Base; // ME - UINT16 Region1Limit; // - UINT16 Region2Base; // GbE - UINT16 Region2Limit; // - UINT16 Region3Base; // PDR - UINT16 Region3Limit; // - UINT16 Region4Base; // Reserved region - UINT16 Region4Limit; // - UINT16 Region5Base; // Reserved region - UINT16 Region5Limit; // - UINT16 Region6Base; // Reserved region - UINT16 Region6Limit; // - UINT16 Region7Base; // Reserved region - UINT16 Region7Limit; // - UINT16 Region8Base; // EC - UINT16 Region8Limit; // + UINT16 DescriptorBase; // Descriptor + UINT16 DescriptorLimit; // + UINT16 BiosBase; // BIOS + UINT16 BiosLimit; // + UINT16 MeBase; // ME + UINT16 MeLimit; // + UINT16 GbeBase; // GbE + 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 Region8Base; // Reserved region + UINT16 Region8Limit; // + UINT16 EcBase; // EC + UINT16 EcLimit; // } FLASH_DESCRIPTOR_REGION_SECTION; -// Flash block erase sizes -#define FLASH_BLOCK_ERASE_SIZE_4KB 0x0000 -#define FLASH_BLOCK_ERASE_SIZE_8KB 0x0001 -#define FLASH_BLOCK_ERASE_SIZE_64KB 0x000F - // Master section typedef struct _FLASH_DESCRIPTOR_MASTER_SECTION { UINT16 BiosId; diff --git a/ffsengine.cpp b/ffsengine.cpp index 42f0a7b..5556ae0 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -282,9 +282,9 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in QByteArray me; UINT32 meBegin = 0; UINT32 meEnd = 0; - if (regionSection->Region1Limit) { - meBegin = calculateRegionOffset(regionSection->Region1Base); - meEnd = calculateRegionSize(regionSection->Region1Base, regionSection->Region1Limit); + if (regionSection->MeLimit) { + meBegin = calculateRegionOffset(regionSection->MeBase); + meEnd = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); me = intelImage.mid(meBegin, meEnd); meEnd += meBegin; } @@ -292,9 +292,9 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in QByteArray bios; UINT32 biosBegin = 0; UINT32 biosEnd = 0; - if (regionSection->Region0Limit) { - biosBegin = calculateRegionOffset(regionSection->Region0Base); - biosEnd = calculateRegionSize(regionSection->Region0Base, regionSection->Region0Limit); + if (regionSection->BiosLimit) { + biosBegin = calculateRegionOffset(regionSection->BiosBase); + biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); // Check for Gigabyte specific descriptor map if (biosEnd - biosBegin == (UINT32)intelImage.size()) { @@ -316,9 +316,9 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in QByteArray gbe; UINT32 gbeBegin = 0; UINT32 gbeEnd = 0; - if (regionSection->Region2Limit) { - gbeBegin = calculateRegionOffset(regionSection->Region2Base); - gbeEnd = calculateRegionSize(regionSection->Region2Base, regionSection->Region2Limit); + if (regionSection->GbeLimit) { + gbeBegin = calculateRegionOffset(regionSection->GbeBase); + gbeEnd = calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); gbe = intelImage.mid(gbeBegin, gbeEnd); gbeEnd += gbeBegin; } @@ -326,9 +326,9 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in QByteArray pdr; UINT32 pdrBegin = 0; UINT32 pdrEnd = 0; - if (regionSection->Region3Limit) { - pdrBegin = calculateRegionOffset(regionSection->Region3Base); - pdrEnd = calculateRegionSize(regionSection->Region3Base, regionSection->Region3Limit); + if (regionSection->PdrLimit) { + pdrBegin = calculateRegionOffset(regionSection->PdrBase); + pdrEnd = calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); pdr = intelImage.mid(pdrBegin, pdrEnd); pdrEnd += pdrBegin; } @@ -337,9 +337,9 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in UINT32 ecBegin = 0; UINT32 ecEnd = 0; if (descriptorVersion == 2) { - if (regionSection->Region8Limit) { - pdrBegin = calculateRegionOffset(regionSection->Region8Base); - pdrEnd = calculateRegionSize(regionSection->Region8Base, regionSection->Region8Limit); + if (regionSection->EcLimit) { + pdrBegin = calculateRegionOffset(regionSection->EcBase); + pdrEnd = calculateRegionSize(regionSection->EcBase, regionSection->EcLimit); pdr = intelImage.mid(ecBegin, ecEnd); ecEnd += ecBegin; } @@ -434,23 +434,23 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in // Check regions presence once again QVector offsets; - if (regionSection->Region2Limit) { + if (regionSection->GbeLimit) { offsets.append(gbeBegin); info += tr("\nGbE region offset: %1h").hexarg(gbeBegin); } - if (regionSection->Region1Limit) { + if (regionSection->MeLimit) { offsets.append(meBegin); info += tr("\nME region offset: %1h").hexarg(meBegin); } - if (regionSection->Region0Limit) { + if (regionSection->BiosLimit) { offsets.append(biosBegin); info += tr("\nBIOS region offset: %1h").hexarg(biosBegin); } - if (regionSection->Region3Limit) { + if (regionSection->PdrLimit) { offsets.append(pdrBegin); info += tr("\nPDR region offset: %1h").hexarg(pdrBegin); } - if (descriptorVersion == 2 && regionSection->Region8Limit) { + if (descriptorVersion == 2 && regionSection->EcLimit) { offsets.append(ecBegin); info += tr("\nEC region offset: %1h").hexarg(ecBegin); } @@ -2813,17 +2813,17 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor.constData() + sizeof(FLASH_DESCRIPTOR_HEADER)); const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8((const UINT8*)descriptor.constData(), descriptorMap->RegionBase); QByteArray gbe; - UINT32 gbeBegin = calculateRegionOffset(regionSection->Region2Base); - UINT32 gbeEnd = gbeBegin + calculateRegionSize(regionSection->Region2Base, regionSection->Region2Limit); + UINT32 gbeBegin = calculateRegionOffset(regionSection->GbeBase); + UINT32 gbeEnd = gbeBegin + calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); QByteArray me; - UINT32 meBegin = calculateRegionOffset(regionSection->Region1Base); - UINT32 meEnd = meBegin + calculateRegionSize(regionSection->Region1Base, regionSection->Region1Limit); + UINT32 meBegin = calculateRegionOffset(regionSection->MeBase); + UINT32 meEnd = meBegin + calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); QByteArray bios; - UINT32 biosBegin = calculateRegionOffset(regionSection->Region0Base); - UINT32 biosEnd = biosBegin + calculateRegionSize(regionSection->Region0Base, regionSection->Region0Limit); + UINT32 biosBegin = calculateRegionOffset(regionSection->BiosBase); + UINT32 biosEnd = biosBegin + calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); QByteArray pdr; - UINT32 pdrBegin = calculateRegionOffset(regionSection->Region3Base); - UINT32 pdrEnd = pdrBegin + calculateRegionSize(regionSection->Region3Base, regionSection->Region3Limit); + UINT32 pdrBegin = calculateRegionOffset(regionSection->PdrBase); + UINT32 pdrEnd = pdrBegin + calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); QByteArray ec; UINT32 ecBegin = 0; UINT32 ecEnd = 0; @@ -2836,8 +2836,8 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec } else if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_17MHZ) { // Skylake+ descriptor descriptorVersion = 2; - ecBegin = calculateRegionOffset(regionSection->Region8Base); - ecEnd = ecBegin + calculateRegionSize(regionSection->Region8Base, regionSection->Region8Limit); + ecBegin = calculateRegionOffset(regionSection->EcBase); + ecEnd = ecBegin + calculateRegionSize(regionSection->EcBase, regionSection->EcLimit); } else { msg(tr("reconstructIntelImage: unknown descriptor version with ReadClockFrequency %1h").hexarg(componentSection->FlashParameters.ReadClockFrequency)); From 9c7c94702dd3b15cd5de3c56ff819cab0ccb9b0d Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sat, 12 Sep 2015 20:46:21 +0200 Subject: [PATCH 13/27] Replace action for paddings - and a very small visual bugfix --- ffsengine.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++++++---- ffsengine.h | 1 + uefitool.cpp | 9 ++++++- uefitool.ui | 2 ++ 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/ffsengine.cpp b/ffsengine.cpp index 5556ae0..6848c3c 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -2093,6 +2093,18 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte // Set action model->setAction(fileIndex, action); } + else if (type == Types::Padding) { + // Get info + QString name = tr("Padding"); + QString info = tr("Full size: %1h (%2)") + .hexarg(body.size()).arg(body.size()); + + // Add tree item + QModelIndex fileIndex = model->addItem(Types::Padding, getPaddingType(body), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, index, mode); + + // Set action + model->setAction(fileIndex, action); + } else if (type == Types::Volume) { QByteArray volume; if (header.isEmpty()) // Whole volume @@ -2351,6 +2363,12 @@ UINT8 FfsEngine::replace(const QModelIndex & index, const QByteArray & object, c else return ERR_NOT_IMPLEMENTED; } + else if (model->type(index) == Types::Padding) { + if (mode == REPLACE_MODE_AS_IS) + result = create(index, Types::Padding, QByteArray(), object, CREATE_MODE_AFTER, Actions::Replace); + else + return ERR_NOT_IMPLEMENTED; + } else if (model->type(index) == Types::Volume) { if (mode == REPLACE_MODE_AS_IS) { result = create(index, Types::Volume, QByteArray(), object, CREATE_MODE_AFTER, Actions::Replace); @@ -2973,14 +2991,14 @@ UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconst if (reconstructed.size() > model->body(index).size()) { msg(tr("reconstructRegion: reconstructed region size %1h (%2) is bigger then original %3h (%4)") .hexarg(reconstructed.size()).arg(reconstructed.size()) - .hexarg(model->body(index).size()).arg(reconstructed.size()), + .hexarg(model->body(index).size()).arg(model->body(index).size()), index); return ERR_INVALID_PARAMETER; } else if (reconstructed.size() < model->body(index).size()) { msg(tr("reconstructRegion: reconstructed region size %1h (%2) is smaller then original %3h (%4)") .hexarg(reconstructed.size()).arg(reconstructed.size()) - .hexarg(model->body(index).size()).arg(reconstructed.size()), + .hexarg(model->body(index).size()).arg(model->body(index).size()), index); return ERR_INVALID_PARAMETER; } @@ -2995,6 +3013,49 @@ UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconst return ERR_NOT_IMPLEMENTED; } +UINT8 FfsEngine::reconstructPadding(const QModelIndex& index, QByteArray& reconstructed) +{ + if (!index.isValid()) + return ERR_SUCCESS; + + // No action + if (model->action(index) == Actions::NoAction) { + reconstructed = model->body(index); + return ERR_SUCCESS; + } + else if (model->action(index) == Actions::Remove) { + reconstructed.clear(); + return ERR_SUCCESS; + } + else if (model->action(index) == Actions::Rebuild || + model->action(index) == Actions::Replace) { + // Use stored item body + reconstructed = model->body(index); + + // Check size of reconstructed region, it must be same + if (reconstructed.size() > model->body(index).size()) { + msg(tr("reconstructPadding: reconstructed padding size %1h (%2) is bigger then original %3h (%4)") + .hexarg(reconstructed.size()).arg(reconstructed.size()) + .hexarg(model->body(index).size()).arg(model->body(index).size()), + index); + return ERR_INVALID_PARAMETER; + } + else if (reconstructed.size() < model->body(index).size()) { + msg(tr("reconstructPadding: reconstructed padding size %1h (%2) is smaller then original %3h (%4)") + .hexarg(reconstructed.size()).arg(reconstructed.size()) + .hexarg(model->body(index).size()).arg(model->body(index).size()), + index); + return ERR_INVALID_PARAMETER; + } + + // Reconstruction successful + return ERR_SUCCESS; + } + + // All other actions are not supported + return ERR_NOT_IMPLEMENTED; +} + UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & reconstructed) { if (!index.isValid()) @@ -3710,9 +3771,9 @@ UINT8 FfsEngine::reconstruct(const QModelIndex &index, QByteArray& reconstructed break; case Types::Padding: - // No reconstruction needed - reconstructed = model->header(index).append(model->body(index)); - return ERR_SUCCESS; + result = reconstructPadding(index, reconstructed); + if (result) + return result; break; case Types::Volume: diff --git a/ffsengine.h b/ffsengine.h index c565921..4fe5223 100644 --- a/ffsengine.h +++ b/ffsengine.h @@ -83,6 +83,7 @@ public: UINT8 reconstruct(const QModelIndex &index, QByteArray & reconstructed); UINT8 reconstructIntelImage(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructRegion(const QModelIndex& index, QByteArray & reconstructed, bool includeHeader = true); + UINT8 reconstructPadding(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructBios(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructVolume(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructFile(const QModelIndex& index, const UINT8 revision, const UINT8 erasePolarity, const UINT32 base, QByteArray& reconstructed); diff --git a/uefitool.cpp b/uefitool.cpp index 39d77ce..1689387 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -153,7 +153,7 @@ void UEFITool::populateUi(const QModelIndex ¤t) (type == Types::Section && (subtype == EFI_SECTION_COMPRESSION || subtype == EFI_SECTION_GUID_DEFINED || subtype == EFI_SECTION_DISPOSABLE))); ui->actionInsertBefore->setEnabled(type == Types::File || type == Types::Section); ui->actionInsertAfter->setEnabled(type == Types::File || type == Types::Section); - ui->actionReplace->setEnabled((type == Types::Region && subtype != Subtypes::DescriptorRegion) || type == Types::Volume || type == Types::File || type == Types::Section); + ui->actionReplace->setEnabled((type == Types::Region && subtype != Subtypes::DescriptorRegion) || type == Types::Padding || type == Types::Volume || type == Types::File || type == Types::Section); ui->actionReplaceBody->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->actionMessagesCopy->setEnabled(false); } @@ -327,6 +327,13 @@ void UEFITool::replace(const UINT8 mode) else return; } + else if (model->type(index) == Types::Padding) { + if (mode == REPLACE_MODE_AS_IS) { + path = QFileDialog::getOpenFileName(this, tr("Select padding file to replace selected object"), currentDir, "Padding files (*.pad *.bin);;All files (*)"); + } + else + return; + } else if (model->type(index) == Types::Volume) { if (mode == REPLACE_MODE_AS_IS) { path = QFileDialog::getOpenFileName(this, tr("Select volume file to replace selected object"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); diff --git a/uefitool.ui b/uefitool.ui index 5b5953a..8e0a19d 100644 --- a/uefitool.ui +++ b/uefitool.ui @@ -232,6 +232,8 @@ &Padding + + From 4e8431b1695087817316bb1cdcbabd3f0ac496bf Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sat, 12 Sep 2015 21:01:42 +0200 Subject: [PATCH 14/27] UT 0.21.1 - added replace action for paddings - fixed a rare visual bug (wrong image sizes in reconstructRegion messages) - parts of the new flash region map are defined a bit better --- uefitool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uefitool.cpp b/uefitool.cpp index 1689387..3b63ae2 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.21.0")) +version(tr("0.21.1")) { clipboard = QApplication::clipboard(); From 93881e48920305ad8abb1c65ed680e7807cf3bb7 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sat, 12 Sep 2015 21:40:45 +0200 Subject: [PATCH 15/27] Corrected EC region placement --- descriptor.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/descriptor.h b/descriptor.h index 579e600..0de5476 100644 --- a/descriptor.h +++ b/descriptor.h @@ -129,8 +129,6 @@ typedef struct _FLASH_DESCRIPTOR_REGION_SECTION { UINT16 Region6Limit; // UINT16 Region7Base; // Reserved region UINT16 Region7Limit; // - UINT16 Region8Base; // Reserved region - UINT16 Region8Limit; // UINT16 EcBase; // EC UINT16 EcLimit; // } FLASH_DESCRIPTOR_REGION_SECTION; From ed038c21bfce62c8344a3c8868a2f4193bc9709e Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Thu, 1 Oct 2015 08:24:35 +0200 Subject: [PATCH 16/27] Corrected #34 - padding data was a part of the image, now BIOS region --- ffsengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffsengine.cpp b/ffsengine.cpp index 6848c3c..1a32bdb 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -589,7 +589,7 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in return ERR_TRUNCATED_IMAGE; } else if (IntelDataEnd < (UINT32)intelImage.size()) { // Insert padding - QByteArray padding = bios.right(intelImage.size() - IntelDataEnd); + QByteArray padding = intelImage.mid(IntelDataEnd); // Get info name = tr("Padding"); info = tr("Full size: %1h (%2)") From 388dd2509358997e81f36f5fea6e406ce202cf1b Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Fri, 2 Oct 2015 09:14:11 +0200 Subject: [PATCH 17/27] UT 0.21.2 - fixed a bug with tailed files extraction and replacing (#35) - fixed a bug with wrong source of padding after all Intel image regions (#34) --- ffsengine.cpp | 24 +++++++++++++++++------- uefitool.cpp | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/ffsengine.cpp b/ffsengine.cpp index 1a32bdb..160dba7 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -1259,7 +1259,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U .arg(guidToQString(fileHeader->Name)) .hexarg2(fileHeader->Type, 2) .hexarg2(fileHeader->Attributes, 2) - .hexarg(header.size() + body.size()).arg(header.size() + body.size()) + .hexarg(header.size() + body.size() + tail.size()).arg(header.size() + body.size() + tail.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()) .hexarg2(fileHeader->State, 2); @@ -2137,12 +2137,22 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte return ERR_INVALID_FILE; QByteArray newHeader = header; + QByteArray newBody = body; EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newHeader.data(); + // Check if the file has a tail + UINT8 tailSize = fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT ? sizeof(UINT16) : 0; + if (tailSize) { + // Remove the tail, it will then be added back for revision 1 volumes + newBody = newBody.left(newBody.size() - tailSize); + // Remove the attribute for rev2+ volumes + if (revision != 1) { + fileHeader->Attributes &= ~(FFS_ATTRIB_TAIL_PRESENT); + } + } // Correct file size - UINT8 tailSize = fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT ? sizeof(UINT16) : 0; - uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + body.size() + tailSize, fileHeader->Size); + uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + newBody.size() + tailSize, fileHeader->Size); // Recalculate header checksum fileHeader->IntegrityCheck.Checksum.Header = 0; @@ -2151,17 +2161,17 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte // Recalculate data checksum, if needed if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) - fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((const UINT8*)body.constData(), body.size()); + fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((const UINT8*)newBody.constData(), newBody.size()); else if (revision == 1) fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; else fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; - // Append body - created.append(body); + // Append new body + created.append(newBody); // Append tail, if needed - if (tailSize) { + if (revision ==1 && tailSize) { UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; created.append(ht).append(ft); diff --git a/uefitool.cpp b/uefitool.cpp index 3b63ae2..c300a89 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.21.1")) +version(tr("0.21.2")) { clipboard = QApplication::clipboard(); From 1ab52fde3599c717964f99a7401d957501d05106 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 4 Oct 2015 21:58:44 +0200 Subject: [PATCH 18/27] UT 0.21.3 / UP 0.3.7 - solved a bug with Gigabyte-specific descriptor settings (BIOS region has size of the whole image), thanks to lordkag for #37 --- UEFIPatch/uefipatch_main.cpp | 2 +- ffsengine.cpp | 24 ++++++++++++++++++++++-- uefitool.cpp | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/UEFIPatch/uefipatch_main.cpp b/UEFIPatch/uefipatch_main.cpp index f1642d0..57f7e25 100644 --- a/UEFIPatch/uefipatch_main.cpp +++ b/UEFIPatch/uefipatch_main.cpp @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) result = w.patchFromFile(a.arguments().at(1)); } else { - std::cout << "UEFIPatch 0.3.6 - UEFI image file patching utility" << std::endl << std::endl << + std::cout << "UEFIPatch 0.3.7 - UEFI image file patching utility" << std::endl << std::endl << "Usage: UEFIPatch image_file" << std::endl << std::endl << "Patches will be read from patches.txt file\n"; return ERR_SUCCESS; diff --git a/ffsengine.cpp b/ffsengine.cpp index 160dba7..a71ec4f 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -303,10 +303,18 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in return ERR_INVALID_FLASH_DESCRIPTOR; } biosBegin = meEnd; + // 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 + } + // Normal descriptor map + else { + // Calculate biosEnd + biosEnd += biosBegin; } bios = intelImage.mid(biosBegin, biosEnd); - biosEnd += biosBegin; + } else { msg(tr("parseIntelImage: descriptor parsing failed, BIOS region not found in descriptor")); @@ -2843,15 +2851,27 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec QByteArray gbe; UINT32 gbeBegin = calculateRegionOffset(regionSection->GbeBase); UINT32 gbeEnd = gbeBegin + calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); + QByteArray me; UINT32 meBegin = calculateRegionOffset(regionSection->MeBase); UINT32 meEnd = meBegin + calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); + QByteArray bios; UINT32 biosBegin = calculateRegionOffset(regionSection->BiosBase); - UINT32 biosEnd = biosBegin + calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); + UINT32 biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); + // Gigabyte descriptor map + if (biosEnd - biosBegin == model->header(index).size() + model->body(index).size()) { + biosBegin = meEnd; + biosEnd = model->header(index).size() + model->body(index).size(); + } + // Normal descriptor map + else + biosEnd += biosBegin; + QByteArray pdr; UINT32 pdrBegin = calculateRegionOffset(regionSection->PdrBase); UINT32 pdrEnd = pdrBegin + calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); + QByteArray ec; UINT32 ecBegin = 0; UINT32 ecEnd = 0; diff --git a/uefitool.cpp b/uefitool.cpp index c300a89..99e5cbf 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.21.2")) +version(tr("0.21.3")) { clipboard = QApplication::clipboard(); From 9d0bcd0bed421e312582932772eb70a9c45a8b42 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 4 Oct 2015 22:05:38 +0200 Subject: [PATCH 19/27] Fix a compiler warning --- ffsengine.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ffsengine.cpp b/ffsengine.cpp index a71ec4f..8a312c8 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -314,7 +314,6 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in } bios = intelImage.mid(biosBegin, biosEnd); - } else { msg(tr("parseIntelImage: descriptor parsing failed, BIOS region not found in descriptor")); @@ -2860,7 +2859,7 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec UINT32 biosBegin = calculateRegionOffset(regionSection->BiosBase); UINT32 biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); // Gigabyte descriptor map - if (biosEnd - biosBegin == model->header(index).size() + model->body(index).size()) { + if (biosEnd - biosBegin == (UINT32)(model->header(index).size() + model->body(index).size())) { biosBegin = meEnd; biosEnd = model->header(index).size() + model->body(index).size(); } From 7ebbc58b9bcfef399284b6f3293590025a77946e Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 4 Oct 2015 22:28:44 +0200 Subject: [PATCH 20/27] UT 0.21.4 / UP 0.3.8 - fixed a bug introduced in 0.21.3 commit --- UEFIPatch/uefipatch_main.cpp | 2 +- ffsengine.cpp | 4 ++-- uefitool.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/UEFIPatch/uefipatch_main.cpp b/UEFIPatch/uefipatch_main.cpp index 57f7e25..419fd97 100644 --- a/UEFIPatch/uefipatch_main.cpp +++ b/UEFIPatch/uefipatch_main.cpp @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) result = w.patchFromFile(a.arguments().at(1)); } else { - std::cout << "UEFIPatch 0.3.7 - UEFI image file patching utility" << std::endl << std::endl << + std::cout << "UEFIPatch 0.3.8 - UEFI image file patching utility" << std::endl << std::endl << "Usage: UEFIPatch image_file" << std::endl << std::endl << "Patches will be read from patches.txt file\n"; return ERR_SUCCESS; diff --git a/ffsengine.cpp b/ffsengine.cpp index 8a312c8..aa85da2 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -303,17 +303,17 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in 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 } // Normal descriptor map else { + bios = intelImage.mid(biosBegin, biosEnd); // Calculate biosEnd biosEnd += biosBegin; } - - bios = intelImage.mid(biosBegin, biosEnd); } else { msg(tr("parseIntelImage: descriptor parsing failed, BIOS region not found in descriptor")); diff --git a/uefitool.cpp b/uefitool.cpp index 99e5cbf..f9d36f8 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.21.3")) +version(tr("0.21.4")) { clipboard = QApplication::clipboard(); From 19cc0312445145bbde46333bfab593e45d770da2 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Thu, 12 Nov 2015 09:37:38 +0100 Subject: [PATCH 21/27] UT 0.21.5 /UP 0.3.9 untested - fixed various crashes reported in #39 - changes aren't tested yet, please don't use until #39 is fixed --- basetypes.h | 1 + descriptor.h | 6 +- ffs.h | 2 +- ffsengine.cpp | 197 ++++++++++++++++++++++++++++++++++++++++++++------ uefitool.cpp | 2 +- 5 files changed, 181 insertions(+), 27 deletions(-) diff --git a/basetypes.h b/basetypes.h index e8589ad..3c07309 100644 --- a/basetypes.h +++ b/basetypes.h @@ -86,6 +86,7 @@ typedef unsigned int UINTN; #define ERR_NOTHING_TO_PATCH 41 #define ERR_DEPEX_PARSE_FAILED 42 #define ERR_TRUNCATED_IMAGE 43 +#define ERR_BAD_RELOCATION_ENTRY 44 #define ERR_NOT_IMPLEMENTED 0xFF // UDK porting definitions diff --git a/descriptor.h b/descriptor.h index 0de5476..5bdefd5 100644 --- a/descriptor.h +++ b/descriptor.h @@ -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 FfVector[16]; // Must be 16 0xFFs UINT32 Signature; // 0x0FF0A55A } FLASH_DESCRIPTOR_HEADER; @@ -53,6 +53,8 @@ typedef struct _FLASH_DESCRIPTOR_MAP { UINT32: 16; } FLASH_DESCRIPTOR_MAP; +#define FLASH_DESCRIPTOR_MAX_BASE 0xE0 + // Component section // Flash parameters DWORD structure typedef struct _FLASH_PARAMETERS { @@ -114,7 +116,7 @@ typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION_V2 { // If limit is zero - region is not present typedef struct _FLASH_DESCRIPTOR_REGION_SECTION { UINT16 DescriptorBase; // Descriptor - UINT16 DescriptorLimit; // + UINT16 DescriptorLimit; // UINT16 BiosBase; // BIOS UINT16 BiosLimit; // UINT16 MeBase; // ME diff --git a/ffs.h b/ffs.h index 0adc031..946a141 100644 --- a/ffs.h +++ b/ffs.h @@ -109,7 +109,7 @@ typedef struct _EFI_FIRMWARE_VOLUME_HEADER { UINT16 ExtHeaderOffset; //Reserved in Revision 1 UINT8 Reserved; UINT8 Revision; - //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[1]; + //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[2]; } EFI_FIRMWARE_VOLUME_HEADER; // Standard file system GUIDs diff --git a/ffsengine.cpp b/ffsengine.cpp index aa85da2..7909f10 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -76,6 +76,7 @@ QString errorMessage(UINT8 errorCode) 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_TRUNCATED_IMAGE: return QObject::tr("Image is truncated"); + case ERR_BAD_RELOCATION_ENTRY: return QObject::tr("Bad image relocation entry"); default: return QObject::tr("Unknown error %1").arg(errorCode); } } @@ -257,13 +258,31 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in // Check for buffer size to be greater or equal to descriptor region size if (intelImage.size() < FLASH_DESCRIPTOR_SIZE) { - msg(tr("parseIntelImage: input file is smaller then minimum descriptor size of %1h (%2) bytes").hexarg(FLASH_DESCRIPTOR_SIZE).arg(FLASH_DESCRIPTOR_SIZE)); + msg(tr("parseIntelImage: input file is smaller than minimum descriptor size of 1000h (4096) bytes")); return ERR_INVALID_FLASH_DESCRIPTOR; } // Parse descriptor map const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor + sizeof(FLASH_DESCRIPTOR_HEADER)); const FLASH_DESCRIPTOR_UPPER_MAP* upperMap = (const FLASH_DESCRIPTOR_UPPER_MAP*)(descriptor + FLASH_DESCRIPTOR_UPPER_MAP_BASE); + + // Check sanity of base values + if (descriptorMap->MasterBase > FLASH_DESCRIPTOR_MAX_BASE + || descriptorMap->MasterBase == descriptorMap->RegionBase + || descriptorMap->MasterBase == descriptorMap->ComponentBase) { + msg(tr("parseIntelImage: invalid descriptor master base %1h").hexarg2(descriptorMap->MasterBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + if (descriptorMap->RegionBase > FLASH_DESCRIPTOR_MAX_BASE + || descriptorMap->RegionBase == descriptorMap->ComponentBase) { + msg(tr("parseIntelImage: invalid descriptor region base %1h").hexarg2(descriptorMap->RegionBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + if (descriptorMap->ComponentBase > FLASH_DESCRIPTOR_MAX_BASE) { + msg(tr("parseIntelImage: invalid descriptor component base %1h").hexarg2(descriptorMap->ComponentBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8(descriptor, descriptorMap->RegionBase); const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8(descriptor, descriptorMap->ComponentBase); @@ -807,19 +826,21 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) // Get volume size result = getVolumeSize(bios, volumeOffset, volumeSize, bmVolumeSize); - if (result) + if (result) { + msg(tr("parseBios: getVolumeSize failed with error \"%1\"").arg(errorMessage(result)), parent); return result; + } - // Check reported size - if (volumeSize != bmVolumeSize) - msgSizeMismach = true; - - //Check that volume is fully present in input - if (volumeOffset + volumeSize > (UINT32)bios.size()) { + // Check that volume is fully present in input + if (volumeSize > (UINT32)bios.size() || volumeOffset + volumeSize > (UINT32)bios.size()) { msg(tr("parseBios: one of volumes inside overlaps the end of data"), parent); return ERR_INVALID_VOLUME; } + // Check reported size against a size calculated using block map + if (volumeSize != bmVolumeSize) + msgSizeMismach = true; + // Check volume revision and alignment const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + volumeOffset); UINT32 alignment; @@ -898,6 +919,10 @@ UINT8 FfsEngine::findNextVolume(const QByteArray & bios, UINT32 volumeOffset, UI UINT8 FfsEngine::getVolumeSize(const QByteArray & bios, UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize) { + // Check that there is space for the volume header and at least two block map entries. + if ((UINT32)bios.size() < volumeOffset + sizeof(EFI_FIRMWARE_VOLUME_HEADER) + 2 * sizeof(EFI_FV_BLOCK_MAP_ENTRY)) + return ERR_INVALID_VOLUME; + // Populate volume header const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + volumeOffset); @@ -918,14 +943,37 @@ UINT8 FfsEngine::getVolumeSize(const QByteArray & bios, UINT32 volumeOffset, UIN volumeSize = volumeHeader->FvLength; bmVolumeSize = calcVolumeSize; + + if (volumeSize == 0) + return ERR_INVALID_VOLUME; + return ERR_SUCCESS; } UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { + // Check that there is space for the volume header + if ((UINT32)volume.size() < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) { + msg(tr("parseVolume: input volume size %1h (%2) is smaller than volume header size 40h (64)").hexarg(volume.size()).arg(volume.size())); + return ERR_INVALID_VOLUME; + } + // Populate volume header const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(volume.constData()); + // Check sanity of HeaderLength value + if (ALIGN8(volumeHeader->HeaderLength) > volume.size()) { + msg(tr("parseVolume: volume header overlaps the end of data")); + return ERR_INVALID_VOLUME; + } + + // Check sanity of ExtHeaderOffset value + if (volumeHeader->ExtHeaderOffset > 0 + && (UINT32)volume.size() < ALIGN8(volumeHeader->ExtHeaderOffset + sizeof(EFI_FIRMWARE_VOLUME_EXT_HEADER))) { + msg(tr("parseVolume: extended volume header overlaps the end of data")); + return ERR_INVALID_VOLUME; + } + // Calculate volume header size UINT32 headerSize; if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { @@ -940,12 +988,10 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Check for volume structure to be known bool volumeIsUnknown = true; - /*UINT8 volumeFfsVersion = 0;*/ // Check for FFS v2 volume if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { volumeIsUnknown = false; - /*volumeFfsVersion = 2;*/ } // Check attributes @@ -1139,6 +1185,8 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co UINT8 FfsEngine::getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize) { + if ((UINT32)volume.size() < fileOffset + sizeof(EFI_FFS_FILE_HEADER)) + return ERR_INVALID_VOLUME; const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)(volume.constData() + fileOffset); fileSize = uint24ToUint32(fileHeader->Size); return ERR_SUCCESS; @@ -1332,6 +1380,8 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U UINT8 FfsEngine::getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize) { + if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER)) + return ERR_INVALID_FILE; const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(file.constData() + sectionOffset); sectionSize = uint24ToUint32(sectionHeader->Size); return ERR_SUCCESS; @@ -1350,6 +1400,9 @@ UINT8 FfsEngine::parseSections(const QByteArray & body, const QModelIndex & pare result = getSectionSize(body, sectionOffset, sectionSize); if (result) return result; + // Exit from loop if no sections left + if (sectionSize == 0) + break; // Parse section QModelIndex sectionIndex; @@ -1531,6 +1584,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c bool msgInvalidCrc = false; bool msgUnknownAuth = false; bool msgSigned = false; + bool msgInvalidSignatureLength = false; bool msgUnknownSignature = false; bool msgUnknownUefiGuidSignature = false; @@ -1594,7 +1648,12 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c else if (QByteArray((const char*)&guidDefinedSectionHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { msgSigned = true; const WIN_CERTIFICATE* certificateHeader = (const WIN_CERTIFICATE*)body.constData(); - if (certificateHeader->CertificateType == WIN_CERT_TYPE_EFI_GUID) { + if ((UINT32)body.size() < sizeof(WIN_CERTIFICATE)) { + info += tr("\nSignature type: invalid, wrong length"); + msgInvalidSignatureLength = true; + parseCurrentSection = false; + } + else if (certificateHeader->CertificateType == WIN_CERT_TYPE_EFI_GUID) { info += tr("\nSignature type: UEFI"); const WIN_CERTIFICATE_UEFI_GUID* guidCertificateHeader = (const WIN_CERTIFICATE_UEFI_GUID*)certificateHeader; if (QByteArray((const char*)&guidCertificateHeader->CertType, sizeof(EFI_GUID)) == EFI_CERT_TYPE_RSA2048_SHA256_GUID) { @@ -1619,10 +1678,17 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c msgUnknownSignature = true; } - // Add additional to the header - header.append(body.left(certificateHeader->Length)); - // Get new body - processed = body = body.mid(certificateHeader->Length); + if ((UINT32)body.size() < certificateHeader->Length) { + info += tr("\nSignature type: invalid, wrong length"); + msgInvalidSignatureLength = true; + parseCurrentSection = false; + } + else { + // Add additional data to the header + header.append(body.left(certificateHeader->Length)); + // Get new body + processed = body = body.mid(certificateHeader->Length); + } } // Unknown GUIDed section else { @@ -1665,6 +1731,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c msg(tr("parseSection: signature may become invalid after any modification"), index); if (msgUnknownUefiGuidSignature) msg(tr("parseSection: GUID defined section with unknown signature subtype"), index); + if (msgInvalidSignatureLength) + msg(tr("parseSection: GUID defined section with invalid signature length"), index); if (msgUnknownSignature) msg(tr("parseSection: GUID defined section with unknown signature type"), index); @@ -1796,6 +1864,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c // Get PE info bool msgInvalidDosSignature = false; + bool msgInvalidDosHeader = false; bool msgInvalidPeSignature = false; bool msgUnknownOptionalHeaderSignature = false; @@ -1806,7 +1875,11 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } else { const EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(body.constData() + dosHeader->e_lfanew); - if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) { + if ((UINT32)body.size() < dosHeader->e_lfanew + sizeof(EFI_IMAGE_PE_HEADER)) { + info += tr("\nDOS lfanew: %1h, invalid").hexarg2(dosHeader->e_lfanew, 8); + msgInvalidDosHeader = true; + } + else if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) { info += tr("\nPE signature: %1h, invalid").hexarg2(peHeader->Signature, 8); msgInvalidPeSignature = true; } @@ -1853,6 +1926,9 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c if (msgInvalidDosSignature) { msg("parseSection: PE32 image with invalid DOS signature", index); } + if (msgInvalidDosHeader) { + msg("parseSection: PE32 image with invalid DOS header", index); + } if (msgInvalidPeSignature) { msg("parseSection: PE32 image with invalid PE signature", index); } @@ -2845,16 +2921,40 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec return result; reconstructed.append(descriptor); + // Check descriptor size + if ((UINT32)descriptor.size() < FLASH_DESCRIPTOR_SIZE) { + msg(tr("reconstructIntelImage: descriptor is smaller than minimum size of 1000h (4096) bytes")); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor.constData() + sizeof(FLASH_DESCRIPTOR_HEADER)); + // Check sanity of base values + if (descriptorMap->MasterBase > FLASH_DESCRIPTOR_MAX_BASE + || descriptorMap->MasterBase == descriptorMap->RegionBase + || descriptorMap->MasterBase == descriptorMap->ComponentBase) { + msg(tr("reconstructIntelImage: invalid descriptor master base %1h").hexarg2(descriptorMap->MasterBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + if (descriptorMap->RegionBase > FLASH_DESCRIPTOR_MAX_BASE + || descriptorMap->RegionBase == descriptorMap->ComponentBase) { + msg(tr("reconstructIntelImage: invalid descriptor region base %1h").hexarg2(descriptorMap->RegionBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + if (descriptorMap->ComponentBase > FLASH_DESCRIPTOR_MAX_BASE) { + msg(tr("reconstructIntelImage: invalid descriptor component base %1h").hexarg2(descriptorMap->ComponentBase, 2)); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + + const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8((const UINT8*)descriptor.constData(), descriptorMap->RegionBase); QByteArray gbe; UINT32 gbeBegin = calculateRegionOffset(regionSection->GbeBase); UINT32 gbeEnd = gbeBegin + calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); - + QByteArray me; UINT32 meBegin = calculateRegionOffset(regionSection->MeBase); UINT32 meEnd = meBegin + calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); - + QByteArray bios; UINT32 biosBegin = calculateRegionOffset(regionSection->BiosBase); UINT32 biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); @@ -2870,7 +2970,7 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec QByteArray pdr; UINT32 pdrBegin = calculateRegionOffset(regionSection->PdrBase); UINT32 pdrEnd = pdrBegin + calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); - + QByteArray ec; UINT32 ecBegin = 0; UINT32 ecEnd = 0; @@ -2890,7 +2990,7 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec msg(tr("reconstructIntelImage: unknown descriptor version with ReadClockFrequency %1h").hexarg(componentSection->FlashParameters.ReadClockFrequency)); return ERR_INVALID_FLASH_DESCRIPTOR; } - + UINT32 offset = descriptor.size(); // Reconstruct other regions @@ -3107,6 +3207,12 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon QByteArray body = model->body(index); EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); + // Check sanity of HeaderLength + if (volumeHeader->HeaderLength > header.size()) { + msg(tr("reconstructVolume: invalid volume header length, reconstruction is not possible"), index); + return ERR_INVALID_VOLUME; + } + // Recalculate volume header checksum volumeHeader->Checksum = 0; volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); @@ -3170,8 +3276,8 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon // Calculate relative base address UINT32 relbase = fileOffset + sectionOffset + model->header(image).size(); // Calculate offset of image relative to file base - UINT32 imagebase; - result = getBase(model->body(image), imagebase); + UINT32 imagebase = 0; + result = getBase(model->body(image), imagebase); // imagebase passed by reference if (!result) { // Calculate volume base volumeBase = imagebase - relbase; @@ -3694,6 +3800,12 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, if (guidDefinedHeader->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) { // CRC32 section if (QByteArray((const char*)&guidDefinedHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_CRC32) { + // Check header size + if ((UINT32)header.size() != sizeof(EFI_GUID_DEFINED_SECTION) + sizeof(UINT32)) { + msg(tr("reconstructSection: invalid CRC32 section size %1h (%2)") + .hexarg(header.size()).arg(header.size()), index); + return ERR_INVALID_SECTION; + } // Calculate CRC32 of section data UINT32 crc = crc32(0, (const UINT8*)compressed.constData(), compressed.size()); // Store new CRC32 @@ -3831,6 +3943,10 @@ UINT8 FfsEngine::reconstruct(const QModelIndex &index, QByteArray& reconstructed UINT8 FfsEngine::growVolume(QByteArray & header, const UINT32 size, UINT32 & newSize) { + // Check sanity + if ((UINT32)header.size() < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) + return ERR_INVALID_VOLUME; + // Adjust new size to be representable by current FvBlockMap EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); EFI_FV_BLOCK_MAP_ENTRY* blockMap = (EFI_FV_BLOCK_MAP_ENTRY*)(header.data() + sizeof(EFI_FIRMWARE_VOLUME_HEADER)); @@ -4042,11 +4158,15 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) QByteArray file = executable; // Populate DOS header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) + return ERR_INVALID_FILE; EFI_IMAGE_DOS_HEADER* dosHeader = (EFI_IMAGE_DOS_HEADER*)file.data(); // Check signature if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ UINT32 offset = dosHeader->e_lfanew; + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) + return ERR_UNKNOWN_IMAGE_TYPE; EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(file.data() + offset); if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) return ERR_UNKNOWN_IMAGE_TYPE; @@ -4054,8 +4174,12 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) // Skip file header offset += sizeof(EFI_IMAGE_FILE_HEADER); // Check optional header magic + if ((UINT32)file.size() < offset + sizeof(UINT16)) + return ERR_UNKNOWN_IMAGE_TYPE; UINT16 magic = *(UINT16*)(file.data() + offset); if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (EFI_IMAGE_OPTIONAL_HEADER32*)(file.data() + offset); delta = base - optHeader->ImageBase; if (!delta) @@ -4067,6 +4191,8 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) optHeader->ImageBase = base; } else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (EFI_IMAGE_OPTIONAL_HEADER64*)(file.data() + offset); delta = base - optHeader->ImageBase; if (!delta) @@ -4082,6 +4208,8 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) } else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ // Populate TE header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) + return ERR_INVALID_FILE; EFI_IMAGE_TE_HEADER* teHeader = (EFI_IMAGE_TE_HEADER*)file.data(); delta = base - teHeader->ImageBase; if (!delta) @@ -4121,7 +4249,10 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) // Run this relocation record while (Reloc < RelocEnd) { - UINT8* data = (UINT8*)(file.data() + RelocBase->VirtualAddress - teFixup + (*Reloc & 0x0FFF)); + UINT32 RelocLocation = RelocBase->VirtualAddress - teFixup + (*Reloc & 0x0FFF); + if ((UINT32)file.size() < RelocLocation) + return ERR_BAD_RELOCATION_ENTRY; + UINT8* data = (UINT8*)(file.data() + RelocLocation); switch ((*Reloc) >> 12) { case EFI_IMAGE_REL_BASED_ABSOLUTE: // Do nothing @@ -4197,11 +4328,15 @@ UINT8 FfsEngine::getEntryPoint(const QByteArray &file, UINT32& entryPoint) return ERR_INVALID_FILE; // Populate DOS header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) + return ERR_INVALID_FILE; const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)file.constData(); // Check signature if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ UINT32 offset = dosHeader->e_lfanew; + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) + return ERR_UNKNOWN_IMAGE_TYPE; const EFI_IMAGE_PE_HEADER* peHeader = (const EFI_IMAGE_PE_HEADER*)(file.constData() + offset); if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) return ERR_UNKNOWN_IMAGE_TYPE; @@ -4213,10 +4348,14 @@ UINT8 FfsEngine::getEntryPoint(const QByteArray &file, UINT32& entryPoint) // Check optional header magic const UINT16 magic = *(const UINT16*)(file.constData() + offset); if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER32*)(file.constData() + offset); entryPoint = optHeader->ImageBase + optHeader->AddressOfEntryPoint; } else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER64*)(file.constData() + offset); entryPoint = optHeader->ImageBase + optHeader->AddressOfEntryPoint; } @@ -4225,6 +4364,8 @@ UINT8 FfsEngine::getEntryPoint(const QByteArray &file, UINT32& entryPoint) } else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ // Populate TE header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) + return ERR_INVALID_FILE; const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)file.constData(); UINT32 teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); entryPoint = teHeader->ImageBase + teHeader->AddressOfEntryPoint - teFixup; @@ -4238,11 +4379,15 @@ UINT8 FfsEngine::getBase(const QByteArray& file, UINT32& base) return ERR_INVALID_FILE; // Populate DOS header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) + return ERR_INVALID_FILE; const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)file.constData(); // Check signature if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ UINT32 offset = dosHeader->e_lfanew; + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) + return ERR_UNKNOWN_IMAGE_TYPE; const EFI_IMAGE_PE_HEADER* peHeader = (const EFI_IMAGE_PE_HEADER*)(file.constData() + offset); if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) return ERR_UNKNOWN_IMAGE_TYPE; @@ -4254,10 +4399,14 @@ UINT8 FfsEngine::getBase(const QByteArray& file, UINT32& base) // Check optional header magic const UINT16 magic = *(const UINT16*)(file.constData() + offset); if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER32*)(file.constData() + offset); base = optHeader->ImageBase; } else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) + return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER64*)(file.constData() + offset); base = optHeader->ImageBase; } @@ -4266,6 +4415,8 @@ UINT8 FfsEngine::getBase(const QByteArray& file, UINT32& base) } else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ // Populate TE header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) + return ERR_INVALID_FILE; const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)file.constData(); //!TODO: add handling base = teHeader->ImageBase; diff --git a/uefitool.cpp b/uefitool.cpp index f9d36f8..34ca9e0 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.21.4")) +version(tr("0.21.5")) { clipboard = QApplication::clipboard(); From d54f215e673d1dc338ef3171e82a220376d40c9a Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Thu, 12 Nov 2015 09:38:14 +0100 Subject: [PATCH 22/27] Forgot to bump UP version --- UEFIPatch/uefipatch_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UEFIPatch/uefipatch_main.cpp b/UEFIPatch/uefipatch_main.cpp index 419fd97..e14e59f 100644 --- a/UEFIPatch/uefipatch_main.cpp +++ b/UEFIPatch/uefipatch_main.cpp @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) result = w.patchFromFile(a.arguments().at(1)); } else { - std::cout << "UEFIPatch 0.3.8 - UEFI image file patching utility" << std::endl << std::endl << + std::cout << "UEFIPatch 0.3.9 - UEFI image file patching utility" << std::endl << std::endl << "Usage: UEFIPatch image_file" << std::endl << std::endl << "Patches will be read from patches.txt file\n"; return ERR_SUCCESS; From 8cfd93c8ebc4258ce785fe563148431beff5ca15 Mon Sep 17 00:00:00 2001 From: Alex Matrosov Date: Sun, 21 Aug 2016 15:48:13 -0700 Subject: [PATCH 23/27] Update patches.txt --- UEFIPatch/patches.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UEFIPatch/patches.txt b/UEFIPatch/patches.txt index 74eac08..f85fbe5 100644 --- a/UEFIPatch/patches.txt +++ b/UEFIPatch/patches.txt @@ -40,3 +40,5 @@ F7731B4C-58A2-4DF4-8980-5645D39ECE58 10 P:0FBA6C24380F:0FBA7424380F # CpuPei | Sandy Bridge with ME 7.xx, old SB-E/IB-E 2BB5AFA9-FF33-417B-8497-CB773C2B93BF 10 P:800018EB050D0080:000018EB050D0000 +# PpmInitialize | Broadwell-E +3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:0FBA6C24400F:0FBA7424400F From b700177e4a8eb9e88efac7c8d04aea037927d8e9 Mon Sep 17 00:00:00 2001 From: Adrian Dumitru Date: Wed, 25 Jan 2017 00:30:53 +0200 Subject: [PATCH 24/27] Add Kaby Lake support. --- UEFIPatch/patches.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/UEFIPatch/patches.txt b/UEFIPatch/patches.txt index f85fbe5..29d156d 100644 --- a/UEFIPatch/patches.txt +++ b/UEFIPatch/patches.txt @@ -42,3 +42,11 @@ F7731B4C-58A2-4DF4-8980-5645D39ECE58 10 P:0FBA6C24380F:0FBA7424380F # PpmInitialize | Broadwell-E 3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:0FBA6C24400F:0FBA7424400F + +# SiInit | Skylake +299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 10 P:75080D00800000:EB080D00800000 +299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 12 P:75080D00800000:EB080D00800000 + +# SiInit | Kaby Lake +299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 10 P:81E10080000033C1:9090909090909090 +299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 12 P:81E10080000033C1:9090909090909090 From 5ba63418bde66681ae81b8f1ae890ab65d746752 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 13 Apr 2017 17:30:44 +0200 Subject: [PATCH 25/27] Add UEFIReplace. --- .gitignore | 3 +- UEFIReplace/uefireplace.cpp | 108 +++++++++++++++++++++++++++++++ UEFIReplace/uefireplace.h | 41 ++++++++++++ UEFIReplace/uefireplace.pro | 42 ++++++++++++ UEFIReplace/uefireplace_main.cpp | 78 ++++++++++++++++++++++ 5 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 UEFIReplace/uefireplace.cpp create mode 100644 UEFIReplace/uefireplace.h create mode 100644 UEFIReplace/uefireplace.pro create mode 100644 UEFIReplace/uefireplace_main.cpp diff --git a/.gitignore b/.gitignore index c7191ed..910d521 100644 --- a/.gitignore +++ b/.gitignore @@ -233,4 +233,5 @@ Makefile UEFIExtract/UEFIExtract UEFIFind/UEFIFind UEFIPatch/UEFIPatch -UEFITool \ No newline at end of file +UEFIReplace/UEFIReplace +UEFITool diff --git a/UEFIReplace/uefireplace.cpp b/UEFIReplace/uefireplace.cpp new file mode 100644 index 0000000..f4ff1ea --- /dev/null +++ b/UEFIReplace/uefireplace.cpp @@ -0,0 +1,108 @@ +/* uefireplace.cpp + +Copyright (c) 2017, mxxxc. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#include "uefireplace.h" + +UEFIReplace::UEFIReplace(QObject *parent) : + QObject(parent) +{ + ffsEngine = new FfsEngine(this); + model = ffsEngine->treeModel(); +} + +UEFIReplace::~UEFIReplace() +{ + delete ffsEngine; +} + +UINT8 UEFIReplace::replace(QString inPath, const QByteArray & guid, const UINT8 sectionType, const QString contentPath) +{ + QFileInfo fileInfo = QFileInfo(inPath); + if (!fileInfo.exists()) + return ERR_FILE_OPEN; + + fileInfo = QFileInfo(contentPath); + if (!fileInfo.exists()) + return ERR_FILE_OPEN; + + QFile inputFile; + inputFile.setFileName(inPath); + + if (!inputFile.open(QFile::ReadOnly)) + return ERR_FILE_READ; + + QByteArray buffer = inputFile.readAll(); + inputFile.close(); + + UINT8 result = ffsEngine->parseImageFile(buffer); + if (result) + return result; + + QFile contentFile; + contentFile.setFileName(contentPath); + + if (!contentFile.open(QFile::ReadOnly)) + return ERR_FILE_READ; + + QByteArray contents = contentFile.readAll(); + contentFile.close(); + + result = replaceInFile(model->index(0, 0), guid, sectionType, contents); + if (result) + return result; + + QByteArray reconstructed; + result = ffsEngine->reconstructImageFile(reconstructed); + if (result) + return result; + if (reconstructed == buffer) + return ERR_NOTHING_TO_PATCH; + + QFile outputFile; + outputFile.setFileName(inPath.append(".patched")); + if (!outputFile.open(QFile::WriteOnly)) + return ERR_FILE_WRITE; + + outputFile.resize(0); + outputFile.write(reconstructed); + outputFile.close(); + + return ERR_SUCCESS; +} + +UINT8 UEFIReplace::replaceInFile(const QModelIndex & index, const QByteArray & guid, const UINT8 sectionType, const QByteArray & newData) +{ + if (!model || !index.isValid()) + return ERR_INVALID_PARAMETER; + if (model->subtype(index) == sectionType) { + QModelIndex fileIndex = model->findParentOfType(index, Types::File); + QByteArray fileGuid = model->header(fileIndex).left(sizeof(EFI_GUID)); + if (fileGuid == guid) { + return ffsEngine->replace(index, newData, REPLACE_MODE_BODY); + } + } + + bool patched = false; + if (model->rowCount(index) > 0) { + for (int i = 0; i < model->rowCount(index); i++) { + UINT8 result = replaceInFile(index.child(i, 0), guid, sectionType, newData); + if (!result) { + patched = true; + break; + } else if (result != ERR_NOTHING_TO_PATCH) + return result; + } + } + + return patched ? ERR_SUCCESS : ERR_NOTHING_TO_PATCH; +} diff --git a/UEFIReplace/uefireplace.h b/UEFIReplace/uefireplace.h new file mode 100644 index 0000000..682a1f6 --- /dev/null +++ b/UEFIReplace/uefireplace.h @@ -0,0 +1,41 @@ +/* uefireplace.h +Copyright (c) 2017, mxxxc. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +*/ + +#ifndef __UEFIREPLACE_H__ +#define __UEFIREPLACE_H__ + +#include +#include +#include +#include +#include +#include + +#include "../basetypes.h" +#include "../ffs.h" +#include "../ffsengine.h" + +class UEFIReplace : public QObject +{ + Q_OBJECT + +public: + explicit UEFIReplace(QObject *parent = 0); + ~UEFIReplace(); + + UINT8 replace(const QString inPath, const QByteArray & guid, const UINT8 sectionType, const QString contentPath); + +private: + UINT8 replaceInFile(const QModelIndex & index, const QByteArray & guid, const UINT8 sectionType, const QByteArray & contents); + FfsEngine* ffsEngine; + TreeModel* model; +}; + +#endif diff --git a/UEFIReplace/uefireplace.pro b/UEFIReplace/uefireplace.pro new file mode 100644 index 0000000..e3d853d --- /dev/null +++ b/UEFIReplace/uefireplace.pro @@ -0,0 +1,42 @@ +QT += core +QT -= gui + +TARGET = UEFIReplace +TEMPLATE = app +CONFIG += console +CONFIG -= app_bundle +DEFINES += _CONSOLE + +SOURCES += uefireplace_main.cpp \ + uefireplace.cpp \ + ../types.cpp \ + ../descriptor.cpp \ + ../ffs.cpp \ + ../ffsengine.cpp \ + ../peimage.cpp \ + ../treeitem.cpp \ + ../treemodel.cpp \ + ../LZMA/LzmaCompress.c \ + ../LZMA/LzmaDecompress.c \ + ../LZMA/SDK/C/LzFind.c \ + ../LZMA/SDK/C/LzmaDec.c \ + ../LZMA/SDK/C/LzmaEnc.c \ + ../Tiano/EfiTianoDecompress.c \ + ../Tiano/EfiTianoCompress.c \ + ../Tiano/EfiTianoCompressLegacy.c + +HEADERS += uefireplace.h \ + ../basetypes.h \ + ../descriptor.h \ + ../gbe.h \ + ../me.h \ + ../ffs.h \ + ../peimage.h \ + ../types.h \ + ../ffsengine.h \ + ../treeitem.h \ + ../treemodel.h \ + ../LZMA/LzmaCompress.h \ + ../LZMA/LzmaDecompress.h \ + ../Tiano/EfiTianoDecompress.h \ + ../Tiano/EfiTianoCompress.h diff --git a/UEFIReplace/uefireplace_main.cpp b/UEFIReplace/uefireplace_main.cpp new file mode 100644 index 0000000..562db22 --- /dev/null +++ b/UEFIReplace/uefireplace_main.cpp @@ -0,0 +1,78 @@ +/* uefireplace_main.cpp + +Copyright (c) 2017, mxxxc. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ +#include +#include +#include +#include +#include "uefireplace.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + a.setOrganizationName("CodeRush"); + a.setOrganizationDomain("coderush.me"); + a.setApplicationName("UEFIReplace"); + + UEFIReplace r; + UINT8 result = ERR_SUCCESS; + QStringList args = a.arguments(); + + if (args.length() < 5) { + std::cout << "UEFIReplace 0.3.9 - UEFI image file replacement utility" << std::endl << std::endl << + "Usage: UEFIReplace image_file guid section_type contents_file" << std::endl; + return ERR_SUCCESS; + } + + QUuid uuid = QUuid(args.at(2)); + QByteArray guid = QByteArray::fromRawData((const char*)&uuid.data1, sizeof(EFI_GUID)); + bool converted; + UINT8 sectionType = (UINT8)args.at(3).toUShort(&converted, 16); + if (!converted) + result = ERR_INVALID_PARAMETER; + else + result = r.replace(args.at(1), guid, sectionType, args.at(4)); + + switch (result) { + case ERR_SUCCESS: + std::cout << "File replaced" << std::endl; + break; + case ERR_INVALID_PARAMETER: + std::cout << "Function called with invalid parameter" << std::endl; + break; + case ERR_INVALID_FILE: + std::cout << "Invalid/corrupted file specified" << std::endl; + break; + case ERR_INVALID_SECTION: + std::cout << "Invalid/corrupted section specified" << std::endl; + break; + case ERR_NOTHING_TO_PATCH: + std::cout << "No replacements can be applied to input file" << std::endl; + break; + case ERR_NOT_IMPLEMENTED: + std::cout << "Can't replace body of this section type" << std::endl; + break; + case ERR_FILE_OPEN: + std::cout << "Input file not found" << std::endl; + break; + case ERR_FILE_READ: + std::cout << "Input file can't be read" << std::endl; + break; + case ERR_FILE_WRITE: + std::cout << "Output file can't be written" << std::endl; + break; + default: + std::cout << "Error " << result << std::endl; + } + + return result; +} From 73019876cf489c1b34639d8f67135c7b8e28ef2f Mon Sep 17 00:00:00 2001 From: Alex Matrosov Date: Sun, 10 Dec 2017 16:54:40 -0800 Subject: [PATCH 26/27] add FFSv3 support with large files and large sections --- UEFIPatch/uefipatch_main.cpp | 8 +- ffs.cpp | 23 ++- ffs.h | 14 +- ffsengine.cpp | 295 +++++++++++++++++++++-------------- ffsengine.h | 3 +- uefitool.cpp | 2 +- 6 files changed, 212 insertions(+), 133 deletions(-) diff --git a/UEFIPatch/uefipatch_main.cpp b/UEFIPatch/uefipatch_main.cpp index e14e59f..aac0ca7 100644 --- a/UEFIPatch/uefipatch_main.cpp +++ b/UEFIPatch/uefipatch_main.cpp @@ -1,6 +1,6 @@ /* uefipatch_main.cpp -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2017, LongSoft. 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,8 +19,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); - a.setOrganizationName("CodeRush"); - a.setOrganizationDomain("coderush.me"); + a.setOrganizationName("LongSoft"); + a.setOrganizationDomain("longsoft.me"); a.setApplicationName("UEFIPatch"); UEFIPatch w; @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) result = w.patchFromFile(a.arguments().at(1)); } else { - std::cout << "UEFIPatch 0.3.9 - UEFI image file patching utility" << std::endl << std::endl << + std::cout << "UEFIPatch 0.3.10 - UEFI image file patching utility" << std::endl << std::endl << "Usage: UEFIPatch image_file" << std::endl << std::endl << "Patches will be read from patches.txt file\n"; return ERR_SUCCESS; diff --git a/ffs.cpp b/ffs.cpp index 33410c3..6c3c773 100644 --- a/ffs.cpp +++ b/ffs.cpp @@ -30,7 +30,10 @@ const QVector FFSv3Volumes = const UINT8 ffsAlignmentTable[] = { 0, 4, 7, 9, 10, 12, 15, 16 }; -UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize) +const UINT8 ffsAlignment2Table[] = +{ 17, 18, 19, 20, 21, 22, 23, 24 }; + +UINT8 calculateSum8(const UINT8* buffer, UINT32 bufferSize) { if (!buffer) return 0; @@ -40,7 +43,15 @@ UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize) while (bufferSize--) counter += buffer[bufferSize]; - return (UINT8)0x100 - counter; + return counter; +} + +UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize) +{ + if (!buffer) + return 0; + + return (UINT8)0x100 - calculateSum8(buffer, bufferSize); } UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize) @@ -120,6 +131,8 @@ QString fileTypeToQString(const UINT8 type) case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: return QObject::tr("Volume image"); case EFI_FV_FILETYPE_COMBINED_SMM_DXE: return QObject::tr("Combined SMM/DXE"); case EFI_FV_FILETYPE_SMM_CORE: return QObject::tr("SMM core"); + case EFI_FV_FILETYPE_SMM_STANDALONE: return QObject::tr("SMM standalone"); + case EFI_FV_FILETYPE_SMM_CORE_STANDALONE: return QObject::tr("SMM core standalone"); case EFI_FV_FILETYPE_PAD: return QObject::tr("Pad"); default: return QObject::tr("Unknown"); }; @@ -155,10 +168,10 @@ UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header) if (!header) return 0; - const bool extended = false; - /*if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) { + bool extended = false; + if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) { extended = true; - }*/ + } switch (header->Type) { diff --git a/ffs.h b/ffs.h index 946a141..c761551 100644 --- a/ffs.h +++ b/ffs.h @@ -292,7 +292,7 @@ UINT8 Type; UINT8 Attributes; UINT8 Size[3]; // Set to 0xFFFFFF UINT8 State; -UINT32 ExtendedSize; +UINT64 ExtendedSize; } EFI_FFS_FILE_HEADER2; // Standard data checksum, used if FFS_ATTRIB_CHECKSUM is clear @@ -314,6 +314,8 @@ UINT32 ExtendedSize; #define EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE 0x0B #define EFI_FV_FILETYPE_COMBINED_SMM_DXE 0x0C #define EFI_FV_FILETYPE_SMM_CORE 0x0D +#define EFI_FV_FILETYPE_SMM_STANDALONE 0x0E +#define EFI_FV_FILETYPE_SMM_CORE_STANDALONE 0x0F #define EFI_FV_FILETYPE_OEM_MIN 0xC0 #define EFI_FV_FILETYPE_OEM_MAX 0xDF #define EFI_FV_FILETYPE_DEBUG_MIN 0xE0 @@ -326,6 +328,7 @@ UINT32 ExtendedSize; #define FFS_ATTRIB_TAIL_PRESENT 0x01 // Valid only for revision 1 volumes #define FFS_ATTRIB_RECOVERY 0x02 // Valid only for revision 1 volumes #define FFS_ATTRIB_LARGE_FILE 0x01 // Valid only for FFSv3 volumes +#define FFS_ATTRIB_DATA_ALIGNMENT2 0x02 // Valid only for revision 2 volumes #define FFS_ATTRIB_FIXED 0x04 #define FFS_ATTRIB_DATA_ALIGNMENT 0x38 #define FFS_ATTRIB_CHECKSUM 0x40 @@ -333,6 +336,9 @@ UINT32 ExtendedSize; // FFS alignment table extern const UINT8 ffsAlignmentTable[]; +// Extended FFS alignment table +extern const UINT8 ffsAlignment2Table[]; + // File states #define EFI_FILE_HEADER_CONSTRUCTION 0x01 #define EFI_FILE_HEADER_VALID 0x02 @@ -360,7 +366,9 @@ const QByteArray EFI_FFS_PAD_FILE_GUID // FFS size conversion routines extern VOID uint32ToUint24(UINT32 size, UINT8* ffsSize); extern UINT32 uint24ToUint32(const UINT8* ffsSize); -// FFS file 8bit checksum calculation routine + +// FFS file 8bit checksum calculation routines +extern UINT8 calculateSum8(const UINT8* buffer, UINT32 bufferSize); extern UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize); //***************************************************************************** @@ -528,7 +536,7 @@ typedef EFI_COMMON_SECTION_HEADER2 EFI_FIRMWARE_VOLUME_IMAGE_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_USER_INTERFACE_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_USER_INTERFACE_SECTION2; -//Section routines +// Section routines extern UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header); //***************************************************************************** diff --git a/ffsengine.cpp b/ffsengine.cpp index 7909f10..9c66dbf 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -287,15 +287,9 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8(descriptor, descriptorMap->ComponentBase); // Check descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency - UINT8 descriptorVersion = 0; - if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Old descriptor + UINT8 descriptorVersion = 2; // Skylake+ by default + if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Old descriptor descriptorVersion = 1; - else if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_17MHZ) // Skylake+ descriptor - descriptorVersion = 2; - else { - msg(tr("parseIntelImage: unknown descriptor version with ReadClockFrequency %1h").hexarg(componentSection->FlashParameters.ReadClockFrequency)); - return ERR_INVALID_FLASH_DESCRIPTOR; - } // ME region QByteArray me; @@ -983,15 +977,16 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co else headerSize = volumeHeader->HeaderLength; - // Sanity check after some new crazy MSI images + // Sanity check after some crazy MSI images headerSize = ALIGN8(headerSize); - // Check for volume structure to be known - bool volumeIsUnknown = true; - - // Check for FFS v2 volume - if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { - volumeIsUnknown = false; + // Check for FFS v2/v3 volume + UINT8 subtype = Subtypes::UnknownVolume; + if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))){ + subtype = Subtypes::Ffs2Volume; + } + else if (FFSv3Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { + subtype = Subtypes::Ffs3Volume; } // Check attributes @@ -1063,10 +1058,10 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Add tree item QByteArray header = volume.left(headerSize); QByteArray body = volume.mid(headerSize, volumeSize - headerSize); - index = model->addItem(Types::Volume, volumeIsUnknown ? Subtypes::UnknownVolume : Subtypes::Ffs2Volume, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parent, mode); + index = model->addItem(Types::Volume, subtype, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parent, mode); // Show messages - if (volumeIsUnknown) { + if (subtype == Subtypes::UnknownVolume) { msg(tr("parseVolume: unknown file system %1").arg(guidToQString(volumeHeader->FileSystemGuid)), index); // Do not parse unknown volumes return ERR_SUCCESS; @@ -1100,19 +1095,42 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co break; } - result = getFileSize(volume, fileOffset, fileSize); - if (result) - return result; + QByteArray tempFile = volume.mid(fileOffset, sizeof(EFI_FFS_FILE_HEADER)); + const EFI_FFS_FILE_HEADER* tempFileHeader = (const EFI_FFS_FILE_HEADER*)tempFile.constData(); + UINT32 fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER); + fileSize = uint24ToUint32(tempFileHeader->Size); + if (volumeHeader->Revision > 1 && (tempFileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + // Check if it's possibly the latest file in the volume + if (volumeSize - fileOffset < sizeof(EFI_FFS_FILE_HEADER2)) { + // 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); + } + 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); + msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex); + } + // Exit from loop + break; + } - // Check file size to be at least size of EFI_FFS_FILE_HEADER - if (fileSize < sizeof(EFI_FFS_FILE_HEADER)) { + fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER2); + tempFile = volume.mid(fileOffset, sizeof(EFI_FFS_FILE_HEADER2)); + const EFI_FFS_FILE_HEADER2* tempFileHeader2 = (const EFI_FFS_FILE_HEADER2*)tempFile.constData(); + fileSize = (UINT32)tempFileHeader2->ExtendedSize; + } + + // Check file size to be at least size of the header + if (fileSize < fileHeaderSize) { msg(tr("parseVolume: volume has FFS file with invalid size"), index); return ERR_INVALID_FILE; } - + QByteArray file = volume.mid(fileOffset, fileSize); - QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); - + QByteArray header = file.left(fileHeaderSize); + // If we are at empty space in the end of volume if (header.count(empty) == header.size()) { // Check free space to be actually free @@ -1152,8 +1170,11 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Check file alignment const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)header.constData(); UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; - UINT32 alignment = (UINT32)pow(2.0, alignmentPower); - if ((fileOffset + sizeof(EFI_FFS_FILE_HEADER)) % alignment) + if (volumeHeader->Revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT2)) + alignmentPower = ffsAlignment2Table[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; + + UINT32 alignment = (UINT32)(1UL << alignmentPower); + if ((fileOffset + fileHeaderSize) % alignment) msgUnalignedFile = true; // Check file GUID @@ -1165,7 +1186,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); + result = parseFile(file, fileIndex, volumeHeader->Revision, empty == '\xFF' ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) msg(tr("parseVolume: FFS file parsing failed with error \"%1\"").arg(errorMessage(result)), index); @@ -1183,16 +1204,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co return ERR_SUCCESS; } -UINT8 FfsEngine::getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize) -{ - if ((UINT32)volume.size() < fileOffset + sizeof(EFI_FFS_FILE_HEADER)) - return ERR_INVALID_VOLUME; - const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)(volume.constData() + fileOffset); - fileSize = uint24ToUint32(fileHeader->Size); - return ERR_SUCCESS; -} - -UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity, const QModelIndex & parent, const UINT8 mode) +UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const UINT8 revision, const UINT8 erasePolarity, const QModelIndex & parent, const UINT8 mode) { bool msgInvalidHeaderChecksum = false; bool msgInvalidDataChecksum = false; @@ -1200,43 +1212,32 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U bool msgInvalidType = false; // Populate file header + if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER)) + return ERR_INVALID_FILE; const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)file.constData(); - // Check file state // Construct empty byte for this file char empty = erasePolarity ? '\xFF' : '\x00'; - // Check header checksum + // Get file header QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); - QByteArray tempHeader = header; - EFI_FFS_FILE_HEADER* tempFileHeader = (EFI_FFS_FILE_HEADER*)(tempHeader.data()); - tempFileHeader->IntegrityCheck.Checksum.Header = 0; - tempFileHeader->IntegrityCheck.Checksum.File = 0; - UINT8 calculated = calculateChecksum8((const UINT8*)tempFileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1); - if (fileHeader->IntegrityCheck.Checksum.Header != calculated) + if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER2)) + return ERR_INVALID_FILE; + header = file.left(sizeof(EFI_FFS_FILE_HEADER2)); + } + + // Check header checksum + UINT8 calculatedHeader = 0x100 -(calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->IntegrityCheck.Checksum.Header - fileHeader->IntegrityCheck.Checksum.File - fileHeader->State); + if (fileHeader->IntegrityCheck.Checksum.Header != calculatedHeader) msgInvalidHeaderChecksum = true; - // Check data checksum - // Data checksum must be calculated - if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { - UINT32 bufferSize = file.size() - sizeof(EFI_FFS_FILE_HEADER); - // Exclude file tail from data checksum calculation - if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) - bufferSize -= sizeof(UINT16); - calculated = calculateChecksum8((const UINT8*)(file.constData() + sizeof(EFI_FFS_FILE_HEADER)), bufferSize); - if (fileHeader->IntegrityCheck.Checksum.File != calculated) - msgInvalidDataChecksum = true; - } - // Data checksum must be one of predefined values - else if (fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM && fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2) - msgInvalidDataChecksum = true; - // Get file body - QByteArray body = file.right(file.size() - sizeof(EFI_FFS_FILE_HEADER)); + QByteArray body = file.mid(header.size()); + // Check for file tail presence QByteArray tail; - if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) - { + if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { //Check file tail; tail = body.right(sizeof(UINT16)); UINT16 tailValue = *(UINT16*)tail.constData(); @@ -1247,48 +1248,47 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U body = body.left(body.size() - sizeof(UINT16)); } + // Check data checksum + // Data checksum must be calculated + UINT8 calculatedData = 0; + if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { + calculatedData = calculateChecksum8((const UINT8*)body.constData(), body.size()); + if (fileHeader->IntegrityCheck.Checksum.File != calculatedData) + msgInvalidDataChecksum = true; + } + // Data checksum must be one of predefined values + else if ((revision == 1 && fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM) + || fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2) + msgInvalidDataChecksum = true; + // Parse current file by default - bool parseCurrentFile = true; + bool parseCurrentFile = false; bool parseAsBios = false; // Check file type - switch (fileHeader->Type) - { + switch (fileHeader->Type) { case EFI_FV_FILETYPE_ALL: - parseAsBios = true; - break; case EFI_FV_FILETYPE_RAW: parseAsBios = true; - break; case EFI_FV_FILETYPE_FREEFORM: - break; case EFI_FV_FILETYPE_SECURITY_CORE: - break; case EFI_FV_FILETYPE_PEI_CORE: - break; case EFI_FV_FILETYPE_DXE_CORE: - break; case EFI_FV_FILETYPE_PEIM: - break; case EFI_FV_FILETYPE_DRIVER: - break; case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: - break; case EFI_FV_FILETYPE_APPLICATION: - break; case EFI_FV_FILETYPE_SMM: - break; case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: - break; case EFI_FV_FILETYPE_COMBINED_SMM_DXE: - break; case EFI_FV_FILETYPE_SMM_CORE: - break; + case EFI_FV_FILETYPE_SMM_STANDALONE: + case EFI_FV_FILETYPE_SMM_CORE_STANDALONE: case EFI_FV_FILETYPE_PAD: + parseCurrentFile = true; break; default: msgInvalidType = true; - parseCurrentFile = false; }; // Check for empty file @@ -1310,25 +1310,27 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U else name = parseAsNonEmptyPadFile ? tr("Non-empty pad-file") : tr("Pad-file"); - info = tr("File GUID: %1\nType: %2h\nAttributes: %3h\nFull size: %4h (%5)\nHeader size: %6h (%7)\nBody size: %8h (%9)\nState: %10h") + info = tr("File GUID: %1\nType: %2h\nAttributes: %3h\nFull size: %4h (%5)\nHeader size: %6h (%7)\nBody size: %8h (%9)\nState: %10h\nHeader checksum: %11h\nData checksum: %12h") .arg(guidToQString(fileHeader->Name)) .hexarg2(fileHeader->Type, 2) .hexarg2(fileHeader->Attributes, 2) .hexarg(header.size() + body.size() + tail.size()).arg(header.size() + body.size() + tail.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()) - .hexarg2(fileHeader->State, 2); + .hexarg2(fileHeader->State, 2) + .hexarg2(fileHeader->IntegrityCheck.Checksum.Header, 2) + .hexarg2(fileHeader->IntegrityCheck.Checksum.File, 2); // Add tree item index = model->addItem(Types::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgInvalidHeaderChecksum) - msg(tr("parseFile: invalid header checksum"), index); + msg(tr("parseFile: invalid header checksum %1h, should be %2h").hexarg2(fileHeader->IntegrityCheck.Checksum.Header, 2).hexarg2(calculatedHeader, 2), index); if (msgInvalidDataChecksum) - msg(tr("parseFile: invalid data checksum"), index); + msg(tr("parseFile: invalid data checksum %1h, should be %2h").hexarg2(fileHeader->IntegrityCheck.Checksum.File, 2).hexarg2(calculatedData, 2), index); if (msgInvalidTailValue) - msg(tr("parseFile: invalid tail value"), index); + msg(tr("parseFile: invalid tail value %1h").hexarg(*(UINT16*)tail.data()), index); if (msgInvalidType) msg(tr("parseFile: unknown file type %1h").arg(fileHeader->Type, 2), index); @@ -1382,8 +1384,15 @@ UINT8 FfsEngine::getSectionSize(const QByteArray & file, const UINT32 sectionOff { if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER)) return ERR_INVALID_FILE; + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(file.constData() + sectionOffset); sectionSize = uint24ToUint32(sectionHeader->Size); + // This may introduce a very rare error with a non-extended section of size equal to 0xFFFFFF + if (sectionSize != 0xFFFFFF) + return ERR_SUCCESS; + + const EFI_COMMON_SECTION_HEADER2* sectionHeader2 = (const EFI_COMMON_SECTION_HEADER2*)(file.constData() + sectionOffset); + sectionSize = sectionHeader2->ExtendedSize; return ERR_SUCCESS; } @@ -1670,7 +1679,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } } else if (certificateHeader->CertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { - info += tr("\nSignature type: PCKS7"); + info += tr("\nSignature type: PKCS7"); // TODO: show signature info in Information panel } else { @@ -2219,28 +2228,51 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte if (header.size() != sizeof(EFI_FFS_FILE_HEADER)) return ERR_INVALID_FILE; - QByteArray newHeader = header; - QByteArray newBody = body; - EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newHeader.data(); + QByteArray newObject = header + body; + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newObject.data(); + + // Determine correct file header size + bool largeFile = false; + UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER); + if (revision == 2 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + largeFile = true; + headerSize = sizeof(EFI_FFS_FILE_HEADER2); + } + + QByteArray newHeader = newObject.left(headerSize); + QByteArray newBody = newObject.mid(headerSize); // Check if the file has a tail - UINT8 tailSize = fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT ? sizeof(UINT16) : 0; + UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0; if (tailSize) { // Remove the tail, it will then be added back for revision 1 volumes newBody = newBody.left(newBody.size() - tailSize); - // Remove the attribute for rev2+ volumes - if (revision != 1) { - fileHeader->Attributes &= ~(FFS_ATTRIB_TAIL_PRESENT); - } } // Correct file size - uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + newBody.size() + tailSize, fileHeader->Size); + if (!largeFile) { + if (newBody.size() >= 0xFFFFFF) { + return ERR_INVALID_FILE; + } + + uint32ToUint24(headerSize + newBody.size() + tailSize, fileHeader->Size); + } + else { + uint32ToUint24(0xFFFFFF, fileHeader->Size); + EFI_FFS_FILE_HEADER2* fileHeader2 = (EFI_FFS_FILE_HEADER2*)newHeader.data(); + fileHeader2->ExtendedSize = headerSize + newBody.size() + tailSize; + } + + // Set file state + UINT8 state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION; + if (erasePolarity) + state = ~state; + fileHeader->State = state; // Recalculate header checksum fileHeader->IntegrityCheck.Checksum.Header = 0; fileHeader->IntegrityCheck.Checksum.File = 0; - fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)fileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1); + fileHeader->IntegrityCheck.Checksum.Header = 0x100 - (calculateSum8((const UINT8*)newHeader.constData(), headerSize) - fileHeader->State); // Recalculate data checksum, if needed if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) @@ -2254,23 +2286,17 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte created.append(newBody); // Append tail, if needed - if (revision ==1 && tailSize) { + if (revision == 1 && tailSize) { UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; created.append(ht).append(ft); } - // Set file state - UINT8 state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION; - if (erasePolarity) - state = ~state; - fileHeader->State = state; - // Prepend header created.prepend(newHeader); // Parse file - result = parseFile(created, fileIndex, erasePolarity ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index, mode); + result = parseFile(created, fileIndex, revision, erasePolarity ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index, mode); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; @@ -2428,17 +2454,20 @@ UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, co } else if (model->type(parent) == Types::File) { type = Types::Section; - const EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData(); + const EFI_COMMON_SECTION_HEADER* commonHeader = (const EFI_COMMON_SECTION_HEADER*)object.constData(); headerSize = sizeOfSectionHeader(commonHeader); } else if (model->type(parent) == Types::Section) { type = Types::Section; - const EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData(); + const EFI_COMMON_SECTION_HEADER* commonHeader = (const EFI_COMMON_SECTION_HEADER*)object.constData(); headerSize = sizeOfSectionHeader(commonHeader); } else return ERR_NOT_IMPLEMENTED; + if ((UINT32)object.size() < headerSize) + return ERR_BUFFER_TOO_SMALL; + return create(index, type, object.left(headerSize), object.right(object.size() - headerSize), mode, Actions::Insert); } @@ -2873,6 +2902,9 @@ UINT8 FfsEngine::constructPadFile(const QByteArray &guid, const UINT32 size, con if (size < sizeof(EFI_FFS_FILE_HEADER) || erasePolarity == ERASE_POLARITY_UNKNOWN) return ERR_INVALID_PARAMETER; + if (size >= 0xFFFFFF) // TODO: large file support + return ERR_INVALID_PARAMETER; + pad = QByteArray(size - guid.size(), erasePolarity == ERASE_POLARITY_TRUE ? '\xFF' : '\x00'); pad.prepend(guid); EFI_FFS_FILE_HEADER* header = (EFI_FFS_FILE_HEADER*)pad.data(); @@ -3265,6 +3297,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)){ QModelIndex peiFile = index.child(i, 0); UINT32 sectionOffset = sizeof(EFI_FFS_FILE_HEADER); + // BUGBUG: this parsing is bad and doesn't support large files, but it needs to be performed only for very old images with uncompressed DXE volumes, so whatever // Search for PE32 or TE section for (int j = 0; j < model->rowCount(peiFile); j++) { if (model->subtype(peiFile.child(j, 0)) == EFI_SECTION_PE32 || @@ -3330,6 +3363,9 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon continue; EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)file.data(); + UINT32 fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER); + if (volumeHeader->Revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) + fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER2); // Pad file if (fileHeader->Type == EFI_FV_FILETYPE_PAD) { @@ -3357,8 +3393,8 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon UINT8 alignmentPower; UINT32 alignmentBase; alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; - alignment = (UINT32)pow(2.0, alignmentPower); - alignmentBase = header.size() + offset + sizeof(EFI_FFS_FILE_HEADER); + alignment = (UINT32)(1UL <action(index) == Actions::Rebuild) { QByteArray header = model->header(index); EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)header.data(); - + // Check erase polarity if (erasePolarity == ERASE_POLARITY_UNKNOWN) { msg(tr("reconstructFile: unknown erase polarity"), index); @@ -3635,6 +3671,10 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, // File contains sections else { UINT32 offset = 0; + UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER); + if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + headerSize = sizeof(EFI_FFS_FILE_HEADER2); + } for (int i = 0; i < model->rowCount(index); i++) { // Align to 4 byte boundary @@ -3646,7 +3686,7 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, } // Calculate section base - UINT32 sectionBase = base ? base + sizeof(EFI_FFS_FILE_HEADER) + offset : 0; + UINT32 sectionBase = base ? base + headerSize + offset : 0; // Reconstruct section QByteArray section; @@ -3668,13 +3708,22 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, // Correct file size UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0; - - uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize, fileHeader->Size); + if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + uint32ToUint24(EFI_SECTION2_IS_USED, fileHeader->Size); + EFI_FFS_FILE_HEADER2* fileHeader2 = (EFI_FFS_FILE_HEADER2*) fileHeader; + fileHeader2->ExtendedSize = sizeof(EFI_FFS_FILE_HEADER2) + reconstructed.size() + tailSize; + } else { + if (sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize > 0xFFFFFF) { + msg(tr("reconstructFile: resulting file size is too big"), index); + return ERR_INVALID_FILE; + } + uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize, fileHeader->Size); + } // Recalculate header checksum fileHeader->IntegrityCheck.Checksum.Header = 0; fileHeader->IntegrityCheck.Checksum.File = 0; - fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)fileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1); + fileHeader->IntegrityCheck.Checksum.Header = 0x100 - (calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->State); } // Use current file body else @@ -3690,7 +3739,7 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; // Append tail, if needed - if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { + if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; reconstructed.append(ht).append(ft); @@ -3732,6 +3781,10 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, model->action(index) == Actions::Rebase) { QByteArray header = model->header(index); EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)header.data(); + bool extended = false; + if(uint24ToUint32(commonHeader->Size) == 0xFFFFFF) { + extended = true; + } // Reconstruct section with children if (model->rowCount(index)) { @@ -3832,7 +3885,13 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, } // Correct section size - uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size); + if (extended) { + EFI_COMMON_SECTION_HEADER2 * extHeader = (EFI_COMMON_SECTION_HEADER2*) commonHeader; + extHeader->ExtendedSize = header.size() + reconstructed.size(); + uint32ToUint24(0xFFFFFF, commonHeader->Size); + } else { + uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size); + } } // Leaf section else diff --git a/ffsengine.h b/ffsengine.h index 4fe5223..32a88ff 100644 --- a/ffsengine.h +++ b/ffsengine.h @@ -70,7 +70,7 @@ public: UINT8 parseEcRegion(const QByteArray & ec, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseBios(const QByteArray & bios, const QModelIndex & parent = QModelIndex()); UINT8 parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); - UINT8 parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); + UINT8 parseFile(const QByteArray & file, QModelIndex & index, const UINT8 revision = 2, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseSections(const QByteArray & body, const QModelIndex & parent = QModelIndex()); UINT8 parseSection(const QByteArray & section, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); @@ -117,7 +117,6 @@ private: UINT8 parseDepexSection(const QByteArray & body, QString & parsed); UINT8 findNextVolume(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); UINT8 getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize); - UINT8 getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize); UINT8 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize); // Reconstruction helpers diff --git a/uefitool.cpp b/uefitool.cpp index 34ca9e0..5fec49a 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.21.5")) +version(tr("0.22.0")) { clipboard = QApplication::clipboard(); From 3ffdae1123b0c3a4c263160f4a123f99fa6c7f64 Mon Sep 17 00:00:00 2001 From: Alex Matrosov Date: Sun, 10 Dec 2017 21:54:50 -0800 Subject: [PATCH 27/27] 0.22.1 multiple FFSv3 support fixes, thanks to @osresearch + multiple FFSv3 support fixes, thanks to @osresearch + fixed removal of FFS files with opposite EP bit value + disabled creation of large sections (too buggy now, will be fixed later) --- UEFIPatch/uefipatch_main.cpp | 2 +- ffs.h | 1 + ffsengine.cpp | 80 ++++++++++++++++++++---------------- uefitool.cpp | 2 +- 4 files changed, 47 insertions(+), 38 deletions(-) diff --git a/UEFIPatch/uefipatch_main.cpp b/UEFIPatch/uefipatch_main.cpp index aac0ca7..36d4f99 100644 --- a/UEFIPatch/uefipatch_main.cpp +++ b/UEFIPatch/uefipatch_main.cpp @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) result = w.patchFromFile(a.arguments().at(1)); } else { - std::cout << "UEFIPatch 0.3.10 - UEFI image file patching utility" << std::endl << std::endl << + std::cout << "UEFIPatch 0.3.11 - UEFI image file patching utility" << std::endl << std::endl << "Usage: UEFIPatch image_file" << std::endl << std::endl << "Patches will be read from patches.txt file\n"; return ERR_SUCCESS; diff --git a/ffs.h b/ffs.h index c761551..d9aca9a 100644 --- a/ffs.h +++ b/ffs.h @@ -346,6 +346,7 @@ extern const UINT8 ffsAlignment2Table[]; #define EFI_FILE_MARKED_FOR_UPDATE 0x08 #define EFI_FILE_DELETED 0x10 #define EFI_FILE_HEADER_INVALID 0x20 +#define EFI_FILE_ERASE_POLARITY 0x80 // Defined as "all other bits must be set to ERASE_POLARITY" in UEFI PI // PEI apriori file const QByteArray EFI_PEI_APRIORI_FILE_GUID diff --git a/ffsengine.cpp b/ffsengine.cpp index 9c66dbf..79bcb47 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -848,7 +848,7 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) } else if (volumeHeader->Revision == 2) { // Acquire alignment - alignment = (UINT32)pow(2.0, (int)(volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16); + alignment = (UINT32)(1UL << ((volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16)); // Check alignment if (volumeOffset % alignment) @@ -1391,6 +1391,9 @@ UINT8 FfsEngine::getSectionSize(const QByteArray & file, const UINT32 sectionOff if (sectionSize != 0xFFFFFF) return ERR_SUCCESS; + if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER2)) + return ERR_INVALID_FILE; + const EFI_COMMON_SECTION_HEADER2* sectionHeader2 = (const EFI_COMMON_SECTION_HEADER2*)(file.constData() + sectionOffset); sectionSize = sectionHeader2->ExtendedSize; return ERR_SUCCESS; @@ -1545,7 +1548,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c QString info; QByteArray header; QByteArray body; - UINT32 headerSize; + UINT32 headerSize = sizeOfSectionHeader(sectionHeader); UINT8 result; switch (sectionHeader->Type) { @@ -1556,8 +1559,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c QByteArray decompressed; UINT8 algorithm; const EFI_COMPRESSION_SECTION* compressedSectionHeader = (const EFI_COMPRESSION_SECTION*)sectionHeader; - header = section.left(sizeof(EFI_COMPRESSION_SECTION)); - body = section.mid(sizeof(EFI_COMPRESSION_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); algorithm = COMPRESSION_ALGORITHM_UNKNOWN; // Decompress section result = decompress(body, compressedSectionHeader->CompressionType, decompressed, &algorithm); @@ -1597,12 +1600,10 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c bool msgUnknownSignature = false; bool msgUnknownUefiGuidSignature = false; - const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader; - header = section.left(sizeof(EFI_GUID_DEFINED_SECTION)); - guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(header.constData()); - header = section.left(guidDefinedSectionHeader->DataOffset); - guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(header.constData()); - body = section.mid(guidDefinedSectionHeader->DataOffset); + header = section.left(headerSize); + body = section.mid(headerSize); + + const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(header.constData()); QByteArray processed = body; // Get info @@ -1757,8 +1758,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c case EFI_SECTION_DISPOSABLE: { - header = section.left(sizeof(EFI_DISPOSABLE_SECTION)); - body = section.mid(sizeof(EFI_DISPOSABLE_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") @@ -1781,7 +1782,6 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c case EFI_SECTION_PEI_DEPEX: case EFI_SECTION_SMM_DEPEX: { bool msgDepexParseFailed = false; - headerSize = sizeOfSectionHeader(sectionHeader); header = section.left(headerSize); body = section.mid(headerSize); @@ -1809,7 +1809,6 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } break; case EFI_SECTION_TE: { - headerSize = sizeOfSectionHeader(sectionHeader); header = section.left(headerSize); body = section.mid(headerSize); @@ -1860,7 +1859,6 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c case EFI_SECTION_PE32: case EFI_SECTION_PIC: { - headerSize = sizeOfSectionHeader(sectionHeader); header = section.left(headerSize); body = section.mid(headerSize); @@ -1956,7 +1954,6 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } break; case EFI_SECTION_COMPATIBILITY16: { - headerSize = sizeOfSectionHeader(sectionHeader); header = section.left(headerSize); body = section.mid(headerSize); @@ -1972,8 +1969,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } break; case EFI_SECTION_FREEFORM_SUBTYPE_GUID: { - header = section.left(sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION)); - body = section.mid(sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); const EFI_FREEFORM_SUBTYPE_GUID_SECTION* fsgHeader = (const EFI_FREEFORM_SUBTYPE_GUID_SECTION*)sectionHeader; // Get info @@ -1992,8 +1989,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } break; case EFI_SECTION_VERSION: { - header = section.left(sizeof(EFI_VERSION_SECTION)); - body = section.mid(sizeof(EFI_VERSION_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); const EFI_VERSION_SECTION* versionHeader = (const EFI_VERSION_SECTION*)sectionHeader; @@ -2011,8 +2008,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } break; case EFI_SECTION_USER_INTERFACE: { - header = section.left(sizeof(EFI_USER_INTERFACE_SECTION)); - body = section.mid(sizeof(EFI_USER_INTERFACE_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); QString text = QString::fromUtf16((const ushort*)body.constData()); // Get info @@ -2031,8 +2028,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } break; case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: { - header = section.left(sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); - body = section.mid(sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") @@ -2054,8 +2051,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c case EFI_SECTION_RAW: { bool parsed = false; - header = section.left(sizeof(EFI_RAW_SECTION)); - body = section.mid(sizeof(EFI_RAW_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") @@ -2109,8 +2106,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c case SCT_SECTION_POSTCODE: case INSYDE_SECTION_POSTCODE: { - header = section.left(sizeof(POSTCODE_SECTION)); - body = section.mid(sizeof(POSTCODE_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); const POSTCODE_SECTION* postcodeHeader = (const POSTCODE_SECTION*)sectionHeader; @@ -2127,8 +2124,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } break; default: - header = section.left(sizeof(EFI_COMMON_SECTION_HEADER)); - body = section.mid(sizeof(EFI_COMMON_SECTION_HEADER)); + header = section.left(headerSize); + body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") .hexarg2(sectionHeader->Type, 2) @@ -2234,7 +2231,7 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte // Determine correct file header size bool largeFile = false; UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER); - if (revision == 2 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { largeFile = true; headerSize = sizeof(EFI_FFS_FILE_HEADER2); } @@ -2310,12 +2307,17 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte if (model->type(parent) != Types::File && model->type(parent) != Types::Section) return ERR_INVALID_SECTION; - if (header.size() < (int) sizeof(EFI_COMMON_SECTION_HEADER)) + if ((UINT32)header.size() < sizeof(EFI_COMMON_SECTION_HEADER)) return ERR_INVALID_SECTION; QByteArray newHeader = header; EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)newHeader.data(); + if (uint24ToUint32(commonHeader->Size) == EFI_SECTION2_IS_USED) { + msg(tr("create: creation of large sections not supported yet"), index); + return ERR_NOT_IMPLEMENTED; + } + switch (commonHeader->Type) { case EFI_SECTION_COMPRESSION: { @@ -2547,9 +2549,15 @@ UINT8 FfsEngine::extract(const QModelIndex & index, QByteArray & extracted, cons extracted.append(model->header(index)); extracted.append(model->body(index)); if (model->type(index) == Types::File) { - //!TODO: add volume revision check, maybe? + UINT8 revision = 2; + QModelIndex parent = model->parent(index); + if (parent.isValid() && model->type(parent) == Types::Volume) { + const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)model->header(parent).constData(); + revision = volumeHeader->Revision; + } + const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)model->header(index).constData(); - if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { + if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; extracted.append(ht).append(ft); @@ -3621,9 +3629,9 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, } // Check file state - // Invert it first if erase polarity is true + // Check top reserved bit of file state to determine it's original erase polarity UINT8 state = fileHeader->State; - if (erasePolarity == ERASE_POLARITY_TRUE) + if (state & EFI_FILE_ERASE_POLARITY) state = ~state; // Order of this checks must be preserved diff --git a/uefitool.cpp b/uefitool.cpp index 5fec49a..befe98a 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.22.0")) +version(tr("0.22.1")) { clipboard = QApplication::clipboard();