From 271465eb61b093ae00971f4ccb67efc62629e4b7 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 23 Feb 2025 11:25:27 +0700 Subject: [PATCH] Add EDK2 FTW parser --- UEFIExtract/CMakeLists.txt | 1 + UEFIFind/CMakeLists.txt | 1 + UEFITool/CMakeLists.txt | 1 + common/generated/edk2_ftw.cpp | 100 ++++++++++++++++++++++++++++++++ common/generated/edk2_ftw.h | 105 ++++++++++++++++++++++++++++++++++ common/ksy/edk2_ftw.ksy | 48 ++++++++++++++++ common/nvram.h | 4 +- common/nvramparser.cpp | 77 +++++++++++++++++++++++-- 8 files changed, 331 insertions(+), 6 deletions(-) create mode 100644 common/generated/edk2_ftw.cpp create mode 100644 common/generated/edk2_ftw.h create mode 100644 common/ksy/edk2_ftw.ksy diff --git a/UEFIExtract/CMakeLists.txt b/UEFIExtract/CMakeLists.txt index 2cf3c63..13dceed 100644 --- a/UEFIExtract/CMakeLists.txt +++ b/UEFIExtract/CMakeLists.txt @@ -37,6 +37,7 @@ SET(PROJECT_SOURCES ../common/generated/ami_nvar.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 diff --git a/UEFIFind/CMakeLists.txt b/UEFIFind/CMakeLists.txt index c626d47..e3b8491 100644 --- a/UEFIFind/CMakeLists.txt +++ b/UEFIFind/CMakeLists.txt @@ -34,6 +34,7 @@ SET(PROJECT_SOURCES ../common/generated/ami_nvar.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 diff --git a/UEFITool/CMakeLists.txt b/UEFITool/CMakeLists.txt index b38a1b3..1b57289 100644 --- a/UEFITool/CMakeLists.txt +++ b/UEFITool/CMakeLists.txt @@ -70,6 +70,7 @@ SET(PROJECT_SOURCES ../common/generated/ami_nvar.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 diff --git a/common/generated/edk2_ftw.cpp b/common/generated/edk2_ftw.cpp new file mode 100644 index 0000000..ebc775b --- /dev/null +++ b/common/generated/edk2_ftw.cpp @@ -0,0 +1,100 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "edk2_ftw.h" +#include "../kaitai/exceptions.h" + +edk2_ftw_t::edk2_ftw_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, edk2_ftw_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = this; (void)p__root; + f_len_ftw_store_header_32 = false; + f_len_ftw_store_header_64 = false; + _read(); +} + +void edk2_ftw_t::_read() { + m_signature = m__io->read_u4le(); + { + uint32_t _ = signature(); + if (!( ((_ == 4293995405UL) || (_ == 2656577835UL)) )) { + throw kaitai::validation_expr_error(signature(), _io(), std::string("/seq/0")); + } + } + n_signature_main = true; + if (signature() == 4293995405UL) { + n_signature_main = false; + m_signature_main = m__io->read_bytes(12); + if (!(signature_main() == std::string("\x96\x76\x8B\x4C\xA9\x85\x27\x47\x07\x5B\x4F\x50", 12))) { + throw kaitai::validation_not_equal_error(std::string("\x96\x76\x8B\x4C\xA9\x85\x27\x47\x07\x5B\x4F\x50", 12), signature_main(), _io(), std::string("/seq/1")); + } + } + n_signature_edk2_working_block = true; + if (signature() == 2656577835UL) { + n_signature_edk2_working_block = false; + m_signature_edk2_working_block = m__io->read_bytes(12); + if (!(signature_edk2_working_block() == std::string("\x68\x7C\x7D\x49\x0A\xCE\x65\x00\xFD\x9F\x1B\x95", 12))) { + throw kaitai::validation_not_equal_error(std::string("\x68\x7C\x7D\x49\x0A\xCE\x65\x00\xFD\x9F\x1B\x95", 12), signature_edk2_working_block(), _io(), std::string("/seq/2")); + } + } + n_signature_vss2_working_block = true; + if (signature() == 2656577835UL) { + n_signature_vss2_working_block = false; + m_signature_vss2_working_block = m__io->read_bytes(12); + if (!(signature_vss2_working_block() == std::string("\x68\x7C\x7D\x49\xA0\xCE\x65\x00\xFD\x9F\x1B\x95", 12))) { + throw kaitai::validation_not_equal_error(std::string("\x68\x7C\x7D\x49\xA0\xCE\x65\x00\xFD\x9F\x1B\x95", 12), signature_vss2_working_block(), _io(), std::string("/seq/3")); + } + } + m_crc = m__io->read_u4le(); + m_state = m__io->read_u1(); + m_reserved = m__io->read_bytes(3); + m_len_write_queue_32 = m__io->read_u4le(); + n_len_write_queue_64 = true; + if (kaitai::kstream::mod(len_write_queue_32(), 16) == 0) { + n_len_write_queue_64 = false; + m_len_write_queue_64 = m__io->read_u4le(); + } + n_write_queue_32 = true; + if (kaitai::kstream::mod(len_write_queue_32(), 16) == 4) { + n_write_queue_32 = false; + m_write_queue_32 = m__io->read_bytes(len_write_queue_32()); + } + n_write_queue_64 = true; + if (kaitai::kstream::mod(len_write_queue_32(), 16) == 0) { + n_write_queue_64 = false; + m_write_queue_64 = m__io->read_bytes(((static_cast(len_write_queue_64()) << 32) + len_write_queue_32())); + } +} + +edk2_ftw_t::~edk2_ftw_t() { + _clean_up(); +} + +void edk2_ftw_t::_clean_up() { + if (!n_signature_main) { + } + if (!n_signature_edk2_working_block) { + } + if (!n_signature_vss2_working_block) { + } + if (!n_len_write_queue_64) { + } + if (!n_write_queue_32) { + } + if (!n_write_queue_64) { + } +} + +int8_t edk2_ftw_t::len_ftw_store_header_32() { + if (f_len_ftw_store_header_32) + return m_len_ftw_store_header_32; + m_len_ftw_store_header_32 = 28; + f_len_ftw_store_header_32 = true; + return m_len_ftw_store_header_32; +} + +int8_t edk2_ftw_t::len_ftw_store_header_64() { + if (f_len_ftw_store_header_64) + return m_len_ftw_store_header_64; + m_len_ftw_store_header_64 = 32; + f_len_ftw_store_header_64 = true; + return m_len_ftw_store_header_64; +} diff --git a/common/generated/edk2_ftw.h b/common/generated/edk2_ftw.h new file mode 100644 index 0000000..7399d3e --- /dev/null +++ b/common/generated/edk2_ftw.h @@ -0,0 +1,105 @@ +#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 + +#if KAITAI_STRUCT_VERSION < 9000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required" +#endif + +class edk2_ftw_t : public kaitai::kstruct { + +public: + + edk2_ftw_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, edk2_ftw_t* p__root = nullptr); + +private: + void _read(); + void _clean_up(); + +public: + ~edk2_ftw_t(); + +private: + bool f_len_ftw_store_header_32; + int8_t m_len_ftw_store_header_32; + +public: + int8_t len_ftw_store_header_32(); + +private: + bool f_len_ftw_store_header_64; + int8_t m_len_ftw_store_header_64; + +public: + int8_t len_ftw_store_header_64(); + +private: + uint32_t m_signature; + std::string m_signature_main; + bool n_signature_main; + +public: + bool _is_null_signature_main() { signature_main(); return n_signature_main; }; + +private: + std::string m_signature_edk2_working_block; + bool n_signature_edk2_working_block; + +public: + bool _is_null_signature_edk2_working_block() { signature_edk2_working_block(); return n_signature_edk2_working_block; }; + +private: + std::string m_signature_vss2_working_block; + bool n_signature_vss2_working_block; + +public: + bool _is_null_signature_vss2_working_block() { signature_vss2_working_block(); return n_signature_vss2_working_block; }; + +private: + uint32_t m_crc; + uint8_t m_state; + std::string m_reserved; + uint32_t m_len_write_queue_32; + uint32_t m_len_write_queue_64; + bool n_len_write_queue_64; + +public: + bool _is_null_len_write_queue_64() { len_write_queue_64(); return n_len_write_queue_64; }; + +private: + std::string m_write_queue_32; + bool n_write_queue_32; + +public: + bool _is_null_write_queue_32() { write_queue_32(); return n_write_queue_32; }; + +private: + std::string m_write_queue_64; + bool n_write_queue_64; + +public: + bool _is_null_write_queue_64() { write_queue_64(); return n_write_queue_64; }; + +private: + edk2_ftw_t* m__root; + kaitai::kstruct* m__parent; + +public: + uint32_t signature() const { return m_signature; } + std::string signature_main() const { return m_signature_main; } + std::string signature_edk2_working_block() const { return m_signature_edk2_working_block; } + std::string signature_vss2_working_block() const { return m_signature_vss2_working_block; } + uint32_t crc() const { return m_crc; } + uint8_t state() const { return m_state; } + std::string reserved() const { return m_reserved; } + uint32_t len_write_queue_32() const { return m_len_write_queue_32; } + uint32_t len_write_queue_64() const { return m_len_write_queue_64; } + std::string write_queue_32() const { return m_write_queue_32; } + std::string write_queue_64() const { return m_write_queue_64; } + edk2_ftw_t* _root() const { return m__root; } + kaitai::kstruct* _parent() const { return m__parent; } +}; diff --git a/common/ksy/edk2_ftw.ksy b/common/ksy/edk2_ftw.ksy new file mode 100644 index 0000000..e3f9fe7 --- /dev/null +++ b/common/ksy/edk2_ftw.ksy @@ -0,0 +1,48 @@ +meta: + id: edk2_ftw + title: EDK2 Fault Tolerant Write NVRAM store + application: EDK2-based UEFI firmware + file-extension: ftw + tags: + - firmware + license: CC0-1.0 + ks-version: 0.9 + endian: le + +seq: +- id: signature + type: u4 + valid: + expr: _ == 0xFFF12B8D or _ == 0x9E58292B +- id: signature_main + contents: [0x96, 0x76, 0x8B, 0x4C, 0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50] # FF12B8D-7696-4C8B-A985-2747075B4F50 + if: signature == 0xFFF12B8D +- id: signature_edk2_working_block + contents: [0x68, 0x7C, 0x7D, 0x49, 0x0A, 0xCE, 0x65, 0x00, 0xFD, 0x9F, 0x1B, 0x95] # 9E58292B-7C68-497D-0ACE-6500FD9F1B95 + if: signature == 0x9E58292B +- id: signature_vss2_working_block + contents: [0x68, 0x7C, 0x7D, 0x49, 0xA0, 0xCE, 0x65, 0x00, 0xFD, 0x9F, 0x1B, 0x95] # 9E58292B-7C68-497D-A0CE-6500FD9F1B95 + if: signature == 0x9E58292B +- id: crc + type: u4 +- id: state + type: u1 +- id: reserved + size: 3 +- id: len_write_queue_32 + type: u4 +- id: len_write_queue_64 + type: u4 + if: len_write_queue_32 % 0x10 == 0 +- id: write_queue_32 + size: len_write_queue_32 + if: len_write_queue_32 % 0x10 == 0x04 +- id: write_queue_64 + size: ((len_write_queue_64.as) << 32) + len_write_queue_32 + if: len_write_queue_32 % 0x10 == 0 + +instances: + len_ftw_store_header_32: + value: 28 + len_ftw_store_header_64: + value: 32 diff --git a/common/nvram.h b/common/nvram.h index 05323d1..fa3d165 100755 --- a/common/nvram.h +++ b/common/nvram.h @@ -204,8 +204,8 @@ typedef struct FDC_VOLUME_HEADER_ { // #define EFI_FAULT_TOLERANT_WORKING_BLOCK_VALID 0x1 #define EFI_FAULT_TOLERANT_WORKING_BLOCK_INVALID 0x2 -extern const UByteArray EDKII_WORKING_BLOCK_SIGNATURE_GUID; // 9E58292B-7C68-497D-0ACE6500FD9F1B95 -extern const UByteArray VSS2_WORKING_BLOCK_SIGNATURE_GUID; // 9E58292B-7C68-497D-A0CE6500FD9F1B95 +extern const UByteArray EDKII_WORKING_BLOCK_SIGNATURE_GUID; // 9E58292B-7C68-497D-0ACE-6500FD9F1B95 +extern const UByteArray VSS2_WORKING_BLOCK_SIGNATURE_GUID; // 9E58292B-7C68-497D-A0CE-6500FD9F1B95 #define NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 0xFFF12B8D #define EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1 0x9E58292B diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp index 6c39c64..54f0704 100644 --- a/common/nvramparser.cpp +++ b/common/nvramparser.cpp @@ -28,6 +28,7 @@ #include "generated/ami_nvar.h" #include "generated/edk2_vss.h" #include "generated/edk2_vss2.h" +#include "generated/edk2_ftw.h" #include "generated/insyde_fdc.h" USTATUS NvramParser::parseNvarStore(const UModelIndex & index) @@ -660,7 +661,76 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 // 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; + } + + 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); + } + 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 + UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); + + storeFound = true; + storeOffset += storeSize; + previousStoreEndOffset = storeOffset; + } catch (...) { + // Parsing failed, try something else + } // Insyde FDC try { if (volumeBodySize - storeOffset < sizeof(FDC_VOLUME_HEADER)) { @@ -693,10 +763,10 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 (UINT32)header.size(), (UINT32)header.size(), (UINT32)body.size(), (UINT32)body.size()); - // Add header tree item with modified body + // Add header tree item UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); - // Parse modified FDC body as normal VSS/VSS2 storage + // Parse FDC body as normal VSS/VSS2 storage with size override parseNvramVolumeBody(headerIndex, (UINT32)body.size()); storeFound = true; @@ -706,7 +776,6 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 // Parsing failed, try something else } - // Apple Fsys/Gaid // Phoenix EVSA