From 40200bca12016e36c65cb5a471299cd20ff57223 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Tue, 5 Apr 2016 00:47:34 +0200 Subject: [PATCH] Support for _FDC and Fsys NVRAM formats - only one format remains - EVSA - added scope to text search UI, because of NVRAM variables having texts in headers --- UEFITool/ffsfinder.cpp | 26 ++- UEFITool/ffsfinder.h | 2 +- UEFITool/searchdialog.ui | 54 ++++++- UEFITool/uefitool.cpp | 28 +++- common/ffs.h | 2 +- common/ffsops.cpp | 2 + common/ffsparser.cpp | 339 +++++++++++++++++++++++++++++++++------ common/ffsparser.h | 1 + common/nvram.h | 24 ++- common/types.cpp | 9 ++ common/types.h | 5 +- common/utility.cpp | 8 +- common/utility.h | 2 +- 13 files changed, 426 insertions(+), 76 deletions(-) diff --git a/UEFITool/ffsfinder.cpp b/UEFITool/ffsfinder.cpp index fb8700d..fd29670 100644 --- a/UEFITool/ffsfinder.cpp +++ b/UEFITool/ffsfinder.cpp @@ -156,7 +156,7 @@ STATUS FfsFinder::findGuidPattern(const QModelIndex & index, const QByteArray & return ERR_SUCCESS; } -STATUS FfsFinder::findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive) +STATUS FfsFinder::findTextPattern(const QModelIndex & index, const QString & pattern, const UINT8 mode, const bool unicode, const Qt::CaseSensitivity caseSensitive) { if (pattern.isEmpty()) return ERR_INVALID_PARAMETER; @@ -166,24 +166,36 @@ STATUS FfsFinder::findTextPattern(const QModelIndex & index, const QString & pat bool hasChildren = (model->rowCount(index) > 0); for (int i = 0; i < model->rowCount(index); i++) { - findTextPattern(index.child(i, index.column()), pattern, unicode, caseSensitive); + findTextPattern(index.child(i, index.column()), pattern, mode, unicode, caseSensitive); } - if (hasChildren) - return ERR_SUCCESS; + QByteArray body; + if (hasChildren) { + if (mode != SEARCH_MODE_BODY) + body = model->header(index); + } + else { + if (mode == SEARCH_MODE_HEADER) + body.append(model->header(index)); + else if (mode == SEARCH_MODE_BODY) + body.append(model->body(index)); + else + body.append(model->header(index)).append(model->body(index)); + } QString data; if (unicode) - data = QString::fromUtf16((const ushort*)model->body(index).data(), model->body(index).length() / 2); + data = QString::fromUtf16((const ushort*)body.constData(), body.length() / 2); else - data = QString::fromLatin1((const char*)model->body(index).data(), model->body(index).length()); + data = QString::fromLatin1((const char*)body.constData(), body.length()); int offset = -1; while ((offset = data.indexOf(pattern, offset + 1, caseSensitive)) >= 0) { - msg(QObject::tr("%1 text \"%2\" found in %3 at offset %4h") + msg(QObject::tr("%1 text \"%2\" found in %3 at %4-offset %5h") .arg(unicode ? "Unicode" : "ASCII") .arg(pattern) .arg(model->name(index)) + .arg(mode == SEARCH_MODE_BODY ? QObject::tr("body") : QObject::tr("header")) .hexarg(unicode ? offset * 2 : offset), index); } diff --git a/UEFITool/ffsfinder.h b/UEFITool/ffsfinder.h index 5863399..789d1e0 100644 --- a/UEFITool/ffsfinder.h +++ b/UEFITool/ffsfinder.h @@ -36,7 +36,7 @@ public: STATUS findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode); STATUS findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode); - STATUS findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive); + STATUS findTextPattern(const QModelIndex & index, const QString & pattern, const UINT8 mode, const bool unicode, const Qt::CaseSensitivity caseSensitive); private: const TreeModel* model; diff --git a/UEFITool/searchdialog.ui b/UEFITool/searchdialog.ui index 6442853..510a46f 100644 --- a/UEFITool/searchdialog.ui +++ b/UEFITool/searchdialog.ui @@ -7,7 +7,7 @@ 0 0 400 - 237 + 218 @@ -143,20 +143,66 @@ + + + 0 + 0 + + Text: - - + + + + + 0 + 0 + + + + + + Search scope + + + + + + Header and body + + + true + + + + + + + Header only + + + + + + + Body only + + + + + + + Text search options - + diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index 7cbb2bb..d3febd1 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.30.0_alpha23")) +version(tr("0.30.0_alpha24")) { clipboard = QApplication::clipboard(); @@ -175,8 +175,9 @@ 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 || type == Types::NvramVariableVss); - + ui->menuVariableActions->setEnabled(type == Types::NvramVariableNvar || type == Types::NvramVariableVss || type == Types::NvramVariableFsys); + ui->menuStorageActions->setEnabled(type == Types::NvramStorageVss || type == Types::NvramStorageFdc || type == Types::NvramStorageFsys); + // Enable actions ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current)); ui->actionGoToData->setEnabled(type == Types::NvramVariableNvar && subtype == Subtypes::LinkNvar); @@ -267,8 +268,14 @@ void UEFITool::search() QString pattern = searchDialog->ui->textEdit->text(); if (pattern.isEmpty()) return; - - ffsFinder->findTextPattern(rootIndex, pattern, searchDialog->ui->textUnicodeCheckBox->isChecked(), + UINT8 mode; + if (searchDialog->ui->textScopeHeaderRadioButton->isChecked()) + mode = SEARCH_MODE_HEADER; + else if (searchDialog->ui->textScopeBodyRadioButton->isChecked()) + mode = SEARCH_MODE_BODY; + else + mode = SEARCH_MODE_ALL; + ffsFinder->findTextPattern(rootIndex, pattern, mode, searchDialog->ui->textUnicodeCheckBox->isChecked(), (Qt::CaseSensitivity) searchDialog->ui->textCaseSensitiveCheckBox->isChecked()); showFinderMessages(); } @@ -537,8 +544,12 @@ void UEFITool::extract(const UINT8 mode) path = QFileDialog::getSaveFileName(this, tr("Save variable to file"), name + ".var", "Variable files (*.var *.bin);;All files (*)"); break; case Types::NvramStorageVss: + case Types::NvramStorageFdc: path = QFileDialog::getSaveFileName(this, tr("Save variable storage to file"), name + ".vss", "Variable storage files (*.vss *.bin);;All files (*)"); break; + case Types::NvramStorageFsys: + path = QFileDialog::getSaveFileName(this, tr("Save Fsys storage to file"), name + ".fsys", "Fsys storage files (*.fsys *.bin);;All files (*)"); + break; default: path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); } @@ -576,8 +587,12 @@ void UEFITool::extract(const UINT8 mode) path = QFileDialog::getSaveFileName(this, tr("Save variable body to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); break; case Types::NvramStorageVss: + case Types::NvramStorageFdc: path = QFileDialog::getSaveFileName(this, tr("Save variable storage body to file"), name + ".vsb", "Variable storage body files (*.vsb *.bin);;All files (*)"); break; + case Types::NvramStorageFsys: + path = QFileDialog::getSaveFileName(this, tr("Save Fsys storage body to file"), name + ".fsb", "Fsys storage body files (*.fsb *.bin);;All files (*)"); + break; default: path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); } @@ -941,9 +956,12 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event) break; case Types::NvramVariableNvar: case Types::NvramVariableVss: + case Types::NvramVariableFsys: ui->menuVariableActions->exec(event->globalPos()); break; case Types::NvramStorageVss: + case Types::NvramStorageFdc: + case Types::NvramStorageFsys: ui->menuStorageActions->exec(event->globalPos()); break; } diff --git a/common/ffs.h b/common/ffs.h index 4ea0b51..a53a641 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -113,7 +113,7 @@ typedef struct _EFI_FIRMWARE_VOLUME_HEADER { UINT16 ExtHeaderOffset; //Reserved in Revision 1 UINT8 Reserved; UINT8 Revision; - //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[1]; + //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[2]; } EFI_FIRMWARE_VOLUME_HEADER; // Standard file system GUIDs diff --git a/common/ffsops.cpp b/common/ffsops.cpp index 8b4bb87..6af8792 100644 --- a/common/ffsops.cpp +++ b/common/ffsops.cpp @@ -74,6 +74,8 @@ STATUS FfsOperations::extract(const QModelIndex & index, QString & name, QByteAr case Types::Region: case Types::Padding: case Types::NvramStorageVss: + case Types::NvramStorageFdc: + case Types::NvramStorageFsys: default: name = itemName.replace(' ', '_').replace('/', '_'); } diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index d0e89f9..2c69eee 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -3364,8 +3364,12 @@ STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex & QModelIndex current = index.child(i, 0); switch (model->type(current)) { case Types::NvramStorageVss: + case Types::NvramStorageFdc: parseVssStorageBody(current); break; + case Types::NvramStorageFsys: + parseFsysStorageBody(current); + break; case Types::Padding: // No parsing required break; @@ -3402,16 +3406,21 @@ STATUS FfsParser::findNextStorage(const QModelIndex & index, const QByteArray & // msg(QObject::tr("findNextStorage: VSS storage candidate at offset %1h skipped, has invalid state %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->State, 2), index); // continue; //} + // All checks passed, storage found break; } - //else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE) { //Fsys signature found - // // No checks yet - // break; - //} + else if (*currentPos == NVRAM_FDC_VOLUME_SIGNATURE) { //FDC signature found + // No checks needed + break; + } + else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE) { //Fsys signature found + // No checks needed + break; + } } // No more storages found - if (offset == dataSize - sizeof(UINT32)) + if (offset >= dataSize - sizeof(UINT32)) return ERR_STORAGES_NOT_FOUND; nextStorageOffset = offset; @@ -3421,66 +3430,198 @@ STATUS FfsParser::findNextStorage(const QModelIndex & index, const QByteArray & STATUS FfsParser::getStorageSize(const QByteArray & data, const UINT32 storageOffset, UINT32 & storageSize) { - //TODO: add Fsys, GUID and _FDC support - const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)(data.constData() + storageOffset); - storageSize = vssHeader->Size; + //TODO: add GUID support + const UINT32* signature = (const UINT32*)(data.constData() + storageOffset); + if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) { + const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)signature; + storageSize = vssHeader->Size; + } + else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) { + const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)signature; + storageSize = fdcHeader->Size; + } + else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE) { + const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)signature; + storageSize = fsysHeader->Size; + } return ERR_SUCCESS; } STATUS FfsParser::parseStorageHeader(const QByteArray & storage, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) { - // Parse VSS volume like raw area, seen for now - // $VSS, $SVS, Fsys, full volume GUID, _FDC and paddings + // Parse VSS volume like raw area + //TODO: seen for now - $VSS, $SVS, Fsys, full volume GUID, _FDC and paddings - // The volume must begin with VSS storage to be valid, but after the first one, there can be many variants const UINT32 dataSize = (UINT32)storage.size(); - if (dataSize < sizeof(VSS_VARIABLE_STORE_HEADER)) { - msg(QObject::tr("parseStorageHeader: volume body is too small even for VSS storage header"), parent); + const UINT32* signature = (const UINT32*)storage.constData(); + if (dataSize < sizeof(UINT32)) { + msg(QObject::tr("parseStorageHeader: volume body is too small even for storage signature"), parent); return ERR_SUCCESS; } - // Get VSS storage header - const VSS_VARIABLE_STORE_HEADER* vssStorageHeader = (const VSS_VARIABLE_STORE_HEADER*)storage.constData(); + // VSS variable storages + if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) { + // The volume must begin with a storage to be valid, but after the first one, there can be many variants + if (dataSize < sizeof(VSS_VARIABLE_STORE_HEADER)) { + msg(QObject::tr("parseStorageHeader: volume body is too small even for VSS storage header"), parent); + return ERR_SUCCESS; + } - // Check signature - if (vssStorageHeader->Signature != NVRAM_VSS_STORE_SIGNATURE && vssStorageHeader->Signature != NVRAM_APPLE_SVS_STORE_SIGNATURE) { - msg(QObject::tr("parseStorageHeader: invalid storage signature %1h").hexarg2(vssStorageHeader->Signature, 8), parent); - return ERR_SUCCESS; + // Get VSS storage header + const VSS_VARIABLE_STORE_HEADER* vssStorageHeader = (const VSS_VARIABLE_STORE_HEADER*)signature; + + // Check storage size + if (dataSize < vssStorageHeader->Size) { + msg(QObject::tr("parseStorageHeader: VSS storage size %1h (%2) is greater than volume body size %3h (%4)") + .hexarg2(vssStorageHeader->Size, 8).arg(vssStorageHeader->Size) + .hexarg2(dataSize, 8).arg(dataSize), parent); + return ERR_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); + + // Construct header and body + QByteArray header = storage.left(sizeof(VSS_VARIABLE_STORE_HEADER)); + QByteArray body = storage.mid(sizeof(VSS_VARIABLE_STORE_HEADER), vssStorageHeader->Size - sizeof(VSS_VARIABLE_STORE_HEADER)); + + // Add info + QString name = QObject::tr("VSS storage"); + QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nFormat: %8h\nState: %9h") + .hexarg2(vssStorageHeader->Signature, 8) + .hexarg(vssStorageHeader->Size).arg(vssStorageHeader->Size) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()) + .hexarg2(vssStorageHeader->Format, 2) + .hexarg2(vssStorageHeader->State, 2); + + // Add unknown field for $SVS storages + if (*signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) + info += QObject::tr("\nUnknown: %1h").hexarg2(vssStorageHeader->Unknown, 4); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::NvramStorageVss, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent); } + else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) { + // The volume must begin with a storage to be valid, but after the first one, there can be many variants + if (dataSize < sizeof(FDC_VOLUME_HEADER)) { + msg(QObject::tr("parseStorageHeader: volume body is too small even for FDC storage header"), parent); + return ERR_SUCCESS; + } - // Check storage size - if (dataSize < vssStorageHeader->Size) { - msg(QObject::tr("parseStorageHeader: first VSS storage size %1h (%2) is greater than volume body size %3h (%4)") - .hexarg2(vssStorageHeader->Size, 8).arg(vssStorageHeader->Size) - .hexarg2(dataSize, 8).arg(dataSize), parent); - return ERR_SUCCESS; + // Get VSS storage header + const FDC_VOLUME_HEADER* fdcStorageHeader = (const FDC_VOLUME_HEADER*)signature; + + // Check storage size + if (dataSize < fdcStorageHeader->Size) { + msg(QObject::tr("parseStorageHeader: FDC storage size %1h (%2) is greater than volume body size %3h (%4)") + .hexarg2(fdcStorageHeader->Size, 8).arg(fdcStorageHeader->Size) + .hexarg2(dataSize, 8).arg(dataSize), parent); + return ERR_SUCCESS; + } + + // Determine internal volume header size + const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(fdcStorageHeader + 1); + UINT32 headerSize; + if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { + const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)((const UINT8*)volumeHeader + volumeHeader->ExtHeaderOffset); + headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize; + } + else + headerSize = volumeHeader->HeaderLength; + + // Extended header end can be unaligned + headerSize = ALIGN8(headerSize); + + // Add VSS storage header + headerSize += sizeof(VSS_VARIABLE_STORE_HEADER); + + // Add FDC header + headerSize += sizeof(FDC_VOLUME_HEADER); + + // Check sanity of combined header size + if (dataSize < headerSize) { + msg(QObject::tr("parseStorageHeader: FDC storage header size %1h (%2) is greater than volume body size %3h (%4)") + .hexarg2(fdcStorageHeader->Size, 8).arg(fdcStorageHeader->Size) + .hexarg2(dataSize, 8).arg(dataSize), parent); + return ERR_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); + + // Construct header and body + QByteArray header = storage.left(headerSize); + QByteArray body = storage.mid(headerSize, fdcStorageHeader->Size - headerSize); + + // Add info + QString name = QObject::tr("FDC storage"); + QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") + .hexarg2(fdcStorageHeader->Signature, 8) + .hexarg(fdcStorageHeader->Size).arg(fdcStorageHeader->Size) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()); + + // TODO: add internal headers info + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::NvramStorageFdc, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent); } + else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE) { + // The volume must begin with a storage to be valid, but after the first one, there can be many variants + if (dataSize < sizeof(APPLE_FSYS_STORE_HEADER)) { + msg(QObject::tr("parseStorageHeader: volume body is too small even for Fsys storage header"), parent); + return ERR_SUCCESS; + } - // Get parsing data - PARSING_DATA pdata = parsingDataFromQModelIndex(parent); + // Get Fsys storage header + const APPLE_FSYS_STORE_HEADER* fsysStorageHeader = (const APPLE_FSYS_STORE_HEADER*)signature; - // Construct header and body - QByteArray header = storage.left(sizeof(VSS_VARIABLE_STORE_HEADER)); - QByteArray body = storage.mid(sizeof(VSS_VARIABLE_STORE_HEADER), vssStorageHeader->Size - sizeof(VSS_VARIABLE_STORE_HEADER)); + // Check storage size + if (dataSize < fsysStorageHeader->Size) { + msg(QObject::tr("parseStorageHeader: Fsys storage size %1h (%2) is greater than volume body size %3h (%4)") + .hexarg2(fsysStorageHeader->Size, 4).arg(fsysStorageHeader->Size) + .hexarg2(dataSize, 8).arg(dataSize), parent); + return ERR_SUCCESS; + } - // Add info - QString name = QObject::tr("VSS storage"); - QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nFormat: %8h\nState: %9h") - .hexarg2(vssStorageHeader->Signature, 8) - .hexarg(vssStorageHeader->Size).arg(vssStorageHeader->Size) - .hexarg(header.size()).arg(header.size()) - .hexarg(body.size()).arg(body.size()) - .hexarg2(vssStorageHeader->Format, 2) - .hexarg2(vssStorageHeader->State, 2); + // Get parsing data + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); - // Add correct offset - pdata.offset = parentOffset; + // Construct header and body + QByteArray header = storage.left(sizeof(APPLE_FSYS_STORE_HEADER)); + QByteArray body = storage.mid(sizeof(APPLE_FSYS_STORE_HEADER), fsysStorageHeader->Size - sizeof(APPLE_FSYS_STORE_HEADER) - sizeof(UINT32)); - // Add tree item - index = model->addItem(Types::NvramStorageVss, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent); + // Check storage checksum + UINT32 storedCrc = *(UINT32*)storage.right(sizeof(UINT32)).constBegin(); + UINT32 calculatedCrc = calculatedCrc = crc32(0, (const UINT8*)storage.constData(), (const UINT32)storage.size() - sizeof(UINT32)); - //Parse the storage - //parseVssStorageBody(body, index); + // Add info + QString name = QObject::tr("Fsys storage"); + QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nUnknown: %9 %10 %11 %12 %13\nCRC32: %14") + .hexarg2(fsysStorageHeader->Signature, 8) + .hexarg(fsysStorageHeader->Size).arg(fsysStorageHeader->Size) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()) + .hexarg2(fsysStorageHeader->Unknown[0], 2) + .hexarg2(fsysStorageHeader->Unknown[1], 2) + .hexarg2(fsysStorageHeader->Unknown[2], 2) + .hexarg2(fsysStorageHeader->Unknown[3], 2) + .hexarg2(fsysStorageHeader->Unknown[4], 2) + .arg(storedCrc == calculatedCrc ? QObject::tr("%1h, valid").hexarg2(storedCrc, 8) : QObject::tr("%1h, invalid, should be %2h").hexarg2(storedCrc, 8).hexarg2(calculatedCrc, 8)); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::NvramStorageFsys, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent); + } return ERR_SUCCESS; } @@ -3689,3 +3830,111 @@ STATUS FfsParser::parseVssStorageBody(const QModelIndex & index) return ERR_SUCCESS; } + +STATUS FfsParser::parseFsysStorageBody(const QModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Get parsing data for the current item + PARSING_DATA pdata = parsingDataFromQModelIndex(index); + UINT32 parentOffset = pdata.offset + model->header(index).size(); + const QByteArray data = model->body(index); + + // Check that the is enough space for variable header + const UINT32 dataSize = (UINT32)data.size(); + UINT32 offset = 0; + + // Parse all variables + while (1) { + UINT32 unparsedSize = dataSize - offset; + UINT32 variableSize = 0; + + // Get nameSize and name of the variable + const UINT8 nameSize = *(UINT8*)(data.constData() + offset); + // Check sanity + if (unparsedSize >= nameSize + sizeof(UINT8)) { + variableSize = nameSize + sizeof(UINT8); + } + + QByteArray name; + if (variableSize) { + name = data.mid(offset + sizeof(UINT8), nameSize); + // Check for EOF variable + if (nameSize == 3 && name[0] == 'E' && name[1] == 'O' && name[2] == 'F') { + // There is no data afterward, add EOF variable and free space and return + QByteArray header = data.mid(offset, sizeof(UINT8) + nameSize); + QString info = QObject::tr("Full size: %1h (%2)") + .hexarg(header.size()).arg(header.size()); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add EOF tree item + model->addItem(Types::NvramVariableFsys, 0, name, QString(), info, header, QByteArray(), FALSE, parsingDataToQByteArray(pdata), index); + + // Add free space + offset += header.size(); + unparsedSize = dataSize - offset; + QByteArray body = data.mid(offset); + info = QObject::tr("Full size: %1h (%2)") + .hexarg(body.size()).arg(body.size()); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add free space tree item + model->addItem(Types::FreeSpace, 0, QObject::tr("Free space"), QString(), info, QByteArray(), body, FALSE, parsingDataToQByteArray(pdata), index); + + return ERR_SUCCESS; + } + } + + // Get dataSize and data of the variable + const UINT16 dataSize = *(UINT16*)(data.constData() + offset + sizeof(UINT8) + nameSize); + if (unparsedSize >= sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize) { + variableSize = sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize; + } + else { + // Last variable is bad, add the rest as padding and return + QByteArray body = data.mid(offset); + QString info = QObject::tr("Full size: %1h (%2)") + .hexarg(body.size()).arg(body.size()); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add free space tree item + model->addItem(Types::Padding, getPaddingType(body), QObject::tr("Padding"), QString(), info, QByteArray(), body, FALSE, parsingDataToQByteArray(pdata), index); + + // Show message + msg(QObject::tr("parseFsysStorageBody: variable appears too big, added as padding"), index); + + return ERR_SUCCESS; + } + + // Construct header and body + QByteArray header = data.mid(offset, sizeof(UINT8) + nameSize + sizeof(UINT16)); + QByteArray body = data.mid(offset + sizeof(UINT8) + nameSize + sizeof(UINT16), dataSize); + + // Add info + QString info = QObject::tr("Full size: %1h (%2)\nHeader size %3h (%4)\nBody size: %5h (%6)") + .hexarg(variableSize).arg(variableSize) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add tree item + model->addItem(Types::NvramVariableFsys, 0, name, QString(), info, header, body, FALSE, parsingDataToQByteArray(pdata), index); + + // Move to next variable + offset += variableSize; + } + + return ERR_SUCCESS; +} + + diff --git a/common/ffsparser.h b/common/ffsparser.h index e202187..6d9344f 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -114,6 +114,7 @@ private: STATUS getStorageSize(const QByteArray & data, const UINT32 storageOffset, UINT32 & storageSize); STATUS parseStorageHeader(const QByteArray & storage, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parseVssStorageBody(const QModelIndex & index); + STATUS parseFsysStorageBody(const QModelIndex & index); // Message helper void msg(const QString & message, const QModelIndex &index = QModelIndex()); diff --git a/common/nvram.h b/common/nvram.h index 69b3f3f..d3756c0 100644 --- a/common/nvram.h +++ b/common/nvram.h @@ -99,10 +99,9 @@ typedef struct _VSS_VARIABLE_STORE_HEADER { // Apple Fsys store header typedef struct _APPLE_FSYS_STORE_HEADER { - UINT32 Signature; // Fsys signature - UINT8 Unknown; // Still unknown - UINT32 Unknown2; // Still unknown - UINT16 Size; // Size of variable storage + UINT32 Signature; // Fsys signature + UINT8 Unknown[5]; // Still unknown + UINT16 Size; // Size of variable storage } APPLE_FSYS_STORE_HEADER; // Apple Fsys variable format @@ -110,8 +109,8 @@ typedef struct _APPLE_FSYS_STORE_HEADER { // CHAR8 Name[]; // UINT16 DataLength; // UINT8 Data[] -// End with a chunk named "EOF" without data -// All free bytes are zeros +// Storage ends with a chunk named "EOF" without data +// All free bytes in storage are zeroed // Has CRC32 of the whole store without checksum field at the end // Normal variable header @@ -168,6 +167,19 @@ typedef struct _VSS_AUTH_VARIABLE_HEADER { #define NVRAM_VSS_VARIABLE_APPEND_WRITE 0x00000040 #define NVRAM_VSS_VARIABLE_APPLE_DATA_CHECKSUM 0x80000000 +// FDC region can be found in some VSS volumes +// It has another VSS volume inside +// _FDC header structure +typedef struct _FDC_VOLUME_HEADER { + UINT32 Signature; + UINT32 Size; + //EFI_FIRMWARE_VOLUME_HEADER VolumeHeader; + //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[2]; + //VSS_VARIABLE_STORE_HEADER VssHeader; +} FDC_VOLUME_HEADER; + +#define NVRAM_FDC_VOLUME_SIGNATURE 0x4344465F + // Restore previous packing rules #pragma pack(pop) diff --git a/common/types.cpp b/common/types.cpp index 047d4a4..4888271 100644 --- a/common/types.cpp +++ b/common/types.cpp @@ -69,8 +69,14 @@ QString itemTypeToQString(const UINT8 type) return QObject::tr("NVAR variable"); case Types::NvramStorageVss: return QObject::tr("VSS storage"); + case Types::NvramStorageFdc: + return QObject::tr("FDC storage"); + case Types::NvramStorageFsys: + return QObject::tr("Fsys storage"); case Types::NvramVariableVss: return QObject::tr("VSS variable"); + case Types::NvramVariableFsys: + return QObject::tr("Fsys variable"); default: return QObject::tr("Unknown"); } @@ -140,6 +146,9 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) else return QObject::tr("Unknown subtype"); case Types::NvramStorageVss: + case Types::NvramStorageFdc: + case Types::NvramStorageFsys: + case Types::NvramVariableFsys: return QString(); case Types::NvramVariableVss: if (subtype == Subtypes::InvalidVss) diff --git a/common/types.h b/common/types.h index 32f71c8..878de73 100644 --- a/common/types.h +++ b/common/types.h @@ -45,7 +45,10 @@ namespace Types { FreeSpace, NvramVariableNvar, NvramStorageVss, - NvramVariableVss + NvramStorageFdc, + NvramStorageFsys, + NvramVariableVss, + NvramVariableFsys }; } diff --git a/common/utility.cpp b/common/utility.cpp index e984968..020f30e 100644 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -101,7 +101,7 @@ QString errorCodeToQString(UINT8 errorCode) } // CRC32 implementation -UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length) +UINT32 crc32(UINT32 initial, const UINT8* buffer, const UINT32 length) { static const UINT32 crcTable[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, @@ -141,12 +141,10 @@ UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length) 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; - UINT32 crc32; - UINT32 i; // Accumulate crc32 for buffer - crc32 = initial ^ 0xFFFFFFFF; - for (i = 0; i < length; i++) { + UINT32 crc32 = initial ^ 0xFFFFFFFF; + for (UINT32 i = 0; i < length; i++) { crc32 = (crc32 >> 8) ^ crcTable[(crc32 ^ buffer[i]) & 0xFF]; } diff --git a/common/utility.h b/common/utility.h index fc572f4..40d52d7 100644 --- a/common/utility.h +++ b/common/utility.h @@ -35,7 +35,7 @@ extern STATUS decompress(const QByteArray & compressed, UINT8 & algorithm, QByte //STATUS compress(const QByteArray & decompressed, QByteArray & compressed, const UINT8 & algorithm); // CRC32 calculation routine -extern UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length); +extern UINT32 crc32(UINT32 initial, const UINT8* buffer, const UINT32 length); // 8bit checksum calculation routine extern UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize);