From 36c26595a0671baa5ab4f2aaaa4958e65d1351b2 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Mon, 21 Mar 2016 09:54:20 +0100 Subject: [PATCH] UT NE A22 - added UI for NVAR variables - added parsing of StdDefaults and MfgDefaults nesting variables --- UEFITool/uefitool.cpp | 49 ++++++++++++++++++++++++++++++++++---- UEFITool/uefitool.h | 1 + UEFITool/uefitool.ui | 20 ++++++++++++++++ common/ffsops.cpp | 2 +- common/ffsparser.cpp | 38 +++++++++++++++++++++++------- common/nvram.cpp | 55 ++++++++++++++++++++----------------------- common/nvram.h | 10 ++++++-- common/types.cpp | 2 ++ common/types.h | 1 + 9 files changed, 133 insertions(+), 45 deletions(-) diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index 8dd751c..5fb0120 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -55,6 +55,7 @@ version(tr("0.30.0_alpha22")) connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about())); connect(ui->actionAboutQt, SIGNAL(triggered()), this, SLOT(aboutQt())); connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(exit())); + connect(ui->actionGoToData, SIGNAL(triggered()), this, SLOT(goToData())); connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(writeSettings())); // Enable Drag-and-Drop actions @@ -161,7 +162,7 @@ void UEFITool::populateUi(const QModelIndex ¤t) return; UINT8 type = model->type(current); - //UINT8 subtype = model->subtype(current); + UINT8 subtype = model->subtype(current); // Set info text ui->infoEdit->setPlainText(model->info(current)); @@ -174,10 +175,12 @@ void UEFITool::populateUi(const QModelIndex ¤t) ui->menuVolumeActions->setEnabled(type == Types::Volume); ui->menuFileActions->setEnabled(type == Types::File); ui->menuSectionActions->setEnabled(type == Types::Section); + ui->menuVariableActions->setEnabled(type == Types::NvramVariableNvar); // Enable actions ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current)); - + ui->actionGoToData->setEnabled(type == Types::NvramVariableNvar && subtype == Subtypes::LinkNvar); + // Disable rebuild for now //ui->actionRebuild->setDisabled(type == Types::Region && subtype == Subtypes::DescriptorRegion); //ui->actionReplace->setDisabled(type == Types::Region && subtype == Subtypes::DescriptorRegion); @@ -271,6 +274,34 @@ void UEFITool::search() } } +void UEFITool::goToData() +{ + QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + if (!index.isValid() || model->type(index) != Types::NvramVariableNvar || model->subtype(index) != Subtypes::LinkNvar) + return; + + // Get parent + QModelIndex parent = model->parent(index); + + for (int i = index.row(); i < model->rowCount(parent); i++) { + PARSING_DATA pdata = parsingDataFromQModelIndex(index); + UINT32 lastVariableFlag = pdata.emptyByte ? 0xFFFFFF : 0; + if (pdata.nvram.nvar.next == lastVariableFlag) { + ui->structureTreeView->scrollTo(index, QAbstractItemView::PositionAtCenter); + ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear); + } + + for (int j = i + 1; j < model->rowCount(parent); j++) { + QModelIndex currentIndex = parent.child(j, 0); + PARSING_DATA currentPdata = parsingDataFromQModelIndex(currentIndex); + if (currentPdata.offset == pdata.offset + pdata.nvram.nvar.next) { + index = currentIndex; + break; + } + } + } +} + void UEFITool::insert(const UINT8 mode) { /*QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); @@ -497,7 +528,10 @@ void UEFITool::extract(const UINT8 mode) path = QFileDialog::getSaveFileName(this, tr("Save FFS file to file"), name + ".ffs", "FFS files (*.ffs *.bin);;All files (*)"); break; case Types::Section: - path = QFileDialog::getSaveFileName(this, tr("Save section file to file"), name + ".sct", "Section files (*.sct *.bin);;All files (*)"); + path = QFileDialog::getSaveFileName(this, tr("Save section to file"), name + ".sct", "Section files (*.sct *.bin);;All files (*)"); + break; + case Types::NvramVariableNvar: + path = QFileDialog::getSaveFileName(this, tr("Save NVAR variable to file"), name + ".nvar", "NVAR variable files (*.nvar *.bin);;All files (*)"); break; default: path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); @@ -531,6 +565,9 @@ void UEFITool::extract(const UINT8 mode) path = QFileDialog::getSaveFileName(this, tr("Save section body to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); } break; + case Types::NvramVariableNvar: + path = QFileDialog::getSaveFileName(this, tr("Save NVAR variable body to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); + break; default: path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); } @@ -847,8 +884,7 @@ void UEFITool::scrollTreeView(QListWidgetItem* item) QModelIndex index = messageItem->index(); if (index.isValid()) { ui->structureTreeView->scrollTo(index, QAbstractItemView::PositionAtCenter); - ui->structureTreeView->selectionModel()->clearSelection(); - ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::Select); + ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear); } } @@ -893,6 +929,9 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event) case Types::Section: ui->menuSectionActions->exec(event->globalPos()); break; + case Types::NvramVariableNvar: + ui->menuVariableActions->exec(event->globalPos()); + break; } } diff --git a/UEFITool/uefitool.h b/UEFITool/uefitool.h index 2a25997..ea505e5 100644 --- a/UEFITool/uefitool.h +++ b/UEFITool/uefitool.h @@ -71,6 +71,7 @@ private slots: void saveImageFile(); void search(); + void goToData(); void extract(const UINT8 mode); void extractAsIs(); void extractBody(); diff --git a/UEFITool/uefitool.ui b/UEFITool/uefitool.ui index c18b774..9dd1f31 100644 --- a/UEFITool/uefitool.ui +++ b/UEFITool/uefitool.ui @@ -394,6 +394,20 @@ + + + Variable + + + + + + + + + + + @@ -401,6 +415,7 @@ + @@ -646,6 +661,11 @@ Ctrl+Alt+E + + + Go to &data + + diff --git a/common/ffsops.cpp b/common/ffsops.cpp index b24b4b6..2d38cd5 100644 --- a/common/ffsops.cpp +++ b/common/ffsops.cpp @@ -56,6 +56,7 @@ STATUS FfsOperations::extract(const QModelIndex & index, QString & name, QByteAr else name = itemName; } break; + case Types::NvramVariableNvar: case Types::File: { name = itemText.isEmpty() ? itemName : itemText.replace(' ', '_'); } break; @@ -67,7 +68,6 @@ STATUS FfsOperations::extract(const QModelIndex & index, QString & name, QByteAr // Append section subtype name name += QChar('_') + itemName.replace(' ', '_'); } break; - case Types::Capsule: case Types::Image: case Types::Region: diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 847ee69..bf2bd40 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -1610,7 +1610,7 @@ STATUS FfsParser::parseFileHeader(const QByteArray & file, const UINT32 parentOf text = QObject::tr("Volume Top File"); } // Check if the file is NVRAM storage with NVAR format - else if (guid == NVRAM_NVAR_FILE_GUID) { + else if (guid == NVRAM_NVAR_STORAGE_FILE_GUID || guid == NVRAM_NVAR_EXTERNAL_DEFAULTS_FILE_GUID) { // Mark the file as NVAR storage pdata.file.format = RAW_FILE_FORMAT_NVAR_STORAGE; } @@ -2883,7 +2883,8 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex & UINT32 offset = 0; UINT32 guidsInStorage = 0; - + + // Parse all variables while (1) { bool msgUnknownExtDataFormat = false; bool msgExtHeaderTooLong = false; @@ -2919,6 +2920,7 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex & if (unparsedSize < sizeof(NVAR_VARIABLE_HEADER) || variableHeader->Signature != NVRAM_NVAR_VARIABLE_SIGNATURE || unparsedSize < variableHeader->Size) { + // Check if the data left is a free space or a padding QByteArray padding = data.mid(offset, unparsedSize); UINT8 type; @@ -2930,6 +2932,12 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex & subtype = 0; } else { + // Nothing is parsed yet, but the file is not empty + if (!offset) { + msg(QObject::tr("parseNvarStorage: file can't be parsed as NVAR variables storage"), index); + return ERR_INVALID_FILE; + } + // It's a padding name = QObject::tr("Padding"); type = Types::Padding; @@ -2962,7 +2970,7 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex & header = data.mid(offset, sizeof(NVAR_VARIABLE_HEADER)); body = data.mid(offset + sizeof(NVAR_VARIABLE_HEADER), variableHeader->Size - sizeof(NVAR_VARIABLE_HEADER)); - UINT32 lastVariableFlag = pdata.emptyByte == 0 ? 0 : 0xFFFFFF; + UINT32 lastVariableFlag = pdata.emptyByte ? 0xFFFFFF : 0; // Set default next to predefined last value pdata.nvram.nvar.next = lastVariableFlag; @@ -2977,7 +2985,7 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex & // Add next node information to parsing data if (variableHeader->Next != lastVariableFlag) { subtype = Subtypes::LinkNvar; - pdata.nvram.nvar.next = offset + variableHeader->Next; + pdata.nvram.nvar.next = variableHeader->Next; } // Variable with extended header @@ -3046,7 +3054,7 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex & for (int i = 0; i < model->rowCount(index); i++) { nvarIndex = index.child(i, 0); PARSING_DATA nvarPdata = parsingDataFromQModelIndex(nvarIndex); - if (nvarPdata.nvram.nvar.next == offset) { // Previous link is present and valid + if (nvarPdata.nvram.nvar.next + nvarPdata.offset - parentOffset == offset) { // Previous link is present and valid isInvalid = false; break; } @@ -3102,8 +3110,14 @@ parsing_done: QString info; // Rename invalid variables according to their types if (isInvalid) { - name = QObject::tr("Invalid"); - subtype = Subtypes::InvalidNvar; + if (variableHeader->Next != lastVariableFlag) { + name = QObject::tr("Invalid link"); + subtype = Subtypes::InvalidLinkNvar; + } + else { + name = QObject::tr("Invalid"); + subtype = Subtypes::InvalidNvar; + } } else // Add GUID info for valid variables info += QObject::tr("Variable GUID: %1\n").arg(name); @@ -3122,7 +3136,7 @@ parsing_done: pdata.nvram.nvar.attributes = variableHeader->Attributes; // Add next node info - if (variableHeader->Next != lastVariableFlag) + if (!isInvalid && variableHeader->Next != lastVariableFlag) info += QObject::tr("\nNext node at offset: %1h").hexarg(parentOffset + offset + variableHeader->Next); // Add extended header info @@ -3165,6 +3179,14 @@ parsing_done: msg(QObject::tr("parseNvarStorage: extended data size (%1h) is smaller than required for timestamp and hash (0x28)") .hexarg(extendedData.size()), varIndex); + // Check variable name to be in the list of nesting variables + for (std::vector::const_iterator iter = nestingVariableNames.cbegin(); iter != nestingVariableNames.cend(); ++iter) + if (QString(*iter) == text.toLatin1()) { + STATUS result = parseNvarStorage(body, varIndex); + if (result) + msg(QObject::tr("parseNvarStorage: parsing of nested NVAR storage failed with error \"%1\"").arg(errorCodeToQString(result)), varIndex); + } + // Move to next variable offset += variableHeader->Size; } diff --git a/common/nvram.cpp b/common/nvram.cpp index 52cfb04..a98ae10 100644 --- a/common/nvram.cpp +++ b/common/nvram.cpp @@ -16,35 +16,32 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. QString variableAttributesToQstring(UINT8 attributes) { - QString str; - - if (attributes == 0x00 || attributes == 0xFF) { + if (attributes == 0x00 || attributes == 0xFF) return QString(); - } - if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_RUNTIME) { - str += QObject::tr(", Runtime"); - } - if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_ASCII_NAME) { - str += QObject::tr(", AsciiName"); - } - if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_GUID) { - str += QObject::tr(", Guid"); - } - if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_DATA_ONLY) { - str += QObject::tr(", DataOnly"); - } - if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_EXT_HEADER) { - str += QObject::tr(", ExtHeader"); - } - if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_HW_ERROR_RECORD) { - str += QObject::tr(", HwErrorRecord"); - } - if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_AUTH_WRITE) { - str += QObject::tr(", AuthWrite"); - } - if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_VALID) { - str += QObject::tr(", Valid"); - } + + QString str; + if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_RUNTIME) + str += QObject::tr(", Runtime"); + if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_ASCII_NAME) + str += QObject::tr(", AsciiName"); + if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_GUID) + str += QObject::tr(", Guid"); + if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_DATA_ONLY) + str += QObject::tr(", DataOnly"); + if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_EXT_HEADER) + str += QObject::tr(", ExtHeader"); + if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_HW_ERROR_RECORD) + str += QObject::tr(", HwErrorRecord"); + if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_AUTH_WRITE) + str += QObject::tr(", AuthWrite"); + if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_VALID) + str += QObject::tr(", Valid"); + return str.mid(2); // Remove the first comma and space -} \ No newline at end of file +} + +std::vector nestingVariableNames = { + "StdDefaults", + "MfgDefaults" +}; \ No newline at end of file diff --git a/common/nvram.h b/common/nvram.h index 9d85d65..80655c5 100644 --- a/common/nvram.h +++ b/common/nvram.h @@ -23,12 +23,18 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // Let's start with NVAR storage, as the most difficult one // -//CEF5B9A3-476D-497F-9FDC-E98143E0422C -const QByteArray NVRAM_NVAR_FILE_GUID +// CEF5B9A3-476D-497F-9FDC-E98143E0422C +const QByteArray NVRAM_NVAR_STORAGE_FILE_GUID ("\xA3\xB9\xF5\xCE\x6D\x47\x7F\x49\x9F\xDC\xE9\x81\x43\xE0\x42\x2C", 16); +// 9221315B-30BB-46B5-813E-1B1BF4712BD3 +const QByteArray NVRAM_NVAR_EXTERNAL_DEFAULTS_FILE_GUID +("\x5B\x31\x21\x92\xBB\x30\xB5\x46\x81\x3E\x1B\x1B\xF4\x71\x2B\xD3", 16); + extern QString variableAttributesToQstring(UINT8 attributes); +extern std::vector nestingVariableNames; + // Make sure we use right packing rules #pragma pack(push,1) diff --git a/common/types.cpp b/common/types.cpp index 31f367c..a541059 100644 --- a/common/types.cpp +++ b/common/types.cpp @@ -123,6 +123,8 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) case Types::NvramVariableNvar: if (subtype == Subtypes::InvalidNvar) return QObject::tr("Invalid"); + if (subtype == Subtypes::InvalidLinkNvar) + return QObject::tr("Invalid link"); if (subtype == Subtypes::LinkNvar) return QObject::tr("Link"); if (subtype == Subtypes::DataNvar) diff --git a/common/types.h b/common/types.h index 13c0242..1f59d0f 100644 --- a/common/types.h +++ b/common/types.h @@ -87,6 +87,7 @@ namespace Subtypes { enum NvarVariableSubtypes { InvalidNvar = 120, + InvalidLinkNvar, LinkNvar, DataNvar, FullNvar