NVAR parsing support

- still no proper menus for data extraction
- other NVRAM formats TBD
This commit is contained in:
Nikolaj Schlej 2016-03-20 23:59:03 +01:00
parent c5dfbe7924
commit 3cf145a3cc
12 changed files with 500 additions and 22 deletions

View File

@ -17,7 +17,7 @@
UEFITool::UEFITool(QWidget *parent) : UEFITool::UEFITool(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::UEFITool), ui(new Ui::UEFITool),
version(tr("0.30.0_alpha21")) version(tr("0.30.0_alpha22"))
{ {
clipboard = QApplication::clipboard(); clipboard = QApplication::clipboard();
@ -161,7 +161,7 @@ void UEFITool::populateUi(const QModelIndex &current)
return; return;
UINT8 type = model->type(current); UINT8 type = model->type(current);
UINT8 subtype = model->subtype(current); //UINT8 subtype = model->subtype(current);
// Set info text // Set info text
ui->infoEdit->setPlainText(model->info(current)); ui->infoEdit->setPlainText(model->info(current));

View File

@ -10,6 +10,7 @@ SOURCES += uefitool_main.cpp \
messagelistitem.cpp \ messagelistitem.cpp \
guidlineedit.cpp \ guidlineedit.cpp \
ffsfinder.cpp \ ffsfinder.cpp \
../common/nvram.cpp \
../common/ffsops.cpp \ ../common/ffsops.cpp \
../common/types.cpp \ ../common/types.cpp \
../common/descriptor.cpp \ ../common/descriptor.cpp \
@ -35,6 +36,7 @@ HEADERS += uefitool.h \
messagelistitem.h \ messagelistitem.h \
guidlineedit.h \ guidlineedit.h \
ffsfinder.h \ ffsfinder.h \
../common/nvram.h \
../common/ffsops.h \ ../common/ffsops.h \
../common/basetypes.h \ ../common/basetypes.h \
../common/descriptor.h \ ../common/descriptor.h \

View File

@ -102,7 +102,7 @@ QString sectionTypeToQString(const UINT8 type)
case EFI_SECTION_PEI_DEPEX: return QObject::tr("PEI dependency"); case EFI_SECTION_PEI_DEPEX: return QObject::tr("PEI dependency");
case EFI_SECTION_SMM_DEPEX: return QObject::tr("SMM dependency"); case EFI_SECTION_SMM_DEPEX: return QObject::tr("SMM dependency");
case INSYDE_SECTION_POSTCODE: return QObject::tr("Insyde postcode"); case INSYDE_SECTION_POSTCODE: return QObject::tr("Insyde postcode");
case SCT_SECTION_POSTCODE: return QObject::tr("SCT postcode"); case PHOENIX_SECTION_POSTCODE: return QObject::tr("Phoenix postcode");
default: return QObject::tr("Unknown"); default: return QObject::tr("Unknown");
} }
} }

View File

