mirror of
https://github.com/LongSoft/UEFITool.git
synced 2024-11-27 02:18:22 +08:00
1955 lines
86 KiB
C++
1955 lines
86 KiB
C++
/* nvramparser.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 <inttypes.h>
|
|
#include <map>
|
|
|
|
#include "nvramparser.h"
|
|
#include "parsingdata.h"
|
|
#include "utility.h"
|
|
#include "nvram.h"
|
|
#include "ffs.h"
|
|
#include "fit.h"
|
|
|
|
#ifdef U_ENABLE_NVRAM_PARSING_SUPPORT
|
|
USTATUS NvramParser::parseNvarStore(const UModelIndex & index)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// Obtain required information from parent file
|
|
UINT8 emptyByte = 0xFF;
|
|
UModelIndex parentFileIndex = model->findParentOfType(index, Types::File);
|
|
if (parentFileIndex.isValid() && model->hasEmptyParsingData(parentFileIndex) == false) {
|
|
UByteArray data = model->parsingData(parentFileIndex);
|
|
const FILE_PARSING_DATA* pdata = (const FILE_PARSING_DATA*)data.constData();
|
|
emptyByte = pdata->emptyByte;
|
|
}
|
|
|
|
// Rename parent file
|
|
model->setText(parentFileIndex, UString("NVAR store"));
|
|
|
|
// Get local offset
|
|
UINT32 localOffset = model->offset(index) + model->header(index).size();
|
|
|
|
// Get item data
|
|
const UByteArray data = model->body(index);
|
|
|
|
// Parse all entries
|
|
UINT32 offset = 0;
|
|
UINT32 guidsInStore = 0;
|
|
while (1) {
|
|
bool msgUnknownExtDataFormat = false;
|
|
bool msgExtHeaderTooLong = false;
|
|
bool msgExtDataTooShort = false;
|
|
|
|
bool isInvalid = false;
|
|
bool isInvalidLink = false;
|
|
bool hasExtendedHeader = false;
|
|
bool hasChecksum = false;
|
|
bool hasTimestamp = false;
|
|
bool hasHash = false;
|
|
bool hasGuidIndex = false;
|
|
|
|
UINT32 guidIndex = 0;
|
|
UINT8 storedChecksum = 0;
|
|
UINT8 calculatedChecksum = 0;
|
|
UINT32 extendedHeaderSize = 0;
|
|
UINT8 extendedAttributes = 0;
|
|
UINT64 timestamp = 0;
|
|
UByteArray hash;
|
|
|
|
UINT8 subtype = Subtypes::FullNvarEntry;
|
|
UString name;
|
|
UString guid;
|
|
UString text;
|
|
UByteArray header;
|
|
UByteArray body;
|
|
UByteArray tail;
|
|
|
|
UINT32 guidAreaSize = guidsInStore * sizeof(EFI_GUID);
|
|
UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize;
|
|
|
|
// Get entry header
|
|
const NVAR_ENTRY_HEADER* entryHeader = (const NVAR_ENTRY_HEADER*)(data.constData() + offset);
|
|
|
|
// Check header size and signature
|
|
if (unparsedSize < sizeof(NVAR_ENTRY_HEADER) ||
|
|
entryHeader->Signature != NVRAM_NVAR_ENTRY_SIGNATURE ||
|
|
unparsedSize < entryHeader->Size) {
|
|
// Check if the data left is a free space or a padding
|
|
UByteArray padding = data.mid(offset, unparsedSize);
|
|
|
|
// Get info
|
|
UString info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
|
|
|
if ((UINT32)padding.count(emptyByte) == unparsedSize) { // Free space
|
|
// Add tree item
|
|
model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Movable, index);
|
|
}
|
|
else {
|
|
// Nothing is parsed yet, but the file is not empty
|
|
if (!offset) {
|
|
msg(usprintf("%s: file can't be parsed as NVAR variables store", __FUNCTION__), index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset + offset, Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
|
|
}
|
|
|
|
// Add GUID store area
|
|
UByteArray guidArea = data.right(guidAreaSize);
|
|
// Get info
|
|
name = UString("GUID store");
|
|
info = usprintf("Full size: %Xh (%u)\nGUIDs in store: %u",
|
|
guidArea.size(), guidArea.size(),
|
|
guidsInStore);
|
|
// Add tree item
|
|
model->addItem(localOffset + offset + padding.size(), Types::Padding, getPaddingType(guidArea), name, UString(), info, UByteArray(), guidArea, UByteArray(), Fixed, index);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Contruct generic header and body
|
|
header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER));
|
|
body = data.mid(offset + sizeof(NVAR_ENTRY_HEADER), entryHeader->Size - sizeof(NVAR_ENTRY_HEADER));
|
|
|
|
UINT32 lastVariableFlag = emptyByte ? 0xFFFFFF : 0;
|
|
|
|
// Set default next to predefined last value
|
|
NVAR_ENTRY_PARSING_DATA pdata;
|
|
pdata.emptyByte = emptyByte;
|
|
pdata.next = lastVariableFlag;
|
|
|
|
// Entry is marked as invalid
|
|
if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_VALID) == 0) { // Valid attribute is not set
|
|
isInvalid = true;
|
|
// Do not parse further
|
|
goto parsing_done;
|
|
}
|
|
|
|
// Add next node information to parsing data
|
|
if (entryHeader->Next != lastVariableFlag) {
|
|
subtype = Subtypes::LinkNvarEntry;
|
|
pdata.next = entryHeader->Next;
|
|
}
|
|
|
|
// Entry with extended header
|
|
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_EXT_HEADER) {
|
|
hasExtendedHeader = true;
|
|
msgUnknownExtDataFormat = true;
|
|
|
|
extendedHeaderSize = *(UINT16*)(body.constData() + body.size() - sizeof(UINT16));
|
|
if (extendedHeaderSize > (UINT32)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_ENTRY_EXT_CHECKSUM) {
|
|
// Get stored checksum
|
|
storedChecksum = *(UINT8*)(body.constData() + body.size() - sizeof(UINT16) - sizeof(UINT8));
|
|
|
|
// Recalculate checksum for the variable
|
|
calculatedChecksum = 0;
|
|
// Include entry data
|
|
UINT8* start = (UINT8*)(entryHeader + 1);
|
|
for (UINT8* p = start; p < start + entryHeader->Size - sizeof(NVAR_ENTRY_HEADER); p++) {
|
|
calculatedChecksum += *p;
|
|
}
|
|
// Include entry size and flags
|
|
start = (UINT8*)&entryHeader->Size;
|
|
for (UINT8*p = start; p < start + sizeof(UINT16); p++) {
|
|
calculatedChecksum += *p;
|
|
}
|
|
// Include entry attributes
|
|
calculatedChecksum += entryHeader->Attributes;
|
|
|
|
hasChecksum = true;
|
|
msgUnknownExtDataFormat = false;
|
|
}
|
|
|
|
tail = body.mid(body.size() - extendedHeaderSize);
|
|
body = body.left(body.size() - extendedHeaderSize);
|
|
|
|
// Entry with authenticated write (for SecureBoot)
|
|
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_AUTH_WRITE) {
|
|
if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY)) {// Data only auth. variables has no hash
|
|
if ((UINT32)tail.size() < sizeof(UINT64)) {
|
|
msgExtDataTooShort = true;
|
|
isInvalid = true;
|
|
// Do not parse further
|
|
goto parsing_done;
|
|
}
|
|
|
|
timestamp = *(UINT64*)(tail.constData() + sizeof(UINT8));
|
|
hasTimestamp = true;
|
|
msgUnknownExtDataFormat = false;
|
|
}
|
|
else { // Full or link variable have hash
|
|
if ((UINT32)tail.size() < sizeof(UINT64) + SHA256_HASH_SIZE) {
|
|
msgExtDataTooShort = true;
|
|
isInvalid = true;
|
|
// Do not parse further
|
|
goto parsing_done;
|
|
}
|
|
|
|
timestamp = *(UINT64*)(tail.constData() + sizeof(UINT8));
|
|
hash = tail.mid(sizeof(UINT64) + sizeof(UINT8), SHA256_HASH_SIZE);
|
|
hasTimestamp = true;
|
|
hasHash = true;
|
|
msgUnknownExtDataFormat = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Entry is data-only (nameless and GUIDless entry or link)
|
|
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY) { // Data-only attribute is set
|
|
isInvalidLink = true;
|
|
UModelIndex nvarIndex;
|
|
// Search prevously added entries for a link to this variable
|
|
// WARNING: O(n^2), may be very slow
|
|
for (int i = model->rowCount(index) - 1; i >= 0; i--) {
|
|
nvarIndex = index.child(i, 0);
|
|
if (model->hasEmptyParsingData(nvarIndex) == false) {
|
|
UByteArray nvarData = model->parsingData(nvarIndex);
|
|
const NVAR_ENTRY_PARSING_DATA* nvarPdata = (const NVAR_ENTRY_PARSING_DATA*)nvarData.constData();
|
|
if (nvarPdata->isValid && nvarPdata->next + model->offset(nvarIndex) - localOffset == offset) { // Previous link is present and valid
|
|
isInvalidLink = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Check if the link is valid
|
|
if (!isInvalidLink) {
|
|
// Use the name and text of the previous link
|
|
name = model->name(nvarIndex);
|
|
text = model->text(nvarIndex);
|
|
|
|
if (entryHeader->Next == lastVariableFlag)
|
|
subtype = Subtypes::DataNvarEntry;
|
|
}
|
|
|
|
//isDataOnly = true;
|
|
// Do not parse further
|
|
goto parsing_done;
|
|
}
|
|
|
|
// Get entry name
|
|
{
|
|
UINT32 nameOffset = (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) ? sizeof(EFI_GUID) : sizeof(UINT8); // GUID can be stored with the variable or in a separate store, so there will only be an index of it
|
|
CHAR8* namePtr = (CHAR8*)(entryHeader + 1) + nameOffset;
|
|
UINT32 nameSize = 0;
|
|
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_ASCII_NAME) { // Name is stored as ASCII string of CHAR8s
|
|
text = UString(namePtr);
|
|
nameSize = text.length() + 1;
|
|
}
|
|
else { // Name is stored as UCS2 string of CHAR16s
|
|
text = UString::fromUtf16((CHAR16*)namePtr);
|
|
nameSize = (text.length() + 1) * 2;
|
|
}
|
|
|
|
// Get entry GUID
|
|
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) { // GUID is strored in the variable itself
|
|
name = guidToUString(*(EFI_GUID*)(entryHeader + 1));
|
|
guid = guidToUString(*(EFI_GUID*)(entryHeader + 1), false);
|
|
}
|
|
// GUID is stored in GUID list at the end of the store
|
|
else {
|
|
guidIndex = *(UINT8*)(entryHeader + 1);
|
|
if (guidsInStore < guidIndex + 1)
|
|
guidsInStore = guidIndex + 1;
|
|
|
|
// The list begins at the end of the store and goes backwards
|
|
const EFI_GUID* guidPtr = (const EFI_GUID*)(data.constData() + data.size()) - 1 - guidIndex;
|
|
name = guidToUString(*guidPtr);
|
|
guid = guidToUString(*guidPtr, false);
|
|
hasGuidIndex = true;
|
|
}
|
|
|
|
// Include name and GUID into the header and remove them from body
|
|
header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER) + nameOffset + nameSize);
|
|
body = body.mid(nameOffset + nameSize);
|
|
}
|
|
parsing_done:
|
|
UString info;
|
|
|
|
// Rename invalid entries according to their types
|
|
pdata.isValid = TRUE;
|
|
if (isInvalid) {
|
|
name = UString("Invalid");
|
|
subtype = Subtypes::InvalidNvarEntry;
|
|
pdata.isValid = FALSE;
|
|
}
|
|
else if (isInvalidLink) {
|
|
name = UString("Invalid link");
|
|
subtype = Subtypes::InvalidLinkNvarEntry;
|
|
pdata.isValid = FALSE;
|
|
}
|
|
else // Add GUID info for valid entries
|
|
info += UString("Variable GUID: ") + guid + UString("\n");
|
|
|
|
// Add GUID index information
|
|
if (hasGuidIndex)
|
|
info += usprintf("GUID index: %u\n", guidIndex);
|
|
|
|
// Add header, body and extended data info
|
|
info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)",
|
|
entryHeader->Size, entryHeader->Size,
|
|
header.size(), header.size(),
|
|
body.size(), body.size());
|
|
|
|
// Add attributes info
|
|
info += usprintf("\nAttributes: %02Xh", entryHeader->Attributes);
|
|
// Translate attributes to text
|
|
if (entryHeader->Attributes && entryHeader->Attributes != 0xFF)
|
|
info += UString(" (") + nvarAttributesToUString(entryHeader->Attributes) + UString(")");
|
|
|
|
// Add next node info
|
|
if (!isInvalid && entryHeader->Next != lastVariableFlag)
|
|
info += usprintf("\nNext node at offset: %Xh", localOffset + offset + entryHeader->Next);
|
|
|
|
// Add extended header info
|
|
if (hasExtendedHeader) {
|
|
info += usprintf("\nExtended header size: %Xh (%u)\nExtended attributes: %Xh (",
|
|
extendedHeaderSize, extendedHeaderSize,
|
|
extendedAttributes) + nvarExtendedAttributesToUString(extendedAttributes) + UString(")");
|
|
|
|
// Add checksum
|
|
if (hasChecksum)
|
|
info += usprintf("\nChecksum: %02Xh", storedChecksum) +
|
|
(calculatedChecksum ? usprintf(", invalid, should be %02Xh", 0x100 - calculatedChecksum) : UString(", valid"));
|
|
|
|
// Add timestamp
|
|
if (hasTimestamp)
|
|
info += usprintf("\nTimestamp: %" PRIX64 "h", timestamp);
|
|
|
|
// Add hash
|
|
if (hasHash)
|
|
info += UString("\nHash: ") + UString(hash.toHex().constData());
|
|
}
|
|
|
|
// Add tree item
|
|
UModelIndex varIndex = model->addItem(localOffset + offset, Types::NvarEntry, subtype, name, text, info, header, body, tail, Movable, index);
|
|
|
|
// Set parsing data for created entry
|
|
model->setParsingData(varIndex, UByteArray((const char*)&pdata, sizeof(pdata)));
|
|
|
|
// Show messages
|
|
if (msgUnknownExtDataFormat) msg(usprintf("%s: unknown extended data format", __FUNCTION__), varIndex);
|
|
if (msgExtHeaderTooLong) msg(usprintf("%s: extended header size (%Xh) is greater than body size (%Xh)", __FUNCTION__,
|
|
extendedHeaderSize, body.size()), varIndex);
|
|
if (msgExtDataTooShort) msg(usprintf("%s: extended header size (%Xh) is too small for timestamp and hash", __FUNCTION__,
|
|
tail.size()), varIndex);
|
|
|
|
// Try parsing the entry data as NVAR storage if it begins with NVAR signature
|
|
if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry)
|
|
&& *(const UINT32*)body.constData() == NVRAM_NVAR_ENTRY_SIGNATURE)
|
|
parseNvarStore(varIndex);
|
|
|
|
// Move to next exntry
|
|
offset += entryHeader->Size;
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// Obtain required fields from parsing data
|
|
UINT8 emptyByte = 0xFF;
|
|
if (model->hasEmptyParsingData(index) == false) {
|
|
UByteArray data = model->parsingData(index);
|
|
const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
|
|
emptyByte = pdata->emptyByte;
|
|
}
|
|
|
|
// Get local offset
|
|
UINT32 localOffset = model->offset(index) + model->header(index).size();
|
|
|
|
// Get item data
|
|
UByteArray data = model->body(index);
|
|
|
|
// Search for first store
|
|
USTATUS result;
|
|
UINT32 prevStoreOffset;
|
|
result = findNextStore(index, data, localOffset, 0, prevStoreOffset);
|
|
if (result)
|
|
return result;
|
|
|
|
// First store is not at the beginning of volume body
|
|
UString name;
|
|
UString info;
|
|
if (prevStoreOffset > 0) {
|
|
// Get info
|
|
UByteArray padding = data.left(prevStoreOffset);
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
|
|
}
|
|
|
|
// Search for and parse all stores
|
|
UINT32 storeOffset = prevStoreOffset;
|
|
UINT32 prevStoreSize = 0;
|
|
|
|
while (!result) {
|
|
// Padding between stores
|
|
if (storeOffset > prevStoreOffset + prevStoreSize) {
|
|
UINT32 paddingOffset = prevStoreOffset + prevStoreSize;
|
|
UINT32 paddingSize = storeOffset - paddingOffset;
|
|
UByteArray padding = data.mid(paddingOffset, paddingSize);
|
|
|
|
// Get info
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset + paddingOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
|
|
}
|
|
|
|
// Get store size
|
|
UINT32 storeSize = 0;
|
|
result = getStoreSize(data, storeOffset, storeSize);
|
|
if (result) {
|
|
msg(usprintf("%s: getStoreSize failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
|
|
return result;
|
|
}
|
|
|
|
// Check that current store is fully present in input
|
|
if (storeSize > (UINT32)data.size() || storeOffset + storeSize > (UINT32)data.size()) {
|
|
// Mark the rest as padding and finish parsing
|
|
UByteArray padding = data.mid(storeOffset);
|
|
|
|
// Get info
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
|
|
|
// Add tree item
|
|
UModelIndex paddingIndex = model->addItem(localOffset + storeOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
|
|
msg(usprintf("%s: one of stores inside overlaps the end of data", __FUNCTION__), paddingIndex);
|
|
|
|
// Update variables
|
|
prevStoreOffset = storeOffset;
|
|
prevStoreSize = padding.size();
|
|
break;
|
|
}
|
|
|
|
// Parse current store header
|
|
UModelIndex storeIndex;
|
|
UByteArray store = data.mid(storeOffset, storeSize);
|
|
result = parseStoreHeader(store, localOffset + storeOffset, index, storeIndex);
|
|
if (result)
|
|
msg(usprintf("%s: store header parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
|
|
|
|
// Go to next store
|
|
prevStoreOffset = storeOffset;
|
|
prevStoreSize = storeSize;
|
|
result = findNextStore(index, data, localOffset, storeOffset + prevStoreSize, storeOffset);
|
|
}
|
|
|
|
// Padding/free space at the end
|
|
storeOffset = prevStoreOffset + prevStoreSize;
|
|
if ((UINT32)data.size() > storeOffset) {
|
|
UByteArray padding = data.mid(storeOffset);
|
|
// Add info
|
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
|
|
|
if (padding.count(emptyByte) == padding.size()) { // Free space
|
|
// Add tree item
|
|
model->addItem(localOffset + storeOffset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Movable, index);
|
|
}
|
|
else {
|
|
// Nothing is parsed yet, but the file is not empty
|
|
if (!storeOffset) {
|
|
msg(usprintf("%s: can't be parsed as NVRAM volume", __FUNCTION__), index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset + storeOffset, Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
|
|
}
|
|
}
|
|
|
|
// Parse bodies
|
|
for (int i = 0; i < model->rowCount(index); i++) {
|
|
UModelIndex current = index.child(i, 0);
|
|
switch (model->type(current)) {
|
|
case Types::FdcStore: return parseFdcStoreBody(current);
|
|
case Types::VssStore: return parseVssStoreBody(current, 0);
|
|
case Types::Vss2Store: return parseVssStoreBody(current, 4);
|
|
case Types::FsysStore: return parseFsysStoreBody(current);
|
|
case Types::EvsaStore: return parseEvsaStoreBody(current);
|
|
case Types::FlashMapStore: return parseFlashMapBody(current);
|
|
}
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::findNextStore(const UModelIndex & index, const UByteArray & volume, const UINT32 localOffset, const UINT32 storeOffset, UINT32 & nextStoreOffset)
|
|
{
|
|
UINT32 dataSize = volume.size();
|
|
|
|
if (dataSize < sizeof(UINT32))
|
|
return U_STORES_NOT_FOUND;
|
|
|
|
UINT32 offset = storeOffset;
|
|
for (; offset < dataSize - sizeof(UINT32); offset++) {
|
|
const UINT32* currentPos = (const UINT32*)(volume.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(usprintf("%s: VSS store candidate at offset %Xh skipped, has invalid format %02Xh", __FUNCTION__, localOffset + offset, vssHeader->Format), index);
|
|
continue;
|
|
}
|
|
if (vssHeader->Size == 0 || vssHeader->Size == 0xFFFFFFFF) {
|
|
msg(usprintf("%s: VSS store candidate at offset %Xh skipped, has invalid size %Xh", __FUNCTION__, localOffset + offset, vssHeader->Size), index);
|
|
continue;
|
|
}
|
|
// All checks passed, store found
|
|
break;
|
|
}
|
|
else if (*currentPos == NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID_PART1 || *currentPos == NVRAM_VSS2_STORE_GUID_PART1) { //VSS2 store signatures found, perform checks
|
|
UByteArray guid = UByteArray(volume.constData() + offset, sizeof(EFI_GUID));
|
|
if (guid != NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID && guid != NVRAM_VSS2_STORE_GUID) // Check the whole signature
|
|
continue;
|
|
|
|
const VSS2_VARIABLE_STORE_HEADER* vssHeader = (const VSS2_VARIABLE_STORE_HEADER*)currentPos;
|
|
if (vssHeader->Format != NVRAM_VSS_VARIABLE_STORE_FORMATTED) {
|
|
msg(usprintf("%s: VSS2 store candidate at offset %Xh skipped, has invalid format %02Xh", __FUNCTION__, localOffset + offset, vssHeader->Format), index);
|
|
continue;
|
|
}
|
|
if (vssHeader->Size == 0 || vssHeader->Size == 0xFFFFFFFF) {
|
|
msg(usprintf("%s: VSS2 store candidate at offset %Xh skipped, has invalid size %Xh", __FUNCTION__, localOffset + offset, vssHeader->Size), index);
|
|
continue;
|
|
}
|
|
// All checks passed, store found
|
|
break;
|
|
}
|
|
else if (*currentPos == NVRAM_FDC_VOLUME_SIGNATURE) { //FDC signature found
|
|
const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)currentPos;
|
|
if (fdcHeader->Size == 0 || fdcHeader->Size == 0xFFFFFFFF) {
|
|
msg(usprintf("%s: FDC store candidate at offset %Xh skipped, has invalid size %Xh", __FUNCTION__, localOffset + offset, fdcHeader->Size), index);
|
|
continue;
|
|
}
|
|
// All checks passed, store found
|
|
break;
|
|
}
|
|
else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_GAID_STORE_SIGNATURE) { //Fsys or Gaid signature found
|
|
const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)currentPos;
|
|
if (fsysHeader->Size == 0 || fsysHeader->Size == 0xFFFF) {
|
|
msg(usprintf("%s: Fsys store candidate at offset %Xh skipped, has invalid size %Xh", __FUNCTION__, localOffset + offset, fsysHeader->Size), index);
|
|
continue;
|
|
}
|
|
// All checks passed, store found
|
|
break;
|
|
}
|
|
else if (*currentPos == NVRAM_EVSA_STORE_SIGNATURE) { //EVSA signature found
|
|
if (offset < sizeof(UINT32))
|
|
continue;
|
|
|
|
const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)(currentPos - 1);
|
|
if (evsaHeader->Header.Type != NVRAM_EVSA_ENTRY_TYPE_STORE) {
|
|
msg(usprintf("%s: EVSA store candidate at offset %Xh skipped, has invalid type %02Xh", __FUNCTION__, localOffset + offset - 4, evsaHeader->Header.Type), index);
|
|
continue;
|
|
}
|
|
if (evsaHeader->StoreSize == 0 || evsaHeader->StoreSize == 0xFFFFFFFF) {
|
|
msg(usprintf("%s: EVSA store candidate at offset %Xh skipped, has invalid size %Xh", __FUNCTION__, localOffset + offset, evsaHeader->StoreSize), index);
|
|
continue;
|
|
}
|
|
// All checks passed, store found
|
|
offset -= sizeof(UINT32);
|
|
break;
|
|
}
|
|
else if (*currentPos == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *currentPos == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) { //Possible FTW block signature found
|
|
UByteArray guid = UByteArray(volume.constData() + offset, sizeof(EFI_GUID));
|
|
if (guid != NVRAM_MAIN_STORE_VOLUME_GUID && guid != EDKII_WORKING_BLOCK_SIGNATURE_GUID && guid != VSS2_WORKING_BLOCK_SIGNATURE_GUID) // Check the whole signature
|
|
continue;
|
|
|
|
// Detect header variant based on WriteQueueSize
|
|
const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftwHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)currentPos;
|
|
if (ftwHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize
|
|
if (ftwHeader->WriteQueueSize == 0 || ftwHeader->WriteQueueSize == 0xFFFFFFFF) {
|
|
msg(usprintf("%s: FTW block candidate at offset %Xh skipped, has invalid body size %Xh", __FUNCTION__, localOffset + offset, ftwHeader->WriteQueueSize), index);
|
|
continue;
|
|
}
|
|
}
|
|
else if (ftwHeader->WriteQueueSize % 0x10 == 0x00) { // Header with 64 bit WriteQueueSize
|
|
const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64Header = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)currentPos;
|
|
if (ftw64Header->WriteQueueSize == 0 || ftw64Header->WriteQueueSize >= 0xFFFFFFFF) {
|
|
msg(usprintf("%s: FTW block candidate at offset %Xh skipped, has invalid body size %Xh", __FUNCTION__, localOffset + offset, ftw64Header->WriteQueueSize), index);
|
|
continue;
|
|
}
|
|
}
|
|
else // Unknown header
|
|
continue;
|
|
|
|
// All checks passed, store found
|
|
break;
|
|
}
|
|
else if (*currentPos == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) {// Phoenix SCT flash map
|
|
UByteArray signature = UByteArray(volume.constData() + offset, NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_LENGTH);
|
|
if (signature != NVRAM_PHOENIX_FLASH_MAP_SIGNATURE) // Check the whole signature
|
|
continue;
|
|
|
|
// All checks passed, store found
|
|
break;
|
|
}
|
|
else if (*currentPos == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) { // Phoenix SCT CMDB store
|
|
const PHOENIX_CMDB_HEADER* cmdbHeader = (const PHOENIX_CMDB_HEADER*)currentPos;
|
|
|
|
// Check size
|
|
if (cmdbHeader->HeaderSize != sizeof(PHOENIX_CMDB_HEADER))
|
|
continue;
|
|
|
|
// All checks passed, store found
|
|
break;
|
|
}
|
|
else if (*currentPos == INTEL_MICROCODE_HEADER_VERSION) {// Intel microcode
|
|
if (!INTEL_MICROCODE_HEADER_SIZES_VALID(currentPos)) // Check header sizes
|
|
continue;
|
|
|
|
// Check reserved bytes
|
|
const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos;
|
|
bool reservedBytesValid = true;
|
|
for (UINT32 i = 0; i < sizeof(ucodeHeader->Reserved); i++)
|
|
if (ucodeHeader->Reserved[i] != INTEL_MICROCODE_HEADER_RESERVED_BYTE) {
|
|
reservedBytesValid = false;
|
|
break;
|
|
}
|
|
if (!reservedBytesValid)
|
|
continue;
|
|
|
|
// All checks passed, store found
|
|
break;
|
|
}
|
|
else if (*currentPos == OEM_ACTIVATION_PUBKEY_MAGIC) { // SLIC pubkey
|
|
if (offset < 4 * sizeof(UINT32))
|
|
continue;
|
|
|
|
const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)(currentPos - 4);
|
|
// Check type
|
|
if (pubkeyHeader->Type != OEM_ACTIVATION_PUBKEY_TYPE)
|
|
continue;
|
|
|
|
// All checks passed, store found
|
|
offset -= 4 * sizeof(UINT32);
|
|
break;
|
|
}
|
|
else if (*currentPos == OEM_ACTIVATION_MARKER_WINDOWS_FLAG_PART1) { // SLIC marker
|
|
if (offset >= dataSize - sizeof(UINT64) ||
|
|
*(const UINT64*)currentPos != OEM_ACTIVATION_MARKER_WINDOWS_FLAG ||
|
|
offset < 26) // Check full windows flag and structure size
|
|
continue;
|
|
|
|
const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)(volume.constData() + offset - 26);
|
|
// Check reserved bytes
|
|
bool reservedBytesValid = true;
|
|
for (UINT32 i = 0; i < sizeof(markerHeader->Reserved); i++)
|
|
if (markerHeader->Reserved[i] != OEM_ACTIVATION_MARKER_RESERVED_BYTE) {
|
|
reservedBytesValid = false;
|
|
break;
|
|
}
|
|
if (!reservedBytesValid)
|
|
continue;
|
|
|
|
// All checks passed, store found
|
|
offset -= 26;
|
|
break;
|
|
}
|
|
}
|
|
// No more stores found
|
|
if (offset >= dataSize - sizeof(UINT32))
|
|
return U_STORES_NOT_FOUND;
|
|
|
|
nextStoreOffset = offset;
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::getStoreSize(const UByteArray & data, const UINT32 storeOffset, UINT32 & storeSize)
|
|
{
|
|
const UINT32* signature = (const UINT32*)(data.constData() + storeOffset);
|
|
if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) {
|
|
const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)signature;
|
|
storeSize = vssHeader->Size;
|
|
}
|
|
else if (*signature == NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID_PART1 || *signature == NVRAM_VSS2_STORE_GUID_PART1) {
|
|
const VSS2_VARIABLE_STORE_HEADER* vssHeader = (const VSS2_VARIABLE_STORE_HEADER*)signature;
|
|
storeSize = vssHeader->Size;
|
|
}
|
|
else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) {
|
|
const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)signature;
|
|
storeSize = fdcHeader->Size;
|
|
}
|
|
else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE) {
|
|
const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)signature;
|
|
storeSize = fsysHeader->Size;
|
|
}
|
|
else if (*(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE) {
|
|
const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)signature;
|
|
storeSize = evsaHeader->StoreSize;
|
|
}
|
|
else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) {
|
|
const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftwHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)signature;
|
|
if (ftwHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize
|
|
storeSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) + ftwHeader->WriteQueueSize;
|
|
}
|
|
else { // Header with 64 bit WriteQueueSize
|
|
const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64Header = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)signature;
|
|
storeSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64) + (UINT32)ftw64Header->WriteQueueSize;
|
|
}
|
|
}
|
|
else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) { // Phoenix SCT flash map
|
|
const PHOENIX_FLASH_MAP_HEADER* flashMapHeader = (const PHOENIX_FLASH_MAP_HEADER*)signature;
|
|
storeSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + sizeof(PHOENIX_FLASH_MAP_ENTRY) * flashMapHeader->NumEntries;
|
|
}
|
|
else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) { // Phoenix SCT CMDB store
|
|
storeSize = NVRAM_PHOENIX_CMDB_SIZE; // It's a predefined max size, no need to calculate
|
|
}
|
|
else if (*(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC) { // SLIC pubkey
|
|
const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)signature;
|
|
storeSize = pubkeyHeader->Size;
|
|
}
|
|
else if (*(const UINT64*)(data.constData() + storeOffset + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG) { // SLIC marker
|
|
const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)signature;
|
|
storeSize = markerHeader->Size;
|
|
}
|
|
else if (*signature == INTEL_MICROCODE_HEADER_VERSION) { // Intel microcode, must be checked after SLIC marker because of the same *signature values
|
|
const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)signature;
|
|
storeSize = ucodeHeader->TotalSize;
|
|
}
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseVssStoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check store size
|
|
if (dataSize < sizeof(VSS_VARIABLE_STORE_HEADER)) {
|
|
msg(usprintf("%s: volume body is too small even for VSS store header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Get VSS store header
|
|
const VSS_VARIABLE_STORE_HEADER* vssStoreHeader = (const VSS_VARIABLE_STORE_HEADER*)store.constData();
|
|
|
|
// Check for size override
|
|
UINT32 storeSize = vssStoreHeader->Size;
|
|
if (sizeOverride) {
|
|
storeSize = dataSize;
|
|
}
|
|
|
|
// Check store size
|
|
if (dataSize < storeSize) {
|
|
msg(usprintf("%s: VSS store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
storeSize, storeSize,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UByteArray header = store.left(sizeof(VSS_VARIABLE_STORE_HEADER));
|
|
UByteArray body = store.mid(sizeof(VSS_VARIABLE_STORE_HEADER), storeSize - sizeof(VSS_VARIABLE_STORE_HEADER));
|
|
|
|
// Add info
|
|
bool isSvsStore = (vssStoreHeader->Signature == NVRAM_APPLE_SVS_STORE_SIGNATURE);
|
|
UString name = isSvsStore ? UString("SVS store") : UString("VSS store");
|
|
UString info = usprintf("Signature: %s\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nFormat: %02Xh\nState: %02Xh\nUnknown: %04Xh",
|
|
isSvsStore ? "$SVS" : "$VSS",
|
|
storeSize, storeSize,
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
vssStoreHeader->Format,
|
|
vssStoreHeader->State,
|
|
vssStoreHeader->Unknown);
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::VssStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseVss2StoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check store size
|
|
if (dataSize < sizeof(VSS2_VARIABLE_STORE_HEADER)) {
|
|
msg(usprintf("%s: volume body is too small even for VSS2 store header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Get VSS2 store header
|
|
const VSS2_VARIABLE_STORE_HEADER* vssStoreHeader = (const VSS2_VARIABLE_STORE_HEADER*)store.constData();
|
|
|
|
// Check for size override
|
|
UINT32 storeSize = vssStoreHeader->Size;
|
|
if (sizeOverride) {
|
|
storeSize = dataSize;
|
|
}
|
|
|
|
// Check store size
|
|
if (dataSize < storeSize) {
|
|
msg(usprintf("%s: VSS2 store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
storeSize, storeSize,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UByteArray header = store.left(sizeof(VSS2_VARIABLE_STORE_HEADER));
|
|
UByteArray body = store.mid(sizeof(VSS2_VARIABLE_STORE_HEADER), storeSize - sizeof(VSS2_VARIABLE_STORE_HEADER));
|
|
|
|
// Add info
|
|
UString name = UString("VSS2 store");
|
|
UString info = UString("Signature: ") + guidToUString(vssStoreHeader->Signature, false) +
|
|
usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nFormat: %02Xh\nState: %02Xh\nUnknown: %04Xh",
|
|
storeSize, storeSize,
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
vssStoreHeader->Format,
|
|
vssStoreHeader->State,
|
|
vssStoreHeader->Unknown);
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::Vss2Store, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseFtwStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check store size
|
|
if (dataSize < sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64)) {
|
|
msg(usprintf("%s: volume body is too small even for FTW store header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Obtain required information from parent volume
|
|
UINT8 emptyByte = 0xFF;
|
|
UModelIndex parentVolumeIndex = model->findParentOfType(parent, Types::Volume);
|
|
if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
|
|
UByteArray data = model->parsingData(parentVolumeIndex);
|
|
const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
|
|
emptyByte = pdata->emptyByte;
|
|
}
|
|
|
|
// Get FTW block headers
|
|
const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftw32BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)store.constData();
|
|
const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)store.constData();
|
|
|
|
// Check store size
|
|
UINT32 ftwBlockSize;
|
|
bool has32bitHeader;
|
|
if (ftw32BlockHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize
|
|
ftwBlockSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) + ftw32BlockHeader->WriteQueueSize;
|
|
has32bitHeader = true;
|
|
}
|
|
else { // Header with 64 bit WriteQueueSize
|
|
ftwBlockSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64) + (UINT32)ftw64BlockHeader->WriteQueueSize;
|
|
has32bitHeader = false;
|
|
}
|
|
if (dataSize < ftwBlockSize) {
|
|
msg(usprintf("%s: FTW store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
ftwBlockSize, ftwBlockSize,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UINT32 headerSize = has32bitHeader ? sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) : sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64);
|
|
UByteArray header = store.left(headerSize);
|
|
UByteArray body = store.mid(headerSize, ftwBlockSize - headerSize);
|
|
|
|
// Check block header checksum
|
|
UByteArray crcHeader = header;
|
|
EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)header.data();
|
|
crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0;
|
|
crcFtwBlockHeader->State = emptyByte ? 0xFF : 0;
|
|
UINT32 calculatedCrc = crc32(0, (const UINT8*)crcFtwBlockHeader, headerSize);
|
|
|
|
// Add info
|
|
UString name("FTW store");
|
|
UString info = UString("Signature: ") + guidToUString(ftw32BlockHeader->Signature, false) +
|
|
usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nHeader CRC32: %08Xh",
|
|
ftwBlockSize, ftwBlockSize,
|
|
headerSize, headerSize,
|
|
body.size(), body.size(),
|
|
ftw32BlockHeader->State,
|
|
ftw32BlockHeader->Crc) +
|
|
(ftw32BlockHeader->Crc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid"));
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseFdcStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check store size
|
|
if (dataSize < sizeof(FDC_VOLUME_HEADER)) {
|
|
msg(usprintf("%s: volume body is too small even for FDC store header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Get Fdc store header
|
|
const FDC_VOLUME_HEADER* fdcStoreHeader = (const FDC_VOLUME_HEADER*)store.constData();
|
|
|
|
// Check store size
|
|
if (dataSize < fdcStoreHeader->Size) {
|
|
msg(usprintf("%s: FDC store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
fdcStoreHeader->Size, fdcStoreHeader->Size,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Check header size
|
|
UINT32 headerSize = sizeof(FDC_VOLUME_HEADER);
|
|
if (dataSize < headerSize) {
|
|
msg(usprintf("%s: FDC store header size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
fdcStoreHeader->Size, fdcStoreHeader->Size,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UByteArray header = store.left(headerSize);
|
|
UByteArray body = store.mid(headerSize, fdcStoreHeader->Size - headerSize);
|
|
|
|
// Add info
|
|
UString name("FDC store");
|
|
UString info = usprintf("Signature: _FDC\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)",
|
|
fdcStoreHeader->Size, fdcStoreHeader->Size,
|
|
header.size(), header.size(),
|
|
body.size(), body.size());
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseFsysStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check store size
|
|
if (dataSize < sizeof(APPLE_FSYS_STORE_HEADER)) {
|
|
msg(usprintf("%s: volume body is too small even for Fsys store header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Get Fsys store header
|
|
const APPLE_FSYS_STORE_HEADER* fsysStoreHeader = (const APPLE_FSYS_STORE_HEADER*)store.constData();
|
|
|
|
// Check store size
|
|
if (dataSize < fsysStoreHeader->Size) {
|
|
msg(usprintf("%s: Fsys store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
fsysStoreHeader->Size, fsysStoreHeader->Size,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UByteArray header = store.left(sizeof(APPLE_FSYS_STORE_HEADER));
|
|
UByteArray body = store.mid(sizeof(APPLE_FSYS_STORE_HEADER), fsysStoreHeader->Size - sizeof(APPLE_FSYS_STORE_HEADER) - sizeof(UINT32));
|
|
|
|
// Check store checksum
|
|
UINT32 storedCrc = *(UINT32*)store.right(sizeof(UINT32)).constData();
|
|
UINT32 calculatedCrc = crc32(0, (const UINT8*)store.constData(), (const UINT32)store.size() - sizeof(UINT32));
|
|
|
|
// Add info
|
|
bool isGaidStore = (fsysStoreHeader->Signature == NVRAM_APPLE_GAID_STORE_SIGNATURE);
|
|
UString name = isGaidStore ? UString("Gaid store") : UString("Fsys store");
|
|
UString info = usprintf("Signature: %s\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nUnknown0: %02Xh\nUnknown1: %08Xh\nCRC32: %08Xh",
|
|
isGaidStore ? "Gaid" : "Fsys",
|
|
fsysStoreHeader->Size, fsysStoreHeader->Size,
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
fsysStoreHeader->Unknown0,
|
|
fsysStoreHeader->Unknown1)
|
|
+ (storedCrc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid"));
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::FsysStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseEvsaStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check dataSize
|
|
if (dataSize < sizeof(EVSA_STORE_ENTRY)) {
|
|
msg(usprintf("%s: volume body is too small even for EVSA store header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Get EVSA store header
|
|
const EVSA_STORE_ENTRY* evsaStoreHeader = (const EVSA_STORE_ENTRY*)store.constData();
|
|
|
|
// Check store size
|
|
if (dataSize < evsaStoreHeader->StoreSize) {
|
|
msg(usprintf("%s: EVSA store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
evsaStoreHeader->StoreSize, evsaStoreHeader->StoreSize,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UByteArray header = store.left(evsaStoreHeader->Header.Size);
|
|
UByteArray body = store.mid(evsaStoreHeader->Header.Size, evsaStoreHeader->StoreSize - evsaStoreHeader->Header.Size);
|
|
|
|
// Recalculate checksum
|
|
UINT8 calculated = calculateChecksum8(((const UINT8*)evsaStoreHeader) + 2, evsaStoreHeader->Header.Size - 2);
|
|
|
|
// Add info
|
|
UString name("EVSA store");
|
|
UString info = usprintf("Signature: EVSA\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nAttributes: %08Xh\nChecksum: %02Xh",
|
|
evsaStoreHeader->StoreSize, evsaStoreHeader->StoreSize,
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
evsaStoreHeader->Header.Type,
|
|
evsaStoreHeader->Attributes,
|
|
evsaStoreHeader->Header.Checksum) +
|
|
(evsaStoreHeader->Header.Checksum != calculated ? usprintf("%, invalid, should be %02Xh", calculated) : UString(", valid"));
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::EvsaStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseFlashMapStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check data size
|
|
if (dataSize < sizeof(PHOENIX_FLASH_MAP_HEADER)) {
|
|
msg(usprintf("%s: volume body is too small even for FlashMap block header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Get FlashMap block header
|
|
const PHOENIX_FLASH_MAP_HEADER* flashMapHeader = (const PHOENIX_FLASH_MAP_HEADER*)store.constData();
|
|
|
|
// Check store size
|
|
UINT32 flashMapSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + flashMapHeader->NumEntries * sizeof(PHOENIX_FLASH_MAP_ENTRY);
|
|
if (dataSize < flashMapSize) {
|
|
msg(usprintf("%s: FlashMap block size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
flashMapSize, flashMapSize,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UByteArray header = store.left(sizeof(PHOENIX_FLASH_MAP_HEADER));
|
|
UByteArray body = store.mid(sizeof(PHOENIX_FLASH_MAP_HEADER), flashMapSize - sizeof(PHOENIX_FLASH_MAP_HEADER));
|
|
|
|
// Add info
|
|
UString name("Phoenix SCT flash map");
|
|
UString info = usprintf("Signature: _FLASH_MAP\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u",
|
|
flashMapSize, flashMapSize,
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
flashMapHeader->NumEntries);
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::FlashMapStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseCmdbStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check store size
|
|
if (dataSize < sizeof(PHOENIX_CMDB_HEADER)) {
|
|
msg(usprintf("%s: volume body is too small even for CMDB store header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
UINT32 cmdbSize = NVRAM_PHOENIX_CMDB_SIZE;
|
|
if (dataSize < cmdbSize) {
|
|
msg(usprintf("%s: CMDB store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
cmdbSize, cmdbSize,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Get store header
|
|
const PHOENIX_CMDB_HEADER* cmdbHeader = (const PHOENIX_CMDB_HEADER*)store.constData();
|
|
|
|
// Construct header and body
|
|
UByteArray header = store.left(cmdbHeader->TotalSize);
|
|
UByteArray body = store.mid(cmdbHeader->TotalSize, cmdbSize - cmdbHeader->TotalSize);
|
|
|
|
// Add info
|
|
UString name("CMDB store");
|
|
UString info = usprintf("Signature: CMDB\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)",
|
|
cmdbSize, cmdbSize,
|
|
header.size(), header.size(),
|
|
body.size(), body.size());
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::CmdbStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseSlicPubkeyHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check data size
|
|
if (dataSize < sizeof(OEM_ACTIVATION_PUBKEY)) {
|
|
msg(usprintf("%s: volume body is too small even for SLIC pubkey header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Get SLIC pubkey header
|
|
const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)store.constData();
|
|
|
|
// Check store size
|
|
if (dataSize < pubkeyHeader->Size) {
|
|
msg(usprintf("%s: SLIC pubkey size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
pubkeyHeader->Size, pubkeyHeader->Size,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UByteArray header = store.left(sizeof(OEM_ACTIVATION_PUBKEY));
|
|
|
|
// Add info
|
|
UString name("SLIC pubkey");
|
|
UString info = usprintf("Type: 0h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\n"
|
|
"Key type: %02Xh\nVersion: %02Xh\nAlgorithm: %08Xh\nMagic: RSA1\nBit length: %08Xh\nExponent: %08Xh",
|
|
pubkeyHeader->Size, pubkeyHeader->Size,
|
|
header.size(), header.size(),
|
|
pubkeyHeader->KeyType,
|
|
pubkeyHeader->Version,
|
|
pubkeyHeader->Algorithm,
|
|
pubkeyHeader->BitLength,
|
|
pubkeyHeader->Exponent);
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::SlicData, Subtypes::PubkeySlicData, name, UString(), info, header, UByteArray(), UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseSlicMarkerHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check data size
|
|
if (dataSize < sizeof(OEM_ACTIVATION_MARKER)) {
|
|
msg(usprintf("%s: volume body is too small even for SLIC marker header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Get SLIC marker header
|
|
const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)store.constData();
|
|
|
|
// Check store size
|
|
if (dataSize < markerHeader->Size) {
|
|
msg(usprintf("%s: SLIC marker size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
markerHeader->Size, markerHeader->Size,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UByteArray header = store.left(sizeof(OEM_ACTIVATION_MARKER));
|
|
|
|
// Add info
|
|
UString name("SLIC marker");
|
|
UString info = usprintf("Type: 1h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\n"
|
|
"Version: %08Xh\nOEM ID: %s\nOEM table ID: %s\nWindows flag: WINDOWS\nSLIC version: %08Xh",
|
|
markerHeader->Size, markerHeader->Size,
|
|
header.size(), header.size(),
|
|
markerHeader->Version,
|
|
(const char*)UString((const char*)&(markerHeader->OemId)).left(6).toLocal8Bit(),
|
|
(const char*)UString((const char*)&(markerHeader->OemTableId)).left(8).toLocal8Bit(),
|
|
markerHeader->SlicVersion);
|
|
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::SlicData, Subtypes::MarkerSlicData, name, UString(), info, header, UByteArray(), UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
|
|
// Check data size
|
|
if (dataSize < sizeof(INTEL_MICROCODE_HEADER)) {
|
|
msg(usprintf("%s: volume body is too small even for Intel microcode header", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Get Intel microcode header
|
|
const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)store.constData();
|
|
|
|
// Check store size
|
|
if (dataSize < ucodeHeader->TotalSize) {
|
|
msg(usprintf("%s: Intel microcode size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
|
|
ucodeHeader->TotalSize, ucodeHeader->TotalSize,
|
|
dataSize, dataSize), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UByteArray header = store.left(sizeof(INTEL_MICROCODE_HEADER));
|
|
UByteArray body = store.mid(sizeof(INTEL_MICROCODE_HEADER), ucodeHeader->DataSize);
|
|
|
|
//TODO: recalculate microcode checksum
|
|
|
|
// Add info
|
|
UString name("Intel microcode");
|
|
UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\n"
|
|
"Date: %02X.%02X.%04x\nCPU signature: %08Xh\nRevision: %08Xh\nChecksum: %08Xh\nLoader revision: %08Xh\nCPU flags: %08Xh",
|
|
ucodeHeader->TotalSize, ucodeHeader->TotalSize,
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
ucodeHeader->DateDay,
|
|
ucodeHeader->DateMonth,
|
|
ucodeHeader->DateYear,
|
|
ucodeHeader->CpuSignature,
|
|
ucodeHeader->Revision,
|
|
ucodeHeader->Checksum,
|
|
ucodeHeader->LoaderRevision,
|
|
ucodeHeader->CpuFlags);
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::Microcode, Subtypes::IntelMicrocode, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
const UINT32 dataSize = (const UINT32)store.size();
|
|
const UINT32* signature = (const UINT32*)store.constData();
|
|
// Check store size
|
|
if (dataSize < sizeof(UINT32)) {
|
|
msg(usprintf("%s: volume body is too small even for a store signature", __FUNCTION__), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Check signature and run parser function needed
|
|
// VSS/SVS store
|
|
if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE)
|
|
return parseVssStoreHeader(store, localOffset, false, parent, index);
|
|
// VSS2 store
|
|
if (*signature == NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID_PART1 || *signature == NVRAM_VSS2_STORE_GUID_PART1)
|
|
return parseVss2StoreHeader(store, localOffset, false, parent, index);
|
|
// FTW store
|
|
else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1)
|
|
return parseFtwStoreHeader(store, localOffset, parent, index);
|
|
// FDC store
|
|
else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE)
|
|
return parseFdcStoreHeader(store, localOffset, parent, index);
|
|
// Apple Fsys/Gaid store
|
|
else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE)
|
|
return parseFsysStoreHeader(store, localOffset, parent, index);
|
|
// EVSA store
|
|
else if (dataSize >= 2 * sizeof(UINT32) && *(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE)
|
|
return parseEvsaStoreHeader(store, localOffset, parent, index);
|
|
// Phoenix SCT flash map
|
|
else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1)
|
|
return parseFlashMapStoreHeader(store, localOffset, parent, index);
|
|
// Phoenix CMDB store
|
|
else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE)
|
|
return parseCmdbStoreHeader(store, localOffset, parent, index);
|
|
// SLIC pubkey
|
|
else if (dataSize >= 5 * sizeof(UINT32) && *(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC)
|
|
return parseSlicPubkeyHeader(store, localOffset, parent, index);
|
|
// SLIC marker
|
|
else if (dataSize >= 34 && *(const UINT64*)(store.constData() + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG)
|
|
return parseSlicMarkerHeader(store, localOffset, parent, index);
|
|
// Intel microcode
|
|
// Must be checked after SLIC marker because of the same *signature values
|
|
else if (*signature == INTEL_MICROCODE_HEADER_VERSION)
|
|
return parseIntelMicrocodeHeader(store, localOffset, parent, index);
|
|
|
|
msg(usprintf("parseStoreHeader: don't know how to parse a header with signature %08Xh", *signature), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseFdcStoreBody(const UModelIndex & index)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// Get item data
|
|
const UByteArray data = model->body(index);
|
|
|
|
// Get local offset
|
|
UINT32 localOffset = model->offset(index) + model->header(index).size();
|
|
|
|
// The body is a firmware volume with either a VSS or VSS2 store
|
|
UModelIndex volumeIndex;
|
|
USTATUS status = ffsParser->parseVolumeHeader(data, localOffset, index, volumeIndex);
|
|
if (status || !volumeIndex.isValid()) {
|
|
msg(usprintf("%s: store can't be parsed as FDC store", __FUNCTION__), index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Determine if it's a VSS or VSS2 store inside
|
|
UByteArray store = model->body(volumeIndex);
|
|
if ((UINT32)store.size() >= sizeof(UINT32) && *(const UINT32*)store.constData() == NVRAM_VSS_STORE_SIGNATURE) {
|
|
UModelIndex vssIndex;
|
|
status = parseVssStoreHeader(store, localOffset + model->header(volumeIndex).size(), true, volumeIndex, vssIndex);
|
|
if (status)
|
|
return status;
|
|
return parseVssStoreBody(vssIndex, 0);
|
|
}
|
|
else if ((UINT32)store.size() >= sizeof(EFI_GUID) && store.left(sizeof(EFI_GUID)) == NVRAM_FDC_STORE_GUID) {
|
|
UModelIndex vss2Index;
|
|
status = parseVss2StoreHeader(store, localOffset + model->header(volumeIndex).size(), true, volumeIndex, vss2Index);
|
|
if (status)
|
|
return status;
|
|
return parseVssStoreBody(vss2Index, 0);
|
|
}
|
|
else {
|
|
msg(usprintf("%s: internal volume can't be parsed as VSS/VSS2 store", __FUNCTION__), index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
}
|
|
|
|
USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index, UINT8 alignment)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// Obtain required information from parent volume
|
|
UINT8 emptyByte = 0xFF;
|
|
UModelIndex parentVolumeIndex = model->findParentOfType(index, Types::Volume);
|
|
if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
|
|
UByteArray data = model->parsingData(parentVolumeIndex);
|
|
const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
|
|
emptyByte = pdata->emptyByte;
|
|
}
|
|
|
|
// Get local offset
|
|
UINT32 localOffset = model->offset(index) + model->header(index).size();
|
|
|
|
// Get item data
|
|
const UByteArray data = model->body(index);
|
|
|
|
// Check that the is enough space for variable header
|
|
const UINT32 dataSize = (UINT32)data.size();
|
|
if (dataSize < sizeof(VSS_VARIABLE_HEADER)) {
|
|
msg(usprintf("%s: store body is too small even for VSS variable header", __FUNCTION__), index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
UINT32 offset = 0;
|
|
|
|
// Parse all variables
|
|
while (1) {
|
|
bool isInvalid = false;
|
|
bool isAuthenticated = false;
|
|
bool isAppleCrc32 = false;
|
|
bool isIntelSpecial = false;
|
|
|
|
UINT32 storedCrc32 = 0;
|
|
UINT32 calculatedCrc32 = 0;
|
|
UINT64 monotonicCounter = 0;
|
|
EFI_TIME timestamp = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
UINT32 pubKeyIndex = 0;
|
|
|
|
UINT8 subtype = 0;
|
|
UString name;
|
|
UString text;
|
|
EFI_GUID* variableGuid = NULL;
|
|
CHAR16* variableName = (CHAR16*)L"";
|
|
UByteArray header;
|
|
UByteArray 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;
|
|
}
|
|
}
|
|
|
|
// Intel special variable
|
|
else if (variableHeader->State == NVRAM_VSS_INTEL_VARIABLE_VALID || variableHeader->State == NVRAM_VSS_INTEL_VARIABLE_INVALID) {
|
|
isIntelSpecial = true;
|
|
const VSS_INTEL_VARIABLE_HEADER* intelVariableHeader = (const VSS_INTEL_VARIABLE_HEADER*)variableHeader;
|
|
variableSize = intelVariableHeader->TotalSize;
|
|
variableGuid = (EFI_GUID*)&intelVariableHeader->VendorGuid;
|
|
variableName = (CHAR16*)(intelVariableHeader + 1);
|
|
|
|
UINT32 i = 0;
|
|
while (variableName[i] != 0) ++i;
|
|
|
|
i = sizeof(VSS_INTEL_VARIABLE_HEADER) + 2 * (i + 1);
|
|
i = i < variableSize ? i : variableSize;
|
|
|
|
header = data.mid(offset, i);
|
|
body = data.mid(offset + header.size(), variableSize - i);
|
|
}
|
|
|
|
// Normal VSS variable
|
|
if (!isAuthenticated && !isAppleCrc32 && !isIntelSpecial) {
|
|
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);
|
|
}
|
|
|
|
// Check variable state
|
|
if (variableHeader->State != NVRAM_VSS_INTEL_VARIABLE_VALID && variableHeader->State != NVRAM_VSS_VARIABLE_ADDED && variableHeader->State != NVRAM_VSS_VARIABLE_HEADER_VALID) {
|
|
isInvalid = true;
|
|
}
|
|
|
|
// Check variable size
|
|
if (variableSize > unparsedSize) {
|
|
variableSize = 0;
|
|
}
|
|
}
|
|
|
|
// 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
|
|
UByteArray padding = data.mid(offset, unparsedSize);
|
|
// Get info
|
|
UString info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
|
|
|
if (padding.count(emptyByte) == padding.size()) { // Free space
|
|
// Add tree item
|
|
model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Movable, index);
|
|
}
|
|
else { // Padding
|
|
// Nothing is parsed yet, but the store is not empty
|
|
if (!offset) {
|
|
msg(usprintf("%s: store can't be parsed as VSS store", __FUNCTION__), index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset + offset, Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
UString info;
|
|
|
|
// Rename invalid variables
|
|
if (isInvalid || !variableGuid) {
|
|
name = UString("Invalid");
|
|
}
|
|
else { // Add GUID and text for valid variables
|
|
name = guidToUString(*variableGuid);
|
|
info += UString("Variable GUID: ") + guidToUString(*variableGuid, false) + UString("\n");
|
|
text = UString::fromUtf16(variableName);
|
|
}
|
|
|
|
// Add info
|
|
info += usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nReserved: %02Xh\nAttributes: %08Xh (",
|
|
variableSize, variableSize,
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
variableHeader->State,
|
|
variableHeader->Reserved,
|
|
variableHeader->Attributes) + vssAttributesToUString(variableHeader->Attributes) + UString(")");
|
|
|
|
// Set subtype and add related info
|
|
if (isInvalid)
|
|
subtype = Subtypes::InvalidVssEntry;
|
|
else if (isAuthenticated) {
|
|
subtype = Subtypes::AuthVssEntry;
|
|
info += usprintf("\nMonotonic counter: %" PRIX64 "h\nTimestamp: ", monotonicCounter) + efiTimeToUString(timestamp)
|
|
+ usprintf("\nPubKey index: %u", pubKeyIndex);
|
|
}
|
|
else if (isAppleCrc32) {
|
|
subtype = Subtypes::AppleVssEntry;
|
|
info += usprintf("\nData checksum: %08Xh", storedCrc32) +
|
|
(storedCrc32 != calculatedCrc32 ? usprintf(", invalid, should be %08Xh", calculatedCrc32) : UString(", valid"));
|
|
}
|
|
else if (isIntelSpecial) {
|
|
subtype = Subtypes::IntelVssEntry;
|
|
}
|
|
else
|
|
subtype = Subtypes::StandardVssEntry;
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset + offset, Types::VssEntry, subtype, name, text, info, header, body, UByteArray(), Movable, index);
|
|
|
|
// Apply alignment, if needed
|
|
if (alignment) {
|
|
variableSize = ((variableSize + alignment - 1) & (~(alignment - 1)));
|
|
}
|
|
|
|
// Move to next variable
|
|
offset += variableSize;
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseFsysStoreBody(const UModelIndex & index)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// Get local offset
|
|
UINT32 localOffset = model->offset(index) + model->header(index).size();
|
|
|
|
// Get item data
|
|
const UByteArray 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
|
|
UINT8 nameSize = *(UINT8*)(data.constData() + offset);
|
|
bool valid = !(nameSize & 0x80); // Last bit is a validity bit, 0 means valid
|
|
nameSize &= 0x7F;
|
|
|
|
// Check sanity
|
|
if (unparsedSize >= nameSize + sizeof(UINT8)) {
|
|
variableSize = nameSize + sizeof(UINT8);
|
|
}
|
|
|
|
UByteArray 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
|
|
UByteArray header = data.mid(offset, sizeof(UINT8) + nameSize);
|
|
UString info = usprintf("Full size: %Xh (%u)", header.size(), header.size());
|
|
|
|
// Add EOF tree item
|
|
model->addItem(localOffset + offset, Types::FsysEntry, Subtypes::NormalFsysEntry, UString("EOF"), UString(), info, header, UByteArray(), UByteArray(), Fixed, index);
|
|
|
|
// Add free space
|
|
offset += header.size();
|
|
UByteArray body = data.mid(offset);
|
|
info = usprintf("Full size: %Xh (%u)", body.size(), body.size());
|
|
|
|
// Add free space tree item
|
|
model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), body, UByteArray(), Movable, index);
|
|
|
|
return U_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
|
|
UByteArray body = data.mid(offset);
|
|
UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size());
|
|
|
|
// Add padding tree item
|
|
model->addItem(localOffset + offset, Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
|
|
|
|
// Show message
|
|
msg(usprintf("%s: next variable appears too big, added as padding", __FUNCTION__), index);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Construct header and body
|
|
UByteArray header = data.mid(offset, sizeof(UINT8) + nameSize + sizeof(UINT16));
|
|
UByteArray body = data.mid(offset + sizeof(UINT8) + nameSize + sizeof(UINT16), dataSize);
|
|
|
|
// Add info
|
|
UString info = usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)",
|
|
variableSize, variableSize,
|
|
header.size(), header.size(),
|
|
body.size(), body.size());
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset + offset, Types::FsysEntry, valid ? Subtypes::NormalFsysEntry : Subtypes::InvalidFsysEntry, UString(name.constData()), UString(), info, header, body, UByteArray(), Movable, index);
|
|
|
|
// Move to next variable
|
|
offset += variableSize;
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS NvramParser::parseEvsaStoreBody(const UModelIndex & index)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// Obtain required information from parent volume
|
|
UINT8 emptyByte = 0xFF;
|
|
UModelIndex parentVolumeIndex = model->findParentOfType(index, Types::Volume);
|
|
if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
|
|
UByteArray data = model->parsingData(parentVolumeIndex);
|
|
const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
|
|
emptyByte = pdata->emptyByte;
|
|
}
|
|
|
|
// Get local offset
|
|
UINT32 localOffset = model->offset(index) + model->header(index).size();
|
|
|
|
// Get item data
|
|
const UByteArray data = model->body(index);
|
|
|
|
// Check that the is enough space for entry header
|
|
const UINT32 dataSize = (UINT32)data.size();
|
|
UINT32 offset = 0;
|
|
|
|
std::map<UINT16, EFI_GUID> guidMap;
|
|
std::map<UINT16, UString> nameMap;
|
|
|
|
// Parse all entries
|
|
UINT32 unparsedSize = dataSize;
|
|
while (unparsedSize) {
|
|
UINT32 variableSize = 0;
|
|
UString name;
|
|
UString info;
|
|
UByteArray header;
|
|
UByteArray body;
|
|
UINT8 subtype;
|
|
UINT8 calculated;
|
|
|
|
const EVSA_ENTRY_HEADER* entryHeader = (const EVSA_ENTRY_HEADER*)(data.constData() + offset);
|
|
|
|
// Check entry size
|
|
variableSize = sizeof(EVSA_ENTRY_HEADER);
|
|
if (unparsedSize < variableSize || unparsedSize < entryHeader->Size) {
|
|
UByteArray body = data.mid(offset);
|
|
UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size());
|
|
|
|
if (body.count(emptyByte) == body.size()) { // Free space
|
|
// Add free space tree item
|
|
model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), body, UByteArray(), Movable, index);
|
|
}
|
|
else {
|
|
// Add padding tree item
|
|
UModelIndex itemIndex = model->addItem(localOffset + offset, Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
|
|
|
|
// Show message
|
|
msg(usprintf("%s: variable parsing failed, the rest of unparsed store added as padding", __FUNCTION__), itemIndex);
|
|
}
|
|
break;
|
|
}
|
|
variableSize = entryHeader->Size;
|
|
|
|
// Recalculate entry checksum
|
|
calculated = calculateChecksum8(((const UINT8*)entryHeader) + 2, entryHeader->Size - 2);
|
|
|
|
// GUID entry
|
|
if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_GUID1 ||
|
|
entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_GUID2) {
|
|
const EVSA_GUID_ENTRY* guidHeader = (const EVSA_GUID_ENTRY*)entryHeader;
|
|
header = data.mid(offset, sizeof(EVSA_GUID_ENTRY));
|
|
body = data.mid(offset + sizeof(EVSA_GUID_ENTRY), guidHeader->Header.Size - sizeof(EVSA_GUID_ENTRY));
|
|
EFI_GUID guid = *(EFI_GUID*)body.constData();
|
|
name = guidToUString(guid);
|
|
info = UString("GUID: ") + guidToUString(guid, false) + usprintf("\nFull size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh",
|
|
variableSize, variableSize,
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
guidHeader->Header.Type,
|
|
guidHeader->Header.Checksum)
|
|
+ (guidHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid"))
|
|
+ usprintf("\nGuidId: %04Xh", guidHeader->GuidId);
|
|
subtype = Subtypes::GuidEvsaEntry;
|
|
guidMap.insert(std::pair<UINT16, EFI_GUID>(guidHeader->GuidId, guid));
|
|
}
|
|
// Name entry
|
|
else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME1 ||
|
|
entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME2) {
|
|
const EVSA_NAME_ENTRY* nameHeader = (const EVSA_NAME_ENTRY*)entryHeader;
|
|
header = data.mid(offset, sizeof(EVSA_NAME_ENTRY));
|
|
body = data.mid(offset + sizeof(EVSA_NAME_ENTRY), nameHeader->Header.Size - sizeof(EVSA_NAME_ENTRY));
|
|
name = UString::fromUtf16((const CHAR16*)body.constData());
|
|
info = UString("Name: ") + name + usprintf("\nFull size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh",
|
|
variableSize, variableSize,
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
nameHeader->Header.Type,
|
|
nameHeader->Header.Checksum)
|
|
+ (nameHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid"))
|
|
+ usprintf("\nVarId: %04Xh", nameHeader->VarId);
|
|
subtype = Subtypes::NameEvsaEntry;
|
|
nameMap.insert(std::pair<UINT16, UString>(nameHeader->VarId, name));
|
|
}
|
|
// Data entry
|
|
else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA1 ||
|
|
entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA2 ||
|
|
entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) {
|
|
const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)entryHeader;
|
|
// Check for extended header
|
|
UINT32 headerSize = sizeof(EVSA_DATA_ENTRY);
|
|
UINT32 dataSize = dataHeader->Header.Size - sizeof(EVSA_DATA_ENTRY);
|
|
if (dataHeader->Attributes & NVRAM_EVSA_DATA_EXTENDED_HEADER) {
|
|
const EVSA_DATA_ENTRY_EXTENDED* dataHeaderExtended = (const EVSA_DATA_ENTRY_EXTENDED*)entryHeader;
|
|
headerSize = sizeof(EVSA_DATA_ENTRY_EXTENDED);
|
|
dataSize = dataHeaderExtended->DataSize;
|
|
variableSize = headerSize + dataSize;
|
|
}
|
|
|
|
header = data.mid(offset, headerSize);
|
|
body = data.mid(offset + headerSize, dataSize);
|
|
name = UString("Data");
|
|
info = usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh",
|
|
variableSize, variableSize,
|
|
headerSize, headerSize,
|
|
dataSize, dataSize,
|
|
dataHeader->Header.Type,
|
|
dataHeader->Header.Checksum)
|
|
+ (dataHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid"))
|
|
+ usprintf("\nVarId: %04Xh\nGuidId: %04Xh\nAttributes: %08Xh (",
|
|
dataHeader->VarId,
|
|
dataHeader->GuidId,
|
|
dataHeader->Attributes)
|
|
+ evsaAttributesToUString(dataHeader->Attributes) + UString(")");
|
|
subtype = Subtypes::DataEvsaEntry;
|
|
}
|
|
// Unknown entry or free space
|
|
else {
|
|
UByteArray body = data.mid(offset);
|
|
UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size());
|
|
|
|
if (body.count(emptyByte) == body.size()) { // Free space
|
|
// Add free space tree item
|
|
model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), body, UByteArray(), Movable, index);
|
|
}
|
|
else {
|
|
// Add padding tree item
|
|
UModelIndex itemIndex = model->addItem(localOffset + offset, Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
|
|
|
|
// Show message
|
|
msg(usprintf("%s: unknown variable of type %02Xh found at offset %Xh, the rest of unparsed store added as padding", __FUNCTION__, entryHeader->Type, offset), itemIndex);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset + offset, Types::EvsaEntry, subtype, name, UString(), info, header, body, UByteArray(), Movable, index);
|
|
|
|
// Move to next variable
|
|
offset += variableSize;
|
|
unparsedSize = dataSize - offset;
|
|
}
|
|
|
|
// Reparse all data variables to detect invalid ones and assign name and test to valid ones
|
|
for (int i = 0; i < model->rowCount(index); i++) {
|
|
UModelIndex current = index.child(i, 0);
|
|
if (model->subtype(current) == Subtypes::DataEvsaEntry) {
|
|
UByteArray header = model->header(current);
|
|
const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)header.constData();
|
|
UString guid;
|
|
if (guidMap.count(dataHeader->GuidId))
|
|
guid = guidToUString(guidMap[dataHeader->GuidId], false);
|
|
UString name;
|
|
if (nameMap.count(dataHeader->VarId))
|
|
name = nameMap[dataHeader->VarId];
|
|
|
|
// Check for variable validity
|
|
if (guid.isEmpty() && name.isEmpty()) { // Both name and guid aren't found
|
|
model->setSubtype(current, Subtypes::InvalidEvsaEntry);
|
|
model->setName(current, UString("Invalid"));
|
|
msg(usprintf("%s: data variable with invalid GuidId and invalid VarId", __FUNCTION__), current);
|
|
}
|
|
else if (guid.isEmpty()) { // Guid not found
|
|
model->setSubtype(current, Subtypes::InvalidEvsaEntry);
|
|
model->setName(current, UString("Invalid"));
|
|
msg(usprintf("%s: data variable with invalid GuidId", __FUNCTION__), current);
|
|
}
|
|
else if (name.isEmpty()) { // Name not found
|
|
model->setSubtype(current, Subtypes::InvalidEvsaEntry);
|
|
model->setName(current, UString("Invalid"));
|
|
msg(usprintf("%s: data variable with invalid VarId", __FUNCTION__), current);
|
|
}
|
|
else { // Variable is OK, rename it
|
|
if (dataHeader->Header.Type == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) {
|
|
model->setSubtype(current, Subtypes::InvalidEvsaEntry);
|
|
model->setName(current, UString("Invalid"));
|
|
}
|
|
else {
|
|
model->setName(current, guid);
|
|
}
|
|
model->setText(current, name);
|
|
model->addInfo(current, UString("GUID: ") + guid + UString("\nName: ") + name + UString("\n"), false);
|
|
}
|
|
}
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
|
|
USTATUS NvramParser::parseFlashMapBody(const UModelIndex & index)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// Get parsing data for the current item
|
|
UINT32 localOffset = model->offset(index) + model->header(index).size();
|
|
const UByteArray data = model->body(index);
|
|
|
|
|
|
const UINT32 dataSize = (UINT32)data.size();
|
|
UINT32 offset = 0;
|
|
UINT32 unparsedSize = dataSize;
|
|
// Parse all entries
|
|
while (unparsedSize) {
|
|
const PHOENIX_FLASH_MAP_ENTRY* entryHeader = (const PHOENIX_FLASH_MAP_ENTRY*)(data.constData() + offset);
|
|
|
|
// Check entry size
|
|
if (unparsedSize < sizeof(PHOENIX_FLASH_MAP_ENTRY)) {
|
|
// Last variable is bad, add the rest as padding and return
|
|
UByteArray body = data.mid(offset);
|
|
UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size());
|
|
|
|
// Add padding tree item
|
|
model->addItem(localOffset + offset, Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
|
|
|
|
// Show message
|
|
if (unparsedSize < entryHeader->Size)
|
|
msg(usprintf("%s: next entry appears too big, added as padding", __FUNCTION__), index);
|
|
|
|
break;
|
|
}
|
|
|
|
UString name = guidToUString(entryHeader->Guid);
|
|
|
|
// Construct header
|
|
UByteArray header = data.mid(offset, sizeof(PHOENIX_FLASH_MAP_ENTRY));
|
|
|
|
// Add info
|
|
UString info = UString("Entry GUID: ") + guidToUString(entryHeader->Guid, false) +
|
|
usprintf("\nFull size: 24h (36)\nHeader size: 24h (36)\nBody size: 0h (0)\n"
|
|
"Entry type: %04Xh\nData type: %04Xh\nMemory address: %08Xh\nSize: %08Xh\nOffset: %08Xh",
|
|
entryHeader->EntryType,
|
|
entryHeader->DataType,
|
|
entryHeader->PhysicalAddress,
|
|
entryHeader->Size,
|
|
entryHeader->Offset);
|
|
|
|
// Determine subtype
|
|
UINT8 subtype = 0;
|
|
switch (entryHeader->DataType) {
|
|
case NVRAM_PHOENIX_FLASH_MAP_ENTRY_TYPE_VOLUME:
|
|
subtype = Subtypes::VolumeFlashMapEntry;
|
|
break;
|
|
case NVRAM_PHOENIX_FLASH_MAP_ENTRY_TYPE_DATA_BLOCK:
|
|
subtype = Subtypes::DataFlashMapEntry;
|
|
break;
|
|
}
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset + offset, Types::FlashMapEntry, subtype, name, flashMapGuidToUString(entryHeader->Guid), info, header, UByteArray(), UByteArray(), Movable, index);
|
|
|
|
// Move to next variable
|
|
offset += sizeof(PHOENIX_FLASH_MAP_ENTRY);
|
|
unparsedSize = dataSize - offset;
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
#endif // U_ENABLE_NVRAM_PARSING_SUPPORT
|