diff --git a/UEFITool/ffsops.cpp b/UEFITool/ffsops.cpp index 82e7b18..c16c78a 100644 --- a/UEFITool/ffsops.cpp +++ b/UEFITool/ffsops.cpp @@ -44,7 +44,7 @@ STATUS FfsOperations::extract(const QModelIndex & index, QString & name, QByteAr return ERR_INVALID_PARAMETER; // Get data from parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); // Construct a name for extracted data QString itemName = model->name(index); diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index e6319c5..37b96df 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.30.0_alpha4")) +version(tr("0.30.0_alpha5")) { clipboard = QApplication::clipboard(); @@ -26,6 +26,7 @@ version(tr("0.30.0_alpha4")) searchDialog = new SearchDialog(this); model = NULL; ffsParser = NULL; + fitParser = NULL; ffsFinder = NULL; ffsOps = NULL; @@ -69,6 +70,7 @@ version(tr("0.30.0_alpha4")) ui->infoEdit->setFont(font); ui->parserMessagesListWidget->setFont(font); ui->finderMessagesListWidget->setFont(font); + ui->fitTableWidget->setFont(font); ui->structureTreeView->setFont(font); searchDialog->ui->guidEdit->setFont(font); searchDialog->ui->hexEdit->setFont(font); @@ -84,6 +86,7 @@ UEFITool::~UEFITool() { delete ffsOps; delete ffsFinder; + delete fitParser; delete ffsParser; delete model; delete searchDialog; @@ -95,6 +98,7 @@ void UEFITool::init() // Clear components ui->parserMessagesListWidget->clear(); ui->finderMessagesListWidget->clear(); + ui->fitTableWidget->clear(); ui->infoEdit->clear(); // Set window title @@ -120,6 +124,10 @@ void UEFITool::init() if (ffsParser) delete ffsParser; ffsParser = new FfsParser(model); + // ... and fitParser + if (fitParser) + delete fitParser; + fitParser = new FitParser(model); // Connect connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), @@ -602,6 +610,12 @@ void UEFITool::openImageFile(QString path) else ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName())); + // Parse FIT + //!TODO: expand and chek errors + result = fitParser->parse(model->index(0, 0), ffsParser->getLastVtf()); + if (!result) + showFitTable(); + // Enable search ... if (ffsFinder) delete ffsFinder; @@ -789,3 +803,78 @@ void UEFITool::writeSettings() settings.setValue("tree/columnWidth2", ui->structureTreeView->columnWidth(2)); settings.setValue("tree/columnWidth3", ui->structureTreeView->columnWidth(3)); } + +void UEFITool::showFitTable() +{ + QVector > fitEntries = fitParser->getFitEntries(); + if (fitEntries.isEmpty()) + return; + + // Set up the FIT table + ui->fitTableWidget->clear(); + ui->fitTableWidget->setRowCount(fitEntries.length()); + ui->fitTableWidget->setColumnCount(6); + //ui->fitTableWidget->verticalHeader()->setVisible(false); + ui->fitTableWidget->setHorizontalHeaderLabels(QStringList() << tr("Address") << tr("Size") << tr("Version") << tr("Type") << tr("Checksum") << tr("Remark")); + ui->fitTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->fitTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->fitTableWidget->setSelectionMode(QAbstractItemView::SingleSelection); + ui->fitTableWidget->horizontalHeader()->setStretchLastSection(true); + + // Add all data to the table widget + for (INT32 i = 0; i < fitEntries.length(); i++) { + FIT_ENTRY* entry = &(fitEntries[i].first); + if (i) + ui->fitTableWidget->setItem(i, 0, new QTableWidgetItem(tr("%1h").hexarg2(entry->Address, 16))); + else + ui->fitTableWidget->setItem(i, 0, new QTableWidgetItem(tr("_FIT_ "))); + + ui->fitTableWidget->setItem(i, 1, new QTableWidgetItem(tr("%1h (%2)").hexarg2(entry->Size * 16, 8).arg(entry->Size * 16))); + ui->fitTableWidget->setItem(i, 2, new QTableWidgetItem(tr("%1h").hexarg2(entry->Version, 4))); + + QString typeString; + switch (entry->Type & 0x7F) { + case FIT_TYPE_HEADER: + typeString.append(tr("Header")); + break; + case FIT_TYPE_MICROCODE: + typeString.append(tr("Microcode")); + break; + case FIT_TYPE_BIOS_AC_MODULE: + typeString.append(tr("BIOS ACM")); + break; + case FIT_TYPE_BIOS_INIT_MODULE: + typeString.append(tr("BIOS Init")); + break; + case FIT_TYPE_TPM_POLICY: + typeString.append(tr("TPM Policy")); + break; + case FIT_TYPE_BIOS_POLICY_DATA: + typeString.append(tr("BIOS Policy Data")); + break; + case FIT_TYPE_TXT_CONF_POLICY: + typeString.append(tr("TXT Configuration Policy")); + break; + case FIT_TYPE_AC_KEY_MANIFEST: + typeString.append(tr("BootGuard Key Manifest")); + break; + case FIT_TYPE_AC_BOOT_POLICY: + typeString.append(tr("BootGuard Boot Policy")); + break; + case FIT_TYPE_EMPTY: + typeString.append(tr("Empty")); + break; + default: + typeString.append(tr("Unknown")); + } + + ui->fitTableWidget->setItem(i, 3, new QTableWidgetItem(typeString)); + ui->fitTableWidget->setItem(i, 4, new QTableWidgetItem(tr("%1h").hexarg2(entry->Checksum, 2))); + ui->fitTableWidget->setItem(i, 5, new QTableWidgetItem(fitEntries[i].second)); + } + + ui->fitTableWidget->resizeColumnsToContents(); + ui->fitTableWidget->resizeRowsToContents(); + ui->messagesTabWidget->setCurrentIndex(2); + +} \ No newline at end of file diff --git a/UEFITool/uefitool.h b/UEFITool/uefitool.h index e996b93..7dd7766 100644 --- a/UEFITool/uefitool.h +++ b/UEFITool/uefitool.h @@ -37,6 +37,7 @@ #include "../common/utility.h" #include "../common/ffs.h" #include "../common/ffsparser.h" +#include "../common/fitparser.h" #include "searchdialog.h" #include "messagelistitem.h" #include "ffsfinder.h" @@ -97,6 +98,7 @@ private: Ui::UEFITool* ui; TreeModel* model; FfsParser* ffsParser; + FitParser* fitParser; FfsFinder* ffsFinder; FfsOperations* ffsOps; SearchDialog* searchDialog; @@ -110,6 +112,7 @@ private: void readSettings(); void showParserMessages(); void showFinderMessages(); + void showFitTable(); }; #endif diff --git a/UEFITool/uefitool.pro b/UEFITool/uefitool.pro index 659f416..067e83e 100644 --- a/UEFITool/uefitool.pro +++ b/UEFITool/uefitool.pro @@ -18,6 +18,7 @@ SOURCES += uefitool_main.cpp \ ../common/utility.cpp \ ../common/ffsbuilder.cpp \ ../common/ffsparser.cpp \ + ../common/fitparser.cpp \ ../common/treeitem.cpp \ ../common/treemodel.cpp \ ../common/LZMA/LzmaCompress.c \ @@ -47,6 +48,7 @@ HEADERS += uefitool.h \ ../common/parsingdata.h \ ../common/ffsbuilder.h \ ../common/ffsparser.h \ + ../common/fitparser.h \ ../common/treeitem.h \ ../common/treemodel.h \ ../common/LZMA/LzmaCompress.h \ diff --git a/UEFITool/uefitool.ui b/UEFITool/uefitool.ui index 1343690..b1eb808 100644 --- a/UEFITool/uefitool.ui +++ b/UEFITool/uefitool.ui @@ -202,6 +202,31 @@ + + + FIT + + + + 0 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + diff --git a/common/ffs.cpp b/common/ffs.cpp index cd2cff4..2649b3b 100644 --- a/common/ffs.cpp +++ b/common/ffs.cpp @@ -30,36 +30,6 @@ const QVector FFSv3Volumes = const UINT8 ffsAlignmentTable[] = { 0, 4, 7, 9, 10, 12, 15, 16 }; -UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize) -{ - if (!buffer) - return 0; - - UINT8 counter = 0; - - while (bufferSize--) - counter += buffer[bufferSize]; - - return (UINT8)(0x100 - counter); -} - -UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize) -{ - if (!buffer) - return 0; - - UINT16 counter = 0; - UINT32 index = 0; - - bufferSize /= sizeof(UINT16); - - for (; index < bufferSize; index++) { - counter = (UINT16)(counter + buffer[index]); - } - - return (UINT16)(0x10000 - counter); -} - VOID uint32ToUint24(UINT32 size, UINT8* ffsSize) { ffsSize[2] = (UINT8)((size) >> 16); diff --git a/common/ffs.h b/common/ffs.h index 4bbec90..ef13e67 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -240,9 +240,6 @@ typedef struct _EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE { //UINT8 Data[]; } EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE; -// Volume header 16bit checksum calculation routine -extern UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); - //***************************************************************************** // EFI FFS File //***************************************************************************** @@ -341,8 +338,6 @@ 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 -extern UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize); //***************************************************************************** // EFI FFS File Section diff --git a/common/ffsbuilder.cpp b/common/ffsbuilder.cpp index 7d2a9eb..dc489f9 100644 --- a/common/ffsbuilder.cpp +++ b/common/ffsbuilder.cpp @@ -43,7 +43,7 @@ STATUS FfsBuilder::erase(const QModelIndex & index, QByteArray & erased) if (!index.isValid()) return ERR_INVALID_PARAMETER; - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); erased.fill(pdata.emptyByte); return ERR_SUCCESS; } diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index c042302..465c865 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -85,7 +85,7 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & .hexarg2(capsuleHeader->Flags, 8); // Construct parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(QModelIndex()); + PARSING_DATA pdata = parsingDataFromQModelIndex(QModelIndex()); pdata.fixed = TRUE; // Add tree item @@ -108,7 +108,7 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & .hexarg2(capsuleHeader->CapsuleHeader.Flags, 8); // Construct parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(QModelIndex()); + PARSING_DATA pdata = parsingDataFromQModelIndex(QModelIndex()); pdata.fixed = TRUE; // Add tree item @@ -146,7 +146,7 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & .hexarg(capsuleHeaderSize).hexarg(flashImage.size()).arg(flashImage.size()); // Construct parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); pdata.fixed = TRUE; pdata.offset = capsuleHeaderSize; @@ -160,7 +160,7 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & // Check if the last VTF is found if (!lastVtf.isValid()) { - msg(tr("parseImageFile: not a single Volume Top File is found, physical memory addresses can't be calculated"), biosIndex); + msg(tr("parseImageFile: not a single Volume Top File is found, the image may be corrupted"), biosIndex); } else { return performSecondPass(biosIndex); @@ -176,7 +176,7 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const QModelInd return EFI_INVALID_PARAMETER; // Get parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Store the beginning of descriptor as descriptor base address const UINT8* descriptor = (const UINT8*)intelImage.constData(); @@ -412,7 +412,7 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const QModelInd // Check if the last VTF is found if (!lastVtf.isValid()) { - msg(tr("parseIntelImage: not a single Volume Top File is found, physical memory addresses can't be calculated"), index); + msg(tr("parseIntelImage: not a single Volume Top File is found, the image may be corrupted"), index); } else { return performSecondPass(index); @@ -428,7 +428,7 @@ STATUS FfsParser::parseGbeRegion(const QByteArray & gbe, const UINT32 parentOffs return ERR_EMPTY_REGION; // Get parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Get info QString name = tr("GbE region"); @@ -463,7 +463,7 @@ STATUS FfsParser::parseMeRegion(const QByteArray & me, const UINT32 parentOffset return ERR_EMPTY_REGION; // Get parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Get info QString name = tr("ME region"); @@ -528,7 +528,7 @@ STATUS FfsParser::parsePdrRegion(const QByteArray & pdr, const UINT32 parentOffs return ERR_EMPTY_REGION; // Get parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Get info QString name = tr("PDR region"); @@ -558,7 +558,7 @@ STATUS FfsParser::parseBiosRegion(const QByteArray & bios, const UINT32 parentOf return ERR_EMPTY_REGION; // Get parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Get info QString name = tr("BIOS region"); @@ -592,7 +592,7 @@ STATUS FfsParser::parseRawArea(const QByteArray & data, const QModelIndex & inde return ERR_INVALID_PARAMETER; // Get parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); UINT32 offset = pdata.offset; UINT32 headerSize = model->header(index).size(); @@ -746,7 +746,7 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare return ERR_INVALID_PARAMETER; // Get parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Populate volume header const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(volume.constData()); @@ -831,14 +831,14 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare UINT8 emptyByte = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; // Check for Apple CRC32 in ZeroVector - bool hasZeroVectorCRC32 = false; + bool hasAppleCrc32 = false; UINT32 volumeSize = volume.size(); - UINT32 crc32FromZeroVector = *(UINT32*)(volume.constData() + 8); - if (crc32FromZeroVector != 0) { + UINT32 appleCrc32 = *(UINT32*)(volume.constData() + 8); + if (appleCrc32 != 0) { // Calculate CRC32 of the volume body UINT32 crc = crc32(0, (const UINT8*)(volume.constData() + volumeHeader->HeaderLength), volumeSize - volumeHeader->HeaderLength); - if (crc == crc32FromZeroVector) { - hasZeroVectorCRC32 = true; + if (crc == appleCrc32) { + hasAppleCrc32 = true; } } @@ -865,11 +865,6 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare .hexarg2(volumeHeader->Attributes, 8) .arg(emptyByte ? "1" : "0"); - // Apple CRC32 volume - if (hasZeroVectorCRC32) { - info += tr("\nCRC32 in ZeroVector: valid"); - } - // Extended header present if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)(volume.constData() + volumeHeader->ExtHeaderOffset); @@ -887,14 +882,14 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare pdata.volume.extendedHeaderGuid = extendedHeaderGuid; pdata.volume.alignment = alignment; pdata.volume.revision = volumeHeader->Revision; - pdata.volume.hasZeroVectorCRC32 = hasZeroVectorCRC32; + pdata.volume.hasAppleCrc32 = hasAppleCrc32; pdata.volume.isWeakAligned = (volumeHeader->Revision > 1 && (volumeHeader->Attributes & EFI_FVB2_WEAK_ALIGNMENT)); if (pdata.isOnFlash) info.prepend(tr("Offset: %1h\n").hexarg(pdata.offset)); // Add text QString text; - if (hasZeroVectorCRC32) - text += tr("ZeroVectorCRC32 "); + if (hasAppleCrc32) + text += tr("AppleCRC32 "); // Add tree item UINT8 subtype = Subtypes::UnknownVolume; @@ -967,7 +962,7 @@ STATUS FfsParser::parseVolumeBody(const QModelIndex & index) UINT32 volumeHeaderSize = model->header(index).size(); // Get parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); UINT32 offset = pdata.offset; if (pdata.ffsVersion != 2 && pdata.ffsVersion != 3) // Don't parse unknown volumes @@ -1146,7 +1141,7 @@ STATUS FfsParser::parseFileHeader(const QByteArray & file, const UINT32 parentOf return ERR_INVALID_PARAMETER; // Get parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Get file header QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); @@ -1328,7 +1323,7 @@ STATUS FfsParser::parsePadFileBody(const QModelIndex & index) return ERR_INVALID_PARAMETER; // Get data from parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); // Check if all bytes of the file are empty QByteArray body = model->body(index); @@ -1395,7 +1390,7 @@ STATUS FfsParser::parseSections(QByteArray sections, const QModelIndex & index) return ERR_INVALID_PARAMETER; // Get data from parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); // Search for and parse all sections UINT32 bodySize = sections.size(); @@ -1491,7 +1486,7 @@ STATUS FfsParser::parseSectionHeader(const QByteArray & section, const UINT32 pa STATUS FfsParser::parseCommonSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) { // Get data from parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Obtain header fields const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); @@ -1523,7 +1518,7 @@ STATUS FfsParser::parseCommonSectionHeader(const QByteArray & section, const UIN STATUS FfsParser::parseCompressedSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) { // Get data from parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Obtain header fields const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); @@ -1566,7 +1561,7 @@ STATUS FfsParser::parseCompressedSectionHeader(const QByteArray & section, const STATUS FfsParser::parseGuidedSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) { // Get data from parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Obtain header fields const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); @@ -1610,7 +1605,7 @@ STATUS FfsParser::parseGuidedSectionHeader(const QByteArray & section, const UIN STATUS FfsParser::parseFreeformGuidedSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) { // Get data from parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Obtain header fields const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); @@ -1697,7 +1692,7 @@ STATUS FfsParser::parseFreeformGuidedSectionHeader(const QByteArray & section, c STATUS FfsParser::parseVersionSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) { // Get data from parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Obtain header fields const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); @@ -1735,7 +1730,7 @@ STATUS FfsParser::parseVersionSectionHeader(const QByteArray & section, const UI STATUS FfsParser::parsePostcodeSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) { // Get data from parent's parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(parent); + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); // Obtain header fields const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); @@ -1812,7 +1807,7 @@ STATUS FfsParser::parseCompressedSectionBody(const QModelIndex & index) return ERR_INVALID_PARAMETER; // Get data from parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); UINT8 algorithm = pdata.section.compressed.compressionType; // Decompress section @@ -1852,7 +1847,7 @@ STATUS FfsParser::parseGuidedSectionBody(const QModelIndex & index) return ERR_INVALID_PARAMETER; // Get data from parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); UINT32 attributes = pdata.section.guidDefined.attributes; EFI_GUID guid = pdata.section.guidDefined.guid; @@ -2243,7 +2238,7 @@ STATUS FfsParser::parseTeImageSectionBody(const QModelIndex & index) } // Get data from parsing data - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); pdata.section.teImage.imageBase = teHeader->ImageBase; pdata.section.teImage.adjustedImageBase = teHeader->ImageBase + teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); @@ -2264,22 +2259,19 @@ STATUS FfsParser::performSecondPass(const QModelIndex & index) return ERR_INVALID_PARAMETER; // Get parsing data for the last VTF - PARSING_DATA pdata = parsingDataFromQByteArray(lastVtf); + PARSING_DATA pdata = parsingDataFromQModelIndex(lastVtf); if (!pdata.isOnFlash) { - msg(tr("addPhysicalAddressInfo: the last VTF appears inside compressed item, the image may be damaged"), lastVtf); + msg(tr("performSecondPass: the last VTF appears inside compressed item, the image may be damaged"), lastVtf); return ERR_SUCCESS; } // Calculate address difference const UINT32 vtfSize = model->header(lastVtf).size() + model->body(lastVtf).size() + (pdata.file.hasTail ? sizeof(UINT16) : 0); - const UINT32 diff = 0xFFFFFFFF - pdata.offset - vtfSize + 1; + const UINT32 diff = 0xFFFFFFFFUL - pdata.offset - vtfSize + 1; // Apply address information to index and all it's child items addMemoryAddressesRecursive(index, diff); - // Find and parse FIT - parseFit(); - return ERR_SUCCESS; } @@ -2290,12 +2282,12 @@ STATUS FfsParser::addMemoryAddressesRecursive(const QModelIndex & index, const U return ERR_SUCCESS; // Get parsing data for the current item - PARSING_DATA pdata = parsingDataFromQByteArray(index); + PARSING_DATA pdata = parsingDataFromQModelIndex(index); // Set address value for non-compressed data if (pdata.isOnFlash) { // Check address sanity - if ((const UINT64)diff + pdata.offset <= 0xFFFFFFFF) { + if ((const UINT64)diff + pdata.offset <= 0xFFFFFFFFUL) { // Update info pdata.address = diff + pdata.offset; UINT32 headerSize = model->header(index).size(); @@ -2335,13 +2327,69 @@ STATUS FfsParser::addMemoryAddressesRecursive(const QModelIndex & index, const U return ERR_SUCCESS; } -STATUS FfsParser::parseFit() +/*STATUS FfsParser::parseFit(const QModelIndex & index) { // Check sanity - if (!lastVtf.isValid) + if (!lastVtf.isValid()) return EFI_INVALID_PARAMETER; + // Search for FIT + QModelIndex fitIndex; + STATUS result = findFitRecursive(index, fitIndex); + if (result) + return result; + // FIT not found + if (!fitIndex.isValid()) + return ERR_SUCCESS; + + // Get parsing data for the current item + PARSING_DATA pdata = parsingDataFromQModelIndex(fitIndex); + + // Explicitly set the item as fixed + pdata.fixed = TRUE; + + // Set modified parsing data + model->setParsingData(fitIndex, parsingDataToQByteArray(pdata)); return ERR_SUCCESS; -} \ No newline at end of file +} + +STATUS FfsParser::findFitRecursive(const QModelIndex & index, QModelIndex & found) +{ + // Sanity check + if (!index.isValid()) + return EFI_SUCCESS; + + // Process child items + for (int i = 0; i < model->rowCount(index); i++) { + findFitRecursive(index.child(i, 0), found); + if (found.isValid()) + return EFI_SUCCESS; + } + + // Get parsing data for the current item + PARSING_DATA pdata = parsingDataFromQModelIndex(index); + + // Check item's address to be in required range + INT32 offset = model->body(index).indexOf(FIT_SIGNATURE); + // Check for FIT signature in item's body + if (offset >= 0) { + // FIT candidate found, calculate it's offset and physical address + UINT32 fitOffset = pdata.offset + model->header(index).size() + (UINT32)offset; + UINT32 fitAddress = pdata.address + model->header(index).size() + (UINT32)offset; + + // Check FIT address to be in the last VTF + QByteArray lastVtfBody = model->body(lastVtf); + if (*(const UINT32*)(lastVtfBody.constData() + lastVtfBody.size() - FIT_POINTER_OFFSET) == fitAddress) { + msg(tr("findFitRecursive: FIT table found at offset %1h, physical address %2h") + .hexarg2(fitOffset, 8) + .hexarg2(fitAddress, 8), + index); + found = index; + return ERR_SUCCESS; + } + } + + return ERR_SUCCESS; +}*/ \ No newline at end of file diff --git a/common/ffsparser.h b/common/ffsparser.h index 347a95e..c24980e 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -53,6 +53,9 @@ public: STATUS parseSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parseSectionBody(const QModelIndex & index); + // Retuns index of the last VTF after parsing is done + const QModelIndex getLastVtf() {return lastVtf;}; + private: TreeModel *model; QVector > messagesVector; @@ -92,7 +95,8 @@ private: STATUS performSecondPass(const QModelIndex & index); STATUS addMemoryAddressesRecursive(const QModelIndex & index, const UINT32 diff); - STATUS parseFit(); + /*STATUS parseFit(const QModelIndex & index); + STATUS findFitRecursive(const QModelIndex & index, QModelIndex & found);*/ // Internal operations BOOLEAN hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2); diff --git a/common/fit.h b/common/fit.h index c12c1a8..ea9d126 100644 --- a/common/fit.h +++ b/common/fit.h @@ -20,10 +20,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #pragma pack(push,1) // Memory address of a pointer to FIT, 40h back from the end of flash chip -#define FIT_POINTER_ADDRESS 0xFFFFFFC0 - -// FIT can reside in the last 1 MB of the flash chip -#define FIT_TABLE_LOWEST_ADDRESS 0xFF000000 +#define FIT_POINTER_OFFSET 0x40 // Entry types #define FIT_TYPE_HEADER 0x00 @@ -35,7 +32,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define FIT_TYPE_TXT_CONF_POLICY 0x0A #define FIT_TYPE_AC_KEY_MANIFEST 0x0B #define FIT_TYPE_AC_BOOT_POLICY 0x0C -#define FIT_TYPE_EMPTY 0xFF +#define FIT_TYPE_EMPTY 0x7F #define FIT_HEADER_VERSION 0x0100 #define FIT_MICROCODE_VERSION 0x0100 @@ -45,10 +42,9 @@ const QByteArray FIT_SIGNATURE typedef struct _FIT_ENTRY { UINT64 Address; - UINT64 ReservedSize; + UINT32 Size; UINT16 Version; - UINT8 ChecksumValid : 1; - UINT8 Type : 7; + UINT8 Type; UINT8 Checksum; } FIT_ENTRY; diff --git a/common/fitparser.cpp b/common/fitparser.cpp new file mode 100644 index 0000000..c034f33 --- /dev/null +++ b/common/fitparser.cpp @@ -0,0 +1,152 @@ +/* fitparser.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, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +*/ +#include "fitparser.h" +#include "types.h" +#include "treemodel.h" + + +FitParser::FitParser(TreeModel* treeModel, QObject *parent) + : QObject(parent), model(treeModel) +{ +} + +FitParser::~FitParser() +{ +} + +STATUS FitParser::parse(const QModelIndex & index, const QModelIndex & lastVtfIndex) +{ + // Check sanity + if (!index.isValid() || !lastVtfIndex.isValid()) + return EFI_INVALID_PARAMETER; + + // Store lastVtfIndex + lastVtf = lastVtfIndex; + + // Search for FIT + QModelIndex fitIndex; + UINT32 fitOffset; + STATUS result = findFitRecursive(index, fitIndex, fitOffset); + if (result) + return result; + + // FIT not found + if (!fitIndex.isValid()) + return ERR_SUCCESS; + + // Get parsing data for the current item + PARSING_DATA pdata = parsingDataFromQModelIndex(fitIndex); + + // Explicitly set the item as fixed + pdata.fixed = TRUE; + + // Set modified parsing data + model->setParsingData(fitIndex, parsingDataToQByteArray(pdata)); + + // Add all FIT entries into QVector + const FIT_ENTRY* fitHeader = (const FIT_ENTRY*)(model->body(fitIndex).constData() + fitOffset); + + // Special case of FIT header + QString remark; + + // Check FIT checksum, if present + if (fitHeader->Type & 0x80) { + // Calculate FIT entry checksum + UINT32 fitSize = (fitHeader->Size & 0xFFFFFF) << 4; + UINT8 calculated = calculateChecksum8((const UINT8*)fitHeader, fitSize); + if (calculated) { + remark.append(tr("Invalid FIT table checksum, ").hexarg2(calculated, 2)); + } + } + + // Check fit header version and type + if (fitHeader->Version != FIT_HEADER_VERSION) { + remark.append(tr("Invalid FIT header version, ")); + } + if ((fitHeader->Type & 0x7F) != FIT_TYPE_HEADER) { + remark.append(tr("Invalid FIT header type, ")); + } + + // Remove the last ", " from remark string, if needed + if (!remark.isEmpty()) + remark = remark.left(remark.length() - 2); + + // Add FIT header to fitEntries vector + fitEntries.append(QPair(*fitHeader, remark)); + + // Process all other entries + for (UINT32 i = 1; i < fitHeader->Size; i++) { + remark.clear(); + const FIT_ENTRY* currentEntry = fitHeader + i; + + // Check entry type + switch (currentEntry->Type & 0x7F) { + case FIT_TYPE_HEADER: + remark.append(tr("Second FIT header found, the table is damaged")); + break; + + case FIT_TYPE_EMPTY: + case FIT_TYPE_MICROCODE: + break; + + case FIT_TYPE_BIOS_AC_MODULE: + case FIT_TYPE_BIOS_INIT_MODULE: + case FIT_TYPE_TPM_POLICY: + case FIT_TYPE_BIOS_POLICY_DATA: + case FIT_TYPE_TXT_CONF_POLICY: + case FIT_TYPE_AC_KEY_MANIFEST: + case FIT_TYPE_AC_BOOT_POLICY: + default: + remark.append(tr("Modified image may not work")); + break; + } + + fitEntries.append(QPair(*currentEntry, remark)); + } + + return ERR_SUCCESS; +} + +STATUS FitParser::findFitRecursive(const QModelIndex & index, QModelIndex & found, UINT32 & fitOffset) +{ + // Sanity check + if (!index.isValid()) + return EFI_SUCCESS; + + // Process child items + for (int i = 0; i < model->rowCount(index); i++) { + findFitRecursive(index.child(i, 0), found, fitOffset); + if (found.isValid()) + return EFI_SUCCESS; + } + + // Get parsing data for the current item + PARSING_DATA pdata = parsingDataFromQModelIndex(index); + + // Check item's address to be in required range + INT32 offset = model->body(index).indexOf(FIT_SIGNATURE); + // Check for FIT signature in item's body + if (offset >= 0) { + // FIT candidate found, calculate it's physical address + UINT32 fitAddress = pdata.address + model->header(index).size() + (UINT32)offset; + + // Check FIT address to be in the last VTF + QByteArray lastVtfBody = model->body(lastVtf); + if (*(const UINT32*)(lastVtfBody.constData() + lastVtfBody.size() - FIT_POINTER_OFFSET) == fitAddress) { + found = index; + fitOffset = offset; + return ERR_SUCCESS; + } + } + + return ERR_SUCCESS; +} \ No newline at end of file diff --git a/common/fitparser.h b/common/fitparser.h new file mode 100644 index 0000000..ab296b8 --- /dev/null +++ b/common/fitparser.h @@ -0,0 +1,50 @@ +/* fitparser.h + +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, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +*/ + +#ifndef __FITPARSER_H__ +#define __FITPARSER_H__ + +#include +#include +#include +#include +#include + +#include "basetypes.h" +#include "treemodel.h" +#include "utility.h" +#include "parsingdata.h" +#include "fit.h" + +class TreeModel; + +class FitParser : public QObject +{ + Q_OBJECT + +public: + // Default constructor and destructor + FitParser(TreeModel* treeModel, QObject *parent = 0); + ~FitParser(); + + STATUS parse(const QModelIndex & index, const QModelIndex & lastVtf); + QVector > getFitEntries() const { return fitEntries; } + +private: + TreeModel *model; + QModelIndex lastVtf; + QVector > fitEntries; + + STATUS findFitRecursive(const QModelIndex & index, QModelIndex & found, UINT32 & fitOffset); +}; + +#endif diff --git a/common/parsingdata.h b/common/parsingdata.h index 3be8071..5ee9eaa 100644 --- a/common/parsingdata.h +++ b/common/parsingdata.h @@ -34,7 +34,8 @@ typedef struct _VOLUME_PARSING_DATA { UINT32 alignment; UINT8 revision; BOOLEAN hasExtendedHeader; - BOOLEAN hasZeroVectorCRC32; + BOOLEAN hasAppleCrc32; + BOOLEAN hasAppleFSO; BOOLEAN isWeakAligned; } VOLUME_PARSING_DATA; diff --git a/common/utility.cpp b/common/utility.cpp index 0054cf2..2073e2b 100644 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -20,7 +20,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "LZMA/LzmaDecompress.h" // Returns either new parsing data instance or obtains it from index -PARSING_DATA parsingDataFromQByteArray(const QModelIndex & index) +PARSING_DATA parsingDataFromQModelIndex(const QModelIndex & index) { if (index.isValid()) { TreeModel* model = (TreeModel*)index.model(); @@ -257,3 +257,34 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr } } +// 8bit checksum calculation routine +UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize) +{ + if (!buffer) + return 0; + + UINT8 counter = 0; + + while (bufferSize--) + counter += buffer[bufferSize]; + + return (UINT8)(0x100 - counter); +} + +// 16bit checksum calculation routine +UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize) +{ + if (!buffer) + return 0; + + UINT16 counter = 0; + UINT32 index = 0; + + bufferSize /= sizeof(UINT16); + + for (; index < bufferSize; index++) { + counter = (UINT16)(counter + buffer[index]); + } + + return (UINT16)(0x10000 - counter); +} \ No newline at end of file diff --git a/common/utility.h b/common/utility.h index 708e683..ba44d04 100644 --- a/common/utility.h +++ b/common/utility.h @@ -20,7 +20,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "parsingdata.h" // Returns either new parsing data instance or obtains it from index -PARSING_DATA parsingDataFromQByteArray(const QModelIndex & index); +PARSING_DATA parsingDataFromQModelIndex(const QModelIndex & index); // Converts parsing data to byte array QByteArray parsingDataToQByteArray(const PARSING_DATA & pdata); @@ -34,7 +34,13 @@ extern STATUS decompress(const QByteArray & compressed, UINT8 & algorithm, QByte // Compression routine //STATUS compress(const QByteArray & decompressed, QByteArray & compressed, const UINT8 & algorithm); -// CRC32 +// CRC32 calculation routine extern UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length); +// 8bit checksum calculation routine +extern UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize); + +// 16bit checksum calculation routine +extern UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); + #endif \ No newline at end of file