@ -91,8 +91,6 @@ const QByteArray APTIO_SIGNED_CAPSULE_GUID
const QByteArray APTIO_UNSIGNED_CAPSULE_GUID const QByteArray APTIO_UNSIGNED_CAPSULE_GUID
("\x90\xBB\xEE\x14\x0A\x89\xDB\x43\xAE\xD1\x5D\x3C\x45\x88\xA4\x18", 16); ("\x90\xBB\xEE\x14\x0A\x89\xDB\x43\xAE\xD1\x5D\x3C\x45\x88\xA4\x18", 16);
//14EEBB90-890A-43DB-AED1-5D3C4588A418
//***************************************************************************** //*****************************************************************************
// EFI Firmware Volume // EFI Firmware Volume
//***************************************************************************** //*****************************************************************************
@ -401,7 +399,7 @@ typedef struct _EFI_COMMON_SECTION_HEADER2 {
#define EFI_SECTION_RAW 0x19 #define EFI_SECTION_RAW 0x19
#define EFI_SECTION_PEI_DEPEX 0x1B #define EFI_SECTION_PEI_DEPEX 0x1B
#define EFI_SECTION_SMM_DEPEX 0x1C #define EFI_SECTION_SMM_DEPEX 0x1C
#define SCT_SECTION_POSTCODE 0xF0 // Specific to Phoenix SCT images #define PHOENIX_SECTION_POSTCODE 0xF0 // Specific to Phoenix SCT images
#define INSYDE_SECTION_POSTCODE 0x20 // Specific to Insyde images #define INSYDE_SECTION_POSTCODE 0x20 // Specific to Insyde images
// Compression section // Compression section

View File

@ -423,7 +423,7 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa
} }
// Sort regions in ascending order // Sort regions in ascending order
qSort(regions); std::sort(regions.begin(), regions.end());
// Check for intersections and paddings between regions // Check for intersections and paddings between regions
REGION_INFO region; REGION_INFO region;
@ -1595,16 +1595,25 @@ STATUS FfsParser::parseFileHeader(const QByteArray & file, const UINT32 parentOf
.hexarg2(fileHeader->IntegrityCheck.Checksum.File, 2) .hexarg2(fileHeader->IntegrityCheck.Checksum.File, 2)
.arg(msgInvalidDataChecksum ? QObject::tr("invalid, should be %1h").hexarg2(calculatedData, 2) : QObject::tr("valid")); .arg(msgInvalidDataChecksum ? QObject::tr("invalid, should be %1h").hexarg2(calculatedData, 2) : QObject::tr("valid"));
// Check if the file is a Volume Top File // Set raw file format to unknown by default
pdata.file.format = RAW_FILE_FORMAT_UNKNOWN;
QString text; QString text;
bool isVtf = false; bool isVtf = false;
if (EFI_FFS_VOLUME_TOP_FILE_GUID == header.left(sizeof(EFI_GUID))) { QByteArray guid = header.left(sizeof(EFI_GUID));
// Check if the file is a Volume Top File
if (guid == EFI_FFS_VOLUME_TOP_FILE_GUID) {
// Mark it as the last VTF // Mark it as the last VTF
// This information will later be used to determine memory addresses of uncompressed image elements // This information will later be used to determine memory addresses of uncompressed image elements
// Because the last byte of the last VFT is mapped to 0xFFFFFFFF physical memory address // Because the last byte of the last VFT is mapped to 0xFFFFFFFF physical memory address
isVtf = true; isVtf = true;
text = QObject::tr("Volume Top File"); text = QObject::tr("Volume Top File");
} }
// Check if the file is NVRAM storage with NVAR format
else if (guid == NVRAM_NVAR_FILE_GUID) {
// Mark the file as NVAR storage
pdata.file.format = RAW_FILE_FORMAT_NVAR_STORAGE;
}
// Construct parsing data // Construct parsing data
bool fixed = fileHeader->Attributes & FFS_ATTRIB_FIXED; bool fixed = fileHeader->Attributes & FFS_ATTRIB_FIXED;
@ -1674,8 +1683,16 @@ STATUS FfsParser::parseFileBody(const QModelIndex & index)
return parsePadFileBody(index); return parsePadFileBody(index);
// Parse raw files as raw areas // Parse raw files as raw areas
if (model->subtype(index) == EFI_FV_FILETYPE_RAW || model->subtype(index) == EFI_FV_FILETYPE_ALL) if (model->subtype(index) == EFI_FV_FILETYPE_RAW || model->subtype(index) == EFI_FV_FILETYPE_ALL) {
// Get data from parsing data
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
// Parse NVAR storage
if (pdata.file.format == RAW_FILE_FORMAT_NVAR_STORAGE)
return parseNvarStorage(model->body(index), index);
return parseRawArea(model->body(index), index); return parseRawArea(model->body(index), index);
}
// Parse sections // Parse sections
return parseSections(model->body(index), index); return parseSections(model->body(index), index);
@ -1834,7 +1851,7 @@ STATUS FfsParser::parseSectionHeader(const QByteArray & section, const UINT32 pa
case EFI_SECTION_GUID_DEFINED: return parseGuidedSectionHeader(section, parentOffset, parent, index, preparse); case EFI_SECTION_GUID_DEFINED: return parseGuidedSectionHeader(section, parentOffset, parent, index, preparse);
case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return parseFreeformGuidedSectionHeader(section, parentOffset, parent, index, preparse); case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return parseFreeformGuidedSectionHeader(section, parentOffset, parent, index, preparse);
case EFI_SECTION_VERSION: return parseVersionSectionHeader(section, parentOffset, parent, index, preparse); case EFI_SECTION_VERSION: return parseVersionSectionHeader(section, parentOffset, parent, index, preparse);
case SCT_SECTION_POSTCODE: case PHOENIX_SECTION_POSTCODE:
case INSYDE_SECTION_POSTCODE: return parsePostcodeSectionHeader(section, parentOffset, parent, index, preparse); case INSYDE_SECTION_POSTCODE: return parsePostcodeSectionHeader(section, parentOffset, parent, index, preparse);
// Common // Common
case EFI_SECTION_DISPOSABLE: case EFI_SECTION_DISPOSABLE:
@ -2234,10 +2251,11 @@ STATUS FfsParser::parseSectionBody(const QModelIndex & index)
// Sanity check // Sanity check
if (!index.isValid()) if (!index.isValid())
return ERR_INVALID_PARAMETER; return ERR_INVALID_PARAMETER;
if ((UINT32)model->header(index).size() < sizeof(EFI_COMMON_SECTION_HEADER)) QByteArray header = model->header(index);
if (header.size() < sizeof(EFI_COMMON_SECTION_HEADER))
return ERR_INVALID_SECTION; return ERR_INVALID_SECTION;
const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(model->header(index).constData()); const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(header.constData());
switch (sectionHeader->Type) { switch (sectionHeader->Type) {
// Encapsulation // Encapsulation
@ -2258,7 +2276,7 @@ STATUS FfsParser::parseSectionBody(const QModelIndex & index)
case EFI_SECTION_RAW: return parseRawSectionBody(index); case EFI_SECTION_RAW: return parseRawSectionBody(index);
// No parsing needed // No parsing needed
case EFI_SECTION_COMPATIBILITY16: case EFI_SECTION_COMPATIBILITY16:
case SCT_SECTION_POSTCODE: case PHOENIX_SECTION_POSTCODE:
case INSYDE_SECTION_POSTCODE: case INSYDE_SECTION_POSTCODE:
default: default:
return ERR_SUCCESS; return ERR_SUCCESS;
@ -2833,11 +2851,8 @@ STATUS FfsParser::addOffsetsRecursive(const QModelIndex & index)
PARSING_DATA pdata = parsingDataFromQModelIndex(index); PARSING_DATA pdata = parsingDataFromQModelIndex(index);
// Add current offset if the element is not compressed // Add current offset if the element is not compressed
if (!model->compressed(index)) { // or it's compressed, but it's parent isn't
model->addInfo(index, QObject::tr("Offset: %1h\n").hexarg(pdata.offset), false); if ((!model->compressed(index)) || (index.parent().isValid() && !model->compressed(index.parent()))) {
}
// Or it's compressed, but it's parent isn't
else if (index.parent().isValid() && !model->compressed(index.parent())) {
model->addInfo(index, QObject::tr("Offset: %1h\n").hexarg(pdata.offset), false); model->addInfo(index, QObject::tr("Offset: %1h\n").hexarg(pdata.offset), false);
} }
@ -2852,3 +2867,307 @@ STATUS FfsParser::addOffsetsRecursive(const QModelIndex & index)
return ERR_SUCCESS; return ERR_SUCCESS;
} }
STATUS FfsParser::parseNvarStorage(const QByteArray & data, 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();
// Rename parent file
model->setText(model->findParentOfType(index, Types::File), QObject::tr("NVAR storage"));
UINT32 offset = 0;
UINT32 guidsInStorage = 0;
while (1) {
bool msgUnknownExtDataFormat = false;
bool msgExtHeaderTooLong = false;
bool msgExtDataTooShort = false;
bool isInvalid = false;
bool isDataOnly = false;
bool hasExtendedHeader = false;
bool hasChecksum = false;
bool hasTimestampAndHash = false;
UINT8 storedChecksum = 0;
UINT8 calculatedChecksum = 0;
UINT16 extendedHeaderSize = 0;
UINT8 extendedAttributes = 0;
UINT64 timestamp = 0;
QByteArray hash;
UINT8 subtype = Subtypes::FullNvar;
QString name;
QString text;
QByteArray header;
QByteArray body;
QByteArray extendedData;
UINT32 guidAreaSize = guidsInStorage * sizeof(EFI_GUID);
UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize;
// Get variable header
const NVAR_VARIABLE_HEADER* variableHeader = (const NVAR_VARIABLE_HEADER*)(data.constData() + offset);
// Check variable header
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;
if (padding.count(pdata.emptyByte) == padding.size()) {
// It's a free space
name = QObject::tr("Free space");
type = Types::FreeSpace;
subtype = 0;
}
else {
// It's a padding
name = QObject::tr("Padding");
type = Types::Padding;
subtype = getPaddingType(padding);
}
// Get info
QString info = QObject::tr("Full size: %1h (%2)")
.hexarg(padding.size()).arg(padding.size());
// Construct parsing data
pdata.offset = parentOffset + offset;
// Add tree item
model->addItem(type, subtype, name, QString(), info, QByteArray(), padding, FALSE, parsingDataToQByteArray(pdata), index);
// Add GUID storage area
QByteArray guidArea = data.right(guidAreaSize);
// Get info
name = QObject::tr("GUID storage area");
info = QObject::tr("Full size: %1h (%2)\nGUIDs in storage: %3")
.hexarg(guidArea.size()).arg(guidArea.size())
.arg(guidsInStorage);
// Construct parsing data
pdata.offset = parentOffset + offset + padding.size();
// Add tree item
model->addItem(Types::Padding, getPaddingType(guidArea), name, QString(), info, QByteArray(), guidArea, FALSE, parsingDataToQByteArray(pdata), index);
return ERR_SUCCESS;
}
// Contruct generic header and body
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;
// Set default next to predefined last value
pdata.nvram.nvar.next = lastVariableFlag;
// Variable is marked as invalid
if ((variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_VALID) == 0) { // Valid attribute is not set
isInvalid = true;
// Do not parse further
goto parsing_done;
}
// Add next node information to parsing data
if (variableHeader->Next != lastVariableFlag) {
subtype = Subtypes::LinkNvar;
pdata.nvram.nvar.next = offset + variableHeader->Next;
}
// Variable with extended header
if (variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_EXT_HEADER) {
hasExtendedHeader = true;
msgUnknownExtDataFormat = true;
extendedHeaderSize = *(UINT16*)(body.constData() + body.size() - sizeof(UINT16));
if (extendedHeaderSize > body.size()) {
msgExtHeaderTooLong = true;
isInvalid = true;
// Do not parse further
goto parsing_done;
}
extendedAttributes = *(UINT8*)(body.constData() + body.size() - extendedHeaderSize);
// Variable with checksum
if (extendedAttributes & NVRAM_NVAR_VARIABLE_EXT_ATTRIB_CHECKSUM) {
// Get stored checksum
storedChecksum = *(UINT8*)(body.constData() + body.size() - sizeof(UINT16) - sizeof(UINT8));
// Recalculate checksum for the variable
calculatedChecksum = 0;
// Include variable data
UINT8* start = (UINT8*)(variableHeader + 1);
for (UINT8* p = start; p < start + variableHeader->Size - sizeof(NVAR_VARIABLE_HEADER); p++) {
calculatedChecksum += *p;
}
// Include variable size and flags
start = (UINT8*)&variableHeader->Size;
for (UINT8*p = start; p < start + sizeof(UINT16); p++) {
calculatedChecksum += *p;
}
// Include variable attributes
calculatedChecksum += variableHeader->Attributes;
hasChecksum = true;
msgUnknownExtDataFormat = false;
}
extendedData = body.mid(body.size() - extendedHeaderSize + sizeof(UINT8), extendedHeaderSize - sizeof(UINT16) - sizeof(UINT8) - (hasChecksum ? 1 : 0));
body = body.left(body.size() - extendedHeaderSize);
// Variable with authenticated write (for SecureBoot)
if (variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_AUTH_WRITE) {
if (extendedData.size() < 40) {
msgExtDataTooShort = true;
isInvalid = true;
// Do not parse further
goto parsing_done;
}
timestamp = *(UINT64*)(extendedData.constData());
hash = extendedData.mid(sizeof(UINT64), 0x20); //Length of SHA256 hash
hasTimestampAndHash = true;
msgUnknownExtDataFormat = false;
}
}
// Variable is data-only (nameless and GUIDless link)
if (variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_DATA_ONLY) { // Data-only attribute is set
isInvalid = true;
QModelIndex nvarIndex;
// Search prevously added variable for a link to this variable
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
isInvalid = false;
break;
}
}
// Check if the link is valid
if (!isInvalid) {
// Use the name and text of the previous link
name = model->name(nvarIndex);
text = model->text(nvarIndex);
if (variableHeader->Next == lastVariableFlag)
subtype = Subtypes::DataNvar;
}
isDataOnly = true;
// Do not parse further
goto parsing_done;
}
// Get variable name
UINT32 nameOffset = (variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_GUID) ? sizeof(EFI_GUID) : 1; // GUID can be stored with the variable or in a separate storage, so there will only be an index of it
CHAR8* namePtr = (CHAR8*)(variableHeader + 1) + nameOffset;
UINT32 nameSize = 0;
if (variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_ASCII_NAME) { // Name is stored as ASCII string of CHAR8s
text = QString(namePtr);
nameSize = text.length() + 1;
}
else { // Name is stored as UCS2 string of CHAR16s
text = QString::fromUtf16((CHAR16*)namePtr);
nameSize = (text.length() + 1) * 2;
}
// Get variable GUID
if (variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_GUID) { // GUID is strored in the variable itself
name = guidToQString(*(EFI_GUID*)(variableHeader + 1));
}
// GUID is stored in GUID list at the end of the storage
else {
UINT32 guidIndex = *(UINT8*)(variableHeader + 1);
if (guidsInStorage < guidIndex + 1)
guidsInStorage = guidIndex + 1;
// The list begins at the end of the storage and goes backwards
const EFI_GUID* guidPtr = (const EFI_GUID*)(data.constData() + data.size()) - guidIndex;
name = guidToQString(*guidPtr);
}
// Include variable name and GUID into the header and remove them from body
header = data.mid(offset, sizeof(NVAR_VARIABLE_HEADER) + nameOffset + nameSize);
body = body.mid(nameOffset + nameSize);
parsing_done:
QString info;
// Rename invalid variables according to their types
if (isInvalid) {
name = QObject::tr("Invalid");
subtype = Subtypes::InvalidNvar;
}
else // Add GUID info for valid variables
info += QObject::tr("Variable GUID: %1\n").arg(name);
// Add header, body and extended data info
info += QObject::tr("Full size: %1h (%2)\nHeader size %3h (%4)\nBody size: %5h (%6)")
.hexarg(variableHeader->Size).arg(variableHeader->Size)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size());
// Add attributes info
info += QObject::tr("\nAttributes: %1h").hexarg2(variableHeader->Attributes, 2);
// Translate attributes to text
if (variableHeader->Attributes)
info += QObject::tr("\nAttributes as text: %1").arg(variableAttributesToQstring(variableHeader->Attributes));
pdata.nvram.nvar.attributes = variableHeader->Attributes;
// Add next node info
if (variableHeader->Next != lastVariableFlag)
info += QObject::tr("\nNext node at offset: %1h").hexarg(parentOffset + offset + variableHeader->Next);
// Add extended header info
if (hasExtendedHeader) {
info += QObject::tr("\nExtended header size: %1h (%2)\nExtended attributes: %3h")
.hexarg(extendedHeaderSize).arg(extendedHeaderSize)
.hexarg2(extendedAttributes, 2);
pdata.nvram.nvar.extendedAttributes = extendedAttributes;
// Checksum
if (hasChecksum)
info += QObject::tr("\nChecksum: %1h%2").hexarg2(storedChecksum, 2)
.arg(calculatedChecksum ? QObject::tr(", invalid, should be %1h").hexarg2(0x100 - calculatedChecksum, 2) : QObject::tr(", valid"));
// Extended data
if (!extendedData.isEmpty())
info += QObject::tr("\nExtended data size: %1h (%2)")
.hexarg(extendedData.size()).arg(extendedData.size());
// Authentication data
if (hasTimestampAndHash) {
info += QObject::tr("\nTimestamp: %1h\nHash: %2")
.hexarg2(timestamp, 16).arg(QString(hash.toHex()));
pdata.nvram.nvar.timestamp = timestamp;
memcpy(pdata.nvram.nvar.hash, hash.constData(), 0x20);
}
}
// Add correct offset to parsing data
pdata.offset = parentOffset + offset;
// Add tree item
QModelIndex varIndex = model->addItem(Types::NvramVariableNvar, subtype, name, text, info, header, body, FALSE, parsingDataToQByteArray(pdata), index);
// Show messages
if (msgUnknownExtDataFormat)
msg(QObject::tr("parseNvarStorage: unknown extended data format"), varIndex);
if (msgExtHeaderTooLong)
msg(QObject::tr("parseNvarStorage: extended header size (%1h) is greater than body size (%2h)")
.hexarg(extendedHeaderSize).hexarg(body.size()), varIndex);
if (msgExtDataTooShort)
msg(QObject::tr("parseNvarStorage: extended data size (%1h) is smaller than required for timestamp and hash (0x28)")
.hexarg(extendedData.size()), varIndex);
// Move to next variable
offset += variableHeader->Size;
}
return ERR_SUCCESS;
}

