Support for _FDC and Fsys NVRAM formats

- only one format remains - EVSA
- added scope to text search UI, because of NVRAM variables having texts
in headers
This commit is contained in:
Nikolaj Schlej 2016-04-05 00:47:34 +02:00
parent d648ce133e
commit 40200bca12
13 changed files with 426 additions and 76 deletions

View File

@ -156,7 +156,7 @@ STATUS FfsFinder::findGuidPattern(const QModelIndex & index, const QByteArray &
return ERR_SUCCESS; return ERR_SUCCESS;
} }
STATUS FfsFinder::findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive) STATUS FfsFinder::findTextPattern(const QModelIndex & index, const QString & pattern, const UINT8 mode, const bool unicode, const Qt::CaseSensitivity caseSensitive)
{ {
if (pattern.isEmpty()) if (pattern.isEmpty())
return ERR_INVALID_PARAMETER; return ERR_INVALID_PARAMETER;
@ -166,24 +166,36 @@ STATUS FfsFinder::findTextPattern(const QModelIndex & index, const QString & pat
bool hasChildren = (model->rowCount(index) > 0); bool hasChildren = (model->rowCount(index) > 0);
for (int i = 0; i < model->rowCount(index); i++) { for (int i = 0; i < model->rowCount(index); i++) {
findTextPattern(index.child(i, index.column()), pattern, unicode, caseSensitive); findTextPattern(index.child(i, index.column()), pattern, mode, unicode, caseSensitive);
} }
if (hasChildren) QByteArray body;
return ERR_SUCCESS; if (hasChildren) {
if (mode != SEARCH_MODE_BODY)
body = model->header(index);
}
else {
if (mode == SEARCH_MODE_HEADER)
body.append(model->header(index));
else if (mode == SEARCH_MODE_BODY)
body.append(model->body(index));
else
body.append(model->header(index)).append(model->body(index));
}
QString data; QString data;
if (unicode) if (unicode)
data = QString::fromUtf16((const ushort*)model->body(index).data(), model->body(index).length() / 2); data = QString::fromUtf16((const ushort*)body.constData(), body.length() / 2);
else else
data = QString::fromLatin1((const char*)model->body(index).data(), model->body(index).length()); data = QString::fromLatin1((const char*)body.constData(), body.length());
int offset = -1; int offset = -1;
while ((offset = data.indexOf(pattern, offset + 1, caseSensitive)) >= 0) { while ((offset = data.indexOf(pattern, offset + 1, caseSensitive)) >= 0) {
msg(QObject::tr("%1 text \"%2\" found in %3 at offset %4h") msg(QObject::tr("%1 text \"%2\" found in %3 at %4-offset %5h")
.arg(unicode ? "Unicode" : "ASCII") .arg(unicode ? "Unicode" : "ASCII")
.arg(pattern) .arg(pattern)
.arg(model->name(index)) .arg(model->name(index))
.arg(mode == SEARCH_MODE_BODY ? QObject::tr("body") : QObject::tr("header"))
.hexarg(unicode ? offset * 2 : offset), .hexarg(unicode ? offset * 2 : offset),
index); index);
} }

View File

@ -36,7 +36,7 @@ public:
STATUS findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode); STATUS findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode);
STATUS findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode); STATUS findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode);
STATUS findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive); STATUS findTextPattern(const QModelIndex & index, const QString & pattern, const UINT8 mode, const bool unicode, const Qt::CaseSensitivity caseSensitive);
private: private:
const TreeModel* model; const TreeModel* model;

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>400</width>
<height>237</height> <height>218</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -143,20 +143,66 @@
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="textLabel"> <widget class="QLabel" name="textLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>Text:</string> <string>Text:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="textEdit"/> <widget class="QLineEdit" name="textEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="hexGroupBox_2">
<property name="title">
<string>Search scope</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QRadioButton" name="textScopeFullRadioButton">
<property name="text">
<string>Header and body</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="textScopeHeaderRadioButton">
<property name="text">
<string>Header only</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="textScopeBodyRadioButton">
<property name="text">
<string>Body only</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="2">
<widget class="QGroupBox" name="textGroupBox"> <widget class="QGroupBox" name="textGroupBox">
<property name="title"> <property name="title">
<string>Text search options</string> <string>Text search options</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QCheckBox" name="textUnicodeCheckBox"> <widget class="QCheckBox" name="textUnicodeCheckBox">
<property name="text"> <property name="text">

