From 856ea2a3aa0071eec88eedb24f6d14fd635ebf1c Mon Sep 17 00:00:00 2001 From: Dmitry Frolov Date: Thu, 23 Aug 2018 08:43:46 +0300 Subject: [PATCH] modification support & other + Replace and rebuild functions + NVRAM volumes rebuild support (without changing the size of volume) + 'TXT' and 'Microcode' parsing tabs + 'Inspect with IDA' function --- UEFITool/uefitool.cpp | 302 +++++++---- UEFITool/uefitool.h | 12 +- UEFITool/uefitool.ui | 109 +++- common/basetypes.h | 107 ++-- common/bootguard.h | 11 +- common/ffs.cpp | 49 ++ common/ffs.h | 71 +++ common/ffsbuilder.cpp | 1089 +++++++++++++++++++++++++++++++++++++++- common/ffsbuilder.h | 21 +- common/ffsops.cpp | 115 ++++- common/ffsops.h | 6 +- common/ffsparser.cpp | 233 +++++++-- common/ffsparser.h | 45 +- common/fit.h | 14 + common/nvramparser.cpp | 68 +-- common/nvramparser.h | 28 +- common/utility.cpp | 450 ++++++++++++++++- common/utility.h | 10 + 18 files changed, 2439 insertions(+), 301 deletions(-) diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index 1eacd17..e64e8b4 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -39,6 +39,7 @@ version(tr(PROGRAM_VERSION)) connect(ui->actionOpenImageFile, SIGNAL(triggered()), this, SLOT(openImageFile())); connect(ui->actionOpenImageFileInNewWindow, SIGNAL(triggered()), this, SLOT(openImageFileInNewWindow())); connect(ui->actionSaveImageFile, SIGNAL(triggered()), this, SLOT(saveImageFile())); + connect(ui->actionSpecifyPathIDA, SIGNAL(triggered()), this, SLOT(specifyPathIDA())); connect(ui->actionSearch, SIGNAL(triggered()), this, SLOT(search())); connect(ui->actionHexView, SIGNAL(triggered()), this, SLOT(hexView())); connect(ui->actionBodyHexView, SIGNAL(triggered()), this, SLOT(bodyHexView())); @@ -52,6 +53,8 @@ version(tr(PROGRAM_VERSION)) connect(ui->actionReplaceBody, SIGNAL(triggered()), this, SLOT(replaceBody())); connect(ui->actionRemove, SIGNAL(triggered()), this, SLOT(remove())); connect(ui->actionRebuild, SIGNAL(triggered()), this, SLOT(rebuild())); + connect(ui->actionInspectBodyIDA, SIGNAL(triggered()), this, SLOT(inspectIDA32())); + connect(ui->actionInspectBodyIDA64, SIGNAL(triggered()), this, SLOT(inspectIDA64())); connect(ui->actionMessagesCopy, SIGNAL(triggered()), this, SLOT(copyMessage())); connect(ui->actionMessagesCopyAll, SIGNAL(triggered()), this, SLOT(copyAllMessages())); connect(ui->actionMessagesClear, SIGNAL(triggered()), this, SLOT(clearMessages())); @@ -71,8 +74,8 @@ version(tr(PROGRAM_VERSION)) // Enable Drag-and-Drop actions setAcceptDrops(true); - // Disable Builder tab, doesn't work right now - ui->messagesTabWidget->setTabEnabled(4, false); + // Disable Builder tab + ui->messagesTabWidget->setTabEnabled(6, false); // Set current directory currentDir = "."; @@ -110,8 +113,12 @@ void UEFITool::init() ui->fitTableWidget->setColumnCount(0); ui->infoEdit->clear(); ui->bootGuardEdit->clear(); + ui->txtEdit->clear(); + ui->microcodeEdit->clear(); ui->messagesTabWidget->setTabEnabled(1, false); ui->messagesTabWidget->setTabEnabled(2, false); + ui->messagesTabWidget->setTabEnabled(3, false); + ui->messagesTabWidget->setTabEnabled(4, false); // Set window title setWindowTitle(tr("UEFITool %1").arg(version)); @@ -139,13 +146,21 @@ void UEFITool::init() delete ffsParser; ffsParser = new FfsParser(model); + // Create ffsBuilder + delete ffsBuilder; + ffsBuilder = new FfsBuilder(model, ffsParser); + + // Create ffsOps + delete ffsOps; + ffsOps = new FfsOperations(model); + // Set proper marking state model->setMarkingEnabled(markingEnabled); ui->actionToggleBootGuardMarking->setChecked(markingEnabled); // Connect - connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), - this, SLOT(populateUi(const QModelIndex &))); + connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const UModelIndex &, const UModelIndex &)), + this, SLOT(populateUi(const UModelIndex &))); connect(ui->structureTreeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(populateUi(const QItemSelection &))); connect(ui->parserMessagesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*))); @@ -167,7 +182,7 @@ void UEFITool::populateUi(const QItemSelection &selected) populateUi(selected.indexes().at(0)); } -void UEFITool::populateUi(const QModelIndex ¤t) +void UEFITool::populateUi(const UModelIndex ¤t) { // Check sanity if (!current.isValid()) { @@ -210,26 +225,42 @@ void UEFITool::populateUi(const QModelIndex ¤t) ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current) && model->hasEmptyTail(current)); ui->actionGoToData->setEnabled(type == Types::NvarEntry && subtype == Subtypes::LinkNvarEntry); - // Disable rebuild for now - //ui->actionRebuild->setDisabled(type == Types::Region && subtype == Subtypes::DescriptorRegion); - //ui->actionReplace->setDisabled(type == Types::Region && subtype == Subtypes::DescriptorRegion); + // Enable rebuild + ui->actionRebuild->setDisabled(type == Types::Region && subtype == Subtypes::DescriptorRegion); + ui->actionReplace->setDisabled(type == Types::Region && subtype == Subtypes::DescriptorRegion); + ui->actionRebuild->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section || + type == Types::EvsaStore || type == Types::FdcStore || + type == Types::FlashMapStore || type == Types::FsysStore || + type == Types::FtwStore || type == Types::Vss2Store || + type == Types::VssStore); - //ui->actionRebuild->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->actionExtractBody->setDisabled(model->hasEmptyBody(current)); ui->actionExtractBodyUncompressed->setEnabled(enableExtractBodyUncompressed(current)); - //ui->actionRemove->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); + + ui->actionInspectBodyIDA->setDisabled(model->hasEmptyBody(current)); + ui->actionInspectBodyIDA64->setDisabled(model->hasEmptyBody(current)); + + ui->actionRemove->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); //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 && 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->actionReplace->setEnabled((type == Types::Region && subtype != Subtypes::DescriptorRegion) || type == Types::Volume || type == Types::File || type == Types::Section || + type == Types::EvsaStore || type == Types::FdcStore || + type == Types::FlashMapStore || type == Types::FsysStore || + type == Types::FtwStore || type == Types::Vss2Store || + type == Types::VssStore || type == Types::CmdbStore); + ui->actionReplaceBody->setEnabled((type == Types::Region && subtype != Subtypes::DescriptorRegion) || type == Types::Volume || type == Types::File || type == Types::Section || + type == Types::EvsaStore || type == Types::FdcStore || + type == Types::FlashMapStore || type == Types::FsysStore || + type == Types::FtwStore || type == Types::Vss2Store || + type == Types::VssStore || type == Types::CmdbStore); ui->menuMessageActions->setEnabled(false); } -bool UEFITool::enableExtractBodyUncompressed(const QModelIndex ¤t) +bool UEFITool::enableExtractBodyUncompressed(const UModelIndex ¤t) { // TODO: rewrite based on model->compressed() U_UNUSED_PARAMETER(current); @@ -245,7 +276,7 @@ bool UEFITool::enableExtractBodyUncompressed(const QModelIndex ¤t) return true; } else if (model->subtype(current) == EFI_SECTION_GUID_DEFINED) { - QByteArray guid = QByteArray((const char*)&pdata.section.guidDefined.guid, sizeof(EFI_GUID)); + UByteArray guid = UByteArray((const char*)&pdata.section.guidDefined.guid, sizeof(EFI_GUID)); if (guid == EFI_GUIDED_SECTION_TIANO || guid == EFI_GUIDED_SECTION_LZMA) { return true; } @@ -260,12 +291,12 @@ void UEFITool::search() if (searchDialog->exec() != QDialog::Accepted) return; - QModelIndex rootIndex = model->index(0, 0); + UModelIndex rootIndex = model->index(0, 0); int index = searchDialog->ui->tabWidget->currentIndex(); if (index == 0) { // Hex pattern searchDialog->ui->hexEdit->setFocus(); - QByteArray pattern = searchDialog->ui->hexEdit->text().toLatin1().replace(" ", ""); + UByteArray pattern = searchDialog->ui->hexEdit->text().toLatin1().replace(" ", ""); if (pattern.isEmpty()) return; UINT8 mode; @@ -281,7 +312,7 @@ void UEFITool::search() else if (index == 1) { // GUID searchDialog->ui->guidEdit->setFocus(); searchDialog->ui->guidEdit->setCursorPosition(0); - QByteArray pattern = searchDialog->ui->guidEdit->text().toLatin1(); + UByteArray pattern = searchDialog->ui->guidEdit->text().toLatin1(); if (pattern.isEmpty()) return; UINT8 mode; @@ -296,7 +327,7 @@ void UEFITool::search() } else if (index == 2) { // Text string searchDialog->ui->textEdit->setFocus(); - QString pattern = searchDialog->ui->textEdit->text(); + UString pattern = searchDialog->ui->textEdit->text(); if (pattern.isEmpty()) return; UINT8 mode; @@ -314,7 +345,7 @@ void UEFITool::search() void UEFITool::hexView() { - QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + UModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; @@ -324,7 +355,7 @@ void UEFITool::hexView() void UEFITool::bodyHexView() { - QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + UModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; @@ -340,7 +371,7 @@ void UEFITool::goToOffset() return; UINT32 offset = (UINT32)goToOffsetDialog->ui->hexSpinBox->value(); - QModelIndex index = model->findByOffset(offset); + UModelIndex index = model->findByOffset(offset); if (index.isValid()) { ui->structureTreeView->scrollTo(index, QAbstractItemView::PositionAtCenter); ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear); @@ -355,7 +386,7 @@ void UEFITool::goToAddress() return; UINT32 address = (UINT32)goToAddressDialog->ui->hexSpinBox->value(); - QModelIndex index = model->findByOffset(address - (UINT32)ffsParser->getAddressDiff()); + UModelIndex index = model->findByOffset(address - (UINT32)ffsParser->getAddressDiff()); if (index.isValid()) { ui->structureTreeView->scrollTo(index, QAbstractItemView::PositionAtCenter); ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear); @@ -364,12 +395,12 @@ void UEFITool::goToAddress() void UEFITool::goToData() { - QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + UModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid() || model->type(index) != Types::NvarEntry || model->subtype(index) != Subtypes::LinkNvarEntry) return; // Get parent - QModelIndex parent = model->parent(index); + UModelIndex parent = model->parent(index); for (int i = index.row(); i < model->rowCount(parent); i++) { if (model->hasEmptyParsingData(index)) @@ -385,7 +416,7 @@ void UEFITool::goToData() } for (int j = i + 1; j < model->rowCount(parent); j++) { - QModelIndex currentIndex = parent.child(j, 0); + UModelIndex currentIndex = parent.child(j, 0); if (model->hasEmptyParsingData(currentIndex)) continue; @@ -401,7 +432,7 @@ void UEFITool::insert(const UINT8 mode) { U_UNUSED_PARAMETER(mode); - /*QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + /*UModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; @@ -412,7 +443,7 @@ void UEFITool::insert(const UINT8 mode) else type = model->type(index); - QString path; + UString path; switch (type) { case Types::Volume: path = QFileDialog::getOpenFileName(this, tr("Select FFS file to insert"), currentDir, "FFS files (*.ffs *.bin);;All files (*)"); @@ -442,10 +473,10 @@ void UEFITool::insert(const UINT8 mode) return; } - QByteArray buffer = inputFile.readAll(); + UByteArray buffer = inputFile.readAll(); inputFile.close(); - UINT8 result = ffsEngine->insert(index, buffer, mode); + USTATUS result = ffsEngine->insert(index, buffer, mode); if (result) { QMessageBox::critical(this, tr("Insertion failed"), errorMessage(result), QMessageBox::Ok); return; @@ -480,14 +511,11 @@ void UEFITool::replaceBody() void UEFITool::replace(const UINT8 mode) { - U_UNUSED_PARAMETER(mode); - - /* - QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + UModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; - QString path; + UString path; if (model->type(index) == Types::Region) { if (mode == REPLACE_MODE_AS_IS) { path = QFileDialog::getOpenFileName(this, tr("Select region file to replace %1").arg(model->name(index)), currentDir, "Region files (*.rgn *.bin);;All files (*)"); @@ -543,6 +571,19 @@ void UEFITool::replace(const UINT8 mode) else return; } + else if(model->type(index) == Types::EvsaStore || model->type(index) == Types::CmdbStore || + model->type(index) == Types::FdcStore || model->type(index) == Types::FlashMapStore || + model->type(index) == Types::FsysStore || model->type(index) == Types::FtwStore || + model->type(index) == Types::Vss2Store || model->type(index) == Types::VssStore) { + if (mode == REPLACE_MODE_AS_IS) { + path = QFileDialog::getOpenFileName(this, tr("Select NVRAM store file to replace selected store"), currentDir, "All files (*)"); + } + else if (mode == REPLACE_MODE_BODY) { + path = QFileDialog::getOpenFileName(this, tr("Select NVRAM store body file to replace selected store body"), currentDir, "All files (*)"); + } + else + return; + } else return; @@ -563,16 +604,16 @@ void UEFITool::replace(const UINT8 mode) return; } - QByteArray buffer = inputFile.readAll(); + UByteArray buffer = inputFile.readAll(); inputFile.close(); - UINT8 result = ffsOps->replace(index, buffer, mode); + USTATUS result = ffsOps->replace(index, buffer, mode); if (result) { QMessageBox::critical(this, tr("Replacing failed"), errorCodeToUString(result), QMessageBox::Ok); return; } ui->actionSaveImageFile->setEnabled(true); - */ + } void UEFITool::extractAsIs() @@ -590,14 +631,14 @@ void UEFITool::extractBodyUncompressed() extract(EXTRACT_MODE_BODY_UNCOMPRESSED); } -void UEFITool::extract(const UINT8 mode) +void UEFITool::extract(const UINT8 mode, UString* pathOut) { - QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + UModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; - QByteArray extracted; - QString name; + UByteArray extracted; + UString name; USTATUS result = ffsOps->extract(index, name, extracted, mode); if (result) { QMessageBox::critical(this, tr("Extraction failed"), errorCodeToUString(result), QMessageBox::Ok); @@ -610,7 +651,7 @@ void UEFITool::extract(const UINT8 mode) UINT8 type = model->type(index); UINT8 subtype = model->subtype(index); - QString path; + UString path; if (mode == EXTRACT_MODE_AS_IS) { switch (type) { case Types::Capsule: path = QFileDialog::getSaveFileName(this, tr("Save capsule to file"), name + ".cap", tr("Capsule files (*.cap *.bin);;All files (*)")); break; @@ -695,30 +736,67 @@ void UEFITool::extract(const UINT8 mode) outputFile.resize(0); outputFile.write(extracted); outputFile.close(); + + if(pathOut) { + pathOut->clear(); + pathOut->append(path); + } +} + +void UEFITool::inspectIDA32() +{ + if(idaPath32.trimmed().isEmpty()) + specifyPathIDA32(); + + inspect(INSPECT_MODE_IDA32); +} + +void UEFITool::inspectIDA64() +{ + if(idaPath64.trimmed().isEmpty()) + specifyPathIDA64(); + + inspect(INSPECT_MODE_IDA64); +} + +void UEFITool::inspect(const UINT8 mode) +{ + UString filePath; + + extract(EXTRACT_MODE_BODY, &filePath); + if(filePath.isEmpty()) + return; + + QStringList arg; + arg << filePath; + + QProcess idaProcess; + idaProcess.setProgram(mode == INSPECT_MODE_IDA32 ? idaPath32 : idaPath64); + idaProcess.setArguments(arg); + if(!idaProcess.startDetached()) { + QMessageBox::critical(this, tr("Inspect failed"), tr("Can't start IDA process"), QMessageBox::Ok); + return; + } } void UEFITool::rebuild() { - /* UModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; if (U_SUCCESS == ffsOps->rebuild(index)) ui->actionSaveImageFile->setEnabled(true); - */ } void UEFITool::remove() { - /* UModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; if (U_SUCCESS == ffsOps->remove(index)) ui->actionSaveImageFile->setEnabled(true); - */ } void UEFITool::about() @@ -748,15 +826,12 @@ void UEFITool::exit() void UEFITool::saveImageFile() { - /*QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"), currentDir, tr("BIOS image files (*.rom *.bin *.cap *.scap *.bio *.fd *.wph *.dec);;All files (*)")); + UString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"), currentDir, tr("BIOS image files (*.rom *.bin *.cap *.scap *.bio *.fd *.wph *.dec);;All files (*)")); if (path.isEmpty()) return; - QByteArray reconstructed; - // Create ffsBuilder - delete ffsBuilder; - ffsBuilder = new FfsBuilder(model); + UByteArray reconstructed; USTATUS result = ffsBuilder->build(model->index(0,0), reconstructed); showBuilderMessages(); if (result) { @@ -776,24 +851,24 @@ void UEFITool::saveImageFile() outputFile.close(); if (QMessageBox::Yes == QMessageBox::information(this, tr("Image build successful"), tr("Open the resulting file?"), QMessageBox::Yes, QMessageBox::No)) openImageFile(path); - */ + } void UEFITool::openImageFile() { - QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)")); + UString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)")); openImageFile(path); } void UEFITool::openImageFileInNewWindow() { - QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file in new window"), currentDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)")); + UString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file in new window"), currentDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)")); if (path.trimmed().isEmpty()) return; QProcess::startDetached(currentProgramPath, QStringList(path)); } -void UEFITool::openImageFile(QString path) +void UEFITool::openImageFile(UString path) { if (path.trimmed().isEmpty()) return; @@ -813,7 +888,7 @@ void UEFITool::openImageFile(QString path) return; } - QByteArray buffer = inputFile.readAll(); + UByteArray buffer = inputFile.readAll(); inputFile.close(); init(); @@ -858,6 +933,36 @@ void UEFITool::openImageFile(QString path) currentPath = path; } +void UEFITool::specifyPathIDA() +{ + specifyPathIDA32(); + specifyPathIDA64(); +} + +void UEFITool::specifyPathIDA32() +{ + QString path; + + path = QFileDialog::getOpenFileName(this, tr("Specify path to ida.exe"), "ida.exe", tr("IDA Pro executable (ida.exe);;All files (*)")); + + if (path.trimmed().isEmpty()) + return; + + idaPath32 = path; +} + +void UEFITool::specifyPathIDA64() +{ + QString path; + + path = QFileDialog::getOpenFileName(this, tr("Specify path to ida64.exe"), "ida64.exe", tr("IDA Pro 64 executable (ida64.exe);;All files (*)")); + + if (path.trimmed().isEmpty()) + return; + + idaPath64 = path; +} + void UEFITool::enableMessagesCopyActions(QListWidgetItem* item) { ui->menuMessageActions->setEnabled(item != NULL); @@ -879,7 +984,7 @@ void UEFITool::copyMessage() void UEFITool::copyAllMessages() { - QString text; + UString text; clipboard->clear(); if (ui->messagesTabWidget->currentIndex() == 0) { // Parser tab for (INT32 i = 0; i < ui->parserMessagesListWidget->count(); i++) @@ -933,7 +1038,7 @@ void UEFITool::dragEnterEvent(QDragEnterEvent* event) void UEFITool::dropEvent(QDropEvent* event) { - QString path = event->mimeData()->urls().at(0).toLocalFile(); + UString path = event->mimeData()->urls().at(0).toLocalFile(); openImageFile(path); } @@ -943,11 +1048,11 @@ void UEFITool::showParserMessages() if (!ffsParser) return; - std::vector > messages = ffsParser->getMessages(); - std::pair msg; + std::vector > messages = ffsParser->getMessages(); + std::pair msg; foreach (msg, messages) { QListWidgetItem* item = new QListWidgetItem(msg.first, NULL, 0); - item->setData(Qt::UserRole, QByteArray((const char*)&msg.second, sizeof(msg.second))); + item->setData(Qt::UserRole, UByteArray((const char*)&msg.second, sizeof(msg.second))); ui->parserMessagesListWidget->addItem(item); } @@ -961,11 +1066,11 @@ void UEFITool::showFinderMessages() if (!ffsParser) return; - std::vector > messages = ffsFinder->getMessages(); - std::pair msg; + std::vector > messages = ffsFinder->getMessages(); + std::pair msg; foreach (msg, messages) { QListWidgetItem* item = new QListWidgetItem(msg.first, NULL, 0); - item->setData(Qt::UserRole, QByteArray((const char*)&msg.second, sizeof(msg.second)));; + item->setData(Qt::UserRole, UByteArray((const char*)&msg.second, sizeof(msg.second)));; ui->finderMessagesListWidget->addItem(item); } @@ -979,22 +1084,23 @@ void UEFITool::showBuilderMessages() if (!ffsBuilder) return; - std::vector > messages = ffsBuilder->getMessages(); - std::pair msg; + std::vector > messages = ffsBuilder->getMessages(); + std::pair msg; foreach (msg, messages) { QListWidgetItem* item = new QListWidgetItem(msg.first, NULL, 0); - item->setData(Qt::UserRole, QByteArray((const char*)&msg.second, sizeof(msg.second))); + item->setData(Qt::UserRole, UByteArray((const char*)&msg.second, sizeof(msg.second))); ui->builderMessagesListWidget->addItem(item); } - ui->messagesTabWidget->setCurrentIndex(4); + ui->messagesTabWidget->setTabEnabled(6, true); + ui->messagesTabWidget->setCurrentIndex(6); ui->builderMessagesListWidget->scrollToBottom(); } void UEFITool::scrollTreeView(QListWidgetItem* item) { - QByteArray second = item->data(Qt::UserRole).toByteArray(); - QModelIndex *index = (QModelIndex *)second.data(); + UByteArray second = item->data(Qt::UserRole).toByteArray(); + UModelIndex *index = (UModelIndex *)second.data(); if (index && index->isValid()) { ui->structureTreeView->scrollTo(*index, QAbstractItemView::PositionAtCenter); ui->structureTreeView->selectionModel()->select(*index, QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear); @@ -1003,8 +1109,8 @@ void UEFITool::scrollTreeView(QListWidgetItem* item) void UEFITool::scrollTreeView(QTableWidgetItem* item) { - QByteArray second = item->data(Qt::UserRole).toByteArray(); - QModelIndex *index = (QModelIndex *)second.data(); + UByteArray second = item->data(Qt::UserRole).toByteArray(); + UModelIndex *index = (UModelIndex *)second.data(); if (index && index->isValid()) { ui->structureTreeView->scrollTo(*index, QAbstractItemView::PositionAtCenter); ui->structureTreeView->selectionModel()->select(*index, QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear); @@ -1025,7 +1131,7 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event) } QPoint pt = event->pos(); - QModelIndex index = ui->structureTreeView->indexAt(ui->structureTreeView->viewport()->mapFrom(this, pt)); + UModelIndex index = ui->structureTreeView->indexAt(ui->structureTreeView->viewport()->mapFrom(this, pt)); if (!index.isValid()) { return; } @@ -1076,17 +1182,21 @@ void UEFITool::readSettings() markingEnabled = settings.value("tree/markingEnabled", true).toBool(); ui->actionToggleBootGuardMarking->setChecked(markingEnabled); + // Get IDA Path + idaPath32 = settings.value("idaPath32").toString(); + idaPath64 = settings.value("idaPath64").toString(); + // Set monospace font for some controls - QString fontName; + UString fontName; int fontSize; #if defined Q_OS_OSX - fontName = settings.value("mainWindow/fontName", QString("Menlo")).toString(); + fontName = settings.value("mainWindow/fontName", UString("Menlo")).toString(); fontSize = settings.value("mainWindow/fontSize", 10).toInt(); #elif defined Q_OS_WIN - fontName = settings.value("mainWindow/fontName", QString("Consolas")).toString(); + fontName = settings.value("mainWindow/fontName", UString("Consolas")).toString(); fontSize = settings.value("mainWindow/fontSize", 9).toInt(); #else - fontName = settings.value("mainWindow/fontName", QString("Courier New")).toString(); + fontName = settings.value("mainWindow/fontName", UString("Courier New")).toString(); fontSize = settings.value("mainWindow/fontSize", 10).toInt(); #endif currentFont = QFont(fontName, fontSize); @@ -1096,6 +1206,8 @@ void UEFITool::readSettings() ui->builderMessagesListWidget->setFont(currentFont); ui->fitTableWidget->setFont(currentFont); ui->bootGuardEdit->setFont(currentFont); + ui->txtEdit->setFont(currentFont); + ui->microcodeEdit->setFont(currentFont); ui->structureTreeView->setFont(currentFont); searchDialog->ui->guidEdit->setFont(currentFont); searchDialog->ui->hexEdit->setFont(currentFont); @@ -1120,6 +1232,8 @@ void UEFITool::writeSettings() settings.setValue("tree/markingEnabled", markingEnabled); settings.setValue("mainWindow/fontName", currentFont.family()); settings.setValue("mainWindow/fontSize", currentFont.pointSize()); + settings.setValue("idaPath32", idaPath32); + settings.setValue("idaPath64", idaPath64); } void UEFITool::showFitTable() @@ -1150,7 +1264,7 @@ void UEFITool::showFitTable() for (size_t i = 0; i < fitTable.size(); i++) { for (UINT8 j = 0; j < 6; j++) { QTableWidgetItem* item = new QTableWidgetItem(fitTable[i].first[j]); - item->setData(Qt::UserRole, QByteArray((const char*)&fitTable[i].second, sizeof(fitTable[i].second))); + item->setData(Qt::UserRole, UByteArray((const char*)&fitTable[i].second, sizeof(fitTable[i].second))); ui->fitTableWidget->setItem((int)i, j, item); } } @@ -1161,15 +1275,27 @@ void UEFITool::showFitTable() // Get BootGuard info UString bgInfo = ffsParser->getBootGuardInfo(); - if (bgInfo.isEmpty()) { - // Disable BootGuard tab - ui->messagesTabWidget->setTabEnabled(2, false); - return; + if (!bgInfo.isEmpty()) { + ui->messagesTabWidget->setTabEnabled(2, true); + ui->bootGuardEdit->setPlainText(bgInfo); + ui->messagesTabWidget->setCurrentIndex(2); } - ui->messagesTabWidget->setTabEnabled(2, true); - ui->bootGuardEdit->setPlainText(bgInfo); - ui->messagesTabWidget->setCurrentIndex(2); + // Get Microcode info + UString microcodeInfo = ffsParser->getMicrocodeInfo(); + if (!microcodeInfo.isEmpty()) { + ui->messagesTabWidget->setTabEnabled(4, true); + ui->microcodeEdit->setPlainText(microcodeInfo); + ui->messagesTabWidget->setCurrentIndex(4); + } + + // Get TXT ACM info + UString txtInfo = ffsParser->getTxtInfo(); + if (!txtInfo.isEmpty()) { + ui->messagesTabWidget->setTabEnabled(3, true); + ui->txtEdit->setPlainText(txtInfo); + ui->messagesTabWidget->setCurrentIndex(3); + } } void UEFITool::currentTabChanged(int index) @@ -1184,7 +1310,7 @@ void UEFITool::currentTabChanged(int index) void UEFITool::loadGuidDatabase() { - QString path = QFileDialog::getOpenFileName(this, tr("Select GUID database file to load"), currentDir, tr("Comma-separated values files (*.csv);;All files (*)")); + UString path = QFileDialog::getOpenFileName(this, tr("Select GUID database file to load"), currentDir, tr("Comma-separated values files (*.csv);;All files (*)")); if (!path.isEmpty()) { initGuidDatabase(path); if (!currentPath.isEmpty() && QMessageBox::Yes == QMessageBox::information(this, tr("New GUID database loaded"), tr("Apply new GUID database on the opened file?\nUnsaved changes and tree position will be lost."), QMessageBox::Yes, QMessageBox::No)) @@ -1210,9 +1336,9 @@ void UEFITool::loadDefaultGuidDatabase() void UEFITool::generateReport() { - QString path = QFileDialog::getSaveFileName(this, tr("Save report to text file"), currentPath + ".report.txt", tr("Text files (*.txt);;All files (*)")); + UString path = QFileDialog::getSaveFileName(this, tr("Save report to text file"), currentPath + ".report.txt", tr("Text files (*.txt);;All files (*)")); if (!path.isEmpty()) { - std::vector report = ffsReport->generate(); + std::vector report = ffsReport->generate(); if (report.size()) { QFile file; file.setFileName(path); diff --git a/UEFITool/uefitool.h b/UEFITool/uefitool.h index ced3065..63103a7 100644 --- a/UEFITool/uefitool.h +++ b/UEFITool/uefitool.h @@ -76,6 +76,10 @@ private slots: void openImageFile(); void openImageFileInNewWindow(); void saveImageFile(); + + void specifyPathIDA(); + void specifyPathIDA32(); + void specifyPathIDA64(); void search(); void goToOffset(); @@ -85,7 +89,7 @@ private slots: void bodyHexView(); void goToData(); - void extract(const UINT8 mode); + void extract(const UINT8 mode, UString* pathOut = NULL); void extractAsIs(); void extractBody(); void extractBodyUncompressed(); @@ -103,6 +107,10 @@ private slots: void remove(); + void inspectIDA32(); + void inspectIDA64(); + void inspect(const UINT8 mode); + void copyMessage(); void copyAllMessages(); void enableMessagesCopyActions(QListWidgetItem* item); @@ -139,6 +147,8 @@ private: QString currentDir; QString currentPath; QString currentProgramPath; + QString idaPath32; + QString idaPath64; QFont currentFont; const QString version; bool markingEnabled; diff --git a/UEFITool/uefitool.ui b/UEFITool/uefitool.ui index 6950a81..752f323 100644 --- a/UEFITool/uefitool.ui +++ b/UEFITool/uefitool.ui @@ -142,7 +142,7 @@ true - 0 + 3 @@ -241,6 +241,82 @@ + + + true + + + TXT + + + + 0 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + false + + + false + + + true + + + + + + + + true + + + Microcode + + + + 0 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + false + + + false + + + true + + + + + Search @@ -311,7 +387,7 @@ 0 0 851 - 31 + 21 @@ -328,6 +404,8 @@ + + @@ -424,6 +502,9 @@ + + + @@ -907,6 +988,30 @@ Ctrl+Alt+D + + + true + + + Specify path to IDA Pro + + + + + false + + + Inspect body with IDA + + + + + false + + + Inspect body with IDA64 + + diff --git a/common/basetypes.h b/common/basetypes.h index 0a8407d..db77299 100644 --- a/common/basetypes.h +++ b/common/basetypes.h @@ -19,54 +19,57 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include typedef size_t USTATUS; -#define U_SUCCESS 0 -#define U_INVALID_PARAMETER 1 -#define U_BUFFER_TOO_SMALL 2 -#define U_OUT_OF_RESOURCES 3 -#define U_OUT_OF_MEMORY 4 -#define U_FILE_OPEN 5 -#define U_FILE_READ 6 -#define U_FILE_WRITE 7 -#define U_ITEM_NOT_FOUND 8 -#define U_UNKNOWN_ITEM_TYPE 9 -#define U_INVALID_FLASH_DESCRIPTOR 10 -#define U_INVALID_REGION 11 -#define U_EMPTY_REGION 12 -#define U_BIOS_REGION_NOT_FOUND 13 -#define U_VOLUMES_NOT_FOUND 14 -#define U_INVALID_VOLUME 15 -#define U_VOLUME_REVISION_NOT_SUPPORTED 16 -#define U_COMPLEX_BLOCK_MAP 17 -#define U_UNKNOWN_FFS 18 -#define U_INVALID_FILE 19 -#define U_INVALID_SECTION 20 -#define U_UNKNOWN_SECTION 21 -#define U_STANDARD_COMPRESSION_FAILED 22 -#define U_CUSTOMIZED_COMPRESSION_FAILED 23 -#define U_STANDARD_DECOMPRESSION_FAILED 24 -#define U_CUSTOMIZED_DECOMPRESSION_FAILED 25 -#define U_UNKNOWN_COMPRESSION_TYPE 26 -#define U_DEPEX_PARSE_FAILED 27 -#define U_UNKNOWN_EXTRACT_MODE 28 -#define U_UNKNOWN_REPLACE_MODE 29 -#define U_UNKNOWN_IMAGE_TYPE 30 -#define U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE 31 -#define U_UNKNOWN_RELOCATION_TYPE 32 -#define U_DIR_ALREADY_EXIST 33 -#define U_DIR_CREATE 34 -#define U_DIR_CHANGE 35 -#define U_TRUNCATED_IMAGE 36 -#define U_INVALID_CAPSULE 37 -#define U_STORES_NOT_FOUND 38 -#define U_INVALID_IMAGE 39 -#define U_INVALID_RAW_AREA 40 -#define U_INVALID_FIT 41 -#define U_INVALID_MICROCODE 42 -#define U_INVALID_ACM 43 -#define U_INVALID_BG_KEY_MANIFEST 44 -#define U_INVALID_BG_BOOT_POLICY 45 -#define U_ELEMENTS_NOT_FOUND 46 -#define U_NOT_IMPLEMENTED 0xFF +#define U_SUCCESS 0 +#define U_INVALID_PARAMETER 1 +#define U_BUFFER_TOO_SMALL 2 +#define U_OUT_OF_RESOURCES 3 +#define U_OUT_OF_MEMORY 4 +#define U_FILE_OPEN 5 +#define U_FILE_READ 6 +#define U_FILE_WRITE 7 +#define U_ITEM_NOT_FOUND 8 +#define U_UNKNOWN_ITEM_TYPE 9 +#define U_INVALID_FLASH_DESCRIPTOR 10 +#define U_INVALID_REGION 11 +#define U_EMPTY_REGION 12 +#define U_BIOS_REGION_NOT_FOUND 13 +#define U_VOLUMES_NOT_FOUND 14 +#define U_INVALID_VOLUME 15 +#define U_VOLUME_REVISION_NOT_SUPPORTED 16 +#define U_COMPLEX_BLOCK_MAP 17 +#define U_UNKNOWN_FFS 18 +#define U_INVALID_FILE 19 +#define U_INVALID_SECTION 20 +#define U_UNKNOWN_SECTION 21 +#define U_STANDARD_COMPRESSION_FAILED 22 +#define U_CUSTOMIZED_COMPRESSION_FAILED 23 +#define U_STANDARD_DECOMPRESSION_FAILED 24 +#define U_CUSTOMIZED_DECOMPRESSION_FAILED 25 +#define U_UNKNOWN_COMPRESSION_TYPE 26 +#define U_DEPEX_PARSE_FAILED 27 +#define U_UNKNOWN_EXTRACT_MODE 28 +#define U_UNKNOWN_REPLACE_MODE 29 +#define U_UNKNOWN_IMAGE_TYPE 30 +#define U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE 31 +#define U_UNKNOWN_RELOCATION_TYPE 32 +#define U_DIR_ALREADY_EXIST 33 +#define U_DIR_CREATE 34 +#define U_DIR_CHANGE 35 +#define U_TRUNCATED_IMAGE 36 +#define U_INVALID_CAPSULE 37 +#define U_STORES_NOT_FOUND 38 +#define U_INVALID_IMAGE 39 +#define U_INVALID_RAW_AREA 40 +#define U_INVALID_FIT 41 +#define U_INVALID_MICROCODE 42 +#define U_INVALID_ACM 43 +#define U_INVALID_BG_KEY_MANIFEST 44 +#define U_INVALID_BG_BOOT_POLICY 45 +#define U_INVALID_TXT_CONF 46 +#define U_ELEMENTS_NOT_FOUND 47 +#define U_PEI_CORE_ENTRY_POINT_NOT_FOUND 48 +#define U_INVALID_STORE_SIZE 49 +#define U_NOT_IMPLEMENTED 0xFF // UDK porting definitions typedef uint8_t BOOLEAN; @@ -87,6 +90,10 @@ typedef ptrdiff_t INTN; #define VOID void #define STATIC static +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif + #ifndef TRUE #define TRUE ((BOOLEAN)(1==1)) #endif @@ -125,6 +132,10 @@ typedef ptrdiff_t INTN; #define EXTRACT_MODE_BODY 1 #define EXTRACT_MODE_BODY_UNCOMPRESSED 2 +// Item inspect modes +#define INSPECT_MODE_IDA32 0 +#define INSPECT_MODE_IDA64 1 + // Item replace modes #define REPLACE_MODE_AS_IS 0 #define REPLACE_MODE_BODY 1 diff --git a/common/bootguard.h b/common/bootguard.h index dcefd20..80fc5d5 100644 --- a/common/bootguard.h +++ b/common/bootguard.h @@ -72,11 +72,14 @@ typedef struct BG_MICROSOFT_PMDA_ENTRY_ // Intel ACM // -#define INTEL_ACM_MODULE_TYPE 0x00030002 -#define INTEL_ACM_MODULE_VENDOR 0x8086 +#define INTEL_ACM_MODULE_TYPE 0x2 +#define INTEL_ACM_MODULE_SUBTYPE_TXT_ACM 0x0 +#define INTEL_ACM_MODULE_SUBTYPE_S_ACM 0x1 +#define INTEL_ACM_MODULE_VENDOR 0x8086 typedef struct INTEL_ACM_HEADER_ { - UINT32 ModuleType; + UINT16 ModuleType; + UINT16 ModuleSubtype; UINT32 HeaderType; UINT32 HeaderVersion; UINT16 ChipsetId; @@ -203,4 +206,4 @@ typedef struct BG_KEY_MANIFEST_ { #pragma pack(pop) -#endif // BOOTGUARD_H \ No newline at end of file +#endif // BOOTGUARD_H diff --git a/common/ffs.cpp b/common/ffs.cpp index 9b87e2d..0138da3 100644 --- a/common/ffs.cpp +++ b/common/ffs.cpp @@ -118,3 +118,52 @@ UString sectionTypeToUString(const UINT8 type) } } +UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header) +{ + if (!header) + return 0; + + bool extended = false; + if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) { + extended = true; + } + + switch (header->Type) + { + case EFI_SECTION_GUID_DEFINED: { + if (!extended) { + const EFI_GUID_DEFINED_SECTION* gdsHeader = (const EFI_GUID_DEFINED_SECTION*)header; + if (QByteArray((const char*)&gdsHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { + const WIN_CERTIFICATE* certificateHeader = (const WIN_CERTIFICATE*)(gdsHeader + 1); + return gdsHeader->DataOffset + certificateHeader->Length; + } + return gdsHeader->DataOffset; + } + else { + const EFI_GUID_DEFINED_SECTION2* gdsHeader = (const EFI_GUID_DEFINED_SECTION2*)header; + if (QByteArray((const char*)&gdsHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { + const WIN_CERTIFICATE* certificateHeader = (const WIN_CERTIFICATE*)(gdsHeader + 1); + return gdsHeader->DataOffset + certificateHeader->Length; + } + return gdsHeader->DataOffset; + } + } + case EFI_SECTION_COMPRESSION: return extended ? sizeof(EFI_COMPRESSION_SECTION2) : sizeof(EFI_COMPRESSION_SECTION); + case EFI_SECTION_DISPOSABLE: return extended ? sizeof(EFI_DISPOSABLE_SECTION2) : sizeof(EFI_DISPOSABLE_SECTION); + case EFI_SECTION_PE32: return extended ? sizeof(EFI_PE32_SECTION2) : sizeof(EFI_PE32_SECTION); + case EFI_SECTION_PIC: return extended ? sizeof(EFI_PIC_SECTION2) : sizeof(EFI_PIC_SECTION); + case EFI_SECTION_TE: return extended ? sizeof(EFI_TE_SECTION2) : sizeof(EFI_TE_SECTION); + case EFI_SECTION_DXE_DEPEX: return extended ? sizeof(EFI_DXE_DEPEX_SECTION2) : sizeof(EFI_DXE_DEPEX_SECTION); + case EFI_SECTION_VERSION: return extended ? sizeof(EFI_VERSION_SECTION2) : sizeof(EFI_VERSION_SECTION); + case EFI_SECTION_USER_INTERFACE: return extended ? sizeof(EFI_USER_INTERFACE_SECTION2) : sizeof(EFI_USER_INTERFACE_SECTION); + case EFI_SECTION_COMPATIBILITY16: return extended ? sizeof(EFI_COMPATIBILITY16_SECTION2) : sizeof(EFI_COMPATIBILITY16_SECTION); + case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: return extended ? sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION2) : sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION); + case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return extended ? sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION2) : sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION); + case EFI_SECTION_RAW: return extended ? sizeof(EFI_RAW_SECTION2) : sizeof(EFI_RAW_SECTION); + case EFI_SECTION_PEI_DEPEX: return extended ? sizeof(EFI_PEI_DEPEX_SECTION2) : sizeof(EFI_PEI_DEPEX_SECTION); + case EFI_SECTION_MM_DEPEX: return extended ? sizeof(EFI_SMM_DEPEX_SECTION2) : sizeof(EFI_SMM_DEPEX_SECTION); + case INSYDE_SECTION_POSTCODE: return extended ? sizeof(POSTCODE_SECTION2) : sizeof(POSTCODE_SECTION); + case PHOENIX_SECTION_POSTCODE: return extended ? sizeof(POSTCODE_SECTION2) : sizeof(POSTCODE_SECTION); + default: return extended ? sizeof(EFI_COMMON_SECTION_HEADER2) : sizeof(EFI_COMMON_SECTION_HEADER); + } +} diff --git a/common/ffs.h b/common/ffs.h index 56e1b85..8830a04 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -26,6 +26,7 @@ extern UString guidToUString(const EFI_GUID& guid, bool convertToString = true); extern UString fileTypeToUString(const UINT8 type); extern UString sectionTypeToUString(const UINT8 type); + //***************************************************************************** // EFI Capsule //***************************************************************************** @@ -369,6 +370,10 @@ const UByteArray AMI_CORE_DXE_GUID // 5AE3F37E-4EAE-41AE-8240-35465B5E81EB const UByteArray EFI_DXE_CORE_GUID // D6A2CB7F-6A18-4E2F-B43B-9920A733700A ("\x7F\xCB\xA2\xD6\x18\x6A\x2F\x4E\xB4\x3B\x99\x20\xA7\x33\x70\x0A", 16); +// TXT ACM +const UByteArray EFI_TXT_ACM_GUID // 2D27C618-7DCD-41F5-BB10-21166BE7E143 +("\x18\xC6\x27\x2D\xCD\x7D\xF5\x41\xBB\x10\x21\x16\x6B\xE7\xE1\x43", 16); + // FFS size conversion routines extern VOID uint32ToUint24(UINT32 size, UINT8* ffsSize); extern UINT32 uint24ToUint32(const UINT8* ffsSize); @@ -432,6 +437,14 @@ typedef struct EFI_COMPRESSION_SECTION_ { UINT8 CompressionType; } EFI_COMPRESSION_SECTION; +typedef struct _EFI_COMPRESSION_SECTION2 { + UINT8 Size[3]; + UINT8 Type; + UINT32 ExtendedSize; + UINT32 UncompressedLength; + UINT8 CompressionType; +} EFI_COMPRESSION_SECTION2; + typedef struct EFI_COMPRESSION_SECTION_APPLE_ { UINT32 UncompressedLength; UINT32 CompressionType; @@ -456,6 +469,16 @@ typedef struct EFI_GUID_DEFINED_SECTION_APPLE_ { UINT32 Reserved; } EFI_GUID_DEFINED_SECTION_APPLE; +typedef struct _EFI_GUID_DEFINED_SECTION2 { + UINT8 Size[3]; + UINT8 Type; + UINT32 ExtendedSize; + EFI_GUID SectionDefinitionGuid; + UINT16 DataOffset; + UINT16 Attributes; +} EFI_GUID_DEFINED_SECTION2; + + // Attributes for GUID defined section #define EFI_GUIDED_SECTION_PROCESSING_REQUIRED 0x01 #define EFI_GUIDED_SECTION_AUTH_STATUS_VALID 0x02 @@ -511,16 +534,64 @@ typedef struct EFI_VERSION_SECTION_ { UINT16 BuildNumber; } EFI_VERSION_SECTION; +typedef struct _EFI_VERSION_SECTION2 { + UINT8 Size[3]; + UINT8 Type; + UINT32 ExtendedSize; + UINT16 BuildNumber; +} EFI_VERSION_SECTION2; + // Freeform subtype GUID section typedef struct EFI_FREEFORM_SUBTYPE_GUID_SECTION_ { EFI_GUID SubTypeGuid; } EFI_FREEFORM_SUBTYPE_GUID_SECTION; +typedef struct _EFI_FREEFORM_SUBTYPE_GUID_SECTION2 { + UINT8 Size[3]; + UINT8 Type; + UINT32 ExtendedSize; + EFI_GUID SubTypeGuid; +} EFI_FREEFORM_SUBTYPE_GUID_SECTION2; + // Phoenix SCT and Insyde postcode section typedef struct POSTCODE_SECTION_ { UINT32 Postcode; } POSTCODE_SECTION; +typedef struct _POSTCODE_SECTION2 { + UINT8 Size[3]; + UINT8 Type; + UINT32 ExtendedSize; + UINT32 Postcode; +} POSTCODE_SECTION2; + +// Other sections +typedef EFI_COMMON_SECTION_HEADER EFI_DISPOSABLE_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_DISPOSABLE_SECTION2; +typedef EFI_COMMON_SECTION_HEADER EFI_RAW_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_RAW_SECTION2; +typedef EFI_COMMON_SECTION_HEADER EFI_DXE_DEPEX_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_DXE_DEPEX_SECTION2; +typedef EFI_COMMON_SECTION_HEADER EFI_PEI_DEPEX_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_PEI_DEPEX_SECTION2; +typedef EFI_COMMON_SECTION_HEADER EFI_SMM_DEPEX_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_SMM_DEPEX_SECTION2; +typedef EFI_COMMON_SECTION_HEADER EFI_PE32_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_PE32_SECTION2; +typedef EFI_COMMON_SECTION_HEADER EFI_PIC_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_PIC_SECTION2; +typedef EFI_COMMON_SECTION_HEADER EFI_TE_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_TE_SECTION2; +typedef EFI_COMMON_SECTION_HEADER EFI_COMPATIBILITY16_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_COMPATIBILITY16_SECTION2; +typedef EFI_COMMON_SECTION_HEADER EFI_FIRMWARE_VOLUME_IMAGE_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_FIRMWARE_VOLUME_IMAGE_SECTION2; +typedef EFI_COMMON_SECTION_HEADER EFI_USER_INTERFACE_SECTION; +typedef EFI_COMMON_SECTION_HEADER2 EFI_USER_INTERFACE_SECTION2; + +// Section routines +UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header); + //***************************************************************************** // EFI Dependency Expression //***************************************************************************** diff --git a/common/ffsbuilder.cpp b/common/ffsbuilder.cpp index f643082..59861b4 100644 --- a/common/ffsbuilder.cpp +++ b/common/ffsbuilder.cpp @@ -16,6 +16,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "ffs.h" #include "peimage.h" #include "utility.h" +#include "nvram.h" USTATUS FfsBuilder::erase(const UModelIndex & index, UByteArray & erased) { @@ -36,29 +37,127 @@ USTATUS FfsBuilder::erase(const UModelIndex & index, UByteArray & erased) } } - erased = QByteArray(model->header(index).size() + model->body(index).size() + model->tail(index).size(), emptyByte); + erased = UByteArray(model->header(index).size() + model->body(index).size() + model->tail(index).size(), emptyByte); return U_SUCCESS; } -USTATUS FfsBuilder::build(const UModelIndex & root, UByteArray & image) +USTATUS FfsBuilder::build(const UModelIndex & index, UByteArray & reconstructed) { - // Sanity check - if (!root.isValid()) - return U_INVALID_PARAMETER; + if (!index.isValid()) + return U_SUCCESS; - if (model->type(root) == Types::Capsule) { - return buildCapsule(root, image); - } - else if (model->type(root) == Types::Image) { - if (model->subtype(root) == Subtypes::IntelImage) { - return buildIntelImage(root, image); + USTATUS result; + + switch (model->type(index)) { + case Types::Image: + if (model->subtype(index) == Subtypes::IntelImage) { + result = buildIntelImage(index, reconstructed); + if (result) + return result; } - else if (model->subtype(root) == Subtypes::UefiImage) { - return buildRawArea(root, image); + else { + //Other images types can be reconstructed like regions + result = buildRegion(index, reconstructed); + if (result) + return result; } + break; + + case Types::Capsule: + result = buildCapsule(index, reconstructed); + if (result) + return result; + break; + + case Types::Region: + result = buildRegion(index, reconstructed); + if (result) + return result; + break; + + case Types::Padding: + result = buildPadding(index, reconstructed); + if (result) + return result; + break; + + case Types::Volume: + // Nvram rebuild support + if(model->subtype(index) == Subtypes::NvramVolume) + result = buildNvramVolume(index, reconstructed); + else + result = buildVolume(index, reconstructed); + + if (result) + return result; + break; + + case Types::Section: + result = buildSection(index, 0, reconstructed); + if (result) + return result; + break; + default: + msg(usprintf("build: unknown item type %1").arg(model->type(index)), index); + return U_UNKNOWN_ITEM_TYPE; } + return U_SUCCESS; +} + +USTATUS FfsBuilder::buildRegion(const UModelIndex& index, UByteArray& reconstructed, bool includeHeader) +{ + if (!index.isValid()) + return U_SUCCESS; + + USTATUS result; + + // No action + if (model->action(index) == Actions::NoAction) { + reconstructed = model->header(index).append(model->body(index)); + return U_SUCCESS; + } + else if (model->action(index) == Actions::Remove) { + reconstructed.clear(); + return U_SUCCESS; + } + else if (model->action(index) == Actions::Rebuild || + model->action(index) == Actions::Replace) { + if (model->rowCount(index)) { + reconstructed.clear(); + // Reconstruct children + for (int i = 0; i < model->rowCount(index); i++) { + UByteArray child; + result = build(index.child(i, 0), child); + if (result) + return result; + reconstructed.append(child); + } + } + // Use stored item body + else + reconstructed = model->body(index); + + // Check size of reconstructed region, it must be same + if (reconstructed.size() > model->body(index).size()) { + msg("buildRegion: reconstructed region size is bigger then original ", + index); + return U_INVALID_PARAMETER; + } + else if (reconstructed.size() < model->body(index).size()) { + msg("buildRegion: reconstructed region size is smaller then original ", + index); + return U_INVALID_PARAMETER; + } + + // Reconstruction successful + if (includeHeader) + reconstructed = model->header(index).append(reconstructed); + return U_SUCCESS; + } + + // All other actions are not supported return U_NOT_IMPLEMENTED; } @@ -239,7 +338,7 @@ USTATUS FfsBuilder::buildIntelImage(const UModelIndex & index, UByteArray & inte return U_NOT_IMPLEMENTED; } -USTATUS FfsBuilder::buildRawArea(const UModelIndex & index, UByteArray & rawArea) +USTATUS FfsBuilder::buildRawArea(const UModelIndex & index, UByteArray & rawArea, bool includeHeader) { // Sanity check if (!index.isValid()) @@ -306,7 +405,11 @@ USTATUS FfsBuilder::buildRawArea(const UModelIndex & index, UByteArray & rawArea } // Build successful, add header if needed - rawArea = model->header(index) + rawArea + model->tail(index); + if(includeHeader) + rawArea = model->header(index) + rawArea + model->tail(index); + else + rawArea = rawArea + model->tail(index); + return U_SUCCESS; } @@ -379,34 +482,966 @@ USTATUS FfsBuilder::buildFreeSpace(const UModelIndex & index, UByteArray & freeS USTATUS FfsBuilder::buildVolume(const UModelIndex & index, UByteArray & volume) { - U_UNUSED_PARAMETER(index); - U_UNUSED_PARAMETER(volume); + if (!index.isValid()) + return U_SUCCESS; + + USTATUS result; + + // No action + if (model->action(index) == Actions::NoAction) { + volume = model->header(index).append(model->body(index)); + return U_SUCCESS; + } + else if (model->action(index) == Actions::Remove) { + volume.clear(); + return U_SUCCESS; + } + else if (model->action(index) == Actions::Replace || + model->action(index) == Actions::Rebuild) { + UByteArray header = model->header(index); + UByteArray body = model->body(index); + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); + + // Check sanity of HeaderLength + if (volumeHeader->HeaderLength > header.size()) { + msg(UString("buildVolume: invalid volume header length, reconstruction is not possible"), index); + return U_INVALID_VOLUME; + } + + // Recalculate volume header checksum + volumeHeader->Checksum = 0; + volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); + + // Get volume size + UINT32 volumeSize = header.size() + body.size(); + + // Reconstruct volume body + UINT32 freeSpaceOffset = 0; + if (model->rowCount(index)) { + volume.clear(); + UINT8 polarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE; + char empty = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; + + // Calculate volume base for volume + UINT32 volumeBase; + UByteArray file; + bool baseFound = false; + + // Search for VTF + for (int i = 0; i < model->rowCount(index); i++) { + file = model->header(index.child(i, 0)); + // VTF found + if (file.left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { + baseFound = true; + volumeBase = (UINT32)(0x100000000 - volumeSize); + break; + } + } + + // Determine if volume is inside compressed item + if (!baseFound) { + // Iterate up to the root, checking for compression type to be other then none + for (UModelIndex parentIndex = index.parent(); model->type(parentIndex) != Types::Root; parentIndex = parentIndex.parent()) { + UByteArray data = model->parsingData(parentIndex); + const COMPRESSED_SECTION_PARSING_DATA* pdata = (const COMPRESSED_SECTION_PARSING_DATA*)data.constData(); + + if (pdata->algorithm != COMPRESSION_ALGORITHM_NONE) { + // No rebase needed for compressed PEI files + baseFound = true; + volumeBase = 0; + break; + } + } + } + + // Find volume base address using first PEI file in it + if (!baseFound) { + // 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->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)){ + UModelIndex peiFile = index.child(i, 0); + UINT32 sectionOffset = sizeof(EFI_FFS_FILE_HEADER); + // BUGBUG: this parsing is bad and doesn't support large files, but it needs to be performed only for very old images with uncompressed DXE volumes, so whatever + // Search for PE32 or TE section + for (int j = 0; j < model->rowCount(peiFile); j++) { + if (model->subtype(peiFile.child(j, 0)) == EFI_SECTION_PE32 || + model->subtype(peiFile.child(j, 0)) == EFI_SECTION_TE) { + UModelIndex image = peiFile.child(j, 0); + // Check for correct action + if (model->action(image) == Actions::Remove || model->action(image) == Actions::Insert) + continue; + // Calculate relative base address + UINT32 relbase = fileOffset + sectionOffset + model->header(image).size(); + // Calculate offset of image relative to file base + UINT32 imagebase = 0; + result = getBase(model->body(image), imagebase); // imagebase passed by reference + if (!result) { + // Calculate volume base + volumeBase = imagebase - relbase; + baseFound = true; + goto out; + } + } + sectionOffset += model->header(peiFile.child(j, 0)).size() + model->body(peiFile.child(j, 0)).size(); + sectionOffset = ALIGN4(sectionOffset); + } + } + fileOffset += model->header(index.child(i, 0)).size() + model->body(index.child(i, 0)).size(); + fileOffset = ALIGN8(fileOffset); + } + } +out: + // Do not set volume base + if (!baseFound) + volumeBase = 0; + + // Reconstruct files in volume + UINT32 offset = 0; + UByteArray padFileGuid = EFI_FFS_PAD_FILE_GUID; + UByteArray vtf; + UModelIndex vtfIndex; + UINT32 nonUefiDataOffset = 0; + UByteArray nonUefiData; + for (int i = 0; i < model->rowCount(index); i++) { + // 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 + + // Align to 8 byte boundary + UINT32 alignment = offset % 8; + if (alignment) { + alignment = 8 - alignment; + offset += alignment; + volume.append(UByteArray(alignment, empty)); + } + + // Calculate file base + UINT32 fileBase = volumeBase ? volumeBase + header.size() + offset : 0; + + // Reconstruct file + result = buildFile(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(); + UINT32 fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER); + if (volumeHeader->Revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) + fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER2); + + // Pad file + if (fileHeader->Type == EFI_FV_FILETYPE_PAD) { + 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 = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; + alignment = (UINT32)(1UL <Revision, polarity, pad); + if (result) + return result; + // Append constructed pad file to volume body + volume.append(pad); + offset += size; + } + + // Append current file to new volume body + volume.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; + } + } + } + + // Check volume sanity + if (!vtf.isEmpty() && !nonUefiData.isEmpty()) { + msg(usprintf("buildVolume: both VTF and non-UEFI data found in the volume, reconstruction is not possible"), index); + return U_INVALID_VOLUME; + } + + // Check for free space offset in ZeroVector + if (model->text(index).contains("AppleFSO ")) { + // Align current offset to 8 byte boundary + UINT32 alignment = offset % 8; + freeSpaceOffset = model->header(index).size() + offset; + if (alignment) { + alignment = 8 - alignment; + freeSpaceOffset += alignment; + } + } + + // Insert VTF or non-UEFI data to it's correct place + if (!vtf.isEmpty()) { // VTF found + // Determine correct VTF offset + UINT32 vtfOffset = model->body(index).size() - vtf.size(); + + if (vtfOffset % 8) { + msg(usprintf("buildVolume: wrong size of the Volume Top File"), index); + return U_INVALID_FILE; + } + // Insert pad file to fill the gap + if (vtfOffset > offset) { + // Determine pad file size + UINT32 size = vtfOffset - offset; + // Construct pad file + UByteArray pad; + result = buildPadFile(padFileGuid, size, volumeHeader->Revision, polarity, pad); + if (result) + return result; + // Append constructed pad file to volume body + volume.append(pad); + } + // No more space left in volume + else if (offset > vtfOffset) { + msg(usprintf("buildVolume: no space left to insert VTF, need %xh (%d) byte(s) more", + offset - vtfOffset, offset - vtfOffset), index); + return U_INVALID_VOLUME; + } + + // Calculate VTF base + UINT32 vtfBase = volumeBase ? volumeBase + vtfOffset : 0; + + // Reconstruct VTF again + result = buildFile(vtfIndex, volumeHeader->Revision, polarity, vtfBase, vtf); + if (result) + return result; + + // Patch VTF + if (!parser->peiCoreEntryPoint) { + msg("patchVtf: PEI Core entry point can't be determined. VTF can't be patched.", index); + return U_PEI_CORE_ENTRY_POINT_NOT_FOUND; + } + if (parser->newPeiCoreEntryPoint && parser->peiCoreEntryPoint != parser->newPeiCoreEntryPoint) { + // Replace last occurrence of oldPeiCoreEntryPoint with newPeiCoreEntryPoint + QByteArray old((char*)&parser->peiCoreEntryPoint, sizeof(parser->peiCoreEntryPoint)); + int i = vtf.lastIndexOf(old); + if (i == -1) + msg("patchVtf: PEI Core entry point can't be found in VTF. VTF not patched.", index); + else { + UINT32* data = (UINT32*)(vtf.data() + i); + *data = parser->newPeiCoreEntryPoint; + } + } + + // Append VTF + volume.append(vtf); + } + else if (!nonUefiData.isEmpty()) { //Non-UEFI data found + // No space left + if (offset > nonUefiDataOffset) { + msg(usprintf("buildVolume: no space left to insert non-UEFI data, need %xh (%d) byte(s) more", + offset - nonUefiDataOffset, offset - nonUefiDataOffset), index); + return U_INVALID_VOLUME; + } + // Append additional free space + else if (nonUefiDataOffset > offset) { + volume.append(UByteArray(nonUefiDataOffset - offset, empty)); + } + + // Append VTF + volume.append(nonUefiData); + } + else { + // Fill the rest of volume space with empty char + if (body.size() > volume.size()) { + // Fill volume end with empty char + volume.append(UByteArray(body.size() - volume.size(), empty)); + } + else if (body.size() < volume.size()) { + // Check if volume can be grown + // Root volume can't be grown + UINT8 parentType = model->type(index.parent()); + if (parentType != Types::File && parentType != Types::Section) { + msg("buildVolume: root volume can't be grown", index); + return U_INVALID_VOLUME; + } + + // Grow volume to fit new body + UINT32 newSize = header.size() + volume.size(); + result = growVolume(header, volumeSize, newSize); + if (result) + return result; + + // Fill volume end with empty char + volume.append(UByteArray(newSize - header.size() - volume.size(), empty)); + volumeSize = newSize; + } + } + } + // Use current volume body + else { + volume = model->body(index); + + // BUGBUG: volume size may change during this operation for volumes withour files in them + // but such volumes are fairly rare + } + + // Check new volume size + if ((UINT32)(header.size() + volume.size()) != volumeSize) { + msg("buildVolume: volume size can't be changed", index); + return U_INVALID_VOLUME; + } + + // Reconstruction successful + volume = header.append(volume); + + // Recalculate CRC32 in ZeroVector, if needed + if (model->text(index).contains("AppleCRC32 ")) { + // Get current CRC32 value from volume header + const UINT32 currentCrc = *(const UINT32*)(volume.constData() + 8); + // Calculate new value + UINT32 crc = crc32(0, (const UINT8*)volume.constData() + volumeHeader->HeaderLength, volume.size() - volumeHeader->HeaderLength); + // Update the value + if (currentCrc != crc) { + *(UINT32*)(volume.data() + 8) = crc; + + // Recalculate header checksum + volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)volume.data(); + volumeHeader->Checksum = 0; + volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); + } + } + + // Store new free space offset, if needed + if (model->text(index).contains("AppleFSO ")) { + // Get current CRC32 value from volume header + const UINT32 currentFso = *(const UINT32*)(volume.constData() + 12); + // Update the value + if (freeSpaceOffset != 0 && currentFso != freeSpaceOffset) { + *(UINT32*)(volume.data() + 12) = freeSpaceOffset; + + // Recalculate header checksum + volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)volume.data(); + volumeHeader->Checksum = 0; + volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); + } + } + + return U_SUCCESS; + } return U_NOT_IMPLEMENTED; } -USTATUS FfsBuilder::buildPadFile(const UModelIndex & index, UByteArray & padFile) +USTATUS FfsBuilder::buildNvramVolume(const UModelIndex & index, UByteArray & volume) { - U_UNUSED_PARAMETER(index); - U_UNUSED_PARAMETER(padFile); + if (!index.isValid()) + return U_SUCCESS; + + USTATUS result; + + // No action + if (model->action(index) == Actions::NoAction) { + volume = model->header(index).append(model->body(index)); + return U_SUCCESS; + } + else if (model->action(index) == Actions::Remove) { + volume.clear(); + return U_SUCCESS; + } + else if (model->action(index) == Actions::Replace || + model->action(index) == Actions::Rebuild) { + UByteArray header = model->header(index); + UByteArray body = model->body(index); + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); + + // Check sanity of HeaderLength + if (volumeHeader->HeaderLength > header.size()) { + msg(UString("buildNvramVolume: invalid volume header length, reconstruction is not possible"), index); + return U_INVALID_VOLUME; + } + + // Recalculate volume header checksum + volumeHeader->Checksum = 0; + volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); + + volume.clear(); + + for (int i = 0; i < model->rowCount(index); i++) { + UModelIndex currentIndex = index.child(i, 0); + UByteArray store; + + result = buildNvramStore(currentIndex, store); + if(result) + return result; + + // Element reconstruct success + volume.append(store); + } + + // Volume reconstruct success + volume = header.append(volume); + + + return U_SUCCESS; + + } return U_NOT_IMPLEMENTED; } -USTATUS FfsBuilder::buildFile(const UModelIndex & index, UByteArray & file) +USTATUS FfsBuilder::buildNvramStore(const UModelIndex & index, UByteArray & store) { - U_UNUSED_PARAMETER(index); - U_UNUSED_PARAMETER(file); + UByteArray header = model->header(index); + UByteArray body = model->body(index); + UINT8 type = model->type(index); - return U_NOT_IMPLEMENTED; + if(model->action(index) == Actions::Remove) { + header.clear(); + body.clear(); + } + else if(model->action(index) == Actions::Replace || + model->action(index) == Actions::Rebuild) { + if(type == Types::FdcStore) { + // Recalculate store header + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); + volumeHeader->Checksum = 0; + volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); + + // Rebuild VSS or VSS2 volume inside + UByteArray vssStore; + buildNvramStore(index.child(0, 0), vssStore); + body = vssStore; + } + else if(type == Types::VssStore || type == Types::Vss2Store) { + if(model->rowCount(index)) { + body.clear(); + for (int i = 0; i < model->rowCount(index); i++) { + UModelIndex currentIndex = index.child(i, 0); + UByteArray currentHeader = model->header(currentIndex); + UByteArray currentBody = model->body(currentIndex); + UINT8 currentAction = model->action(currentIndex); + + if(currentAction == Actions::Remove) { + currentHeader.clear(); + currentBody.clear(); + } + else if(currentAction == Actions::Rebuild || + currentAction == Actions::Replace) { + // Recalculate all Apple variables crc's + if(model->subtype(currentIndex) == Subtypes::AppleVssEntry) { + VSS_APPLE_VARIABLE_HEADER* appleVariableHeader = (VSS_APPLE_VARIABLE_HEADER*)currentHeader.data(); + appleVariableHeader->DataCrc32 = crc32(0, (const UINT8*)currentBody.constData(), currentBody.size()); + //appleVariableHeader->DataSize = currentBody.size(); + } + } + body.append(currentHeader.append(currentBody)); + } + } + } + else if(type == Types::FsysStore) { + UByteArray store = header + body; + + // Recalculate store checksum + UINT32 calculatedCrc = crc32(0, (const UINT8*)store.constData(), (const UINT32)store.size() - sizeof(UINT32)); + // Write new checksum + body.replace((const UINT32)body.size() - sizeof(UINT32), sizeof(UINT32), (const char*)calculatedCrc, sizeof(UINT32)); + } + else if(type == Types::EvsaStore) { + UByteArray store = header + body; + + // Recalculate header checksum + const EVSA_STORE_ENTRY* evsaStoreHeader = (const EVSA_STORE_ENTRY*)store.constData(); + UINT8 storeCrc = calculateChecksum8(((const UINT8*)evsaStoreHeader) + 2, evsaStoreHeader->Header.Size - 2); + // Write new checksum + EVSA_ENTRY_HEADER* evsaEntryHeader = (EVSA_ENTRY_HEADER*)header.data(); + evsaEntryHeader->Checksum = storeCrc; + + // Recalculate all crc's + if(model->rowCount(index)) { + body.clear(); + for (int i = 0; i < model->rowCount(index); i++) { + UModelIndex currentIndex = index.child(i, 0); + UByteArray currentHeader = model->header(currentIndex); + UByteArray currentBody = model->body(currentIndex); + UINT8 currentSubtype = model->subtype(currentIndex); + UINT8 currentAction = model->action(currentIndex); + + if(currentAction == Actions::Remove) { + currentHeader.clear(); + currentBody.clear(); + } + else if(currentAction == Actions::Rebuild || + currentAction == Actions::Replace) { + + if(currentSubtype == Subtypes::DataEvsaEntry || + currentSubtype == Subtypes::GuidEvsaEntry || + currentSubtype == Subtypes::NameEvsaEntry) { + UByteArray currentStore = currentHeader + currentBody; + + // Recalculate header checksum + const EVSA_STORE_ENTRY* evsaStoreHeader = (const EVSA_STORE_ENTRY*)currentStore.constData(); + UINT8 entryCrc = calculateChecksum8(((const UINT8*)evsaStoreHeader) + 2, evsaStoreHeader->Header.Size - 2); + // Write new checksum + EVSA_ENTRY_HEADER* evsaEntryHeader = (EVSA_ENTRY_HEADER*)currentHeader.data(); + evsaEntryHeader->Checksum = entryCrc; + } + } + + body.append(currentHeader.append(currentBody)); + } + } + } + else if(type == Types::FtwStore) { + // Recalculate block header checksum + EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)header.data(); + crcFtwBlockHeader->Crc = crc32(0, (const UINT8*)crcFtwBlockHeader, header.size()); + } + } + + // Rebuild end + store.clear(); + store = header.append(body); + + return U_SUCCESS; } -USTATUS FfsBuilder::buildSection(const UModelIndex & index, UByteArray & section) +USTATUS FfsBuilder::buildPadFile(const UByteArray & guid, const UINT32 size, const UINT8 revision, const UINT8 erasePolarity, UByteArray & pad) { - U_UNUSED_PARAMETER(index); - U_UNUSED_PARAMETER(section); + if (size < sizeof(EFI_FFS_FILE_HEADER) || erasePolarity == ERASE_POLARITY_UNKNOWN) + return U_INVALID_PARAMETER; + if (size >= 0xFFFFFF) // TODO: large file support + return U_INVALID_PARAMETER; + + pad = UByteArray(size - guid.size(), erasePolarity == ERASE_POLARITY_TRUE ? '\xFF' : '\x00'); + pad.prepend(guid); + EFI_FFS_FILE_HEADER* header = (EFI_FFS_FILE_HEADER*)pad.data(); + uint32ToUint24(size, header->Size); + header->Attributes = 0x00; + header->Type = EFI_FV_FILETYPE_PAD; + header->State = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID; + // Invert state bits if erase polarity is true + if (erasePolarity == ERASE_POLARITY_TRUE) + header->State = ~header->State; + + // Calculate header checksum + header->IntegrityCheck.Checksum.Header = 0; + header->IntegrityCheck.Checksum.File = 0; + header->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)header, sizeof(EFI_FFS_FILE_HEADER) - 1); + + // Set data checksum + if (revision == 1) + header->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; + else + header->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; + + return U_SUCCESS; +} + +USTATUS FfsBuilder::buildFile(const UModelIndex & index, const UINT8 revision, const UINT8 erasePolarity, const UINT32 base, UByteArray & reconstructed) +{ + if (!index.isValid()) + return U_SUCCESS; + + USTATUS result; + + // No action + if (model->action(index) == Actions::NoAction) { + reconstructed = model->header(index).append(model->body(index)); + const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)model->header(index).constData(); + // Append tail, if needed + if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { + UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; + UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; + reconstructed.append(ht).append(ft); + } + return U_SUCCESS; + } + else if (model->action(index) == Actions::Remove) { + reconstructed.clear(); + return U_SUCCESS; + } + else if (model->action(index) == Actions::Insert || + model->action(index) == Actions::Replace || + model->action(index) == Actions::Rebuild) { + UByteArray header = model->header(index); + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)header.data(); + + // Check erase polarity + if (erasePolarity == ERASE_POLARITY_UNKNOWN) { + msg("buildFile: unknown erase polarity", index); + return U_INVALID_PARAMETER; + } + + // Check file state + // Check top reserved bit of file state to determine it's original erase polarity + UINT8 state = fileHeader->State; + if (state & EFI_FILE_ERASE_POLARITY) + state = ~state; + + // Order of this checks must be preserved + // Check file to have valid state, or delete it otherwise + if (state & EFI_FILE_HEADER_INVALID) { + // File marked to have invalid header and must be deleted + // Do not add anything to queue + msg("buildFile: file is HEADER_INVALID state, and will be removed from reconstructed image", index); + return U_SUCCESS; + } + else if (state & EFI_FILE_DELETED) { + // File marked to have been deleted form and must be deleted + // Do not add anything to queue + msg("buildFile: file is in DELETED state, and will be removed from reconstructed image", index); + return U_SUCCESS; + } + else if (state & EFI_FILE_MARKED_FOR_UPDATE) { + // File is marked for update, the mark must be removed + msg("buildFile: file's MARKED_FOR_UPDATE state cleared", index); + } + else if (state & EFI_FILE_DATA_VALID) { + // File is in good condition, reconstruct it + } + else if (state & EFI_FILE_HEADER_VALID) { + // Header is valid, but data is not, so file must be deleted + msg("buildFile: file is in HEADER_VALID (but not in DATA_VALID) state, and will be removed from reconstructed image", index); + return U_SUCCESS; + } + else if (state & EFI_FILE_HEADER_CONSTRUCTION) { + // Header construction not finished, so file must be deleted + msg("buildFile: file is in HEADER_CONSTRUCTION (but not in DATA_VALID) state, and will be removed from reconstructed image", index); + return U_SUCCESS; + } + + // Reconstruct file body + if (model->rowCount(index)) { + reconstructed.clear(); + // Construct new file body + // File contains raw data, must be parsed as region without header + if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) { + result = buildRawArea(index, reconstructed, false); + if (result) + return result; + } + // File contains sections + else { + UINT32 offset = 0; + UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER); + if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + headerSize = sizeof(EFI_FFS_FILE_HEADER2); + } + + for (int i = 0; i < model->rowCount(index); i++) { + // Align to 4 byte boundary + UINT8 alignment = offset % 4; + if (alignment) { + alignment = 4 - alignment; + offset += alignment; + reconstructed.append(UByteArray(alignment, '\x00')); + } + + // Calculate section base + UINT32 sectionBase = base ? base + headerSize + offset : 0; + UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; + UINT32 fileAlignment = (UINT32)(1UL << alignmentPower); + UINT32 alignmentBase = base + headerSize; + if (alignmentBase % fileAlignment) { + // File will be unaligned if added as is, so we must add pad file before it + // Determine pad file size + UINT32 size = fileAlignment - (alignmentBase % fileAlignment); + // Required padding is smaller then minimal pad file size + while (size < sizeof(EFI_FFS_FILE_HEADER)) { + size += fileAlignment; + } + // Adjust file base to incorporate pad file that will be added to align it + sectionBase += size; + } + + // Reconstruct section + UByteArray section; + result = buildSection(index.child(i, 0), sectionBase, section); + if (result) + return result; + + // Check for empty section + if (section.isEmpty()) + continue; + + // Append current section to new file body + reconstructed.append(section); + + // Change current file offset + offset += section.size(); + } + } + } + // Use current file body + else + reconstructed = model->body(index); + + // Correct file size + UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0; + if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { + uint32ToUint24(EFI_SECTION2_IS_USED, fileHeader->Size); + EFI_FFS_FILE_HEADER2* fileHeader2 = (EFI_FFS_FILE_HEADER2*)fileHeader; + fileHeader2->ExtendedSize = sizeof(EFI_FFS_FILE_HEADER2) + reconstructed.size() + tailSize; + } + else { + if (sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize > 0xFFFFFF) { + msg("buildFile: resulting file size is too big", index); + return U_INVALID_FILE; + } + uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize, fileHeader->Size); + } + + // Recalculate header checksum + fileHeader->IntegrityCheck.Checksum.Header = 0; + fileHeader->IntegrityCheck.Checksum.File = 0; + fileHeader->IntegrityCheck.Checksum.Header = 0x100 - (calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->State); + + // Recalculate data checksum, if needed + if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { + fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((const UINT8*)reconstructed.constData(), reconstructed.size()); + } + else if (revision == 1) + fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; + else + fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; + + // Append tail, if needed + if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { + UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; + UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; + reconstructed.append(ht).append(ft); + } + + // Set file state + state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION; + if (erasePolarity == ERASE_POLARITY_TRUE) + state = ~state; + fileHeader->State = state; + + // Reconstruction successful + reconstructed = header.append(reconstructed); + return U_SUCCESS; + } + + // All other actions are not supported return U_NOT_IMPLEMENTED; } +USTATUS FfsBuilder::buildSection(const UModelIndex & index, const UINT32 base, UByteArray & reconstructed) +{ + if (!index.isValid()) + return U_SUCCESS; + + USTATUS result; + + // No action + if (model->action(index) == Actions::NoAction) { + reconstructed = model->header(index).append(model->body(index)); + return U_SUCCESS; + } + else if (model->action(index) == Actions::Remove) { + reconstructed.clear(); + return U_SUCCESS; + } + else if (model->action(index) == Actions::Insert || + model->action(index) == Actions::Replace || + model->action(index) == Actions::Rebuild || + model->action(index) == Actions::Rebase) { + UByteArray header = model->header(index); + EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)header.data(); + bool extended = false; + UByteArray data = model->parsingData(index); + const COMPRESSED_SECTION_PARSING_DATA* compress_data = (const COMPRESSED_SECTION_PARSING_DATA*)data.constData(); + + if (uint24ToUint32(commonHeader->Size) == 0xFFFFFF) { + extended = true; + } + + // Reconstruct section with children + if (model->rowCount(index)) { + reconstructed.clear(); + // Construct new section body + UINT32 offset = 0; + + // Reconstruct section body + for (int i = 0; i < model->rowCount(index); i++) { + // Align to 4 byte boundary + UINT8 alignment = offset % 4; + if (alignment) { + alignment = 4 - alignment; + offset += alignment; + reconstructed.append(UByteArray(alignment, '\x00')); + } + + // Reconstruct subsections + UByteArray section; + result = build(index.child(i, 0), section); + if (result) + return result; + + // Check for empty queue + if (section.isEmpty()) + continue; + + // Append current subsection to new section body + reconstructed.append(section); + + // Change current file offset + offset += section.size(); + } + + // Only this 2 sections can have compressed body + if (model->subtype(index) == EFI_SECTION_COMPRESSION) { + EFI_COMPRESSION_SECTION* compessionHeader = (EFI_COMPRESSION_SECTION*)header.data(); + // Set new uncompressed size + compessionHeader->UncompressedLength = reconstructed.size(); + // Compress new section body + UByteArray compressed; + result = compress(reconstructed, compress_data->algorithm, compressed); + if (result) + return result; + // Correct compression type + compessionHeader->CompressionType = compress_data->compressionType; + + // Replace new section body + reconstructed = compressed; + } + else if (model->subtype(index) == EFI_SECTION_GUID_DEFINED) { + EFI_GUID_DEFINED_SECTION* guidDefinedHeader = (EFI_GUID_DEFINED_SECTION*)header.data(); + // Compress new section body + UByteArray compressed; + result = compress(reconstructed, compress_data->algorithm, compressed); + if (result) + return result; + // Check for authentication status valid attribute + if (guidDefinedHeader->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) { + // CRC32 section + if (UByteArray((const char*)&guidDefinedHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_CRC32) { + // Check header size + if ((UINT32)header.size() != sizeof(EFI_GUID_DEFINED_SECTION) + sizeof(UINT32)) { + msg("buildSection: invalid CRC32 section size", index); + return U_INVALID_SECTION; + } + // Calculate CRC32 of section data + UINT32 crc = crc32(0, (const UINT8*)compressed.constData(), compressed.size()); + // Store new CRC32 + *(UINT32*)(header.data() + sizeof(EFI_GUID_DEFINED_SECTION)) = crc; + } + else { + msg(usprintf("buildSection: GUID defined section authentication info can become invalid") + .arg(guidToUString(guidDefinedHeader->SectionDefinitionGuid)), index); + } + } + // Check for Intel signed section + if (guidDefinedHeader->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED + && UByteArray((const char*)&guidDefinedHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { + msg(usprintf("buildSection: GUID defined section signature can become invalid") + .arg(guidToUString(guidDefinedHeader->SectionDefinitionGuid)), index); + } + // Replace new section body + reconstructed = compressed; + } + else if (compress_data->algorithm != COMPRESSION_ALGORITHM_NONE) { + msg(usprintf("buildSection: incorrectly required compression for section of type %1") + .arg(model->subtype(index)), index); + return U_INVALID_SECTION; + } + } + // Leaf section + else { + reconstructed = model->body(index); + } + + // Correct section size + if (extended) { + EFI_COMMON_SECTION_HEADER2 * extHeader = (EFI_COMMON_SECTION_HEADER2*)commonHeader; + extHeader->ExtendedSize = header.size() + reconstructed.size(); + uint32ToUint24(0xFFFFFF, commonHeader->Size); + } + else { + uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size); + } + + // Rebase PE32 or TE image, if needed + 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; + + // Most EFI images today include teFixup in ImageBase value, + // which doesn't follow the UEFI spec, but is so popular that + // only a few images out of thousands are different + + // There are some heuristics possible here to detect if an entry point is calculated correctly + // or needs a proper fixup, but new_engine already have them and it's better to work on proper + // builder for it than trying to fix this mess + + //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); + // + + if (base) { + result = rebase(reconstructed, base - teFixup + header.size()); + if (result) { + msg("buildSection: executable section rebase failed", index); + return result; + } + + // Special case of PEI Core rebase + if (model->subtype(index.parent()) == EFI_FV_FILETYPE_PEI_CORE) { + result = getEntryPoint(reconstructed, parser->newPeiCoreEntryPoint); + if (result) + msg("buildSection: can't get entry point of PEI core", index); + } + } + } + + // Reconstruction successful + reconstructed = header.append(reconstructed); + + return U_SUCCESS; + } + + // All other actions are not supported + return U_NOT_IMPLEMENTED; +} + + + diff --git a/common/ffsbuilder.h b/common/ffsbuilder.h index 03dbc62..62811b3 100644 --- a/common/ffsbuilder.h +++ b/common/ffsbuilder.h @@ -20,11 +20,14 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "ubytearray.h" #include "ustring.h" #include "treemodel.h" +#include "ffsparser.h" class FfsBuilder { public: FfsBuilder(const TreeModel * treeModel) : model(treeModel) {} + FfsBuilder(const TreeModel * treeModel, FfsParser * ffsParser) : model(treeModel), parser(ffsParser) {} + ~FfsBuilder() {} std::vector > getMessages() const { return messagesVector; } @@ -33,7 +36,9 @@ public: USTATUS build(const UModelIndex & root, UByteArray & image); private: - const TreeModel* model; + const TreeModel * model; + FfsParser * parser; + std::vector > messagesVector; void msg(const UString & message, const UModelIndex &index = UModelIndex()) { messagesVector.push_back(std::pair(message, index)); @@ -41,17 +46,23 @@ private: USTATUS buildCapsule(const UModelIndex & index, UByteArray & capsule); USTATUS buildIntelImage(const UModelIndex & index, UByteArray & intelImage); - USTATUS buildRawArea(const UModelIndex & index, UByteArray & rawArea); + USTATUS buildRawArea(const UModelIndex & index, UByteArray & rawArea, bool includeHeader = true); USTATUS buildPadding(const UModelIndex & index, UByteArray & padding); USTATUS buildVolume(const UModelIndex & index, UByteArray & volume); + USTATUS buildNvramVolume(const UModelIndex & index, UByteArray & volume); + USTATUS buildNvramStore(const UModelIndex & index, UByteArray & store); + USTATUS buildNvarStore(const UModelIndex & index, UByteArray & store); USTATUS buildNonUefiData(const UModelIndex & index, UByteArray & data); USTATUS buildFreeSpace(const UModelIndex & index, UByteArray & freeSpace); - USTATUS buildPadFile(const UModelIndex & index, UByteArray & padFile); - USTATUS buildFile(const UModelIndex & index, UByteArray & file); - USTATUS buildSection(const UModelIndex & index, UByteArray & section); + USTATUS buildPadFile(const UByteArray &guid, const UINT32 size, const UINT8 revision, const UINT8 erasePolarity, UByteArray & pad); + USTATUS buildFile(const UModelIndex & index, const UINT8 revision, const UINT8 erasePolarity, const UINT32 base, UByteArray & reconstructed); + USTATUS buildSection(const UModelIndex & index, const UINT32 base, UByteArray & reconstructed); + USTATUS buildRegion(const UModelIndex& index, UByteArray & reconstructed, bool includeHeader = true); // Utility functions USTATUS erase(const UModelIndex & index, UByteArray & erased); + + }; #endif // FFSBUILDER_H diff --git a/common/ffsops.cpp b/common/ffsops.cpp index 4ef2b8b..2d094f7 100644 --- a/common/ffsops.cpp +++ b/common/ffsops.cpp @@ -14,6 +14,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "ffsops.h" #include "ffs.h" #include "utility.h" +#include "nvramparser.h" USTATUS FfsOperations::extract(const UModelIndex & index, UString & name, UByteArray & extracted, const UINT8 mode) { @@ -59,22 +60,114 @@ USTATUS FfsOperations::extract(const UModelIndex & index, UString & name, UByteA return U_SUCCESS; } -USTATUS FfsOperations::replace(const UModelIndex & index, const UString & data, const UINT8 mode) +USTATUS FfsOperations::replace(const UModelIndex & index, UByteArray & data, const UINT8 mode) { - U_UNUSED_PARAMETER(data); - - // Sanity check - if (!index.isValid()) + if (!index.isValid() || !index.parent().isValid()) return U_INVALID_PARAMETER; - if (mode == REPLACE_MODE_AS_IS) { - return U_NOT_IMPLEMENTED; + USTATUS result; + UByteArray empty = ""; + FfsParser parser(model); + UINT32 localOffset = model->offset(index) + model->header(index).size(); + UModelIndex index_out; + + if(mode == REPLACE_MODE_BODY) + data = model->header(index) + data; + + if (model->type(index) == Types::Region) { + UINT8 type = model->subtype(index); + switch (type) { + case Subtypes::BiosRegion: + result = parser.parseBiosRegion(data, localOffset, index, index_out, CREATE_MODE_AFTER); + break; + case Subtypes::MeRegion: + result = parser.parseMeRegion(data, localOffset, index, index_out, CREATE_MODE_AFTER); + break; + case Subtypes::GbeRegion: + result = parser.parseGbeRegion(data, localOffset, index, index_out, CREATE_MODE_AFTER); + break; + case Subtypes::PdrRegion: + result = parser.parsePdrRegion(data, localOffset, index, index_out, CREATE_MODE_AFTER); + break; + default: + return U_NOT_IMPLEMENTED; + } + + if (result && result != U_VOLUMES_NOT_FOUND && result != U_INVALID_VOLUME) + return result; } - else if (mode == REPLACE_MODE_BODY) { - return U_NOT_IMPLEMENTED; + else if (model->type(index) == Types::Padding) { + // Get info + QString name = usprintf("Padding"); + QString info = usprintf("Full size: %Xh (%u)", data.size(), data.size()); + // Add tree item + //!TODO UModelIndex fileIndex = model->addItem(Types::Padding, getPaddingType(body), COMPRESSION_ALGORITHM_NONE, name, "", info, UByteArray(), body, index, mode); } - - return U_UNKNOWN_REPLACE_MODE; + else if (model->type(index) == Types::Volume) { + result = parser.parseVolumeHeader(data, localOffset, index, index_out, CREATE_MODE_AFTER); + if (result) + return result; + + result = parser.parseVolumeBody(index_out); + if (result) + return result; + + } + else if (model->type(index) == Types::File) { + result = parser.parseFileHeader(data, localOffset, index, index_out, CREATE_MODE_AFTER); + if (result && result != U_VOLUMES_NOT_FOUND && result != U_INVALID_VOLUME) + return result; + + result = parser.parseFileBody(index_out); + if (result && result != U_VOLUMES_NOT_FOUND && result != U_INVALID_VOLUME) + return result; + } + else if (model->type(index) == Types::Section) { + result = parser.parseSectionHeader(data, localOffset, index, index_out, true, CREATE_MODE_AFTER); + if (result && result != U_VOLUMES_NOT_FOUND && result != U_INVALID_VOLUME) + return result; + + result = parser.parseSectionBody(index_out); + if(result && result != U_VOLUMES_NOT_FOUND && result != U_INVALID_VOLUME) + return result; + } + else if(model->type(index) == Types::EvsaStore || model->type(index) == Types::CmdbStore || + model->type(index) == Types::FdcStore || model->type(index) == Types::FlashMapStore || + model->type(index) == Types::FsysStore || model->type(index) == Types::FtwStore || + model->type(index) == Types::Vss2Store || model->type(index) == Types::VssStore) { + if(data.size() != model->header(index).size() + model->body(index).size()) + return U_INVALID_STORE_SIZE; + + NvramParser nvramParser(model, &parser); + + result = nvramParser.parseStoreHeader(data, localOffset, index, index_out, CREATE_MODE_AFTER); + if (result && result != U_VOLUMES_NOT_FOUND && result != U_INVALID_VOLUME) + return result; + + UINT8 type = model->type(index); + switch (type) { + case Types::FdcStore: nvramParser.parseFdcStoreBody(index_out); break; + case Types::VssStore: nvramParser.parseVssStoreBody(index_out, 0); break; + case Types::Vss2Store: nvramParser.parseVssStoreBody(index_out, 4); break; + case Types::FsysStore: nvramParser.parseFsysStoreBody(index_out); break; + case Types::EvsaStore: nvramParser.parseEvsaStoreBody(index_out); break; + case Types::FlashMapStore: nvramParser.parseFlashMapBody(index_out); break; + } + } + else + return U_NOT_IMPLEMENTED; + + // Set remove action to replaced item + model->setAction(index, Actions::Remove); + model->setAction(index_out, Actions::Replace); + + // Rebuild parent, if it has no action now + UModelIndex parent = index.parent(); + if (parent.isValid() && model->type(parent) != Types::Root + && model->action(parent) == Actions::NoAction) + rebuild(parent); + + return U_SUCCESS; } USTATUS FfsOperations::remove(const UModelIndex & index) diff --git a/common/ffsops.h b/common/ffsops.h index 4208cef..ee5ae0b 100644 --- a/common/ffsops.h +++ b/common/ffsops.h @@ -20,6 +20,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "ubytearray.h" #include "ustring.h" #include "treemodel.h" +#include "ffsparser.h" class FfsOperations { @@ -32,13 +33,14 @@ public: void clearMessages() { messagesVector.clear(); } USTATUS extract(const UModelIndex & index, UString & name, UByteArray & extracted, const UINT8 mode); - USTATUS replace(const UModelIndex & index, const UString & data, const UINT8 mode); + USTATUS replace(const UModelIndex & index, UByteArray & data, const UINT8 mode); USTATUS remove(const UModelIndex & index); USTATUS rebuild(const UModelIndex & index); private: - TreeModel* model; + TreeModel * model; + std::vector > messagesVector; void msg(const UString & message, const UModelIndex &index = UModelIndex()) { diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 157f485..72eab21 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -55,7 +55,7 @@ struct REGION_INFO { // Constructor FfsParser::FfsParser(TreeModel* treeModel) : model(treeModel), -imageBase(0), addressDiff(0x100000000ULL), +imageBase(0), addressDiff(0x100000000ULL), peiCoreEntryPoint(0), newPeiCoreEntryPoint(0), bgAcmFound(false), bgKeyManifestFound(false), bgBootPolicyFound(false), bgFirstVolumeOffset(0x100000000ULL) { nvramParser = new NvramParser(treeModel, this); meParser = new MeParser(treeModel); @@ -101,6 +101,9 @@ USTATUS FfsParser::parse(const UByteArray & buffer) USTATUS FfsParser::performFirstPass(const UByteArray & buffer, UModelIndex & index) { + peiCoreEntryPoint = 0; + newPeiCoreEntryPoint = 0; + // Sanity check if (buffer.isEmpty()) { return EFI_INVALID_PARAMETER; @@ -630,7 +633,7 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l return parseResult; } -USTATUS FfsParser::parseGbeRegion(const UByteArray & gbe, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS FfsParser::parseGbeRegion(const UByteArray & gbe, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { // Check sanity if (gbe.isEmpty()) @@ -650,12 +653,12 @@ USTATUS FfsParser::parseGbeRegion(const UByteArray & gbe, const UINT32 localOffs version->minor); // Add tree item - index = model->addItem(model->offset(parent) + localOffset, Types::Region, Subtypes::GbeRegion, name, UString(), info, UByteArray(), gbe, UByteArray(), Fixed, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Region, Subtypes::GbeRegion, name, UString(), info, UByteArray(), gbe, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS FfsParser::parseMeRegion(const UByteArray & me, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS FfsParser::parseMeRegion(const UByteArray & me, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { // Check sanity if (me.isEmpty()) @@ -702,7 +705,7 @@ USTATUS FfsParser::parseMeRegion(const UByteArray & me, const UINT32 localOffset } // Add tree item - index = model->addItem(model->offset(parent) + localOffset, Types::Region, Subtypes::MeRegion, name, UString(), info, UByteArray(), me, UByteArray(), Fixed, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Region, Subtypes::MeRegion, name, UString(), info, UByteArray(), me, UByteArray(), Fixed, parent, mode); // Show messages if (emptyRegion) { @@ -718,7 +721,7 @@ USTATUS FfsParser::parseMeRegion(const UByteArray & me, const UINT32 localOffset return U_SUCCESS; } -USTATUS FfsParser::parsePdrRegion(const UByteArray & pdr, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS FfsParser::parsePdrRegion(const UByteArray & pdr, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { // Check sanity if (pdr.isEmpty()) @@ -729,7 +732,7 @@ USTATUS FfsParser::parsePdrRegion(const UByteArray & pdr, const UINT32 localOffs UString info = usprintf("Full size: %Xh (%u)", pdr.size(), pdr.size()); // Add tree item - index = model->addItem(model->offset(parent) + localOffset, Types::Region, Subtypes::PdrRegion, name, UString(), info, UByteArray(), pdr, UByteArray(), Fixed, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Region, Subtypes::PdrRegion, name, UString(), info, UByteArray(), pdr, UByteArray(), Fixed, parent, mode); // Parse PDR region as BIOS space USTATUS result = parseRawArea(index); @@ -739,7 +742,7 @@ USTATUS FfsParser::parsePdrRegion(const UByteArray & pdr, const UINT32 localOffs return U_SUCCESS; } -USTATUS FfsParser::parseGenericRegion(const UINT8 subtype, const UByteArray & region, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS FfsParser::parseGenericRegion(const UINT8 subtype, const UByteArray & region, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { // Check sanity if (region.isEmpty()) @@ -750,12 +753,12 @@ USTATUS FfsParser::parseGenericRegion(const UINT8 subtype, const UByteArray & re UString info = usprintf("Full size: %Xh (%u)", region.size(), region.size()); // Add tree item - index = model->addItem(model->offset(parent) + localOffset, Types::Region, subtype, name, UString(), info, UByteArray(), region, UByteArray(), Fixed, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Region, subtype, name, UString(), info, UByteArray(), region, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS FfsParser::parseBiosRegion(const UByteArray & bios, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS FfsParser::parseBiosRegion(const UByteArray & bios, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { // Sanity check if (bios.isEmpty()) @@ -766,7 +769,7 @@ USTATUS FfsParser::parseBiosRegion(const UByteArray & bios, const UINT32 localOf UString info = usprintf("Full size: %Xh (%u)", bios.size(), bios.size()); // Add tree item - index = model->addItem(model->offset(parent) + localOffset, Types::Region, Subtypes::BiosRegion, name, UString(), info, UByteArray(), bios, UByteArray(), Fixed, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Region, Subtypes::BiosRegion, name, UString(), info, UByteArray(), bios, UByteArray(), Fixed, parent, mode); return parseRawArea(index); } @@ -907,7 +910,7 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) return U_SUCCESS; } -USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { // Sanity check if (volume.isEmpty()) @@ -1069,7 +1072,7 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc else if (isNvramVolume) subtype = Subtypes::NvramVolume; } - index = model->addItem(model->offset(parent) + localOffset, Types::Volume, subtype, name, text, info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Volume, subtype, name, text, info, header, body, UByteArray(), Fixed, parent, mode); // Set parsing data for created volume VOLUME_PARSING_DATA pdata; @@ -1376,7 +1379,7 @@ UINT32 FfsParser::getFileSize(const UByteArray & volume, const UINT32 fileOffset return 0; } -USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { // Sanity check if (file.isEmpty()) { @@ -1496,6 +1499,7 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf usprintf("\nHeader checksum: %02Xh", fileHeader->IntegrityCheck.Checksum.Header) + (msgInvalidHeaderChecksum ? usprintf(", invalid, should be %02Xh", calculatedHeader) : UString(", valid")) + usprintf("\nData checksum: %02Xh", fileHeader->IntegrityCheck.Checksum.File) + (msgInvalidDataChecksum ? usprintf(", invalid, should be %02Xh", calculatedData) : UString(", valid")); + UString text; bool isVtf = false; bool isDxeCore = false; @@ -1514,12 +1518,62 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf // This information may be used to determine DXE volume offset for old AMI or post-IBB protected ranges isDxeCore = true; } + else if (fileGuid == EFI_TXT_ACM_GUID) { + // Detect TXT ACM and update TXT tab + const INTEL_ACM_HEADER* acmHeader = (const INTEL_ACM_HEADER*)body.constData(); + + // Add ACM header info + txtInfo += usprintf( + "TXT ACM found at offset %Xh\n" + "ModuleType: %04Xh ModuleSubtype: %04Xh HeaderLength: %08Xh\n" + "HeaderVersion: %08Xh ChipsetId: %04Xh Flags: %04Xh\n" + "ModuleVendor: %04Xh Date: %02X.%02X.%04X ModuleSize: %08Xh\n" + "EntryPoint: %08Xh AcmSvn: %04Xh Unknown1: %08Xh\n" + "Unknown2: %08Xh GdtBase: %08Xh GdtMax: %08Xh\n" + "SegSel: %08Xh KeySize: %08Xh Unknown3: %08Xh", + model->offset(parent) + localOffset, + acmHeader->ModuleType, + acmHeader->ModuleSubtype, + acmHeader->ModuleSize * sizeof(UINT32), + acmHeader->HeaderVersion, + acmHeader->ChipsetId, + acmHeader->Flags, + acmHeader->ModuleVendor, + acmHeader->DateDay, acmHeader->DateMonth, acmHeader->DateYear, + acmHeader->ModuleSize * sizeof(UINT32), + acmHeader->EntryPoint, + acmHeader->AcmSvn, + acmHeader->Unknown1, + acmHeader->Unknown2, + acmHeader->GdtBase, + acmHeader->GdtMax, + acmHeader->SegmentSel, + acmHeader->KeySize * sizeof(UINT32), + acmHeader->Unknown4 * sizeof(UINT32) + ); + // Add PubKey + txtInfo += usprintf("\n\nACM RSA Public Key (Exponent: %Xh):", acmHeader->RsaPubExp); + for (UINT16 i = 0; i < sizeof(acmHeader->RsaPubKey); i++) { + if (i % 32 == 0) + txtInfo += UString("\n"); + txtInfo += usprintf("%02X", acmHeader->RsaPubKey[i]); + } + // Add RsaSig + txtInfo += UString("\n\nACM RSA Signature:"); + for (UINT16 i = 0; i < sizeof(acmHeader->RsaSig); i++) { + if (i % 32 == 0) + txtInfo += UString("\n"); + txtInfo += usprintf("%02X", acmHeader->RsaSig[i]); + } + txtInfo += UString("\n------------------------------------------------------------------------\n\n"); + + } // Construct fixed state ItemFixedState fixed = (ItemFixedState)((fileHeader->Attributes & FFS_ATTRIB_FIXED) != 0); // Add tree item - index = model->addItem(model->offset(parent) + localOffset, Types::File, fileHeader->Type, name, text, info, header, body, tail, fixed, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::File, fileHeader->Type, name, text, info, header, body, tail, fixed, parent, mode); // Set parsing data for created file FILE_PARSING_DATA pdata; @@ -1763,7 +1817,7 @@ USTATUS FfsParser::parseSections(const UByteArray & sections, const UModelIndex return U_SUCCESS; } -USTATUS FfsParser::parseSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree) +USTATUS FfsParser::parseSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode) { // Check sanity if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER)) { @@ -1773,12 +1827,12 @@ USTATUS FfsParser::parseSectionHeader(const UByteArray & section, const UINT32 l const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); switch (sectionHeader->Type) { // Special - case EFI_SECTION_COMPRESSION: return parseCompressedSectionHeader(section, localOffset, parent, index, insertIntoTree); - case EFI_SECTION_GUID_DEFINED: return parseGuidedSectionHeader(section, localOffset, parent, index, insertIntoTree); - case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return parseFreeformGuidedSectionHeader(section, localOffset, parent, index, insertIntoTree); - case EFI_SECTION_VERSION: return parseVersionSectionHeader(section, localOffset, parent, index, insertIntoTree); + case EFI_SECTION_COMPRESSION: return parseCompressedSectionHeader(section, localOffset, parent, index, insertIntoTree, mode); + case EFI_SECTION_GUID_DEFINED: return parseGuidedSectionHeader(section, localOffset, parent, index, insertIntoTree, mode); + case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return parseFreeformGuidedSectionHeader(section, localOffset, parent, index, insertIntoTree, mode); + case EFI_SECTION_VERSION: return parseVersionSectionHeader(section, localOffset, parent, index, insertIntoTree, mode); case PHOENIX_SECTION_POSTCODE: - case INSYDE_SECTION_POSTCODE: return parsePostcodeSectionHeader(section, localOffset, parent, index, insertIntoTree); + case INSYDE_SECTION_POSTCODE: return parsePostcodeSectionHeader(section, localOffset, parent, index, insertIntoTree, mode); // Common case EFI_SECTION_DISPOSABLE: case EFI_SECTION_DXE_DEPEX: @@ -1790,16 +1844,16 @@ USTATUS FfsParser::parseSectionHeader(const UByteArray & section, const UINT32 l case EFI_SECTION_COMPATIBILITY16: case EFI_SECTION_USER_INTERFACE: case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: - case EFI_SECTION_RAW: return parseCommonSectionHeader(section, localOffset, parent, index, insertIntoTree); + case EFI_SECTION_RAW: return parseCommonSectionHeader(section, localOffset, parent, index, insertIntoTree, mode); // Unknown default: - USTATUS result = parseCommonSectionHeader(section, localOffset, parent, index, insertIntoTree); + USTATUS result = parseCommonSectionHeader(section, localOffset, parent, index, insertIntoTree, mode); msg(usprintf("%s: section with unknown type %02Xh", __FUNCTION__, sectionHeader->Type), index); return result; } } -USTATUS FfsParser::parseCommonSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree) +USTATUS FfsParser::parseCommonSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode) { // Check sanity if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER)) { @@ -1847,15 +1901,23 @@ USTATUS FfsParser::parseCommonSectionHeader(const UByteArray & section, const UI headerSize, headerSize, body.size(), body.size()); + // Special case of PEI Core + if (model->subtype(parent) == EFI_FV_FILETYPE_PEI_CORE + && peiCoreEntryPoint == 0) { + USTATUS result = getEntryPoint(body, peiCoreEntryPoint); + if (result) + msg("parseCommonSectionHeader: can't get original PEI core entry point", index); + } + // Add tree item if (insertIntoTree) { - index = model->addItem(model->offset(parent) + localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent, mode); } return U_SUCCESS; } -USTATUS FfsParser::parseCompressedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree) +USTATUS FfsParser::parseCompressedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode) { // Check sanity if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER)) @@ -1919,7 +1981,7 @@ USTATUS FfsParser::parseCompressedSectionHeader(const UByteArray & section, cons // Add tree item if (insertIntoTree) { - index = model->addItem(model->offset(parent) + localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent, mode); // Set section parsing data COMPRESSED_SECTION_PARSING_DATA pdata; @@ -1931,7 +1993,7 @@ USTATUS FfsParser::parseCompressedSectionHeader(const UByteArray & section, cons return U_SUCCESS; } -USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree) +USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode) { // Check sanity if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER)) @@ -2097,7 +2159,7 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI // Add tree item if (insertIntoTree) { - index = model->addItem(model->offset(parent) + localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent, mode); // Set parsing data GUIDED_SECTION_PARSING_DATA pdata; @@ -2124,7 +2186,7 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI return U_SUCCESS; } -USTATUS FfsParser::parseFreeformGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree) +USTATUS FfsParser::parseFreeformGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode) { // Check sanity if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER)) @@ -2186,7 +2248,7 @@ USTATUS FfsParser::parseFreeformGuidedSectionHeader(const UByteArray & section, // Add tree item if (insertIntoTree) { - index = model->addItem(model->offset(parent) + localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent, mode); // Set parsing data FREEFORM_GUIDED_SECTION_PARSING_DATA pdata; @@ -2200,7 +2262,7 @@ USTATUS FfsParser::parseFreeformGuidedSectionHeader(const UByteArray & section, return U_SUCCESS; } -USTATUS FfsParser::parseVersionSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree) +USTATUS FfsParser::parseVersionSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode) { // Check sanity if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER)) @@ -2260,13 +2322,13 @@ USTATUS FfsParser::parseVersionSectionHeader(const UByteArray & section, const U // Add tree item if (insertIntoTree) { - index = model->addItem(model->offset(parent) + localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent, mode); } return U_SUCCESS; } -USTATUS FfsParser::parsePostcodeSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree) +USTATUS FfsParser::parsePostcodeSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode) { // Check sanity if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER)) @@ -2326,7 +2388,7 @@ USTATUS FfsParser::parsePostcodeSectionHeader(const UByteArray & section, const // Add tree item if (insertIntoTree) { - index = model->addItem(model->offset(parent) + localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem(model->offset(parent) + localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent, mode); } return U_SUCCESS; @@ -3407,6 +3469,9 @@ USTATUS FfsParser::parseFit(const UModelIndex & index) return U_INVALID_FIT; } + if (currentEntry->Type == FIT_TYPE_TXT_CONF_POLICY) + parseTxtConfigurationPolicy(currentEntry, info); + // Set item index if (currentEntry->Address > addressDiff && currentEntry->Address < 0xFFFFFFFFUL) { // Only elements in the image need to be parsed currentEntryOffset = (UINT32)(currentEntry->Address - addressDiff); @@ -3554,6 +3619,64 @@ USTATUS FfsParser::parseIntelMicrocode(const UByteArray & microcode, const UINT3 header->DateYear ); realSize = mcSize; + + // Add Microcode header info + microcodeInfo += usprintf( + "Microcode Update Capsule found at offset %Xh\n" + "Checksum: %08Xh CPU Flags: %08Xh CPU Signature: %08Xh\n" + "Data Size: %08Xh Date: %02X.%02X.%04X Loader Revision: %08Xh\n" + "Date: %02X.%02X.%04X ModuleSize: %08Xh EntryPoint: %08Xh\n" + "Reserved: %02Xh Revision: %08Xh TotalSize: %08Xh\n" + "Version: %08Xh" + "\n------------------------------------------------------------------------\n\n", + model->offset(parent) + localOffset, + header->Checksum, + header->CpuFlags, + header->CpuSignature, + header->DataSize, + header->DateDay, header->DateMonth, header->DateYear, + header->LoaderRevision, + header->Reserved, + header->Revision, + header->TotalSize, + header->Version + ); + + return U_SUCCESS; +} + +USTATUS FfsParser::parseTxtConfigurationPolicy(const FIT_ENTRY* entry, UString & info) +{ + U_UNUSED_PARAMETER(info); + const TXT_CONFIG_POLICY* txtCfg = (const TXT_CONFIG_POLICY*)entry; + + if(entry->Version == 0) { + txtInfo += usprintf( + "TXT Configuration Policy found (Version = 0)\n" + "Index: %04Xh Bit Position: %02Xh\n" + "Access width in bytes: %02Xh\n" + "Data Register Address: %04Xh\n" + "Index Register Address: %04Xh", + txtCfg->IndexedIOPtrInfo.Index, + txtCfg->IndexedIOPtrInfo.BitPosition, + txtCfg->IndexedIOPtrInfo.AccessWidth, + txtCfg->IndexedIOPtrInfo.DataRegisterAddress, + txtCfg->IndexedIOPtrInfo.IndexRegisterAddress + ); + } + else if(entry->Version == 1) { + txtInfo += usprintf( + "TXT Configuration Policy found (Version = 1)\n" + "Physical Address: %16Xh ", + txtCfg->FlatMemoryPtrInfo.FlatMemoryPhysicalAddress + ); + txtInfo += txtCfg->FlatMemoryPtrInfo.TxtEnabled ? + "(Bit 0 is zero - Intel TXT disabled)" : "(Bit 0 is 1 - Intel TXT enabled)"; + } + else + txtInfo += "\nUnknown version of TXT Configuration Policy"; + + txtInfo += "\n------------------------------------------------------------------------\n\n"; return U_SUCCESS; } @@ -3585,16 +3708,18 @@ USTATUS FfsParser::parseIntelAcm(const UByteArray & acm, const UINT32 localOffse realSize = acmSize; // Add ACM header info - bootGuardInfo += usprintf( - "Intel ACM found at offset %Xh\n" - "ModuleType: %08Xh HeaderLength: %08Xh HeaderVersion: %08Xh\n" - "ChipsetId: %04Xh Flags: %04Xh ModuleVendor: %04Xh\n" - "Date: %02X.%02X.%04X ModuleSize: %08Xh EntryPoint: %08Xh\n" - "AcmSvn: %04Xh Unknown1: %08Xh Unknown2: %08Xh\n" - "GdtBase: %08Xh GdtMax: %08Xh SegSel: %08Xh\n" - "KeySize: %08Xh Unknown3: %08Xh", + UString acmInfo; + acmInfo += usprintf( + " found at offset %Xh\n" + "ModuleType: %04Xh ModuleSubtype: %04Xh HeaderLength: %08Xh\n" + "HeaderVersion: %08Xh ChipsetId: %04Xh Flags: %04Xh\n" + "ModuleVendor: %04Xh Date: %02X.%02X.%04X ModuleSize: %08Xh\n" + "EntryPoint: %08Xh AcmSvn: %04Xh Unknown1: %08Xh\n" + "Unknown2: %08Xh GdtBase: %08Xh GdtMax: %08Xh\n" + "SegSel: %08Xh KeySize: %08Xh Unknown3: %08Xh", model->offset(parent) + localOffset, header->ModuleType, + header->ModuleSubtype, header->ModuleSize * sizeof(UINT32), header->HeaderVersion, header->ChipsetId, @@ -3613,20 +3738,28 @@ USTATUS FfsParser::parseIntelAcm(const UByteArray & acm, const UINT32 localOffse header->Unknown4 * sizeof(UINT32) ); // Add PubKey - bootGuardInfo += usprintf("\n\nACM RSA Public Key (Exponent: %Xh):", header->RsaPubExp); + acmInfo += usprintf("\n\nACM RSA Public Key (Exponent: %Xh):", header->RsaPubExp); for (UINT16 i = 0; i < sizeof(header->RsaPubKey); i++) { if (i % 32 == 0) - bootGuardInfo += UString("\n"); - bootGuardInfo += usprintf("%02X", header->RsaPubKey[i]); + acmInfo += UString("\n"); + acmInfo += usprintf("%02X", header->RsaPubKey[i]); } // Add RsaSig - bootGuardInfo += UString("\n\nACM RSA Signature:"); + acmInfo += UString("\n\nACM RSA Signature:"); for (UINT16 i = 0; i < sizeof(header->RsaSig); i++) { if (i % 32 == 0) - bootGuardInfo += UString("\n"); - bootGuardInfo += usprintf("%02X", header->RsaSig[i]); + acmInfo += UString("\n"); + acmInfo += usprintf("%02X", header->RsaSig[i]); } - bootGuardInfo += UString("\n------------------------------------------------------------------------\n\n"); + acmInfo += UString("\n------------------------------------------------------------------------\n\n"); + + if(header->ModuleSubtype == INTEL_ACM_MODULE_SUBTYPE_TXT_ACM) + txtInfo += "TXT ACM (From FIT)" + acmInfo; + else if(header->ModuleSubtype == INTEL_ACM_MODULE_SUBTYPE_S_ACM) + txtInfo += "S-ACM (From FIT)" + acmInfo; + else + bootGuardInfo += "Intel ACM" + acmInfo; + bgAcmFound = true; return U_SUCCESS; } diff --git a/common/ffsparser.h b/common/ffsparser.h index 4274212..8956caf 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -20,6 +20,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "ubytearray.h" #include "treemodel.h" #include "bootguard.h" +#include "fit.h" typedef struct BG_PROTECTED_RANGE_ { @@ -60,9 +61,17 @@ public: // Obtain BootGuardInfo UString getBootGuardInfo() const { return bootGuardInfo; } + // Obtain TXT ACM Info + UString getTxtInfo() const { return txtInfo; } + + // Obtain Microcode Info + UString getMicrocodeInfo() const { return microcodeInfo; } + // Obtain offset/address difference UINT64 getAddressDiff() { return addressDiff; } + void setNewPeiCoreEntryPoint(UINT32 entry) { newPeiCoreEntryPoint = entry; } + private: TreeModel *model; std::vector > messagesVector; @@ -77,6 +86,8 @@ private: UModelIndex lastVtf; UINT32 imageBase; UINT64 addressDiff; + UINT32 peiCoreEntryPoint; + UINT32 newPeiCoreEntryPoint; std::vector, UModelIndex> > fitTable; UString bootGuardInfo; @@ -90,6 +101,9 @@ private: UINT64 bgFirstVolumeOffset; UModelIndex bgDxeCoreIndex; + UString txtInfo; + UString microcodeInfo; + // First pass USTATUS performFirstPass(const UByteArray & imageFile, UModelIndex & index); @@ -98,29 +112,29 @@ private: USTATUS parseGenericImage(const UByteArray & intelImage, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); USTATUS parseRawArea(const UModelIndex & index); - USTATUS parseVolumeHeader(const UByteArray & volume, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseVolumeHeader(const UByteArray & volume, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); USTATUS parseVolumeBody(const UModelIndex & index); - USTATUS parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); USTATUS parseFileBody(const UModelIndex & index); - USTATUS parseSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree); + USTATUS parseSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode = CREATE_MODE_APPEND); USTATUS parseSectionBody(const UModelIndex & index); - USTATUS parseGbeRegion(const UByteArray & gbe, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseMeRegion(const UByteArray & me, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseBiosRegion(const UByteArray & bios, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parsePdrRegion(const UByteArray & pdr, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseGenericRegion(const UINT8 subtype, const UByteArray & region, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseGbeRegion(const UByteArray & gbe, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseMeRegion(const UByteArray & me, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseBiosRegion(const UByteArray & bios, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parsePdrRegion(const UByteArray & pdr, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseGenericRegion(const UINT8 subtype, const UByteArray & region, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); USTATUS parsePadFileBody(const UModelIndex & index); USTATUS parseVolumeNonUefiData(const UByteArray & data, const UINT32 localOffset, const UModelIndex & index); USTATUS parseSections(const UByteArray & sections, const UModelIndex & index, const bool insertIntoTree); - USTATUS parseCommonSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree); - USTATUS parseCompressedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree); - USTATUS parseGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree); - USTATUS parseFreeformGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree); - USTATUS parseVersionSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree); - USTATUS parsePostcodeSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree); + USTATUS parseCommonSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseCompressedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseFreeformGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseVersionSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parsePostcodeSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree, const UINT8 mode = CREATE_MODE_APPEND); USTATUS parseCompressedSectionBody(const UModelIndex & index); USTATUS parseGuidedSectionBody(const UModelIndex & index); @@ -156,12 +170,15 @@ private: USTATUS parseIntelAcm(const UByteArray & acm, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize); USTATUS parseIntelBootGuardKeyManifest(const UByteArray & keyManifest, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize); USTATUS parseIntelBootGuardBootPolicy(const UByteArray & bootPolicy, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize); + USTATUS parseTxtConfigurationPolicy(const FIT_ENTRY* entry, UString & info); USTATUS findNextElement(const UByteArray & bootPolicy, const UINT32 elementOffset, UINT32 & nextElementOffset, UINT32 & nextElementSize); #endif #ifdef U_ENABLE_NVRAM_PARSING_SUPPORT friend class NvramParser; // Make FFS parsing routines accessible to NvramParser #endif + friend class FfsOperations; + friend class FfsBuilder; }; #endif // FFSPARSER_H diff --git a/common/fit.h b/common/fit.h index 564296a..61546de 100644 --- a/common/fit.h +++ b/common/fit.h @@ -65,6 +65,20 @@ typedef struct INTEL_MICROCODE_HEADER_ { UINT8 Reserved[12]; } INTEL_MICROCODE_HEADER; +typedef union { + struct { + UINT16 IndexRegisterAddress; + UINT16 DataRegisterAddress; + UINT8 AccessWidth; + UINT8 BitPosition; + UINT16 Index; + } IndexedIOPtrInfo; + struct { + UINT64 FlatMemoryPhysicalAddress : 63; + UINT64 TxtEnabled : 1; // bit 0 - configuration policy (0 = TXT Disabled, 1 = TXT Enabled) + } FlatMemoryPtrInfo; +} TXT_CONFIG_POLICY; + #define INTEL_MICROCODE_HEADER_VERSION 0x00000001 #define INTEL_MICROCODE_HEADER_RESERVED_BYTE 0x00 #define INTEL_MICROCODE_HEADER_SIZES_VALID(ptr) (((INTEL_MICROCODE_HEADER*)ptr)->TotalSize - ((INTEL_MICROCODE_HEADER*)ptr)->DataSize == sizeof(INTEL_MICROCODE_HEADER)) diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp index 1d574b4..7bc86b3 100644 --- a/common/nvramparser.cpp +++ b/common/nvramparser.cpp @@ -764,7 +764,7 @@ USTATUS NvramParser::getStoreSize(const UByteArray & data, const UINT32 storeOff return U_SUCCESS; } -USTATUS NvramParser::parseVssStoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseVssStoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -808,12 +808,12 @@ USTATUS NvramParser::parseVssStoreHeader(const UByteArray & store, const UINT32 vssStoreHeader->Unknown); // Add tree item - index = model->addItem(localOffset, Types::VssStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::VssStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseVss2StoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseVss2StoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -856,12 +856,12 @@ USTATUS NvramParser::parseVss2StoreHeader(const UByteArray & store, const UINT32 vssStoreHeader->Unknown); // Add tree item - index = model->addItem(localOffset, Types::Vss2Store, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::Vss2Store, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseFtwStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseFtwStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -926,12 +926,12 @@ USTATUS NvramParser::parseFtwStoreHeader(const UByteArray & store, const UINT32 (ftw32BlockHeader->Crc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); // Add tree item - index = model->addItem(localOffset, Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseFdcStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseFdcStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -964,12 +964,12 @@ USTATUS NvramParser::parseFdcStoreHeader(const UByteArray & store, const UINT32 body.size(), body.size()); // Add tree item - index = model->addItem(localOffset, Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseFsysStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseFsysStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -1011,12 +1011,12 @@ USTATUS NvramParser::parseFsysStoreHeader(const UByteArray & store, const UINT32 + (storedCrc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); // Add tree item - index = model->addItem(localOffset, Types::FsysStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::FsysStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseEvsaStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseEvsaStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -1056,12 +1056,12 @@ USTATUS NvramParser::parseEvsaStoreHeader(const UByteArray & store, const UINT32 (evsaStoreHeader->Header.Checksum != calculated ? usprintf("%, invalid, should be %02Xh", calculated) : UString(", valid")); // Add tree item - index = model->addItem(localOffset, Types::EvsaStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::EvsaStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseFlashMapStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseFlashMapStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -1096,12 +1096,12 @@ USTATUS NvramParser::parseFlashMapStoreHeader(const UByteArray & store, const UI flashMapHeader->NumEntries); // Add tree item - index = model->addItem(localOffset, Types::FlashMapStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::FlashMapStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseCmdbStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseCmdbStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -1134,12 +1134,12 @@ USTATUS NvramParser::parseCmdbStoreHeader(const UByteArray & store, const UINT32 body.size(), body.size()); // Add tree item - index = model->addItem(localOffset, Types::CmdbStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::CmdbStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseSlicPubkeyHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseSlicPubkeyHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -1176,12 +1176,12 @@ USTATUS NvramParser::parseSlicPubkeyHeader(const UByteArray & store, const UINT3 pubkeyHeader->Exponent); // Add tree item - index = model->addItem(localOffset, Types::SlicData, Subtypes::PubkeySlicData, name, UString(), info, header, UByteArray(), UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::SlicData, Subtypes::PubkeySlicData, name, UString(), info, header, UByteArray(), UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseSlicMarkerHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseSlicMarkerHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -1218,12 +1218,12 @@ USTATUS NvramParser::parseSlicMarkerHeader(const UByteArray & store, const UINT3 // Add tree item - index = model->addItem(localOffset, Types::SlicData, Subtypes::MarkerSlicData, name, UString(), info, header, UByteArray(), UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::SlicData, Subtypes::MarkerSlicData, name, UString(), info, header, UByteArray(), UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); @@ -1267,12 +1267,12 @@ USTATUS NvramParser::parseIntelMicrocodeHeader(const UByteArray & store, const U ucodeHeader->CpuFlags); // Add tree item - index = model->addItem(localOffset, Types::Microcode, Subtypes::IntelMicrocode, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(localOffset, Types::Microcode, Subtypes::IntelMicrocode, name, UString(), info, header, body, UByteArray(), Fixed, parent, mode); return U_SUCCESS; } -USTATUS NvramParser::parseStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS NvramParser::parseStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode) { const UINT32 dataSize = (const UINT32)store.size(); const UINT32* signature = (const UINT32*)store.constData(); @@ -1285,38 +1285,38 @@ USTATUS NvramParser::parseStoreHeader(const UByteArray & store, const UINT32 loc // Check signature and run parser function needed // VSS/SVS store if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) - return parseVssStoreHeader(store, localOffset, false, parent, index); + return parseVssStoreHeader(store, localOffset, false, parent, index, mode); // VSS2 store if (*signature == NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID_PART1 || *signature == NVRAM_VSS2_STORE_GUID_PART1) - return parseVss2StoreHeader(store, localOffset, false, parent, index); + return parseVss2StoreHeader(store, localOffset, false, parent, index, mode); // FTW store else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) - return parseFtwStoreHeader(store, localOffset, parent, index); + return parseFtwStoreHeader(store, localOffset, parent, index, mode); // FDC store else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) - return parseFdcStoreHeader(store, localOffset, parent, index); + return parseFdcStoreHeader(store, localOffset, parent, index, mode); // Apple Fsys/Gaid store else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE) - return parseFsysStoreHeader(store, localOffset, parent, index); + return parseFsysStoreHeader(store, localOffset, parent, index, mode); // EVSA store else if (dataSize >= 2 * sizeof(UINT32) && *(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE) - return parseEvsaStoreHeader(store, localOffset, parent, index); + return parseEvsaStoreHeader(store, localOffset, parent, index, mode); // Phoenix SCT flash map else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) - return parseFlashMapStoreHeader(store, localOffset, parent, index); + return parseFlashMapStoreHeader(store, localOffset, parent, index, mode); // Phoenix CMDB store else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) - return parseCmdbStoreHeader(store, localOffset, parent, index); + return parseCmdbStoreHeader(store, localOffset, parent, index, mode); // SLIC pubkey else if (dataSize >= 5 * sizeof(UINT32) && *(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC) - return parseSlicPubkeyHeader(store, localOffset, parent, index); + return parseSlicPubkeyHeader(store, localOffset, parent, index, mode); // SLIC marker else if (dataSize >= 34 && *(const UINT64*)(store.constData() + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG) - return parseSlicMarkerHeader(store, localOffset, parent, index); + return parseSlicMarkerHeader(store, localOffset, parent, index, mode); // Intel microcode // Must be checked after SLIC marker because of the same *signature values else if (*signature == INTEL_MICROCODE_HEADER_VERSION) - return parseIntelMicrocodeHeader(store, localOffset, parent, index); + return parseIntelMicrocodeHeader(store, localOffset, parent, index, mode); msg(usprintf("parseStoreHeader: don't know how to parse a header with signature %08Xh", *signature), parent); return U_SUCCESS; diff --git a/common/nvramparser.h b/common/nvramparser.h index facd4d2..4a6ca10 100644 --- a/common/nvramparser.h +++ b/common/nvramparser.h @@ -39,7 +39,7 @@ public: // NVRAM parsing USTATUS parseNvramVolumeBody(const UModelIndex & index); USTATUS parseNvarStore(const UModelIndex & index); - + private: TreeModel *model; FfsParser *ffsParser; @@ -50,25 +50,27 @@ private: USTATUS findNextStore(const UModelIndex & index, const UByteArray & volume, const UINT32 localOffset, const UINT32 storeOffset, UINT32 & nextStoreOffset); USTATUS getStoreSize(const UByteArray & data, const UINT32 storeOffset, UINT32 & storeSize); - USTATUS parseStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); - USTATUS parseVssStoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index); - USTATUS parseVss2StoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index); - USTATUS parseFtwStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseFdcStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseFsysStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseEvsaStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseFlashMapStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseCmdbStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseSlicPubkeyHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseSlicMarkerHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseVssStoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseVss2StoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseFtwStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseFdcStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseFsysStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseEvsaStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseFlashMapStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseCmdbStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseSlicPubkeyHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseSlicMarkerHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); + USTATUS parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const UINT8 mode = CREATE_MODE_APPEND); USTATUS parseFdcStoreBody(const UModelIndex & index); USTATUS parseVssStoreBody(const UModelIndex & index, const UINT8 alignment); USTATUS parseFsysStoreBody(const UModelIndex & index); USTATUS parseEvsaStoreBody(const UModelIndex & index); USTATUS parseFlashMapBody(const UModelIndex & index); + + friend class FfsOperations; }; #else class NvramParser diff --git a/common/utility.cpp b/common/utility.cpp index 0232a07..7f5b2a6 100644 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -17,7 +17,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "treemodel.h" #include "utility.h" +#include "peimage.h" #include "ffs.h" +#include "basetypes.h" #include "Tiano/EfiTianoCompress.h" #include "Tiano/EfiTianoDecompress.h" #include "LZMA/LzmaCompress.h" @@ -131,6 +133,7 @@ UString errorCodeToUString(USTATUS errorCode) case U_TRUNCATED_IMAGE: return UString("Image is truncated"); case U_INVALID_CAPSULE: return UString("Invalid capsule"); case U_STORES_NOT_FOUND: return UString("Stores not found"); + case U_INVALID_STORE_SIZE: return UString("Invalid store size"); default: return usprintf("Unknown error %02X", errorCode); } } @@ -329,6 +332,139 @@ USTATUS decompress(const UByteArray & compressedData, const UINT8 compressionTyp } } +UINT8 compress(const UByteArray & data, const UINT8 algorithm, UByteArray & compressedData) +{ + UINT8* compressed; + + switch (algorithm) { + case COMPRESSION_ALGORITHM_NONE: + { + compressedData = data; + return U_SUCCESS; + } + break; + case COMPRESSION_ALGORITHM_EFI11: + { + // Try legacy function first + UINT32 compressedSize = 0; + if (EfiCompressLegacy(data.constData(), data.size(), NULL, &compressedSize) != U_BUFFER_TOO_SMALL) + return U_STANDARD_COMPRESSION_FAILED; + compressed = new UINT8[compressedSize]; + if (EfiCompressLegacy(data.constData(), data.size(), compressed, &compressedSize) != U_SUCCESS) { + delete[] compressed; + return U_STANDARD_COMPRESSION_FAILED; + } + compressedData = UByteArray((const char*)compressed, compressedSize); + + // Check that compressed data can be decompressed normally + UByteArray decompressed; + UByteArray efiDecompressed; + UINT8 alg = 0; // TODO !! + if (decompress(compressedData, EFI_STANDARD_COMPRESSION, alg, decompressed, efiDecompressed) == U_SUCCESS + && decompressed == data) { + delete[] compressed; + return U_SUCCESS; + } + delete[] compressed; + + // Legacy function failed, use current one + compressedSize = 0; + if (EfiCompress(data.constData(), data.size(), NULL, &compressedSize) != U_BUFFER_TOO_SMALL) + return U_STANDARD_COMPRESSION_FAILED; + compressed = new UINT8[compressedSize]; + if (EfiCompress(data.constData(), data.size(), compressed, &compressedSize) != U_SUCCESS) { + delete[] compressed; + return U_STANDARD_COMPRESSION_FAILED; + } + compressedData = UByteArray((const char*)compressed, compressedSize); + + // New functions will be trusted here, because another check will reduce performance + delete[] compressed; + return U_SUCCESS; + } + break; + case COMPRESSION_ALGORITHM_TIANO: + { + // Try legacy function first + UINT32 compressedSize = 0; + if (TianoCompressLegacy(data.constData(), data.size(), NULL, &compressedSize) != U_BUFFER_TOO_SMALL) + return U_STANDARD_COMPRESSION_FAILED; + compressed = new UINT8[compressedSize]; + if (TianoCompressLegacy(data.constData(), data.size(), compressed, &compressedSize) != U_SUCCESS) { + delete[] compressed; + return U_STANDARD_COMPRESSION_FAILED; + } + compressedData = UByteArray((const char*)compressed, compressedSize); + + // Check that compressed data can be decompressed normally + UByteArray decompressed; + UByteArray efiDecompressed; + UINT8 alg = 0; // TODO !! + if (decompress(compressedData, EFI_STANDARD_COMPRESSION, alg, decompressed, efiDecompressed) == U_SUCCESS + && decompressed == data) { + delete[] compressed; + return U_SUCCESS; + } + delete[] compressed; + + // Legacy function failed, use current one + compressedSize = 0; + if (TianoCompress(data.constData(), data.size(), NULL, &compressedSize) != U_BUFFER_TOO_SMALL) + return U_STANDARD_COMPRESSION_FAILED; + compressed = new UINT8[compressedSize]; + if (TianoCompress(data.constData(), data.size(), compressed, &compressedSize) != U_SUCCESS) { + delete[] compressed; + return U_STANDARD_COMPRESSION_FAILED; + } + compressedData = UByteArray((const char*)compressed, compressedSize); + + // New functions will be trusted here, because another check will reduce performance + delete[] compressed; + return U_SUCCESS; + } + break; + case COMPRESSION_ALGORITHM_LZMA: + { + UINT32 compressedSize = 0; + if (LzmaCompress((const UINT8*)data.constData(), data.size(), NULL, &compressedSize) != U_BUFFER_TOO_SMALL) + return U_CUSTOMIZED_COMPRESSION_FAILED; + compressed = new UINT8[compressedSize]; + if (LzmaCompress((const UINT8*)data.constData(), data.size(), compressed, &compressedSize) != U_SUCCESS) { + delete[] compressed; + return U_CUSTOMIZED_COMPRESSION_FAILED; + } + compressedData = UByteArray((const char*)compressed, compressedSize); + delete[] compressed; + return U_SUCCESS; + } + break; + case COMPRESSION_ALGORITHM_IMLZMA: + { + UINT32 compressedSize = 0; + UByteArray header = data.left(sizeof(EFI_COMMON_SECTION_HEADER)); + const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)header.constData(); + UINT32 headerSize = sizeOfSectionHeader(sectionHeader); + header = data.left(headerSize); + UByteArray newData = data.mid(headerSize); + if (LzmaCompress((const UINT8*)newData.constData(), newData.size(), NULL, &compressedSize) != U_BUFFER_TOO_SMALL) + return U_CUSTOMIZED_COMPRESSION_FAILED; + compressed = new UINT8[compressedSize]; + if (LzmaCompress((const UINT8*)newData.constData(), newData.size(), compressed, &compressedSize) != U_SUCCESS) { + delete[] compressed; + return U_CUSTOMIZED_COMPRESSION_FAILED; + } + compressedData = header.append(UByteArray((const char*)compressed, compressedSize)); + delete[] compressed; + return U_SUCCESS; + } + break; + default: + //msg(usprintf("compress: unknown compression algorithm %1").arg(algorithm)); + //return U_UNKNOWN_COMPRESSION_ALGORITHM; + return -1; + } +} + // 8bit sum calculation routine UINT8 calculateSum8(const UINT8* buffer, UINT32 bufferSize) { @@ -414,7 +550,7 @@ INTN findPattern(const UINT8 *pattern, const UINT8 *patternMask, UINTN patternSi } BOOLEAN makePattern(const CHAR8 *textPattern, std::vector &pattern, std::vector &patternMask) { - UINTN len = std::strlen(textPattern); + UINTN len = strlen(textPattern); if (len == 0 || len % 2 != 0) return FALSE; @@ -441,6 +577,316 @@ BOOLEAN makePattern(const CHAR8 *textPattern, std::vector &pattern, std:: pattern[i] |= static_cast(v2); } } - + return TRUE; } + +USTATUS getEntryPoint(const UByteArray &file, UINT32& entryPoint) +{ + if (file.isEmpty()) + return U_INVALID_FILE; + + // Populate DOS header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) + return U_INVALID_FILE; + const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)file.constData(); + + // Check signature + if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ + UINT32 offset = dosHeader->e_lfanew; + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) + return U_UNKNOWN_IMAGE_TYPE; + const EFI_IMAGE_PE_HEADER* peHeader = (const EFI_IMAGE_PE_HEADER*)(file.constData() + offset); + if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) + return U_UNKNOWN_IMAGE_TYPE; + offset += sizeof(EFI_IMAGE_PE_HEADER); + + // Skip file header + offset += sizeof(EFI_IMAGE_FILE_HEADER); + + // Check optional header magic + const UINT16 magic = *(const UINT16*)(file.constData() + offset); + if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) + return U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; + const EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER32*)(file.constData() + offset); + entryPoint = optHeader->ImageBase + optHeader->AddressOfEntryPoint; + } + else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) + return U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; + const EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER64*)(file.constData() + offset); + entryPoint = optHeader->ImageBase + optHeader->AddressOfEntryPoint; + } + else + return U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; + } + else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ + // Populate TE header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) + return U_INVALID_FILE; + const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)file.constData(); + UINT32 teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); + entryPoint = teHeader->ImageBase + teHeader->AddressOfEntryPoint - teFixup; + } + return U_SUCCESS; +} + +USTATUS getBase(const UByteArray& file, UINT32& base) +{ + if (file.isEmpty()) + return U_INVALID_FILE; + + // Populate DOS header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) + return U_INVALID_FILE; + const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)file.constData(); + + // Check signature + if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ + UINT32 offset = dosHeader->e_lfanew; + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) + return U_UNKNOWN_IMAGE_TYPE; + const EFI_IMAGE_PE_HEADER* peHeader = (const EFI_IMAGE_PE_HEADER*)(file.constData() + offset); + if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) + return U_UNKNOWN_IMAGE_TYPE; + offset += sizeof(EFI_IMAGE_PE_HEADER); + + // Skip file header + offset += sizeof(EFI_IMAGE_FILE_HEADER); + + // Check optional header magic + const UINT16 magic = *(const UINT16*)(file.constData() + offset); + if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) + return U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; + const EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER32*)(file.constData() + offset); + base = optHeader->ImageBase; + } + else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) + return U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; + const EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER64*)(file.constData() + offset); + base = optHeader->ImageBase; + } + else + return U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; + } + else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ + // Populate TE header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) + return U_INVALID_FILE; + const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)file.constData(); + //!TODO: add handling + base = teHeader->ImageBase; + } + + return U_SUCCESS; +} + +USTATUS growVolume(UByteArray & header, const UINT32 size, UINT32 & newSize) +{ + // Check sanity + if ((UINT32)header.size() < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) + return U_INVALID_VOLUME; + + // Adjust new size to be representable by current FvBlockMap + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); + EFI_FV_BLOCK_MAP_ENTRY* blockMap = (EFI_FV_BLOCK_MAP_ENTRY*)(header.data() + sizeof(EFI_FIRMWARE_VOLUME_HEADER)); + + // Get block map size + UINT32 blockMapSize = volumeHeader->HeaderLength - sizeof(EFI_FIRMWARE_VOLUME_HEADER); + if (blockMapSize % sizeof(EFI_FV_BLOCK_MAP_ENTRY)) + return U_INVALID_VOLUME; + UINT32 blockMapCount = blockMapSize / sizeof(EFI_FV_BLOCK_MAP_ENTRY); + + // Check blockMap validity + if (blockMap[blockMapCount - 1].NumBlocks != 0 || blockMap[blockMapCount - 1].Length != 0) + return U_INVALID_VOLUME; + + // Case of complex blockMap + if (blockMapCount > 2) + return U_COMPLEX_BLOCK_MAP; + + // Calculate new size + if (newSize <= size) + return U_INVALID_PARAMETER; + + newSize += blockMap[0].Length - newSize % blockMap[0].Length; + + // Recalculate number of blocks + blockMap[0].NumBlocks = newSize / blockMap[0].Length; + + // Set new volume size + volumeHeader->FvLength = 0; + for (UINT8 i = 0; i < blockMapCount; i++) { + volumeHeader->FvLength += blockMap[i].NumBlocks * blockMap[i].Length; + } + + // Recalculate volume header checksum + volumeHeader->Checksum = 0; + volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); + + return U_SUCCESS; +} + +USTATUS rebase(UByteArray &executable, const UINT32 base) +{ + UINT32 delta; // Difference between old and new base addresses + UINT32 relocOffset; // Offset of relocation region + UINT32 relocSize; // Size of relocation region + UINT32 teFixup = 0; // Bytes removed form PE header for TE images + + // Copy input data to local storage + UByteArray file = executable; + + // Populate DOS header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) + return U_INVALID_FILE; + EFI_IMAGE_DOS_HEADER* dosHeader = (EFI_IMAGE_DOS_HEADER*)file.data(); + + // Check signature + if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ + UINT32 offset = dosHeader->e_lfanew; + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) + return U_UNKNOWN_IMAGE_TYPE; + EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(file.data() + offset); + if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) + return U_UNKNOWN_IMAGE_TYPE; + offset += sizeof(EFI_IMAGE_PE_HEADER); + // Skip file header + offset += sizeof(EFI_IMAGE_FILE_HEADER); + // Check optional header magic + if ((UINT32)file.size() < offset + sizeof(UINT16)) + return U_UNKNOWN_IMAGE_TYPE; + UINT16 magic = *(UINT16*)(file.data() + offset); + if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) + return U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; + EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (EFI_IMAGE_OPTIONAL_HEADER32*)(file.data() + offset); + delta = base - optHeader->ImageBase; + if (!delta) + // No need to rebase + return U_SUCCESS; + relocOffset = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; + relocSize = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; + // Set new base + optHeader->ImageBase = base; + } + else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { + if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) + return U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; + EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (EFI_IMAGE_OPTIONAL_HEADER64*)(file.data() + offset); + delta = base - optHeader->ImageBase; + if (!delta) + // No need to rebase + return U_SUCCESS; + relocOffset = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; + relocSize = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; + // Set new base + optHeader->ImageBase = base; + } + else + return U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; + } + else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ + // Populate TE header + if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) + return U_INVALID_FILE; + EFI_IMAGE_TE_HEADER* teHeader = (EFI_IMAGE_TE_HEADER*)file.data(); + delta = base - teHeader->ImageBase; + if (!delta) + // No need to rebase + return U_SUCCESS; + relocOffset = teHeader->DataDirectory[EFI_IMAGE_TE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; + teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); + relocSize = teHeader->DataDirectory[EFI_IMAGE_TE_DIRECTORY_ENTRY_BASERELOC].Size; + // Set new base + teHeader->ImageBase = base; + + // Warn the user about possible outcome of incorrect rebase of TE image + //msg(tr("rebase: can't determine if TE image base is adjusted or not, rebased TE image may stop working"), index); + } + else + return U_UNKNOWN_IMAGE_TYPE; + + // No relocations + if (relocOffset == 0) { + // No need to fix relocations + executable = file; + return U_SUCCESS; + } + + EFI_IMAGE_BASE_RELOCATION *RelocBase; + EFI_IMAGE_BASE_RELOCATION *RelocBaseEnd; + UINT16 *Reloc; + UINT16 *RelocEnd; + UINT16 *F16; + UINT32 *F32; + UINT64 *F64; + + // Run the whole relocation block + RelocBase = (EFI_IMAGE_BASE_RELOCATION*)(file.data() + relocOffset - teFixup); + RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION*)(file.data() + relocOffset - teFixup + relocSize); + + while (RelocBase < RelocBaseEnd) { + Reloc = (UINT16*)((UINT8*)RelocBase + sizeof(EFI_IMAGE_BASE_RELOCATION)); + RelocEnd = (UINT16*)((UINT8*)RelocBase + RelocBase->SizeOfBlock); + + // Run this relocation record + while (Reloc < RelocEnd) { + if (*Reloc == 0x0000) { + // Skip last emtpy reloc entry + Reloc += 1; + continue; + } + + UINT32 RelocLocation = RelocBase->VirtualAddress - teFixup + (*Reloc & 0x0FFF); + if ((UINT32)file.size() < RelocLocation) + //return U_BAD_RELOCATION_ENTRY; + return U_UNKNOWN_RELOCATION_TYPE; + UINT8* data = (UINT8*)(file.data() + RelocLocation); + switch ((*Reloc) >> 12) { + case EFI_IMAGE_REL_BASED_ABSOLUTE: + // Do nothing + break; + + case EFI_IMAGE_REL_BASED_HIGH: + // Add second 16 bits of delta + F16 = (UINT16*)data; + *F16 = (UINT16)(*F16 + (UINT16)(((UINT32)delta) >> 16)); + break; + + case EFI_IMAGE_REL_BASED_LOW: + // Add first 16 bits of delta + F16 = (UINT16*)data; + *F16 = (UINT16)(*F16 + (UINT16)delta); + break; + + case EFI_IMAGE_REL_BASED_HIGHLOW: + // Add first 32 bits of delta + F32 = (UINT32*)data; + *F32 = *F32 + (UINT32)delta; + break; + + case EFI_IMAGE_REL_BASED_DIR64: + // Add all 64 bits of delta + F64 = (UINT64*)data; + *F64 = *F64 + (UINT64)delta; + break; + + default: + return U_UNKNOWN_RELOCATION_TYPE; + } + + // Next relocation record + Reloc += 1; + } + + // Next relocation block + RelocBase = (EFI_IMAGE_BASE_RELOCATION*)RelocEnd; + } + + executable = file; + return U_SUCCESS; +} diff --git a/common/utility.h b/common/utility.h index ef768aa..00af634 100644 --- a/common/utility.h +++ b/common/utility.h @@ -31,6 +31,7 @@ UString errorCodeToUString(USTATUS errorCode); USTATUS decompress(const UByteArray & compressed, const UINT8 compressionType, UINT8 & algorithm, UByteArray & decompressed, UByteArray & efiDecompressed); // Compression routine +UINT8 compress(const UByteArray & data, const UINT8 algorithm, UByteArray & compressedData); //USTATUS compress(const UByteArray & decompressed, UByteArray & compressed, const UINT8 & algorithm); // CRC32 calculation routine @@ -55,4 +56,13 @@ BOOLEAN makePattern(const CHAR8 *textPattern, std::vector &pattern, std:: INTN findPattern(const UINT8 *pattern, const UINT8 *patternMask, UINTN patternSize, const UINT8 *data, UINTN dataSize, UINTN dataOff); +// Image base adress +USTATUS getBase(const UByteArray& file, UINT32& base); +// Entry point +USTATUS getEntryPoint(const UByteArray& file, UINT32 &entryPoint); + +USTATUS growVolume(UByteArray & header, const UINT32 size, UINT32 & newSize); + +USTATUS rebase(UByteArray &executable, const UINT32 base); + #endif // UTILITY_H