View File

@ -31,6 +31,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include "gbe.h" #include "gbe.h"
#include "me.h" #include "me.h"
#include "fit.h" #include "fit.h"
#include "nvram.h"
class TreeModel; class TreeModel;
@ -105,6 +106,9 @@ private:
STATUS addOffsetsRecursive(const QModelIndex & index); STATUS addOffsetsRecursive(const QModelIndex & index);
STATUS addMemoryAddressesRecursive(const QModelIndex & index, const UINT32 diff); STATUS addMemoryAddressesRecursive(const QModelIndex & index, const UINT32 diff);
// NVRAM parsing
STATUS parseNvarStorage(const QByteArray & data, const QModelIndex & index);
// Message helper // Message helper
void msg(const QString & message, const QModelIndex &index = QModelIndex()); void msg(const QString & message, const QModelIndex &index = QModelIndex());
}; };

50
common/nvram.cpp Normal file
View File

@ -0,0 +1,50 @@
/* nvram.cpp
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
#include <QObject>
#include "nvram.h"
QString variableAttributesToQstring(UINT8 attributes)
{
QString str;
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");
}
return str.mid(2); // Remove the first comma and space
}

64
common/nvram.h Normal file
View File

@ -0,0 +1,64 @@
/* nvram.h
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
#ifndef __NVRAM_H__
#define __NVRAM_H__
#include <QByteArray>
#include <QString>
#include "basetypes.h"
//
// Let's start with NVAR storage, as the most difficult one
//
//CEF5B9A3-476D-497F-9FDC-E98143E0422C
const QByteArray NVRAM_NVAR_FILE_GUID
("\xA3\xB9\xF5\xCE\x6D\x47\x7F\x49\x9F\xDC\xE9\x81\x43\xE0\x42\x2C", 16);
extern QString variableAttributesToQstring(UINT8 attributes);
// Make sure we use right packing rules
#pragma pack(push,1)
// Variable header
typedef struct _NVAR_VARIABLE_HEADER {
UINT32 Signature; // NVAR signature
UINT16 Size; // Size of the variable including header
UINT32 Next : 24; // Offset to the next variable in a list, or empty if latest in the list
UINT32 Attributes : 8; // Attributes
} NVAR_VARIABLE_HEADER;
// NVAR signature
#define NVRAM_NVAR_VARIABLE_SIGNATURE 0x5241564E
// Attributes
#define NVRAM_NVAR_VARIABLE_ATTRIB_RUNTIME 0x01
#define NVRAM_NVAR_VARIABLE_ATTRIB_ASCII_NAME 0x02
#define NVRAM_NVAR_VARIABLE_ATTRIB_GUID 0x04
#define NVRAM_NVAR_VARIABLE_ATTRIB_DATA_ONLY 0x08
#define NVRAM_NVAR_VARIABLE_ATTRIB_EXT_HEADER 0x10
#define NVRAM_NVAR_VARIABLE_ATTRIB_HW_ERROR_RECORD 0x20
#define NVRAM_NVAR_VARIABLE_ATTRIB_AUTH_WRITE 0x40
#define NVRAM_NVAR_VARIABLE_ATTRIB_VALID 0x80
// Extended attributes
#define NVRAM_NVAR_VARIABLE_EXT_ATTRIB_CHECKSUM 0x01
#define NVRAM_NVAR_VARIABLE_EXT_ATTRIB_AUTH_WRITE 0x10
#define NVRAM_NVAR_VARIABLE_EXT_ATTRIB_TIME_BASED 0x20
// Restore previous packing rules
#pragma pack(pop)
#endif

View File

@ -47,8 +47,12 @@ typedef struct _FILE_PARSING_DATA {
UINT16 tail; UINT16 tail;
}; };
BOOLEAN hasTail; BOOLEAN hasTail;
UINT8 format;
} FILE_PARSING_DATA; } FILE_PARSING_DATA;
#define RAW_FILE_FORMAT_UNKNOWN 0
#define RAW_FILE_FORMAT_NVAR_STORAGE 1
typedef struct _COMPRESSED_SECTION_PARSING_DATA { typedef struct _COMPRESSED_SECTION_PARSING_DATA {
UINT32 uncompressedSize; UINT32 uncompressedSize;
UINT8 compressionType; UINT8 compressionType;
@ -78,6 +82,21 @@ typedef struct _SECTION_PARSING_DATA {
}; };
} SECTION_PARSING_DATA; } SECTION_PARSING_DATA;
typedef struct _NVRAM_NVAR_PARSING_DATA {
UINT32 next;
UINT8 attributes;
UINT8 extendedAttributes;
UINT64 timestamp;
UINT8 hash[0x20]; //SHA256
} NVRAM_NVAR_PARSING_DATA;
typedef struct _NVRAM_PARSING_DATA {
//union {
NVRAM_NVAR_PARSING_DATA nvar;
//};
} NVRAM_PARSING_DATA;
typedef struct _PARSING_DATA { typedef struct _PARSING_DATA {
UINT8 emptyByte; UINT8 emptyByte;
UINT8 ffsVersion; UINT8 ffsVersion;
@ -91,6 +110,7 @@ typedef struct _PARSING_DATA {
//FREE_SPACE_PARSING_DATA freeSpace; //FREE_SPACE_PARSING_DATA freeSpace;
FILE_PARSING_DATA file; FILE_PARSING_DATA file;
SECTION_PARSING_DATA section; SECTION_PARSING_DATA section;
NVRAM_PARSING_DATA nvram;
}; };
} PARSING_DATA; } PARSING_DATA;

View File

@ -65,6 +65,8 @@ QString itemTypeToQString(const UINT8 type)
return QObject::tr("Section"); return QObject::tr("Section");
case Types::FreeSpace: case Types::FreeSpace:
return QObject::tr("Free space"); return QObject::tr("Free space");
case Types::NvramVariableNvar:
return QObject::tr("NVAR");
default: default:
return QObject::tr("Unknown"); return QObject::tr("Unknown");
} }
@ -118,6 +120,17 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype)
return sectionTypeToQString(subtype); return sectionTypeToQString(subtype);
case Types::FreeSpace: case Types::FreeSpace:
return QString(); return QString();
case Types::NvramVariableNvar:
if (subtype == Subtypes::InvalidNvar)
return QObject::tr("Invalid");
if (subtype == Subtypes::LinkNvar)
return QObject::tr("Link");
if (subtype == Subtypes::DataNvar)
return QObject::tr("Data");
if (subtype == Subtypes::FullNvar)
return QObject::tr("Full");
else
return QObject::tr("Unknown subtype");
default: default:
return QObject::tr("Unknown subtype"); return QObject::tr("Unknown subtype");
} }

View File

@ -42,7 +42,8 @@ namespace Types {
Volume, Volume,
File, File,
Section, Section,
FreeSpace FreeSpace,
NvramVariableNvar
}; };
} }
@ -83,6 +84,13 @@ namespace Subtypes {
OnePadding, OnePadding,
DataPadding DataPadding
}; };
enum NvarVariableSubtypes {
InvalidNvar = 120,
LinkNvar,
DataNvar,
FullNvar
};
}; };
// *ToQString conversion routines // *ToQString conversion routines

View File

@ -73,7 +73,7 @@ QString errorCodeToQString(UINT8 errorCode)
case ERR_STANDARD_COMPRESSION_FAILED: return QObject::tr("Standard compression failed"); case ERR_STANDARD_COMPRESSION_FAILED: return QObject::tr("Standard compression failed");
case ERR_CUSTOMIZED_COMPRESSION_FAILED: return QObject::tr("Customized compression failed"); case ERR_CUSTOMIZED_COMPRESSION_FAILED: return QObject::tr("Customized compression failed");
case ERR_STANDARD_DECOMPRESSION_FAILED: return QObject::tr("Standard decompression failed"); case ERR_STANDARD_DECOMPRESSION_FAILED: return QObject::tr("Standard decompression failed");
case ERR_CUSTOMIZED_DECOMPRESSION_FAILED: return QObject::tr("Customized compression failed"); case ERR_CUSTOMIZED_DECOMPRESSION_FAILED: return QObject::tr("Customized decompression failed");
case ERR_UNKNOWN_COMPRESSION_TYPE: return QObject::tr("Unknown compression type"); case ERR_UNKNOWN_COMPRESSION_TYPE: return QObject::tr("Unknown compression type");
case ERR_UNKNOWN_EXTRACT_MODE: return QObject::tr("Unknown extract mode"); case ERR_UNKNOWN_EXTRACT_MODE: return QObject::tr("Unknown extract mode");
case ERR_UNKNOWN_REPLACE_MODE: return QObject::tr("Unknown replace mode"); case ERR_UNKNOWN_REPLACE_MODE: return QObject::tr("Unknown replace mode");