View File

@ -17,7 +17,7 @@
UEFITool::UEFITool(QWidget *parent) : UEFITool::UEFITool(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::UEFITool), ui(new Ui::UEFITool),
version(tr("0.30.0_alpha23")) version(tr("0.30.0_alpha24"))
{ {
clipboard = QApplication::clipboard(); clipboard = QApplication::clipboard();
@ -175,7 +175,8 @@ void UEFITool::populateUi(const QModelIndex &current)
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 || type == Types::NvramVariableVss); ui->menuVariableActions->setEnabled(type == Types::NvramVariableNvar || type == Types::NvramVariableVss || type == Types::NvramVariableFsys);
ui->menuStorageActions->setEnabled(type == Types::NvramStorageVss || type == Types::NvramStorageFdc || type == Types::NvramStorageFsys);
// Enable actions // Enable actions
ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current)); ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current));
@ -267,8 +268,14 @@ void UEFITool::search()
QString pattern = searchDialog->ui->textEdit->text(); QString pattern = searchDialog->ui->textEdit->text();
if (pattern.isEmpty()) if (pattern.isEmpty())
return; return;
UINT8 mode;
ffsFinder->findTextPattern(rootIndex, pattern, searchDialog->ui->textUnicodeCheckBox->isChecked(), if (searchDialog->ui->textScopeHeaderRadioButton->isChecked())
mode = SEARCH_MODE_HEADER;
else if (searchDialog->ui->textScopeBodyRadioButton->isChecked())
mode = SEARCH_MODE_BODY;
else
mode = SEARCH_MODE_ALL;
ffsFinder->findTextPattern(rootIndex, pattern, mode, searchDialog->ui->textUnicodeCheckBox->isChecked(),
(Qt::CaseSensitivity) searchDialog->ui->textCaseSensitiveCheckBox->isChecked()); (Qt::CaseSensitivity) searchDialog->ui->textCaseSensitiveCheckBox->isChecked());
showFinderMessages(); showFinderMessages();
} }
@ -537,8 +544,12 @@ void UEFITool::extract(const UINT8 mode)
path = QFileDialog::getSaveFileName(this, tr("Save variable to file"), name + ".var", "Variable files (*.var *.bin);;All files (*)"); path = QFileDialog::getSaveFileName(this, tr("Save variable to file"), name + ".var", "Variable files (*.var *.bin);;All files (*)");
break; break;
case Types::NvramStorageVss: case Types::NvramStorageVss:
case Types::NvramStorageFdc:
path = QFileDialog::getSaveFileName(this, tr("Save variable storage to file"), name + ".vss", "Variable storage files (*.vss *.bin);;All files (*)"); path = QFileDialog::getSaveFileName(this, tr("Save variable storage to file"), name + ".vss", "Variable storage files (*.vss *.bin);;All files (*)");
break; break;
case Types::NvramStorageFsys:
path = QFileDialog::getSaveFileName(this, tr("Save Fsys storage to file"), name + ".fsys", "Fsys storage files (*.fsys *.bin);;All files (*)");
break;
default: 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 (*)");
} }
@ -576,8 +587,12 @@ void UEFITool::extract(const UINT8 mode)
path = QFileDialog::getSaveFileName(this, tr("Save variable body to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); path = QFileDialog::getSaveFileName(this, tr("Save variable body to file"), name + ".bin", "Binary files (*.bin);;All files (*)");
break; break;
case Types::NvramStorageVss: case Types::NvramStorageVss:
case Types::NvramStorageFdc:
path = QFileDialog::getSaveFileName(this, tr("Save variable storage body to file"), name + ".vsb", "Variable storage body files (*.vsb *.bin);;All files (*)"); path = QFileDialog::getSaveFileName(this, tr("Save variable storage body to file"), name + ".vsb", "Variable storage body files (*.vsb *.bin);;All files (*)");
break; break;
case Types::NvramStorageFsys:
path = QFileDialog::getSaveFileName(this, tr("Save Fsys storage body to file"), name + ".fsb", "Fsys storage body files (*.fsb *.bin);;All files (*)");
break;
default: 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 (*)");
} }
@ -941,9 +956,12 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event)
break; break;
case Types::NvramVariableNvar: case Types::NvramVariableNvar:
case Types::NvramVariableVss: case Types::NvramVariableVss:
case Types::NvramVariableFsys:
ui->menuVariableActions->exec(event->globalPos()); ui->menuVariableActions->exec(event->globalPos());
break; break;
case Types::NvramStorageVss: case Types::NvramStorageVss:
case Types::NvramStorageFdc:
case Types::NvramStorageFsys:
ui->menuStorageActions->exec(event->globalPos()); ui->menuStorageActions->exec(event->globalPos());
break; break;
} }

