From 2ec7ce1c301a4aa9a8f52649462f0265ddd13e61 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Fri, 6 Feb 2015 09:47:19 +0100 Subject: [PATCH] Engine 0.20.1 -reverted some changes done in 0.20.0 update -added proper handling of non-standard data in volume's free space - new type "Free space" added - added machine type information - solved a typo in DOS/PE signature check --- .gitignore | 10 + README.rst | 2 +- ffs.cpp | 2 +- ffsengine.cpp | 554 +++++++++++++++++++++++++++++--------------------- peimage.cpp | 30 +++ peimage.h | 18 +- treeitem.cpp | 40 ++-- treeitem.h | 17 +- treemodel.cpp | 49 +++-- treemodel.h | 11 +- types.cpp | 88 ++++---- types.h | 99 ++++----- uefitool.cpp | 31 +-- uefitool.pro | 1 + 14 files changed, 554 insertions(+), 398 deletions(-) create mode 100644 peimage.cpp diff --git a/.gitignore b/.gitignore index 8ba7c83..c7191ed 100644 --- a/.gitignore +++ b/.gitignore @@ -224,3 +224,13 @@ pip-log.txt #Mr Developer .mr.developer.cfg + +############# +## qmake / make +############# +*.o +Makefile +UEFIExtract/UEFIExtract +UEFIFind/UEFIFind +UEFIPatch/UEFIPatch +UEFITool \ No newline at end of file diff --git a/README.rst b/README.rst index dafe939..6c48723 100644 --- a/README.rst +++ b/README.rst @@ -38,7 +38,7 @@ Usage Known issues ------------ +* Some images has non-standard calculation of base address of TE images, so the program can rebase them incorrectly after modifications. Will be solved ASAP. * Some images may not work after modification because of no FIT table support implemented yet. It's on my high priority features list, so I hope it will be corrected soon. * The program is meant to work with BIOS images, not some vendor-specific BIOS update files, that is why some of that update file either can\t be opened at all or return errors on reconstruction. If someone wants to write an unpacker for such crappy files - I will be glad to use it. -* Search is searching only inside leaf elements of the tree, that is why some information can be found with hex editor but not with UEFITool. It's an intended behaviour and the tool tries to warn if there are some data in unusual places that must be empty by specifications (like the data inside padding files or after the last file of the UEFI volume). * AMI-specific features like NCBs, ROM_AREA structure and other things like that can't be implemented by me because of the NDA I have. diff --git a/ffs.cpp b/ffs.cpp index b94dfa0..9b50ea1 100644 --- a/ffs.cpp +++ b/ffs.cpp @@ -128,7 +128,7 @@ QString sectionTypeToQString(const UINT8 type) case EFI_SECTION_COMPRESSION: return QObject::tr("Compressed"); case EFI_SECTION_GUID_DEFINED: return QObject::tr("GUID defined"); case EFI_SECTION_DISPOSABLE: return QObject::tr("Disposable"); - case EFI_SECTION_PE32: return QObject::tr("PE32(+) image"); + case EFI_SECTION_PE32: return QObject::tr("PE32 image"); case EFI_SECTION_PIC: return QObject::tr("PIC image"); case EFI_SECTION_TE: return QObject::tr("TE image"); case EFI_SECTION_DXE_DEPEX: return QObject::tr("DXE dependency"); diff --git a/ffsengine.cpp b/ffsengine.cpp index 1c7e9a6..feaf899 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -146,7 +146,7 @@ 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 %1 bytes").arg(sizeof(EFI_CAPSULE_HEADER))); + msg(tr("parseImageFile: image file is smaller then minimum size of %1 bytes").arg(sizeof(EFI_CAPSULE_HEADER))); return ERR_INVALID_PARAMETER; } @@ -168,7 +168,7 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) .hexarg2(capsuleHeader->Flags, 8); // Add tree item - index = model->addItem(Types::Capsule, ATTR_CAPSULE_TYPE_UEFI20, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); + index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); } // Check buffer for being extended Aptio signed capsule header @@ -190,14 +190,8 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) //!TODO: more info about Aptio capsule - // Fill attributes - UINT32 attr = 0; - CAPSULE_ATTRIBUTES* attributes = (CAPSULE_ATTRIBUTES*)&attr; - attributes->Type = ATTR_CAPSULE_TYPE_APTIO; - attributes->Signed = signedCapsule; - // Add tree item - index = model->addItem(Types::Capsule, attr, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); + 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) { @@ -227,7 +221,7 @@ UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) .hexarg(flashImage.size()).arg(flashImage.size()); // Add tree item - index = model->addItem(Types::Image, ATTR_IMAGE_TYPE_UEFI, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), flashImage, index); + index = model->addItem(Types::Image, Subtypes::UefiImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), flashImage, QByteArray(), index); return parseBios(flashImage, index); } @@ -244,7 +238,7 @@ 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 %1 bytes").arg(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)); return ERR_INVALID_FLASH_DESCRIPTOR; } @@ -295,7 +289,7 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in // Check for Gigabyte specific descriptor map if (biosEnd - biosBegin == (UINT32)intelImage.size()) { if (!meEnd) { - msg(tr("parseIntelImage: can determine BIOS region start from Gigabyte-specific descriptor")); + msg(tr("parseIntelImage: can't determine BIOS region start from Gigabyte-specific descriptor")); return ERR_INVALID_FLASH_DESCRIPTOR; } biosBegin = meEnd; @@ -365,7 +359,7 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in .arg(descriptorMap->NumberOfIccTableEntries); // Add Intel image tree item - index = model->addItem(Types::Image, ATTR_IMAGE_TYPE_DESCRIPTOR, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), intelImage, parent); + index = model->addItem(Types::Image, Subtypes::IntelImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), intelImage, QByteArray(), parent); // Descriptor // Get descriptor info @@ -432,7 +426,7 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in } // Add descriptor tree item - model->addItem(Types::Region, ATTR_REGION_TYPE_DESCRIPTOR, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, index); + model->addItem(Types::Region, Subtypes::DescriptorRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, QByteArray(), index); // Sort regions in ascending order qSort(offsets); @@ -489,7 +483,7 @@ UINT8 FfsEngine::parseGbeRegion(const QByteArray & gbe, QModelIndex & index, con .arg(version->minor); // Add tree item - index = model->addItem(Types::Region, ATTR_REGION_TYPE_GBE, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), gbe, parent, mode); + index = model->addItem(Types::Region, Subtypes::GbeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), gbe, QByteArray(), parent, mode); return ERR_SUCCESS; } @@ -537,14 +531,8 @@ UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const } } - // Fill attributes - UINT32 attr = 0; - REGION_ATTRIBUTES* attributes = (REGION_ATTRIBUTES*)&attr; - attributes->Type = ATTR_REGION_TYPE_ME; - attributes->Empty = emptyRegion; - // Add tree item - index = model->addItem(Types::Region, attr, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), me, parent, mode); + index = model->addItem(Types::Region, Subtypes::MeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), me, QByteArray(), parent, mode); // Show messages if (emptyRegion) { @@ -569,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, ATTR_REGION_TYPE_PDR, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), pdr, parent, mode); + index = model->addItem(Types::Region, Subtypes::PdrRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), pdr, QByteArray(), parent, mode); // Parse PDR region as BIOS space UINT8 result = parseBios(pdr, index); @@ -590,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, ATTR_REGION_TYPE_BIOS, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), bios, parent, mode); + index = model->addItem(Types::Region, Subtypes::BiosRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), bios, QByteArray(), parent, mode); return parseBios(bios, index); } @@ -598,10 +586,10 @@ UINT8 FfsEngine::parseBiosRegion(const QByteArray & bios, QModelIndex & index, c UINT32 FfsEngine::getPaddingType(const QByteArray & padding) { if (padding.count('\x00') == padding.count()) - return ATTR_PADDING_ZERO_EMPTY; + return Subtypes::ZeroPadding; if (padding.count('\xFF') == padding.count()) - return ATTR_PADDING_ONE_EMPTY; - return ATTR_PADDING_DATA; + return Subtypes::OnePadding; + return Subtypes::DataPadding; } UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) @@ -625,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, parent); + model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, QByteArray(), parent); } // Search for and parse all volumes @@ -650,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, parent); + model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, QByteArray(), parent); } // Get volume size @@ -661,11 +649,10 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) // Check reported size if (volumeSize != bmVolumeSize) msgSizeMismach = true; - //!TODO: now we trust header size, sometimes it's the bmVolumeSize that is OK, need to implement some settings for it //Check that volume is fully present in input if (volumeOffset + volumeSize > (UINT32)bios.size()) { - msg(tr("parseBios: One of volumes inside overlaps the end of data"), parent); + msg(tr("parseBios: one of volumes inside overlaps the end of data"), parent); return ERR_INVALID_VOLUME; } @@ -695,19 +682,20 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) QModelIndex index; UINT8 result = parseVolume(bios.mid(volumeOffset, volumeSize), index, parent); if (result) - msg(tr("parseBios: Volume parsing failed with error \"%1\"").arg(errorMessage(result)), parent); + msg(tr("parseBios: volume parsing failed with error \"%1\"").arg(errorMessage(result)), parent); // Show messages if (msgAlignmentBitsSet) - msg("parseBios: Alignment bits set on volume without alignment capability", index); + msg("parseBios: alignment bits set on volume without alignment capability", index); if (msgUnaligned) - msg(tr("parseBios: Unaligned revision 2 volume"), index); + msg(tr("parseBios: unaligned revision 2 volume"), index); if (msgUnknownRevision) - msg(tr("parseBios: Unknown volume revision %1").arg(volumeHeader->Revision), index); + msg(tr("parseBios: unknown volume revision %1").arg(volumeHeader->Revision), index); if (msgSizeMismach) - msg(tr("parseBios: Volume size stored in header %1h differs from calculated using block map %2h") - .hexarg(volumeSize) - .hexarg(bmVolumeSize), index); + msg(tr("parseBios: volume size stored in header %1h (%2) differs from calculated using block map %3h (%4)") + .hexarg(volumeSize).arg(volumeSize) + .hexarg(bmVolumeSize).arg(bmVolumeSize), + index); // Go to next volume prevVolumeOffset = volumeOffset; @@ -724,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, parent); + model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, QByteArray(), parent); } break; } @@ -787,19 +775,17 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co headerSize = ALIGN8(headerSize); // Check for volume structure to be known - UINT32 attr = 0; - VOLUME_ATTRIBUTES* attributes = (VOLUME_ATTRIBUTES*)&attr; - attributes->Unknown = true; + bool volumeIsUnknown = true; + UINT8 volumeFfsVersion = 0; // Check for FFS v2 volume if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { - attributes->Unknown = false; - attributes->FsVersion = 2; + volumeIsUnknown = false; + volumeFfsVersion = 2; } //!TODO:Check for FFS v3 volume - - + // Check attributes // Determine value of empty byte char empty = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; @@ -813,18 +799,19 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co return result; // Check for Apple CRC32 in ZeroVector + bool volumeHasZVCRC = false; UINT32 crc32FromZeroVector = *(UINT32*)(volume.constData() + 8); 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) { - attributes->ZeroVectorCrc = true; + volumeHasZVCRC = true; } } // Check header checksum by recalculating it bool msgInvalidChecksum = false; - if (!attributes->Unknown && calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength)) + if (!volumeIsUnknown && calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength)) msgInvalidChecksum = true; // Get info @@ -844,7 +831,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co .arg(empty ? "1" : "0"); // Apple CRC32 volume - if (attributes->ZeroVectorCrc) { + if (volumeHasZVCRC) { info += tr("\nCRC32 in ZeroVector: valid"); } @@ -856,19 +843,30 @@ 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 = ParsingDataTypes::VolumeParsingData; + pdata->Data.Volume.HasZeroVectorCRC = volumeHasZVCRC; + + // Add text + QString text; + if (volumeHasZVCRC) + text += tr("ZeroVectorCRC "); + // Add tree item QByteArray header = volume.left(headerSize); QByteArray body = volume.mid(headerSize, volumeSize - headerSize); - index = model->addItem(Types::Volume, attr, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); + index = model->addItem(Types::Volume, volumeIsUnknown ? Subtypes::UnknownVolume : Subtypes::Ffs2Volume, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parsingData, parent, mode); // Show messages - if (attributes->Unknown) { - msg(tr("parseVolume: Unknown file system %1").arg(guidToQString(volumeHeader->FileSystemGuid)), index); + if (volumeIsUnknown) { + msg(tr("parseVolume: unknown file system %1").arg(guidToQString(volumeHeader->FileSystemGuid)), index); // Do not parse unknown volumes return ERR_SUCCESS; } if (msgInvalidChecksum) { - msg(tr("parseVolume: Volume header checksum is invalid"), index); + msg(tr("parseVolume: volume header checksum is invalid"), index); } // Search for and parse all files @@ -886,7 +884,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // Check file size to be at least size of EFI_FFS_FILE_HEADER if (fileSize < sizeof(EFI_FFS_FILE_HEADER)) { - msg(tr("parseVolume: Volume has FFS file with invalid size"), index); + msg(tr("parseVolume: volume has FFS file with invalid size"), index); return ERR_INVALID_FILE; } @@ -896,9 +894,37 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co // If we are at empty space in the end of volume if (header.count(empty) == header.size()) { // Check free space to be actually free - QByteArray freeSpace = volume.right(volumeSize - fileOffset); - if (freeSpace.count(empty) != freeSpace.count()) - msg(tr("parseVolume: Non-UEFI data found in volume's free space will be destroyed after volume modification"), index); + QByteArray freeSpace = volume.mid(fileOffset); + if (freeSpace.count(empty) != freeSpace.count()) { + msg(tr("parseVolume: non-standard data found in volume's free space"), index); + + // Search for the first non-empty byte + UINT32 i; + UINT32 size = freeSpace.size(); + const CHAR8* current = freeSpace.constData(); + for (i = 0; i < size; i++) { + if (*current++ != empty) + break; + } + + // Align found index to file alignment + // It must be possible because minimum 16 bytes of empty were found before + if (i != ALIGN8(i)) + i = ALIGN8(i) - 8; + + // 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); + } + // ... and all bytes after as a padding + QByteArray padding = freeSpace.mid(i); + 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); + } + 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); + } break; // Exit from loop } @@ -922,11 +948,19 @@ 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 = ParsingDataTypes::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); + msg(tr("parseVolume: unaligned file %1").arg(guidToQString(fileHeader->Name)), fileIndex); if (msgDuplicateGuid) - msg(tr("parseVolume: File with duplicate GUID %1").arg(guidToQString(fileHeader->Name)), fileIndex); + msg(tr("parseVolume: file with duplicate GUID %1").arg(guidToQString(fileHeader->Name)), fileIndex); // Move to next file fileOffset += fileSize; @@ -949,7 +983,6 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U bool msgInvalidDataChecksum = false; bool msgInvalidTailValue = false; bool msgInvalidType = false; - bool msgNonEmptyPadFile = false; // Populate file header const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)file.constData(); @@ -1037,7 +1070,6 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U case EFI_FV_FILETYPE_SMM_CORE: break; case EFI_FV_FILETYPE_PAD: - parseCurrentFile = false; break; default: msgInvalidType = true; @@ -1045,13 +1077,15 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U }; // Check for empty file + bool parseAsNonEmptyPadFile = false; if (body.count(empty) == body.size()) { // No need to parse empty files parseCurrentFile = false; } // Check for non-empty pad file else if (fileHeader->Type == EFI_FV_FILETYPE_PAD) { - msgNonEmptyPadFile = true; + parseAsNonEmptyPadFile = true; + msg(tr("parseFile: non-empty pad-file contents will be destroyed after volume modifications"), index); } // Get info @@ -1059,8 +1093,9 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U QString info; if (fileHeader->Type != EFI_FV_FILETYPE_PAD) name = guidToQString(fileHeader->Name); - else - name = tr("Pad-file"); + 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") .arg(guidToQString(fileHeader->Name)) .hexarg2(fileHeader->Type, 2) @@ -1071,29 +1106,49 @@ 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, parent, mode); + index = model->addItem(Types::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Show messages if (msgInvalidHeaderChecksum) - msg(tr("parseFile: Invalid header checksum"), index); + msg(tr("parseFile: invalid header checksum"), index); if (msgInvalidDataChecksum) - msg(tr("parseFile: Invalid data checksum"), index); + msg(tr("parseFile: invalid data checksum"), index); if (msgInvalidTailValue) - msg(tr("parseFile: Invalid tail value"), index); + msg(tr("parseFile: invalid tail value"), index); if (msgInvalidType) - msg(tr("parseFile: Unknown file type %1h").arg(fileHeader->Type, 2), index); - if (msgNonEmptyPadFile) - msg(tr("parseFile: Non-empty pad file contents will be destroyed after volume modification"), index); - + msg(tr("parseFile: unknown file type %1h").arg(fileHeader->Type, 2), index); + + // No parsing needed if (!parseCurrentFile) return ERR_SUCCESS; + // Parse non-empty pad file + if (parseAsNonEmptyPadFile) { + // Search for the first non-empty byte + UINT32 i; + UINT32 size = body.size(); + const CHAR8* current = body.constData(); + for (i = 0; i < size; i++) { + if (*current++ != empty) + break; + } + // 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); + } + // ... and all bytes after as a padding + QByteArray padding = body.mid(i); + 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); + return ERR_SUCCESS; + } + // Parse file as BIOS space UINT8 result; if (parseAsBios) { result = parseBios(body, index); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) - msg(tr("parseFile: Parsing file as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); + msg(tr("parseFile: parsing file as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); return result; } @@ -1287,11 +1342,11 @@ 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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, QByteArray(), parent, mode); // Show message if (!parseCurrentSection) - msg(tr("parseSection: Decompression failed with error \"%1\"").arg(errorMessage(result)), index); + msg(tr("parseSection: decompression failed with error \"%1\"").arg(errorMessage(result)), index); else { // Parse decompressed data result = parseSections(decompressed, index); if (result) @@ -1317,7 +1372,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c // Get info name = guidToQString(guidDefinedSectionHeader->SectionDefinitionGuid); - info = tr("Section GUID: %1h\nType %2h\nFull size: %3h (%4)\nHeader size: %5h (%6)\nBody size: %7h (%8)\nData offset: %9h\nAttributes: %10h") + info = tr("Section GUID: %1\nType: %2h\nFull size: %3h (%4)\nHeader size: %5h (%6)\nBody size: %7h (%8)\nData offset: %9h\nAttributes: %10h") .arg(name) .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) @@ -1397,7 +1452,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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, QByteArray(), parent, mode); // Show messages if (msgUnknownGuid) @@ -1413,7 +1468,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c else { // Parse processed data if (!signature.isEmpty()) { // Add Intel signature padding to the tree - QModelIndex signatureIndex = model->addItem(Types::Padding, ATTR_PADDING_DATA, COMPRESSION_ALGORITHM_NONE, tr("Intel signature"), "", tr("Full size: %1h (%2)").hexarg(signature.size()).arg(signature.size()), QByteArray(), signature, index, mode); + QModelIndex signatureIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Intel signature"), "", tr("Full size: %1h (%2)").hexarg(signature.size()).arg(signature.size()), QByteArray(), signature, QByteArray(), index, mode); // Show message msg(tr("parseSection: Intel signature may become invalid after any modification of the following sections"), signatureIndex); } @@ -1437,7 +1492,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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Parse section body result = parseSections(body, index); @@ -1470,7 +1525,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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Show messages if (msgDepexParseFailed) @@ -1498,9 +1553,9 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c msgInvalidSignature = true; } else { - info += tr("\nSignature: %1h\nMachine type: %2h\nNumber of sections: %3\nSubsystem: %4h\nStrippedSize: %5h (%6)\nBaseOfCode: %7h\nRelativeEntryPoint: %8h\nImageBase: %9h\nEntryPoint: %10h") + 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) - .hexarg2(teHeader->Machine, 2) + .arg(machineTypeToQString(teHeader->Machine)) .arg(teHeader->NumberOfSections) .hexarg2(teHeader->Subsystem, 2) .hexarg(teHeader->StrippedSize).arg(teHeader->StrippedSize) @@ -1510,7 +1565,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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Show messages if (msgInvalidSignature) { @@ -1519,11 +1574,11 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c // Special case of PEI Core QModelIndex core = model->findParentOfType(index, Types::File); - if (core.isValid() && model->attributes(core) == EFI_FV_FILETYPE_PEI_CORE + if (core.isValid() && model->subtype(core) == EFI_FV_FILETYPE_PEI_CORE && oldPeiCoreEntryPoint == 0) { result = getEntryPoint(model->body(index), oldPeiCoreEntryPoint); if (result) - msg(tr("parseSection: Can't get original PEI core entry point"), index); + msg(tr("parseSection: can't get original PEI core entry point"), index); } } break; @@ -1540,12 +1595,10 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()); - // Get PE32(+) info + // Get PE info bool msgInvalidDosSignature = false; bool msgInvalidPeSignature = false; bool msgUnknownOptionalHeaderSignature = false; - bool renameToPE32 = false; - bool renameToPE32Plus = false; const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)body.constData(); if (dosHeader->e_magic != EFI_IMAGE_DOS_SIGNATURE) { @@ -1560,17 +1613,16 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c } else { const EFI_IMAGE_FILE_HEADER* imageFileHeader = (const EFI_IMAGE_FILE_HEADER*)(peHeader + 1); - info += tr("\nDOS signature: %1h\nPE signature: %2h\nMachine type: %3h\nNumber of sections: %4\nCharacteristics: %5h") + info += tr("\nDOS signature: %1h\nPE signature: %2h\nMachine type: %3\nNumber of sections: %4\nCharacteristics: %5h") .hexarg2(dosHeader->e_magic, 4) .hexarg2(peHeader->Signature, 8) - .hexarg2(imageFileHeader->Machine, 4) + .arg(machineTypeToQString(imageFileHeader->Machine)) .arg(imageFileHeader->NumberOfSections) .hexarg2(imageFileHeader->Characteristics, 4); EFI_IMAGE_OPTIONAL_HEADER_POINTERS_UNION optionalHeader; optionalHeader.H32 = (const EFI_IMAGE_OPTIONAL_HEADER32*)(imageFileHeader + 1); if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { - renameToPE32 = true; info += tr("\nOptional header signature: %1h\nSubsystem: %2h\nRelativeEntryPoint: %3h\nBaseOfCode: %4h\nImageBase: %5h\nEntryPoint: %6h") .hexarg2(optionalHeader.H32->Magic, 4) .hexarg2(optionalHeader.H32->Subsystem, 4) @@ -1580,7 +1632,6 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c .hexarg(optionalHeader.H32->ImageBase + optionalHeader.H32->AddressOfEntryPoint); } else if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { - renameToPE32Plus = true; info += tr("\nOptional header signature: %1h\nSubsystem: %2h\nRelativeEntryPoint: %3h\nBaseOfCode: %4h\nImageBase: %5h\nEntryPoint: %6h") .hexarg2(optionalHeader.H64->Magic, 4) .hexarg2(optionalHeader.H64->Subsystem, 4) @@ -1597,32 +1648,26 @@ 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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Show messages if (msgInvalidDosSignature) { msg("parseSection: PE32 image with invalid DOS signature", index); } - if (msgInvalidDosSignature) { + if (msgInvalidPeSignature) { msg("parseSection: PE32 image with invalid PE signature", index); } if (msgUnknownOptionalHeaderSignature) { msg("parseSection: PE32 image with unknown optional header signature", index); } - // Rename section - if (renameToPE32) - model->setName(index, tr("PE32 image")); - if (renameToPE32Plus) - model->setName(index, tr("PE32+ image")); - // Special case of PEI Core QModelIndex core = model->findParentOfType(index, Types::File); - if (core.isValid() && model->attributes(core) == EFI_FV_FILETYPE_PEI_CORE + if (core.isValid() && model->subtype(core) == EFI_FV_FILETYPE_PEI_CORE && oldPeiCoreEntryPoint == 0) { result = getEntryPoint(model->body(index), oldPeiCoreEntryPoint); if (result) - msg(tr("parseSection: Can't get original PEI core entry point"), index); + msg(tr("parseSection: can't get original PEI core entry point"), index); } } break; @@ -1639,7 +1684,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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); } break; case EFI_SECTION_FREEFORM_SUBTYPE_GUID: { @@ -1656,7 +1701,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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Rename section model->setName(index, guidToQString(fsgHeader->SubTypeGuid)); @@ -1678,7 +1723,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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); } break; case EFI_SECTION_USER_INTERFACE: { @@ -1695,7 +1740,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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Rename parent file model->setText(model->findParentOfType(parent, Types::File), text); @@ -1713,12 +1758,12 @@ 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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Parse section body as BIOS space result = parseBios(body, index); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) { - msg(tr("parseSection: Parsing firmware volume image section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); + msg(tr("parseSection: parsing firmware volume image section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); return result; } } break; @@ -1766,13 +1811,13 @@ 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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Parse section body as BIOS space 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); + msg(tr("parseSection: parsing raw section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); return result; } } @@ -1794,7 +1839,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, parent, mode); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); } break; default: @@ -1808,8 +1853,8 @@ 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, parent, mode); - msg(tr("parseSection: Section with unknown type %1h").hexarg2(sectionHeader->Type, 2), index); + index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + msg(tr("parseSection: section with unknown type %1h").hexarg2(sectionHeader->Type, 2), index); } return ERR_SUCCESS; } @@ -1832,19 +1877,18 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte // Create item if (type == Types::Region) { - UINT32 attr = model->attributes(index); - REGION_ATTRIBUTES* attributes = (REGION_ATTRIBUTES*)&attr; - switch (attributes->Type) { - case ATTR_REGION_TYPE_BIOS: + UINT8 type = model->subtype(index); + switch (type) { + case Subtypes::BiosRegion: result = parseBiosRegion(body, fileIndex, index, mode); break; - case ATTR_REGION_TYPE_ME: + case Subtypes::MeRegion: result = parseMeRegion(body, fileIndex, index, mode); break; - case ATTR_REGION_TYPE_GBE: + case Subtypes::GbeRegion: result = parseGbeRegion(body, fileIndex, index, mode); break; - case ATTR_REGION_TYPE_PDR: + case Subtypes::PdrRegion: result = parsePdrRegion(body, fileIndex, index, mode); break; default: @@ -2052,14 +2096,14 @@ void FfsEngine::rebasePeiFiles(const QModelIndex & index) for (int i = index.row(); i < model->rowCount(index.parent()); i++) { // PEI-file QModelIndex currentFileIndex = index.parent().child(i, 0); - if (model->attributes(currentFileIndex) == EFI_FV_FILETYPE_PEI_CORE || - model->attributes(currentFileIndex) == EFI_FV_FILETYPE_PEIM || - model->attributes(currentFileIndex) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER) { + if (model->subtype(currentFileIndex) == EFI_FV_FILETYPE_PEI_CORE || + model->subtype(currentFileIndex) == EFI_FV_FILETYPE_PEIM || + model->subtype(currentFileIndex) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER) { for (int j = 0; j < model->rowCount(currentFileIndex); j++) { // Section in that file QModelIndex currentSectionIndex = currentFileIndex.child(j, 0); // If section stores PE32 or TE image - if (model->attributes(currentSectionIndex) == EFI_SECTION_PE32 || model->attributes(currentSectionIndex) == EFI_SECTION_TE) + if (model->subtype(currentSectionIndex) == EFI_SECTION_PE32 || model->subtype(currentSectionIndex) == EFI_SECTION_TE) // Set rebase action model->setAction(currentSectionIndex, Actions::Rebase); } @@ -2187,7 +2231,7 @@ UINT8 FfsEngine::extract(const QModelIndex & index, QByteArray & extracted, cons if (model->type(index) == Types::Section) { QByteArray decompressed; UINT8 result; - if (model->attributes(index) == EFI_SECTION_COMPRESSION) { + if (model->subtype(index) == EFI_SECTION_COMPRESSION) { const EFI_COMPRESSION_SECTION* compressedHeader = (const EFI_COMPRESSION_SECTION*)model->header(index).constData(); result = decompress(model->body(index), compressedHeader->CompressionType, decompressed); if (result) @@ -2195,7 +2239,7 @@ UINT8 FfsEngine::extract(const QModelIndex & index, QByteArray & extracted, cons extracted.append(decompressed); return ERR_SUCCESS; } - else if (model->attributes(index) == EFI_SECTION_GUID_DEFINED) { + else if (model->subtype(index) == EFI_SECTION_GUID_DEFINED) { QByteArray decompressed; // Check if section requires processing const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)model->header(index).constData(); @@ -2385,7 +2429,7 @@ UINT8 FfsEngine::decompress(const QByteArray & compressedData, const UINT8 compr delete[] decompressed; return ERR_SUCCESS; default: - msg(tr("decompress: Unknown compression type %1").arg(compressionType)); + msg(tr("decompress: unknown compression type %1").arg(compressionType)); if (algorithm) *algorithm = COMPRESSION_ALGORITHM_UNKNOWN; return ERR_UNKNOWN_COMPRESSION_ALGORITHM; @@ -2515,7 +2559,7 @@ UINT8 FfsEngine::compress(const QByteArray & data, const UINT8 algorithm, QByteA } break; default: - msg(tr("compress: Unknown compression algorithm %1").arg(algorithm)); + msg(tr("compress: unknown compression algorithm %1").arg(algorithm)); return ERR_UNKNOWN_COMPRESSION_ALGORITHM; } } @@ -2598,33 +2642,31 @@ UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& rec if (result) return result; - UINT32 attr = model->attributes(index.child(i, 0)); - REGION_ATTRIBUTES* attributes = (REGION_ATTRIBUTES*)&attr; - - switch (attributes->Type) + UINT8 type = model->subtype(index.child(i, 0)); + switch (type) { - case ATTR_REGION_TYPE_GBE: + case Subtypes::GbeRegion: gbe = region; if (gbeBegin > offset) reconstructed.append(QByteArray(gbeBegin - offset, empty)); reconstructed.append(gbe); offset = gbeEnd; break; - case ATTR_REGION_TYPE_ME: + case Subtypes::MeRegion: me = region; if (meBegin > offset) reconstructed.append(QByteArray(meBegin - offset, empty)); reconstructed.append(me); offset = meEnd; break; - case ATTR_REGION_TYPE_BIOS: + case Subtypes::BiosRegion: bios = region; if (biosBegin > offset) reconstructed.append(QByteArray(biosBegin - offset, empty)); reconstructed.append(bios); offset = biosEnd; break; - case ATTR_REGION_TYPE_PDR: + case Subtypes::PdrRegion: pdr = region; if (pdrBegin > offset) reconstructed.append(QByteArray(pdrBegin - offset, empty)); @@ -2739,8 +2781,8 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon } else if (model->action(index) == Actions::Replace || model->action(index) == Actions::Rebuild) { - //!TODO: add check for weak aligned volume QByteArray header = model->header(index); + QByteArray body = model->body(index); EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); // Recalculate volume header checksum @@ -2748,13 +2790,8 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); // Get volume size - UINT32 volumeSize; - UINT32 bmVolumeSize; - result = getVolumeSize(header, 0, volumeSize, bmVolumeSize); - if (result) - return result; - //!TODO: now we trust header size, sometimes it's the bmVolumeSize that is OK, need to implement some settings for it - + UINT32 volumeSize = header.size() + body.size(); + // Reconstruct volume body if (model->rowCount(index)) { reconstructed.clear(); @@ -2794,15 +2831,15 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon // Search for first PEI-file and use it as base source UINT32 fileOffset = header.size(); for (int i = 0; i < model->rowCount(index); i++) { - if ((model->attributes(index.child(i, 0)) == EFI_FV_FILETYPE_PEI_CORE || - model->attributes(index.child(i, 0)) == EFI_FV_FILETYPE_PEIM || - model->attributes(index.child(i, 0)) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)){ + if ((model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_PEI_CORE || + model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_PEIM || + 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); // Search for PE32 or TE section for (int j = 0; j < model->rowCount(peiFile); j++) { - if (model->attributes(peiFile.child(j, 0)) == EFI_SECTION_PE32 || - model->attributes(peiFile.child(j, 0)) == EFI_SECTION_TE) { + if (model->subtype(peiFile.child(j, 0)) == EFI_SECTION_PE32 || + model->subtype(peiFile.child(j, 0)) == EFI_SECTION_TE) { QModelIndex image = peiFile.child(j, 0); // Check for correct action if (model->action(image) == Actions::Remove || model->action(image) == Actions::Insert) @@ -2837,78 +2874,107 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon QByteArray padFileGuid = EFI_FFS_PAD_FILE_GUID; QByteArray vtf; QModelIndex vtfIndex; + UINT32 nonUefiDataOffset = 0; + QByteArray nonUefiData; for (int i = 0; i < model->rowCount(index); i++) { - // Align to 8 byte boundary - UINT32 alignment = offset % 8; - if (alignment) { - alignment = 8 - alignment; - offset += alignment; - reconstructed.append(QByteArray(alignment, empty)); - } + // Inside a volume can be files, free space or padding with non-UEFI data + if (model->type(index.child(i, 0)) == Types::File) { // Next item is a file - // Calculate file base - UINT32 fileBase = volumeBase ? volumeBase + header.size() + offset : 0; - - // Reconstruct file - result = reconstructFile(index.child(i, 0), volumeHeader->Revision, polarity, fileBase, file); - if (result) - return result; - - // Empty file - if (file.isEmpty()) - continue; - - EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)file.data(); - - // Pad file - if (fileHeader->Type == EFI_FV_FILETYPE_PAD) { - padFileGuid = file.left(sizeof(EFI_GUID)); - continue; - } - - // Volume Top File - if (file.left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { - vtf = file; - vtfIndex = index.child(i, 0); - continue; - } - - // Normal file - // Ensure correct alignment - 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); - if (alignmentBase % alignment) { - // File will be unaligned if added as is, so we must add pad file before it - // Determine pad file size - UINT32 size = alignment - (alignmentBase % alignment); - // Required padding is smaller then minimal pad file size - while (size < sizeof(EFI_FFS_FILE_HEADER)) { - size += alignment; + // Align to 8 byte boundary + UINT32 alignment = offset % 8; + if (alignment) { + alignment = 8 - alignment; + offset += alignment; + reconstructed.append(QByteArray(alignment, empty)); } - // Construct pad file - QByteArray pad; - result = constructPadFile(padFileGuid, size, volumeHeader->Revision, polarity, pad); + + // Calculate file base + UINT32 fileBase = volumeBase ? volumeBase + header.size() + offset : 0; + + // Reconstruct file + result = reconstructFile(index.child(i, 0), volumeHeader->Revision, polarity, fileBase, file); if (result) return result; - // Append constructed pad file to volume body - reconstructed.append(pad); - offset += size; + + // Empty file + if (file.isEmpty()) + continue; + + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)file.data(); + + // 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 + continue; + } + // Skip empty pad-file + else + continue; + } + + // Volume Top File + if (file.left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { + vtf = file; + vtfIndex = index.child(i, 0); + continue; + } + + // Normal file + // Ensure correct alignment + 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); + if (alignmentBase % alignment) { + // File will be unaligned if added as is, so we must add pad file before it + // Determine pad file size + UINT32 size = alignment - (alignmentBase % alignment); + // Required padding is smaller then minimal pad file size + while (size < sizeof(EFI_FFS_FILE_HEADER)) { + size += alignment; + } + // Construct pad file + QByteArray pad; + result = constructPadFile(padFileGuid, size, volumeHeader->Revision, polarity, pad); + if (result) + return result; + // Append constructed pad file to volume body + reconstructed.append(pad); + offset += size; + } + + // Append current file to new volume body + reconstructed.append(file); + + // Change current file offset + offset += file.size(); + } + else if (model->type(index.child(i, 0)) == Types::FreeSpace) { //Next item is a free space + // Some data are located beyond free space + if (offset + (UINT32)model->body(index.child(i, 0)).size() < (UINT32)model->body(index).size()) { + // Get non-UEFI data and it's offset + nonUefiData = model->body(index.child(i + 1, 0)); + nonUefiDataOffset = body.size() - nonUefiData.size(); + break; + } } - - // Append current file to new volume body - reconstructed.append(file); - - // Change current file offset - offset += file.size(); } - // Insert VTF to it's correct place - if (!vtf.isEmpty()) { + // Check volume sanity + if (!vtf.isEmpty() && !nonUefiData.isEmpty()) { + msg(tr("reconstructVolume: both VTF and non-UEFI data found in the volume, reconstruction is not possible"), index); + return ERR_INVALID_VOLUME; + } + + // Insert VTF or non-UEFI data to it's correct place + if (!vtf.isEmpty()) { // VTF found // Determine correct VTF offset - UINT32 vtfOffset = volumeSize - header.size() - vtf.size(); + UINT32 vtfOffset = model->body(index).size() - vtf.size(); if (vtfOffset % 8) { msg(tr("reconstructVolume: wrong size of the Volume Top File"), index); @@ -2927,8 +2993,9 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon reconstructed.append(pad); } // No more space left in volume - else if (vtfOffset < offset) { - msg(tr("reconstructVolume: volume has no free space left"), index); + else if (offset > vtfOffset) { + msg(tr("reconstructVolume: no space left to insert VTF, need %1h (%2) byte(s) more") + .hexarg(offset - vtfOffset).arg(offset - vtfOffset), index); return ERR_INVALID_VOLUME; } @@ -2948,16 +3015,30 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon // Append VTF reconstructed.append(vtf); } + else if (!nonUefiData.isEmpty()) { //Non-UEFI data found + // No space left + if (offset > nonUefiDataOffset) { + msg(tr("reconstructVolume: no space left to insert non-UEFI data, need %1h (%2) byte(s) more") + .hexarg(offset - nonUefiDataOffset).arg(offset - nonUefiDataOffset), index); + return ERR_INVALID_VOLUME; + } + // Append additional free space + else if (nonUefiDataOffset > offset) { + reconstructed.append(QByteArray(nonUefiDataOffset - offset, empty)); + } + + // Append VTF + reconstructed.append(nonUefiData); + } else { // Fill the rest of volume space with empty char - UINT32 volumeBodySize = volumeSize - header.size(); - if (volumeBodySize > (UINT32)reconstructed.size()) { + if (body.size() > reconstructed.size()) { // Fill volume end with empty char - reconstructed.append(QByteArray(volumeBodySize - reconstructed.size(), empty)); + reconstructed.append(QByteArray(body.size() - reconstructed.size(), empty)); } - else if (volumeBodySize < (UINT32)reconstructed.size()) { + else if (body.size() < reconstructed.size()) { // Check if volume can be grown - // Root volume can't be grown yet + // Root volume can't be grown UINT8 parentType = model->type(index.parent()); if (parentType != Types::File && parentType != Types::Section) { msg(tr("reconstructVolume: root volume can't be grown"), index); @@ -2991,9 +3072,8 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon reconstructed = header.append(reconstructed); // Recalculate CRC32 in ZeroVector, if needed - UINT32 attr = model->attributes(index); - VOLUME_ATTRIBUTES* attributes = (VOLUME_ATTRIBUTES*)&attr; - if (attributes->ZeroVectorCrc) { + const PARSING_DATA* pdata = (const PARSING_DATA*)model->parsingData(index).constData(); + if (pdata->Type == ParsingDataTypes::VolumeParsingData && pdata->Data.Volume.HasZeroVectorCRC) { // Get current CRC32 value from volume header const UINT32 current = *(const UINT32*)(reconstructed.constData() + 8); // Calculate new value @@ -3093,7 +3173,7 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, reconstructed.clear(); // Construct new file body // File contains raw data, must be parsed as region - if (model->attributes(index) == EFI_FV_FILETYPE_ALL || model->attributes(index) == EFI_FV_FILETYPE_RAW) { + if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) { result = reconstructRegion(index, reconstructed); if (result) return result; @@ -3133,7 +3213,7 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, } // Correct file size - UINT8 tailSize = (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) ? sizeof(UINT16) : 0; + UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0; uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize, fileHeader->Size); @@ -3233,7 +3313,7 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, } // Only this 2 sections can have compressed body - if (model->attributes(index) == EFI_SECTION_COMPRESSION) { + if (model->subtype(index) == EFI_SECTION_COMPRESSION) { EFI_COMPRESSION_SECTION* compessionHeader = (EFI_COMPRESSION_SECTION*)header.data(); // Set new uncompressed size compessionHeader->UncompressedLength = reconstructed.size(); @@ -3255,7 +3335,7 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, // Replace new section body reconstructed = compressed; } - else if (model->attributes(index) == EFI_SECTION_GUID_DEFINED) { + else if (model->subtype(index) == EFI_SECTION_GUID_DEFINED) { EFI_GUID_DEFINED_SECTION* guidDefinedHeader = (EFI_GUID_DEFINED_SECTION*)header.data(); // Compress new section body QByteArray compressed; @@ -3287,7 +3367,7 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, } else if (model->compression(index) != COMPRESSION_ALGORITHM_NONE) { msg(tr("reconstructSection: incorrectly required compression for section of type %1") - .arg(model->attributes(index)), index); + .arg(model->subtype(index)), index); return ERR_INVALID_SECTION; } @@ -3299,13 +3379,13 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, reconstructed = model->body(index); // Rebase PE32 or TE image, if needed - if ((model->attributes(index) == EFI_SECTION_PE32 || model->attributes(index) == EFI_SECTION_TE) && - (model->attributes(index.parent()) == EFI_FV_FILETYPE_PEI_CORE || - model->attributes(index.parent()) == EFI_FV_FILETYPE_PEIM || - model->attributes(index.parent()) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)) { + if ((model->subtype(index) == EFI_SECTION_PE32 || model->subtype(index) == EFI_SECTION_TE) && + (model->subtype(index.parent()) == EFI_FV_FILETYPE_PEI_CORE || + model->subtype(index.parent()) == EFI_FV_FILETYPE_PEIM || + model->subtype(index.parent()) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)) { UINT16 teFixup = 0; //TODO: add proper handling - /*if (model->attributes(index) == EFI_SECTION_TE) { + /*if (model->subtype(index) == EFI_SECTION_TE) { const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)model->body(index).constData(); teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); }*/ @@ -3318,7 +3398,7 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, } // Special case of PEI Core rebase - if (model->attributes(index.parent()) == EFI_FV_FILETYPE_PEI_CORE) { + if (model->subtype(index.parent()) == EFI_FV_FILETYPE_PEI_CORE) { result = getEntryPoint(reconstructed, newPeiCoreEntryPoint); if (result) msg(tr("reconstructSection: can't get entry point of PEI core"), index); @@ -3345,7 +3425,7 @@ UINT8 FfsEngine::reconstruct(const QModelIndex &index, QByteArray& reconstructed switch (model->type(index)) { case Types::Image: - if (model->attributes(index) == ATTR_IMAGE_TYPE_DESCRIPTOR) { + if (model->subtype(index) == Subtypes::IntelImage) { result = reconstructIntelImage(index, reconstructed); if (result) return result; @@ -3742,7 +3822,7 @@ UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) UINT8 FfsEngine::patchVtf(QByteArray &vtf) { if (!oldPeiCoreEntryPoint) { - msg(tr("PEI Core entry point can't be determined. VTF can't be patched.")); + msg(tr("patchVtf: PEI Core entry point can't be determined. VTF can't be patched.")); return ERR_PEI_CORE_ENTRY_POINT_NOT_FOUND; } @@ -3754,7 +3834,7 @@ UINT8 FfsEngine::patchVtf(QByteArray &vtf) QByteArray old((char*)&oldPeiCoreEntryPoint, sizeof(oldPeiCoreEntryPoint)); int i = vtf.lastIndexOf(old); if (i == -1) { - msg(tr("PEI Core entry point can't be found in VTF. VTF not patched.")); + msg(tr("patchVtf: PEI Core entry point can't be found in VTF. VTF not patched.")); return ERR_SUCCESS; } UINT32* data = (UINT32*)(vtf.data() + i); @@ -3944,7 +4024,7 @@ UINT8 FfsEngine::recursiveDump(const QModelIndex & index, const QString & path, QString info = tr("Type: %1\nSubtype: %2\n%3%4") .arg(itemTypeToQString(model->type(index))) - .arg(itemAttributesToQString(model->type(index), model->attributes(index))) + .arg(itemSubtypeToQString(model->type(index), model->subtype(index))) .arg(model->text(index).isEmpty() ? "" : tr("Text: %1\n").arg(model->text(index))) .arg(model->info(index)); file.setFileName(tr("%1/info.txt").arg(path)); diff --git a/peimage.cpp b/peimage.cpp new file mode 100644 index 0000000..3f9085e --- /dev/null +++ b/peimage.cpp @@ -0,0 +1,30 @@ +/* peimage.cpp + +Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#include + +#include "peimage.h" + +QString machineTypeToQString(UINT16 machineType) +{ + switch (machineType){ + case IMAGE_FILE_MACHINE_AMD64: return QObject::tr("x86-64"); + case IMAGE_FILE_MACHINE_ARM: return QObject::tr("ARM"); + case IMAGE_FILE_MACHINE_ARMV7: return QObject::tr("ARMv7"); + case IMAGE_FILE_MACHINE_EBC: return QObject::tr("EBC"); + case IMAGE_FILE_MACHINE_I386: return QObject::tr("x86"); + case IMAGE_FILE_MACHINE_IA64: return QObject::tr("IA64"); + case IMAGE_FILE_MACHINE_THUMB: return QObject::tr("Thumb"); + default: return QObject::tr("Unknown %1").hexarg2(machineType, 4); + } +} \ No newline at end of file diff --git a/peimage.h b/peimage.h index 973dc3d..c81cbbf 100644 --- a/peimage.h +++ b/peimage.h @@ -16,9 +16,15 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef __PE_IMAGE_H__ #define __PE_IMAGE_H__ +#include + +#include "basetypes.h" + // Make sure we use right packing rules #pragma pack(push,1) +extern QString machineTypeToQString(UINT16 machineType); + // // PE32+ Subsystem type for EFI images // @@ -30,11 +36,13 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // // PE32+ Machine type for EFI images // -#define IMAGE_FILE_MACHINE_I386 0x014c -#define IMAGE_FILE_MACHINE_IA64 0x0200 -#define IMAGE_FILE_MACHINE_EBC 0x0EBC -#define IMAGE_FILE_MACHINE_X64 0x8664 -#define IMAGE_FILE_MACHINE_ARMTHUMB_MIXED 0x01c2 +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_ARM 0x01c0 +#define IMAGE_FILE_MACHINE_ARMV7 0x01c4 +#define IMAGE_FILE_MACHINE_EBC 0x0ebc +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_IA64 0x0200 +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 // // EXE file formats diff --git a/treeitem.cpp b/treeitem.cpp index f306d18..94514dd 100644 --- a/treeitem.cpp +++ b/treeitem.cpp @@ -15,19 +15,20 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "treeitem.h" #include "types.h" -TreeItem::TreeItem(const UINT8 type, const UINT32 attributes, const UINT8 compression, +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 & header, const QByteArray & body, const QByteArray & parsingData, TreeItem *parent) : itemAction(Actions::NoAction), itemType(type), - itemAttributes(attributes), + itemSubtype(subtype), itemCompression(compression), itemName(name), itemText(text), itemInfo(info), itemHeader(header), itemBody(body), + itemParsingData(parsingData), parentItem(parent) { } @@ -37,12 +38,6 @@ TreeItem::~TreeItem() qDeleteAll(childItems); } -/*void TreeItem::setDefaultNames() -{ - itemTypeName = itemTypeToQString(itemType); - itemSubtypeName = itemSubtypeToQString(itemType, itemSubtype); -}*/ - void TreeItem::appendChild(TreeItem *item) { childItems.append(item); @@ -96,8 +91,8 @@ QVariant TreeItem::data(int column) const return actionTypeToQString(itemAction); case 2: // Type return itemTypeToQString(itemType); - case 3: // Attributes - return itemAttributesToQString(itemType, itemAttributes); + case 3: // Subtype + return itemSubtypeToQString(itemType, itemSubtype); case 4: // Text return itemText; default: @@ -163,14 +158,14 @@ void TreeItem::setType(const UINT8 type) itemType = type; } -UINT32 TreeItem::attributes() const +UINT8 TreeItem::subtype() const { - return itemAttributes; + return itemSubtype; } -void TreeItem::setAttributes(const UINT32 attributes) +void TreeItem::setSubtype(const UINT8 subtype) { - itemAttributes = attributes; + itemSubtype = subtype; } @@ -189,6 +184,11 @@ QByteArray TreeItem::body() const return itemBody; } +QByteArray TreeItem::parsingData() const +{ + return itemParsingData; +} + bool TreeItem::hasEmptyHeader() const { return itemHeader.isEmpty(); @@ -199,6 +199,16 @@ 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 205ce50..8697a75 100644 --- a/treeitem.h +++ b/treeitem.h @@ -24,9 +24,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. class TreeItem { public: - TreeItem(const UINT8 type, const UINT32 attributes = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, + 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 & header = QByteArray(), const QByteArray & body = QByteArray(), const QByteArray & parsingData = QByteArray(), TreeItem *parent = 0); ~TreeItem(); @@ -51,8 +51,8 @@ public: UINT8 type() const; void setType(const UINT8 type); - UINT32 attributes() const; - void setAttributes(const UINT32 attributes); + UINT8 subtype() const; + void setSubtype(const UINT8 subtype); QString text() const; void setText(const QString &text); @@ -62,7 +62,11 @@ 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); @@ -76,13 +80,14 @@ private: QList childItems; UINT8 itemAction; UINT8 itemType; - UINT32 itemAttributes; + UINT8 itemSubtype; UINT8 itemCompression; QString itemName; QString itemText; QString itemInfo; QByteArray itemHeader; QByteArray itemBody; + QByteArray itemParsingData; TreeItem *parentItem; }; diff --git a/treemodel.cpp b/treemodel.cpp index 7fb4cdb..ef83561 100644 --- a/treemodel.cpp +++ b/treemodel.cpp @@ -138,12 +138,12 @@ UINT8 TreeModel::type(const QModelIndex &index) const return item->type(); } -UINT32 TreeModel::attributes(const QModelIndex &index) const +UINT8 TreeModel::subtype(const QModelIndex &index) const { if (!index.isValid()) return 0; TreeItem *item = static_cast(index.internalPointer()); - return item->attributes(); + return item->subtype(); } QByteArray TreeModel::header(const QModelIndex &index) const @@ -178,6 +178,22 @@ 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()) @@ -218,13 +234,13 @@ UINT8 TreeModel::compression(const QModelIndex &index) const return item->compression(); } -void TreeModel::setAttributes(const QModelIndex & index, const UINT32 attributes) +void TreeModel::setSubtype(const QModelIndex & index, const UINT8 subtype) { if (!index.isValid()) return; TreeItem *item = static_cast(index.internalPointer()); - item->setAttributes(attributes); + item->setSubtype(subtype); emit dataChanged(index, index); } @@ -258,15 +274,6 @@ void TreeModel::setText(const QModelIndex &index, const QString &data) emit dataChanged(index, index); } -/*QString TreeModel::name(const QModelIndex &index) const -{ - if (!index.isValid()) - return QString(); - - TreeItem *item = static_cast(index.internalPointer()); - return item->name(); -}*/ - void TreeModel::setAction(const QModelIndex &index, const UINT8 action) { if (!index.isValid()) @@ -277,9 +284,19 @@ void TreeModel::setAction(const QModelIndex &index, const UINT8 action) emit dataChanged(this->index(0, 0), index); } -QModelIndex TreeModel::addItem(const UINT8 type, const UINT32 attributes, const UINT8 compression, +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 & header, const QByteArray & body, const QByteArray & parsingData, const QModelIndex & parent, const UINT8 mode) { TreeItem *item = 0; @@ -301,7 +318,7 @@ QModelIndex TreeModel::addItem(const UINT8 type, const UINT32 attributes, const } } - TreeItem *newItem = new TreeItem(type, attributes, compression, name, text, info, header, body, parentItem); + TreeItem *newItem = new TreeItem(type, subtype, compression, name, text, info, header, body, parsingData, parentItem); if (mode == CREATE_MODE_APPEND) { emit layoutAboutToBeChanged(); parentItem->appendChild(newItem); diff --git a/treemodel.h b/treemodel.h index cd79c7b..3c8c210 100644 --- a/treemodel.h +++ b/treemodel.h @@ -44,25 +44,28 @@ public: void setAction(const QModelIndex &index, const UINT8 action); void setType(const QModelIndex &index, const UINT8 type); - void setAttributes(const QModelIndex &index, const UINT32 attributes); + void setSubtype(const QModelIndex &index, const UINT8 subtype); void setName(const QModelIndex &index, const QString &name); void setText(const QModelIndex &index, const QString &text); + void setParsingData(const QModelIndex &index, const QByteArray &data); QString name(const QModelIndex &index) const; QString text(const QModelIndex &index) const; QString info(const QModelIndex &index) const; UINT8 type(const QModelIndex &index) const; - UINT32 attributes(const QModelIndex &index) const; + UINT8 subtype(const QModelIndex &index) const; QByteArray header(const QModelIndex &index) const; bool hasEmptyHeader(const QModelIndex &index) const; QByteArray body(const QModelIndex &index) const; bool hasEmptyBody(const QModelIndex &index) const; + QByteArray parsingData(const QModelIndex &index) const; + bool hasEmptyParsingData(const QModelIndex &index) const; UINT8 action(const QModelIndex &index) const; UINT8 compression(const QModelIndex &index) const; - QModelIndex addItem(const UINT8 type, const UINT32 attributes = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, + 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 & header = QByteArray(), const QByteArray & body = QByteArray(), const QByteArray & parsingData = QByteArray(), const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); QModelIndex findParentOfType(const QModelIndex & index, UINT8 type) const; diff --git a/types.cpp b/types.cpp index 9bd2466..5904e9e 100644 --- a/types.cpp +++ b/types.cpp @@ -19,15 +19,15 @@ QString regionTypeToQString(const UINT8 type) { switch (type) { - case ATTR_REGION_TYPE_DESCRIPTOR: + case Subtypes::DescriptorRegion: return QObject::tr("Descriptor"); - case ATTR_REGION_TYPE_GBE: + case Subtypes::GbeRegion: return QObject::tr("GbE"); - case ATTR_REGION_TYPE_ME: + case Subtypes::MeRegion: return QObject::tr("ME/TXE"); - case ATTR_REGION_TYPE_BIOS: + case Subtypes::BiosRegion: return QObject::tr("BIOS"); - case ATTR_REGION_TYPE_PDR: + case Subtypes::PdrRegion: return QObject::tr("PDR"); default: return QObject::tr("Unknown"); @@ -53,77 +53,61 @@ QString itemTypeToQString(const UINT8 type) return QObject::tr("File"); case Types::Section: return QObject::tr("Section"); + case Types::FreeSpace: + return QObject::tr("Free space"); default: return QObject::tr("Unknown"); } } -QString itemAttributesToQString(const UINT8 type, const UINT8 attributes) +QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) { switch (type) { case Types::Root: case Types::Image: - if (attributes == ATTR_IMAGE_TYPE_DESCRIPTOR) + if (subtype == Subtypes::IntelImage) return QObject::tr("Intel"); - else if (attributes == ATTR_IMAGE_TYPE_UEFI) + else if (Subtypes::UefiImage) return QObject::tr("UEFI"); else - return QObject::tr("Unknown"); + return QObject::tr("Unknown subtype"); case Types::Padding: - if (attributes == ATTR_PADDING_ZERO_EMPTY) + if (subtype == Subtypes::ZeroPadding) return QObject::tr("Empty (0x00)"); - else if (attributes == ATTR_PADDING_ONE_EMPTY) + else if (subtype == Subtypes::OnePadding) return QObject::tr("Empty (0xFF)"); - else if (attributes == ATTR_PADDING_DATA) + else if (subtype == Subtypes::DataPadding) return QObject::tr("Non-empty"); else + return QObject::tr("Unknown subtype"); + case Types::Volume: + if (subtype == Subtypes::UnknownVolume) return QObject::tr("Unknown"); - case Types::Volume: { - QString string; - VOLUME_ATTRIBUTES* volumeAttr = (VOLUME_ATTRIBUTES*)&attributes; - if (volumeAttr->ZeroVectorCrc) - string += QObject::tr("ZVCRC "); - - if (volumeAttr->VtfPresent) - string += QObject::tr("Boot "); - - if (volumeAttr->Unknown) { - string += QObject::tr("Unknown"); - return string; - } - - if (volumeAttr->FsVersion == 2 || volumeAttr->FsVersion == 3) - string += QObject::tr("FFSv%1").arg(volumeAttr->FsVersion); + else if (subtype == Subtypes::Ffs2Volume) + return QObject::tr("FFSv2"); + else if (subtype == Subtypes::Ffs3Volume) + return QObject::tr("FFSv3"); else - return QObject::tr("Unknown FFS version"); - - return string; - } - case Types::Capsule: { - QString string; - CAPSULE_ATTRIBUTES* capsuleAttr = (CAPSULE_ATTRIBUTES*)&attributes; - if (capsuleAttr->Type == ATTR_CAPSULE_TYPE_APTIO) - string += QObject::tr("Aptio "); - else if (capsuleAttr->Type == ATTR_CAPSULE_TYPE_UEFI20) - string += QObject::tr("UEFI 2.0 "); + return QObject::tr("Unknown subtype"); + 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("Unknown type"); - - if (capsuleAttr->Signed) - string += QObject::tr("signed"); - else - string += QObject::tr("unsigned"); - - return string; - } + return QObject::tr("Unknown subtype"); case Types::Region: - return regionTypeToQString(attributes); + return regionTypeToQString(subtype); case Types::File: - return fileTypeToQString(attributes); + return fileTypeToQString(subtype); case Types::Section: - return sectionTypeToQString(attributes); + return sectionTypeToQString(subtype); + case Types::FreeSpace: + return QString(); default: - return QObject::tr("Unknown"); + return QObject::tr("Unknown subtype"); } } diff --git a/types.h b/types.h index c487f38..a65b73e 100644 --- a/types.h +++ b/types.h @@ -16,8 +16,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "basetypes.h" -#pragma pack(push, 1) - // Actions namespace Actions { @@ -42,61 +40,70 @@ namespace Types { Padding, Volume, File, - Section + Section, + FreeSpace }; } -// Capsule attributes -typedef struct _CAPSULE_ATTRIBUTES { - UINT32 Type : 7; - UINT32 Signed : 1; - UINT32 Reserved : 24; -} CAPSULE_ATTRIBUTES; -#define ATTR_CAPSULE_TYPE_UEFI20 0 -#define ATTR_CAPSULE_TYPE_APTIO 1 +namespace Subtypes { + enum ImageSubtypes{ + IntelImage = 70, + UefiImage + }; -typedef struct _IMAGE_ATTRIBUTES { - UINT32 IntelDescriptor : 1; - UINT32 Reserved : 31; -} IMAGE_ATTRIBUTES; -#define ATTR_IMAGE_TYPE_UEFI 0 -#define ATTR_IMAGE_TYPE_DESCRIPTOR 1 + enum CapsuleSubtypes { + AptioSignedCapsule = 80, + AptioUnsignedCapsule, + UefiCapsule + }; -typedef struct _REGION_ATTRIBUTES { - UINT32 Type : 7; - UINT32 Empty : 1; - UINT32 Reserved : 24; -} REGION_ATTRIBUTES; + enum VolumeSubtypes { + UnknownVolume = 90, + Ffs2Volume, + Ffs3Volume + }; -#define ATTR_REGION_TYPE_DESCRIPTOR 0 -#define ATTR_REGION_TYPE_GBE 1 -#define ATTR_REGION_TYPE_ME 2 -#define ATTR_REGION_TYPE_BIOS 3 -#define ATTR_REGION_TYPE_PDR 4 + enum RegionSubtypes { + DescriptorRegion = 100, + GbeRegion, + MeRegion, + BiosRegion, + PdrRegion + }; -typedef struct _VOLUME_ATTRIBUTES { - UINT32 Unknown : 1; - UINT32 VtfPresent : 1; - UINT32 ZeroVectorCrc : 1; - UINT32 FsVersion : 5; - UINT32 Reserved : 24; -} VOLUME_ATTRIBUTES; - -typedef struct _PADDING_ATTRIBUTES { - UINT32 Empty : 1; - UINT32 ErasePolarity : 1; - UINT32 Reserved : 30; -} PADDING_ATTRIBUTES; -#define ATTR_PADDING_DATA 0 -#define ATTR_PADDING_ZERO_EMPTY 1 -#define ATTR_PADDING_ONE_EMPTY 3 - -#pragma pack(pop) + enum PaddingSubtypes { + ZeroPadding = 110, + OnePadding, + DataPadding + }; +}; // *ToQString conversion routines extern QString actionTypeToQString(const UINT8 action); extern QString itemTypeToQString(const UINT8 type); -extern QString itemAttributesToQString(const UINT8 type, const UINT8 attributes); +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 8923006..bc885c9 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.0")) +version(tr("0.20.1")) { clipboard = QApplication::clipboard(); @@ -123,7 +123,7 @@ void UEFITool::populateUi(const QModelIndex ¤t) TreeModel* model = ffsEngine->treeModel(); UINT8 type = model->type(current); - UINT32 attributes = model->attributes(current); + UINT8 subtype = model->subtype(current); // Set info text ui->infoEdit->setPlainText(model->info(current)); @@ -142,12 +142,12 @@ void UEFITool::populateUi(const QModelIndex ¤t) ui->actionRebuild->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->actionExtractBody->setDisabled(model->hasEmptyBody(current)); ui->actionRemove->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); - ui->actionInsertInto->setEnabled((type == Types::Volume && ((VOLUME_ATTRIBUTES*)&attributes)->Unknown == 0) || - (type == Types::File && attributes != EFI_FV_FILETYPE_ALL && attributes != EFI_FV_FILETYPE_RAW && attributes != EFI_FV_FILETYPE_PAD) || - (type == Types::Section && (attributes == EFI_SECTION_COMPRESSION || attributes == EFI_SECTION_GUID_DEFINED || attributes == EFI_SECTION_DISPOSABLE))); + ui->actionInsertInto->setEnabled((type == Types::Volume && subtype != Subtypes::UnknownVolume) || + (type == Types::File && subtype != EFI_FV_FILETYPE_ALL && subtype != EFI_FV_FILETYPE_RAW && subtype != EFI_FV_FILETYPE_PAD) || + (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 && ((REGION_ATTRIBUTES*)&attributes)->Type != ATTR_REGION_TYPE_DESCRIPTOR) || type == Types::Volume || 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->actionReplaceBody->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->actionMessagesCopy->setEnabled(false); } @@ -336,9 +336,10 @@ void UEFITool::replace(const UINT8 mode) path = QFileDialog::getOpenFileName(this, tr("Select FFS file to replace selected object"), currentDir, "FFS files (*.ffs *.bin);;All files (*)"); } else if (mode == REPLACE_MODE_BODY) { - if (model->attributes(index) == EFI_FV_FILETYPE_ALL || model->attributes(index) == EFI_FV_FILETYPE_RAW) + if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) path = QFileDialog::getOpenFileName(this, tr("Select raw file to replace body"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); - else if (model->attributes(index) == EFI_FV_FILETYPE_PAD) // Pad file body can't be replaced + else if (model->subtype(index) == EFI_FV_FILETYPE_PAD) // Pad file body can't be replaced + //!TODO: handle non-empty pad files return; else path = QFileDialog::getOpenFileName(this, tr("Select FFS file body to replace body"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); @@ -351,11 +352,11 @@ void UEFITool::replace(const UINT8 mode) path = QFileDialog::getOpenFileName(this, tr("Select section file to replace selected object"), currentDir, "Section files (*.sec *.bin);;All files (*)"); } else if (mode == REPLACE_MODE_BODY) { - if (model->attributes(index) == EFI_SECTION_COMPRESSION || model->attributes(index) == EFI_SECTION_GUID_DEFINED || model->attributes(index) == EFI_SECTION_DISPOSABLE) + if (model->subtype(index) == EFI_SECTION_COMPRESSION || model->subtype(index) == EFI_SECTION_GUID_DEFINED || model->subtype(index) == EFI_SECTION_DISPOSABLE) path = QFileDialog::getOpenFileName(this, tr("Select FFS file body file to replace body"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); - else if (model->attributes(index) == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) + else if (model->subtype(index) == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) path = QFileDialog::getOpenFileName(this, tr("Select volume file to replace body"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); - else if (model->attributes(index) == EFI_SECTION_RAW) + 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 path = QFileDialog::getOpenFileName(this, tr("Select file to replace body"), currentDir, "Binary files (*.bin);;All files (*)"); @@ -450,18 +451,18 @@ void UEFITool::extract(const UINT8 mode) path = QFileDialog::getSaveFileName(this, tr("Save volume body to file"), currentDir, "Volume body files (*.vbd *.bin);;All files (*)"); break; case Types::File: { - if (model->attributes(index) == EFI_FV_FILETYPE_ALL || model->attributes(index) == EFI_FV_FILETYPE_RAW) + if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to raw file"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); else path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to file"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); } break; case Types::Section: { - if (model->attributes(index) == EFI_SECTION_COMPRESSION || model->attributes(index) == EFI_SECTION_GUID_DEFINED || model->attributes(index) == EFI_SECTION_DISPOSABLE) + if (model->subtype(index) == EFI_SECTION_COMPRESSION || model->subtype(index) == EFI_SECTION_GUID_DEFINED || model->subtype(index) == EFI_SECTION_DISPOSABLE) path = QFileDialog::getSaveFileName(this, tr("Save encapsulation section body to FFS body file"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); - else if (model->attributes(index) == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) + else if (model->subtype(index) == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) path = QFileDialog::getSaveFileName(this, tr("Save section body to volume file"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); - else if (model->attributes(index) == EFI_SECTION_RAW) + else if (model->subtype(index) == EFI_SECTION_RAW) path = QFileDialog::getSaveFileName(this, tr("Save section body to raw file"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); else path = QFileDialog::getSaveFileName(this, tr("Save section body to file"), currentDir, "Binary files (*.bin);;All files (*)"); diff --git a/uefitool.pro b/uefitool.pro index 9f1cc03..59f6c21 100644 --- a/uefitool.pro +++ b/uefitool.pro @@ -10,6 +10,7 @@ SOURCES += uefitool_main.cpp \ types.cpp \ descriptor.cpp \ ffs.cpp \ + peimage.cpp \ ffsengine.cpp \ treeitem.cpp \ treemodel.cpp \