Switch AMI NVAR parser to Kaitai

This commit is contained in:
Nikolaj Schlej 2023-02-19 12:24:20 -08:00
parent 2d1ebcc11b
commit 7eb565d788
17 changed files with 1330 additions and 411 deletions

View file

@ -33,6 +33,7 @@ SET(PROJECT_SOURCES
../common/ustring.cpp
../common/bstrlib/bstrlib.c
../common/bstrlib/bstrwrap.cpp
../common/generated/ami_nvar.cpp
../common/generated/intel_acbp_v1.cpp
../common/generated/intel_acbp_v2.cpp
../common/generated/intel_keym_v1.cpp

View file

@ -30,6 +30,7 @@ SET(PROJECT_SOURCES
../common/ustring.cpp
../common/bstrlib/bstrlib.c
../common/bstrlib/bstrwrap.cpp
../common/generated/ami_nvar.cpp
../common/generated/intel_acbp_v1.cpp
../common/generated/intel_acbp_v2.cpp
../common/generated/intel_keym_v1.cpp

View file

@ -70,6 +70,7 @@ SET(PROJECT_SOURCES
../common/digest/sha256.c
../common/digest/sha512.c
../common/digest/sm3.c
../common/generated/ami_nvar.cpp
../common/generated/intel_acbp_v1.cpp
../common/generated/intel_acbp_v2.cpp
../common/generated/intel_keym_v1.cpp

View file

@ -243,6 +243,7 @@ void UEFITool::populateUi(const QModelIndex &current)
|| type == Types::EvsaStore
|| type == Types::FtwStore
|| type == Types::FlashMapStore
|| type == Types::NvarGuidStore
|| type == Types::CmdbStore
|| type == Types::FptStore
|| type == Types::BpdtStore
@ -407,9 +408,8 @@ void UEFITool::goToData()
UByteArray rdata = model->parsingData(index);
const NVAR_ENTRY_PARSING_DATA* pdata = (const NVAR_ENTRY_PARSING_DATA*)rdata.constData();
UINT32 lastVariableFlag = pdata->emptyByte ? 0xFFFFFF : 0;
UINT32 offset = model->offset(index);
if (pdata->next == lastVariableFlag) {
if (pdata->next == 0xFFFFFF) {
ui->structureTreeView->scrollTo(index, QAbstractItemView::PositionAtCenter);
ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear);
}
@ -783,13 +783,7 @@ void UEFITool::showParserMessages()
std::vector<std::pair<QString, QModelIndex> > messages = ffsParser->getMessages();
#if QT_VERSION_MAJOR < 6
std::pair<QString, QModelIndex> msg;
foreach (msg, messages)
#else
for (const auto &msg : messages)
#endif
{
for (const auto &msg : messages) {
QListWidgetItem* item = new QListWidgetItem(msg.first, NULL, 0);
item->setData(Qt::UserRole, QByteArray((const char*)&msg.second, sizeof(msg.second)));
ui->parserMessagesListWidget->addItem(item);
@ -807,13 +801,7 @@ void UEFITool::showFinderMessages()
std::vector<std::pair<QString, QModelIndex> > messages = ffsFinder->getMessages();
#if QT_VERSION_MAJOR < 6
std::pair<QString, QModelIndex> msg;
foreach (msg, messages)
#else
for (const auto &msg : messages)
#endif
{
for (const auto &msg : messages) {
QListWidgetItem* item = new QListWidgetItem(msg.first, NULL, 0);
item->setData(Qt::UserRole, QByteArray((const char*)&msg.second, sizeof(msg.second)));;
ui->finderMessagesListWidget->addItem(item);
@ -832,13 +820,7 @@ void UEFITool::showBuilderMessages()
std::vector<std::pair<QString, QModelIndex> > messages = ffsBuilder->getMessages();
#if QT_VERSION_MAJOR < 6
std::pair<QString, QModelIndex> msg;
foreach (msg, messages)
#else
for (const auto &msg : messages)
#endif
{
for (const auto &msg : messages) {
QListWidgetItem* item = new QListWidgetItem(msg.first, NULL, 0);
item->setData(Qt::UserRole, QByteArray((const char*)&msg.second, sizeof(msg.second)));
ui->builderMessagesListWidget->addItem(item);
@ -891,8 +873,7 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event)
return;
}
switch (model->type(index))
{
switch (model->type(index)) {
case Types::Capsule: ui->menuCapsuleActions->exec(event->globalPos()); break;
case Types::Image: ui->menuImageActions->exec(event->globalPos()); break;
case Types::Region: ui->menuRegionActions->exec(event->globalPos()); break;
@ -907,6 +888,7 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event)
case Types::EvsaStore:
case Types::FtwStore:
case Types::FlashMapStore:
case Types::NvarGuidStore:
case Types::CmdbStore:
case Types::FptStore:
case Types::CpdStore:

View file

@ -46,9 +46,11 @@ HEADERS += uefitool.h \
../common/Tiano/EfiTianoCompress.h \
../common/ustring.h \
../common/ubytearray.h \
../common/umemstream.h \
../common/digest/sha1.h \
../common/digest/sha2.h \
../common/digest/sm3.h \
../common/generated/ami_nvar.h \
../common/generated/intel_acbp_v1.h \
../common/generated/intel_acbp_v2.h \
../common/generated/intel_keym_v1.h \
@ -103,6 +105,7 @@ SOURCES += uefitool_main.cpp \
../common/digest/sha256.c \
../common/digest/sha512.c \
../common/digest/sm3.c \
../common/generated/ami_nvar.cpp \
../common/generated/intel_acbp_v1.cpp \
../common/generated/intel_acbp_v2.cpp \
../common/generated/intel_keym_v1.cpp \

View file

@ -18,6 +18,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <stdint.h>
#include <stddef.h>
// TODO: improve
typedef size_t USTATUS;
#define U_SUCCESS 0
#define U_INVALID_PARAMETER 1
@ -76,6 +77,7 @@ typedef size_t USTATUS;
#define U_PATCH_OFFSET_OUT_OF_BOUNDS 54
#define U_INVALID_SYMBOL 55
#define U_ZLIB_DECOMPRESSION_FAILED 56
#define U_INVALID_STORE 57
#define U_INVALID_MANIFEST 251
#define U_UNKNOWN_MANIFEST_HEADER_VERSION 252

View file

@ -21,7 +21,7 @@
#include "utility.h"
#include "digest/sha2.h"
#include <sstream>
#include "umemstream.h"
#include "kaitai/kaitaistream.h"
#include "generated/intel_acbp_v1.h"
#include "generated/intel_acbp_v2.h"
@ -29,45 +29,6 @@
#include "generated/intel_keym_v2.h"
#include "generated/intel_acm.h"
// TODO: put into separate H/CPP when we start using Kaitai for other parsers
// TODO: this implementation is certainly not a valid replacement to std::stringstream
// TODO: because it only supports getting through the buffer once
// TODO: however, we already do it this way, so it's enough for practical purposes of this file
class membuf : public std::streambuf {
public:
membuf(const char *p, size_t l) {
setg((char*)p, (char*)p, (char*)p + l);
}
pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which = std::ios_base::in) override
{
(void)which;
if (dir == std::ios_base::cur)
gbump((int)off);
else if (dir == std::ios_base::end)
setg(eback(), egptr() + off, egptr());
else if (dir == std::ios_base::beg)
setg(eback(), eback() + off, egptr());
return gptr() - eback();
}
pos_type seekpos(pos_type sp, std::ios_base::openmode which) override
{
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
}
};
class memstream : public std::istream {
public:
memstream(const char *p, size_t l) : std::istream(&buffer_),
buffer_(p, l) {
rdbuf(&buffer_);
}
private:
membuf buffer_;
};
USTATUS FitParser::parseFit(const UModelIndex & index)
{
// Reset parser state
@ -318,7 +279,7 @@ USTATUS FitParser::parseFitEntryMicrocode(const UByteArray & microcode, const UI
USTATUS FitParser::parseFitEntryAcm(const UByteArray & acm, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize)
{
try {
memstream is(acm.constData(), acm.size());
umemstream is(acm.constData(), acm.size());
is.seekg(localOffset, is.beg);
kaitai::kstream ks(&is);
intel_acm_t parsed(&ks);
@ -436,7 +397,7 @@ USTATUS FitParser::parseFitEntryBootGuardKeyManifest(const UByteArray & keyManif
// v1
try {
memstream is(keyManifest.constData(), keyManifest.size());
umemstream is(keyManifest.constData(), keyManifest.size());
is.seekg(localOffset, is.beg);
kaitai::kstream ks(&is);
intel_keym_v1_t parsed(&ks);
@ -539,7 +500,7 @@ USTATUS FitParser::parseFitEntryBootGuardKeyManifest(const UByteArray & keyManif
// v2
try {
memstream is(keyManifest.constData(), keyManifest.size());
umemstream is(keyManifest.constData(), keyManifest.size());
is.seekg(localOffset, is.beg);
kaitai::kstream ks(&is);
intel_keym_v2_t parsed(&ks);
@ -671,7 +632,7 @@ USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolic
// v1
try {
memstream is(bootPolicy.constData(), bootPolicy.size());
umemstream is(bootPolicy.constData(), bootPolicy.size());
is.seekg(localOffset, is.beg);
kaitai::kstream ks(&is);
intel_acbp_v1_t parsed(&ks);
@ -935,7 +896,7 @@ USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolic
// v2
try {
memstream is(bootPolicy.constData(), bootPolicy.size());
umemstream is(bootPolicy.constData(), bootPolicy.size());
is.seekg(localOffset, is.beg);
kaitai::kstream ks(&is);
intel_acbp_v2_t parsed(&ks); // This already verified the version to be >= 0x20

View file

@ -0,0 +1,442 @@
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "ami_nvar.h"
#include "../kaitai/exceptions.h"
ami_nvar_t::ami_nvar_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this; (void)p__root;
m_entries = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void ami_nvar_t::_read() {
m_entries = new std::vector<nvar_entry_t*>();
{
int i = 0;
nvar_entry_t* _;
do {
_ = new nvar_entry_t(m__io, this, m__root);
m_entries->push_back(_);
i++;
} while (!( ((_->signature_first() != 78) || (_io()->is_eof())) ));
}
}
ami_nvar_t::~ami_nvar_t() {
_clean_up();
}
void ami_nvar_t::_clean_up() {
if (m_entries) {
for (std::vector<nvar_entry_t*>::iterator it = m_entries->begin(); it != m_entries->end(); ++it) {
delete *it;
}
delete m_entries; m_entries = 0;
}
}
ami_nvar_t::nvar_attributes_t::nvar_attributes_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_t* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void ami_nvar_t::nvar_attributes_t::_read() {
m_valid = m__io->read_bits_int_be(1);
m_auth_write = m__io->read_bits_int_be(1);
m_hw_error_record = m__io->read_bits_int_be(1);
m_extended_header = m__io->read_bits_int_be(1);
m_data_only = m__io->read_bits_int_be(1);
m_local_guid = m__io->read_bits_int_be(1);
m_ascii_name = m__io->read_bits_int_be(1);
m_runtime = m__io->read_bits_int_be(1);
}
ami_nvar_t::nvar_attributes_t::~nvar_attributes_t() {
_clean_up();
}
void ami_nvar_t::nvar_attributes_t::_clean_up() {
}
ami_nvar_t::ucs2_string_t::ucs2_string_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_body_t* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_ucs2_chars = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void ami_nvar_t::ucs2_string_t::_read() {
m_ucs2_chars = new std::vector<uint16_t>();
{
int i = 0;
uint16_t _;
do {
_ = m__io->read_u2le();
m_ucs2_chars->push_back(_);
i++;
} while (!(_ == 0));
}
}
ami_nvar_t::ucs2_string_t::~ucs2_string_t() {
_clean_up();
}
void ami_nvar_t::ucs2_string_t::_clean_up() {
if (m_ucs2_chars) {
delete m_ucs2_chars; m_ucs2_chars = 0;
}
}
ami_nvar_t::nvar_extended_attributes_t::nvar_extended_attributes_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_body_t* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void ami_nvar_t::nvar_extended_attributes_t::_read() {
m_reserved_high = m__io->read_bits_int_be(2);
m_time_based_auth = m__io->read_bits_int_be(1);
m_auth_write = m__io->read_bits_int_be(1);
m_reserved_low = m__io->read_bits_int_be(3);
m_checksum = m__io->read_bits_int_be(1);
}
ami_nvar_t::nvar_extended_attributes_t::~nvar_extended_attributes_t() {
_clean_up();
}
void ami_nvar_t::nvar_extended_attributes_t::_clean_up() {
}
ami_nvar_t::nvar_entry_t::nvar_entry_t(kaitai::kstream* p__io, ami_nvar_t* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_attributes = 0;
m_body = 0;
m__io__raw_body = 0;
f_offset = false;
f_end_offset = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void ami_nvar_t::nvar_entry_t::_read() {
n_invoke_offset = true;
if (offset() >= 0) {
n_invoke_offset = false;
m_invoke_offset = m__io->read_bytes(0);
}
m_signature_first = m__io->read_u1();
n_signature_rest = true;
if (signature_first() == 78) {
n_signature_rest = false;
m_signature_rest = m__io->read_bytes(3);
if (!(signature_rest() == std::string("\x56\x41\x52", 3))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x56\x41\x52", 3), signature_rest(), _io(), std::string("/types/nvar_entry/seq/2"));
}
}
n_size = true;
if (signature_first() == 78) {
n_size = false;
m_size = m__io->read_u2le();
{
uint16_t _ = size();
if (!(_ > ((4 + 2) + 4))) {
throw kaitai::validation_expr_error<uint16_t>(size(), _io(), std::string("/types/nvar_entry/seq/3"));
}
}
}
n_next = true;
if (signature_first() == 78) {
n_next = false;
m_next = m__io->read_bits_int_le(24);
}
m__io->align_to_byte();
n_attributes = true;
if (signature_first() == 78) {
n_attributes = false;
m_attributes = new nvar_attributes_t(m__io, this, m__root);
}
n_body = true;
if (signature_first() == 78) {
n_body = false;
m__raw_body = m__io->read_bytes((size() - ((4 + 2) + 4)));
m__io__raw_body = new kaitai::kstream(m__raw_body);
m_body = new nvar_entry_body_t(m__io__raw_body, this, m__root);
}
n_invoke_end_offset = true;
if ( ((signature_first() == 78) && (end_offset() >= 0)) ) {
n_invoke_end_offset = false;
m_invoke_end_offset = m__io->read_bytes(0);
}
}
ami_nvar_t::nvar_entry_t::~nvar_entry_t() {
_clean_up();
}
void ami_nvar_t::nvar_entry_t::_clean_up() {
if (!n_invoke_offset) {
}
if (!n_signature_rest) {
}
if (!n_size) {
}
if (!n_next) {
}
if (!n_attributes) {
if (m_attributes) {
delete m_attributes; m_attributes = 0;
}
}
if (!n_body) {
if (m__io__raw_body) {
delete m__io__raw_body; m__io__raw_body = 0;
}
if (m_body) {
delete m_body; m_body = 0;
}
}
if (!n_invoke_end_offset) {
}
}
int32_t ami_nvar_t::nvar_entry_t::offset() {
if (f_offset)
return m_offset;
m_offset = _io()->pos();
f_offset = true;
return m_offset;
}
int32_t ami_nvar_t::nvar_entry_t::end_offset() {
if (f_end_offset)
return m_end_offset;
m_end_offset = _io()->pos();
f_end_offset = true;
return m_end_offset;
}
ami_nvar_t::nvar_entry_body_t::nvar_entry_body_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_t* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_ucs2_name = 0;
m_extended_header_attributes = 0;
f_extended_header_attributes = false;
f_data_start_offset = false;
f_extended_header_size_field = false;
f_extended_header_timestamp = false;
f_data_size = false;
f_extended_header_checksum = false;
f_data_end_offset = false;
f_extended_header_size = false;
f_extended_header_hash = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void ami_nvar_t::nvar_entry_body_t::_read() {
n_guid_index = true;
if ( ((!(_parent()->attributes()->local_guid())) && (!(_parent()->attributes()->data_only())) && (_parent()->attributes()->valid())) ) {
n_guid_index = false;
m_guid_index = m__io->read_u1();
}
n_guid = true;
if ( ((_parent()->attributes()->local_guid()) && (!(_parent()->attributes()->data_only())) && (_parent()->attributes()->valid())) ) {
n_guid = false;
m_guid = m__io->read_bytes(16);
}
n_ascii_name = true;
if ( ((_parent()->attributes()->ascii_name()) && (!(_parent()->attributes()->data_only())) && (_parent()->attributes()->valid())) ) {
n_ascii_name = false;
m_ascii_name = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("ASCII"));
}
n_ucs2_name = true;
if ( ((!(_parent()->attributes()->ascii_name())) && (!(_parent()->attributes()->data_only())) && (_parent()->attributes()->valid())) ) {
n_ucs2_name = false;
m_ucs2_name = new ucs2_string_t(m__io, this, m__root);
}
n_invoke_data_start = true;
if (data_start_offset() >= 0) {
n_invoke_data_start = false;
m_invoke_data_start = m__io->read_bytes(0);
}
m_data = m__io->read_bytes_full();
}
ami_nvar_t::nvar_entry_body_t::~nvar_entry_body_t() {
_clean_up();
}
void ami_nvar_t::nvar_entry_body_t::_clean_up() {
if (!n_guid_index) {
}
if (!n_guid) {
}
if (!n_ascii_name) {
}
if (!n_ucs2_name) {
if (m_ucs2_name) {
delete m_ucs2_name; m_ucs2_name = 0;
}
}
if (!n_invoke_data_start) {
}
if (f_extended_header_attributes && !n_extended_header_attributes) {
if (m_extended_header_attributes) {
delete m_extended_header_attributes; m_extended_header_attributes = 0;
}
}
if (f_extended_header_size_field && !n_extended_header_size_field) {
}
if (f_extended_header_timestamp && !n_extended_header_timestamp) {
}
if (f_extended_header_checksum && !n_extended_header_checksum) {
}
if (f_extended_header_hash && !n_extended_header_hash) {
}
}
ami_nvar_t::nvar_extended_attributes_t* ami_nvar_t::nvar_entry_body_t::extended_header_attributes() {
if (f_extended_header_attributes)
return m_extended_header_attributes;
n_extended_header_attributes = true;
if ( ((_parent()->attributes()->valid()) && (_parent()->attributes()->extended_header()) && (extended_header_size() >= (1 + 2))) ) {
n_extended_header_attributes = false;
std::streampos _pos = m__io->pos();
m__io->seek((_io()->pos() - extended_header_size()));
m_extended_header_attributes = new nvar_extended_attributes_t(m__io, this, m__root);
m__io->seek(_pos);
f_extended_header_attributes = true;
}
return m_extended_header_attributes;
}
int32_t ami_nvar_t::nvar_entry_body_t::data_start_offset() {
if (f_data_start_offset)
return m_data_start_offset;
m_data_start_offset = _io()->pos();
f_data_start_offset = true;
return m_data_start_offset;
}
uint16_t ami_nvar_t::nvar_entry_body_t::extended_header_size_field() {
if (f_extended_header_size_field)
return m_extended_header_size_field;
n_extended_header_size_field = true;
if ( ((_parent()->attributes()->valid()) && (_parent()->attributes()->extended_header()) && (_parent()->size() > (((4 + 2) + 4) + 2))) ) {
n_extended_header_size_field = false;
std::streampos _pos = m__io->pos();
m__io->seek((_io()->pos() - 2));
m_extended_header_size_field = m__io->read_u2le();
m__io->seek(_pos);
f_extended_header_size_field = true;
}
return m_extended_header_size_field;
}
uint64_t ami_nvar_t::nvar_entry_body_t::extended_header_timestamp() {
if (f_extended_header_timestamp)
return m_extended_header_timestamp;
n_extended_header_timestamp = true;
if ( ((_parent()->attributes()->valid()) && (_parent()->attributes()->extended_header()) && (extended_header_size() >= ((1 + 8) + 2)) && (extended_header_attributes()->time_based_auth())) ) {
n_extended_header_timestamp = false;
std::streampos _pos = m__io->pos();
m__io->seek(((_io()->pos() - extended_header_size()) + 1));
m_extended_header_timestamp = m__io->read_u8le();
m__io->seek(_pos);
f_extended_header_timestamp = true;
}
return m_extended_header_timestamp;
}
int32_t ami_nvar_t::nvar_entry_body_t::data_size() {
if (f_data_size)
return m_data_size;
m_data_size = ((data_end_offset() - data_start_offset()) - extended_header_size());
f_data_size = true;
return m_data_size;
}
uint8_t ami_nvar_t::nvar_entry_body_t::extended_header_checksum() {
if (f_extended_header_checksum)
return m_extended_header_checksum;
n_extended_header_checksum = true;
if ( ((_parent()->attributes()->valid()) && (_parent()->attributes()->extended_header()) && (extended_header_size() >= ((1 + 1) + 2)) && (extended_header_attributes()->checksum())) ) {
n_extended_header_checksum = false;
std::streampos _pos = m__io->pos();
m__io->seek(((_io()->pos() - 2) - 1));
m_extended_header_checksum = m__io->read_u1();
m__io->seek(_pos);
f_extended_header_checksum = true;
}
return m_extended_header_checksum;
}
int32_t ami_nvar_t::nvar_entry_body_t::data_end_offset() {
if (f_data_end_offset)
return m_data_end_offset;
m_data_end_offset = _io()->pos();
f_data_end_offset = true;
return m_data_end_offset;
}
uint16_t ami_nvar_t::nvar_entry_body_t::extended_header_size() {
if (f_extended_header_size)
return m_extended_header_size;
m_extended_header_size = ((_parent()->attributes()->extended_header()) ? (((extended_header_size_field() >= (1 + 2)) ? (extended_header_size_field()) : (0))) : (0));
f_extended_header_size = true;
return m_extended_header_size;
}
std::string ami_nvar_t::nvar_entry_body_t::extended_header_hash() {
if (f_extended_header_hash)
return m_extended_header_hash;
n_extended_header_hash = true;
if ( ((_parent()->attributes()->valid()) && (_parent()->attributes()->extended_header()) && (extended_header_size() >= (((1 + 8) + 32) + 2)) && (extended_header_attributes()->time_based_auth()) && (!(_parent()->attributes()->data_only()))) ) {
n_extended_header_hash = false;
std::streampos _pos = m__io->pos();
m__io->seek((((_io()->pos() - extended_header_size()) + 1) + 8));
m_extended_header_hash = m__io->read_bytes(32);
m__io->seek(_pos);
f_extended_header_hash = true;
}
return m_extended_header_hash;
}

398
common/generated/ami_nvar.h Normal file
View file

@ -0,0 +1,398 @@
#ifndef AMI_NVAR_H_
#define AMI_NVAR_H_
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "../kaitai/kaitaistruct.h"
#include <stdint.h>
#include <vector>
#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
class ami_nvar_t : public kaitai::kstruct {
public:
class nvar_attributes_t;
class ucs2_string_t;
class nvar_extended_attributes_t;
class nvar_entry_t;
class nvar_entry_body_t;
ami_nvar_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, ami_nvar_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~ami_nvar_t();
class nvar_attributes_t : public kaitai::kstruct {
public:
nvar_attributes_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_t* p__parent = 0, ami_nvar_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~nvar_attributes_t();
private:
bool m_valid;
bool m_auth_write;
bool m_hw_error_record;
bool m_extended_header;
bool m_data_only;
bool m_local_guid;
bool m_ascii_name;
bool m_runtime;
ami_nvar_t* m__root;
ami_nvar_t::nvar_entry_t* m__parent;
public:
bool valid() const { return m_valid; }
bool auth_write() const { return m_auth_write; }
bool hw_error_record() const { return m_hw_error_record; }
bool extended_header() const { return m_extended_header; }
bool data_only() const { return m_data_only; }
bool local_guid() const { return m_local_guid; }
bool ascii_name() const { return m_ascii_name; }
bool runtime() const { return m_runtime; }
ami_nvar_t* _root() const { return m__root; }
ami_nvar_t::nvar_entry_t* _parent() const { return m__parent; }
};
class ucs2_string_t : public kaitai::kstruct {
public:
ucs2_string_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_body_t* p__parent = 0, ami_nvar_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~ucs2_string_t();
private:
std::vector<uint16_t>* m_ucs2_chars;
ami_nvar_t* m__root;
ami_nvar_t::nvar_entry_body_t* m__parent;
public:
std::vector<uint16_t>* ucs2_chars() const { return m_ucs2_chars; }
ami_nvar_t* _root() const { return m__root; }
ami_nvar_t::nvar_entry_body_t* _parent() const { return m__parent; }
};
class nvar_extended_attributes_t : public kaitai::kstruct {
public:
nvar_extended_attributes_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_body_t* p__parent = 0, ami_nvar_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~nvar_extended_attributes_t();
private:
uint64_t m_reserved_high;
bool m_time_based_auth;
bool m_auth_write;
uint64_t m_reserved_low;
bool m_checksum;
ami_nvar_t* m__root;
ami_nvar_t::nvar_entry_body_t* m__parent;
public:
uint64_t reserved_high() const { return m_reserved_high; }
bool time_based_auth() const { return m_time_based_auth; }
bool auth_write() const { return m_auth_write; }
uint64_t reserved_low() const { return m_reserved_low; }
bool checksum() const { return m_checksum; }
ami_nvar_t* _root() const { return m__root; }
ami_nvar_t::nvar_entry_body_t* _parent() const { return m__parent; }
};
class nvar_entry_t : public kaitai::kstruct {
public:
nvar_entry_t(kaitai::kstream* p__io, ami_nvar_t* p__parent = 0, ami_nvar_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~nvar_entry_t();
private:
bool f_offset;
int32_t m_offset;
public:
int32_t offset();
private:
bool f_end_offset;
int32_t m_end_offset;
public:
int32_t end_offset();
private:
std::string m_invoke_offset;
bool n_invoke_offset;
public:
bool _is_null_invoke_offset() { invoke_offset(); return n_invoke_offset; };
private:
uint8_t m_signature_first;
std::string m_signature_rest;
bool n_signature_rest;
public:
bool _is_null_signature_rest() { signature_rest(); return n_signature_rest; };
private:
uint16_t m_size;
bool n_size;
public:
bool _is_null_size() { size(); return n_size; };
private:
uint64_t m_next;
bool n_next;
public:
bool _is_null_next() { next(); return n_next; };
private:
nvar_attributes_t* m_attributes;
bool n_attributes;
public:
bool _is_null_attributes() { attributes(); return n_attributes; };
private:
nvar_entry_body_t* m_body;
bool n_body;
public:
bool _is_null_body() { body(); return n_body; };
private:
std::string m_invoke_end_offset;
bool n_invoke_end_offset;
public:
bool _is_null_invoke_end_offset() { invoke_end_offset(); return n_invoke_end_offset; };
private:
ami_nvar_t* m__root;
ami_nvar_t* m__parent;
std::string m__raw_body;
bool n__raw_body;
public:
bool _is_null__raw_body() { _raw_body(); return n__raw_body; };
private:
kaitai::kstream* m__io__raw_body;
public:
std::string invoke_offset() const { return m_invoke_offset; }
uint8_t signature_first() const { return m_signature_first; }
std::string signature_rest() const { return m_signature_rest; }
uint16_t size() const { return m_size; }
uint64_t next() const { return m_next; }
nvar_attributes_t* attributes() const { return m_attributes; }
nvar_entry_body_t* body() const { return m_body; }
std::string invoke_end_offset() const { return m_invoke_end_offset; }
ami_nvar_t* _root() const { return m__root; }
ami_nvar_t* _parent() const { return m__parent; }
std::string _raw_body() const { return m__raw_body; }
kaitai::kstream* _io__raw_body() const { return m__io__raw_body; }
};
class nvar_entry_body_t : public kaitai::kstruct {
public:
nvar_entry_body_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_t* p__parent = 0, ami_nvar_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~nvar_entry_body_t();
private:
bool f_extended_header_attributes;
nvar_extended_attributes_t* m_extended_header_attributes;
bool n_extended_header_attributes;
public:
bool _is_null_extended_header_attributes() { extended_header_attributes(); return n_extended_header_attributes; };
private:
public:
nvar_extended_attributes_t* extended_header_attributes();
private:
bool f_data_start_offset;
int32_t m_data_start_offset;
public:
int32_t data_start_offset();
private:
bool f_extended_header_size_field;
uint16_t m_extended_header_size_field;
bool n_extended_header_size_field;
public:
bool _is_null_extended_header_size_field() { extended_header_size_field(); return n_extended_header_size_field; };
private:
public:
uint16_t extended_header_size_field();
private:
bool f_extended_header_timestamp;
uint64_t m_extended_header_timestamp;
bool n_extended_header_timestamp;
public:
bool _is_null_extended_header_timestamp() { extended_header_timestamp(); return n_extended_header_timestamp; };
private:
public:
uint64_t extended_header_timestamp();
private:
bool f_data_size;
int32_t m_data_size;
public:
int32_t data_size();
private:
bool f_extended_header_checksum;
uint8_t m_extended_header_checksum;
bool n_extended_header_checksum;
public:
bool _is_null_extended_header_checksum() { extended_header_checksum(); return n_extended_header_checksum; };
private:
public:
uint8_t extended_header_checksum();
private:
bool f_data_end_offset;
int32_t m_data_end_offset;
public:
int32_t data_end_offset();
private:
bool f_extended_header_size;
uint16_t m_extended_header_size;
public:
uint16_t extended_header_size();
private:
bool f_extended_header_hash;
std::string m_extended_header_hash;
bool n_extended_header_hash;
public:
bool _is_null_extended_header_hash() { extended_header_hash(); return n_extended_header_hash; };
private:
public:
std::string extended_header_hash();
private:
uint8_t m_guid_index;
bool n_guid_index;
public:
bool _is_null_guid_index() { guid_index(); return n_guid_index; };
private:
std::string m_guid;
bool n_guid;
public:
bool _is_null_guid() { guid(); return n_guid; };
private:
std::string m_ascii_name;
bool n_ascii_name;
public:
bool _is_null_ascii_name() { ascii_name(); return n_ascii_name; };
private:
ucs2_string_t* m_ucs2_name;
bool n_ucs2_name;
public:
bool _is_null_ucs2_name() { ucs2_name(); return n_ucs2_name; };
private:
std::string m_invoke_data_start;
bool n_invoke_data_start;
public:
bool _is_null_invoke_data_start() { invoke_data_start(); return n_invoke_data_start; };
private:
std::string m_data;
ami_nvar_t* m__root;
ami_nvar_t::nvar_entry_t* m__parent;
public:
uint8_t guid_index() const { return m_guid_index; }
std::string guid() const { return m_guid; }
std::string ascii_name() const { return m_ascii_name; }
ucs2_string_t* ucs2_name() const { return m_ucs2_name; }
std::string invoke_data_start() const { return m_invoke_data_start; }
std::string data() const { return m_data; }
ami_nvar_t* _root() const { return m__root; }
ami_nvar_t::nvar_entry_t* _parent() const { return m__parent; }
};
private:
std::vector<nvar_entry_t*>* m_entries;
ami_nvar_t* m__root;
kaitai::kstruct* m__parent;
public:
std::vector<nvar_entry_t*>* entries() const { return m_entries; }
ami_nvar_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
#endif // AMI_NVAR_H_

162
common/ksy/ami_nvar.ksy Normal file
View file

@ -0,0 +1,162 @@
meta:
id: ami_nvar
title: AMI Aptio NVRAM Storage
application: AMI Aptio-based UEFI firmware
file-extension: nvar
tags:
- firmware
license: CC0-1.0
ks-version: 0.9
endian: le
seq:
- id: entries
type: nvar_entry
repeat: until
repeat-until: _.signature_first != 0x4e or _io.eof
types:
nvar_entry:
seq:
- id: invoke_offset
size: 0
if: offset >= 0
- id: signature_first
type: u1
- id: signature_rest
contents: [VAR]
if: signature_first == 0x4e
- id: size
type: u2
valid:
expr: _ > sizeof<u4> + sizeof<u2> + sizeof<u4>
if: signature_first == 0x4e
- id: next
type: b24le
if: signature_first == 0x4e
- id: attributes
type: nvar_attributes
if: signature_first == 0x4e
- id: body
type: nvar_entry_body
size: size - (sizeof<u4> + sizeof<u2> + sizeof<u4>)
if: signature_first == 0x4e
- id: invoke_end_offset
size: 0
if: signature_first == 0x4e and end_offset >= 0
instances:
offset:
value: _io.pos
end_offset:
value: _io.pos
nvar_attributes:
seq:
- id: valid
type: b1
- id: auth_write
type: b1
- id: hw_error_record
type: b1
- id: extended_header
type: b1
- id: data_only
type: b1
- id: local_guid
type: b1
- id: ascii_name
type: b1
- id: runtime
type: b1
nvar_extended_attributes:
seq:
- id: reserved_high
type: b2
- id: time_based_auth
type: b1
- id: auth_write
type: b1
- id: reserved_low
type: b3
- id: checksum
type: b1
ucs2_string:
seq:
- id: ucs2_chars
type: u2
repeat: until
repeat-until: _ == 0
nvar_entry_body:
seq:
- id: guid_index
type: u1
if: (not _parent.attributes.local_guid)
and (not _parent.attributes.data_only)
and (_parent.attributes.valid)
- id: guid
size: 16
if: (_parent.attributes.local_guid)
and (not _parent.attributes.data_only)
and (_parent.attributes.valid)
- id: ascii_name
type: strz
encoding: ASCII
if: (_parent.attributes.ascii_name)
and (not _parent.attributes.data_only)
and (_parent.attributes.valid)
- id: ucs2_name
type: ucs2_string
if: (not _parent.attributes.ascii_name)
and (not _parent.attributes.data_only)
and (_parent.attributes.valid)
- id: invoke_data_start
size: 0
if: data_start_offset >= 0
- id: data
size-eos: true
instances:
extended_header_size_field:
pos: _io.pos - sizeof<u2>
type: u2
if: _parent.attributes.valid
and _parent.attributes.extended_header
and _parent.size > sizeof<u4> + sizeof<u2> + sizeof<u4> + sizeof<u2>
extended_header_size:
value: '_parent.attributes.extended_header ? (extended_header_size_field >= sizeof<nvar_extended_attributes> + sizeof<u2> ? extended_header_size_field : 0) : 0'
extended_header_attributes:
pos: _io.pos - extended_header_size
type: nvar_extended_attributes
if: _parent.attributes.valid
and _parent.attributes.extended_header
and (extended_header_size >= sizeof<nvar_extended_attributes> + sizeof<u2>)
extended_header_timestamp:
pos: _io.pos - extended_header_size + sizeof<nvar_extended_attributes>
type: u8
if: _parent.attributes.valid
and _parent.attributes.extended_header
and (extended_header_size >= sizeof<nvar_extended_attributes> + sizeof<u8> + sizeof<u2>)
and extended_header_attributes.time_based_auth
extended_header_hash:
pos: _io.pos - extended_header_size + sizeof<nvar_extended_attributes> + sizeof<u8>
size: 32
if: _parent.attributes.valid
and _parent.attributes.extended_header
and (extended_header_size >= sizeof<nvar_extended_attributes> + sizeof<u8> + 32 + sizeof<u2>)
and extended_header_attributes.time_based_auth
and (not _parent.attributes.data_only)
extended_header_checksum:
pos: _io.pos - sizeof<u2> - sizeof<u1>
type: u1
if: _parent.attributes.valid
and _parent.attributes.extended_header
and (extended_header_size >= sizeof<nvar_extended_attributes> + sizeof<u1> + sizeof<u2>)
and extended_header_attributes.checksum
data_start_offset:
value: _io.pos
data_end_offset:
value: _io.pos
data_size:
value: (data_end_offset - data_start_offset) - extended_header_size

View file

@ -12,356 +12,270 @@
*/
#ifdef U_ENABLE_NVRAM_PARSING_SUPPORT
#include <map>
#include "nvramparser.h"
#include "parsingdata.h"
#include "ustring.h"
#include "utility.h"
#include "nvram.h"
#include "ffs.h"
#include "intel_microcode.h"
#ifdef U_ENABLE_NVRAM_PARSING_SUPPORT
#include "umemstream.h"
#include "kaitai/kaitaistream.h"
#include "generated/ami_nvar.h"
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 = readUnaligned(pdata).emptyByte;
}
UByteArray nvar = model->body(index);
// Get local offset
UINT32 localOffset = (UINT32)model->header(index).size();
// Nothing to parse in an empty store
if (nvar.isEmpty())
return U_SUCCESS;
// Get item data
const UByteArray data = model->body(index);
try {
const UINT32 localOffset = (UINT32)model->header(index).size();
umemstream is(nvar.constData(), nvar.size());
kaitai::kstream ks(&is);
ami_nvar_t parsed(&ks);
// Parse all entries
UINT32 offset = 0;
UINT32 guidsInStore = 0;
while (1) {
bool msgUnknownExtDataFormat = false;
bool msgExtHeaderTooLong = false;
bool msgExtDataTooShort = false;
UINT16 guidsInStore = 0;
UINT32 currentEntryIndex = 0;
for (const auto & entry : *parsed.entries()) {
UINT8 subtype = Subtypes::FullNvarEntry;
UString name;
UString text;
UString info;
UString guid;
UByteArray header;
UByteArray body;
UByteArray tail;
bool isInvalid = false;
bool isInvalidLink = false;
bool hasExtendedHeader = false;
bool hasChecksum = false;
bool hasTimestamp = false;
bool hasHash = false;
bool hasGuidIndex = false;
// This is a terminating entry, needs special processing
if (entry->_is_null_signature_rest()) {
UINT32 guidAreaSize = guidsInStore * sizeof(EFI_GUID);
UINT32 unparsedSize = (UINT32)nvar.size() - entry->offset() - guidAreaSize;
UINT32 guidIndex = 0;
UINT8 storedChecksum = 0;
UINT8 calculatedChecksum = 0;
UINT32 extendedHeaderSize = 0;
UINT8 extendedAttributes = 0;
UINT64 timestamp = 0;
UByteArray hash;
// Check if the data left is a free space or a padding
UByteArray padding = nvar.mid(entry->offset(), unparsedSize);
UINT8 subtype = Subtypes::FullNvarEntry;
UString name;
UString guid;
UString text;
UByteArray header;
UByteArray body;
UByteArray tail;
// Get info
UString info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size());
UINT32 guidAreaSize = guidsInStore * sizeof(EFI_GUID);
UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize;
if ((UINT32)padding.count(0xFF) == unparsedSize) { // Free space
// Add tree item
model->addItem(localOffset + entry->offset(), Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
}
else {
// Nothing is parsed yet, but the file is not empty
if (entry->offset() == 0) {
msg(usprintf("%s: file can't be parsed as NVAR variable store", __FUNCTION__), index);
return U_SUCCESS;
}
// Get entry header
const NVAR_ENTRY_HEADER* entryHeader = (const NVAR_ENTRY_HEADER*)(data.constData() + offset);
// Add tree item
model->addItem(localOffset + entry->offset(), Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
}
// 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)", (UINT32)padding.size(), (UINT32)padding.size());
if ((UINT32)padding.count(emptyByte) == unparsedSize) { // Free space
// Add GUID store area
UByteArray guidArea = nvar.right(guidAreaSize);
// Get info
name = UString("GUID store");
info = usprintf("Full size: %Xh (%u)\nGUIDs in store: %u",
(UINT32)guidArea.size(), (UINT32)guidArea.size(),
guidsInStore);
// Add tree item
model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, 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;
}
model->addItem((UINT32)(localOffset + entry->offset() + padding.size()), Types::NvarGuidStore, 0, name, UString(), info, UByteArray(), guidArea, UByteArray(), Fixed, index);
// Add tree item
model->addItem(localOffset + offset, Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
return U_SUCCESS;
}
// 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",
(UINT32)guidArea.size(), (UINT32)guidArea.size(),
guidsInStore);
// Add tree item
model->addItem((UINT32)(localOffset + offset + padding.size()), Types::Padding, getPaddingType(guidArea), name, UString(), info, UByteArray(), guidArea, UByteArray(), Fixed, index);
// This is a normal entry
const auto entry_body = entry->body();
return U_SUCCESS;
}
// Set default next to predefined last value
NVAR_ENTRY_PARSING_DATA pdata = {};
pdata.emptyByte = 0xFF;
pdata.next = 0xFFFFFF;
pdata.isValid = TRUE;
// 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 = readUnaligned((UINT16*)(body.constData() + body.size() - sizeof(UINT16)));
if (extendedHeaderSize > (UINT32)body.size()) {
msgExtHeaderTooLong = true;
isInvalid = true;
// Do not parse further
goto parsing_done;
// Check for invalid entry
if (!entry->attributes()->valid()) {
subtype = Subtypes::InvalidNvarEntry;
name = UString("Invalid");
pdata.isValid = FALSE;
goto processing_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;
// Check for link entry
if (entry->next() != 0xFFFFFF) {
subtype = Subtypes::LinkNvarEntry;
pdata.next = (UINT32)entry->next();
}
tail = body.mid(body.size() - extendedHeaderSize);
body = body.left(body.size() - extendedHeaderSize);
// Check for data-only entry (nameless and GUIDless entry or link)
if (entry->attributes()->data_only()) {
// Search backwards for a previous entry with a link to this variable
UModelIndex prevEntryIndex;
if (currentEntryIndex > 0) {
for (UINT32 i = currentEntryIndex - 1; i > 0; i--) {
const auto previousEntry = parsed.entries()->at(i);
// 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;
}
if (previousEntry == entry)
break;
timestamp = readUnaligned(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 = readUnaligned((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 previously 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.model()->index(i, 0, index);
if (model->hasEmptyParsingData(nvarIndex) == false) {
UByteArray nvarData = model->parsingData(nvarIndex);
const NVAR_ENTRY_PARSING_DATA nvarPdata = readUnaligned((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;
if (previousEntry->next() + previousEntry->offset() == entry->offset()) { // Previous link is present and valid
prevEntryIndex = index.model()->index(i, 0, index);
// Make sure that we are linking to a valid entry
NVAR_ENTRY_PARSING_DATA pd = readUnaligned((NVAR_ENTRY_PARSING_DATA*)model->parsingData(prevEntryIndex).constData());
if (!pd.isValid) {
prevEntryIndex = UModelIndex();
}
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);
// Check if the link is valid
if (prevEntryIndex.isValid()) {
// Use the name and text of the previous entry
name = model->name(prevEntryIndex);
text = model->text(prevEntryIndex);
if (entryHeader->Next == lastVariableFlag)
subtype = Subtypes::DataNvarEntry;
if (entry->next() == 0xFFFFFF)
subtype = Subtypes::DataNvarEntry;
}
else {
subtype = Subtypes::InvalidLinkNvarEntry;
name = UString("InvalidLink");
pdata.isValid = FALSE;
}
goto processing_done;
}
// 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 = (UINT32)(text.length() + 1);
// Obtain text
if (!entry_body->_is_null_ascii_name()) {
text = entry_body->ascii_name().c_str();
}
else { // Name is stored as UCS2 string of CHAR16s
text = uFromUcs2(namePtr);
nameSize = (UINT32)((text.length() + 1) * 2);
else if (!entry_body->_is_null_ucs2_name()) {
UByteArray temp;
for (const auto & ch : *entry_body->ucs2_name()->ucs2_chars()) {
temp += UByteArray((const char*)&ch, sizeof(ch));
}
text = uFromUcs2(temp.constData());
}
// Get entry GUID
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) { // GUID is strored in the variable itself
name = guidToUString(readUnaligned((EFI_GUID*)(entryHeader + 1)));
guid = guidToUString(readUnaligned((EFI_GUID*)(entryHeader + 1)), false);
// Obtain GUID
if (!entry_body->_is_null_guid()) { // GUID is stored in the entry itself
const EFI_GUID g = readUnaligned((EFI_GUID*)entry_body->guid().c_str());
name = guidToUString(g);
guid = guidToUString(g, 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;
else { // GUID is stored in GUID store at the end of the NVAR store
// Grow the GUID store if needed
if (guidsInStore < entry_body->guid_index() + 1)
guidsInStore = entry_body->guid_index() + 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(readUnaligned(guidPtr));
guid = guidToUString(readUnaligned(guidPtr), false);
hasGuidIndex = true;
const EFI_GUID g = readUnaligned((EFI_GUID*)(nvar.constData() + nvar.size()) - (entry_body->guid_index() + 1));
name = guidToUString(g);
guid = guidToUString(g, false);
}
// 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);
processing_done:
// This feels hacky, but I haven't found a way to ask Kaitai for raw bytes
header = nvar.mid(entry->offset(), sizeof(NVAR_ENTRY_HEADER) + entry_body->data_start_offset());
body = nvar.mid(entry->offset() + sizeof(NVAR_ENTRY_HEADER) + entry_body->data_start_offset(), entry_body->data_size());
tail = nvar.mid(entry->end_offset() - entry_body->extended_header_size(), entry_body->extended_header_size());
// Add GUID info for valid entries
if (!guid.isEmpty())
info += UString("Variable GUID: ") + guid + "\n";
// Add GUID index information
if (!entry_body->_is_null_guid_index())
info += usprintf("GUID index: %u\n", entry_body->guid_index());
// Add header, body and extended data info
info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nTail size: %Xh (%u)",
entry->size(), entry->size(),
(UINT32)header.size(), (UINT32)header.size(),
(UINT32)body.size(), (UINT32)body.size(),
(UINT32)tail.size(), (UINT32)tail.size());
// Add attributes info
const NVAR_ENTRY_HEADER entryHeader = readUnaligned((NVAR_ENTRY_HEADER*)header.constData());
info += usprintf("\nAttributes: %02Xh", entryHeader.Attributes);
// Translate attributes to text
if (entryHeader.Attributes != 0x00 && entryHeader.Attributes != 0xFF)
info += UString(" (") + nvarAttributesToUString(entryHeader.Attributes) + UString(")");
// Add next node info
if (entry->next() != 0xFFFFFF)
info += usprintf("\nNext node at offset: %Xh", localOffset + entry->offset() + (UINT32)entry->next());
// Add extended header info
if (entry_body->extended_header_size() > 0) {
info += usprintf("\nExtended header size: %Xh (%u)",
entry_body->extended_header_size(), entry_body->extended_header_size());
const UINT8 extendedAttributes = *tail.constData();
info += usprintf("\nExtended attributes: %02Xh (", extendedAttributes) + nvarExtendedAttributesToUString(extendedAttributes) + UString(")");
// Add checksum
if (!entry_body->_is_null_extended_header_checksum()) {
UINT8 calculatedChecksum = 0;
UByteArray wholeBody = body + tail;
// Include entry body
UINT8* start = (UINT8*)wholeBody.constData();
for (UINT8* p = start; p < start + wholeBody.size(); 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;
info += usprintf("\nChecksum: %02Xh, ", entry_body->extended_header_checksum())
+ (calculatedChecksum ? usprintf(", invalid, should be %02Xh", 0x100 - calculatedChecksum) : UString(", valid"));
}
// Add timestamp
if (!entry_body->_is_null_extended_header_timestamp())
info += usprintf("\nTimestamp: %" PRIX64 "h", entry_body->extended_header_timestamp());
// Add hash
if (!entry_body->_is_null_extended_header_hash()) {
UByteArray hash = UByteArray(entry_body->extended_header_hash().c_str(), entry_body->extended_header_hash().size());
info += UString("\nHash: ") + UString(hash.toHex().constData());
}
}
// Add tree item
UModelIndex varIndex = model->addItem(localOffset + entry->offset(), Types::NvarEntry, subtype, name, text, info, header, body, tail, Fixed, index);
currentEntryIndex++;
// Set parsing data
model->setParsingData(varIndex, UByteArray((const char*)&pdata, sizeof(pdata)));
// Try parsing the entry data as NVAR storage if it begins with NVAR signature
if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry)
&& body.size() >= 4 && readUnaligned((const UINT32*)body.constData()) == NVRAM_NVAR_ENTRY_SIGNATURE)
(void)parseNvarStore(varIndex);
}
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 + "\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,
(UINT32)header.size(), (UINT32)header.size(),
(UINT32)body.size(), (UINT32)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, Fixed, 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, (UINT32)body.size()), varIndex);
if (msgExtDataTooShort) msg(usprintf("%s: extended header size (%Xh) is too small for timestamp and hash", __FUNCTION__,
(UINT32)tail.size()), varIndex);
// Try parsing the entry data as NVAR storage if it begins with NVAR signature
if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry)
&& body.size() >= 4 && readUnaligned((const UINT32*)body.constData()) == NVRAM_NVAR_ENTRY_SIGNATURE)
parseNvarStore(varIndex);
// Move to next exntry
offset += entryHeader->Size;
}
catch (...) {
msg(usprintf("%s: unable to parse AMI NVAR storage", __FUNCTION__), index);
return U_INVALID_STORE;
}
return U_SUCCESS;

View file

@ -59,6 +59,7 @@ UString itemTypeToUString(const UINT8 type)
case Types::EvsaStore: return UString("EVSA store");
case Types::CmdbStore: return UString("CMDB store");
case Types::FlashMapStore: return UString("FlashMap store");
case Types::NvarGuidStore: return UString("NVAR GUID store");
case Types::NvarEntry: return UString("NVAR entry");
case Types::VssEntry: return UString("VSS entry");
case Types::FsysEntry: return UString("Fsys entry");

View file

@ -51,6 +51,7 @@ namespace Types {
EvsaStore,
FlashMapStore,
CmdbStore,
NvarGuidStore,
NvarEntry,
VssEntry,
FsysEntry,

58
common/umemstream.h Normal file
View file

@ -0,0 +1,58 @@
/* umemstream.h
Copyright (c) 2023, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
#ifndef UMEMSTREAM_H
#define UMEMSTREAM_H
#include <sstream>
// NOTE: this implementation is certainly not a valid replacement to std::stringstream
// NOTE: because it only supports getting through the buffer once
// NOTE: however, we already do it this way, so it's enough for practical purposes
class umembuf : public std::streambuf {
public:
umembuf(const char *p, size_t l) {
setg((char*)p, (char*)p, (char*)p + l);
}
pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which = std::ios_base::in) override
{
(void)which;
if (dir == std::ios_base::cur)
gbump((int)off);
else if (dir == std::ios_base::end)
setg(eback(), egptr() + off, egptr());
else if (dir == std::ios_base::beg)
setg(eback(), eback() + off, egptr());
return gptr() - eback();
}
pos_type seekpos(pos_type sp, std::ios_base::openmode which) override
{
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
}
};
class umemstream : public std::istream {
public:
umemstream(const char *p, size_t l) : std::istream(&buffer_),
buffer_(p, l) {
rdbuf(&buffer_);
}
private:
umembuf buffer_;
};
#endif // UMEMSTREAM_H

View file

@ -113,12 +113,10 @@ UString uFromUcs2(const char* str, size_t max_len)
UString msg;
const char *str8 = str;
size_t rest = (max_len == 0) ? SIZE_MAX : max_len;
if (max_len == 0) {
while (str8[0] && rest) {
msg += str8[0];
str8 += 2;
rest--;
}
while (str8[0] && rest) {
msg += str8[0];
str8 += 2;
rest--;
}
return msg;
}

View file

@ -146,6 +146,7 @@ void fixFileName(UString &name, bool replaceSpaces)
// Returns text representation of error code
UString errorCodeToUString(USTATUS errorCode)
{
// TODO: improve
switch (errorCode) {
case U_SUCCESS: return UString("Success");
case U_NOT_IMPLEMENTED: return UString("Not implemented");
@ -165,7 +166,6 @@ UString errorCodeToUString(USTATUS errorCode)
case U_VOLUMES_NOT_FOUND: return UString("UEFI volumes not found");
case U_INVALID_VOLUME: return UString("Invalid UEFI volume");
case U_VOLUME_REVISION_NOT_SUPPORTED: return UString("Volume revision not supported");
//case U_VOLUME_GROW_FAILED: return UString("Volume grow failed");
case U_UNKNOWN_FFS: return UString("Unknown file system");
case U_INVALID_FILE: return UString("Invalid file");
case U_INVALID_SECTION: return UString("Invalid section");
@ -177,26 +177,19 @@ UString errorCodeToUString(USTATUS errorCode)
case U_UNKNOWN_COMPRESSION_TYPE: return UString("Unknown compression type");
case U_UNKNOWN_EXTRACT_MODE: return UString("Unknown extract mode");
case U_UNKNOWN_REPLACE_MODE: return UString("Unknown replace mode");
//case U_UNKNOWN_INSERT_MODE: return UString("Unknown insert mode");
case U_UNKNOWN_IMAGE_TYPE: return UString("Unknown executable image type");
case U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE: return UString("Unknown PE optional header type");
case U_UNKNOWN_RELOCATION_TYPE: return UString("Unknown relocation type");
//case U_GENERIC_CALL_NOT_SUPPORTED: return UString("Generic call not supported");
//case U_VOLUME_BASE_NOT_FOUND: return UString("Volume base address not found");
//case U_PEI_CORE_ENTRY_POINT_NOT_FOUND: return UString("PEI core entry point not found");
case U_COMPLEX_BLOCK_MAP: return UString("Block map structure too complex for correct analysis");
case U_DIR_ALREADY_EXIST: return UString("Directory already exists");
case U_DIR_CREATE: return UString("Directory can't be created");
case U_DIR_CHANGE: return UString("Change directory failed");
//case U_UNKNOWN_PATCH_TYPE: return UString("Unknown patch type");
//case U_PATCH_OFFSET_OUT_OF_BOUNDS: return UString("Patch offset out of bounds");
//case U_INVALID_SYMBOL: return UString("Invalid symbol");
//case U_NOTHING_TO_PATCH: return UString("Nothing to patch");
case U_DEPEX_PARSE_FAILED: return UString("Dependency expression parsing failed");
case U_TRUNCATED_IMAGE: return UString("Image is truncated");
case U_INVALID_CAPSULE: return UString("Invalid capsule");
case U_STORES_NOT_FOUND: return UString("Stores not found");
case U_INVALID_STORE_SIZE: return UString("Invalid store size");
case U_INVALID_STORE: return UString("Invalid store");
default: return usprintf("Unknown error %02lX", errorCode);
}
}

View file

@ -31,6 +31,7 @@ SET(PROJECT_SOURCES
../common/LZMA/SDK/C/LzmaDec.c
../common/Tiano/EfiTianoDecompress.c
../common/ustring.cpp
../common/generated/ami_nvar.cpp
../common/generated/intel_acbp_v1.cpp
../common/generated/intel_acbp_v2.cpp
../common/generated/intel_keym_v1.cpp
@ -86,7 +87,7 @@ ADD_DEFINITIONS(
ADD_EXECUTABLE(ffsparser_fuzzer ${PROJECT_SOURCES})
IF(USE_AFL_DRIVER)
IF(NOT USE_AFL_DRIVER)
TARGET_COMPILE_OPTIONS(ffsparser_fuzzer PRIVATE -O1 -fno-omit-frame-pointer -g -ggdb3 -fsanitize=fuzzer,address,undefined -fsanitize-address-use-after-scope -fno-sanitize-recover=undefined)
TARGET_LINK_LIBRARIES(ffsparser_fuzzer PRIVATE -fsanitize=fuzzer,address,undefined)
ELSE()