View File

@ -113,7 +113,7 @@ typedef struct _EFI_FIRMWARE_VOLUME_HEADER {
UINT16 ExtHeaderOffset; //Reserved in Revision 1 UINT16 ExtHeaderOffset; //Reserved in Revision 1
UINT8 Reserved; UINT8 Reserved;
UINT8 Revision; UINT8 Revision;
//EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[1]; //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[2];
} EFI_FIRMWARE_VOLUME_HEADER; } EFI_FIRMWARE_VOLUME_HEADER;
// Standard file system GUIDs // Standard file system GUIDs

View File

@ -74,6 +74,8 @@ STATUS FfsOperations::extract(const QModelIndex & index, QString & name, QByteAr
case Types::Region: case Types::Region:
case Types::Padding: case Types::Padding:
case Types::NvramStorageVss: case Types::NvramStorageVss:
case Types::NvramStorageFdc:
case Types::NvramStorageFsys:
default: default:
name = itemName.replace(' ', '_').replace('/', '_'); name = itemName.replace(' ', '_').replace('/', '_');
} }

View File

@ -3364,8 +3364,12 @@ STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex &
QModelIndex current = index.child(i, 0); QModelIndex current = index.child(i, 0);
switch (model->type(current)) { switch (model->type(current)) {
case Types::NvramStorageVss: case Types::NvramStorageVss:
case Types::NvramStorageFdc:
parseVssStorageBody(current); parseVssStorageBody(current);
break; break;
case Types::NvramStorageFsys:
parseFsysStorageBody(current);
break;
case Types::Padding: case Types::Padding:
// No parsing required // No parsing required
break; break;
@ -3402,16 +3406,21 @@ STATUS FfsParser::findNextStorage(const QModelIndex & index, const QByteArray &
// msg(QObject::tr("findNextStorage: VSS storage candidate at offset %1h skipped, has invalid state %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->State, 2), index); // msg(QObject::tr("findNextStorage: VSS storage candidate at offset %1h skipped, has invalid state %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->State, 2), index);
// continue; // continue;
//} //}
// All checks passed, storage found // All checks passed, storage found
break; break;
} }
//else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE) { //Fsys signature found else if (*currentPos == NVRAM_FDC_VOLUME_SIGNATURE) { //FDC signature found
// // No checks yet // No checks needed
// break; break;
//} }
else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE) { //Fsys signature found
// No checks needed
break;
}
} }
// No more storages found // No more storages found
if (offset == dataSize - sizeof(UINT32)) if (offset >= dataSize - sizeof(UINT32))
return ERR_STORAGES_NOT_FOUND; return ERR_STORAGES_NOT_FOUND;
nextStorageOffset = offset; nextStorageOffset = offset;
@ -3421,66 +3430,198 @@ STATUS FfsParser::findNextStorage(const QModelIndex & index, const QByteArray &
STATUS FfsParser::getStorageSize(const QByteArray & data, const UINT32 storageOffset, UINT32 & storageSize) STATUS FfsParser::getStorageSize(const QByteArray & data, const UINT32 storageOffset, UINT32 & storageSize)
{ {
//TODO: add Fsys, GUID and _FDC support //TODO: add GUID support
const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)(data.constData() + storageOffset); const UINT32* signature = (const UINT32*)(data.constData() + storageOffset);
storageSize = vssHeader->Size; if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) {
const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)signature;
storageSize = vssHeader->Size;
}
else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) {
const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)signature;
storageSize = fdcHeader->Size;
}
else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE) {
const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)signature;
storageSize = fsysHeader->Size;
}
return ERR_SUCCESS; return ERR_SUCCESS;
} }
STATUS FfsParser::parseStorageHeader(const QByteArray & storage, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) STATUS FfsParser::parseStorageHeader(const QByteArray & storage, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index)
{ {
// Parse VSS volume like raw area, seen for now // Parse VSS volume like raw area
// $VSS, $SVS, Fsys, full volume GUID, _FDC and paddings //TODO: seen for now - $VSS, $SVS, Fsys, full volume GUID, _FDC and paddings
// The volume must begin with VSS storage to be valid, but after the first one, there can be many variants
const UINT32 dataSize = (UINT32)storage.size(); const UINT32 dataSize = (UINT32)storage.size();
if (dataSize < sizeof(VSS_VARIABLE_STORE_HEADER)) { const UINT32* signature = (const UINT32*)storage.constData();
msg(QObject::tr("parseStorageHeader: volume body is too small even for VSS storage header"), parent); if (dataSize < sizeof(UINT32)) {
msg(QObject::tr("parseStorageHeader: volume body is too small even for storage signature"), parent);
return ERR_SUCCESS; return ERR_SUCCESS;
} }
// Get VSS storage header // VSS variable storages
const VSS_VARIABLE_STORE_HEADER* vssStorageHeader = (const VSS_VARIABLE_STORE_HEADER*)storage.constData(); if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) {
// The volume must begin with a storage to be valid, but after the first one, there can be many variants
if (dataSize < sizeof(VSS_VARIABLE_STORE_HEADER)) {
msg(QObject::tr("parseStorageHeader: volume body is too small even for VSS storage header"), parent);
return ERR_SUCCESS;
}
// Check signature // Get VSS storage header
if (vssStorageHeader->Signature != NVRAM_VSS_STORE_SIGNATURE && vssStorageHeader->Signature != NVRAM_APPLE_SVS_STORE_SIGNATURE) { const VSS_VARIABLE_STORE_HEADER* vssStorageHeader = (const VSS_VARIABLE_STORE_HEADER*)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: VSS storage size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(vssStorageHeader->Size, 8).arg(vssStorageHeader->Size)
.hexarg2(dataSize, 8).arg(dataSize), parent);
return ERR_SUCCESS;
}
// Get parsing data
PARSING_DATA pdata = parsingDataFromQModelIndex(parent);
// Construct header and body
QByteArray header = storage.left(sizeof(VSS_VARIABLE_STORE_HEADER));
QByteArray body = storage.mid(sizeof(VSS_VARIABLE_STORE_HEADER), vssStorageHeader->Size - sizeof(VSS_VARIABLE_STORE_HEADER));
// Add info
QString name = QObject::tr("VSS storage");
QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nFormat: %8h\nState: %9h")
.hexarg2(vssStorageHeader->Signature, 8)
.hexarg(vssStorageHeader->Size).arg(vssStorageHeader->Size)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size())
.hexarg2(vssStorageHeader->Format, 2)
.hexarg2(vssStorageHeader->State, 2);
// Add unknown field for $SVS storages
if (*signature == NVRAM_APPLE_SVS_STORE_SIGNATURE)
info += QObject::tr("\nUnknown: %1h").hexarg2(vssStorageHeader->Unknown, 4);
// Add correct offset
pdata.offset = parentOffset;
// Add tree item
index = model->addItem(Types::NvramStorageVss, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
} }
else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) {
// The volume must begin with a storage to be valid, but after the first one, there can be many variants
if (dataSize < sizeof(FDC_VOLUME_HEADER)) {
msg(QObject::tr("parseStorageHeader: volume body is too small even for FDC storage header"), parent);
return ERR_SUCCESS;
}
// Check storage size // Get VSS storage header
if (dataSize < vssStorageHeader->Size) { const FDC_VOLUME_HEADER* fdcStorageHeader = (const FDC_VOLUME_HEADER*)signature;
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) // Check storage size
.hexarg2(dataSize, 8).arg(dataSize), parent); if (dataSize < fdcStorageHeader->Size) {
return ERR_SUCCESS; msg(QObject::tr("parseStorageHeader: FDC storage size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(fdcStorageHeader->Size, 8).arg(fdcStorageHeader->Size)
.hexarg2(dataSize, 8).arg(dataSize), parent);
return ERR_SUCCESS;
}
// Determine internal volume header size
const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(fdcStorageHeader + 1);
UINT32 headerSize;
if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) {
const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)((const UINT8*)volumeHeader + volumeHeader->ExtHeaderOffset);
headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize;
}
else
headerSize = volumeHeader->HeaderLength;
// Extended header end can be unaligned
headerSize = ALIGN8(headerSize);
// Add VSS storage header
headerSize += sizeof(VSS_VARIABLE_STORE_HEADER);
// Add FDC header
headerSize += sizeof(FDC_VOLUME_HEADER);
// Check sanity of combined header size
if (dataSize < headerSize) {
msg(QObject::tr("parseStorageHeader: FDC storage header size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(fdcStorageHeader->Size, 8).arg(fdcStorageHeader->Size)
.hexarg2(dataSize, 8).arg(dataSize), parent);
return ERR_SUCCESS;
}
// Get parsing data
PARSING_DATA pdata = parsingDataFromQModelIndex(parent);
// Construct header and body
QByteArray header = storage.left(headerSize);
QByteArray body = storage.mid(headerSize, fdcStorageHeader->Size - headerSize);
// Add info
QString name = QObject::tr("FDC storage");
QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)")
.hexarg2(fdcStorageHeader->Signature, 8)
.hexarg(fdcStorageHeader->Size).arg(fdcStorageHeader->Size)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size());
// TODO: add internal headers info
// Add correct offset
pdata.offset = parentOffset;
// Add tree item
index = model->addItem(Types::NvramStorageFdc, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
} }
else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE) {
// The volume must begin with a storage to be valid, but after the first one, there can be many variants
if (dataSize < sizeof(APPLE_FSYS_STORE_HEADER)) {
msg(QObject::tr("parseStorageHeader: volume body is too small even for Fsys storage header"), parent);
return ERR_SUCCESS;
}
// Get parsing data // Get Fsys storage header
PARSING_DATA pdata = parsingDataFromQModelIndex(parent); const APPLE_FSYS_STORE_HEADER* fsysStorageHeader = (const APPLE_FSYS_STORE_HEADER*)signature;
// Construct header and body // Check storage size
QByteArray header = storage.left(sizeof(VSS_VARIABLE_STORE_HEADER)); if (dataSize < fsysStorageHeader->Size) {
QByteArray body = storage.mid(sizeof(VSS_VARIABLE_STORE_HEADER), vssStorageHeader->Size - sizeof(VSS_VARIABLE_STORE_HEADER)); msg(QObject::tr("parseStorageHeader: Fsys storage size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(fsysStorageHeader->Size, 4).arg(fsysStorageHeader->Size)
.hexarg2(dataSize, 8).arg(dataSize), parent);
return ERR_SUCCESS;
}
// Add info // Get parsing data
QString name = QObject::tr("VSS storage"); PARSING_DATA pdata = parsingDataFromQModelIndex(parent);
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 // Construct header and body
pdata.offset = parentOffset; QByteArray header = storage.left(sizeof(APPLE_FSYS_STORE_HEADER));
QByteArray body = storage.mid(sizeof(APPLE_FSYS_STORE_HEADER), fsysStorageHeader->Size - sizeof(APPLE_FSYS_STORE_HEADER) - sizeof(UINT32));
// Add tree item // Check storage checksum
index = model->addItem(Types::NvramStorageVss, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent); UINT32 storedCrc = *(UINT32*)storage.right(sizeof(UINT32)).constBegin();
UINT32 calculatedCrc = calculatedCrc = crc32(0, (const UINT8*)storage.constData(), (const UINT32)storage.size() - sizeof(UINT32));
//Parse the storage // Add info
//parseVssStorageBody(body, index); QString name = QObject::tr("Fsys storage");
QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nUnknown: %9 %10 %11 %12 %13\nCRC32: %14")
.hexarg2(fsysStorageHeader->Signature, 8)
.hexarg(fsysStorageHeader->Size).arg(fsysStorageHeader->Size)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size())
.hexarg2(fsysStorageHeader->Unknown[0], 2)
.hexarg2(fsysStorageHeader->Unknown[1], 2)
.hexarg2(fsysStorageHeader->Unknown[2], 2)
.hexarg2(fsysStorageHeader->Unknown[3], 2)
.hexarg2(fsysStorageHeader->Unknown[4], 2)
.arg(storedCrc == calculatedCrc ? QObject::tr("%1h, valid").hexarg2(storedCrc, 8) : QObject::tr("%1h, invalid, should be %2h").hexarg2(storedCrc, 8).hexarg2(calculatedCrc, 8));
// Add correct offset
pdata.offset = parentOffset;
// Add tree item
index = model->addItem(Types::NvramStorageFsys, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
}
return ERR_SUCCESS; return ERR_SUCCESS;
} }
@ -3689,3 +3830,111 @@ STATUS FfsParser::parseVssStorageBody(const QModelIndex & index)
return ERR_SUCCESS; return ERR_SUCCESS;
} }
STATUS FfsParser::parseFsysStorageBody(const QModelIndex & index)
{
// Sanity check
if (!index.isValid())
return ERR_INVALID_PARAMETER;
// Get parsing data for the current item
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
UINT32 parentOffset = pdata.offset + model->header(index).size();
const QByteArray data = model->body(index);
// Check that the is enough space for variable header
const UINT32 dataSize = (UINT32)data.size();
UINT32 offset = 0;
// Parse all variables
while (1) {
UINT32 unparsedSize = dataSize - offset;
UINT32 variableSize = 0;
// Get nameSize and name of the variable
const UINT8 nameSize = *(UINT8*)(data.constData() + offset);
// Check sanity
if (unparsedSize >= nameSize + sizeof(UINT8)) {
variableSize = nameSize + sizeof(UINT8);
}
QByteArray name;
if (variableSize) {
name = data.mid(offset + sizeof(UINT8), nameSize);
// Check for EOF variable
if (nameSize == 3 && name[0] == 'E' && name[1] == 'O' && name[2] == 'F') {
// There is no data afterward, add EOF variable and free space and return
QByteArray header = data.mid(offset, sizeof(UINT8) + nameSize);
QString info = QObject::tr("Full size: %1h (%2)")
.hexarg(header.size()).arg(header.size());
// Add correct offset to parsing data
pdata.offset = parentOffset + offset;
// Add EOF tree item
model->addItem(Types::NvramVariableFsys, 0, name, QString(), info, header, QByteArray(), FALSE, parsingDataToQByteArray(pdata), index);
// Add free space
offset += header.size();
unparsedSize = dataSize - offset;
QByteArray body = data.mid(offset);
info = QObject::tr("Full size: %1h (%2)")
.hexarg(body.size()).arg(body.size());
// Add correct offset to parsing data
pdata.offset = parentOffset + offset;
// Add free space tree item
model->addItem(Types::FreeSpace, 0, QObject::tr("Free space"), QString(), info, QByteArray(), body, FALSE, parsingDataToQByteArray(pdata), index);
return ERR_SUCCESS;
}
}
// Get dataSize and data of the variable
const UINT16 dataSize = *(UINT16*)(data.constData() + offset + sizeof(UINT8) + nameSize);
if (unparsedSize >= sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize) {
variableSize = sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize;
}
else {
// Last variable is bad, add the rest as padding and return
QByteArray body = data.mid(offset);
QString info = QObject::tr("Full size: %1h (%2)")
.hexarg(body.size()).arg(body.size());
// Add correct offset to parsing data
pdata.offset = parentOffset + offset;
// Add free space tree item
model->addItem(Types::Padding, getPaddingType(body), QObject::tr("Padding"), QString(), info, QByteArray(), body, FALSE, parsingDataToQByteArray(pdata), index);
// Show message
msg(QObject::tr("parseFsysStorageBody: variable appears too big, added as padding"), index);
return ERR_SUCCESS;
}
// Construct header and body
QByteArray header = data.mid(offset, sizeof(UINT8) + nameSize + sizeof(UINT16));
QByteArray body = data.mid(offset + sizeof(UINT8) + nameSize + sizeof(UINT16), dataSize);
// Add info
QString info = QObject::tr("Full size: %1h (%2)\nHeader size %3h (%4)\nBody size: %5h (%6)")
.hexarg(variableSize).arg(variableSize)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size());
// Add correct offset to parsing data
pdata.offset = parentOffset + offset;
// Add tree item
model->addItem(Types::NvramVariableFsys, 0, name, QString(), info, header, body, FALSE, parsingDataToQByteArray(pdata), index);
// Move to next variable
offset += variableSize;
}
return ERR_SUCCESS;
}

