mirror of
https://github.com/LongSoft/UEFITool.git
synced 2024-11-25 01:18:22 +08:00
Initial support for VSS format
- normal, auth and apple variations supported - some UI additions and code cleanup TBD
This commit is contained in:
parent
5138a49591
commit
95290abb94
@ -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_alpha22"))
|
version(tr("0.30.0_alpha23"))
|
||||||
{
|
{
|
||||||
clipboard = QApplication::clipboard();
|
clipboard = QApplication::clipboard();
|
||||||
|
|
||||||
@ -175,7 +175,7 @@ void UEFITool::populateUi(const QModelIndex ¤t)
|
|||||||
ui->menuVolumeActions->setEnabled(type == Types::Volume);
|
ui->menuVolumeActions->setEnabled(type == Types::Volume);
|
||||||
ui->menuFileActions->setEnabled(type == Types::File);
|
ui->menuFileActions->setEnabled(type == Types::File);
|
||||||
ui->menuSectionActions->setEnabled(type == Types::Section);
|
ui->menuSectionActions->setEnabled(type == Types::Section);
|
||||||
ui->menuVariableActions->setEnabled(type == Types::NvramVariableNvar);
|
ui->menuVariableActions->setEnabled(type == Types::NvramVariableNvar || type == Types::NvramVariableVss);
|
||||||
|
|
||||||
// Enable actions
|
// Enable actions
|
||||||
ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current));
|
ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current));
|
||||||
@ -505,6 +505,8 @@ void UEFITool::extract(const UINT8 mode)
|
|||||||
|
|
||||||
name = QDir::toNativeSeparators(currentDir + QDir::separator() + name);
|
name = QDir::toNativeSeparators(currentDir + QDir::separator() + name);
|
||||||
|
|
||||||
|
//ui->statusBar->showMessage(name);
|
||||||
|
|
||||||
UINT8 type = model->type(index);
|
UINT8 type = model->type(index);
|
||||||
QString path;
|
QString path;
|
||||||
if (mode == EXTRACT_MODE_AS_IS) {
|
if (mode == EXTRACT_MODE_AS_IS) {
|
||||||
@ -531,7 +533,8 @@ void UEFITool::extract(const UINT8 mode)
|
|||||||
path = QFileDialog::getSaveFileName(this, tr("Save section 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;
|
break;
|
||||||
case Types::NvramVariableNvar:
|
case Types::NvramVariableNvar:
|
||||||
path = QFileDialog::getSaveFileName(this, tr("Save NVAR variable to file"), name + ".nvar", "NVAR variable files (*.nvar *.bin);;All files (*)");
|
case Types::NvramVariableVss:
|
||||||
|
path = QFileDialog::getSaveFileName(this, tr("Save variable to file"), name + ".var", "Variable files (*.var *.bin);;All files (*)");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)");
|
path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)");
|
||||||
@ -566,7 +569,8 @@ void UEFITool::extract(const UINT8 mode)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Types::NvramVariableNvar:
|
case Types::NvramVariableNvar:
|
||||||
path = QFileDialog::getSaveFileName(this, tr("Save NVAR variable body to file"), name + ".bin", "Binary files (*.bin);;All files (*)");
|
case Types::NvramVariableVss:
|
||||||
|
path = QFileDialog::getSaveFileName(this, tr("Save variable body to file"), name + ".bin", "Binary files (*.bin);;All files (*)");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)");
|
path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)");
|
||||||
@ -930,6 +934,7 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event)
|
|||||||
ui->menuSectionActions->exec(event->globalPos());
|
ui->menuSectionActions->exec(event->globalPos());
|
||||||
break;
|
break;
|
||||||
case Types::NvramVariableNvar:
|
case Types::NvramVariableNvar:
|
||||||
|
case Types::NvramVariableVss:
|
||||||
ui->menuVariableActions->exec(event->globalPos());
|
ui->menuVariableActions->exec(event->globalPos());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,7 @@ typedef UINT8 STATUS;
|
|||||||
#define ERR_DIR_CREATE 34
|
#define ERR_DIR_CREATE 34
|
||||||
#define ERR_TRUNCATED_IMAGE 35
|
#define ERR_TRUNCATED_IMAGE 35
|
||||||
#define ERR_INVALID_CAPSULE 36
|
#define ERR_INVALID_CAPSULE 36
|
||||||
|
#define ERR_STORAGES_NOT_FOUND 37
|
||||||
#define ERR_NOT_IMPLEMENTED 0xFF
|
#define ERR_NOT_IMPLEMENTED 0xFF
|
||||||
|
|
||||||
// UDK porting definitions
|
// UDK porting definitions
|
||||||
@ -140,6 +141,21 @@ typedef struct _EFI_GUID {
|
|||||||
UINT8 Data[16];
|
UINT8 Data[16];
|
||||||
} EFI_GUID;
|
} EFI_GUID;
|
||||||
|
|
||||||
|
// EFI Time
|
||||||
|
typedef struct _EFI_TIME {
|
||||||
|
UINT16 Year; // Year: 2000 - 20XX
|
||||||
|
UINT8 Month; // Month: 1 - 12
|
||||||
|
UINT8 Day; // Day: 1 - 31
|
||||||
|
UINT8 Hour; // Hour: 0 - 23
|
||||||
|
UINT8 Minute; // Minute: 0 - 59
|
||||||
|
UINT8 Second; // Second: 0 - 59
|
||||||
|
UINT8: 8;
|
||||||
|
UINT32 Nanosecond; // Nanosecond: 0 - 999,999,999
|
||||||
|
INT16 TimeZone; // TimeZone: -1440 to 1440 or UNSPECIFIED (0x07FF)
|
||||||
|
UINT8 Daylight; // Daylight: ADJUST_DAYLIGHT (1) or IN_DAYLIGHT (2)
|
||||||
|
UINT8: 8;
|
||||||
|
} EFI_TIME;
|
||||||
|
|
||||||
#define ALIGN4(Value) (((Value)+3) & ~3)
|
#define ALIGN4(Value) (((Value)+3) & ~3)
|
||||||
#define ALIGN8(Value) (((Value)+7) & ~7)
|
#define ALIGN8(Value) (((Value)+7) & ~7)
|
||||||
|
|
||||||
@ -148,7 +164,7 @@ typedef struct _EFI_GUID {
|
|||||||
|
|
||||||
//Hexarg macros
|
//Hexarg macros
|
||||||
#define hexarg(X) arg(QString("%1").arg((X),0,16).toUpper())
|
#define hexarg(X) arg(QString("%1").arg((X),0,16).toUpper())
|
||||||
#define hexarg2(X, Y) arg(QString("%1").arg((X),(Y),16,QChar('0')).toUpper())
|
#define hexarg2(X, Y) arg(QString("%1").arg((X),(Y),16,QLatin1Char('0')).toUpper())
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
10
common/ffs.h
10
common/ffs.h
@ -126,20 +126,22 @@ const QByteArray EFI_APPLE_BOOT_VOLUME_FILE_SYSTEM_GUID
|
|||||||
("\xAD\xEE\xAD\x04\xFF\x61\x31\x4D\xB6\xBA\x64\xF8\xBF\x90\x1F\x5A", 16);
|
("\xAD\xEE\xAD\x04\xFF\x61\x31\x4D\xB6\xBA\x64\xF8\xBF\x90\x1F\x5A", 16);
|
||||||
const QByteArray EFI_APPLE_BOOT_VOLUME_FILE_SYSTEM2_GUID
|
const QByteArray EFI_APPLE_BOOT_VOLUME_FILE_SYSTEM2_GUID
|
||||||
("\x8C\x1B\x00\xBD\x71\x6A\x7B\x48\xA1\x4F\x0C\x2A\x2D\xCF\x7A\x5D", 16);
|
("\x8C\x1B\x00\xBD\x71\x6A\x7B\x48\xA1\x4F\x0C\x2A\x2D\xCF\x7A\x5D", 16);
|
||||||
|
|
||||||
|
// AD3FFFFF-D28B-44C4-9F13-9EA98A97F9F0 // Intel 1
|
||||||
const QByteArray EFI_INTEL_FILE_SYSTEM_GUID
|
const QByteArray EFI_INTEL_FILE_SYSTEM_GUID
|
||||||
("\xFF\xFF\x3F\xAD\x8B\xD2\xC4\x44\x9F\x13\x9E\xA9\x8A\x97\xF9\xF0", 16);
|
("\xFF\xFF\x3F\xAD\x8B\xD2\xC4\x44\x9F\x13\x9E\xA9\x8A\x97\xF9\xF0", 16);
|
||||||
// AD3FFFFF-D28B-44C4-9F13-9EA98A97F9F0 // Intel 1
|
// D6A1CD70-4B33-4994-A6EA-375F2CCC5437 // Intel 2
|
||||||
const QByteArray EFI_INTEL_FILE_SYSTEM2_GUID
|
const QByteArray EFI_INTEL_FILE_SYSTEM2_GUID
|
||||||
("\x70\xCD\xA1\xD6\x33\x4B\x94\x49\xA6\xEA\x37\x5F\x2C\xCC\x54\x37", 16);
|
("\x70\xCD\xA1\xD6\x33\x4B\x94\x49\xA6\xEA\x37\x5F\x2C\xCC\x54\x37", 16);
|
||||||
// D6A1CD70-4B33-4994-A6EA-375F2CCC5437 // Intel 2
|
// 4F494156-AED6-4D64-A537-B8A5557BCEEC // Sony 1
|
||||||
const QByteArray EFI_SONY_FILE_SYSTEM_GUID
|
const QByteArray EFI_SONY_FILE_SYSTEM_GUID
|
||||||
("\x56\x41\x49\x4F\xD6\xAE\x64\x4D\xA5\x37\xB8\xA5\x55\x7B\xCE\xEC", 16);
|
("\x56\x41\x49\x4F\xD6\xAE\x64\x4D\xA5\x37\xB8\xA5\x55\x7B\xCE\xEC", 16);
|
||||||
// 4F494156-AED6-4D64-A537-B8A5557BCEEC // Sony 1
|
|
||||||
|
|
||||||
// Vector of volume GUIDs with FFSv2-compatible files
|
// Vector of volume GUIDs with FFSv2-compatible files
|
||||||
extern const std::vector<QByteArray> FFSv2Volumes;
|
extern const std::vector<QByteArray> FFSv2Volumes;
|
||||||
|
|
||||||
const QByteArray EFI_FIRMWARE_FILE_SYSTEM3_GUID // 5473C07A-3DCB-4dca-BD6F-1E9689E7349A
|
const QByteArray EFI_FIRMWARE_FILE_SYSTEM3_GUID // 5473C07A-3DCB-4DCA-BD6F-1E9689E7349A
|
||||||
("\x7A\xC0\x73\x54\xCB\x3D\xCA\x4D\xBD\x6F\x1E\x96\x89\xE7\x34\x9A", 16);
|
("\x7A\xC0\x73\x54\xCB\x3D\xCA\x4D\xBD\x6F\x1E\x96\x89\xE7\x34\x9A", 16);
|
||||||
|
|
||||||
// Vector of volume GUIDs with FFSv3-compatible files
|
// Vector of volume GUIDs with FFSv3-compatible files
|
||||||
|
@ -57,6 +57,7 @@ STATUS FfsOperations::extract(const QModelIndex & index, QString & name, QByteAr
|
|||||||
name = itemName;
|
name = itemName;
|
||||||
} break;
|
} break;
|
||||||
case Types::NvramVariableNvar:
|
case Types::NvramVariableNvar:
|
||||||
|
case Types::NvramVariableVss:
|
||||||
case Types::File: {
|
case Types::File: {
|
||||||
name = itemText.isEmpty() ? itemName : itemText.replace(' ', '_');
|
name = itemText.isEmpty() ? itemName : itemText.replace(' ', '_');
|
||||||
} break;
|
} break;
|
||||||
|
@ -847,7 +847,7 @@ STATUS FfsParser::parseRawArea(const QByteArray & data, const QModelIndex & inde
|
|||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
// First volume is not at the beginning of BIOS space
|
// First volume is not at the beginning of RAW area
|
||||||
QString name;
|
QString name;
|
||||||
QString info;
|
QString info;
|
||||||
if (prevVolumeOffset > 0) {
|
if (prevVolumeOffset > 0) {
|
||||||
@ -928,13 +928,13 @@ STATUS FfsParser::parseRawArea(const QByteArray & data, const QModelIndex & inde
|
|||||||
|
|
||||||
// Parse current volume's header
|
// Parse current volume's header
|
||||||
QModelIndex volumeIndex;
|
QModelIndex volumeIndex;
|
||||||
result = parseVolumeHeader(volume, model->header(index).size() + volumeOffset, index, volumeIndex);
|
result = parseVolumeHeader(volume, headerSize + volumeOffset, index, volumeIndex);
|
||||||
if (result)
|
if (result)
|
||||||
msg(QObject::tr("parseRawArea: volume header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index);
|
msg(QObject::tr("parseRawArea: volume header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index);
|
||||||
else {
|
else {
|
||||||
// Show messages
|
// Show messages
|
||||||
if (volumeSize != bmVolumeSize)
|
if (volumeSize != bmVolumeSize)
|
||||||
msg(QObject::tr("parseBiosBody: volume size stored in header %1h (%2) differs from calculated using block map %3h (%4)")
|
msg(QObject::tr("parseRawArea: volume size stored in header %1h (%2) differs from calculated using block map %3h (%4)")
|
||||||
.hexarg(volumeSize).arg(volumeSize)
|
.hexarg(volumeSize).arg(volumeSize)
|
||||||
.hexarg(bmVolumeSize).arg(bmVolumeSize),
|
.hexarg(bmVolumeSize).arg(bmVolumeSize),
|
||||||
volumeIndex);
|
volumeIndex);
|
||||||
@ -946,7 +946,7 @@ STATUS FfsParser::parseRawArea(const QByteArray & data, const QModelIndex & inde
|
|||||||
result = findNextVolume(index, data, offset, volumeOffset + prevVolumeSize, volumeOffset);
|
result = findNextVolume(index, data, offset, volumeOffset + prevVolumeSize, volumeOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Padding at the end of BIOS space
|
// Padding at the end of RAW area
|
||||||
volumeOffset = prevVolumeOffset + prevVolumeSize;
|
volumeOffset = prevVolumeOffset + prevVolumeSize;
|
||||||
if ((UINT32)data.size() > volumeOffset) {
|
if ((UINT32)data.size() > volumeOffset) {
|
||||||
QByteArray padding = data.mid(volumeOffset);
|
QByteArray padding = data.mid(volumeOffset);
|
||||||
@ -1029,6 +1029,7 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare
|
|||||||
|
|
||||||
// Check for volume structure to be known
|
// Check for volume structure to be known
|
||||||
bool isUnknown = true;
|
bool isUnknown = true;
|
||||||
|
bool isVssNvramVolume = false;
|
||||||
UINT8 ffsVersion = 0;
|
UINT8 ffsVersion = 0;
|
||||||
|
|
||||||
// Check for FFS v2 volume
|
// Check for FFS v2 volume
|
||||||
@ -1044,6 +1045,12 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare
|
|||||||
ffsVersion = 3;
|
ffsVersion = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for VSS NVRAM volume
|
||||||
|
if (guid == NVRAM_VSS_STORAGE_VOLUME_GUID) {
|
||||||
|
isUnknown = false;
|
||||||
|
isVssNvramVolume = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check volume revision and alignment
|
// Check volume revision and alignment
|
||||||
bool msgAlignmentBitsSet = false;
|
bool msgAlignmentBitsSet = false;
|
||||||
bool msgUnaligned = false;
|
bool msgUnaligned = false;
|
||||||
@ -1172,6 +1179,8 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare
|
|||||||
subtype = Subtypes::Ffs2Volume;
|
subtype = Subtypes::Ffs2Volume;
|
||||||
else if (ffsVersion == 3)
|
else if (ffsVersion == 3)
|
||||||
subtype = Subtypes::Ffs3Volume;
|
subtype = Subtypes::Ffs3Volume;
|
||||||
|
else if (isVssNvramVolume)
|
||||||
|
subtype = Subtypes::VssNvramVolume;
|
||||||
}
|
}
|
||||||
index = model->addItem(Types::Volume, subtype, name, text, info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
|
index = model->addItem(Types::Volume, subtype, name, text, info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
|
||||||
|
|
||||||
@ -1214,7 +1223,7 @@ STATUS FfsParser::findNextVolume(const QModelIndex & index, const QByteArray & b
|
|||||||
// All checks passed, volume found
|
// All checks passed, volume found
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// No additional volumes found
|
// No more volumes found
|
||||||
if (nextIndex < EFI_FV_SIGNATURE_OFFSET)
|
if (nextIndex < EFI_FV_SIGNATURE_OFFSET)
|
||||||
return ERR_VOLUMES_NOT_FOUND;
|
return ERR_VOLUMES_NOT_FOUND;
|
||||||
|
|
||||||
@ -1335,6 +1344,10 @@ STATUS FfsParser::parseVolumeBody(const QModelIndex & index)
|
|||||||
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
|
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
|
||||||
UINT32 offset = pdata.offset;
|
UINT32 offset = pdata.offset;
|
||||||
|
|
||||||
|
// Parse VSS NVRAM volumes with a dedicated function
|
||||||
|
if (model->subtype(index) == Subtypes::VssNvramVolume)
|
||||||
|
return parseStorageArea(volumeBody, index);
|
||||||
|
|
||||||
if (pdata.ffsVersion != 2 && pdata.ffsVersion != 3) // Don't parse unknown volumes
|
if (pdata.ffsVersion != 2 && pdata.ffsVersion != 3) // Don't parse unknown volumes
|
||||||
return ERR_SUCCESS;
|
return ERR_SUCCESS;
|
||||||
|
|
||||||
@ -2912,8 +2925,6 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex &
|
|||||||
QByteArray body;
|
QByteArray body;
|
||||||
QByteArray extendedData;
|
QByteArray extendedData;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UINT32 guidAreaSize = guidsInStorage * sizeof(EFI_GUID);
|
UINT32 guidAreaSize = guidsInStorage * sizeof(EFI_GUID);
|
||||||
UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize;
|
UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize;
|
||||||
|
|
||||||
@ -2939,7 +2950,7 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex &
|
|||||||
// Nothing is parsed yet, but the file is not empty
|
// Nothing is parsed yet, but the file is not empty
|
||||||
if (!offset) {
|
if (!offset) {
|
||||||
msg(QObject::tr("parseNvarStorage: file can't be parsed as NVAR variables storage"), index);
|
msg(QObject::tr("parseNvarStorage: file can't be parsed as NVAR variables storage"), index);
|
||||||
return ERR_INVALID_FILE;
|
return ERR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's a padding
|
// It's a padding
|
||||||
@ -3142,7 +3153,7 @@ parsing_done:
|
|||||||
info += QObject::tr("\nAttributes: %1h").hexarg2(variableHeader->Attributes, 2);
|
info += QObject::tr("\nAttributes: %1h").hexarg2(variableHeader->Attributes, 2);
|
||||||
// Translate attributes to text
|
// Translate attributes to text
|
||||||
if (variableHeader->Attributes)
|
if (variableHeader->Attributes)
|
||||||
info += QObject::tr("\nAttributes as text: %1").arg(variableAttributesToQstring(variableHeader->Attributes));
|
info += QObject::tr("\nAttributes as text: %1").arg(nvarAttributesToQString(variableHeader->Attributes));
|
||||||
pdata.nvram.nvar.attributes = variableHeader->Attributes;
|
pdata.nvram.nvar.attributes = variableHeader->Attributes;
|
||||||
|
|
||||||
// Add next node info
|
// Add next node info
|
||||||
@ -3202,3 +3213,478 @@ parsing_done:
|
|||||||
|
|
||||||
return ERR_SUCCESS;
|
return ERR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex & index)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!index.isValid())
|
||||||
|
return ERR_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
// Get parsing data
|
||||||
|
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
|
||||||
|
UINT32 parentOffset = pdata.offset + model->header(index).size();
|
||||||
|
|
||||||
|
// Search for first volume
|
||||||
|
STATUS result;
|
||||||
|
UINT32 prevStorageOffset;
|
||||||
|
|
||||||
|
result = findNextStorage(index, data, parentOffset, 0, prevStorageOffset);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// First storage is not at the beginning of volume body
|
||||||
|
QString name;
|
||||||
|
QString info;
|
||||||
|
if (prevStorageOffset > 0) {
|
||||||
|
// Get info
|
||||||
|
QByteArray padding = data.left(prevStorageOffset);
|
||||||
|
name = QObject::tr("Padding");
|
||||||
|
info = QObject::tr("Full size: %1h (%2)")
|
||||||
|
.hexarg(padding.size()).arg(padding.size());
|
||||||
|
|
||||||
|
// Construct parsing data
|
||||||
|
pdata.offset = parentOffset;
|
||||||
|
|
||||||
|
// Add tree item
|
||||||
|
model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for and parse all storages
|
||||||
|
UINT32 storageOffset = prevStorageOffset;
|
||||||
|
UINT32 prevStorageSize = 0;
|
||||||
|
|
||||||
|
while (!result)
|
||||||
|
{
|
||||||
|
// Padding between storages
|
||||||
|
if (storageOffset > prevStorageOffset + prevStorageSize) {
|
||||||
|
UINT32 paddingOffset = prevStorageOffset + prevStorageSize;
|
||||||
|
UINT32 paddingSize = storageOffset - paddingOffset;
|
||||||
|
QByteArray padding = data.mid(paddingOffset, paddingSize);
|
||||||
|
|
||||||
|
// Get info
|
||||||
|
name = QObject::tr("Padding");
|
||||||
|
info = QObject::tr("Full size: %1h (%2)")
|
||||||
|
.hexarg(padding.size()).arg(padding.size());
|
||||||
|
|
||||||
|
// Construct parsing data
|
||||||
|
pdata.offset = parentOffset + paddingOffset;
|
||||||
|
|
||||||
|
// Add tree item
|
||||||
|
model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get storage size
|
||||||
|
UINT32 storageSize = 0;
|
||||||
|
result = getStorageSize(data, storageOffset, storageSize);
|
||||||
|
if (result) {
|
||||||
|
msg(QObject::tr("parseStorageArea: getVssStorageSize failed with error \"%1\"").arg(errorCodeToQString(result)), index);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that storage is fully present in input
|
||||||
|
if (storageSize > (UINT32)data.size() || storageOffset + storageSize > (UINT32)data.size()) {
|
||||||
|
msg(QObject::tr("parseVssStorageArea: one of storages inside overlaps the end of data"), index);
|
||||||
|
return ERR_INVALID_VOLUME;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray storage = data.mid(storageOffset, storageSize);
|
||||||
|
if (storageSize > (UINT32)storage.size()) {
|
||||||
|
// Mark the rest as padding and finish the parsing
|
||||||
|
QByteArray padding = data.right(storage.size());
|
||||||
|
|
||||||
|
// Get info
|
||||||
|
name = QObject::tr("Padding");
|
||||||
|
info = QObject::tr("Full size: %1h (%2)")
|
||||||
|
.hexarg(padding.size()).arg(padding.size());
|
||||||
|
|
||||||
|
// Construct parsing data
|
||||||
|
pdata.offset = parentOffset + storageOffset;
|
||||||
|
|
||||||
|
// Add tree item
|
||||||
|
QModelIndex paddingIndex = model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
|
||||||
|
msg(QObject::tr("parseStorageArea: one of storages inside overlaps the end of data"), paddingIndex);
|
||||||
|
|
||||||
|
// Update variables
|
||||||
|
prevStorageOffset = storageOffset;
|
||||||
|
prevStorageSize = padding.size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse current volume's header
|
||||||
|
QModelIndex storageIndex;
|
||||||
|
result = parseStorageHeader(storage, parentOffset + storageOffset, index, storageIndex);
|
||||||
|
if (result) {
|
||||||
|
msg(QObject::tr("parseStorageArea: storage header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to next volume
|
||||||
|
prevStorageOffset = storageOffset;
|
||||||
|
prevStorageSize = storageSize;
|
||||||
|
result = findNextStorage(index, data, parentOffset, storageOffset + prevStorageSize, storageOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Padding/free space at the end of volume
|
||||||
|
storageOffset = prevStorageOffset + prevStorageSize;
|
||||||
|
if ((UINT32)data.size() > storageOffset) {
|
||||||
|
QByteArray padding = data.mid(storageOffset);
|
||||||
|
UINT8 type;
|
||||||
|
UINT8 subtype;
|
||||||
|
if (padding.count(pdata.emptyByte) == padding.size()) {
|
||||||
|
// It's a free space
|
||||||
|
name = QObject::tr("Free space");
|
||||||
|
type = Types::FreeSpace;
|
||||||
|
subtype = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Nothing is parsed yet, but the file is not empty
|
||||||
|
if (!storageOffset) {
|
||||||
|
msg(QObject::tr("parseStorageArea: area can't be parsed as storage"), index);
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's a padding
|
||||||
|
name = QObject::tr("Padding");
|
||||||
|
type = Types::Padding;
|
||||||
|
subtype = getPaddingType(padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add info
|
||||||
|
info = QObject::tr("Full size: %1h (%2)")
|
||||||
|
.hexarg(padding.size()).arg(padding.size());
|
||||||
|
|
||||||
|
// Construct parsing data
|
||||||
|
pdata.offset = parentOffset + storageOffset;
|
||||||
|
|
||||||
|
// Add tree item
|
||||||
|
model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse bodies
|
||||||
|
/* for (int i = 0; i < model->rowCount(index); i++) {
|
||||||
|
QModelIndex current = index.child(i, 0);
|
||||||
|
switch (model->type(current)) {
|
||||||
|
case Types::Volume:
|
||||||
|
parseVolumeBody(current);
|
||||||
|
break;
|
||||||
|
case Types::Padding:
|
||||||
|
// No parsing required
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ERR_UNKNOWN_ITEM_TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS FfsParser::findNextStorage(const QModelIndex & index, const QByteArray & data, const UINT32 parentOffset, const UINT32 storageOffset, UINT32 & nextStorageOffset)
|
||||||
|
{
|
||||||
|
UINT32 dataSize = data.size();
|
||||||
|
|
||||||
|
if (dataSize < sizeof(UINT32))
|
||||||
|
return ERR_STORAGES_NOT_FOUND;
|
||||||
|
|
||||||
|
UINT32 offset = storageOffset;
|
||||||
|
for (; offset < dataSize - sizeof(UINT32); offset++) {
|
||||||
|
const UINT32* currentPos = (const UINT32*)(data.constData() + offset);
|
||||||
|
if (*currentPos == NVRAM_VSS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_SVS_STORE_SIGNATURE) { //$VSS or $SVS signatures found, perform checks
|
||||||
|
const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)currentPos;
|
||||||
|
if (vssHeader->Format != NVRAM_VSS_VARIABLE_STORE_FORMATTED) {
|
||||||
|
msg(QObject::tr("findNextStorage: VSS storage candidate at offset %1h skipped, has invalid format %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->Format, 2), index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (vssHeader->Size == 0 || vssHeader->Size == 0xFFFFFFFF) {
|
||||||
|
msg(QObject::tr("findNextStorage: VSS storage candidate at offset %1h skipped, has invalid size %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->Size, 8), index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (vssHeader->State != NVRAM_VSS_VARIABLE_STORE_HEALTHY) {
|
||||||
|
// 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;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
// No more storages found
|
||||||
|
if (offset == dataSize - sizeof(UINT32))
|
||||||
|
return ERR_STORAGES_NOT_FOUND;
|
||||||
|
|
||||||
|
nextStorageOffset = offset;
|
||||||
|
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS FfsParser::getStorageSize(const QByteArray & data, const UINT32 storageOffset, UINT32 & storageSize)
|
||||||
|
{
|
||||||
|
//TODO: add Fsys support
|
||||||
|
const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)(data.constData() + storageOffset);
|
||||||
|
storageSize = vssHeader->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
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get VSS storage header
|
||||||
|
const VSS_VARIABLE_STORE_HEADER* vssStorageHeader = (const VSS_VARIABLE_STORE_HEADER*)storage.constData();
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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 correct offset
|
||||||
|
pdata.offset = parentOffset;
|
||||||
|
|
||||||
|
// Add tree item
|
||||||
|
index = model->addItem(Types::NvramStorageVss, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
|
||||||
|
|
||||||
|
//Parse the storage
|
||||||
|
parseVssStorageBody(body, index);
|
||||||
|
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS FfsParser::parseVssStorageBody(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();
|
||||||
|
|
||||||
|
// Check that the is enough space for variable header
|
||||||
|
const UINT32 dataSize = (UINT32)data.size();
|
||||||
|
if (dataSize < sizeof(VSS_VARIABLE_HEADER)) {
|
||||||
|
msg(QObject::tr("parseVssStorageBody: storage body is too small even for VSS variable header"), index);
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT32 offset = 0;
|
||||||
|
|
||||||
|
// Parse all variables
|
||||||
|
while (1) {
|
||||||
|
bool isInvalid = false;
|
||||||
|
bool isAuthenticated = false;
|
||||||
|
bool isAppleCrc32 = false;
|
||||||
|
|
||||||
|
UINT32 storedCrc32 = 0;
|
||||||
|
UINT32 calculatedCrc32 = 0;
|
||||||
|
UINT64 monotonicCounter = 0;
|
||||||
|
EFI_TIME timestamp = { 0 };
|
||||||
|
UINT32 pubKeyIndex = 0;
|
||||||
|
|
||||||
|
UINT8 subtype = 0;
|
||||||
|
QString name;
|
||||||
|
QString text;
|
||||||
|
EFI_GUID* variableGuid;
|
||||||
|
CHAR16* variableName;
|
||||||
|
QByteArray header;
|
||||||
|
QByteArray body;
|
||||||
|
|
||||||
|
UINT32 unparsedSize = dataSize - offset;
|
||||||
|
|
||||||
|
// Get variable header
|
||||||
|
const VSS_VARIABLE_HEADER* variableHeader = (const VSS_VARIABLE_HEADER*)(data.constData() + offset);
|
||||||
|
|
||||||
|
// Check variable header to fit in still unparsed data
|
||||||
|
UINT32 variableSize = 0;
|
||||||
|
if (unparsedSize >= sizeof(VSS_VARIABLE_HEADER)
|
||||||
|
&& variableHeader->StartId == NVRAM_VSS_VARIABLE_START_ID) {
|
||||||
|
|
||||||
|
// Apple VSS variable with CRC32 of the data
|
||||||
|
if (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPLE_DATA_CHECKSUM) {
|
||||||
|
isAppleCrc32 = true;
|
||||||
|
if (unparsedSize < sizeof(VSS_APPLE_VARIABLE_HEADER)) {
|
||||||
|
variableSize = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const VSS_APPLE_VARIABLE_HEADER* appleVariableHeader = (const VSS_APPLE_VARIABLE_HEADER*)variableHeader;
|
||||||
|
variableSize = sizeof(VSS_APPLE_VARIABLE_HEADER) + appleVariableHeader->NameSize + appleVariableHeader->DataSize;
|
||||||
|
variableGuid = (EFI_GUID*)&appleVariableHeader->VendorGuid;
|
||||||
|
variableName = (CHAR16*)(appleVariableHeader + 1);
|
||||||
|
|
||||||
|
header = data.mid(offset, sizeof(VSS_APPLE_VARIABLE_HEADER) + appleVariableHeader->NameSize);
|
||||||
|
body = data.mid(offset + header.size(), appleVariableHeader->DataSize);
|
||||||
|
|
||||||
|
// Calculate CRC32 of the variable data
|
||||||
|
storedCrc32 = appleVariableHeader->DataCrc32;
|
||||||
|
calculatedCrc32 = crc32(0, (const UINT8*)body.constData(), body.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticated variable
|
||||||
|
else if ((variableHeader->Attributes & NVRAM_VSS_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
|
||||||
|
|| (variableHeader->Attributes & NVRAM_VSS_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
|
||||||
|
|| (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPEND_WRITE)
|
||||||
|
|| (variableHeader->NameSize == 0 && variableHeader->DataSize == 0)) { // If both NameSize and DataSize are zeros, it's auth variable with zero montonic counter
|
||||||
|
isAuthenticated = true;
|
||||||
|
if (unparsedSize < sizeof(VSS_AUTH_VARIABLE_HEADER)) {
|
||||||
|
variableSize = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const VSS_AUTH_VARIABLE_HEADER* authVariableHeader = (const VSS_AUTH_VARIABLE_HEADER*)variableHeader;
|
||||||
|
variableSize = sizeof(VSS_AUTH_VARIABLE_HEADER) + authVariableHeader->NameSize + authVariableHeader->DataSize;
|
||||||
|
variableGuid = (EFI_GUID*)&authVariableHeader->VendorGuid;
|
||||||
|
variableName = (CHAR16*)(authVariableHeader + 1);
|
||||||
|
|
||||||
|
header = data.mid(offset, sizeof(VSS_AUTH_VARIABLE_HEADER) + authVariableHeader->NameSize);
|
||||||
|
body = data.mid(offset + header.size(), authVariableHeader->DataSize);
|
||||||
|
|
||||||
|
monotonicCounter = authVariableHeader->MonotonicCounter;
|
||||||
|
timestamp = authVariableHeader->Timestamp;
|
||||||
|
pubKeyIndex = authVariableHeader->PubKeyIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal VSS variable
|
||||||
|
if (!isAuthenticated && !isAppleCrc32) {
|
||||||
|
variableSize = sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize + variableHeader->DataSize;
|
||||||
|
variableGuid = (EFI_GUID*)&variableHeader->VendorGuid;
|
||||||
|
variableName = (CHAR16*)(variableHeader + 1);
|
||||||
|
|
||||||
|
header = data.mid(offset, sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize);
|
||||||
|
body = data.mid(offset + header.size(), variableHeader->DataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is also a case of authenticated Apple variables, but I haven't seen one yet
|
||||||
|
|
||||||
|
// Check variable state
|
||||||
|
if (variableHeader->State != NVRAM_VSS_VARIABLE_ADDED && variableHeader->State != NVRAM_VSS_VARIABLE_HEADER_VALID) {
|
||||||
|
isInvalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't parse further, add the last element and break the loop
|
||||||
|
if (!variableSize) {
|
||||||
|
// 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 {
|
||||||
|
// Nothing is parsed yet, but the storage is not empty
|
||||||
|
if (!offset) {
|
||||||
|
msg(QObject::tr("parseVssStorageBody: storage can't be parsed as VSS storage"), index);
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString info;
|
||||||
|
|
||||||
|
// Rename deleted variables
|
||||||
|
if (isInvalid) {
|
||||||
|
name = QObject::tr("Invalid");
|
||||||
|
}
|
||||||
|
else { // Add GUID and text for valid variables
|
||||||
|
name = guidToQString(*variableGuid);
|
||||||
|
info += QObject::tr("Variable GUID: %1\n").arg(name);
|
||||||
|
text = QString::fromUtf16(variableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add header, body and extended data info
|
||||||
|
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 state info
|
||||||
|
info += QObject::tr("\nState: %1h").hexarg2(variableHeader->State, 2);
|
||||||
|
|
||||||
|
// Add attributes info
|
||||||
|
info += QObject::tr("\nAttributes: %1h").hexarg2(variableHeader->Attributes, 8);
|
||||||
|
|
||||||
|
// Set subtype and add related info
|
||||||
|
if (isInvalid)
|
||||||
|
subtype = Subtypes::InvalidVss;
|
||||||
|
else if (isAuthenticated) {
|
||||||
|
subtype = Subtypes::AuthVss;
|
||||||
|
info += QObject::tr("\nMonotonic counter: %1h\nTimestamp: %2\nPubKey index: %3")
|
||||||
|
.hexarg(monotonicCounter).arg(efiTimeToQString(timestamp)).arg(pubKeyIndex);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (isAppleCrc32) {
|
||||||
|
subtype = Subtypes::AppleCrc32Vss;
|
||||||
|
info += QObject::tr("\nCRC32: %1h%2").hexarg2(storedCrc32, 8)
|
||||||
|
.arg(storedCrc32 == calculatedCrc32 ? QObject::tr(", valid") : QObject::tr(", invalid, should be %1h").hexarg2(calculatedCrc32,8));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
subtype = Subtypes::StandardVss;
|
||||||
|
|
||||||
|
// Add correct offset to parsing data
|
||||||
|
pdata.offset = parentOffset + offset;
|
||||||
|
|
||||||
|
// Add tree item
|
||||||
|
QModelIndex varIndex = model->addItem(Types::NvramVariableVss, subtype, name, text, info, header, body, FALSE, parsingDataToQByteArray(pdata), index);
|
||||||
|
|
||||||
|
// Move to next variable
|
||||||
|
offset += variableSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
@ -109,6 +109,12 @@ private:
|
|||||||
// NVRAM parsing
|
// NVRAM parsing
|
||||||
STATUS parseNvarStorage(const QByteArray & data, const QModelIndex & index);
|
STATUS parseNvarStorage(const QByteArray & data, const QModelIndex & index);
|
||||||
|
|
||||||
|
STATUS parseStorageArea(const QByteArray & data, const QModelIndex & index);
|
||||||
|
STATUS findNextStorage(const QModelIndex & index, const QByteArray & data, const UINT32 parentOffset, const UINT32 storageOffset, UINT32 & nextStorageOffset);
|
||||||
|
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 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());
|
||||||
};
|
};
|
||||||
|
@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include "nvram.h"
|
#include "nvram.h"
|
||||||
|
|
||||||
QString variableAttributesToQstring(UINT8 attributes)
|
QString nvarAttributesToQString(const UINT8 attributes)
|
||||||
{
|
{
|
||||||
if (attributes == 0x00 || attributes == 0xFF)
|
if (attributes == 0x00 || attributes == 0xFF)
|
||||||
return QString();
|
return QString();
|
||||||
@ -38,6 +38,17 @@ QString variableAttributesToQstring(UINT8 attributes)
|
|||||||
if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_VALID)
|
if (attributes & NVRAM_NVAR_VARIABLE_ATTRIB_VALID)
|
||||||
str += QObject::tr(", Valid");
|
str += QObject::tr(", Valid");
|
||||||
|
|
||||||
return str.mid(2); // Remove the first comma and space
|
return str.mid(2); // Remove first comma and space
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString efiTimeToQString(const EFI_TIME & time)
|
||||||
|
{
|
||||||
|
return QObject::tr("%1-%2-%3T%4:%5:%6.%7")
|
||||||
|
.arg(time.Year, 4, 10, QLatin1Char('0'))
|
||||||
|
.arg(time.Month, 2, 10, QLatin1Char('0'))
|
||||||
|
.arg(time.Day, 2, 10, QLatin1Char('0'))
|
||||||
|
.arg(time.Hour, 2, 10, QLatin1Char('0'))
|
||||||
|
.arg(time.Minute, 2, 10, QLatin1Char('0'))
|
||||||
|
.arg(time.Second, 2, 10, QLatin1Char('0'))
|
||||||
|
.arg(time.Nanosecond);
|
||||||
|
}
|
||||||
|
108
common/nvram.h
108
common/nvram.h
@ -31,7 +31,9 @@ const QByteArray NVRAM_NVAR_STORAGE_FILE_GUID
|
|||||||
const QByteArray NVRAM_NVAR_EXTERNAL_DEFAULTS_FILE_GUID
|
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);
|
("\x5B\x31\x21\x92\xBB\x30\xB5\x46\x81\x3E\x1B\x1B\xF4\x71\x2B\xD3", 16);
|
||||||
|
|
||||||
extern QString variableAttributesToQstring(UINT8 attributes);
|
extern QString nvarAttributesToQString(const UINT8 attributes);
|
||||||
|
|
||||||
|
extern QString efiTimeToQString(const EFI_TIME & time);
|
||||||
|
|
||||||
// Make sure we use right packing rules
|
// Make sure we use right packing rules
|
||||||
#pragma pack(push,1)
|
#pragma pack(push,1)
|
||||||
@ -62,6 +64,110 @@ typedef struct _NVAR_VARIABLE_HEADER {
|
|||||||
#define NVRAM_NVAR_VARIABLE_EXT_ATTRIB_AUTH_WRITE 0x10
|
#define NVRAM_NVAR_VARIABLE_EXT_ATTRIB_AUTH_WRITE 0x10
|
||||||
#define NVRAM_NVAR_VARIABLE_EXT_ATTRIB_TIME_BASED 0x20
|
#define NVRAM_NVAR_VARIABLE_EXT_ATTRIB_TIME_BASED 0x20
|
||||||
|
|
||||||
|
//
|
||||||
|
// Next format is TianoCore VSS and it's variations
|
||||||
|
//
|
||||||
|
|
||||||
|
// FFF12B8D-7696-4C8B-A985-2747075B4F50
|
||||||
|
const QByteArray NVRAM_VSS_STORAGE_VOLUME_GUID
|
||||||
|
("\x8D\x2B\xF1\xFF\x96\x76\x8B\x4C\xA9\x85\x27\x47\x07\x5B\x4F\x50", 16);
|
||||||
|
|
||||||
|
#define NVRAM_VSS_STORE_SIGNATURE 0x53535624 // $VSS
|
||||||
|
#define NVRAM_APPLE_SVS_STORE_SIGNATURE 0x53565324 // $SVS
|
||||||
|
#define NVRAM_APPLE_FSYS_STORE_SIGNATURE 0x73797346 // Fsys
|
||||||
|
#define NVRAM_VSS_VARIABLE_START_ID 0x55AA
|
||||||
|
|
||||||
|
// Variable store header flags
|
||||||
|
#define NVRAM_VSS_VARIABLE_STORE_FORMATTED 0x5a
|
||||||
|
#define NVRAM_VSS_VARIABLE_STORE_HEALTHY 0xfe
|
||||||
|
|
||||||
|
// Variable store status
|
||||||
|
#define NVRAM_VSS_VARIABLE_STORE_STATUS_RAW 0
|
||||||
|
#define NVRAM_VSS_VARIABLE_STORE_STATUS_VALID 1
|
||||||
|
#define NVRAM_VSS_VARIABLE_STORE_STATUS_INVALID 2
|
||||||
|
#define NVRAM_VSS_VARIABLE_STORE_STATUS_UNKNOWN 3
|
||||||
|
|
||||||
|
// Variable store header
|
||||||
|
typedef struct _VSS_VARIABLE_STORE_HEADER {
|
||||||
|
UINT32 Signature; // $VSS signature
|
||||||
|
UINT32 Size; // Size of variable storage, including storage header
|
||||||
|
UINT8 Format; // Storage format state
|
||||||
|
UINT8 State; // Storage health state
|
||||||
|
UINT16 Unknown; // Used in Apple $SVS varstores
|
||||||
|
UINT32 : 32;
|
||||||
|
} 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
|
||||||
|
} APPLE_FSYS_STORE_HEADER;
|
||||||
|
|
||||||
|
// Apple Fsys variable format
|
||||||
|
// UINT8 NameLength;
|
||||||
|
// CHAR8 Name[];
|
||||||
|
// UINT16 DataLength;
|
||||||
|
// UINT8 Data[]
|
||||||
|
// End with a chunk named "EOF" without data
|
||||||
|
// All free bytes are zeros
|
||||||
|
// Has CRC32 of the whole store without checksum field at the end
|
||||||
|
|
||||||
|
// Normal variable header
|
||||||
|
typedef struct _VSS_VARIABLE_HEADER {
|
||||||
|
UINT16 StartId; // Variable start marker AA55
|
||||||
|
UINT8 State; // Variable state
|
||||||
|
UINT8 : 8;
|
||||||
|
UINT32 Attributes; // Variable attributes
|
||||||
|
UINT32 NameSize; // Size of variable name, null-terminated UCS2 string
|
||||||
|
UINT32 DataSize; // Size of variable data without header and name
|
||||||
|
EFI_GUID VendorGuid; // Variable vendor GUID
|
||||||
|
} VSS_VARIABLE_HEADER;
|
||||||
|
|
||||||
|
// Apple variation of normal variable header, with one new field
|
||||||
|
typedef struct _VSS_APPLE_VARIABLE_HEADER {
|
||||||
|
UINT16 StartId; // Variable start marker AA55
|
||||||
|
UINT8 State; // Variable state
|
||||||
|
UINT8 : 8;
|
||||||
|
UINT32 Attributes; // Variable attributes
|
||||||
|
UINT32 NameSize; // Size of variable name, null-terminated UCS2 string
|
||||||
|
UINT32 DataSize; // Size of variable data without header and name
|
||||||
|
EFI_GUID VendorGuid; // Variable vendor GUID
|
||||||
|
UINT32 DataCrc32; // CRC32 of the data
|
||||||
|
} VSS_APPLE_VARIABLE_HEADER;
|
||||||
|
|
||||||
|
// Authenticated variable header, used for SecureBoot vars
|
||||||
|
typedef struct _VSS_AUTH_VARIABLE_HEADER {
|
||||||
|
UINT16 StartId; // Variable start marker AA55
|
||||||
|
UINT8 State; // Variable state
|
||||||
|
UINT8 : 8;
|
||||||
|
UINT32 Attributes; // Variable attributes
|
||||||
|
UINT64 MonotonicCounter; // Monotonic counter against replay attack
|
||||||
|
EFI_TIME Timestamp; // Time stamp against replay attack
|
||||||
|
UINT32 PubKeyIndex; // Index in PubKey database
|
||||||
|
UINT32 NameSize; // Size of variable name, null-terminated UCS2 string
|
||||||
|
UINT32 DataSize; // Size of variable data without header and name
|
||||||
|
EFI_GUID VendorGuid; // Variable vendor GUID
|
||||||
|
} VSS_AUTH_VARIABLE_HEADER;
|
||||||
|
|
||||||
|
// VSS variable states
|
||||||
|
#define NVRAM_VSS_VARIABLE_IN_DELETED_TRANSITION 0xfe // Variable is in obsolete transistion
|
||||||
|
#define NVRAM_VSS_VARIABLE_DELETED 0xfd // Variable is obsolete
|
||||||
|
#define NVRAM_VSS_VARIABLE_HEADER_VALID 0x7f // Variable has valid header
|
||||||
|
#define NVRAM_VSS_VARIABLE_ADDED 0x3f // Variable has been completely added
|
||||||
|
#define NVRAM_VSS_IS_VARIABLE_STATE(_c, _Mask) (BOOLEAN) (((~_c) & (~_Mask)) != 0)
|
||||||
|
|
||||||
|
// VSS variable attributes
|
||||||
|
#define NVRAM_VSS_VARIABLE_NON_VOLATILE 0x00000001
|
||||||
|
#define NVRAM_VSS_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
|
||||||
|
#define NVRAM_VSS_VARIABLE_RUNTIME_ACCESS 0x00000004
|
||||||
|
#define NVRAM_VSS_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008
|
||||||
|
#define NVRAM_VSS_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010
|
||||||
|
#define NVRAM_VSS_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x00000020
|
||||||
|
#define NVRAM_VSS_VARIABLE_APPEND_WRITE 0x00000040
|
||||||
|
#define NVRAM_VSS_VARIABLE_APPLE_DATA_CHECKSUM 0x80000000
|
||||||
|
|
||||||
// Restore previous packing rules
|
// Restore previous packing rules
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
@ -66,7 +66,11 @@ QString itemTypeToQString(const UINT8 type)
|
|||||||
case Types::FreeSpace:
|
case Types::FreeSpace:
|
||||||
return QObject::tr("Free space");
|
return QObject::tr("Free space");
|
||||||
case Types::NvramVariableNvar:
|
case Types::NvramVariableNvar:
|
||||||
return QObject::tr("NVAR");
|
return QObject::tr("NVAR variable");
|
||||||
|
case Types::NvramStorageVss:
|
||||||
|
return QObject::tr("VSS storage");
|
||||||
|
case Types::NvramVariableVss:
|
||||||
|
return QObject::tr("VSS variable");
|
||||||
default:
|
default:
|
||||||
return QObject::tr("Unknown");
|
return QObject::tr("Unknown");
|
||||||
}
|
}
|
||||||
@ -99,6 +103,8 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype)
|
|||||||
return QObject::tr("FFSv2");
|
return QObject::tr("FFSv2");
|
||||||
else if (subtype == Subtypes::Ffs3Volume)
|
else if (subtype == Subtypes::Ffs3Volume)
|
||||||
return QObject::tr("FFSv3");
|
return QObject::tr("FFSv3");
|
||||||
|
else if (subtype == Subtypes::VssNvramVolume)
|
||||||
|
return QObject::tr("VSS NVRAM");
|
||||||
else
|
else
|
||||||
return QObject::tr("Unknown subtype");
|
return QObject::tr("Unknown subtype");
|
||||||
case Types::Capsule:
|
case Types::Capsule:
|
||||||
@ -133,6 +139,19 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype)
|
|||||||
return QObject::tr("Full");
|
return QObject::tr("Full");
|
||||||
else
|
else
|
||||||
return QObject::tr("Unknown subtype");
|
return QObject::tr("Unknown subtype");
|
||||||
|
case Types::NvramStorageVss:
|
||||||
|
return QString();
|
||||||
|
case Types::NvramVariableVss:
|
||||||
|
if (subtype == Subtypes::InvalidVss)
|
||||||
|
return QObject::tr("Invalid");
|
||||||
|
if (subtype == Subtypes::StandardVss)
|
||||||
|
return QObject::tr("Standard");
|
||||||
|
if (subtype == Subtypes::AppleCrc32Vss)
|
||||||
|
return QObject::tr("Apple CRC32");
|
||||||
|
if (subtype == Subtypes::AuthVss)
|
||||||
|
return QObject::tr("Auth");
|
||||||
|
else
|
||||||
|
return QObject::tr("Unknown subtype");
|
||||||
default:
|
default:
|
||||||
return QObject::tr("Unknown subtype");
|
return QObject::tr("Unknown subtype");
|
||||||
}
|
}
|
||||||
@ -162,7 +181,7 @@ QString actionTypeToQString(const UINT8 action)
|
|||||||
{
|
{
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case Actions::NoAction:
|
case Actions::NoAction:
|
||||||
return "";
|
return QString();
|
||||||
case Actions::Create:
|
case Actions::Create:
|
||||||
return QObject::tr("Create");
|
return QObject::tr("Create");
|
||||||
case Actions::Insert:
|
case Actions::Insert:
|
||||||
|
@ -43,7 +43,9 @@ namespace Types {
|
|||||||
File,
|
File,
|
||||||
Section,
|
Section,
|
||||||
FreeSpace,
|
FreeSpace,
|
||||||
NvramVariableNvar
|
NvramVariableNvar,
|
||||||
|
NvramStorageVss,
|
||||||
|
NvramVariableVss
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +65,8 @@ namespace Subtypes {
|
|||||||
enum VolumeSubtypes {
|
enum VolumeSubtypes {
|
||||||
UnknownVolume = 90,
|
UnknownVolume = 90,
|
||||||
Ffs2Volume,
|
Ffs2Volume,
|
||||||
Ffs3Volume
|
Ffs3Volume,
|
||||||
|
VssNvramVolume
|
||||||
};
|
};
|
||||||
|
|
||||||
enum RegionSubtypes {
|
enum RegionSubtypes {
|
||||||
@ -92,6 +95,13 @@ namespace Subtypes {
|
|||||||
DataNvar,
|
DataNvar,
|
||||||
FullNvar
|
FullNvar
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum VssVariableSubtypes {
|
||||||
|
InvalidVss = 130,
|
||||||
|
StandardVss,
|
||||||
|
AppleCrc32Vss,
|
||||||
|
AuthVss
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// *ToQString conversion routines
|
// *ToQString conversion routines
|
||||||
|
@ -32,6 +32,7 @@ PARSING_DATA parsingDataFromQModelIndex(const QModelIndex & index)
|
|||||||
data.offset = 0;
|
data.offset = 0;
|
||||||
data.address = 0;
|
data.address = 0;
|
||||||
data.ffsVersion = 0; // Unknown by default
|
data.ffsVersion = 0; // Unknown by default
|
||||||
|
data.emptyByte = 0xFF; // Default value for SPI flash
|
||||||
|
|
||||||
// Type-specific parts remain unitialized
|
// Type-specific parts remain unitialized
|
||||||
return data;
|
return data;
|
||||||
@ -94,6 +95,7 @@ QString errorCodeToQString(UINT8 errorCode)
|
|||||||
case ERR_DEPEX_PARSE_FAILED: return QObject::tr("Dependency expression parsing failed");
|
case ERR_DEPEX_PARSE_FAILED: return QObject::tr("Dependency expression parsing failed");
|
||||||
case ERR_TRUNCATED_IMAGE: return QObject::tr("Image is truncated");
|
case ERR_TRUNCATED_IMAGE: return QObject::tr("Image is truncated");
|
||||||
case ERR_INVALID_CAPSULE: return QObject::tr("Invalid capsule");
|
case ERR_INVALID_CAPSULE: return QObject::tr("Invalid capsule");
|
||||||
|
case ERR_STORAGES_NOT_FOUND: return QObject::tr("Storages not found");
|
||||||
default: return QObject::tr("Unknown error %1").arg(errorCode);
|
default: return QObject::tr("Unknown error %1").arg(errorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user