From 4e600eb986697eb7dbcf4e0c7ca2c0897b6d973c Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Mon, 24 Feb 2025 18:12:02 +0700 Subject: [PATCH] Add Apple SysF/Diag parser --- UEFIExtract/CMakeLists.txt | 1 + UEFIFind/CMakeLists.txt | 1 + UEFITool/CMakeLists.txt | 1 + UEFITool/QHexView/src/qhexview.cpp | 2 +- UEFITool/uefitool.cpp | 6 +- UEFITool/uefitool.pro | 10 + common/generated/apple_sysf.cpp | 115 +++++++++ common/generated/apple_sysf.h | 129 ++++++++++ common/ksy/apple_fsys.ksy | 60 +++++ common/meson.build | 5 + common/nvram.h | 10 +- common/nvramparser.cpp | 383 +++++++++++++++++++---------- common/types.cpp | 10 +- common/types.h | 10 +- common/ubytearray.h | 1 + common/utility.cpp | 2 +- fuzzing/CMakeLists.txt | 5 + 17 files changed, 597 insertions(+), 154 deletions(-) create mode 100644 common/generated/apple_sysf.cpp create mode 100644 common/generated/apple_sysf.h create mode 100644 common/ksy/apple_fsys.ksy diff --git a/UEFIExtract/CMakeLists.txt b/UEFIExtract/CMakeLists.txt index 13dceed..2c5842f 100644 --- a/UEFIExtract/CMakeLists.txt +++ b/UEFIExtract/CMakeLists.txt @@ -35,6 +35,7 @@ SET(PROJECT_SOURCES ../common/bstrlib/bstrlib.c ../common/bstrlib/bstrwrap.cpp ../common/generated/ami_nvar.cpp + ../common/generated/apple_sysf.cpp ../common/generated/edk2_vss.cpp ../common/generated/edk2_vss2.cpp ../common/generated/edk2_ftw.cpp diff --git a/UEFIFind/CMakeLists.txt b/UEFIFind/CMakeLists.txt index e3b8491..07d27f8 100644 --- a/UEFIFind/CMakeLists.txt +++ b/UEFIFind/CMakeLists.txt @@ -32,6 +32,7 @@ SET(PROJECT_SOURCES ../common/bstrlib/bstrlib.c ../common/bstrlib/bstrwrap.cpp ../common/generated/ami_nvar.cpp + ../common/generated/apple_sysf.cpp ../common/generated/edk2_vss.cpp ../common/generated/edk2_vss2.cpp ../common/generated/edk2_ftw.cpp diff --git a/UEFITool/CMakeLists.txt b/UEFITool/CMakeLists.txt index 1b57289..711b073 100644 --- a/UEFITool/CMakeLists.txt +++ b/UEFITool/CMakeLists.txt @@ -68,6 +68,7 @@ SET(PROJECT_SOURCES ../common/digest/sha512.c ../common/digest/sm3.c ../common/generated/ami_nvar.cpp + ../common/generated/apple_sysf.cpp ../common/generated/edk2_vss.cpp ../common/generated/edk2_vss2.cpp ../common/generated/edk2_ftw.cpp diff --git a/UEFITool/QHexView/src/qhexview.cpp b/UEFITool/QHexView/src/qhexview.cpp index 334ccef..e9a35da 100644 --- a/UEFITool/QHexView/src/qhexview.cpp +++ b/UEFITool/QHexView/src/qhexview.cpp @@ -466,7 +466,7 @@ void QHexView::checkState() { int doclines = static_cast(this->lines()), vislines = this->visibleLines(true); - qint64 vscrollmax = doclines - vislines; + qint64 vscrollmax = doclines - vislines + 1; // UEFITool: ensure the very last line is visible on macOS if(doclines >= vislines) vscrollmax++; diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index 4c8011d..99a3c04 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -245,7 +245,7 @@ void UEFITool::populateUi(const QModelIndex ¤t) || type == Types::SlicData || type == Types::NvarEntry || type == Types::VssEntry - || type == Types::FsysEntry + || type == Types::SysFEntry || type == Types::EvsaEntry || type == Types::FlashMapEntry || type == Types::IfwiHeader @@ -263,7 +263,7 @@ void UEFITool::populateUi(const QModelIndex ¤t) ui->menuStoreActions->setEnabled(type == Types::VssStore || type == Types::Vss2Store || type == Types::FdcStore - || type == Types::FsysStore + || type == Types::SysFStore || type == Types::EvsaStore || type == Types::FtwStore || type == Types::FlashMapStore @@ -907,7 +907,7 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event) case Types::VssStore: case Types::Vss2Store: case Types::FdcStore: - case Types::FsysStore: + case Types::SysFStore: case Types::EvsaStore: case Types::FtwStore: case Types::FlashMapStore: diff --git a/UEFITool/uefitool.pro b/UEFITool/uefitool.pro index 7e8699b..411b3df 100644 --- a/UEFITool/uefitool.pro +++ b/UEFITool/uefitool.pro @@ -51,6 +51,11 @@ HEADERS += uefitool.h \ ../common/digest/sha2.h \ ../common/digest/sm3.h \ ../common/generated/ami_nvar.h \ + ../common/generated/apple_sysf.h \ + ../common/generated/edk2_vss.h \ + ../common/generated/edk2_vss2.h \ + ../common/generated/edk2_ftw.h \ + ../common/generated/insyde_fdc.h \ ../common/generated/intel_acbp_v1.h \ ../common/generated/intel_acbp_v2.h \ ../common/generated/intel_keym_v1.h \ @@ -118,6 +123,11 @@ SOURCES += uefitool_main.cpp \ ../common/digest/sha512.c \ ../common/digest/sm3.c \ ../common/generated/ami_nvar.cpp \ + ../common/generated/apple_sysf.cpp \ + ../common/generated/edk2_vss.cpp \ + ../common/generated/edk2_vss2.cpp \ + ../common/generated/edk2_ftw.cpp \ + ../common/generated/insyde_fdc.cpp \ ../common/generated/intel_acbp_v1.cpp \ ../common/generated/intel_acbp_v2.cpp \ ../common/generated/intel_keym_v1.cpp \ diff --git a/common/generated/apple_sysf.cpp b/common/generated/apple_sysf.cpp new file mode 100644 index 0000000..84a0678 --- /dev/null +++ b/common/generated/apple_sysf.cpp @@ -0,0 +1,115 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "apple_sysf.h" +#include "../kaitai/exceptions.h" + +apple_sysf_t::apple_sysf_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, apple_sysf_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = this; (void)p__root; + m_body = nullptr; + m__io__raw_body = nullptr; + f_len_sysf_store_header = false; + _read(); +} + +void apple_sysf_t::_read() { + m_signature = m__io->read_u4le(); + { + uint32_t _ = signature(); + if (!( ((_ == 1937339206) || (_ == 1684627783)) )) { + throw kaitai::validation_expr_error(signature(), _io(), std::string("/seq/0")); + } + } + m_unknown = m__io->read_u1(); + m_unknown1 = m__io->read_u4le(); + m_sysf_size = m__io->read_u2le(); + m__raw_body = m__io->read_bytes(((sysf_size() - len_sysf_store_header()) - 4)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr(new sysf_store_body_t(m__io__raw_body.get(), this, m__root)); + m_crc = m__io->read_u4le(); +} + +apple_sysf_t::~apple_sysf_t() { + _clean_up(); +} + +void apple_sysf_t::_clean_up() { +} + +apple_sysf_t::sysf_store_body_t::sysf_store_body_t(kaitai::kstream* p__io, apple_sysf_t* p__parent, apple_sysf_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_variables = nullptr; + m_zeroes = nullptr; + _read(); +} + +void apple_sysf_t::sysf_store_body_t::_read() { + m_variables = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + sysf_variable_t* _; + do { + _ = new sysf_variable_t(m__io, this, m__root); + m_variables->push_back(std::move(std::unique_ptr(_))); + i++; + } while (!( (( ((_->len_name() == 3) && (_->name() == (std::string("EOF")))) ) || (_io()->is_eof())) )); + } + m_zeroes = std::unique_ptr>(new std::vector()); + { + int i = 0; + while (!m__io->is_eof()) { + m_zeroes->push_back(std::move(m__io->read_u1())); + i++; + } + } +} + +apple_sysf_t::sysf_store_body_t::~sysf_store_body_t() { + _clean_up(); +} + +void apple_sysf_t::sysf_store_body_t::_clean_up() { +} + +apple_sysf_t::sysf_variable_t::sysf_variable_t(kaitai::kstream* p__io, apple_sysf_t::sysf_store_body_t* p__parent, apple_sysf_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void apple_sysf_t::sysf_variable_t::_read() { + m_len_name = m__io->read_bits_int_le(7); + m_invalid_flag = m__io->read_bits_int_le(1); + m__io->align_to_byte(); + m_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(len_name()), 0, false), std::string("ascii")); + n_len_data = true; + if (name() != std::string("EOF")) { + n_len_data = false; + m_len_data = m__io->read_u2le(); + } + n_data = true; + if (name() != std::string("EOF")) { + n_data = false; + m_data = m__io->read_bytes(len_data()); + } +} + +apple_sysf_t::sysf_variable_t::~sysf_variable_t() { + _clean_up(); +} + +void apple_sysf_t::sysf_variable_t::_clean_up() { + if (!n_len_data) { + } + if (!n_data) { + } +} + +int8_t apple_sysf_t::len_sysf_store_header() { + if (f_len_sysf_store_header) + return m_len_sysf_store_header; + m_len_sysf_store_header = 11; + f_len_sysf_store_header = true; + return m_len_sysf_store_header; +} diff --git a/common/generated/apple_sysf.h b/common/generated/apple_sysf.h new file mode 100644 index 0000000..8de0be8 --- /dev/null +++ b/common/generated/apple_sysf.h @@ -0,0 +1,129 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "../kaitai/kaitaistruct.h" +#include +#include +#include + +#if KAITAI_STRUCT_VERSION < 9000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required" +#endif + +class apple_sysf_t : public kaitai::kstruct { + +public: + class sysf_store_body_t; + class sysf_variable_t; + + apple_sysf_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, apple_sysf_t* p__root = nullptr); + +private: + void _read(); + void _clean_up(); + +public: + ~apple_sysf_t(); + + class sysf_store_body_t : public kaitai::kstruct { + + public: + + sysf_store_body_t(kaitai::kstream* p__io, apple_sysf_t* p__parent = nullptr, apple_sysf_t* p__root = nullptr); + + private: + void _read(); + void _clean_up(); + + public: + ~sysf_store_body_t(); + + private: + std::unique_ptr>> m_variables; + std::unique_ptr> m_zeroes; + apple_sysf_t* m__root; + apple_sysf_t* m__parent; + + public: + std::vector>* variables() const { return m_variables.get(); } + std::vector* zeroes() const { return m_zeroes.get(); } + apple_sysf_t* _root() const { return m__root; } + apple_sysf_t* _parent() const { return m__parent; } + }; + + class sysf_variable_t : public kaitai::kstruct { + + public: + + sysf_variable_t(kaitai::kstream* p__io, apple_sysf_t::sysf_store_body_t* p__parent = nullptr, apple_sysf_t* p__root = nullptr); + + private: + void _read(); + void _clean_up(); + + public: + ~sysf_variable_t(); + + private: + uint64_t m_len_name; + bool m_invalid_flag; + std::string m_name; + uint16_t m_len_data; + bool n_len_data; + + public: + bool _is_null_len_data() { len_data(); return n_len_data; }; + + private: + std::string m_data; + bool n_data; + + public: + bool _is_null_data() { data(); return n_data; }; + + private: + apple_sysf_t* m__root; + apple_sysf_t::sysf_store_body_t* m__parent; + + public: + uint64_t len_name() const { return m_len_name; } + bool invalid_flag() const { return m_invalid_flag; } + std::string name() const { return m_name; } + uint16_t len_data() const { return m_len_data; } + std::string data() const { return m_data; } + apple_sysf_t* _root() const { return m__root; } + apple_sysf_t::sysf_store_body_t* _parent() const { return m__parent; } + }; + +private: + bool f_len_sysf_store_header; + int8_t m_len_sysf_store_header; + +public: + int8_t len_sysf_store_header(); + +private: + uint32_t m_signature; + uint8_t m_unknown; + uint32_t m_unknown1; + uint16_t m_sysf_size; + std::unique_ptr m_body; + uint32_t m_crc; + apple_sysf_t* m__root; + kaitai::kstruct* m__parent; + std::string m__raw_body; + std::unique_ptr m__io__raw_body; + +public: + uint32_t signature() const { return m_signature; } + uint8_t unknown() const { return m_unknown; } + uint32_t unknown1() const { return m_unknown1; } + uint16_t sysf_size() const { return m_sysf_size; } + sysf_store_body_t* body() const { return m_body.get(); } + uint32_t crc() const { return m_crc; } + apple_sysf_t* _root() const { return m__root; } + kaitai::kstruct* _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.get(); } +}; diff --git a/common/ksy/apple_fsys.ksy b/common/ksy/apple_fsys.ksy new file mode 100644 index 0000000..934c74e --- /dev/null +++ b/common/ksy/apple_fsys.ksy @@ -0,0 +1,60 @@ +meta: + id: apple_sysf + title: Apple system variable store + application: Apple MacEFI-based UEFI firmware + file-extension: sysf + tags: + - firmware + license: CC0-1.0 + ks-version: 0.9 + endian: le + +seq: +- id: signature + type: u4 + valid: + expr: _ == 0x73797346 or _ == 0x64696147 # Fsys/Gaid +- id: unknown + type: u1 +- id: unknown1 + type: u4 +- id: sysf_size + type: u2 +- id: body + type: sysf_store_body + size: sysf_size - len_sysf_store_header - sizeof +- id: crc + type: u4 + +instances: + len_sysf_store_header: + value: 11 + +types: + sysf_store_body: + seq: + - id: variables + type: sysf_variable + repeat: until + repeat-until: (_.len_name == 3 and _.name == "EOF") or _io.eof + - id: zeroes + type: u1 + repeat: eos + + sysf_variable: + seq: + - id: len_name + type: b7le + - id: invalid_flag + type: b1le + - id: name + type: strz + encoding: ascii + size: len_name + - id: len_data + type: u2 + if: name != "EOF" + - id: data + size: len_data + if: name != "EOF" + diff --git a/common/meson.build b/common/meson.build index 096b4de..de04cb9 100644 --- a/common/meson.build +++ b/common/meson.build @@ -33,6 +33,11 @@ uefitoolcommon = static_library('uefitoolcommon', 'utility.cpp', 'ustring.cpp', 'generated/ami_nvar.cpp', + 'generated/apple_sysf.cpp', + 'generated/edk2_vss.cpp', + 'generated/edk2_vss2.cpp', + 'generated/edk2_ftw.cpp', + 'generated/insyde_fdc.cpp', 'generated/intel_acbp_v1.cpp', 'generated/intel_acbp_v2.cpp', 'generated/intel_keym_v1.cpp', diff --git a/common/nvram.h b/common/nvram.h index fa3d165..598aa47 100755 --- a/common/nvram.h +++ b/common/nvram.h @@ -70,8 +70,8 @@ extern const UByteArray NVRAM_ADDITIONAL_STORE_VOLUME_GUID; // 00504624-8A59-4EE #define NVRAM_VSS_STORE_SIGNATURE 0x53535624 // $VSS #define NVRAM_APPLE_SVS_STORE_SIGNATURE 0x53565324 // $SVS #define NVRAM_APPLE_NSS_STORE_SIGNATURE 0x53534E24 // $NSS -#define NVRAM_APPLE_FSYS_STORE_SIGNATURE 0x73797346 // Fsys -#define NVRAM_APPLE_GAID_STORE_SIGNATURE 0x64696147 // Gaid +#define NVRAM_APPLE_SYSF_STORE_SIGNATURE 0x73797346 // Fsys +#define NVRAM_APPLE_DIAG_STORE_SIGNATURE 0x64696147 // Gaid #define NVRAM_VSS_VARIABLE_START_ID 0x55AA // Variable store header flags @@ -229,15 +229,15 @@ typedef struct EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64_ { } EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64; // -// Apple Fsys store +// Apple System Flags store // -typedef struct APPLE_FSYS_STORE_HEADER_ { +typedef struct APPLE_SYSF_STORE_HEADER_ { UINT32 Signature; // Fsys or Gaid signature UINT8 Unknown0; // Still unknown UINT32 Unknown1; // Still unknown UINT16 Size; // Size of variable store -} APPLE_FSYS_STORE_HEADER; +} APPLE_SYSF_STORE_HEADER; // Apple Fsys entry format // UINT8 NameLength; diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp index 54f0704..cb1369b 100644 --- a/common/nvramparser.cpp +++ b/common/nvramparser.cpp @@ -26,6 +26,7 @@ #include "umemstream.h" #include "kaitai/kaitaistream.h" #include "generated/ami_nvar.h" +#include "generated/apple_sysf.h" #include "generated/edk2_vss.h" #include "generated/edk2_vss2.h" #include "generated/edk2_ftw.h" @@ -72,7 +73,7 @@ USTATUS NvramParser::parseNvarStore(const UModelIndex & index) // Get info UString info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size()); - if ((UINT32)padding.count(0xFF) == unparsedSize) { // Free space + if ((UINT32)padding.count('\'xFF') == unparsedSize) { // Free space // Add tree item model->addItem(localOffset + entry->offset(), Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); } @@ -312,7 +313,6 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 for (UINT32 storeOffset = 0; storeOffset < volumeBodySize; storeOffset++) { - bool storeFound = false; // VSS try { if (volumeBodySize - storeOffset < sizeof(VSS_VARIABLE_STORE_HEADER)) { @@ -375,18 +375,18 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 // Add header tree item UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::VssStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); + // Add variables UINT32 vssVariableOffset = storeOffset + parsed.len_vss_store_header(); for (const auto & variable : *parsed.body()->variables()) { UINT8 subtype; UString text; - info.clear(); - name.clear(); - // This is thew terminating entry, needs special processing + // This is the terminating entry, needs special processing if (variable->_is_null_signature_last()) { // Add free space or padding after all variables, if needed - if (vssVariableOffset < storeSize) { - UByteArray freeSpace = vss.mid(vssVariableOffset, storeSize - vssVariableOffset); + UINT32 freeSpaceOffset = vssVariableOffset - storeOffset; + if (freeSpaceOffset < storeSize) { + UByteArray freeSpace = vss.mid(freeSpaceOffset, storeSize - freeSpaceOffset); // Add info info = usprintf("Full size: %Xh (%u)", (UINT32)freeSpace.size(), (UINT32)freeSpace.size()); @@ -493,9 +493,9 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 vssVariableOffset += variableSize; } - storeFound = true; - storeOffset += storeSize; - previousStoreEndOffset = storeOffset; + storeOffset += storeSize - 1; + previousStoreEndOffset = storeOffset + 1; + continue; } catch (...) { // Parsing failed, try something else } @@ -513,7 +513,7 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 VSS2_VARIABLE_STORE_HEADER* vss2Header = (VSS2_VARIABLE_STORE_HEADER*)vss2.data(); UByteArray guid = UByteArray((const char*)&vss2Header->Signature, sizeof(EFI_GUID)); bool fdcHeaderSizeOverrideRequired = (fdcStoreSizeOverride > 0 && guid == NVRAM_FDC_STORE_GUID && vss2Header->Size == 0xFFFFFFFF); - if (fdcStoreSizeOverride) { + if (fdcHeaderSizeOverrideRequired) { originalStoreSize = vss2Header->Size; vss2Header->Size = fdcStoreSizeOverride; } @@ -522,7 +522,7 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 edk2_vss2_t parsed(&ks); UINT32 storeSize = parsed.vss2_size(); // Restore original store size, if needed - if (fdcStoreSizeOverride) { + if (fdcHeaderSizeOverrideRequired) { vss2Header->Size = originalStoreSize; } @@ -563,18 +563,18 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 // Add header tree item UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::Vss2Store, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); + // Add variables UINT32 vss2VariableOffset = storeOffset + parsed.len_vss2_store_header(); for (const auto & variable : *parsed.body()->variables()) { UINT8 subtype; UString text; - info.clear(); - name.clear(); - // This is thew terminating entry, needs special processing + // This is the terminating entry, needs special processing if (variable->_is_null_signature_last()) { // Add free space or padding after all variables, if needed - if (vss2VariableOffset < storeSize) { - UByteArray freeSpace = vss2.mid(vss2VariableOffset, storeSize - vss2VariableOffset); + UINT32 freeSpaceOffset = vss2VariableOffset - storeOffset; + if (freeSpaceOffset < storeSize) { + UByteArray freeSpace = vss2.mid(freeSpaceOffset, storeSize - freeSpaceOffset); // Add info info = usprintf("Full size: %Xh (%u)", (UINT32)freeSpace.size(), (UINT32)freeSpace.size()); @@ -651,140 +651,247 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 vss2VariableOffset += variableSize; } - storeFound = true; - storeOffset += storeSize; - previousStoreEndOffset = storeOffset; + storeOffset += storeSize - 1; + previousStoreEndOffset = storeOffset + 1; + continue; } catch (...) { // Parsing failed, try something else } // Do not try any other parsers if we are here for FDC store parsing - if (fdcStoreSizeOverride == 0) { - // FTW - try { - if (volumeBodySize - storeOffset < sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32)) { - // No need to parse further, the rest of the volume is too small - throw 0; - } + if (fdcStoreSizeOverride != 0) { + continue; + } + // FTW + try { + if (volumeBodySize - storeOffset < sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32)) { + // No need to parse further, the rest of the volume is too small + throw 0; + } + + UByteArray ftw = volumeBody.mid(storeOffset); + umemstream is(ftw.constData(), ftw.size()); + kaitai::kstream ks(&is); + edk2_ftw_t parsed(&ks); + UINT64 storeSize; + UINT64 headerSize; + UINT32 calculatedCrc; + UByteArray header; + if (parsed._is_null_len_write_queue_64()) { + headerSize = parsed.len_ftw_store_header_32(); + storeSize = headerSize + parsed.len_write_queue_32(); + header = ftw.left(headerSize); - UByteArray ftw = volumeBody.mid(storeOffset); - umemstream is(ftw.constData(), ftw.size()); - kaitai::kstream ks(&is); - edk2_ftw_t parsed(&ks); - UINT64 storeSize; - UINT64 headerSize; - UINT32 calculatedCrc; - UByteArray header; - if (parsed._is_null_len_write_queue_64()) { - headerSize = parsed.len_ftw_store_header_32(); - storeSize = headerSize + parsed.len_write_queue_32(); - header = ftw.left(headerSize); - - // Check block header checksum - UByteArray crcHeader = header; - EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)crcHeader.data(); - crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0; - crcFtwBlockHeader->State = emptyByte ? 0xFF : 0; - calculatedCrc = (UINT32)crc32(0, (const UINT8*)crcFtwBlockHeader, (UINT32)headerSize); + // Check block header checksum + UByteArray crcHeader = header; + EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)crcHeader.data(); + crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0; + crcFtwBlockHeader->State = emptyByte ? 0xFF : 0; + calculatedCrc = (UINT32)crc32(0, (const UINT8*)crcFtwBlockHeader, (UINT32)headerSize); + } + else { + headerSize = parsed.len_ftw_store_header_64(); + storeSize = headerSize + parsed.len_write_queue_32() + (((UINT64)parsed.len_write_queue_64()) << 32); + header = ftw.left(headerSize); + + // Check block header checksum + UByteArray crcHeader = header; + EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)crcHeader.data(); + crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0; + crcFtwBlockHeader->State = emptyByte ? 0xFF : 0; + calculatedCrc = (UINT32)crc32(0, (const UINT8*)crcFtwBlockHeader, (UINT32)headerSize); + } + + // FTW store at current offset parsed correctly + // Check if we need to add a padding before it + if (!outerPadding.isEmpty()) { + UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); + model->addItem(previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + outerPadding.clear(); + } + + // Construct header and body + UByteArray body = ftw.mid(header.size(), storeSize - header.size()); + + // Add info + const EFI_GUID* guid = (const EFI_GUID*)header.constData(); + UString name = UString("FTW store"); + UString info = UString("Signature: ") + guidToUString(*guid, false); + info += usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nHeader CRC32: %08Xh", + (UINT32)storeSize, (UINT32)storeSize, + (UINT32)header.size(), (UINT32)header.size(), + (UINT32)body.size(), (UINT32)body.size(), + parsed.state(), + parsed.crc()) + (parsed.crc() != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); + + // Add header tree item + model->addItem(localOffset + storeOffset, Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); + + storeOffset += storeSize - 1; + previousStoreEndOffset = storeOffset + 1; + continue; + } catch (...) { + // Parsing failed, try something else + } + // Insyde FDC + try { + if (volumeBodySize - storeOffset < sizeof(FDC_VOLUME_HEADER)) { + // No need to parse further, the rest of the volume is too small + throw 0; + } + + UByteArray fdc = volumeBody.mid(storeOffset); + umemstream is(fdc.constData(), fdc.size()); + kaitai::kstream ks(&is); + insyde_fdc_t parsed(&ks); + UINT32 storeSize = parsed.fdc_size(); + + // FDC store at current offset parsed correctly + // Check if we need to add a padding before it + if (!outerPadding.isEmpty()) { + UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); + model->addItem(previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + outerPadding.clear(); + } + + // Construct header and body + UByteArray header = fdc.left(parsed.len_fdc_store_header()); + UByteArray body = fdc.mid(header.size(),storeSize - header.size()); + + // Add info + UString name = UString("FDC store"); + UString info = usprintf("Signature: _FDC\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)", + storeSize, storeSize, + (UINT32)header.size(), (UINT32)header.size(), + (UINT32)body.size(), (UINT32)body.size()); + + // Add header tree item + UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); + + // Parse FDC body as normal VSS/VSS2 storage with size override + parseNvramVolumeBody(headerIndex, (UINT32)body.size()); + + storeOffset += storeSize - 1; + previousStoreEndOffset = storeOffset + 1; + continue; + } catch (...) { + // Parsing failed, try something else + } + + // Apple SysF + try { + if (volumeBodySize - storeOffset < sizeof(APPLE_SYSF_STORE_HEADER)) { + // No need to parse further, the rest of the volume is too small + throw 0; + } + + UByteArray sysf = volumeBody.mid(storeOffset); + umemstream is(sysf.constData(), sysf.size()); + kaitai::kstream ks(&is); + apple_sysf_t parsed(&ks); + UINT32 storeSize = parsed.sysf_size(); + + // Apple SysF store at current offset parsed correctly + // Check if we need to add a padding before it + if (!outerPadding.isEmpty()) { + UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); + model->addItem(previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + outerPadding.clear(); + } + + // Construct header and body + UByteArray header = sysf.left(parsed.len_sysf_store_header()); + UByteArray body = sysf.mid(header.size(), storeSize - header.size()); + + // Check store checksum + UINT32 calculatedCrc = (UINT32)crc32(0, (const UINT8*)sysf.constData(), storeSize - sizeof(UINT32)); + + // Add info + UString name; + UString info; + if (parsed.signature() == NVRAM_APPLE_SYSF_STORE_SIGNATURE) { + name = UString("SysF store"); + info = UString("Signature: Fsys\n"); + } + else { + name = UString("Diag store"); + info = UString("Signature: Gaid\n"); + } + info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nUnknown: %02Xh\nUnknown1: %08Xh\nCRC32: %08Xh", + storeSize, storeSize, + (UINT32)header.size(), (UINT32)header.size(), + (UINT32)body.size(), (UINT32)body.size(), + parsed.unknown(), + parsed.unknown1(), + parsed.crc()) + (parsed.crc() != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); + + // Add header tree item + UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::SysFStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); + + // Add variables + UINT32 sysfVariableOffset = storeOffset + parsed.len_sysf_store_header(); + for (const auto & variable : *parsed.body()->variables()) { + UINT8 subtype; + + if (variable->invalid_flag()) { + subtype = Subtypes::InvalidSysFEntry; + name = UString("Invalid"); } else { - headerSize = parsed.len_ftw_store_header_64(); - storeSize = headerSize + parsed.len_write_queue_32() + (((UINT64)parsed.len_write_queue_64()) << 32); - header = ftw.left(headerSize); - - // Check block header checksum - UByteArray crcHeader = header; - EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)crcHeader.data(); - crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0; - crcFtwBlockHeader->State = emptyByte ? 0xFF : 0; - calculatedCrc = (UINT32)crc32(0, (const UINT8*)crcFtwBlockHeader, (UINT32)headerSize); + subtype = Subtypes::NormalSysFEntry; + name = usprintf("%s", variable->name().c_str()); } - - // FTW store at current offset parsed correctly - // Check if we need to add a padding before it - if (!outerPadding.isEmpty()) { - UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); - outerPadding.clear(); + if (variable->len_name() == 3 && variable->name() == "EOF") { + header = volumeBody.mid(sysfVariableOffset, 4); } - - // Construct header and body - UByteArray body = ftw.mid(header.size(), storeSize - header.size()); - - // Add info - const EFI_GUID* guid = (const EFI_GUID*)header.constData(); - UString name = UString("FTW store"); - UString info = UString("Signature: ") + guidToUString(*guid, false); - info += usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nHeader CRC32: %08Xh", - (UINT32)storeSize, (UINT32)storeSize, + else { + header = volumeBody.mid(sysfVariableOffset, sizeof(UINT8) + (UINT32)variable->len_name() + sizeof(UINT16)); + body = volumeBody.mid(sysfVariableOffset + header.size(), (UINT32)variable->len_data()); + } + // Add generic info + UINT32 variableSize = (UINT32)header.size() + (UINT32)body.size(); + info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\n", + variableSize, variableSize, (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), - parsed.state(), - parsed.crc()) + (parsed.crc() != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); + (UINT32)body.size(), (UINT32)body.size()); - // Add header tree item - UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); + // Add tree item + model->addItem(sysfVariableOffset, Types::SysFEntry, subtype, name, UString(), info, header, body, UByteArray(), Fixed, headerIndex); - storeFound = true; - storeOffset += storeSize; - previousStoreEndOffset = storeOffset; - } catch (...) { - // Parsing failed, try something else + sysfVariableOffset += variableSize; } - // Insyde FDC - try { - if (volumeBodySize - storeOffset < sizeof(FDC_VOLUME_HEADER)) { - // No need to parse further, the rest of the volume is too small - throw 0; - } - - UByteArray fdc = volumeBody.mid(storeOffset); - umemstream is(fdc.constData(), fdc.size()); - kaitai::kstream ks(&is); - insyde_fdc_t parsed(&ks); - UINT32 storeSize = parsed.fdc_size(); - - // FDC store at current offset parsed correctly - // Check if we need to add a padding before it - if (!outerPadding.isEmpty()) { - UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); - outerPadding.clear(); - } - - // Construct header and body - UByteArray header = fdc.left(parsed.len_fdc_store_header()); - UByteArray body = fdc.mid(header.size(),storeSize - header.size()); - + + // Add free space or padding after all variables, if needed + UINT32 freeSpaceOffset = sysfVariableOffset - storeOffset; + if (freeSpaceOffset < storeSize) { + UByteArray freeSpace = sysf.mid(freeSpaceOffset, storeSize - freeSpaceOffset); // Add info - UString name = UString("FDC store"); - UString info = usprintf("Signature: _FDC\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)", - storeSize, storeSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size()); - - // Add header tree item - UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); + info = usprintf("Full size: %Xh (%u)", (UINT32)freeSpace.size(), (UINT32)freeSpace.size()); - // Parse FDC body as normal VSS/VSS2 storage with size override - parseNvramVolumeBody(headerIndex, (UINT32)body.size()); - - storeFound = true; - storeOffset += storeSize; - previousStoreEndOffset = storeOffset; - } catch (...) { - // Parsing failed, try something else + // Check that remaining unparsed bytes are actually zeroes + if (freeSpace.count('\x00') == freeSpace.size() - 4) { // Free space, 4 last bytes are always CRC32 + // Add tree item + model->addItem(sysfVariableOffset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + } + else { + // Add tree item + model->addItem(sysfVariableOffset, Types::Padding, getPaddingType(freeSpace), UString("Padding"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + } } - // Apple Fsys/Gaid - - // Phoenix EVSA - // Phoenix FlashMap - // Phoenix CMDB - // Phoenix SLIC Pubkey/Marker - // Intel uCode + storeOffset += storeSize - 1; + previousStoreEndOffset = storeOffset + 1; + continue; + } catch (...) { + // Parsing failed, try something else } - + + // Phoenix EVSA + // Phoenix FlashMap + // Phoenix CMDB + // Phoenix SLIC Pubkey/Marker + // Intel uCode + // Padding if (storeOffset < volumeBodySize) { outerPadding += volumeBody[storeOffset]; @@ -795,8 +902,16 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 if (!outerPadding.isEmpty()) { // Add info UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - // Add tree item - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + + // Check that remaining unparsed bytes are actually empty + if (outerPadding.count(emptyByte) == outerPadding.size()) { + // Add tree item + model->addItem(localOffset + previousStoreEndOffset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + } + else { + // Add tree item + model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + } } return U_SUCCESS; diff --git a/common/types.cpp b/common/types.cpp index 32e3791..9b51628 100755 --- a/common/types.cpp +++ b/common/types.cpp @@ -55,14 +55,14 @@ UString itemTypeToUString(const UINT8 type) case Types::Vss2Store: return UString("VSS2 store"); case Types::FtwStore: return UString("FTW store"); case Types::FdcStore: return UString("FDC store"); - case Types::FsysStore: return UString("Fsys store"); + case Types::SysFStore: return UString("SysF store"); 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"); + case Types::SysFEntry: return UString("SysF entry"); case Types::EvsaEntry: return UString("EVSA entry"); case Types::FlashMapEntry: return UString("FlashMap entry"); case Types::Microcode: return UString("Microcode"); @@ -128,9 +128,9 @@ UString itemSubtypeToUString(const UINT8 type, const UINT8 subtype) else if (subtype == Subtypes::AuthVssEntry) return UString("Auth"); else if (subtype == Subtypes::IntelVssEntry) return UString("Intel"); break; - case Types::FsysEntry: - if (subtype == Subtypes::InvalidFsysEntry) return UString("Invalid"); - else if (subtype == Subtypes::NormalFsysEntry) return UString("Normal"); + case Types::SysFEntry: + if (subtype == Subtypes::InvalidSysFEntry) return UString("Invalid"); + else if (subtype == Subtypes::NormalSysFEntry) return UString("Normal"); break; case Types::EvsaEntry: if (subtype == Subtypes::InvalidEvsaEntry) return UString("Invalid"); diff --git a/common/types.h b/common/types.h index 5785a96..fde5e11 100755 --- a/common/types.h +++ b/common/types.h @@ -47,14 +47,14 @@ namespace Types { Vss2Store, FtwStore, FdcStore, - FsysStore, + SysFStore, EvsaStore, FlashMapStore, CmdbStore, NvarGuidStore, NvarEntry, VssEntry, - FsysEntry, + SysFEntry, EvsaEntry, FlashMapEntry, Microcode, @@ -138,9 +138,9 @@ namespace Subtypes { IntelVssEntry, }; - enum FsysEntrySubtypes { - InvalidFsysEntry = 150, - NormalFsysEntry, + enum SysFEntrySubtypes { + InvalidSysFEntry = 150, + NormalSysFEntry, }; enum EvsaEntrySubtypes { diff --git a/common/ubytearray.h b/common/ubytearray.h index b72faca..1623403 100644 --- a/common/ubytearray.h +++ b/common/ubytearray.h @@ -69,6 +69,7 @@ public: UByteArray & operator=(const UByteArray & ba) { d = ba.d; return *this; } UByteArray & operator+=(const UByteArray & ba) { d += ba.d; return *this; } + UByteArray & operator+=(const char c) { d += c; return *this; } bool operator== (const UByteArray & ba) const { return d == ba.d; } bool operator!= (const UByteArray & ba) const { return d != ba.d; } inline void swap(UByteArray &other) { std::swap(d, other.d); } diff --git a/common/utility.cpp b/common/utility.cpp index ec66bb2..ac809eb 100755 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -81,7 +81,7 @@ UString uniqueItemName(const UModelIndex & index) switch (model->type(index)) { case Types::NvarEntry: case Types::VssEntry: - case Types::FsysEntry: + case Types::SysFEntry: case Types::EvsaEntry: case Types::FlashMapEntry: case Types::File: diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index 6d89299..2fba14c 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -32,6 +32,11 @@ SET(PROJECT_SOURCES ../common/Tiano/EfiTianoDecompress.c ../common/ustring.cpp ../common/generated/ami_nvar.cpp + ../common/generated/apple_sysf.cpp + ../common/generated/edk2_vss.cpp + ../common/generated/edk2_vss2.cpp + ../common/generated/edk2_ftw.cpp + ../common/generated/insyde_fdc.cpp ../common/generated/intel_acbp_v1.cpp ../common/generated/intel_acbp_v2.cpp ../common/generated/intel_keym_v1.cpp