View File

@ -114,6 +114,7 @@ private:
STATUS getStorageSize(const QByteArray & data, const UINT32 storageOffset, UINT32 & storageSize); STATUS getStorageSize(const QByteArray & data, const UINT32 storageOffset, UINT32 & storageSize);
STATUS parseStorageHeader(const QByteArray & storage, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parseStorageHeader(const QByteArray & storage, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
STATUS parseVssStorageBody(const QModelIndex & index); STATUS parseVssStorageBody(const QModelIndex & index);
STATUS parseFsysStorageBody(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());

View File

@ -99,10 +99,9 @@ typedef struct _VSS_VARIABLE_STORE_HEADER {
// Apple Fsys store header // Apple Fsys store header
typedef struct _APPLE_FSYS_STORE_HEADER { typedef struct _APPLE_FSYS_STORE_HEADER {
UINT32 Signature; // Fsys signature UINT32 Signature; // Fsys signature
UINT8 Unknown; // Still unknown UINT8 Unknown[5]; // Still unknown
UINT32 Unknown2; // Still unknown UINT16 Size; // Size of variable storage
UINT16 Size; // Size of variable storage
} APPLE_FSYS_STORE_HEADER; } APPLE_FSYS_STORE_HEADER;
// Apple Fsys variable format // Apple Fsys variable format
@ -110,8 +109,8 @@ typedef struct _APPLE_FSYS_STORE_HEADER {
// CHAR8 Name[]; // CHAR8 Name[];
// UINT16 DataLength; // UINT16 DataLength;
// UINT8 Data[] // UINT8 Data[]
// End with a chunk named "EOF" without data // Storage ends with a chunk named "EOF" without data
// All free bytes are zeros // All free bytes in storage are zeroed
// Has CRC32 of the whole store without checksum field at the end // Has CRC32 of the whole store without checksum field at the end
// Normal variable header // Normal variable header
@ -168,6 +167,19 @@ typedef struct _VSS_AUTH_VARIABLE_HEADER {
#define NVRAM_VSS_VARIABLE_APPEND_WRITE 0x00000040 #define NVRAM_VSS_VARIABLE_APPEND_WRITE 0x00000040
#define NVRAM_VSS_VARIABLE_APPLE_DATA_CHECKSUM 0x80000000 #define NVRAM_VSS_VARIABLE_APPLE_DATA_CHECKSUM 0x80000000
// FDC region can be found in some VSS volumes
// It has another VSS volume inside
// _FDC header structure
typedef struct _FDC_VOLUME_HEADER {
UINT32 Signature;
UINT32 Size;
//EFI_FIRMWARE_VOLUME_HEADER VolumeHeader;
//EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[2];
//VSS_VARIABLE_STORE_HEADER VssHeader;
} FDC_VOLUME_HEADER;
#define NVRAM_FDC_VOLUME_SIGNATURE 0x4344465F
// Restore previous packing rules // Restore previous packing rules
#pragma pack(pop) #pragma pack(pop)

View File

@ -69,8 +69,14 @@ QString itemTypeToQString(const UINT8 type)
return QObject::tr("NVAR variable"); return QObject::tr("NVAR variable");
case Types::NvramStorageVss: case Types::NvramStorageVss:
return QObject::tr("VSS storage"); return QObject::tr("VSS storage");
case Types::NvramStorageFdc:
return QObject::tr("FDC storage");
case Types::NvramStorageFsys:
return QObject::tr("Fsys storage");
case Types::NvramVariableVss: case Types::NvramVariableVss:
return QObject::tr("VSS variable"); return QObject::tr("VSS variable");
case Types::NvramVariableFsys:
return QObject::tr("Fsys variable");
default: default:
return QObject::tr("Unknown"); return QObject::tr("Unknown");
} }
@ -140,6 +146,9 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype)
else else
return QObject::tr("Unknown subtype"); return QObject::tr("Unknown subtype");
case Types::NvramStorageVss: case Types::NvramStorageVss:
case Types::NvramStorageFdc:
case Types::NvramStorageFsys:
case Types::NvramVariableFsys:
return QString(); return QString();
case Types::NvramVariableVss: case Types::NvramVariableVss:
if (subtype == Subtypes::InvalidVss) if (subtype == Subtypes::InvalidVss)

View File

@ -45,7 +45,10 @@ namespace Types {
FreeSpace, FreeSpace,
NvramVariableNvar, NvramVariableNvar,
NvramStorageVss, NvramStorageVss,
NvramVariableVss NvramStorageFdc,
NvramStorageFsys,
NvramVariableVss,
NvramVariableFsys
}; };
} }

View File

@ -101,7 +101,7 @@ QString errorCodeToQString(UINT8 errorCode)
} }
// CRC32 implementation // CRC32 implementation
UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length) UINT32 crc32(UINT32 initial, const UINT8* buffer, const UINT32 length)
{ {
static const UINT32 crcTable[256] = { static const UINT32 crcTable[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
@ -141,12 +141,10 @@ UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length)
0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D };
UINT32 crc32;
UINT32 i;
// Accumulate crc32 for buffer // Accumulate crc32 for buffer
crc32 = initial ^ 0xFFFFFFFF; UINT32 crc32 = initial ^ 0xFFFFFFFF;
for (i = 0; i < length; i++) { for (UINT32 i = 0; i < length; i++) {
crc32 = (crc32 >> 8) ^ crcTable[(crc32 ^ buffer[i]) & 0xFF]; crc32 = (crc32 >> 8) ^ crcTable[(crc32 ^ buffer[i]) & 0xFF];
} }

View File

@ -35,7 +35,7 @@ extern STATUS decompress(const QByteArray & compressed, UINT8 & algorithm, QByte
//STATUS compress(const QByteArray & decompressed, QByteArray & compressed, const UINT8 & algorithm); //STATUS compress(const QByteArray & decompressed, QByteArray & compressed, const UINT8 & algorithm);
// CRC32 calculation routine // CRC32 calculation routine
extern UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length); extern UINT32 crc32(UINT32 initial, const UINT8* buffer, const UINT32 length);
// 8bit checksum calculation routine // 8bit checksum calculation routine
extern UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize); extern UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize);